feat: slash system / input service / agent scope

This commit is contained in:
imccyu
2026-07-27 03:17:52 +08:00
parent f3a4833dbf
commit a27be43ac1
210 changed files with 15969 additions and 2213 deletions
@@ -4,10 +4,9 @@
* details), the drag handles (pointer capture + rAF throttle), the concession
* chain (columns.ts), and the child-slot render decisions: the sidebar slot
* renders HERE with live parameters from the concession solve, and the
* session pair renders under the SessionProvider standard seat (render-prop
* form, injected by the renderer because the children declaration contains
* session-scope slots; session data arrives through framework-standard props
* and each registrant's inject face). Pure component: everything arrives
* session-aware occupants render in fixed column positions; strict entries
* gate themselves on current-session availability while session-maybe
* entries retain identity. Pure component: everything arrives
* through the three framework shares — zero cordis or framework imports,
* zero self-made hooks.
*/
@@ -21,7 +20,7 @@ import css from './AppFrame.module.css'
/** Full composed props: runtime share + child-slot render share + store share. */
export type AppFrameProps =
& PropsRuntime<'root'>
& PropsRenderSlots<'sidebar' | 'conversation' | 'details' | 'conversation.empty'>
& PropsRenderSlots<'sidebar' | 'conversation' | 'details'>
& PropsStore<ReturnType<typeof createLayoutStore>>
/** Center column grid item (session-body building block). */
@@ -81,18 +80,13 @@ function DragHandle(props: { side: 'sidebar' | 'details'; left: number; onStart:
)
}
/** The three-column frame (see module doc). SessionProvider arrives as a standard seat (declaring a session-scope child summons it — no framework import). */
/** The three-column frame (see module doc). */
export function AppFrame({
useStore,
actions,
renderSlot,
SessionProvider,
useSessions,
useWorkspaces,
}: AppFrameProps) {
const panels = useStore((s) => s)
const sessions = useSessions(s => s)
const baselinesReady = useWorkspaces(s => s.baselinesReady)
const frameRef = useRef<HTMLDivElement | null>(null)
const [viewport, setViewport] = useState(() => window.innerWidth)
@@ -157,42 +151,13 @@ export function AppFrame({
width: cols.sidebar,
})}
</div>
{!baselinesReady
? (
<>
<CenterColumn>
<div role="status">Loading workspaces and sessions</div>
</CenterColumn>
<DetailsColumn />
</>
)
: sessions.intent !== undefined
? (
<>
<CenterColumn>
{renderSlot('conversation.empty', {})}
</CenterColumn>
<DetailsColumn />
</>
)
: (
<SessionProvider
empty={() => (
<>
<CenterColumn><div role="status">Opening session</div></CenterColumn>
<DetailsColumn />
</>
)}
>
{() => (
<>
{/* Session data and actions arrive from standard hooks and the registrant's inject face. */}
<CenterColumn>{renderSlot('conversation', {})}</CenterColumn>
<DetailsColumn>{renderSlot('details', {})}</DetailsColumn>
</>
)}
</SessionProvider>
)}
<>
{/* Both column occupants stay at fixed tree positions. The
conversation is session-maybe; the strict details entry
naturally renders empty while no session is current. */}
<CenterColumn>{renderSlot('conversation', {})}</CenterColumn>
<DetailsColumn>{renderSlot('details', {})}</DetailsColumn>
</>
{/* The collapsed rail is fixed-width: no resize handle while closed. */}
{panels.sidebar > 0 && <DragHandle side="sidebar" left={cols.sidebar} onStart={onSidebarStart} onDrag={onSidebarDrag} onEnd={onDragEnd} />}
{cols.details > 0 && <DragHandle side="details" left={viewport - cols.details} onStart={onDetailsStart} onDrag={onDetailsDrag} onEnd={onDragEnd} />}
@@ -35,9 +35,10 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
// register() call that contributes AppFrame. Session owners never pass
// sessionId: the framework injects it as a standard prop.
'sidebar': { kind: 'single'; scope: 'root'; owner: SidebarOwnerProps }
'conversation': { kind: 'single'; scope: 'session'; owner: ConvOwnerProps }
// Current-session-optional: the occupant owns both the no-session hero
// and live conversation states without changing its React identity.
'conversation': { kind: 'single'; scope: 'session-maybe'; owner: ConvOwnerProps }
'details': { kind: 'single'; scope: 'session'; owner: DetailsOwnerProps }
'conversation.empty': { kind: 'single'; scope: 'root'; owner: EmptyOwnerProps }
}
}
@@ -61,9 +62,6 @@ export interface ConvOwnerProps {}
/** Details owner share: empty — sessionId arrives as a framework-standard prop. */
export interface DetailsOwnerProps {}
/** Empty-state owner share: business state and actions belong to the registrant. */
export interface EmptyOwnerProps { children?: never }
/** Required services (cordis fiber inject — the loader passes the whole export surface as an object plugin). */
export const inject = ['slots', 'theme']
@@ -81,9 +79,8 @@ export function apply(ctx: ClientContext): void {
name: 'root',
children: {
'sidebar': { kind: 'single', scope: 'root' },
'conversation': { kind: 'single', scope: 'session' },
'conversation': { kind: 'single', scope: 'session-maybe' },
'details': { kind: 'single', scope: 'session' },
'conversation.empty': { kind: 'single', scope: 'root' },
},
// Exclusive store: the factory itself — the framework instantiates per
// entry and delivers useStore/actions to AppFrame as standard props.