Repository navigation
[feat] drag to place from the Explorer (roadmap 39 P1-P6) - #347
Merged
Merged
Conversation
- placementDims.js (leaf): one Dims shape {size [w,h,d], box, tris, bytes, animated, known,
source}; read from a pack row / manifest item (a NUMERIC `size` is the old manifest's byte
count), else what this device measured on first load (ONE idb record `pack:dims-v1`, writes
debounced), else the 1 m "size unknown" box. Labels W x D x H + cost.
- packs.js normalizes `dims` onto every default-pack item and imported manifest item.
- packRefs.js: loadPackTemplate measures + remembers dims for a row that had none (also for an
animated file, before it refuses to reference it — AnimatedRefError), keeps the FINISHED
templates for a sync peek (peekPackTemplate = "decoded in memory"), and reads/writes the
pack cache in fetchWithProgress and the LOD file fetch.
- packCache.js (leaf): ONE CacheStorage cache `tp-pack-files-v1` with a size index store, so
downloaded items can be listed, sized and deleted; degrades to plain fetch without caches.
- explorer.js: a new library model records `dims` from the same parse its thumbnail makes.
- static/library/default/default.json: the bundled starter rows carry dims (copied from the
packs repo's measured rows; identical files).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ep spawn (39 P1 + P2) - placeDrag.js: a POINTER gesture (a native drag delivers no keydown/wheel, so R / the wheel / Esc could not work on it). Explorer cancels a TRUSTED dragstart on a placeable card (pack object, library model, prefab) and hands over; a touch long-press does the same with the Explorer collapsed. Over any non-viewport element the payload is re-dispatched as synthetic dragenter/over/leave/drop (+ dragend on the source), flagged x-tp-place-bridge, so every existing HTML5 target keeps working and App's window drop ignores it. Raycast onto surfaces (ground fallback), the box RESTS on the surface along its normal, grid snap on the surface's two axes (Shift = free), R / Shift+R / wheel = 15° steps, align-to-surface via snapTargets.alignNormal, Alt = the camera focus point, a row for a multi-selection, amber over 50% inside another object, red (no drop) off the world. - placeGhost.js: scene-root, never saved/sent/picked. BOX tier (wire box + fill + footprint) first; the MODEL tier (the template's own geometry, one translucent material, no shadows) swaps in on a later frame when decoded + allowed by the preview setting (placementPrefs.js: full / within budget (default) / box, 50k tris, 15k on phones and headsets). Pending boxes hold a drop's spot while an import path parses. - explorerDrop.placeFromGhost: a STATIC pack item spawns as a pack STUB through addImported (one create entry, the stub element on the wire, Placeholder Style until it fills, peers refill with derived uuids); animated / behaviour / over-budget items, library models and prefabs take their import paths at the ghost pose (importFile gains `quaternion`). Several items share one history batch. Hovering the viewport prefetches (template parse / cache / library or prefab decode). - PlaceDragOverlay.svelte (kit tokens + Icon): the chip, W x D x H / size unknown, tris and size, the turn and the keys. - the two older pack fetches go through the pack cache; importGate.wouldAskFor; debug rows. - suite place-drag (33, two peers, real mouse, the CDN path served from static/library). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…, touch drag (39 P3 + P5) - Settings ▸ Scene ▸ Performance ▸ Placement preview (PlacementSettings.svelte, its own file, keywords + settingsIndex rows): Full model / Within budget (default) / Box only, the triangle budget (50k desktop, 15k phone/headset) and Show dimensions. LOCAL, safeStorage. - Explorer ▸ Packs: a Downloaded badge (bottom right, size in its title) on every pack item this device holds, a "Downloaded" axis in the filter menu inside the Packs, the item menu's Delete -> "Delete cache (size)" (disabled when nothing is held; the item stays in the pack), and the pack row's "Delete downloads (size)". - Storage: a "Pack downloads" category (one row per pack, CacheStorage — reclaimed through packCache) + "Clear all pack downloads"; the measured-sizes key is claimed there too. - placing a pack item re-downloads it when its cache was deleted (a file still parsed in memory). - touch: the place drag blocks the browser's pan with a non-passive touchmove (touch-action is decided at touchstart, before the long-press) — without it every finger drag ended in a pointercancel (measured). - suites: place-drag 39 (+ release over chrome places nothing — proven red with App's bridge guard removed, 0 -> 1 —, a library model's import-time size, and the HTML5 bridge still moving a card into a folder), place-drag-cache 33 (downloads, filter, Delete cache, Storage, the preview tiers, the Settings rows, a long-press drag at 390 x 844). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…me (39 P2/P3) - builds a 200340-tri sphere in the page, serves it as a pack item, warms it into the pack cache + memory, then samples rAF intervals 4 s idle vs 4 s mid-drag (pointer moving every 16 ms): 16.67 vs 16.67 ms, no long tasks; the ghost is the box over the 50k budget. - run under e2e-slot --exclusive; writes after-39/39-place/perf-drag.json. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s (39 P1/P4) - the chip under a finger says 'Lift to place · back to the Explorer to cancel' instead of the keyboard hints. - PACKS.md: size [w,h,d] / box / tris / bytes / animated on item rows, what writes them (kit-build dims) and the legacy numeric size = bytes reading. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…uites (39 P6)
- Alt while dropping: the orbit focus point is usually in the air, so it is projected down
onto the first surface below it (explorerDrop.surfaceBelow; ground plane fallback).
- suite place-drag-extras: a multi-selection drags as one gesture with a ghost per item in a
row and drops as ONE undo step (one Ctrl+Z removes both); Alt = focus point on the ground;
aiming above the horizon is red ('No place to put it here') and the release places nothing.
- vitest placementDims (13): row dims incl. the legacy numeric size = bytes, the ghost tier
rule, an item's download urls.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (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.
Roadmap 39 (lane 39-place): drag to place from the Explorer, P1-P6 (VR radial placement deferred to a follow-up).
What it does
(resting ON the surface under the pointer, grid snap unless Shift) and how big it is (W × D × H in metres +
tris + download size). R / Shift+R / the wheel turn it 15°; Alt = the camera's focus point (dropped
onto the surface below it);
snapTargets.alignNormaltilts it onto the surface; a multi-selection drops as a row.spawns as a pack stub (30c reference) drawn in the Placeholder Style until it loads; every peer refills it.
Animated items, library models and prefabs take their import paths at the ghost pose.
Settings ▸ Scene ▸ Performance ▸ Placement preview (Full / Within budget (default, 50k; 15k phone/Quest) / Box
only); otherwise the box. Hovering the viewport prefetches. Amber = mostly inside another object (still places),
red = no surface (release does nothing).
packCache.js) — Downloaded badge + size on pack cards, aDownloaded filter in the Packs, item menu Delete cache (size) (the item stays in the pack), pack row
Delete downloads, Storage Pack downloads + Clear all pack downloads.
size[w,h,d] /box/tris/bytes/animated(packs PR); missing → measured on firstload and kept in idb; library models record their dims at import. PACKS.md documented.
Design note: a pointer drag with an HTML5 bridge
A native drag delivers no keydown/wheel, so R/wheel/Esc cannot work on it. A trusted
dragstarton a placeable cardis cancelled and
placeDrag.jsruns the gesture; over any non-viewport element it re-dispatches the card'sapplication/x-explorer-itempayload as synthetic dragenter/over/leave/drop (+ dragend on the source), flaggedapplication/x-tp-place-bridge, which App's window drop ignores. Every existing HTML5 target keeps working(Explorer folders/packs/bin, Inspector texture slot, LOD panel, pickers). Synthetic dragstarts (suites) keep the
native path.
Proof
place-drag39 (two peers, real mouse): row dims, model swap, rotation, Esc / back-to-Explorer = no spawn + noundo, one-undo drop at the exact pose, stub replicated to B and refilled there, undo reaches B, release over chrome
places nothing (counterfactual: 0 → 1 with the App guard removed), bridge moves a card into a folder.
place-drag-cache33: downloads, badge, filter, Delete cache (bytes gone, item stays), Storage clear-all, previewtiers, the Settings rows, touch long-press at 390×844 (counterfactual: without the touchmove block every
finger drag ended in pointercancel).
place-drag-extras15: multi-select row as one undo, Alt focus on the ground, red over the sky.place-drag-perf(exclusive): a cached 200,340-tri item with the default budget → box ghost, frame time16.67 ms dragging vs 16.67 ms idle, no long tasks (headless rAF is vsync-capped).
Shots:
~/.code/lanes-30/after-39/39-place/NN-*-{before,after}.png; docs fragment beside them.🤖 Generated with Claude Code