Conversation
- fix node inputs (slider/color/select) being reset by store round-trip: one-way data flow via setNodeData instead of bind:value - fix peer handshake being dropped: send on connection open event instead of a fixed 500ms delay (channel can take longer to open) - right-click context menu: create nodes from grouped submenus, disconnect/delete nodes, disconnect edges; Delete key also works - node catalog with Input/Scene/Animation/Effects groups - animation runtime (shake, spin, bounce, orbit, pulse, blink) driving scene objects via objectselector connections, runs with drawer closed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r toasts - flow drawer and object list visibility moved from DOM classList hacks to stores (flowGraphClose, objectListClose) so they can be orchestrated - hidePanels/restorePanels helper in appStore with a single snapshot slot - settings modal hides all panels (incl. chat and flow) and restores on close - spectate mode hides editing panels, keeps chat and flow, restores on exit - peerjs error/disconnected handlers with user-facing toasts and up to 3 reconnect attempts to the signaling server Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- central shortcut registry in src/lib/shortcuts.js binds the keys and renders the Settings -> Shortcuts section from the same source - W/E/R switch transform modes, O/N/C toggle object list/node editor/chat - guards: inert while typing in inputs, in play mode, or when settings is open (except Ctrl+/) - Ctrl+/ opens settings with the shortcuts section expanded - keycap hints in the bottom nav tooltips Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shared selection logic extracted to src/lib/objectActions.js (lock-aware, gizmo attach, properties drawer refresh) and reused by the object list - clicking an object in the viewport selects it (raycast against scene objects, resolves to the top-level group child); clicking empty space deselects and closes the properties drawers; drag gestures never select - pointerup is captured on window because the Canvas wrapper swallows it - VR: controller trigger press selects the pointed-at object - F focuses the selected object: pans the orbit target and dollies to a framing distance over 400ms, keeping the view direction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shared ContextMenu component (edge-aware flip, submenus, disabled items with tooltips); flow editor refactored to use it - object list right-click menu: focus camera, duplicate, rename (inline input), show/hide, enable/disable flow effects; lock-aware disabling - duplicate replicates with a lean message: peers clone their own copy of the source and apply the originator uuids (same depth-first order), cloned meshes get their own materials - Ctrl+D duplicates the selected object - per-object flow-effects mute list respected by the animation runtime - drop .glb/.gltf/.json files anywhere on the viewport to import; other formats show a supported-formats toast - toast container no longer blocks clicks on the area beneath it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- right-click on the viewport (click, not orbit-pan drag) opens a menu: undo/redo, snapping submenu, focus selected, duplicate selected, grid toggle, screenshot; measure/align greyed out as to-be-implemented - transform-only history (50 steps): one entry per gizmo drag captured via dragging-changed; undo/redo re-broadcasts the move to peers and explains itself with toasts at the limits; Ctrl+Z / Ctrl+Y / Ctrl+Shift+Z - snapping for translate, rotate AND scale with selectable steps, persisted in localStorage (snap to surface deferred, shown disabled) - screenshot re-renders and downloads the canvas as png Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- pointer position broadcast in flow coordinates (throttled to 20/s), so every peer renders it correctly under their own pan/zoom - PeerCursors overlay projects flow -> screen via the shared viewport store; arrow + name pill colored per peer (stable hash of the id) - cursors vanish on pane leave (explicit message) or after 4s without updates (peer closed the drawer or disconnected) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Texture row in mesh properties: pick an image (downscaled client-side to 1024px webp/jpeg), thumbnail preview, remove button; replicates via objectParameters map messages and reaches late joiners through the existing GLTF full-object sync - roughness/metalness (standard), shininess (phong) and wireframe controls replicate with a generic materialParam message - material type switching now carries over color/texture/opacity both locally and on peers (receivers previously reset to a default material) - shared stores get JSDoc types (fixes ~260 pre-existing check errors) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- importFile refactored into a per-format dispatch with a shared add-to-scene tail (select, gizmo, GLTF replication to peers) - OBJ (no .mtl materials), STL (standard material), FBX (static meshes, corrupt/old versions surface a toast instead of failing silently) - drag-and-drop and the Import button accept the new extensions - opt-in window.__stores test hook behind a localStorage flag Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- animation time base switches to wall clock (wrapped daily) so all peers compute the same phase without any sync protocol - Settings -> Scene: Sync animations checkbox (default on), persisted Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- primitives catalog drives the sidebar: Basics (Cylinder, Plane, Torus, Capsule) join Cube/Cone/Sphere, plus a Building blocks group - custom geometries Wedge, Stairs, Arch, Corner (Shape+Extrude based), centered on X/Z and resting on y=0 - registered in createGeometry so the replicated /create command builds identical shapes on every peer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- dropToSurface: raycast down from the dragged object, rest its bounding-box bottom on the first surface underneath (other objects or the ground plane); world-space math so grouped objects behave - surface snap toggle in the viewport Snapping submenu (persisted); applied during translate drags except deliberate Y-axis lifts, peers receive the snapped positions - Align to ground un-greyed: one-shot drop, replicated and undoable Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- moveObjectToGroup shared action (group uuid / up / root) with cycle guards; root moves replay one replicated up-hop per level because the root group uuid differs per client - object rows are draggable (locked rows excluded); group rows highlight and accept drops; dropping on the list header moves back to the root - Properties Move to group select now uses the same shared action Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- vrhands message: world-space controller poses broadcast from inside a VR session (same movement-threshold pattern as the camera stream), active:false sent on session end - peers render two colored controller markers with an aim pointer per VR user; markers clear on session end and on disconnect - manual headset check pending; rendering path verified by injecting vrhands messages in a two-peer test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- per-object edit mode (object list / viewport menu / Tab; Esc exits): instanced vertex handles deduplicated by position so corners never tear, dragged with the regular gizmo via a proxy object - wireframe overlay while editing; object locked for peers meanwhile - verts message replicates handle moves live (throttled) with a final unthrottled state on drag end; late joiners covered by the GLTF sync - undo/redo gains pluggable entry kinds; vertex drags are undoable and replay through the same replicated path - duplicated objects get their own geometry so editing a copy no longer deforms the original Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- peers render as configurable characters (body color, hat: cap/top hat/crown, optional avatar-photo face) instead of the gray sphere; the name label finally shows the username when set - config rides userdata slot 5: replicated on join and on change, persisted in localStorage; the userData merge forwards it - Customize Character (user dropdown) opens a modal with live-applying controls; late joiners receive avatars through the userdata handshake - moveCamera/spectate untouched: the rig root keeps the peer id name Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- custom 3D quick-menu floats above the configurable menu hand (A/X toggles it): move/rotate mode, snapping, grid, undo/redo, spawn primitives in front of the user, swap hands, close; the other hand points and confirms with the trigger - squeeze grabs the pointed-at object: Move follows the controller (grid snap honored), Rotate applies controller rotation, squeezing with both hands scales by controller distance; everything broadcasts the regular move message and records undo entries - VR menu hand selectable in Settings (and from the menu itself), persisted; risky HTMLMesh floating panels skipped per scope decision - manual headset checklist: menu on configured hand, tile actions, grab/rotate/two-hand scale replicate, selection via trigger Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
writeSelectedHandle returned a vector aliased to the shared tempVector, which refreshHandleMatrix overwrote with the handle WORLD position right before the value was broadcast and recorded in history. With the object at the origin world == local and nothing showed; after moving it, peers received vertex positions offset by the object translation - clicking a handle (gizmo attach fires a change event) made the peer vertex jump and drags continued from that offset. - write path uses a dedicated vector and captures a plain array before any shared temp vector is reused (broadcast + undo history now local) - gizmo attach no longer broadcasts: no-move changes are skipped Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- IndexedDB snapshots of the scene (GLTF json), node graph and camera: debounced 30s after changes + 3-minute interval + best-effort on unload; empty scenes never overwrite a good snapshot - restore toast on boot when a snapshot exists and the scene is empty; restoring re-adds objects (replicating to connected peers) and the flow graph, and repositions the camera - Settings: Autosave toggle + Clear saved session; annotations hook registered for the upcoming pins phase Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- measure mode (viewport menu, Esc exits): click two points (objects or the ground plane), a line with a billboarded distance label renders; local-only inspection tool - camera bookmarks: save up to 5 views, recall from the menu or Shift+1..5 with the smooth flyTo tween (extracted from focusObject and reused), persisted in localStorage - shortcut combos use event.code for digits so Shift+1 works across keyboard layouts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- mic toggle button (bottom right) streams to every whitelisted peer via peerjs MediaConnection; hold V is push-to-talk while the toggle is off (track-enable flip on a persistent stream, so it is instant) - listeners need no mic permission: answering without a stream still receives audio; only whitelisted callers are answered - speaking detection (AnalyserNode) drives a green ring under talking avatars; right-click a user avatar to mute/unmute them locally - calls join new peers automatically on the connection handshake and tear down on disconnect Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CLAUDE.md: architecture map, replication golden rules, hard-won svelte/three gotchas, verification requirements, workflow preferences, and the agreed modules/addons + code-node scripting baseline - .claude/skills/e2e-verify: Playwright single-page and two-peer recipes (debugStores hook, PeerJS cloud flow, known flakes) - .claude/skills/peer-feature: checklist for adding replicated features and the module/addon extension-point strategy Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add note from the object list or viewport menu: a billboarded numbered pin anchors to the object (object-local offset, follows moves); one note per pin with author and timestamp - replicates live (annotation set/delete messages) and to late joiners via a getannotations handshake reply; pins die with their object - clicking a pin flies the camera to it and opens the note editor; annotations persist inside the autosave snapshot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- every light gets a type-specific helper plus a wireframe bulb pick proxy at the scene root (never inside objectsGroup, so saves and peer syncs stay clean); clicking the proxy selects the light and the gizmo drags it with the existing move replication - Show light helpers toggle in Configure Scene, persisted - WASD flies the camera on its horizontal plane, Q down / E up, Shift 3x (camera and orbit target pan together); inert while typing, in play mode, spectating or VR - transform-mode hotkeys move from W/E/R to 1/2/3 to free the keys; tooltips and the shortcuts list updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- thin ray lines on both controllers, visible only in a session and shortening to the first hit - the pointer-hand ray tints the pointed object emissive (restored on leave, suppressed while the quick-menu is open) - thumbstick flick on the pointer hand snap-turns the rig around the viewer via reference-space rotation (movement math stays consistent); angle 15/30/45 in Settings, persisted Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Alt+click anywhere (object or ground), Ping this object in the list context menu, or VR thumbstick press on the pointed spot - pulsing ring + beam + author label rendered at the world point on every peer (peer-hash color, same scheme as flow cursors), expires 4s Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- dragging/undoing/aligning an animated object now edits its animation base instead of fighting the tick (suspend while dragging, adopt the release spot; remote moves and undo rebase via notifyExternalMove) - slider node scales its target (20 = neutral), switcher swaps the target geometry cube/pyramid - both deterministic on every peer - peers exchange a node-graph hash every 10s and the poorer side pulls a snapshot on mismatch, healing missed nodedata/move messages; snapshots now update existing nodes in place Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The vrhands broadcast labeled each pose by getController(slot).userData .handedness (the 194/210 stamp). In hand-tracking sessions that stamp can be missing, so BOTH left/right came out null -> the VR peer broadcast empty hands and desktop peers rendered nothing (the VR user still saw their own hands via threlte, hiding the bug). - resolve a slot's handedness robustly: stamped value, else the live session.inputSource (positional/handedness match) - isHandTracking now checks inputSource.hand through the same resolver - e2e: vr-peer-hands-net (two-peer) proves a vrhands broadcast reaches a desktop peer and renders (25 joint spheres + a box) -- the cross-peer coverage that was missing Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gers) Hand-tracking still fell back to the controller box on peers because the capture read renderer.xr.getHand(SLOT).joints by slot index — the tracked hand is not necessarily at that slot, so joints came back empty. Now read joints from threlte useHand(left/right) stores (the SAME XRHand spaces it renders locally), keyed by handedness, so a tracked hand at either slot is captured. - broadcastVRHands fills each hand by handedness: articulated joints if that hand is tracked (wrist.visible), else the controller slot pose - readControllerPose returns joints: [] (not null); peers branch on joints.length (Player), so a box shows for 0 joints and spheres for 25 - keeps the 194/210-safe controller labeling for the box path Still needs the on-device check (no hand-tracking session in CI); the two-peer + render suites stay green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CLAUDE.md: status = roadmap #7 shipped; architecture gains packs/PACKS.md, ModelPreview, networkQuality; gotchas add Threlte oncreate ref-capture (ref passed directly, not destructured) + reading WebXR hand joints via threlte useHand by handedness - e2e-verify skill: __stores adds networkQuality/packs/snapping/flowSockets; bare dynamic import unresolvable in page eval (build zip in Node); cross-peer hand test recipe; param JSDoc ignored in Scene.svelte (use default params) - peer-feature skill: the deliberately-local do-not-replicate pattern (networkQuality, pack library, local prefs) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- P1: double-clicking an object item always opens the 3D preview popup; the enable3dPreview toggle now gates only the inline Properties preview - P2: cache each pack item resolved thumbnail URL (localStorage), so switching packs no longer re-probes webp/png/screenshot or flashes; dropped on remove - P3: importPackZip descends a single wrapper folder (GitHub Download-ZIP shape, metadata/ + assets/ inside) - P4: single-click the Packs root shows a grid of pack cards; double-click expands the tree; clicking a pack (card or tree row) opens its items - P5: right-click a pack -> Delete (imported) or Hide (built-in, reversible via Settings show-hidden) + Attribution; built-ins are never hard-deleted - P6: right-click empty packs grid -> Import pack (.zip) or Load pack from URL (.zip or a GitHub repo link via codeload; CORS-gated, graceful failure) - e2e: packs (thumb cache + wrapper-zip), packs-explorer (click model + menus + place), model-preview (popup with toggle off) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…menu - 2.1 target refresh rate: applyVRFrameRate reads supportedFrameRates on session start and requests auto(max)/90/120 per the vrTargetHz setting (gated on supported rates - bad values reject a promise threlte cannot catch). noVR Settings select (Max/90/120) + VR panel cycling row. Root cause of the stuck 90Hz: <XR> never received a frameRate. - 2.2 hands<->controllers switch now reaches peers: the !moved && !hasJoints gate ate the switch-back message; shouldSendHands forces a send when the representation flips (last-sent joints.length per hand) - 2.3 cuboid-bone peer hands: handBoneSegments builds ~24 box segments from the 25 wrist-local joints; peerHandStyle setting (hands default | spheres), LOCAL pref, rows in noVR Settings (under Hold to move vertex) + VR panel - 2.4 pinch-HOLD (>=500ms) on the menu hand toggles the radial (hands have no B/Y button); the release-select is swallowed so it cannot click a sector - e2e: vr-hz-pinch (rate picker + fallback, rep-flip decision, bone math, pinch state machine); vr-peer-hands(+net) updated for the style setting - roadmap-9 plan files: pending/opus-b1-save-packs-fixes, pending/b5-network- stress (skipped), backlog flow-stage-2 entries, overview pointer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- .tpscene = the session .zip bundle as a first-class save format:
session.json (objects/flow/annotations/camera) + assets/<hash> (scene
manifest binaries) + NEW packs/ section (imported packs metadata + item
blobs, content-hash restored with item-id remap + re-registered on import)
- exportSessionZip gains include options {assets, packs, flow}; flow OFF
strips nodes/edges; buildSessionPayload exported
- fileHandler: save(tpscene) downloads the bundle honoring the export
settings; load() applies .tpscene via importSessionZip + requestLoadSession
- Sidebar: format row is now [GLTF | Scene | cog]; the cog opens an export
settings modal (include assets/packs/flow + Show JSON toggle) - JSON is
demoted behind the toggle (default off), Scene is the default format
- e2e: tpscene (bundle contents, checkbox matrix, pack restore round-trip,
cog UI); sidebar-reorg updated (Packs now opens the packs grid, not the
old 126 library drawer)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s (B4.1) - NODES.md: the 34-type audit table (verdicts OK/FIX/DOC) + new-node recommendations (clamp/map-range + select land in 4.6; split/join, delay, text, keypress recommended for a user pick) - edge ids now include handles: e-<src>.<h>-<tgt>.<h>. One source wired into both a+b of a node used to collide ids, the peer dedupe dropped edge #2 and the graphs diverged permanently (nodesync loop, never converging) - evalNode cycle guard is PATH-based: the global seen set made a source feeding two inputs of one node evaluate once (second input read undefined and used its fallback) - found by the new suite - distance/proximity accept wired vector3 LITERALS as world points (the coercion matrix allowed it; the runtime returned 0/false) - e2e: flow-audit (distinct a+b ids, 2+2=4 through both edges, 3-4-5 vector3 distance); all flow regressions green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Socket.svelte: shared wrapper around the xyflow Handle that paints each socket with its TYPE color (flowSockets.typeColor - defined in 165, unused until now): source = outputType(node), target = inputType(node, handle) - all 33 handles across 21 node components migrated (mechanical swap); unmigrated/module handles keep the --node-accent fallback via flow.css - an incompatible drag target reads RED (xyflow stamps valid only when isValidFlowConnection passes - pure CSS on connectingto:not(.valid)) - socket type -> color legend in the Flow properties panel (Graph view) - e2e: flow-socket-colors asserts computed handle colors per type + legend; all flow regressions green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- the Flow right panel gains Explorer-style tabs: info = the SELECTED node parameters (editors live here, cards stay compact); settings = graph settings + node name/note (unchanged, default tab) - slider: adjustable min/max (node data, catalog-seeded, runtime CLAMPS so stale values cannot escape the range); card readout shows the raw value - switcher: adjustable items list (add/edit/remove rows in the info tab, fresh-array setNodeData writes) and becomes a REAL number source outputting the selected index (OUTPUT.switcher=number, sourceValueTypes); the legacy cube/pyramid geometry swap keeps working via items[index] with a shape fallback for saved graphs - number: step editor in the info tab (was stored but uneditable) - e2e: flow-adjustable-params (clamp semantics, index output, legacy shape, info editors write + BROADCAST nodedata, card re-renders a grown list); all flow regressions green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CustomNode renders one target Socket per def RANGE param (the runtime already resolved wired inputs via resolveInputs - only the sockets were missing, so custom-node params were unwirable) - pruneCustomNodeEdges(defId): editing a def to remove a param drops edges into the now-gone socket. Runs in applyNodeDef (every peer prunes identically - an applier-side invariant, never a broadcast) AND as a post-pass in applyNodesSnapshot so a stale drift-heal snapshot cannot resurrect dangling edges - customNodes + nodesHandler exposed on the __stores debug hook - e2e: flow-customnode-io (sockets per param, prune on def edit, socket disappears, stale-snapshot resurrection blocked) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- maprange (Logic): remap a from [inMin..inMax] to [outMin..outMax] with an optional clamp - the missing glue between free-range sources (time, distance, counter) and bounded effect params - select (Logic): outputs a when index < 0.5 else b - pairs with the switcher-as-number output and compare/gate booleans (previously only a script node could choose between two wired values) - registered across catalog, component map, typed sockets (number in/out), valueTypes + evalNode; NODES.md rows added - e2e: flow-new-nodes (remap math, clamp on/off, wired switcher index drives select, card + socket counts) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Found while writing the node docs: the typed-socket rules rejected wiring an On Click (event) output into the Object Selector (effect) input - the ONLY sink through which fireObjectClick can act on the scene. Saved graphs worked (edges are not re-validated) but the connection could not be drag-authored. canConnect now allows event->effect explicitly. NODES.md corrected (onclick verdict; script/customnode outputs are effect, not number). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
B1.1: dropping a file into the Explorer Packs view now routes a .zip through the pack importer and rejects non-zip with a toast, instead of importing with a bogus folderId (orphaned/invisible). Scene (derived, read-only) views also reject drops. Library/prefab drops unchanged. B1.2: GLTF save exports the SELECTION (primary selection + multi-select set), not the whole scene. Nothing selected shows a warning toast with an Export-all action. JSON save keeps its whole-scene behavior. exportGltf/selectedRoots extracted; parkAnimatedAtBase preserved in all paths. - e2e: packs-drop (zip imports, non-zip rejected, library drop still works); gltf-export-selection (warning toast on empty selection, 1-mesh export for a single selected object via the download) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- the modal/toast z-tiers were 50/60, but the avatar/peers popover (Users, ~996-998) and Connect (300) live outside the scale, so flowbite modal X buttons were covered. Raise --z-modal to 1100 / --z-toast to 1200 and map flowbite dialog + backdrop onto the modal tier (unlayered CSS beats the hardcoded z-40/z-50 utilities; still below ThemedSelect 9999 so in-modal dropdowns work) - export-settings panel was a fixed child of .app-sidebar, whose backdrop-blur creates a containing block -> it centered on the ~200px sidebar and spilled off the left edge (8-bit theme hid it via a wider sidebar). Rendered at the component root now, on the modal tier, max-w-[92vw] - e2e: modal-layering (settings modal z>=1100; export settings centered + fully on-screen) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- mobile + HUD button (bottom-left, MobileAddButton.svelte) opens the same create/context menu as a right-click; a canvas touch long-press (~500ms) opens it at the touch point. Scene exposes the opener via viewportMenuOpener; openViewportMenuAt is the shared entry (forceEmpty -> always the create menu) - object context menu is now one shared builder (objectMenu.js buildObjectMenuItems): the direct object menu (Controls) and the empty-space menu's submenu (ViewportMenu) expose the SAME actions (they had drifted). The submenu title is a fixed Selected (object names get very long); pingObject moved into ping.js so the shared builder needs no THREE import - object-list window drags/resizes via pointer (was mouse) so touch works; drag/resize handles get touch-action:none - narrow screens: the + and the chat/mic stack lift above the centred Controls pill (media query) so the pill stays one line; the + shows only on touch/narrow. mic button gets an id for the reflow - e2e: mobile-hud (opener, parity set, pointer drag); context-menu-v2 updated for the Selected label + full submenu Note: Scene gains 2 implicit-any touch-event params (this file treats the DOM canvas as any; no JSDoc form is honored) -> svelte-check 502, the documented baseline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…able) A window docked to the LEFT sat at x:0 on the z-drawer tier, but the app-sidebar floats above it (z-hud) — on a narrow screen the docked panel is thin and its topbar dock/close buttons ended up under the sidebar. When the menu is open, a left-docked panel now insets past the sidebar (mirrors the right-side drawer offset); re-applied next frame since the sidebar mounts after the store fires. - e2e: dock-sidebar-inset (menu closed -> flush at 0; menu open -> left clears the sidebar's right edge) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Connect (approved revision): moderate corners (not a full pill); a fixed-size floating bar centred on top for wide screens; it never shrinks. Narrow screens: - Connect becomes a full-width bar stuck to the top edge (the input flexes so the Connect button never runs off-screen); the logo + peers/profile chrome drop to a second row below it, with a clear gap - neither Connect nor the Controls pill shrinks with window width Z-order + menu: - new --z-menu tier (above Connect 300 and toasts 1200): the logo/burger + its sidebar now open ON TOP of Connect and toasts (were behind them) - opening a modal from the menu (Modules/Sessions/Settings) closes the menu so the top-most menu can't cover the modal - the logo menu touch-scrolls on short viewports (no visible scrollbar) Popups: - export-settings opens anchored BELOW-RIGHT of its cog (shows the relation), clamped to the viewport instead of centred - the connected-peers list pins to the viewport on narrow screens so it can't spill off the left edge - e2e: modal-layering (export anchored to the cog, on-screen); sidebar-reorg (menu closes when it opens a modal) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Affects every menu (viewport +, object, Flow, Explorer, Users — one shared ContextMenu): - a too-tall menu/submenu caps to the viewport and scrolls vertically with a visible slim scrollbar (.ctx-scroll); never scrolls horizontally - each submenu decides its own flip in openSubmenu from its row rect (open left if it would cross the right edge, up if more room above) instead of inheriting the root click flip, so deep chains no longer march off-screen - max-height is the available space in the chosen direction, so the box always fits; fixed submenus still escape the root scroll box (no x-bar) - e2e: context-menu-overflow (tall menu caps + scrolls + on-screen; right-edge submenu flips left); context-menu-v2 updated (vertical scroll now allowed, horizontal + transform still forbidden); CLAUDE.md note updated Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hable) The undocked Flow window persisted at 760px wide with no clamp on load or window resize, so on a narrow (mobile) viewport its header Dock/X buttons ran off the right edge (the window is overflow-hidden, so they could not be scrolled to). Now winW/winH clamp to the viewport on init + on resize, and the corner-resize minimum drops to 280 so the window can shrink below a phone width. - e2e: flow-window-mobile (over-wide preset clamps on a 420px viewport, Dock button on-screen, re-clamps on resize) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The palette used draggable + dragstart/dataTransfer, which mobile browsers do not synthesize from touch, so holding a palette item did nothing. A tap/click on a palette item now adds that node at the flow pane centre (it can then be dragged on the canvas, which touch supports). A real mouse drag fires no click event, so desktop drag-to-place is unchanged. - Nodes.addNodeAtCenter reuses addNode + screenToFlowPosition (handles the customnode: prefix like the drop path); passed to the palette as onPick - e2e: flow-palette-touch (tap adds a positioned node); palette regressions green Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CLAUDE.md: status = roadmap #9 shipped (B1-B4/B6 + Opus UI/mobile tail); architecture gains flowSockets/Socket + NODES.md, objectMenu shared builder, custom-node input sockets, .tpscene as the session-bundle format; new gotchas: backdrop-filter is a containing block for fixed children, the z-index tier table (modal 1100 / toast 1200 / menu 1300 above the ad-hoc chrome), and the mobile/touch rules (no right-click or HTML5 DnD -> long-press + tap-add, pointer not mouse for window drag, clamp floating windows). Retired the blanket keep-as-is note for Connect/Controls/Toasts (revised for responsive). - e2e-verify skill: __stores adds customNodes/nodesHandler/objectMenu + viewportMenuOpener; download capture, OS file-drop via dt.items.add, responsive/setViewportSize + dispatched-contextmenu testing, and the git-stash exact-delta trick (machine output double-escapes paths). Baseline 502/77. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Connect: the peer-ID input now shrinks (down to a min-width) so the Connect button stays visible when the row is tight; the pill reserves room for the logo/profile so it never slides under them (input flexes instead). Button no longer disappears at ~640-760px widths. - object-list window: clamp its persisted rect to the viewport on open + on window resize (was reopening partly off a narrow screen with clipped subgroups and no scroll — same bug the Flow window had). - ContextMenu: portal the menu + backdrop to <body> so it escapes a host window's stacking context (the Flow editor's docked/floating window trapped its context menu below other windows); its z-1000 now ranks above windows. - e2e: mobile-ui-fixes (object-list clamp + ctx-menu portal); context-menu + object-list-drag regressions green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Flow + Explorer share the bottom dock when both are docked (only the active one shows). The toolbar icons now highlight the panel that is actually VISIBLE, not just open: - flowVisible/explorerVisible derive from flowGraphClose/explorerClose + dockShared + bottomDockActive - clicking Explorer opens it AND makes it the shown dock occupant (Flow un-highlights); clicking Node editor shows Flow (Explorer hidden but still open, so windowed Flow + docked Explorer can both show); clicking a shown panel again closes it - e2e: flow-explorer-dock (both-docked highlight, switch active, close-on-reclick) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Flow tab strip now shows [Node editor][+] (Explorer removed - it is toolbar- controlled since the dock-highlight change); the + (docked strip + floating header) opens an add-menu: Flow Code (works) + Animation (soon, next batch). - FlowCode.svelte: an editable JSON view of the flow graph (ex-backlog). Seeds from the live graph; Apply parses + REPLACES it (delete removed + snapshot the rest) locally and broadcasts (nodedelete/edgedelete + nodes) so peers converge. A floating window (tabbable) - drag to group with Flow, tear a tab off to detach. - TabStrips: right-click a tab -> Hide tab (closeMember) via the portaled ContextMenu. - e2e: flow-code (+ menu opens the window + seeds the graph JSON); window-tabs / docking / flow regressions green. Animation window (local-only, no blending per the fork) is the next batch and will join the + menu when built. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Submenu placement used a width GUESS to pick left/right, so on a narrow screen a suboption could still run off either edge. Now the submenu MEASURES itself (placeSubmenu action) and positions against the row: prefer right, flip left if it would cross the right edge, and if it still will not fit, clamp it fully into the viewport - even if that covers the parent menu (better than off-screen). Vertically it aligns to the row then clamps; too-tall submenus still cap+scroll. Fixes both the viewport and Flow context menus (shared ContextMenu). - e2e: context-menu-overflow gains a 360px-wide case asserting the submenu clamps on-screen when neither side fits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ContextMenu root now measures + clamps fully into the viewport (place action) instead of guessing flip direction, so a menu opened near the right edge on a narrow screen (e.g. 360px) no longer runs off-screen - ContextMenuItems submenus keep the measure-and-clamp placement; dropped the now-unused flipX/flipY props from both components - Connect bar spans the full width at <=640px (max-width:none on the full bar) instead of inheriting the wide pill reserve-room cap, which had squished it to ~80px at 360px - extend context-menu-overflow e2e: root menu opened near the right edge on a 360px viewport clamps on-screen Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- the create/context + button (MobileAddButton, bottom-left) was gated to touch/<=820px; show it on every width (user request) so a fine-pointer desktop gets it too, alongside right-click - the + now opens its menu anchored to the button (place flips it up off the bottom edge) instead of at screen-centre; it still rays a new object into the middle of the view - viewportMenuOpener gains optional menuX/menuY (default to the ray coords) so the menu position can be decoupled from the ray point - extend mobile-hud e2e: button visible on a wide desktop; menu opens at the button anchor while the ground point still comes from the ray coords Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge the
betaintegration branch intomain(262 commits, no divergence —mainfast-forwards). This lands every roadmap and fix accumulated sinced335d1a.Highlights
Roadmap #9 — release runway
.zipdrop (non-zip rejected) + GLTF selection-only export with warnings.tpscenefirst-class scene format + export-settings cog (JSON demoted)theprototype-docsrepoRoadmaps #5/#6/#7 — VR mesh-edit/face-edit, controller-handedness fixes, world-grab presence, Explorer window chrome (WindowShell), VR peer approve/deny, face polygon-select + multiselect, VR Tools radial + box-select, Explorer packs + 3D preview, articulated peer hands, per-peer network-quality dot.
UI / mobile pass (this session)
Verification
npm run buildgreennpx svelte-checkholds the baseline (502 errors / 77 warnings)Merge strategy: use a merge commit (not squash) so the per-phase commit history lands on
mainintact.