Bundles in-progress working-tree UI work not authored this session (CoderPane ChatInput migration, AgentComposerBar/CoderMessageList/tab-bar/sidebar/pane refinements, provider icons) with this session's changes to the same files: MessageBubble renders a collapsible 'Thinking' block from reasoning_text/reasoning_parts (surfacing ACP agent_thought_chunk + native reasoning), and the DiffPanel approve/reject calls are repointed to the real /api/coder/pending/:id/apply and /reject routes (the old /sessions/:id/pending/:id/approve|reject paths did not exist). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
216 lines
5.0 KiB
TypeScript
216 lines
5.0 KiB
TypeScript
// Tiny in-app event bus for session metadata changes that need to propagate
|
|
// across hooks (e.g. AI rename arriving via WS in the session view needs to
|
|
// also refresh the sidebar's session list).
|
|
|
|
import type {
|
|
Chat,
|
|
ErrorReason,
|
|
HtmlArtifactState,
|
|
MarkdownArtifactState,
|
|
Project,
|
|
Session,
|
|
} from '@/api/types';
|
|
import type { Attachment } from '@/lib/attachments';
|
|
|
|
export interface SessionRenamedEvent {
|
|
type: 'session_renamed';
|
|
session_id: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface ProjectCreatedEvent {
|
|
type: 'project_created';
|
|
project: Project;
|
|
}
|
|
|
|
export interface ProjectDeletedEvent {
|
|
type: 'project_deleted';
|
|
project_id: string;
|
|
}
|
|
|
|
export interface SessionCreatedEvent {
|
|
type: 'session_created';
|
|
session: Session;
|
|
project_id: string;
|
|
}
|
|
|
|
export interface SessionDeletedEvent {
|
|
type: 'session_deleted';
|
|
session_id: string;
|
|
project_id: string;
|
|
}
|
|
|
|
export interface SessionUpdatedEvent {
|
|
type: 'session_updated';
|
|
session_id: string;
|
|
project_id: string;
|
|
name: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface SessionWorkspaceUpdatedEvent {
|
|
type: 'session_workspace_updated';
|
|
session_id: string;
|
|
workspace_panes: import('@/api/types').WorkspacePane[];
|
|
}
|
|
|
|
export interface SessionLoadedEvent {
|
|
type: 'session_loaded';
|
|
session_id: string;
|
|
project_id: string;
|
|
}
|
|
|
|
export interface OpenFileInBrowserEvent {
|
|
type: 'open_file_in_browser';
|
|
path: string; // project-relative
|
|
}
|
|
|
|
export interface AttachChatFileEvent {
|
|
type: 'attach_chat_file';
|
|
attachment: Omit<Attachment, 'id'>;
|
|
}
|
|
|
|
export interface OpenChatInActivePaneEvent {
|
|
type: 'open_chat_in_active_pane';
|
|
chat_id: string;
|
|
}
|
|
|
|
// v1.14.x-html-artifact-panes: ActionRow's "Open in pane" button emits one of
|
|
// these; useWorkspacePanes subscribes and inserts the corresponding artifact
|
|
// pane (or focuses an existing one keyed by message_id).
|
|
export interface OpenMarkdownArtifactPaneEvent {
|
|
type: 'open_markdown_artifact_pane';
|
|
state: MarkdownArtifactState;
|
|
}
|
|
|
|
export interface OpenHtmlArtifactPaneEvent {
|
|
type: 'open_html_artifact_pane';
|
|
state: HtmlArtifactState;
|
|
}
|
|
|
|
// Client-side event fired by the sidebar Settings button when a session is
|
|
// currently mounted. Session.tsx subscribes and calls
|
|
// panesHook.toggleSettingsPane() (open on first click, close on second).
|
|
// Sidebar handles the no-session case by navigating to /settings directly.
|
|
export interface OpenSettingsPaneEvent {
|
|
type: 'open_settings_pane';
|
|
}
|
|
|
|
export interface SessionArchivedEvent {
|
|
type: 'session_archived';
|
|
session_id: string;
|
|
project_id: string;
|
|
}
|
|
|
|
export interface ChatCreatedEvent {
|
|
type: 'chat_created';
|
|
chat: Chat;
|
|
session_id: string;
|
|
}
|
|
|
|
export interface ChatUpdatedEvent {
|
|
type: 'chat_updated';
|
|
chat_id: string;
|
|
session_id: string;
|
|
name: string | null;
|
|
updated_at: string;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
export interface ProjectArchivedEvent {
|
|
type: 'project_archived';
|
|
project_id: string;
|
|
}
|
|
|
|
export interface ProjectUnarchivedEvent {
|
|
type: 'project_unarchived';
|
|
project: Project;
|
|
}
|
|
|
|
export interface ProjectUpdatedEvent {
|
|
type: 'project_updated';
|
|
project_id: string;
|
|
name: string;
|
|
}
|
|
|
|
// v1.8 mobile-tabs: broadcast on user channel from inference.ts so any device
|
|
// subscribed sees a chat working/idle/error. Frontend stores per-chat; panes
|
|
// derive their dot from pane.activeChatId.
|
|
// v1.8.2: optional `reason` carries a machine-readable code when status is
|
|
// 'error'. UI prefers reason for inline error rendering.
|
|
export interface ChatStatusEvent {
|
|
type: 'chat_status';
|
|
chat_id: string;
|
|
status: 'streaming' | 'tool_running' | 'waiting_for_input' | 'idle' | 'error';
|
|
at: string;
|
|
reason?: ErrorReason;
|
|
}
|
|
|
|
export interface RefetchMessagesEvent {
|
|
type: 'refetch_messages';
|
|
}
|
|
|
|
export type SessionEvent =
|
|
| SessionRenamedEvent
|
|
| ProjectCreatedEvent
|
|
| ProjectDeletedEvent
|
|
| SessionCreatedEvent
|
|
| SessionDeletedEvent
|
|
| SessionUpdatedEvent
|
|
| SessionWorkspaceUpdatedEvent
|
|
| SessionLoadedEvent
|
|
| OpenFileInBrowserEvent
|
|
| AttachChatFileEvent
|
|
| OpenChatInActivePaneEvent
|
|
| OpenMarkdownArtifactPaneEvent
|
|
| OpenHtmlArtifactPaneEvent
|
|
| OpenSettingsPaneEvent
|
|
| SessionArchivedEvent
|
|
| ChatCreatedEvent
|
|
| ChatUpdatedEvent
|
|
| ChatArchivedEvent
|
|
| ChatUnarchivedEvent
|
|
| ChatDeletedEvent
|
|
| ProjectArchivedEvent
|
|
| ProjectUnarchivedEvent
|
|
| ProjectUpdatedEvent
|
|
| ChatStatusEvent
|
|
| RefetchMessagesEvent;
|
|
type Listener = (event: SessionEvent) => void;
|
|
|
|
const listeners = new Set<Listener>();
|
|
|
|
export const sessionEvents = {
|
|
emit(event: SessionEvent) {
|
|
for (const listener of listeners) {
|
|
try {
|
|
listener(event);
|
|
} catch {
|
|
// swallow — one bad listener shouldn't break others
|
|
}
|
|
}
|
|
},
|
|
subscribe(listener: Listener): () => void {
|
|
listeners.add(listener);
|
|
return () => {
|
|
listeners.delete(listener);
|
|
};
|
|
},
|
|
};
|