| Age | Commit message (Collapse) | Author |
|
shared navbar look on the home header
|
|
touch capture
Two blockers for scrolling the tall grid on a phone:
1. HomeTileGrid's root was `flex-1` inside a BLOCK parent (the workspace
region), so flex-1 did nothing and the root sized to its content; the
scroller's `h-full` collapsed to auto and overflow-y-auto never engaged —
extra tiles were clipped by the shell's `overflow-hidden` with no way to
scroll. The root is now `h-full`, so the scroller gets a definite height
and tall mode scrolls (wide mode was unaffected: horizontal overflow
scrolls regardless of container height).
2. `setPointerCapture` was called for TOUCH pointers at pointerdown, which
can suppress native panning in WebKit. Touch now skips explicit capture:
the browser's implicit capture already retargets the press, and after the
long-press lift the moved events bubble up to the grid container where the
handlers live — scroll gestures keep working and touch drags are intact.
Verification: svelte-check 0 errors/0 warnings; vitest 1694/1694; build ok.
Needs a human on a real phone: swipe the tall grid when tiles overflow —
it must scroll; long-press-and-drag a tile must still reorder.
|
|
grows/shrinks with the gesture
Revert the fixed append-canvas reservation (9ede1f0): pre-reserving the 3
spaces at drag start made the view jump right and created far too much free
canvas. The grid is back to hugging its content (width: max-content / auto),
so the preview extends the canvas as the tile reaches toward the edge and it
shrinks back when the gesture ends — the scrollbar moves with the drag, as
intended. The drag-target clamp (dragAppendLimit, +3 past the committed
edge) still bounds how far the preview can extend, so the "endless growth"
problem stays solved without reserving anything up front.
Dots restore the one-pitch padding extension for the append slot; the
scroll-measure effect still re-runs on drag start/end (the canvas shrinks
back on cancel). Test reverted to the content-driven assertions.
Verification: svelte-check 0 errors/0 warnings; vitest 1693/1693; biome
clean on touched files; build ok.
|
|
scrollbar change mid-drag
Previously the grid kept `width: max-content` during a drag, so the canvas
grew/shrunk as the preview placed the tile at each hovered cell — the
scrollbar resized mid-drag, making placement harder. Now the moment a drag
starts the grid's growth axis becomes a FIXED reservation: the append limit
(committed content edge + 3, span-aware) computed at drag start, so the 3
spaces "pop in" immediately, the scroller size is constant for the entire
gesture, and the unused reservation disappears when the drag ends (the
canvas returns to max-content / auto). The dragged tile's span room is
included so its body always stays inside the reserved canvas.
Also: the scroll-measure effect now tracks `dragging`, so canPan and the
scroll indicator re-measure when the reservation appears and disappears.
Test: the drag test now asserts the grid's style is a fixed px width while
dragging (never max-content) and back to max-content after the drop. (This
caught a missing `px` unit that would have silently voided the reservation.)
Update the handoff bullet to describe the reservation.
Verification: svelte-check 0 errors/0 warnings; vitest 1693/1693; biome
clean on touched files; build ok.
|
|
Dragging a tile far right grew the wide canvas endlessly (the target cell
resolved from the pointer with no upper bound), making precise placement
impossible. Add a pure `dragAppendLimit(placements, geometry, extra = 3)`:
the drag target may aim at most 3 columns (wide) / rows (tall) past the
current span-aware content edge. The bound is measured on the COMMITTED
layout — never the live preview — so it can't ratchet mid-drag; it steps
forward only when a drop commits new content. cellAt applies it on the
growth axis (wide: col, tall: row); the fixed cross-axis clamps are
unchanged. Exported via the unit index.
Verification: svelte-check 0 errors/0 warnings; vitest 1693/1693 (4 new
dragAppendLimit tests); biome clean on touched files; build ok.
|
|
The tile-organization animation used power2.inOut (eases INTO the motion
too). Switch to power3.out so the sweep starts fast and settles slowly into
the destination — the Windows-8-style snap-to-place feel.
Verification: svelte-check 0 errors/0 warnings; vitest 1689/1689; build ok.
|
|
empty-cell hover
Three drag-interaction bugs on the Win8 tile home, one shared root cause
(capture loss from DOM reordering) plus two wiring defects:
- DROP NEVER COMMITS (stuck tile): displacePlace returned the displacement
preview SORTED BY STREAM ORDER, so crossing stream positions moved the
dragged tile's DOM node in the keyed {#each} and the browser dropped its
pointer capture — pointerup never landed, phase stayed "dragging", the
follow transform never cleared. displacePlace now preserves the INPUT
render order (coordinates change only); placeAtCell re-sorts to stream
order itself, so the commit's order-renumbering semantics are unchanged.
- FOLLOW TRANSFORM OFFSET: the follow translate was raw pointer displacement,
so a grab away from the tile's top-left made the tile jump (its ORIGIN rode
the pointer). gridPointerDown records the grab offset (pointer minus the
tile's rect) and followStyle cancels the tile's true grid anchor (targetCell
origin), so the GRAB POINT tracks the pointer.
- CAN'T HOVER EMPTY CELLS: pointer handling moved from per-tile elements to
the GRID CONTAINER, which captures the pointer and is never re-parented,
never pointer-events:none, never unmounted — move/up stream to it over
empty cells and off-canvas, and capture can never be lost to a DOM move.
Tests updated: the four order-sensitive displacePlace assertions now expect
input render order (all coordinates unchanged — the plan's stream examples
still hold); HomeTileGrid drag tests still pass via event bubbling.
Verification: svelte-check 0 errors/0 warnings; vitest 1689/1689; biome clean
on touched files; vite build succeeds. Pointer capture is browser-only —
human check at the page: (1) drag a tile across columns, release — it must
drop, not stay stuck; (2) grab a tile by its corner/center — the grabbed
point must stay under the cursor; (3) hover/drag over empty cells past the
last tile — the ghost + drop must keep tracking.
|
|
Tiles can now be dropped onto specific cells; empty cells are real
estate that persists (notes/pinned-tiles-plan.md).
Data (v2): each tile carries an optional pinned anchor in a UNIVERSAL
4-row frame (tall folds its 2 columns onto frame column 1; a wide col>1
pin reads as unpinned in tall). v1 documents migrate in memory to
all-unpinned (render-identical) and write back as v2 on the next change.
sanitizeLayout drops garbage anchors.
Packing: pinned-first (first-wins on overlap, one-ply), then unpinned
tiles first-fit into the REMAINING cells — holes between pins count and
never compact. displacePlace: the user-specified semantics — the dragged
tile leaves a gap at its origin (nothing slides backward), anchors at
the drop cell, and the contiguous filled run pushes FORWARD until the
first empty cell absorbs it (X_X + Y@1 → YXX; _XZ_ + Y@2 → _YXZ).
placeAtCell commits the displacement and pins every tile; pinAtFirstEmpty
pins the create-flow tile at the first truly-empty slot;
sortByRecentActivity now also clears pins (an explicit arrangement
reset — the menu item says so).
Drag UX: pointer → cell hit-math (empty cells are drop targets); a
faint dot marks every cell center during a drag (CSS radial-gradient
overlay, one extra pitch of end padding for the append slot); the
dragged tile element itself follows the pointer (lifted via transform,
pointer-events none); a dashed tone-colored ghost marks the landing
footprint. The preview recomputes from the COMMITTED layout on every
hover-cell change (as if the user moved straight from the original to
there) while GSAP Flip animates from wherever the tiles currently are
(re-entrant — mid-flight positions captured). Escape/pointercancel
discards the preview and flies the tile home; a drop settles it onto
its new cell.
Tests: displacement (both user examples + internal moves + spans + tall
stream), pinned pack (anchors, gaps kept, first-wins, universal-frame
mapping, deleted-leaves-holes), v1→v2 migration + anchor sanitization,
store placeAtCell/no-op write guard, Escape-cancels-drag component test.
1689 green.
|
|
The home canvas now reads as a real Windows 8 Start screen instead of a
cramped, edge-hugging list:
- Geometry: the wide unit is DERIVED from the canvas height (4 rows fill
it: unit=height/5 clamped to 112-184px, gap=unit/9, margin=unit/3 clamped
24-64px) — tiles are no longer tiny, and the tile field always carries a
proportional outer margin (never compressed against the left edge). Tall
mode keeps fluid columns with a taller 148px row + 12px gap.
- Tiles take a per-workspace SATURATED tone (new pure tileTone(id): FNV-1a
hash → one of the six theme-semantic colors) with tone-content text —
the Win8 colored-tile identity. Starred is now a ★ badge (a tile keeps
its own color), not a gray-tint override.
- The tile face is the Win8 layout: small uppercase slug caption at the top,
a bold span-aware title (lg → 3xl across 1×1 → 4×2), and a single-line
stat row pinned to the bottom (count compacts to 'conv.' on 1×1 tiles,
truncation-safe so narrow tiles never clip).
- Selection is a high-contrast offset ring (visible on any tile color);
tiles lift on hover; the pan arrow is a solid elevated circle.
- Header gains a 'WORKSPACES' kicker; empty/loading states are centered
and styled for the Start-screen canvas.
Persisted data (order + spans only) and all interaction semantics
(drag/FLIP, long-press, action bar, resize picker, create popover) are
untouched. Tests: geometry derivation/clamping, tileTone determinism,
wide-vs-compact count label — 1664 green.
|
|
Rebuild the workspace home as a Windows 8 Start-screen-style tile grid,
replacing the max-w-3xl vertical card list.
Pure core (zero DOM/Svelte/fetch, fully unit-tested):
- logic/tile-layout.ts — geometryFor(width, height) resolves WIDE (4 fixed
~120px rows, column-wise flow, 4x2 span cap) or TALL (2 fixed columns,
row-wise flow, 2x2 cap); pack() is the ONLY order->coordinates transform
(greedy first-fit in fill order, hole-free, render-clamped per geometry);
firstEmptySlot/insertAtFirstEmptySlot/mergeLayout/resizeTile/moveTile/
sortByRecentActivity/sanitizeLayout. Persisted data is
orientation-independent {version:1, tiles:[{id,colSpan,rowSpan,order}]} —
order + spans only, never positions; the same data renders in either
geometry (spans clamp for the render, persisted values untouched).
- logic/drag-controller.ts — pure pointer/press state machine: mouse
threshold-drag + tap-open; touch long-press lifts + selects, pre-lift
movement cancels so vertical scrolling never fights the gesture.
Shell:
- tile-layout-store.svelte.ts — thin reactive wrapper persisting through
the injected createLocalStore adapter (dispatch.workspaces.tiles), ops:
sync/resize/move/placeFirstEmpty/sortByRecentActivity.
- ui/HomeTileGrid.svelte — CSS-grid tile canvas (wide: horizontal scroll +
snap + wheel pan + pan arrow + scroll indicator; tall: native vertical
scroll), FLIP-animated reflow, drag ghost, transient resize preview.
- ui/HomeHeader.svelte — fixed chrome: Dispatch display title, New
workspace popover (same slug validation; creates via create-on-miss and
does NOT navigate — the new tile lands in the first empty slot in fill
order and enters rename mode), global Plugins button, sort-by-recent
overflow; compact icon buttons + bottom sheet on tall screens.
- ui/WorkspaceTile.svelte — tile face (title, /slug, conversation count,
relative last-activity, live active dots, starred tint); inline rename
with the old card's semantics.
- ui/TileActionBar.svelte — slides up on select (right-click / long-press):
Open, Rename, Resize, Star/Unstar, Details, Plugins, Delete (old confirm
+ store call preserved).
- ui/ResizePicker.svelte — Word-style insert-table hover grid (wide 4x2 /
tall 2x2) with arrow/Enter/Escape support and ghost preview on commit.
- ui/WorkspaceDetails.svelte — flyout holding the old card's default-cwd
input + default-computer select (same store calls + error states).
- WorkspacesHome.svelte orchestrates; App.svelte untouched (same props).
- WorkspaceCard.svelte is removed (replaced by the tile system).
Manual tile order rules; starred workspaces get a tint (star is no longer
a sort key); workspaces unknown to the layout append at the end.
vitest: jsdom gains PointerEvent (MouseEvent-based) + Element.animate
stubs — the pointer logic is the outermost edge for the drag tests.
Verification: svelte-check 0/0, vitest 1655 passed (102 files, twice),
biome clean, vite build OK.
|