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>
This commit is contained in:
@@ -77,8 +77,19 @@ export interface ChatUpdatedEvent {
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ChatClosedEvent {
|
||||
type: 'chat_closed';
|
||||
export interface ChatArchivedEvent {
|
||||
type: 'chat_archived';
|
||||
chat_id: string;
|
||||
session_id: string;
|
||||
}
|
||||
|
||||
export interface ChatUnarchivedEvent {
|
||||
type: 'chat_unarchived';
|
||||
chat: Chat;
|
||||
}
|
||||
|
||||
export interface ChatDeletedEvent {
|
||||
type: 'chat_deleted';
|
||||
chat_id: string;
|
||||
session_id: string;
|
||||
}
|
||||
@@ -112,7 +123,9 @@ export type SessionEvent =
|
||||
| SessionArchivedEvent
|
||||
| ChatCreatedEvent
|
||||
| ChatUpdatedEvent
|
||||
| ChatClosedEvent
|
||||
| ChatArchivedEvent
|
||||
| ChatUnarchivedEvent
|
||||
| ChatDeletedEvent
|
||||
| ProjectArchivedEvent
|
||||
| ProjectUnarchivedEvent
|
||||
| ProjectUpdatedEvent;
|
||||
|
||||
@@ -165,7 +165,9 @@ function applyEvent(prev: SidebarResponse, event: import('./sessionEvents').Sess
|
||||
}
|
||||
case 'chat_created':
|
||||
case 'chat_updated':
|
||||
case 'chat_closed':
|
||||
case 'chat_archived':
|
||||
case 'chat_unarchived':
|
||||
case 'chat_deleted':
|
||||
return prev;
|
||||
case 'project_archived': {
|
||||
const next = prev.projects.filter((p) => p.id !== event.project_id);
|
||||
|
||||
Reference in New Issue
Block a user