Skip to content

feat: minor ui fixes and alignments - #98

Merged
winlp4ever merged 12 commits into
mainfrom
feat/click-place-tools-only
Jun 3, 2026
Merged

winlp4ever merged 12 commits into
mainfrom
feat/click-place-tools-only

Conversation

@winlp4ever

Copy link
Copy Markdown
Contributor

No description provided.

winlp4ever added 12 commits June 3, 2026 14:18
`EditorMount`'s host div is `pointer-events: none` so the canvas
behind still receives pan/select events while editing — the editor
adapter has to opt back in. The default `createDefaultTextareaEditor`
in canvas-harness does (`wrap.style.pointerEvents = 'auto'`); our
custom `createHarnessTextareaEditor` was missing it. Result: mouse
clicks on the textarea passed through to the canvas; keyboard /
arrow keys worked because focus was set programmatically, but the
caret couldn't be repositioned by clicking.

One line + comment matching the upstream rationale.
`handleClick` previously created a default-size node for any shape
tool. That meant a sub-5px drag (canvas-harness's threshold for
treating a gesture as a click instead of a drag) littered the canvas
with accidental 200×120 shapes whenever the user tapped without
intending to draw.

Narrow `handleClick` to a `CLICK_PLACE_TOOLS` subset: text + the
fixed-size surface tools (folder / sheet / code-sandbox / widget),
where click-to-place is the established UX (matches tldraw /
excalidraw / figma for text; the surface tools have no meaningful
drag-to-size). Resizable shapes (rect, ellipse, diamond, tag,
capsule, thought-cloud, layered-*, soft-diamond, frame) now require
an actual drag past 5px — click does nothing.

`handleCreateDrag` is unchanged; canvas-harness's internal 5px
threshold still gates whether a gesture fires drag vs. click.
Double-click on empty space still creates a quick text node via the
separate `handleDoubleClick` path.
Every single-node create path now calls `store.setSelection([id])`
after `store.addNode`. Matches tldraw / excalidraw / figma — the
user expects to immediately resize / style / move the thing they
just made, not first round-trip through the select tool.

Touchpoints:
- `handleCreateDrag` (drag-to-create shapes)
- `handleClick` (click-to-place: text / folder / sheet / code-sandbox
  / widget)
- `handleDoubleClick` (dbl-click empty space → text node, now both
  selected AND in edit mode — matches sticky-note UX)
- `useHarnessAddImage` (drop image)

The icon path already auto-selected. AI mindmap drain still doesn't
— selecting a 20-node tree on a single user action would be jarring.
Pan / Select / Slides already switched to `weight="fill"` on active.
The remaining four tool buttons (Shape, Connector, Text, Note) kept
outline icons even when their tool was the current selection, so the
toolbar's "active tool" signal was a mix of bg swap + sometimes-fill.

Add `weight={isActive ? "fill" : undefined}` to all four. Now every
tool button uses the same fill-on-active pattern.
Pull the secondary chip + its foreground onto hue 38 in both light
and dark parchment. Matches the wiki-link / ring tokens that already
use that hue, so the "secondary" surface reads as part of the same
warm palette instead of a slightly-off neighbour at hue 50/45.

Light:
  --secondary             0.92 0.04 50  →  0.94 0.04 38
  --secondary-foreground  0.38 0.10 38  →  0.55 0.11 38
Dark:
  --secondary             0.24 0.05 40  →  0.26 0.04 38
  --secondary-foreground  0.86 0.08 45  →  0.80 0.11 38

Other themes (catppuccin / tokyo-night / gruvbox / monokai-pro /
rose-pine) keep their intentional palettes.
- View dropdown trigger now renders its icon `weight="fill"` (the
  button represents the current view, perpetually active). Menu items
  fill the icon only for the option matching `viewMode` so the open
  menu surfaces the active row visually.
- List view (`ListView`) rows now sit edge-to-edge: container gap
  `gap-3` → `gap-0`. The tree-bracket connector already draws a
  continuous vertical line through the row midpoints, so collapsing
  the gap yields a tight Finder-like density without breaking the
  bracket layout.
- Sidebar accent + ring tokens in parchment (light + dark) realigned
  to the new sienna palette (hue 38) so the sidebar's "active" chip
  and focus ring match the now-unified secondary surface.
The four `--sidebar-icon-*` tokens in parchment-light were the only
hex values left among the sidebar-icon family — every other theme
already uses oklch. Round-tripped each hex through the CSS Color
Module 4 conversion and verified inverse renders back to the
identical byte, so this is purely a code-format change with zero
visual delta.

  #3439c9 -> oklch(0.448 0.216 272.1)  blue
  #336d3f -> oklch(0.483 0.095 148.6)  green
  #965e30 -> oklch(0.535 0.095 58.0)   sienna
  #a6395c -> oklch(0.509 0.146 4.1)    rose

`--code-block-bg` stays hex everywhere — it's load-bearing match
with Shiki theme files which are hex-based; converting risks a 1-bit
mismatch between our wrapper bg and the syntax-highlighted code
output.
Three chrome elements stayed visible during presentation mode and
broke the "clean slide" feel: the top toolbar, the bottom-left
viewport controls panel, and the bottom-right minimap.

Gate each on `presentationMode` in `HarnessCanvasInner` — same
pattern `HarnessReadonlyChip` already uses. When presenting, the
three unmount; toggling presentation off remounts them. The
top-right chrome row (peer chip / collab status / share) stays —
those are informational and each child decides its own visibility.

Marginal perf win during presentation: minimap stops repainting on
every camera move, viewport controls stop re-rendering on zoom,
toolbar stops re-rendering on tool changes.
canvas-harness 0.1.15 added an `assetCache` field to `ExportOptions`.
When omitted, `exportSelection` silently skips image and icon nodes
in the PNG output (back-compat shape). Our context menu's PNG export
wasn't passing it, so any selection containing an image or icon
exported as an empty space.

Thread the `rendererRef` already kept by `HarnessCanvas` into
`CanvasContextMenu` and pass `rendererRef.current?.getAssetCache()`
on the `exportSelection` call. The cache holds already-decoded
bitmaps the live canvas paid to populate, so this is also a slight
perf win at export time (no fresh decode work).

SVG export (`exportSelectionSvg`) is unaffected — it inlines
`data.src` directly for both image (data URI) and icon (SVG markup)
nodes, no cache needed.
@winlp4ever
winlp4ever merged commit 44fcd04 into main Jun 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant