Commit Graph

8 Commits

Author SHA1 Message Date
cd897d6893 feat(mobile): single-pane stack + long-press tab menu + swipe-to-close
- Workspace: on mobile renders only panes[activePaneIdx] (rest skipped).
  When panes.length > 1, adds a horizontal pane-navigator strip built
  from SwipeablePaneTab above the active pane. URL state ?pane=<paneId>
  written by switchActivePane (user-initiated only) and read on URL
  change (back-button + deep-link). Bare URL resets activePaneIdx to 0.
- useLongPress: 500ms touchstart timer; on fire, dispatches a synthetic
  contextmenu event on target.closest('[data-tab-id]') so the existing
  Radix ContextMenuTrigger opens at the touch coordinates. Suppresses
  the synthetic click that follows touchend. Cancels on touchmove /
  touchend / touchcancel.
- ChatTabBar: each tab gets data-tab-id, touch handlers wired to
  useLongPress, and WebkitTouchCallout: 'none' to disable iOS Safari's
  text-selection callout.
- SwipeablePaneTab: tracks horizontal drag; bails if vertical delta
  exceeds 30px (so vertical scroll still works); past 60px on release
  fires onClose (removePane), else snaps back. Opacity fades 1->0.4
  approaching the threshold. Hand-rolled per spec.
- Pane drag-and-drop disabled on mobile (HTML5 drag is broken on touch
  anyway; mobile uses the navigator + swipe-to-close instead).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 05:55:05 +00:00
9436a81b5f refactor: split FileBrowserPane / Workspace / runAssistantTurn
- FileBrowserPane.tsx: deleted (unreferenced post-v1.4 PaneTab.tsx removal;
  the legacy file_browser pane kind isn't part of the active WorkspacePane
  taxonomy).
- Workspace.tsx (524 -> 172 lines): extracted useWorkspacePanes(sessionId)
  and useSessionChats(sessionId) hooks. Workspace is layout-only composition
  now. localStorage key + WS frame handling + drag semantics unchanged.
- inference.ts runAssistantTurn (~265 -> 48 lines): bundled args into
  TurnArgs interface, extracted executeStreamPhase / executeToolPhase /
  finalizeCompletion / handleAbortOrError. All WS publish ordering preserved
  byte-for-byte (mentally traced for tool / non-tool / abort / error /
  depth-exceeded paths). flushPromise chain + setImmediate + signal
  propagation unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 04:35:20 +00:00
59fe6f0522 v1.4-fork-header: fork from message + delete message + header polish + housekeeping
- Fork: POST /api/chats/:id/fork creates a new chat in the same session,
  copies messages up to target (status=complete) with row-offset
  clock_timestamp() for stable ordering. Client emits open_chat_in_active_pane
  event; Workspace opens it in the active pane. No maybeAutoNameChat on forks.
- Delete: DELETE /api/chats/:id/messages/:message_id with 409 if the chat is
  currently streaming. Cascading-forward delete (created_at >= target).
  MessageBubble Trash button + confirm Dialog.
- Header: Projects -> Project -> Session breadcrumb, model badge pill,
  inline session rename, active file path via new useActivePane() hook.
  Server now publishes session_renamed on PATCH /api/sessions/:id;
  client-side dup emit removed from Session.tsx.
- Housekeeping: NOW() -> clock_timestamp() in schema.sql defaults, dead
  PaneTab.tsx and panes/PaneShell.tsx removed, session_panes backfill
  INSERT removed (CREATE TABLE retained), Tailnet trust comment near
  app.listen().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 04:12:01 +00:00
e09c67d65c tab-close + chat archive/delete + landing-card buttons + 1000px content cap
Feature 1 — Tab close menu (pure local pane state, no API):
- ChatTabBar context menu: Rename / sep / Close / Close others / Close to right / Close all
- Workspace bulk-tab primitives: closeOtherTabs, closeTabsToRight, closeAllTabs (manipulate panes[].chatIds, no fetch)
- Drop in-bar Delete; landing card's name-typed Delete is the canonical destructive path

Feature 2 — Chat archive + delete:
- chats.status vocabulary aligned with projects ('open' | 'archived'); DROP old inline CHECK, UPDATE 'closed' → 'archived', ADD new named chats_status_chk
- POST /api/chats/:id/archive (204) + POST /api/chats/:id/unarchive (200) + GET /api/sessions/:id/chats?status=archived; DELETE publishes chat_deleted; PATCH simplified to name-only
- 3 new WS frames: chat_archived, chat_unarchived, chat_deleted (renamed from chat_closed)
- Same dedup discipline: server-only publish, no local sessionEvents.emit in client
- SessionLandingPage: right-click ContextMenu (Open / Rename / Archive / sep / Delete-destructive), inline rename, archive confirm dialog, delete dialog with name-typed Input gated until typed text === chat.name, Archived chats collapsible section with Restore
- Card-level Archive + Delete icon buttons reusing the same dialog state setters; stopPropagation on both so card click still opens the chat; archived cards keep only Restore

UX — chat content width cap:
- ChatPane content (MessageList, queue chips, stop button, ChatInput) wrapped in inner max-w-[1000px] mx-auto w-full so messages center; outer border-t / scroll containers stay full-width so pane chrome and backgrounds remain edge-to-edge
- No new deps, no media queries (narrow viewports collapse to width naturally)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 03:21:26 +00:00
051f3b96ae batch4.1-5.1: dedup audit, archive 400 fix, sidebar Delete, landing-page enrichment, auto-name tool-call fix
- Fastify global empty-JSON-body parser fixes archive/unarchive/stop 400s
- Removed redundant local sessionEvents.emit at all 5+2 sites with server-side WS publishers; added dedupe guards in useSidebar/Workspace/Project handlers
- Sidebar session right-click adds Delete (destructive) with confirm Dialog
- Session.tsx navigates away on session_deleted/session_archived for the active session
- SessionLandingPage chat rows show message_count, effective_context_tokens, last_message_preview via LATERAL joins on GET /api/sessions/:id/chats
- Workspace.tsx pane drag-to-reorder using native HTML5 events (no new deps)
- CompactCard: Copy toast, Send-to-chat with target chat name, empty-state in share popover, Re-run button
- auto_name.ts: filter count gate and assistant-fetch by content <> '' so tool-call assistant rows don't trip the once-and-only-once guard
- Adds CLAUDE.md and apps/web/src/lib/format.ts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 23:36:01 +00:00
c35ec65fc4 batch4: chats-in-sessions, force-send, /compact, right-rail file browser
Session 1:N Chat data model with backfill. Workspace switches to client-side
multi-tab pane management. Right-rail file browser with float-over viewer and
click-drag line selection replaces FileBrowserPane. Adds /compact streaming
summarizer (respects compact markers in context builder), force-send (cancels
in-flight, persists partial as 'cancelled', awaits cancellation completion via
deferred Promise + 5s timeout), message queue, stop generation, chat
auto-rename, session archive/unarchive with Closed Sessions section on repo
landing page. CHECK constraints on sessions.status, messages.role,
messages.status with KEEP IN SYNC comments tying to MESSAGE_ROLES /
MESSAGE_STATUSES const arrays. Deletes dead pane routes/hook and the
api.panes.* client block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:39:48 +00:00
60a0036850 batch3 T7 review fix: serialize open_file_in_browser to avoid double pane
When no file_browser pane exists, two rapid open_file_in_browser events
could both trigger create() since the ref check happens before the first
create resolves. Add a creating flag/promise so the second event waits
for the first create then updates the newly-created pane's state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:50:30 +00:00
fb31e63d10 batch3 T7: pane components — PaneShell, ChatPane, FileBrowserPane, PaneTab, Workspace
- PaneShell: per-pane chrome (kind label + close)
- ChatPane: extracts message+input rendering, subscribes to useSessionStream
- FileBrowserPane: tree + filter (debounced 100ms) + inline viewer via Shiki
- PaneTab: tab with kind icon + context menu (Split, Close, Close others,
  Close to right, Close all) via shadcn ContextMenu
- Workspace: tab strip + pane grid (CSS grid repeat(N,1fr)), native HTML5
  drag-to-reorder, "+" button (disabled at 5), subscribes to
  open_file_in_browser (focus existing file-browser pane or spawn one)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:46:14 +00:00