feat: slot system + entries/priority/errorreport + typert generator

This commit is contained in:
imccyu
2026-08-12 21:59:23 +08:00
parent eec7f2ec74
commit 0367506471
28 changed files with 777 additions and 165 deletions

View File

@@ -14,14 +14,14 @@ export interface ViewTab { id: string; label: string }
/**
* Per-session state shared by conversation, chat-view, and details slots.
* Unknown persisted view ids fall back to the first registered view.
* Unknown persisted view ids fall back to the stable Chat view.
*/
export interface ChatStoreState {
/** Details-linkage channel (conversation writes, details reads). */
selection: SelectionTarget | null
/** Composer draft (persisted; survives session switches and reloads). */
draft: string
/** Active conversation view id ('conversation.view' entry id); null falls back to the first view. */
/** Active conversation view id ('conversation.view' entry id); null falls back to Chat. */
view: string | null
/**
* One-shot inspect handoff: chat writes the call to reveal, the trajectory

View File

@@ -280,7 +280,7 @@
overflow-y: auto;
}
.scrollBody:has([data-conversation-composer-overlay]) > .viewArea {
.scrollBody:has([data-conversation-composer-overlay]) > :global([data-slot='conversation.session']) > .viewArea {
flex: 1 1 0;
min-height: 0;
overflow: hidden;

View File

@@ -6,6 +6,7 @@ import type { SessionId, SessionListState, SessionSummary } from '@deepseek-ai/d
import type {
ConversationSessionHeaderSlotProps, ConversationSessionSlotProps,
} from '../contract/slots.ts'
import type { ViewTab } from '../contract/views.ts'
import css from './ConversationRoot.module.css'
/** Full props composed from the strict session body contract. */
@@ -19,6 +20,15 @@ interface Breadcrumb {
readonly displayTitle: string
}
const DEFAULT_VIEW_ID = 'chat'
/** Resolve by id and keep stale persisted selections on the stable Chat fallback. */
function resolveActiveView(tabs: readonly ViewTab[], selectedId: string | null): ViewTab | undefined {
const requestedId = selectedId ?? DEFAULT_VIEW_ID
return tabs.find(view => view.id === requestedId)
?? tabs.find(view => view.id === DEFAULT_VIEW_ID)
}
function deriveAncestry(list: SessionListState, id: SessionId): readonly Breadcrumb[] {
const chain: Breadcrumb[] = []
const seen = new Set<SessionId>()
@@ -54,8 +64,8 @@ export function ConversationSessionHeader({
}: ConversationSessionHeaderProps) {
useSyncExternalStore(views.subscribe, views.version)
const tabs = views.list()
const activeId = useStore(s => s.view) ?? 'chat'
const active = tabs.find(view => view.id === activeId) ?? tabs[0]
const selectedId = useStore(s => s.view)
const active = resolveActiveView(tabs, selectedId)
const ancestry = useSessions(s => deriveAncestry(s, sessionId), equalBreadcrumbs)
const composerPhase = useSession(s => s.composerPhase)
const blank = useSession(s => s.blank)
@@ -131,8 +141,8 @@ export function ConversationSession({
}: ConversationSessionProps) {
useSyncExternalStore(views.subscribe, views.version)
const tabs = views.list()
const activeId = useStore(s => s.view) ?? 'chat'
const active = tabs.find(view => view.id === activeId) ?? tabs[0]
const selectedId = useStore(s => s.view)
const active = resolveActiveView(tabs, selectedId)
const composerPhase = useSession(s => s.composerPhase)
const blank = useSession(s => s.blank)
const inputState = useInput(s => s)