summaryrefslogtreecommitdiffhomepage
path: root/src/features/workspaces
AgeCommit message (Collapse)Author
11 daysfeat(ui): raised '+ Add view' sidebar button (mirror of the view wells) with ↵Adam Malczewski
gap-matched bottom spacing; dvh app-shell height for iOS toolbars
11 daysfeat(home): navbar header actions — raised primary 'New workspace' button, ↵Adam Malczewski
secondary actions as text links, Phosphor icons when compact
11 daysfeat(ui): raised (popped-out) home tiles, the inverse of the inset well; ↵Adam Malczewski
shared navbar look on the home header
2026-09-20feat(workspaces): archive option + archived-workspaces viewAdam Malczewski
Consume the backend's archive contract (feature/archive-workspaces): - adapter/http: archive(id)/unarchive(id) (PUT/DELETE /workspaces/:id/archive); list(archived?) passes the ?archived= query - logic/view-model: pure applyArchived(list, id, archived) - store.svelte: setArchived(id, archived) (optimistic + revert-on-error + reconciling refresh); archivedList + refreshArchived() for the archived view - ui/TileActionBar: new Archive action (the right-click→select action bar) - ui/HomeHeader: new Archived button opens the archived view - ui/ArchivedWorkspaces (new): modal listing archived workspaces with Unarchive + View (navigates to /<id>) - ui/WorkspacesHome: wire the Archive action + the Archived modal - .dispatch/wire.reference.md + backend-handoff.md: record the contract delta
2026-09-07feat(workspaces): sync the home tile layout per device orientationAdam Malczewski
The Win8-Start home grid's arrangement now syncs from the backend's home-tile-layout plugin: ONE shared document per device orientation (wide = landscape/desktop, tall = portrait/mobile) — every landscape device renders the same layout, every portrait device the same other one. localStorage persistence is retired (no migration — devices start from the shared document; the first arrangement creates it). - adapter/tile-layout-http.ts: the injected HTTP edge (GET/PUT/DELETE /home/tile-layout/:mode), the features/layout adapter's pattern - tile-layout-store: server-backed, keyed by geometry mode — boot + orientation-change probes (stale in-flight responses never land), locally-applied edits + debounced full-document PUT with rollback to the last server document, WS push filtered to the active orientation, quiet ephemeral degrade when the backend is unreachable (no error UI), sync gated while a probe is in flight - WorkspacesHome locks the store's orientation to the resolved grid geometry (the landscape/portrait split IS the device-type split) - app store: the tile store lives beside the layout store; the WS dispatch feeds 'tile-layout' broadcasts into it; 'tiles' prop from App.svelte (the page entry owns the httpBase) - ws adapter: the 'tile-layout' server message (parse guard) + socket handler; wire conformance case
2026-09-07feat(workspaces): tile stat line shows months instead of a short dateAdam Malczewski
The relative last-activity time switched to an MM/DD date once past a week. Now it stays relative: days continue to a month (30 days), then months ("10d", "29d", "1mo", "2mo", ...). A month ≈ 30 days; the label never becomes an absolute date. Verification: svelte-check 0 errors/0 warnings; vitest 1694/1694; biome clean on touched files; build ok.
2026-09-07fix(workspaces): mobile (tall) grid can't scroll — root height collapse + ↵Adam Malczewski
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.
2026-09-07feat(workspaces): drop the conversation-count icon from the tile stat lineAdam Malczewski
The ◱ glyph next to the count was redundant decoration. The stat line is now just "3 conv." on the left and the relative last-activity time on the right. Verification: svelte-check 0 errors/0 warnings; vitest 1693/1693; build ok.
2026-09-07feat(workspaces): content-driven canvas while dragging — scrollbar ↵Adam Malczewski
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.
2026-09-07feat(workspaces): reserve the append canvas for the whole drag — no ↵Adam Malczewski
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.
2026-09-07feat(workspaces): bound the drag append area — 3 columns past the content edgeAdam Malczewski
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.
2026-09-07feat(workspaces): FLIP reflow eases OUT — fast start, decelerating arrivalAdam Malczewski
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.
2026-09-07fix(workspaces): tile-grid drag bugs — stuck drop, grab-offset follow, ↵Adam Malczewski
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.
2026-09-07feat(workspaces): pinned tiles with displacement reflow + FLIP drag previewAdam Malczewski
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.
2026-09-07feat(workspaces): rework tile-grid visuals — Start-screen polishAdam Malczewski
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.
2026-09-07feat(workspaces): Win8 Start-style tile-grid home pageAdam Malczewski
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.
2026-09-05feat(layout): plan 04 — ABI layout.applyOps + reset affordanceAdam Malczewski
2026-09-03feat(plugin-system): plan 07 frontend plugin manager uiAdam Malczewski
2026-06-28fix(predev): resolve frontend merge conflictsAdam Malczewski
2026-06-28Merge branch 'feature/workspace-star' into predevAdam Malczewski
# Conflicts: # backend-handoff.md # src/features/workspaces/ui/WorkspaceCard.test.ts
2026-06-28feat(workspace-active-indicator): show loading dots on workspace cards with ↵Adam Malczewski
active chats
2026-06-28feat(workspaces): star toggle for concurrency priorityAdam Malczewski
Backend (feature/workspace-star) shipped Workspace.starred: boolean (additive to [email protected], no version bump) + PUT/DELETE /workspaces/:id/star endpoints (no body; create-on-miss; return the updated Workspace). A starred workspace's agents jump ahead of non-starred ones in the concurrency limiter queue (oldest-agent-first within each group); takes effect immediately for already-queued agents. FE consumed: - adapter/http.ts: star(id)/unstar(id) -> WorkspaceResult<Workspace> (PUT/DELETE /workspaces/:id/star, no body). - logic/view-model.ts: pure sortWorkspaces (starred-first, then lastActivityAt desc, stable) + pure applyStarred (the optimistic apply/revert transform). - store.svelte.ts: setStarred(id, starred) — optimistic flip with error revert; the list is now a $derived sorted view (starred bubble to top reactively); no full refresh on success (avoids flicker). - ui/WorkspaceCard.svelte: star toggle button (filled gold when starred, outline when not; spinner in flight; aria-pressed/aria-label; tooltip notes concurrency priority). - Re-mirrored .dispatch/wire.reference.md (starred + delta note). - GLOSSARY.md: 'starred' term. Tests (+27): http star/unstar (5), view-model sort+applyStarred (12), store optimistic+revert+re-sort (6), WorkspaceCard star button (4). Verification: typecheck 0/0, 1045 tests green, biome clean, build OK. backend-handoff.md updated (workspace-star slice, no open backend asks).
2026-06-27feat(sidebar-tabs): add dashboard home button + same-tab workspace openAdam Malczewski
2026-06-26style: switch from tabs to 2-space indentation (incl. svelte)Adam Malczewski
2026-06-25Merge branch 'feature/ssh-support' into devAdam Malczewski
# Conflicts: # src/features/workspaces/ui/WorkspaceCard.svelte # src/features/workspaces/ui/WorkspaceCard.test.ts # src/features/workspaces/ui/WorkspacesHome.svelte
2026-06-25feat(computer): SSH computer selection + status + workspace default (handoff #2)Adam Malczewski
Mirrors the cwd/workspace UI for the SSH-computer feature: - New feature library src/features/computer/: - logic/view-model.ts (pure): viewComputer/viewComputerStatus/viewTestResult/ summarizeComputers/formatHost/knownHostLabel + state->badge for the 4 ComputerStatusResponse states + SaveComputer/LoadComputerStatus/ TestComputer/LoadComputers ports. 20 view-model tests. - ui/ComputerField.svelte: per-conversation selector (dropdown + connection-status badge + Test-connection, polling the selected alias). - ui/ComputerSelect.svelte: reusable Local/computers dropdown, shared with the workspace default-computer control. - AppStore: computerId state + refreshComputer (at every focus site, parallel to refreshCwd) + setComputer (PUT /conversations/:id/computer, null=clear) + global computers catalog (GET /computers on boot, like models) + computerStatus(alias) + testComputer(alias). chat.send UNCHANGED (resolved server-side like cwd). - App.svelte: ComputerField in the Model sidebar view next to CwdField; adapted ports wrap the store. - workspaces: setDefaultComputer on WorkspaceHttp+WorkspaceStore (PUT /workspaces/:id/default-computer); default-computer selector in WorkspaceCard (reuses ComputerSelect); router passes store.computers through. - Re-mirrored .dispatch/transport-contract.reference.md (Computers section + ChatRequest.computerId); updated .dispatch/wire.reference.md (Computer/ ComputerEntry/defaultComputerId + the provider-retry divergence note from handoff #1); GLOSSARY + backend-handoff.md (handoff #2, §2e). Transparency invariant: the computer is USER-facing only (a tool-execution target, never part of the model prompt); the agent never sees it. Verify: 795/795 tests green; biome clean; vite build succeeds; 0 typecheck errors from the computer feature. (11 pre-existing svelte-check errors remain from the open §2d provider-retry divergence — backend feature/ssh-support still lacks TurnProviderRetryEvent; not from this feature.)
2026-06-25feat(workspaces): open workspace in a new browser tabAdam Malczewski
The 'Open' button on a workspace card did client-side navigation (onNavigate -> pushState), opening the workspace in the SAME tab. Replace the JS click handler with a native anchor using target="_blank" (+ rel=noopener noreferrer) so clicking Open opens the workspace in a NEW browser tab. The new tab does a full load and routes to /<id> via the existing parsePath -> App wiring. The card no longer needs the onNavigate prop (its only navigation was the Open button), so drop it from WorkspaceCard + WorkspacesHome and update the card tests to assert the new-tab link attributes instead of the onNavigate call.
2026-06-25feat: workspaces shell + cwd-lsp rename + mcp/settings/system-prompt ↵Adam Malczewski
features + app wiring - workspaces: URL-driven conversation grouping (home listing at /, routing, store, http adapter, WorkspaceCard) wired into the App.svelte shell - rename features/workspace -> features/cwd-lsp (the cwd/lsp status feature) - new features: mcp (status view), settings (chat-limit field), system-prompt (prompt builder), all rendered via the generic surface host - chat: store + ChatView updates - tabs: tabs-store updates - app wiring: ErrorModal (full-screen error surface), app/App.svelte + store.svelte This commit makes HEAD typecheck clean for the first time: the prior HEAD (dfe1711) imported features/settings from app/App.svelte but never committed the feature, so only the full working tree was green.