@deepseek-ai/dsh-client-ui-commands 0.0.1-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.i18n.yaml +6 -0
- package/README.md +29 -0
- package/README.zh.md +29 -0
- package/lib/client.js +1116 -0
- package/lib/index.js +12 -0
- package/lib/invariant.js +24 -0
- package/lib/types/client/PopupSelectView.d.ts +16 -0
- package/lib/types/client/contract.d.ts +84 -0
- package/lib/types/client/directory.d.ts +70 -0
- package/lib/types/client/index.d.ts +38 -0
- package/lib/types/client/locales.d.ts +24 -0
- package/lib/types/client/popup.d.ts +154 -0
- package/lib/types/client/service.d.ts +121 -0
- package/lib/types/index.d.ts +10 -0
- package/lib/types/invariant.d.ts +16 -0
- package/package.json +87 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region lib/types/index.js
|
|
2
|
+
/**
|
|
3
|
+
* Command UI plugin, node half. Pure UI plugin: the empty apply exists so
|
|
4
|
+
* the plugin appears in the host cordis.yml / Loader; the browser half ships
|
|
5
|
+
* via exports["./client"], discovered through the package.json dsh.client
|
|
6
|
+
* declaration. The host command registry itself mounts separately
|
|
7
|
+
* (bootHost + CommandUiRuntime).
|
|
8
|
+
*/
|
|
9
|
+
/** Host plugin body — no host-side behavior for the command UI plugin. */
|
|
10
|
+
function apply() {}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { apply };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-commands`.
|
|
4
|
+
* @module @deepseek-ai/dsh-client-ui-commands/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@deepseek-ai/dsh-client-ui-commands";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "client-ui-commands-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: a browser-side source over the wire command
|
|
13
|
+
* directory — it emits no cordis events and owns no cross-plugin mutable
|
|
14
|
+
* state; dispatch and cache behavior are asserted by this package's specs.
|
|
15
|
+
*/
|
|
16
|
+
const install = () => {};
|
|
17
|
+
/**
|
|
18
|
+
* Register this package's invariant companion.
|
|
19
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
20
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
21
|
+
*/
|
|
22
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
23
|
+
//#endregion
|
|
24
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { PopupSelectController } from './popup.ts';
|
|
3
|
+
/** Injected business face of the popupSelect overlay entry. */
|
|
4
|
+
export interface PopupSelectInjected {
|
|
5
|
+
/** The session's shell controller (state store + verbs; the view never touches the open-context type). */
|
|
6
|
+
popup: PopupSelectController;
|
|
7
|
+
}
|
|
8
|
+
/** Full shell props: injected face + the locale seat. */
|
|
9
|
+
export type PopupSelectViewProps = PopupSelectInjected & PropsLocale<'command'>;
|
|
10
|
+
/**
|
|
11
|
+
* Render the popupSelect shell overlay entry.
|
|
12
|
+
* @param props - injected face: the session's shell controller; `t` rides the standard locale seat.
|
|
13
|
+
* @returns the select card while open; null while closed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function PopupSelectView({ popup, t }: PopupSelectViewProps): import("react").JSX.Element | null;
|
|
16
|
+
//# sourceMappingURL=PopupSelectView.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen contract of the client command surface. Types only. The
|
|
3
|
+
* CommandUiRuntime (`ctx.commandUi`) implements this face; business packages
|
|
4
|
+
* consume `register` alone.
|
|
5
|
+
*/
|
|
6
|
+
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
7
|
+
import type { ClientSessionContext } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
8
|
+
/** Copy for an option that must be acknowledged before onSelect can run. */
|
|
9
|
+
export interface SelectConfirmation {
|
|
10
|
+
readonly title: string;
|
|
11
|
+
readonly description: string;
|
|
12
|
+
readonly acknowledgeLabel: string;
|
|
13
|
+
readonly cancelLabel: string;
|
|
14
|
+
readonly confirmLabel: string;
|
|
15
|
+
}
|
|
16
|
+
/** One option row of a popupSelect shell. */
|
|
17
|
+
export interface SelectOption {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
readonly detail?: string;
|
|
21
|
+
readonly active?: boolean;
|
|
22
|
+
/** Optional in-page risk gate owned by the shared popup shell. */
|
|
23
|
+
readonly confirmation?: SelectConfirmation;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Business registration for the popupSelect command kind. Data is
|
|
27
|
+
* self-served: options/onSelect use the business package's own protocol.
|
|
28
|
+
* The shell component is owned by ui-commands; business never sees it. Both
|
|
29
|
+
* callbacks receive the ClientSessionContext captured at popup open.
|
|
30
|
+
*/
|
|
31
|
+
export type CommandUiSpec = {
|
|
32
|
+
readonly kind: 'popupSelect';
|
|
33
|
+
options(session: ClientSessionContext, signal: AbortSignal): Promise<readonly SelectOption[]>;
|
|
34
|
+
onSelect(option: SelectOption, session: ClientSessionContext): void | Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* One client-owned command contribution: a slash-menu entry whose behavior
|
|
38
|
+
* lives entirely on the client (no host descriptor). Merged with the host
|
|
39
|
+
* catalog by name — a collision with a host command fails loud at candidate
|
|
40
|
+
* synthesis, never shadows.
|
|
41
|
+
*/
|
|
42
|
+
export interface CommandContribution {
|
|
43
|
+
/** Command name without the leading slash (unique across contributions). */
|
|
44
|
+
readonly name: string;
|
|
45
|
+
/** Menu row description. */
|
|
46
|
+
readonly description: string;
|
|
47
|
+
/** Capability filter, called with a fresh projection per candidate pass. */
|
|
48
|
+
available(session: ClientSessionContext): boolean;
|
|
49
|
+
/** The command's UI behavior (this phase: popupSelect only). */
|
|
50
|
+
readonly ui: CommandUiSpec;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A UI decoration hung on one HOST command: what its BARE invocation does on
|
|
54
|
+
* this client. Not a second command — the host command keeps its catalog
|
|
55
|
+
* row, its argument claim (space / argued enter), and its lifecycle logging;
|
|
56
|
+
* the decoration replaces only the bare menu-pick/enter with a popup whose
|
|
57
|
+
* onSelect typically submits a completed line back through command.execute.
|
|
58
|
+
* A decoration never manufactures a row: a name with no host catalog entry
|
|
59
|
+
* in the session's directory simply never reaches the decoration.
|
|
60
|
+
*/
|
|
61
|
+
export interface CommandDecoration {
|
|
62
|
+
/** The HOST command name this decorates (without the leading slash). */
|
|
63
|
+
readonly name: string;
|
|
64
|
+
/** Capability filter, called with a fresh projection per bare invocation. */
|
|
65
|
+
available(session: ClientSessionContext): boolean;
|
|
66
|
+
/** The bare-invocation UI (this phase: popupSelect only). */
|
|
67
|
+
readonly ui: CommandUiSpec;
|
|
68
|
+
}
|
|
69
|
+
/** The `ctx.commandUi` service face visible to business packages. */
|
|
70
|
+
export interface CommandUiContract {
|
|
71
|
+
/**
|
|
72
|
+
* Register one client command contribution; effect disposer. Duplicate
|
|
73
|
+
* names throw at registration.
|
|
74
|
+
*/
|
|
75
|
+
register(contribution: CommandContribution): () => void;
|
|
76
|
+
/**
|
|
77
|
+
* Hang a bare-invocation decoration on one host command; effect disposer.
|
|
78
|
+
* Duplicate names throw at registration.
|
|
79
|
+
*/
|
|
80
|
+
decorate(decoration: CommandDecoration): () => void;
|
|
81
|
+
/** Resolve the per-session popup controller for one session scope (wiring/overlay layer). */
|
|
82
|
+
popupFor(actx: ClientContext): unknown;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command-directory cache keyed by session: one entry per served catalog —
|
|
3
|
+
* every session is agent-backed, so `command.list({sessionId})` is the only
|
|
4
|
+
* request fields. Each entry keeps the single-flight / soft-hard invalidation
|
|
5
|
+
* / epoch-guard behavior of the original global cache; the session-key axis
|
|
6
|
+
* is the only extra dimension.
|
|
7
|
+
*/
|
|
8
|
+
import type { CommandDescriptor } from '@deepseek-ai/dsh-commands/types';
|
|
9
|
+
import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client';
|
|
10
|
+
export type { CommandDescriptor } from '@deepseek-ai/dsh-commands/types';
|
|
11
|
+
/**
|
|
12
|
+
* cold = never pulled; pending = pull in flight with nothing servable;
|
|
13
|
+
* ready = snapshot serving (a soft-invalidate repull keeps this status);
|
|
14
|
+
* failed = last winning pull rejected, snapshot dropped.
|
|
15
|
+
*/
|
|
16
|
+
export type DirectoryStatus = 'cold' | 'pending' | 'ready' | 'failed';
|
|
17
|
+
/** Injected pull (the service binds command.list off the root connection). */
|
|
18
|
+
export type FetchCommands = (sessionId: SessionId) => Promise<readonly CommandDescriptor[]>;
|
|
19
|
+
/** The session-keyed directory cache. Plain class — the owning service wires events and RPC. */
|
|
20
|
+
export declare class CommandDirectory {
|
|
21
|
+
private readonly fetchCommands;
|
|
22
|
+
private readonly entries;
|
|
23
|
+
constructor(fetchCommands: FetchCommands);
|
|
24
|
+
/**
|
|
25
|
+
* Current cache status for one session.
|
|
26
|
+
* @param sessionId - session key.
|
|
27
|
+
* @returns the entry status (cold when never touched).
|
|
28
|
+
*/
|
|
29
|
+
status(sessionId: SessionId): DirectoryStatus;
|
|
30
|
+
/**
|
|
31
|
+
* Synchronous exact-name lookup over one session's hot snapshot.
|
|
32
|
+
* @param sessionId - session key.
|
|
33
|
+
* @param name - command name without the leading slash.
|
|
34
|
+
* @returns the descriptor, or undefined when absent or the entry is not ready.
|
|
35
|
+
*/
|
|
36
|
+
resolve(sessionId: SessionId, name: string): CommandDescriptor | undefined;
|
|
37
|
+
/** Soft invalidation (commands-changed): background repull on every touched key; ready snapshots keep serving. */
|
|
38
|
+
invalidateAll(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Hard reset on reconnect: every entry drops its snapshot (the agent world
|
|
41
|
+
* may have changed shape across the generation) and prewarms.
|
|
42
|
+
*/
|
|
43
|
+
resetConnected(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Fire-and-forget prewarm of one session (the command source's scope-birth
|
|
46
|
+
* warm hook lands here).
|
|
47
|
+
* @param sessionId - session key.
|
|
48
|
+
*/
|
|
49
|
+
warm(sessionId: SessionId): void;
|
|
50
|
+
/**
|
|
51
|
+
* Start one pull for one session. Publishes ready/failed only while it is
|
|
52
|
+
* still the key's latest pull (epoch guard); a ready snapshot is not
|
|
53
|
+
* demoted while the pull flies.
|
|
54
|
+
* @param sessionId - session key.
|
|
55
|
+
* @returns settled when this pull's outcome is published or discarded.
|
|
56
|
+
*/
|
|
57
|
+
refresh(sessionId: SessionId): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Strong-wait until one session's catalog is servable (the enter-
|
|
60
|
+
* adjudication "directory must be reached" rule): ready returns at once;
|
|
61
|
+
* cold/failed launch a fresh pull; pending joins the flying one. Rejects
|
|
62
|
+
* when the awaited pull fails or the signal aborts.
|
|
63
|
+
* @param sessionId - session key.
|
|
64
|
+
* @param signal - attempt-scoped abort (the SubmitAttempt signal).
|
|
65
|
+
* @returns the hot command snapshot.
|
|
66
|
+
*/
|
|
67
|
+
ensureReady(sessionId: SessionId, signal: AbortSignal): Promise<readonly CommandDescriptor[]>;
|
|
68
|
+
private entry;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=directory.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command UI plugin, browser half: CommandUiRuntime (`ctx.commandUi`) owning the
|
|
3
|
+
* capability-keyed directory cache, the '/' command source, the client
|
|
4
|
+
* contribution registry, and the per-session popupSelect controllers; the
|
|
5
|
+
* popupSelect shell self-registers into conversation.input.overlay with
|
|
6
|
+
* per-session resolution.
|
|
7
|
+
*/
|
|
8
|
+
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
9
|
+
import { CommandUiRuntime } from './service.ts';
|
|
10
|
+
import { type CommandKey } from './locales.ts';
|
|
11
|
+
export { CommandUiRuntime } from './service.ts';
|
|
12
|
+
export { CommandDirectory } from './directory.ts';
|
|
13
|
+
export type { CommandDescriptor, DirectoryStatus } from './directory.ts';
|
|
14
|
+
export { filterOptions, PopupSelectController } from './popup.ts';
|
|
15
|
+
export type { PopupSelectDeps, PopupSpec, PopupState, TokenSegment } from './popup.ts';
|
|
16
|
+
export type { PopupSelectInjected, PopupSelectViewProps } from './PopupSelectView.tsx';
|
|
17
|
+
export type { CommandContribution, CommandDecoration, CommandUiContract, CommandUiSpec, SelectConfirmation, SelectOption, } from './contract.ts';
|
|
18
|
+
export type { CommandKey } from './locales.ts';
|
|
19
|
+
declare module '@deepseek-ai/cordis' {
|
|
20
|
+
interface Context {
|
|
21
|
+
commandUi: CommandUiRuntime;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
25
|
+
interface LocaleNamespaceMap {
|
|
26
|
+
/** The popupSelect shell's copy. */
|
|
27
|
+
command: CommandKey;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Required services: the '/' source registry, session scopes, commands Remote, and locale registry. */
|
|
31
|
+
export declare const inject: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Client plugin body: mount the service, then register the popupSelect shell
|
|
34
|
+
* into the input overlay once its declarer is up.
|
|
35
|
+
* @param ctx - client root context.
|
|
36
|
+
*/
|
|
37
|
+
export declare function apply(ctx: ClientContext): void;
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** `command` namespace dictionaries (the popupSelect shell's copy). */
|
|
2
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
|
+
export declare const zh: {
|
|
4
|
+
'search.placeholder': string;
|
|
5
|
+
'search.aria': string;
|
|
6
|
+
'status.loading': string;
|
|
7
|
+
'status.applying': string;
|
|
8
|
+
'status.empty': string;
|
|
9
|
+
'overlay.aria': string;
|
|
10
|
+
'listbox.aria': string;
|
|
11
|
+
};
|
|
12
|
+
/** The command namespace key union. */
|
|
13
|
+
export type CommandKey = keyof typeof zh;
|
|
14
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
15
|
+
export declare const en: {
|
|
16
|
+
'search.placeholder': string;
|
|
17
|
+
'search.aria': string;
|
|
18
|
+
'status.loading': string;
|
|
19
|
+
'status.applying': string;
|
|
20
|
+
'status.empty': string;
|
|
21
|
+
'overlay.aria': string;
|
|
22
|
+
'listbox.aria': string;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
|
+
import type { TokenSpan } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
3
|
+
import type { SelectOption } from './contract.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The command token segment snapshotted at shell-open time, replayed to the
|
|
6
|
+
* injected {@link PopupSelectDeps.consume} callback after a successful
|
|
7
|
+
* selection. The Input side guards it: a menu-path span consumes iff draftRev
|
|
8
|
+
* is unchanged, an enter-path line iff the trimmed draft still equals the
|
|
9
|
+
* bare token.
|
|
10
|
+
*/
|
|
11
|
+
export type TokenSegment = {
|
|
12
|
+
readonly via: 'menu';
|
|
13
|
+
readonly span: TokenSpan;
|
|
14
|
+
} | {
|
|
15
|
+
readonly via: 'enter';
|
|
16
|
+
readonly token: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Structural business spec the shell settles against — the popupSelect half
|
|
20
|
+
* of CommandUiSpec, generic in the context value the opener captures (the
|
|
21
|
+
* session wiring passes its session projection; the controller only carries
|
|
22
|
+
* it from open() to the callbacks).
|
|
23
|
+
*/
|
|
24
|
+
export interface PopupSpec<TCtx> {
|
|
25
|
+
/** Load the option rows once per open (retry after failure reuses the same signal). */
|
|
26
|
+
options(context: TCtx, signal: AbortSignal): Promise<readonly SelectOption[]>;
|
|
27
|
+
/** Settle the picked option against the open-time context. */
|
|
28
|
+
onSelect(option: SelectOption, context: TCtx): void | Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/** Injected session-wiring callbacks of one controller (tests pass fakes). */
|
|
31
|
+
export interface PopupSelectDeps {
|
|
32
|
+
/**
|
|
33
|
+
* Consume the open-time token segment after a successful onSelect (the
|
|
34
|
+
* wiring dispatches the consume-token event to the opening session).
|
|
35
|
+
* @param segment - the open-time token segment snapshot.
|
|
36
|
+
* @returns whether the token was consumed; false (CAS miss) is benign and
|
|
37
|
+
* never retried.
|
|
38
|
+
*/
|
|
39
|
+
consume(segment: TokenSegment): boolean;
|
|
40
|
+
/** Return focus to the session composer (successful settle and Escape close paths). */
|
|
41
|
+
focusComposer(): void;
|
|
42
|
+
}
|
|
43
|
+
/** Popup shell state (the shell component renders from here; closed = render null). */
|
|
44
|
+
export interface PopupState {
|
|
45
|
+
readonly open: boolean;
|
|
46
|
+
/** Command name the shell is open for (null while closed). */
|
|
47
|
+
readonly command: string | null;
|
|
48
|
+
/** Options-load lifecycle; 'failed' keeps the shell open for retry(). */
|
|
49
|
+
readonly status: 'pending' | 'ready' | 'failed';
|
|
50
|
+
/** Options as loaded — never re-fetched per keystroke; views render {@link filterOptions} over them. */
|
|
51
|
+
readonly options: readonly SelectOption[];
|
|
52
|
+
/** Local filter text over the loaded options. */
|
|
53
|
+
readonly search: string;
|
|
54
|
+
/** Highlight index into the filtered row list (0 when empty/pending). */
|
|
55
|
+
readonly active: number;
|
|
56
|
+
/** A select() settlement is in flight: further select/search/highlight no-op until it settles. */
|
|
57
|
+
readonly submitting: boolean;
|
|
58
|
+
/** Option waiting for explicit risk acknowledgement; null during normal selection. */
|
|
59
|
+
readonly confirming: SelectOption | null;
|
|
60
|
+
/** Caller-controlled checkbox state for the pending confirmation. */
|
|
61
|
+
readonly acknowledged: boolean;
|
|
62
|
+
/** Surfaced settlement failure (options load or onSelect); null when none. */
|
|
63
|
+
readonly error: string | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Filter option rows against the shell's local search text (case-insensitive
|
|
67
|
+
* substring over label and detail; blank search keeps every row).
|
|
68
|
+
* @param options - the loaded rows.
|
|
69
|
+
* @param search - the shell's search text.
|
|
70
|
+
* @returns the rows the shell shows and highlights over.
|
|
71
|
+
*/
|
|
72
|
+
export declare function filterOptions(options: readonly SelectOption[], search: string): readonly SelectOption[];
|
|
73
|
+
/**
|
|
74
|
+
* Headless controller of one session's popupSelect shell. Late settlements
|
|
75
|
+
* lose their write rights through binding identity: dismiss/dispose/reopen
|
|
76
|
+
* swap the binding, so a settling options fetch or onSelect that no longer
|
|
77
|
+
* matches writes nothing and consumes nothing.
|
|
78
|
+
*/
|
|
79
|
+
export declare class PopupSelectController<TCtx = unknown> {
|
|
80
|
+
private readonly deps;
|
|
81
|
+
/** Shell state store (the overlay component subscribes here). */
|
|
82
|
+
readonly state: SnapshotStore<PopupState>;
|
|
83
|
+
private binding;
|
|
84
|
+
/**
|
|
85
|
+
* @param deps - session-wiring callbacks (token consumption + composer focus).
|
|
86
|
+
*/
|
|
87
|
+
constructor(deps: PopupSelectDeps);
|
|
88
|
+
/**
|
|
89
|
+
* Open the shell for one command: publish pending state and fetch options
|
|
90
|
+
* once through the business spec. A reopen supersedes the previous shell
|
|
91
|
+
* (its options fetch is aborted, its late settlements are dropped).
|
|
92
|
+
* @param command - command name the shell serves.
|
|
93
|
+
* @param spec - the registered popupSelect spec.
|
|
94
|
+
* @param context - open-time context snapshot, handed verbatim to options/onSelect.
|
|
95
|
+
* @param segment - open-time token segment snapshot for post-select consumption.
|
|
96
|
+
*/
|
|
97
|
+
open(command: string, spec: PopupSpec<TCtx>, context: TCtx, segment: TokenSegment): void;
|
|
98
|
+
/** Run the one options fetch of a binding; settlement rights die with the binding. */
|
|
99
|
+
private load;
|
|
100
|
+
/** Re-run a failed options fetch (search survives; no-op unless status is 'failed'). */
|
|
101
|
+
retry(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Replace the local search text (pure local filter — the provider is never
|
|
104
|
+
* re-queried) and rebase the highlight onto the new filtered list.
|
|
105
|
+
* @param search - the shell search input's text.
|
|
106
|
+
*/
|
|
107
|
+
setSearch(search: string): void;
|
|
108
|
+
/**
|
|
109
|
+
* Move the highlight across the filtered rows (wraps around; no-op unless
|
|
110
|
+
* options are ready and no selection is in flight).
|
|
111
|
+
* @param dir - +1 down, -1 up.
|
|
112
|
+
*/
|
|
113
|
+
move(dir: 1 | -1): void;
|
|
114
|
+
/**
|
|
115
|
+
* Set the highlight directly (pointer hover; no-op unless ready, idle, and
|
|
116
|
+
* in filtered range).
|
|
117
|
+
* @param index - filtered-row index.
|
|
118
|
+
*/
|
|
119
|
+
highlight(index: number): void;
|
|
120
|
+
/**
|
|
121
|
+
* Select one filtered row: single-flight — the first call enters
|
|
122
|
+
* `submitting` and later calls no-op until it settles. Success consumes the
|
|
123
|
+
* open-time token segment (a false CAS answer is benign), closes, and
|
|
124
|
+
* returns focus to the composer. Failure keeps the shell open with search,
|
|
125
|
+
* highlight, and token intact, surfaces the error, and re-arms select as
|
|
126
|
+
* the retry.
|
|
127
|
+
* @param index - filtered-row index (callers pass the highlight or the clicked row).
|
|
128
|
+
* @returns settled when the attempt has closed the shell or surfaced its failure.
|
|
129
|
+
*/
|
|
130
|
+
select(index: number): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Update the explicit checkbox for the currently pending risk gate.
|
|
133
|
+
* @param acknowledged - whether the user has acknowledged the displayed risk.
|
|
134
|
+
*/
|
|
135
|
+
acknowledge(acknowledged: boolean): void;
|
|
136
|
+
/** Cancel only the risk gate and return to the still-open option picker. */
|
|
137
|
+
cancelConfirmation(): void;
|
|
138
|
+
/** Settle the gated option only after the checkbox is acknowledged. */
|
|
139
|
+
confirm(): Promise<void>;
|
|
140
|
+
/** Run the business settlement for an already admitted option. */
|
|
141
|
+
private settle;
|
|
142
|
+
/**
|
|
143
|
+
* Close the shell; aborts a flying options fetch and revokes settlement
|
|
144
|
+
* rights. An outside pointer interaction dismisses plainly (the click's own
|
|
145
|
+
* target takes focus); Escape passes focusComposer to return focus explicitly.
|
|
146
|
+
* @param opts - focusComposer: also restore composer focus (Escape path).
|
|
147
|
+
*/
|
|
148
|
+
dismiss(opts?: {
|
|
149
|
+
readonly focusComposer?: boolean;
|
|
150
|
+
}): void;
|
|
151
|
+
/** Scope-teardown disposer: abort in-flight work and clear state (no focus side effect). */
|
|
152
|
+
dispose(): void;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=popup.d.ts.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandUiRuntime (`ctx.commandUi`): the '/' command source over the
|
|
3
|
+
* session-keyed directory, the client-contribution registry, and the
|
|
4
|
+
* per-session popupSelect controllers. Candidate synthesis merges the host
|
|
5
|
+
* catalog with contributions by availability, then fuzzy query/position
|
|
6
|
+
* filtering; a host/contribution name collision fails loud. Every execute
|
|
7
|
+
* addresses the session's agent by sessionId — sessions are always
|
|
8
|
+
* agent-backed.
|
|
9
|
+
*/
|
|
10
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import type { CommandResult } from '@deepseek-ai/dsh-commands/types';
|
|
13
|
+
import type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client';
|
|
14
|
+
import type { ClientSessionContext } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
15
|
+
import type { CommandContribution, CommandDecoration, CommandUiContract } from './contract.ts';
|
|
16
|
+
import { PopupSelectController } from './popup.ts';
|
|
17
|
+
declare module '@deepseek-ai/cordis' {
|
|
18
|
+
interface Events {
|
|
19
|
+
/**
|
|
20
|
+
* This browser client completed one admitted Host command execution.
|
|
21
|
+
* Other clients receive the durable command nodes but never this local
|
|
22
|
+
* submission acknowledgment.
|
|
23
|
+
* @param sessionId - Session addressed by the local submission.
|
|
24
|
+
* @param name - Executed command name without the leading slash.
|
|
25
|
+
* @param result - Host command result returned to this browser.
|
|
26
|
+
* @mode emit
|
|
27
|
+
*/
|
|
28
|
+
'command/executed'(sessionId: SessionId, name: string, result: CommandResult): void;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Command surface: session-keyed directory + '/' source + contribution registry + per-session popups. */
|
|
32
|
+
export declare class CommandUiRuntime extends Service implements CommandUiContract {
|
|
33
|
+
static inject: string[];
|
|
34
|
+
private readonly directory;
|
|
35
|
+
private readonly live;
|
|
36
|
+
/**
|
|
37
|
+
* @param ctx - owning root context (plugin fiber; the service registers
|
|
38
|
+
* itself as `command` and follows that fiber's lifetime).
|
|
39
|
+
*/
|
|
40
|
+
constructor(ctx: Context);
|
|
41
|
+
/**
|
|
42
|
+
* Register one client command contribution; effect disposer (rides the
|
|
43
|
+
* caller's fiber). Duplicate names throw.
|
|
44
|
+
* @param contribution - the contribution (descriptor + availability + popup spec).
|
|
45
|
+
* @returns the disposer removing the registration.
|
|
46
|
+
*/
|
|
47
|
+
register(contribution: CommandContribution): () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Hang a bare-invocation decoration on one host command; effect disposer
|
|
50
|
+
* (rides the caller's fiber). Duplicate names throw.
|
|
51
|
+
* @param decoration - host command name + availability + popup spec.
|
|
52
|
+
* @returns the disposer removing the registration.
|
|
53
|
+
*/
|
|
54
|
+
decorate(decoration: CommandDecoration): () => void;
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the per-session popup controller (lazy; dies with the session
|
|
57
|
+
* scope). The controller's consume callback dispatches the scoped
|
|
58
|
+
* consume-token event back to this session; focusComposer reaches the
|
|
59
|
+
* composer through the overlay slot currency.
|
|
60
|
+
* @param actx - session-scope ctx.
|
|
61
|
+
* @returns the resident controller.
|
|
62
|
+
*/
|
|
63
|
+
popupFor(actx: ClientContext): PopupSelectController<ClientSessionContext>;
|
|
64
|
+
/** Composer focus hooks by session (the overlay wiring binds the textarea focus here). */
|
|
65
|
+
private readonly focusHooks;
|
|
66
|
+
/**
|
|
67
|
+
* Bind one session's composer-focus hook (overlay slot wiring; unbind on unmount).
|
|
68
|
+
* @param id - session id.
|
|
69
|
+
* @param focus - textarea focus callback.
|
|
70
|
+
* @returns the unbind disposer.
|
|
71
|
+
*/
|
|
72
|
+
bindComposerFocus(id: SessionId, focus: () => void): () => void;
|
|
73
|
+
/** Menu candidates: host catalog + contribution availability, then position filtering and fuzzy name ranking. */
|
|
74
|
+
private candidates;
|
|
75
|
+
/** Decision table, menu column: contribution/decorated-host → popup; host input → claim; host bare → detached execute. */
|
|
76
|
+
private dispatch;
|
|
77
|
+
/** Decision table, space column: hot-key sync check; only host leadingInput claims. */
|
|
78
|
+
private matchSpace;
|
|
79
|
+
/**
|
|
80
|
+
* Decision table, enter column. Strong-waits the session's catalog (a
|
|
81
|
+
* warmup failure rejects — never a silent downgrade). Contributions and
|
|
82
|
+
* bare host commands act on the bare token only; leadingInput claims
|
|
83
|
+
* args-tolerant.
|
|
84
|
+
*/
|
|
85
|
+
private matchEnter;
|
|
86
|
+
/** Open the session's popup for one contribution or decoration (menu pick / bare enter). */
|
|
87
|
+
private openPopup;
|
|
88
|
+
/** Build the leadingInput claim: token `/name ` + the command.execute submit transaction. */
|
|
89
|
+
private leadingClaim;
|
|
90
|
+
/**
|
|
91
|
+
* The command.execute transaction, addressed to the session's agent — pure
|
|
92
|
+
* admission semantics. An unmatched line reports an error outcome (the
|
|
93
|
+
* composer's immediate admission feedback); an admitted command reports
|
|
94
|
+
* plain success regardless of its handler outcome, because the host
|
|
95
|
+
* executor durably logged the lifecycle (`command/run`/`command/done`) and
|
|
96
|
+
* the outcome renders as a persistent flow node — the composer never
|
|
97
|
+
* echoes it. Transport failures throw.
|
|
98
|
+
*/
|
|
99
|
+
private execute;
|
|
100
|
+
/** Publish the local acknowledgment without letting an observer change command admission. */
|
|
101
|
+
private notifyExecuted;
|
|
102
|
+
/** Log one contained `command/executed` observer failure. */
|
|
103
|
+
private warnExecutedListenerFailure;
|
|
104
|
+
/**
|
|
105
|
+
* Fire-and-forget execute for the internal ('handled') paths. Outcomes are
|
|
106
|
+
* NOT surfaced here: the host executor durably logs the command lifecycle
|
|
107
|
+
* (`command/run`/`command/done`), and the mux-broadcast events render as a
|
|
108
|
+
* persistent flow node on every tab. Only a transport/admission failure —
|
|
109
|
+
* which never entered a handler and therefore never logged — falls back to
|
|
110
|
+
* the composer notice as immediate feedback.
|
|
111
|
+
*/
|
|
112
|
+
private runDetached;
|
|
113
|
+
/** Dispatch a consume-token event to one session (menu-pick / bare-enter execute paths). */
|
|
114
|
+
private consumeVia;
|
|
115
|
+
/** Route an admission/transport failure to the session's composer notice channel (scope gone = attempt died with it). */
|
|
116
|
+
private noticeFor;
|
|
117
|
+
/** id → actx interchange (registered exchange point: this service coordinates for projection-only sources). */
|
|
118
|
+
private scopeFor;
|
|
119
|
+
private sessions;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command UI plugin, node half. Pure UI plugin: the empty apply exists so
|
|
3
|
+
* the plugin appears in the host cordis.yml / Loader; the browser half ships
|
|
4
|
+
* via exports["./client"], discovered through the package.json dsh.client
|
|
5
|
+
* declaration. The host command registry itself mounts separately
|
|
6
|
+
* (bootHost + CommandUiRuntime).
|
|
7
|
+
*/
|
|
8
|
+
/** Host plugin body — no host-side behavior for the command UI plugin. */
|
|
9
|
+
export declare function apply(): void;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-commands`.
|
|
3
|
+
* @module @deepseek-ai/dsh-client-ui-commands/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "client-ui-commands-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deepseek-ai/dsh-client-ui-commands",
|
|
3
|
+
"description": "Client command surface: global directory cache, '/' source, three command UI kinds, popupSelect registry",
|
|
4
|
+
"version": "0.0.1-rc.3",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "restricted"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/client/ui-commands"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./client": {
|
|
26
|
+
"types": "./lib/types/client/index.d.ts",
|
|
27
|
+
"default": "./lib/client.js"
|
|
28
|
+
},
|
|
29
|
+
"./src/*": "./src/*",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"dsh": {
|
|
33
|
+
"client": {
|
|
34
|
+
"inject": [
|
|
35
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
36
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
37
|
+
"@deepseek-ai/dsh-client-locale",
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-input-trigger",
|
|
39
|
+
"@deepseek-ai/dsh-client-ui-conversation"
|
|
40
|
+
],
|
|
41
|
+
"platform": "web"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"license": "BSD-3-Clause",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"clsx": "^2.0.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": "^18.2.0",
|
|
50
|
+
"@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.3",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.0.1-rc.3",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.3",
|
|
53
|
+
"@deepseek-ai/dsh-client-locale": "^0.0.1-rc.3",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.0.1-rc.3",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.3",
|
|
56
|
+
"@deepseek-ai/dsh-commands": "^0.0.1-rc.3",
|
|
57
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.3",
|
|
59
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.3"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/react": "~18.3.1",
|
|
63
|
+
"react": "^18.2.0",
|
|
64
|
+
"@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.3",
|
|
65
|
+
"@deepseek-ai/dsh-client-connection": "^0.0.1-rc.3",
|
|
66
|
+
"@deepseek-ai/dsh-client-locale": "^0.0.1-rc.3",
|
|
67
|
+
"@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.3",
|
|
68
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.0.1-rc.3",
|
|
69
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.0.1-rc.3",
|
|
70
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.3",
|
|
71
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.0.1-rc.3",
|
|
72
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.3",
|
|
73
|
+
"@deepseek-ai/dsh-commands": "^0.0.1-rc.3",
|
|
74
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.3",
|
|
75
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1"
|
|
76
|
+
},
|
|
77
|
+
"files": [
|
|
78
|
+
"lib/index.js",
|
|
79
|
+
"lib/invariant.js",
|
|
80
|
+
"lib/client.js",
|
|
81
|
+
"lib/types/**/*.d.ts"
|
|
82
|
+
],
|
|
83
|
+
"scripts": {
|
|
84
|
+
"bundle": "tsdown",
|
|
85
|
+
"watch": "tsdown --watch"
|
|
86
|
+
}
|
|
87
|
+
}
|