Commit Graph

22 Commits

Author SHA1 Message Date
Jeff Emmett 6dae60ea1f feat: single-click text inputs to edit, drag/resize on shape body
Shapes are now grabbable/draggable/resizable on first click (body or
handles), editable on double-click, but clicking directly on a text
input (textarea, contenteditable, text input) immediately enters edit
mode and focuses the input — no double-click required.

Works by hit-testing text inputs at pointer coordinates before
initiating drag, checking both light DOM and shadow DOM of slotted
content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 12:03:33 -08:00
Jeff Emmett 4f9b036cc0 fix: preserve shape x/y/size on reload + fix eraser Automerge persistence
createRenderRoot() was unconditionally reading x/y/width/height from HTML
attributes, overwriting values already set via JS properties before DOM
insertion. This caused all shapes to stack at (0,0) with auto dimensions
on page reload. Now only reads from attributes when they exist.

Also fixed eraser: hardDeleteShape() was only in the click handler which
never fired because pointerdown already removed the target element.
Moved Automerge deletion into the pointerdown handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:07:41 -08:00
Jeff Emmett 4e9284fa5a feat: feed view — mobile-friendly infinite scroll for canvas
Add a toggle that switches the 2D spatial canvas into a vertical
scrollable feed layout. Shapes flow as a flex-column list, sortable
by position, creation time, type, or alphabetically. Pan/zoom/drag
gestures are suppressed in feed mode while shape editing stays active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:57:24 -08:00
Jeff Emmett 5e0f30567a fix: prevent pointer events from hijacking two-finger touch pan
On touch devices, both pointer and touch events fire. When a second
finger was added, the pointer handler re-captured the interaction,
fighting the touch-based pan/pinch. Now the touch handler releases
pointer captures and sets a flag that blocks the pointer handler
during two-finger gestures. Also cancels shape drag on multi-touch
and closes the context menu on touchstart for reliable mobile dismiss.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 18:02:53 -08:00
Jeff Emmett 658eb966d6 fix: push overlapping siblings instead of displacing the dragged shape
The overlap resolver now moves siblings in the drag direction rather
than snapping the dragged shape away from them. Supports chain-pushing
(A pushes B into C) with a recursion depth of 3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:51:34 -08:00
Jeff Emmett 7db6068229 feat: add forgotten-shape tooltip and "Hide Faded" toggle
Hovering a forgotten shape now shows a tooltip explaining the state.
A new "Hide Faded" toolbar button lets users hide all forgotten shapes
entirely, with the preference persisted in localStorage. Hidden shapes
reappear automatically when another user remembers them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 15:05:20 -08:00
Jeff Emmett 317bc46de6 feat: three-state FUN — present, forgotten (faded), deleted
Shapes now have three states instead of two. "Forgetting" a shape fades
it (35% opacity, greyscale) for all connected clients rather than hiding
it. Other users can then choose to "forget too", "remember" (restore),
or "delete" (hard-remove from DOM). A forgottenBy map tracks who forgot,
enabling social signaling around shared attention.

- folk-shape.ts: :state(forgotten) CSS + forgotten property
- community-sync.ts: forgetShape(id,did), rememberShape, hardDeleteShape,
  getShapeVisualState, hasUserForgotten, getFadedShapes, getDeletedShapes
- community-store.ts: forgottenBy map server-side, rememberShape clears map
- canvas.html: right-click context menu, two-section memory panel (Fading/
  Deleted), close button fades instead of removes, Delete key escalates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:44:02 -08:00
Jeff Emmett f69ee9f977 fix: shape resize sync and transform event name alignment
- Sync #attrWidth/#attrHeight during resize so CSS reflects new dimensions
- Call #dispatchTransformEvent() directly for immediate visual update
- Fix TransformEvent to fire "folk-transform" matching CommunitySync listeners
- Reduce min-width/min-height on folk-markdown and folk-wrapper to 80x40px

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:11:02 -08:00
Jeff Emmett aeb9247f96 fix: shape resize/rotate by converting screen coords to canvas space
Resize and rotation handlers in folk-shape.ts passed raw event.clientX/Y
(screen coordinates) to toLocalSpace/angleFromOrigin, but those methods
expect canvas-parent coordinates. With any zoom/pan, the two coordinate
systems diverge, making resize non-functional and rotation erratic.

Added #screenToParent() to convert viewport coords to the parent's
coordinate space using getBoundingClientRect + parent scale. Applied to:
- Resize handle drag (pointermove → toLocalSpace)
- Rotation start (pointerdown → angleFromOrigin)
- Rotation drag (pointermove → angleFromOrigin)

Also syncs ghost placeholder size with zoom changes so the dotted
preview stays accurate if user zooms while in placement mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:38:06 -08:00
Jeff Emmett 7616fe0757 feat: show resize handles on selected shapes + pan-first canvas navigation
Resize handles now appear when a shape is highlighted/focused, not just during
active drag. Canvas left-click+drag now pans by default; hold 250ms then drag
for marquee selection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:13:22 -08:00
Jeff Emmett 9742bd1409 fix: zoom-aware drag, minimum-penetration collision, replace unicode escapes
- Shape drag now accounts for canvas CSS transform scale so elements
  track the cursor correctly at all zoom levels
- Collision resolution uses minimum penetration depth algorithm instead
  of movement-direction bias, preventing elements from flipping sides
- Replaced all \uD83D surrogate pair escapes and \u00D7/\u276E/\u276F/
  \u2022 escapes with actual Unicode characters across 60+ files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:48:50 -08:00
Jeff Emmett 1d8fc2b23b feat: default selector tool with marquee multi-select, space+drag pan
Replace single-click-to-pan with selector as default tool. Left-click-drag
on canvas background draws a blue marquee rectangle to select multiple shapes.
Shift/Ctrl+click toggles additive selection. Panning now via Space+drag,
middle-click, or wheel/trackpad (unchanged). Delete/Backspace removes all
selected shapes. folk-shape highlighted state shows blue selection outline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:35:31 -08:00
Jeff Emmett eee9cbed69 fix: shape overlap push-aside, coordinate persistence, toolbar panel clipping
- Collision: shapes now slide-off in movement direction by minimum
  penetration depth instead of flipping to the opposite side
- Coordinates: use nullish coalescing (??) so x=0/y=0 are preserved
  on reload instead of being replaced by falsy-check defaults
- Toolbar: remove overflow:hidden from #toolbar-panel so submenus
  render fully visible instead of being clipped/scrolled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:20:06 -08:00
Jeff Emmett 303433fa49 fix: make shape content interactive + render emojis properly
- Scope pointer-events:none to .slot-container instead of all divs,
  so buttons/textareas/inputs inside shapes are clickable again
- Replace unicode escapes (\u{xxxx}) in html`` tagged templates with
  actual emoji characters — String.raw doesn't process escapes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:15:31 -08:00
Jeff Emmett 15e6a9b9ba fix: remove scrollbar arrows from shapes + add collision slide-off
- Change inner div overflow from scroll to hidden, removing browser
  scrollbar arrows that appeared on every canvas shape
- Add shape collision detection: shapes now slide off each other with
  an 8px gap instead of overlapping when dragged (pointer + touch)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:07:10 -08:00
Jeff Emmett 3f7c649b54 fix: remove distracting outlines and resize handles from canvas shapes
Handles now only appear during active move/resize, not on hover or focus.
Removes blue outline borders that cluttered the canvas.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:03:51 -08:00
Jeff Emmett 2d0cf499f6 fix: mobile input focus, toolbar UX, and viewport clipping
- folk-shape: skip preventDefault on touch events targeting inputs/textareas
  so mobile keyboards can open inside shapes
- toolbar: replace unreadable emoji-only strip with FAB toggle (+) that
  opens a 3-column grid overlay with emoji + labels; auto-closes on tool
  select; separate always-visible zoom strip at bottom-left
- canvas: remove overflow:hidden from #canvas so viewport moves with
  pan/zoom instead of clipping at initial bounds
- canvas-content: add width/height 100% and overflow:visible for robust
  containing block

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:55:43 +00:00
Jeff Emmett 42b29ff9d7 fix: Resolve replaceChild and activeElement errors in FolkJS components
- Fix activeElement undefined error by guarding against missing shadowRoot
- Fix replaceChild "parameter 2 is not of type Node" error in all 15 child
  components by using :scope > div selector to find container div directly
  instead of incorrectly searching inside slot.parentElement

The bug was caused by looking for a nested div that doesn't exist - the slot's
parent IS the container div that needs to be replaced.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 13:38:06 +01:00
Jeff Emmett 10786f5723 feat: Add mobile touch support for canvas
FolkShape:
- Single-touch drag with position delta tracking
- Touch event handling (touchstart, touchmove, touchend)
- Respects viewport zoom level

Canvas:
- Pinch-to-zoom with two-finger gesture
- Two-finger pan for navigation
- Mouse wheel zoom for desktop
- touch-action: none to prevent browser gestures
- Larger touch targets on coarse pointer devices

Completes task-6: Mobile touch support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 18:57:51 +01:00
Jeff Emmett aa204a530a feat: Add shared FolkJS utilities (maximize, pinned-view, toJSON)
- maximize.ts: maximizeShape(), restoreShape(), toggleMaximize()
- pinned-view.ts: PinnedViewManager class for viewport-fixed shapes
- folk-shape.ts: Base toJSON() method for Automerge sync
- Updated exports in lib/index.ts

Completes task-8: Port shared hooks as FolkJS utilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 18:36:40 +01:00
Jeff Emmett 7ebf45e984 Fix shape dragging - allow drag from header elements 2026-01-02 14:32:02 +01:00
Jeff Emmett 1ec463f193 Initial rspace-online: FolkJS collaborative canvas with subdomain routing
- Pure FolkJS implementation with folk-shape, folk-markdown components
- Bun server with WebSocket sync and Host header subdomain detection
- Community creation API at /api/communities
- Docker setup with Traefik labels for wildcard *.rspace.online routing
- Landing page with community creation form
- Canvas page with basic markdown note creation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 16:27:07 +01:00