dsh-quick-actions 0.1.0-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 +21 -0
- package/README.en.md +199 -0
- package/README.md +199 -0
- package/cordis.patch.yml +29 -0
- package/lib/client.js +3901 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +737 -0
- package/lib/types/client/controller.d.ts +207 -0
- package/lib/types/client/dsh.d.ts +171 -0
- package/lib/types/client/index.d.ts +43 -0
- package/lib/types/client/manager/ActionForm.d.ts +20 -0
- package/lib/types/client/manager/ActionPanel.d.ts +15 -0
- package/lib/types/client/manager/ManagedRow.d.ts +40 -0
- package/lib/types/client/manager/ManagerPanel.d.ts +9 -0
- package/lib/types/client/manager/press.d.ts +38 -0
- package/lib/types/client/manager/search.d.ts +56 -0
- package/lib/types/client/manager/status.d.ts +23 -0
- package/lib/types/client/modal.d.ts +55 -0
- package/lib/types/client/session/ConfirmPanel.d.ts +23 -0
- package/lib/types/client/session/availability.d.ts +19 -0
- package/lib/types/client/session/execution.d.ts +86 -0
- package/lib/types/client/session/guards.d.ts +59 -0
- package/lib/types/client/surfaces/ActionFace.d.ts +21 -0
- package/lib/types/client/surfaces/ErrorBoundary.d.ts +31 -0
- package/lib/types/client/surfaces/QuickActionsSurface.d.ts +17 -0
- package/lib/types/client/surfaces/entries.d.ts +27 -0
- package/lib/types/client/surfaces/layout.d.ts +49 -0
- package/lib/types/client/surfaces/residency.d.ts +65 -0
- package/lib/types/host/config.d.ts +18 -0
- package/lib/types/host/index.d.ts +38 -0
- package/lib/types/host/presets.d.ts +26 -0
- package/lib/types/host/settings.d.ts +113 -0
- package/lib/types/index.d.ts +35 -0
- package/lib/types/locales/index.d.ts +34 -0
- package/lib/types/model/catalog.d.ts +63 -0
- package/lib/types/model/index.d.ts +13 -0
- package/lib/types/model/json.d.ts +11 -0
- package/lib/types/model/mutations.d.ts +98 -0
- package/lib/types/model/normalize.d.ts +15 -0
- package/lib/types/model/projection.d.ts +49 -0
- package/lib/types/model/settings.d.ts +48 -0
- package/lib/types/model/text.d.ts +28 -0
- package/lib/types/model/types.d.ts +89 -0
- package/lib/types/model/validation.d.ts +40 -0
- package/lib/types/styles/index.d.ts +39 -0
- package/lib/types/types.d.ts +11 -0
- package/lib/types.js +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type { CustomActionId, PresetActionId, PresetCatalog, QuickActionDraft, QuickActionLayout, QuickActionMutationRejection, QuickActionProjection, QuickActionRef, QuickActionSettingsV1 } from '../model/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* One path-addressed edit inside a namespace section. The transport also accepts
|
|
4
|
+
* an `unset` form; this plugin writes the whole section field by field and never
|
|
5
|
+
* clears one, so only the form it submits is declared.
|
|
6
|
+
*/
|
|
7
|
+
export interface SettingsSetOp {
|
|
8
|
+
readonly op: 'set';
|
|
9
|
+
readonly path: readonly string[];
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
}
|
|
12
|
+
/** One bound namespace as the scope publishes it. */
|
|
13
|
+
export interface SettingsScopeSnapshot {
|
|
14
|
+
/** `loading` until the shared document answers; `unavailable` when the namespace is not served. */
|
|
15
|
+
readonly status: 'loading' | 'ready' | 'unavailable';
|
|
16
|
+
/** Resolved section: schema defaults, then composition base, then the user layer. */
|
|
17
|
+
readonly value?: unknown;
|
|
18
|
+
/** Composition base layer, where an author-owned layer such as the catalog rides. */
|
|
19
|
+
readonly base?: unknown;
|
|
20
|
+
/** Revision of the raw user section, sent back to fence a write. */
|
|
21
|
+
readonly revision?: number;
|
|
22
|
+
/** Whether the provider accepts writes at all. */
|
|
23
|
+
readonly writable: boolean;
|
|
24
|
+
/** `memory` on a page this Client keeps process-local; no write ever crosses the wire. */
|
|
25
|
+
readonly mode: 'host' | 'memory';
|
|
26
|
+
}
|
|
27
|
+
/** The namespace contract handed to `bind`. */
|
|
28
|
+
export interface SettingsScopeSpec {
|
|
29
|
+
readonly namespace: string;
|
|
30
|
+
/** Narrows the resolved value; returning `undefined` publishes no value at all. */
|
|
31
|
+
readonly decode?: (value: unknown) => unknown;
|
|
32
|
+
}
|
|
33
|
+
/** One namespace scope: a derived read plus that namespace's serialized writes. */
|
|
34
|
+
export interface BoundSettingsScope {
|
|
35
|
+
getSnapshot(): SettingsScopeSnapshot;
|
|
36
|
+
subscribe(listener: () => void): () => void;
|
|
37
|
+
mutate(ops: readonly SettingsSetOp[], expectedRevision?: number): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/** The shared settings document as the mirror holds it. */
|
|
40
|
+
export interface SettingsMirrorSnapshot {
|
|
41
|
+
/** Whether any document answer is held at all; absent while no read has succeeded. */
|
|
42
|
+
readonly view?: unknown;
|
|
43
|
+
/** The last read's failure message, or `null` when it answered. */
|
|
44
|
+
readonly error: string | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The shared describe mirror's read face. A bound scope publishes nothing at all
|
|
48
|
+
* while the document is unanswered, so a read that *failed* is only visible here
|
|
49
|
+
* — which is what tells a first catalog read failure apart from one still in
|
|
50
|
+
* flight (spec 10).
|
|
51
|
+
*/
|
|
52
|
+
export interface SettingsMirror {
|
|
53
|
+
getSnapshot(): SettingsMirrorSnapshot;
|
|
54
|
+
subscribe(listener: () => void): () => void;
|
|
55
|
+
load(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
/** `ctx.settingsScope`. */
|
|
58
|
+
export interface SettingsScopeService {
|
|
59
|
+
bind(spec: SettingsScopeSpec): BoundSettingsScope;
|
|
60
|
+
describe(): SettingsMirror;
|
|
61
|
+
}
|
|
62
|
+
/** Connection lifecycle as `ctx.connection` publishes it; `undefined` before the loop starts. */
|
|
63
|
+
export type ConnectionState = 'connected' | 'connecting' | 'disconnected' | undefined;
|
|
64
|
+
/** `ctx.connection`, narrowed to the observable state this controller reads. */
|
|
65
|
+
export interface ConnectionLike {
|
|
66
|
+
readonly state: {
|
|
67
|
+
getSnapshot(): ConnectionState;
|
|
68
|
+
subscribe(listener: () => void): () => void;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Why the authoritative catalog cannot be shown (spec 10). */
|
|
72
|
+
export type CatalogErrorReason =
|
|
73
|
+
/** The settings document could not be read at all; retrying is the remedy. */
|
|
74
|
+
'unreadable'
|
|
75
|
+
/** The Host serves no catalog namespace, or published no `base` layer on it. */
|
|
76
|
+
| 'unavailable'
|
|
77
|
+
/** A `base` layer this release cannot read in full; never a truncated catalog (spec 5.1). */
|
|
78
|
+
| 'undecodable';
|
|
79
|
+
/** The authoritative Preset Catalog as the Client currently knows it. */
|
|
80
|
+
export type CatalogState = {
|
|
81
|
+
readonly status: 'loading';
|
|
82
|
+
} | {
|
|
83
|
+
readonly status: 'ready';
|
|
84
|
+
readonly catalog: PresetCatalog;
|
|
85
|
+
} | {
|
|
86
|
+
readonly status: 'error';
|
|
87
|
+
readonly reason: CatalogErrorReason;
|
|
88
|
+
};
|
|
89
|
+
/** The last Host-confirmed user state, and the fence a write against it must carry. */
|
|
90
|
+
export type SettingsState = {
|
|
91
|
+
readonly status: 'loading';
|
|
92
|
+
}
|
|
93
|
+
/** No namespace to read or write: unserved, or a page kept process-local. */
|
|
94
|
+
| {
|
|
95
|
+
readonly status: 'unavailable';
|
|
96
|
+
} | {
|
|
97
|
+
readonly status: 'ready';
|
|
98
|
+
readonly settings: QuickActionSettingsV1;
|
|
99
|
+
readonly revision: number;
|
|
100
|
+
readonly writable: boolean;
|
|
101
|
+
};
|
|
102
|
+
/** Why a write did not reach the stored state. */
|
|
103
|
+
export type QuickActionWriteFailure =
|
|
104
|
+
/** No catalog or no readable namespace yet, or the controller is disposed. */
|
|
105
|
+
{
|
|
106
|
+
readonly kind: 'not-ready';
|
|
107
|
+
}
|
|
108
|
+
/** The provider accepts no writes, or the connection is not current (spec 10). */
|
|
109
|
+
| {
|
|
110
|
+
readonly kind: 'read-only';
|
|
111
|
+
}
|
|
112
|
+
/** The shared model refused the plan; nothing crossed the wire. */
|
|
113
|
+
| {
|
|
114
|
+
readonly kind: 'rejected';
|
|
115
|
+
readonly rejection: QuickActionMutationRejection;
|
|
116
|
+
}
|
|
117
|
+
/** The Host refused the write itself; the authoritative snapshot was re-read. */
|
|
118
|
+
| {
|
|
119
|
+
readonly kind: 'refused';
|
|
120
|
+
}
|
|
121
|
+
/** The revision fence was lost: refresh, then ask the user to confirm again (spec 10). */
|
|
122
|
+
| {
|
|
123
|
+
readonly kind: 'conflict';
|
|
124
|
+
}
|
|
125
|
+
/** The write never settled against the Host. Never retried automatically. */
|
|
126
|
+
| {
|
|
127
|
+
readonly kind: 'failed';
|
|
128
|
+
readonly message: string;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* The structured result every mutation answers with (spec 15, decision 4).
|
|
132
|
+
*
|
|
133
|
+
* The shipped `SettingsScope.mutate` resolves to `void`, and this release adds no
|
|
134
|
+
* DSH core interface (spec 16.4), so success, refusal and conflict are told apart
|
|
135
|
+
* here, from the authoritative snapshot the scope publishes after the write: the
|
|
136
|
+
* write committed when the stored state now reads back as the plan, the fence was
|
|
137
|
+
* lost when the namespace revision moved elsewhere, and anything else is a Host
|
|
138
|
+
* refusal. Each failing case leaves the caller's form content untouched — the
|
|
139
|
+
* caller owns the draft — and the recovery read has already happened by the time
|
|
140
|
+
* this resolves.
|
|
141
|
+
*/
|
|
142
|
+
export type QuickActionWriteOutcome = {
|
|
143
|
+
readonly ok: true;
|
|
144
|
+
readonly changed: boolean;
|
|
145
|
+
} | {
|
|
146
|
+
readonly ok: false;
|
|
147
|
+
readonly failure: QuickActionWriteFailure;
|
|
148
|
+
};
|
|
149
|
+
/** The global management panel's own state (spec 7.1). */
|
|
150
|
+
export interface QuickActionManagerState {
|
|
151
|
+
readonly open: boolean;
|
|
152
|
+
}
|
|
153
|
+
/** Everything the Quick Action surfaces render from. */
|
|
154
|
+
export interface QuickActionsClientState {
|
|
155
|
+
readonly catalog: CatalogState;
|
|
156
|
+
readonly settings: SettingsState;
|
|
157
|
+
/**
|
|
158
|
+
* What the surfaces list, or `undefined` while no catalog is readable. With a
|
|
159
|
+
* catalog but no readable user state it projects the defaults, so the
|
|
160
|
+
* authoritative presets still render while management stays read-only (spec 10).
|
|
161
|
+
*/
|
|
162
|
+
readonly projection: QuickActionProjection | undefined;
|
|
163
|
+
/** Whether any write may be attempted right now. */
|
|
164
|
+
readonly readOnly: boolean;
|
|
165
|
+
/** The connection is not current, so the held snapshot may have moved on (spec 10). */
|
|
166
|
+
readonly stale: boolean;
|
|
167
|
+
readonly manager: QuickActionManagerState;
|
|
168
|
+
/** A write is in flight; the panel disables its controls rather than queueing clicks. */
|
|
169
|
+
readonly writing: boolean;
|
|
170
|
+
/** The last failure, until it is dismissed or superseded by the next write. */
|
|
171
|
+
readonly failure?: QuickActionWriteFailure;
|
|
172
|
+
}
|
|
173
|
+
/** The controller as the surfaces consume it. */
|
|
174
|
+
export interface QuickActionsController {
|
|
175
|
+
/** Current state; the same reference until something actually changes. */
|
|
176
|
+
getSnapshot(): QuickActionsClientState;
|
|
177
|
+
subscribe(listener: () => void): () => void;
|
|
178
|
+
/** Re-read the shared settings document — the retry behind a catalog error (spec 10). */
|
|
179
|
+
refresh(): Promise<void>;
|
|
180
|
+
openManager(): void;
|
|
181
|
+
closeManager(): void;
|
|
182
|
+
dismissFailure(): void;
|
|
183
|
+
createCustomAction(draft: QuickActionDraft): Promise<QuickActionWriteOutcome>;
|
|
184
|
+
clonePreset(presetId: PresetActionId): Promise<QuickActionWriteOutcome>;
|
|
185
|
+
updateCustomAction(id: CustomActionId, draft: QuickActionDraft): Promise<QuickActionWriteOutcome>;
|
|
186
|
+
setCustomActionEnabled(id: CustomActionId, enabled: boolean): Promise<QuickActionWriteOutcome>;
|
|
187
|
+
deleteCustomAction(id: CustomActionId): Promise<QuickActionWriteOutcome>;
|
|
188
|
+
setPresetHidden(presetId: PresetActionId, hidden: boolean): Promise<QuickActionWriteOutcome>;
|
|
189
|
+
reorderActions(order: readonly QuickActionRef[]): Promise<QuickActionWriteOutcome>;
|
|
190
|
+
moveAction(ref: QuickActionRef, toIndex: number): Promise<QuickActionWriteOutcome>;
|
|
191
|
+
setLayout(layout: QuickActionLayout): Promise<QuickActionWriteOutcome>;
|
|
192
|
+
/** Release the scope subscriptions and the connection listener. */
|
|
193
|
+
dispose(): void;
|
|
194
|
+
}
|
|
195
|
+
/** What the controller is built over. */
|
|
196
|
+
export interface QuickActionsControllerOptions {
|
|
197
|
+
readonly settingsScope: SettingsScopeService;
|
|
198
|
+
readonly connection: ConnectionLike;
|
|
199
|
+
/** Mints one Custom Action ID. Defaults to `crypto.randomUUID()`. */
|
|
200
|
+
readonly mintCustomActionId?: () => string;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create the controller. Both bindings are made on the calling fiber, so the
|
|
204
|
+
* scope disposers the binder registers are withdrawn with it; `dispose()`
|
|
205
|
+
* releases what this module owns on top of that.
|
|
206
|
+
*/
|
|
207
|
+
export declare function createQuickActionsController(options: QuickActionsControllerOptions): QuickActionsController;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DSH Client faces the Composer surfaces and the per-session execution layer
|
|
3
|
+
* consume, declared structurally and no wider than this plugin actually reads.
|
|
4
|
+
*
|
|
5
|
+
* Every shape here is transcribed from a published declaration of the target
|
|
6
|
+
* DSH release (0.1.5-rc.1) rather than guessed:
|
|
7
|
+
*
|
|
8
|
+
* - `InputState`, `InputActions`, `Occurrence`
|
|
9
|
+
* → `@deepseek-ai/dsh-client-ui-conversation/lib/types/client/contract/input.d.ts`
|
|
10
|
+
* - `SessionSnapshot`
|
|
11
|
+
* → `@deepseek-ai/dsh-api-session-controller/lib/types/client/contract/snapshot.d.ts`
|
|
12
|
+
* - the two dock Slot contracts and their standard props
|
|
13
|
+
* → `@deepseek-ai/dsh-client-ui-conversation/lib/types/client/contract/slots.d.ts`
|
|
14
|
+
* and the generated Slot ledger in `@deepseek-ai/dsh-cordis-client-runner`
|
|
15
|
+
* - `ComposerBlock` / `ComposerBlocks`
|
|
16
|
+
* → `@deepseek-ai/dsh-client-ui-conversation/lib/types/client/contract/composer-blocks.d.ts`
|
|
17
|
+
* - `slots.inject` / `slots.register` and `locale.register` / `locale.bind`
|
|
18
|
+
* → the generated Service ledger in `@deepseek-ai/dsh-cordis-client-runner`
|
|
19
|
+
*
|
|
20
|
+
* They are declared here instead of imported because the plugin ships against a
|
|
21
|
+
* DSH it does not depend on at build time: the Client bundle may only `require`
|
|
22
|
+
* specifiers the build adapter lists as externals (`react` and
|
|
23
|
+
* `react/jsx-runtime`), and every DSH face arrives as a Slot prop or a Cordis
|
|
24
|
+
* service on `ctx`. Declaring them narrowly also keeps the compiler honest about
|
|
25
|
+
* spec 9.1: nothing outside these shapes is reachable, so no DOM, Lexical,
|
|
26
|
+
* private shell, private event or private keyboard path can be typed into
|
|
27
|
+
* existence by accident.
|
|
28
|
+
*/
|
|
29
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
30
|
+
export type { ComponentType };
|
|
31
|
+
/** Browser-runtime identity of one unsent attachment draft. */
|
|
32
|
+
export type DraftAttachmentId = string;
|
|
33
|
+
/**
|
|
34
|
+
* One reference occurrence projected from the editor's chip nodes. Only the
|
|
35
|
+
* fields this plugin reads are declared; an occurrence's presence is what
|
|
36
|
+
* matters here, never its content.
|
|
37
|
+
*/
|
|
38
|
+
export interface InputOccurrence {
|
|
39
|
+
readonly occurrenceId: number;
|
|
40
|
+
readonly source: string;
|
|
41
|
+
readonly ref: string;
|
|
42
|
+
}
|
|
43
|
+
/** One row of the read-only transient queue projection. */
|
|
44
|
+
export interface InputQueueRow {
|
|
45
|
+
readonly placement: 'queued' | 'steering' | 'context';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The published per-Session input state — the whole public snapshot, listed
|
|
49
|
+
* field by field because spec 9.5 makes this exact set the only evidence the
|
|
50
|
+
* send single-flight may be judged from.
|
|
51
|
+
*/
|
|
52
|
+
export interface InputState {
|
|
53
|
+
/** Clipboard-text projection of the editor document (chips expanded). */
|
|
54
|
+
readonly draft: string;
|
|
55
|
+
/** Ordered runtime-only attachment ids. */
|
|
56
|
+
readonly attachmentIds: readonly DraftAttachmentId[];
|
|
57
|
+
/** Monotonic editor revision; bumps once per content-changing editor commit. */
|
|
58
|
+
readonly draftRev: number;
|
|
59
|
+
/** Submit-plane phase; `plain` is the only phase that accepts a new submission. */
|
|
60
|
+
readonly phase: 'plain' | 'adjudicating' | 'claimed' | 'submitting';
|
|
61
|
+
/** Present exactly while claimed/submitting. */
|
|
62
|
+
readonly claim?: {
|
|
63
|
+
readonly token: string;
|
|
64
|
+
readonly hint?: string;
|
|
65
|
+
readonly attachments?: boolean;
|
|
66
|
+
};
|
|
67
|
+
/** Reference chips currently in the draft, sorted by offset. */
|
|
68
|
+
readonly occurrences: readonly InputOccurrence[];
|
|
69
|
+
/** Read-only transient inbox projection from Session control. */
|
|
70
|
+
readonly queue: readonly InputQueueRow[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The public input action face handed to every session-scope Slot component.
|
|
74
|
+
*
|
|
75
|
+
* Only the two members spec 9.4 allows are declared. The shipped object also
|
|
76
|
+
* carries `addAttachments`, `removeAttachment` and `pruneAttachments`; leaving
|
|
77
|
+
* them out is deliberate — this plugin owns no draft attachment and must never
|
|
78
|
+
* touch one.
|
|
79
|
+
*/
|
|
80
|
+
export interface InputActions {
|
|
81
|
+
/** Replace the whole draft. */
|
|
82
|
+
setDraft(text: string): void;
|
|
83
|
+
/** Enter submission: the same official path the composer's send button takes. */
|
|
84
|
+
submit(): void;
|
|
85
|
+
}
|
|
86
|
+
/** The Session lifecycle fields the composer's own send guard reads. */
|
|
87
|
+
export interface SessionSnapshot {
|
|
88
|
+
readonly sessionId: string;
|
|
89
|
+
readonly removed: boolean;
|
|
90
|
+
readonly running: boolean;
|
|
91
|
+
readonly subagent: {
|
|
92
|
+
readonly address: {
|
|
93
|
+
readonly mode: string;
|
|
94
|
+
};
|
|
95
|
+
readonly parentAvailable?: boolean;
|
|
96
|
+
} | null;
|
|
97
|
+
}
|
|
98
|
+
/** Why one session's composer is inert (a feature-owned block). */
|
|
99
|
+
export interface ComposerBlock {
|
|
100
|
+
readonly reason: string;
|
|
101
|
+
}
|
|
102
|
+
/** An observable value as the DSH stores publish it. */
|
|
103
|
+
export interface ObservableSnapshot<T> {
|
|
104
|
+
getSnapshot(): T;
|
|
105
|
+
subscribe(listener: () => void): () => void;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* `ctx.conversation.blocks` — documented as "the registry face other plugins
|
|
109
|
+
* reach": how a plugin the composer cannot import makes a session's input
|
|
110
|
+
* inert. Reading it is the only public way to honour the `blocked` guard of
|
|
111
|
+
* spec 9.2. The Client reaches it through `ctx.get`, the documented read
|
|
112
|
+
* "without the inject requirement", so the declared dependency list stays
|
|
113
|
+
* exactly the four of spec 7.3, and degrades to "not blocked" when absent.
|
|
114
|
+
*/
|
|
115
|
+
export interface ComposerBlocks {
|
|
116
|
+
storeFor(sessionId: string): ObservableSnapshot<ComposerBlock | undefined>;
|
|
117
|
+
}
|
|
118
|
+
/** The narrow slice of `ctx.conversation` this plugin reads. */
|
|
119
|
+
export interface ConversationLike {
|
|
120
|
+
readonly blocks: ComposerBlocks;
|
|
121
|
+
}
|
|
122
|
+
/** `SnapshotSelectorHook<T>`: a `useSyncExternalStoreWithSelector` binding. */
|
|
123
|
+
export type SnapshotSelectorHook<T> = <S>(selector: (value: T) => S, equality?: (a: S, b: S) => boolean) => S;
|
|
124
|
+
/** Point-in-time owner values of `conversation.input.dock`. */
|
|
125
|
+
export interface InputZoneOwnerProps {
|
|
126
|
+
readonly session: SessionSnapshot;
|
|
127
|
+
readonly input: InputState;
|
|
128
|
+
}
|
|
129
|
+
/** Translation function bound to one locale namespace. */
|
|
130
|
+
export type Translate = (key: string, params?: Record<string, string | number>) => string;
|
|
131
|
+
/** The standard props both dock Slots hand every session-scoped entry. */
|
|
132
|
+
export interface SessionSlotProps {
|
|
133
|
+
readonly sessionId: string;
|
|
134
|
+
readonly useSession: SnapshotSelectorHook<SessionSnapshot>;
|
|
135
|
+
readonly useInput: SnapshotSelectorHook<InputState>;
|
|
136
|
+
readonly inputActions: InputActions;
|
|
137
|
+
readonly t: Translate;
|
|
138
|
+
}
|
|
139
|
+
/** Props of the `conversation.input.dock` entry (standard props plus the zone). */
|
|
140
|
+
export type InputDockProps = SessionSlotProps & InputZoneOwnerProps;
|
|
141
|
+
/** Props of the `conversation.composer.dock` entry (this Slot declares no owner props). */
|
|
142
|
+
export type ComposerDockProps = SessionSlotProps;
|
|
143
|
+
/** Registration options both dock Slots accept. */
|
|
144
|
+
export interface SlotRegisterOptions {
|
|
145
|
+
readonly name: 'conversation.input.dock' | 'conversation.composer.dock';
|
|
146
|
+
/** Cell key; a fresh id is added beside the shipped entries. */
|
|
147
|
+
readonly id: string;
|
|
148
|
+
/** Position among the entries, ascending. */
|
|
149
|
+
readonly order?: number;
|
|
150
|
+
/** Locale namespace backing the entry's `t` prop. */
|
|
151
|
+
readonly locale?: string;
|
|
152
|
+
}
|
|
153
|
+
/** One synchronous effect installed while an injected Slot declaration is live. */
|
|
154
|
+
export type SlotInjectionEffect = (() => void) | Iterable<() => void>;
|
|
155
|
+
/** `ctx.slots`, narrowed to the two calls this plugin makes. */
|
|
156
|
+
export interface SlotsService {
|
|
157
|
+
/**
|
|
158
|
+
* Install an effect for each declaration lifetime of a Slot. The controller
|
|
159
|
+
* belongs to the caller's fiber, so plugin unload removes the contribution.
|
|
160
|
+
*/
|
|
161
|
+
inject(key: string, callback: () => SlotInjectionEffect): () => void;
|
|
162
|
+
/** Register one entry; disposal runs through the caller's `ctx.effect`. */
|
|
163
|
+
register(options: SlotRegisterOptions, component: ComponentType<never>): () => void;
|
|
164
|
+
}
|
|
165
|
+
/** `ctx.locale`, narrowed to dictionary registration and namespace binding. */
|
|
166
|
+
export interface LocaleService {
|
|
167
|
+
register(ns: string, dictionaries: Record<string, Record<string, string>>): () => void;
|
|
168
|
+
bind(ns: string): Translate;
|
|
169
|
+
}
|
|
170
|
+
/** A React element, kept as a type alias so surfaces need no React value import. */
|
|
171
|
+
export type SurfaceNode = ReactNode;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client composition entry for Composer Quick Actions.
|
|
3
|
+
*
|
|
4
|
+
* This fiber owns everything the feature holds in the browser: one global
|
|
5
|
+
* `QuickActionsController` over the two Settings namespaces, the per-Session
|
|
6
|
+
* execution registry, the Resident Composer registry, the locale dictionaries,
|
|
7
|
+
* the stylesheet, and the two dock Slot registrations. All of it is installed
|
|
8
|
+
* through `ctx.effect` and `ctx.slots.inject`, so unloading the plugin leaves
|
|
9
|
+
* no listener, registration, namespace, style tag or subscription behind
|
|
10
|
+
* (spec 7.3).
|
|
11
|
+
*
|
|
12
|
+
* `settingsScope` is how this Client reaches Host-authoritative state at all — it
|
|
13
|
+
* never writes a file, never treats browser storage as a source of truth, and
|
|
14
|
+
* never reaches past Settings to a provider (spec 6.3). `connection` supplies the
|
|
15
|
+
* generation state the read-only-while-disconnected rule needs (spec 10).
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import type { ConnectionLike, SettingsScopeService } from './controller.js';
|
|
19
|
+
import type { ConversationLike, LocaleService, SlotsService } from './dsh.js';
|
|
20
|
+
declare module '@deepseek-ai/cordis' {
|
|
21
|
+
interface Context {
|
|
22
|
+
settingsScope: SettingsScopeService;
|
|
23
|
+
connection: ConnectionLike;
|
|
24
|
+
slots: SlotsService;
|
|
25
|
+
locale: LocaleService;
|
|
26
|
+
conversation: ConversationLike;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export declare const name = "composer-quick-actions";
|
|
30
|
+
/**
|
|
31
|
+
* The services this Client requires (spec 7.3). `conversation` is deliberately
|
|
32
|
+
* absent: its Composer-block registry is read through `ctx.get`, the documented
|
|
33
|
+
* read "without the inject requirement", because the service is guaranteed
|
|
34
|
+
* present wherever the two dock Slots it declares are rendered.
|
|
35
|
+
*/
|
|
36
|
+
export declare const inject: readonly string[];
|
|
37
|
+
/**
|
|
38
|
+
* Start the feature and tie every piece of it to this fiber.
|
|
39
|
+
*
|
|
40
|
+
* Nothing is returned and no internal type is re-exported: the controller, the
|
|
41
|
+
* execution engine and the surfaces are internal to this package (spec 14).
|
|
42
|
+
*/
|
|
43
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { ManagerWriteGate } from './press.js';
|
|
3
|
+
import type { QuickActionDraft } from '../../model/index.js';
|
|
4
|
+
import type { Translate } from '../dsh.js';
|
|
5
|
+
export interface ActionFormProps {
|
|
6
|
+
/** `new` for a creation, `edit` for an existing Custom Quick Action. */
|
|
7
|
+
readonly mode: 'new' | 'edit';
|
|
8
|
+
readonly draft: QuickActionDraft;
|
|
9
|
+
/**
|
|
10
|
+
* Whether the user has already tried to save. Blank fields only report
|
|
11
|
+
* themselves after that, so a freshly opened form does not open shouting.
|
|
12
|
+
*/
|
|
13
|
+
readonly attempted: boolean;
|
|
14
|
+
readonly gate: ManagerWriteGate;
|
|
15
|
+
readonly t: Translate;
|
|
16
|
+
readonly onChange: (draft: QuickActionDraft) => void;
|
|
17
|
+
readonly onSave: () => void;
|
|
18
|
+
readonly onCancel: () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function ActionForm(props: ActionFormProps): ReactElement;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { QuickActionSessionState } from '../session/execution.js';
|
|
3
|
+
import type { ProjectedQuickAction } from '../../model/index.js';
|
|
4
|
+
import type { Translate } from '../dsh.js';
|
|
5
|
+
export interface ActionPanelProps {
|
|
6
|
+
/** The actions this entry offers, already in the shared order. */
|
|
7
|
+
readonly actions: readonly ProjectedQuickAction[];
|
|
8
|
+
readonly session: QuickActionSessionState;
|
|
9
|
+
readonly t: Translate;
|
|
10
|
+
/** Id of the element naming this dialog. */
|
|
11
|
+
readonly labelledBy: string;
|
|
12
|
+
readonly onActivate: (action: ProjectedQuickAction) => void;
|
|
13
|
+
readonly onClose: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function ActionPanel(props: ActionPanelProps): ReactElement;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One row of the management list: what the action is, and everything the user
|
|
3
|
+
* may do to it (spec 3, 5.1, 5.2, 8.3).
|
|
4
|
+
*
|
|
5
|
+
* The two sources differ in exactly the way spec 5.1 says they must: a Preset
|
|
6
|
+
* Quick Action may be reordered, hidden, restored and cloned — never edited or
|
|
7
|
+
* deleted, because its definition is the author's — while a Custom Quick Action
|
|
8
|
+
* has its whole life cycle here.
|
|
9
|
+
*
|
|
10
|
+
* A hidden or disabled action is rendered, dimmed and labelled, never removed:
|
|
11
|
+
* removal from the Composer is the Hidden Quick Action projection, and this
|
|
12
|
+
* panel is where it stays recoverable (spec 3).
|
|
13
|
+
*/
|
|
14
|
+
import type { ReactElement } from 'react';
|
|
15
|
+
import type { ManagerWriteGate } from './press.js';
|
|
16
|
+
import type { ProjectedQuickAction, QuickActionRef } from '../../model/index.js';
|
|
17
|
+
import type { Translate } from '../dsh.js';
|
|
18
|
+
/** Everything one row can ask the panel to do. */
|
|
19
|
+
export interface ManagedRowHandlers {
|
|
20
|
+
readonly onMove: (ref: QuickActionRef, toIndex: number) => void;
|
|
21
|
+
readonly onEdit: () => void;
|
|
22
|
+
readonly onClone: () => void;
|
|
23
|
+
readonly onToggleHidden: () => void;
|
|
24
|
+
readonly onToggleEnabled: () => void;
|
|
25
|
+
readonly onAskDelete: () => void;
|
|
26
|
+
readonly onCancelDelete: () => void;
|
|
27
|
+
readonly onConfirmDelete: () => void;
|
|
28
|
+
}
|
|
29
|
+
export interface ManagedRowProps {
|
|
30
|
+
readonly action: ProjectedQuickAction;
|
|
31
|
+
/** Position in the shared order, which is what the move controls address. */
|
|
32
|
+
readonly index: number;
|
|
33
|
+
readonly total: number;
|
|
34
|
+
readonly gate: ManagerWriteGate;
|
|
35
|
+
/** Whether this row has already been asked about deleting. */
|
|
36
|
+
readonly deleting: boolean;
|
|
37
|
+
readonly t: Translate;
|
|
38
|
+
readonly on: ManagedRowHandlers;
|
|
39
|
+
}
|
|
40
|
+
export declare function ManagedRow({ action, index, total, gate, deleting, t, on }: ManagedRowProps): ReactElement;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { QuickActionsClientState, QuickActionsController } from '../controller.js';
|
|
3
|
+
import type { Translate } from '../dsh.js';
|
|
4
|
+
export interface ManagerPanelProps {
|
|
5
|
+
readonly client: QuickActionsClientState;
|
|
6
|
+
readonly controller: QuickActionsController;
|
|
7
|
+
readonly t: Translate;
|
|
8
|
+
}
|
|
9
|
+
export declare function ManagerPanel({ client, controller, t }: ManagerPanelProps): ReactElement;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a mutating control in the management panel may be pressed, and how it
|
|
3
|
+
* says so (spec 5.4, 8.4, 10).
|
|
4
|
+
*
|
|
5
|
+
* ## Why two ways of saying "no"
|
|
6
|
+
*
|
|
7
|
+
* Only sustained, externally-imposed unavailability — a read-only namespace, or a
|
|
8
|
+
* connection that can no longer vouch for the held snapshot — uses a real
|
|
9
|
+
* `disabled`, which takes the control out of the tab order entirely.
|
|
10
|
+
*
|
|
11
|
+
* Everything else uses `aria-disabled` plus a guarded handler, because
|
|
12
|
+
* everything else can become true *as a result of the press itself*: the write
|
|
13
|
+
* this control just started, the row that just reached an end of the list, the
|
|
14
|
+
* layout it just selected becoming current, the clone that just filled the last
|
|
15
|
+
* slot. A real `disabled` there would drop the keyboard caret to the document
|
|
16
|
+
* body mid-reorder — and focus handling is a hard gate of spec 8.4, not a nicety.
|
|
17
|
+
*/
|
|
18
|
+
/** What every mutating control in the panel is gated on. */
|
|
19
|
+
export interface ManagerWriteGate {
|
|
20
|
+
/** Sustained and external: the panel is genuinely inert and must not be tabbable. */
|
|
21
|
+
readonly readOnly: boolean;
|
|
22
|
+
/** A write is in flight; transient, and usually started by the focused control. */
|
|
23
|
+
readonly busy: boolean;
|
|
24
|
+
/** Whether creating or cloning is allowed at all right now (spec 5.4). */
|
|
25
|
+
readonly canAdd: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** The props one mutating control spreads onto its `<button>`. */
|
|
28
|
+
export interface PressProps {
|
|
29
|
+
readonly disabled: boolean;
|
|
30
|
+
readonly 'aria-disabled': boolean;
|
|
31
|
+
readonly onClick: () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Gate one control. `blocked` is its own extra condition — an end of the list,
|
|
35
|
+
* the ceiling, the choice already made; the gate's own `busy` is folded in here
|
|
36
|
+
* so no caller has to remember it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function pressProps(gate: ManagerWriteGate, blocked: boolean, onPress: () => void): PressProps;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared action panel's search rule (spec 8.1).
|
|
3
|
+
*
|
|
4
|
+
* Spec 8.1 requires the search to *actually* filter — the prototype's
|
|
5
|
+
* "shows everything" placeholder is explicitly forbidden — to keep matches in
|
|
6
|
+
* their `actionOrder` relative order, and to have one stated policy for which
|
|
7
|
+
* fields are searched, how case is handled and how Unicode is normalized.
|
|
8
|
+
*
|
|
9
|
+
* That policy is:
|
|
10
|
+
*
|
|
11
|
+
* - **Fields**: the label and the static text, and nothing else. Those are the
|
|
12
|
+
* two things the user wrote and can read; the icon is decoration and never an
|
|
13
|
+
* accessible or searchable name (spec 8.4), and the Command badge is derived
|
|
14
|
+
* from the text that is already searched.
|
|
15
|
+
* - **Case**: folded with `toLowerCase()`, which is the locale-independent
|
|
16
|
+
* Unicode default mapping. `toLocaleLowerCase()` is deliberately avoided: it
|
|
17
|
+
* would make the same query behave differently for a Turkish `i` depending on
|
|
18
|
+
* the UI language, and a filter that depends on the locale cannot be pinned.
|
|
19
|
+
* - **Unicode**: NFKC. It folds the compatibility forms a CJK IME actually
|
|
20
|
+
* produces — full-width Latin and full-width punctuation above all — so a
|
|
21
|
+
* query typed with the IME still finds half-width text.
|
|
22
|
+
* - **Whitespace**: every run collapses to one space, on both sides, so a
|
|
23
|
+
* one-line query can match a multi-line action text. The run definition is
|
|
24
|
+
* the regex `\s`, which is the same ECMAScript whitespace `trim()` uses, so
|
|
25
|
+
* this stays consistent with the blank rules of spec 4.3.
|
|
26
|
+
*
|
|
27
|
+
* Matching is per field and by substring: an action matches when the normalized
|
|
28
|
+
* query appears in its normalized label or in its normalized text. Fields are
|
|
29
|
+
* never concatenated first, which would let a query match across a boundary
|
|
30
|
+
* that does not exist.
|
|
31
|
+
*
|
|
32
|
+
* The comparison is deliberately not locale-collated (`Intl.Collator`): a
|
|
33
|
+
* collator answers "are these equal", not "does this contain that", and a
|
|
34
|
+
* substring search is what a filter box means.
|
|
35
|
+
*/
|
|
36
|
+
import type { ProjectedQuickAction } from '../../model/index.js';
|
|
37
|
+
/**
|
|
38
|
+
* The one normalization both sides of a comparison go through. Applying it to
|
|
39
|
+
* the query and to the haystack through the same function is what keeps the two
|
|
40
|
+
* from drifting apart.
|
|
41
|
+
*/
|
|
42
|
+
export declare function normalizeQuickActionSearchText(value: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a query asks for anything at all. A whitespace-only box is an empty
|
|
45
|
+
* one, and telling the two apart is what lets a panel say "nothing matched"
|
|
46
|
+
* rather than "nothing to run".
|
|
47
|
+
*/
|
|
48
|
+
export declare function hasQuickActionQuery(query: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* The actions matching `query`, in their original relative order.
|
|
51
|
+
*
|
|
52
|
+
* An empty or whitespace-only query hands the same array back by identity: an
|
|
53
|
+
* unsearched panel is not a filtered panel, and returning a copy would remount
|
|
54
|
+
* every row for nothing.
|
|
55
|
+
*/
|
|
56
|
+
export declare function filterQuickActions(actions: readonly ProjectedQuickAction[], query: string): readonly ProjectedQuickAction[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { QuickActionsClientState, QuickActionWriteFailure } from '../controller.js';
|
|
2
|
+
import type { Translate } from '../dsh.js';
|
|
3
|
+
/** Why every write is refused right now, or `undefined` when none is. */
|
|
4
|
+
export type ManagerReadOnlyReason =
|
|
5
|
+
/** The connection cannot vouch for the held snapshot, so it must not be written over. */
|
|
6
|
+
'offline'
|
|
7
|
+
/** No writable namespace: an unreadable first read, or a process-local page. */
|
|
8
|
+
| 'storage';
|
|
9
|
+
/**
|
|
10
|
+
* Report read-only state, and only read-only state.
|
|
11
|
+
*
|
|
12
|
+
* Spec 10 keeps two first-read failures apart: a catalog that could not be read
|
|
13
|
+
* shows a retryable catalog error, while a *readable* catalog whose user
|
|
14
|
+
* namespace failed shows "storage unavailable". Without the projection guard
|
|
15
|
+
* below, a loading or failed catalog also reads as read-only — the write gate
|
|
16
|
+
* refuses on `not-ready` — and the panel would print both notices at once,
|
|
17
|
+
* telling the user their storage is broken when it is the catalog that is
|
|
18
|
+
* missing. With no catalog there is nothing to be read-only *about*: the catalog
|
|
19
|
+
* notice is the whole story.
|
|
20
|
+
*/
|
|
21
|
+
export declare function managerReadOnlyReason(client: QuickActionsClientState): ManagerReadOnlyReason | undefined;
|
|
22
|
+
/** One write failure as a sentence, with the recovery it implies (spec 10). */
|
|
23
|
+
export declare function managerFailureMessage(failure: QuickActionWriteFailure, t: Translate): string;
|