85 lines
3.7 KiB
TypeScript
85 lines
3.7 KiB
TypeScript
/**
|
|
* Slash trigger plugin, browser half: the SlashService (`ctx.slash`) owning
|
|
* trigger detection, the candidate menu, and the pick pipeline; MenuView
|
|
* self-registers into the conversation.input.overlay slot. Frozen pipeline
|
|
* contract in ./contract.ts; sources register through ctx.slash alone.
|
|
*/
|
|
// Type-only: pulls the locale plugin's Context merge (ctx.locale).
|
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
|
|
import { SlashService } from './service.ts'
|
|
import type { MenuViewInjected } from './slots.ts'
|
|
import { MenuView } from './MenuView.tsx'
|
|
import { en, zh, type MenuKey } from './locales.ts'
|
|
|
|
export { SlashService } from './service.ts'
|
|
export { SlashController } from './controller.ts'
|
|
export type { SlashControllerDeps, SourceRoster } from './controller.ts'
|
|
export type { MenuViewInjected } from './slots.ts'
|
|
export type { MenuViewProps } from './MenuView.tsx'
|
|
export type { MenuKey } from './locales.ts'
|
|
export type {
|
|
ArbitrateKey, ArbitrateOutcome, BeginCommandRequest, CandidateRequest, ClientSessionContext,
|
|
CommandClaim, ConsumeTokenRequest, InsertReferenceRequest, PickOutcome, PickVia, ReferenceCodec,
|
|
ReferenceInsert, SlashCandidate, SlashPick, SlashSource, SubmitOutcome, TokenSpan,
|
|
TriggerChar, TriggerGuard, TriggerPosition,
|
|
} from '../types.ts'
|
|
export type { DetectTrigger, ExactMatch, MenuEvent, MenuReduce, MenuState, TriggerHit } from '../core/contract.ts'
|
|
export type { SlashServiceContract } from './contract.ts'
|
|
|
|
declare module 'cordis' {
|
|
interface Context {
|
|
/** The outward face only; the concrete service stays inside this plugin. */
|
|
slash: import('./contract.ts').SlashServiceContract
|
|
}
|
|
}
|
|
|
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
interface LocaleNamespaceMap {
|
|
/** The candidate menu's copy: group titles keyed by source name, the pending row, and the listbox aria. */
|
|
'slash.menu': MenuKey
|
|
}
|
|
}
|
|
|
|
/** Namespace owning the candidate-menu copy. */
|
|
const MENU_NS = 'slash.menu'
|
|
|
|
/** Required services: controller resolution reads the session scope tree; the menu copy is localized. */
|
|
export const inject = ['sessions', 'locale']
|
|
|
|
/**
|
|
* Client plugin body: mount the service, then register MenuView into the
|
|
* input overlay once its declarer is up.
|
|
* @param ctx - client root context.
|
|
*/
|
|
export function apply(ctx: ClientContext): void {
|
|
ctx.plugin(SlashService)
|
|
ctx.effect(() => ctx.locale.register(MENU_NS, { zh, en }), 'ui-slash: menu dictionaries')
|
|
// Conditional mount: 'conversation.input.overlay' is declared by the
|
|
// conversation composer entry, and the conversation service is mounted
|
|
// after that declaration lands on the ledger — its presence is the
|
|
// registration-safe signal (same seam as toolview registrants).
|
|
ctx.inject(['slots', 'conversation', 'slash', 'sessions'], (scope: ClientContext) => {
|
|
const slash = scope.slash
|
|
const sessions = scope.sessions
|
|
scope.effect(() => scope.slots.register({
|
|
name: 'conversation.input.overlay',
|
|
id: 'slash-menu',
|
|
order: 0,
|
|
locale: MENU_NS,
|
|
inject: (sessionId): MenuViewInjected => {
|
|
// Session-scoped slot: resolve this session's controller (the slot
|
|
// frame hands ids, not ctx — the registered id→ctx interchange).
|
|
const actx = sessions.scope(sessionId)
|
|
if (actx === undefined) throw new Error(`ui-slash: session "${String(sessionId)}" resolved no scope`)
|
|
const controller = slash.sessionOf(actx)
|
|
return {
|
|
menu: controller.menu,
|
|
onPick: (source, index) => { controller.pick(source, index) },
|
|
onDismiss: () => { controller.dismiss() },
|
|
}
|
|
},
|
|
}, MenuView), 'ui-slash: MenuView overlay registration')
|
|
})
|
|
}
|