@deepseek-ai/dsh-client-ui-workspace 0.1.6-alpha.2 → 0.1.7-alpha.2

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.
@@ -8,8 +8,8 @@
8
8
  * - WorkspacePicker fills the conversation empty-state hole (menu + error
9
9
  * dialog shared with the browser).
10
10
  *
11
- * Each registration also declares one **directory-flow hole** (`single`
12
- * kind): the slot a composed picker package's client half fills with its
11
+ * Both registrations declare a **directory-flow hole** (`single` kind): the
12
+ * slot a composed picker package's client half fills with its
13
13
  * picking interaction — a renderless native-chooser driver or an in-app
14
14
  * browsing dialog. ui-workspace owns the trigger (the "Add workspace…"
15
15
  * entry, present only while the hole is occupied) and the adoption
@@ -21,11 +21,21 @@
21
21
  * Two holes exist because the two menu surfaces are independent slot entries
22
22
  * and a hole has exactly one declaring entry — they carry the same owner
23
23
  * contract and the same occupant.
24
+ *
25
+ * WorkspaceBrowser also declares the two **Session row action lists**: every
26
+ * row of a Session's "..." menu is an entry of
27
+ * `sidebar.workspaces.session.menu.item`, and every hover button at the row's
28
+ * end is an entry of `sidebar.workspaces.session.row.action`. The shipped
29
+ * actions — pin, rename, fork, archive — are ordinary entries this package
30
+ * registers from `apply`, each carrying its own behavior in its own inject
31
+ * face and reading its own Host state through hooks that face injects, so a
32
+ * client plugin's action lands beside them by `order` and needs nothing from
33
+ * the browser beyond the row identity.
24
34
  */
25
- import type { HostObservable, PropsHooks, PropsLocale, PropsRenderSlots, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
35
+ import type { HostObservable, InjectFace, PropsHooks, PropsLocale, PropsRenderSlots, PropsRuntime, PropsStore, SlotHookFactory } from '@deepseek-ai/dsh-client-ui-slots';
26
36
  import type { SessionSearchResultItem } from '@deepseek-ai/dsh-api-session-controller/client';
27
37
  import type { RemoteHostFacts } from '@deepseek-ai/dsh-api-remotes/client';
28
- import type { WorkspaceId, WorkspaceView } from '@deepseek-ai/dsh-api-workspace-controller/client';
38
+ import type { SessionActivity, WorkspaceId, WorkspaceView } from '@deepseek-ai/dsh-api-workspace-controller/client';
29
39
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
30
40
  import type { createWorkspaceViewStore } from '../stores.ts';
31
41
  /**
@@ -45,6 +55,30 @@ export interface DirectoryFlowOwnerProps {
45
55
  /** The interaction itself failed (chooser missing, listing denied); the owner shows its error surface. */
46
56
  onError: (message: string) => void;
47
57
  }
58
+ /** Owner share of one Session row action occurrence: the row the action belongs to. */
59
+ export interface SessionRowOwnerProps {
60
+ /** Session the row shows. */
61
+ sessionId: SessionId;
62
+ /** Row display title: persisted title, project basename, or Session id. */
63
+ displayTitle: string;
64
+ }
65
+ /** The row menu's open state as its owner holds it: the `useState` pair. */
66
+ export type MenuOpenState = readonly [open: boolean, setOpen: (open: boolean) => void];
67
+ /**
68
+ * Hook every row-menu entry receives. Rows only render while the menu is
69
+ * open, so an entry reads it for the setter: `setOpen(false)` dismisses the
70
+ * menu the entry sits in — the same state the owner's `onClose` sets.
71
+ */
72
+ export type UseMenuOpenState = () => MenuOpenState;
73
+ /**
74
+ * Bind the row's render occurrence into the entries' `useMenuOpenState` hook:
75
+ * the owner supplies its open-state pair as the occurrence's `hookContext`,
76
+ * and the hook hands that pair back.
77
+ * @param _standard - framework standard props (unused).
78
+ * @param state - the menu's open-state pair from the render occurrence.
79
+ * @returns the hook the entry calls.
80
+ */
81
+ export declare const menuOpenStateFactory: SlotHookFactory<'sidebar.workspaces.session.menu.item', UseMenuOpenState>;
48
82
  declare module '@deepseek-ai/dsh-client-ui-slots' {
49
83
  interface SlotMap {
50
84
  /** Directory-flow hole under the conversation empty-state picker (declared by the WorkspacePicker entry). */
@@ -59,6 +93,61 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
59
93
  scope: 'root';
60
94
  owner: DirectoryFlowOwnerProps;
61
95
  };
96
+ /**
97
+ * The rows of one Session's "..." menu, in ascending `order`. ui-workspace
98
+ * registers the shipped rows here — `pin` (100), `rename` (200), `fork`
99
+ * (300), `archive` (400) — so a plugin row is placed by its own `order`
100
+ * among them. Use a package-namespaced `id`; reusing a shipped id at
101
+ * another `priority` shadows that row. Each entry renders one
102
+ * `role="menuitem"` `<button>` (the shipped rows use ui-primitives'
103
+ * `MenuItemButton`, which adds the host styling and `separatorBefore`),
104
+ * decides its own visibility from its own state, and dismisses the menu
105
+ * through the injected `useMenuOpenState` hook after acting; the list's
106
+ * keyboard walk and focus return read the DOM, so any such button joins
107
+ * them. Labels come from the contributing package's locale namespace.
108
+ * @example
109
+ * return {
110
+ * inject: ['slots'],
111
+ * apply(ctx) {
112
+ * const copyLabel = 'Copy Session ID' // Localize in the contributing package.
113
+ * ctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register(
114
+ * { name: 'sidebar.workspaces.session.menu.item', id: 'copy-session-id', order: 500 },
115
+ * ({ sessionId, useMenuOpenState }) => {
116
+ * const [, setMenuOpen] = useMenuOpenState()
117
+ * return React.createElement(
118
+ * 'button',
119
+ * { type: 'button', role: 'menuitem', onClick: () => { setMenuOpen(false); void navigator.clipboard.writeText(sessionId) } },
120
+ * copyLabel,
121
+ * )
122
+ * },
123
+ * ))
124
+ * },
125
+ * }
126
+ */
127
+ 'sidebar.workspaces.session.menu.item': {
128
+ kind: 'list';
129
+ scope: 'root';
130
+ owner: SessionRowOwnerProps;
131
+ hookContext: MenuOpenState;
132
+ inject: {
133
+ hooks: {
134
+ menuOpenState: SlotHookFactory<'sidebar.workspaces.session.menu.item', UseMenuOpenState>;
135
+ };
136
+ };
137
+ };
138
+ /**
139
+ * The hover buttons at the end of one Session row, in ascending `order`,
140
+ * after the "..." menu trigger. ui-workspace registers `archive` (100) and
141
+ * `pin` (200) here. An entry renders one icon button (or nothing, when its
142
+ * action does not apply to the row) and owns the action it performs. Clicks
143
+ * inside the strip stay in the strip, so the button needs no propagation
144
+ * handling to keep the row from opening.
145
+ */
146
+ 'sidebar.workspaces.session.row.action': {
147
+ kind: 'list';
148
+ scope: 'root';
149
+ owner: SessionRowOwnerProps;
150
+ };
62
151
  }
63
152
  }
64
153
  /** The two directory-flow holes; a flow package's client half registers its one component into both. */
@@ -111,10 +200,10 @@ export type WorkspaceBrowserInjected = {
111
200
  }>;
112
201
  /** Maximum number of merged rows rendered for one search. */
113
202
  searchResultLimit: number;
114
- /** Rename a Session (explicit user title; resolves on host acceptance). */
115
- renameSession: (sessionId: SessionId, title: string) => Promise<void>;
116
- /** Fork a Session at its last completed turn and open the child. */
117
- forkSession: (sessionId: SessionId) => void;
203
+ /** Open the Session rename dialog (a row title double-click); the rename action entry raises the same request. */
204
+ requestSessionRename: (sessionId: SessionId, currentTitle: string) => void;
205
+ /** Tell the user an archived row cannot be opened (a click on it). */
206
+ notifyArchivedNotOpenable: () => void;
118
207
  /** Rename a Host Workspace (rejects on name conflict; resolves on durability). */
119
208
  renameWorkspace: (workspaceId: WorkspaceId, title: string) => Promise<void>;
120
209
  /** Delete only a Host Workspace registration; directory and Session logs remain. */
@@ -124,19 +213,167 @@ export type WorkspaceBrowserInjected = {
124
213
  * Omitted anchor appends to the end.
125
214
  */
126
215
  insertWorkspaceBefore: (workspaceId: WorkspaceId, beforeWorkspaceId?: WorkspaceId) => Promise<void>;
127
- /**
128
- * Archive a Session into the registry-global set: hidden from grouping
129
- * surfaces, log and accounting slot retained. Archiving the current
130
- * session clears the selection into the New Session view state.
131
- */
132
- archiveSession: (sessionId: SessionId) => Promise<void>;
216
+ /** Remove a Session from the registry-global archived set (the search results' restore button). */
217
+ unarchiveSession: (sessionId: SessionId) => Promise<void>;
133
218
  /** Adopt a picked host directory as a real Workspace before targeting a Session. */
134
219
  createWorkspace: (input: {
135
220
  path: string;
136
221
  }) => Promise<WorkspaceView>;
137
222
  };
223
+ /** The browser's declared viewing store handle, shared with the row actions that write view state. */
224
+ export type WorkspaceViewStoreHandle = ReturnType<typeof createWorkspaceViewStore>;
225
+ /** Props of one shipped row-menu entry: owner share + locale seat + the entry's own injected share. */
226
+ export type SessionMenuItemProps<Injected extends object = object> = PropsRuntime<'sidebar.workspaces.session.menu.item'> & PropsLocale<'workspace'> & InjectFace<Injected>;
227
+ /** Props of one shipped row hover button: owner share + locale seat + the entry's own injected share. */
228
+ export type SessionRowActionProps<Injected extends object = object> = PropsRuntime<'sidebar.workspaces.session.row.action'> & PropsLocale<'workspace'> & InjectFace<Injected>;
229
+ /** One transient Workspace notice rendered by the overlay toast entry. */
230
+ export type RowToast = {
231
+ kind: 'archived';
232
+ sessionId: SessionId;
233
+ } | {
234
+ kind: 'stoppedAndArchived';
235
+ sessionId: SessionId;
236
+ } | {
237
+ kind: 'pinFailed';
238
+ } | {
239
+ kind: 'unpinFailed';
240
+ } | {
241
+ kind: 'archivedNotOpenable';
242
+ } | {
243
+ kind: 'defaultWorkspaceFailed';
244
+ }
245
+ /**
246
+ * An explicit New Session request that failed. `message` is untranslated:
247
+ * a Host refusal as `code: message` — the stable code stays in the copy so
248
+ * a report can be searched by it — and any other failure's own message.
249
+ */
250
+ | {
251
+ kind: 'createFailed';
252
+ message: string;
253
+ };
254
+ /** The notice on display; `seq` keys remounts so a repeated notice restarts its hold. */
255
+ export type RowToastState = RowToast & {
256
+ seq: number;
257
+ };
258
+ /**
259
+ * Pin action share (menu row and hover button). The callbacks carry the whole
260
+ * behavior: the Host call, fronting the Session in its saved orders after a
261
+ * pin, and the failure notice. The hooks are the registry-global sets as
262
+ * Sets, so a row reads its own membership with one lookup.
263
+ */
264
+ export interface PinSessionInjected {
265
+ hooks: {
266
+ /** Pinned Session ids. */
267
+ pinned: HostObservable<ReadonlySet<SessionId>>;
268
+ /** Archived Session ids (pin does not apply to an archived row). */
269
+ archived: HostObservable<ReadonlySet<SessionId>>;
270
+ };
271
+ /** Pin a Session; on success it leads its accounts' saved orders, on failure the notice says so. */
272
+ pinSession: (sessionId: SessionId) => void;
273
+ /** Unpin a Session; saved positions stay as they are. */
274
+ unpinSession: (sessionId: SessionId) => void;
275
+ }
276
+ /**
277
+ * Archive action share (menu row and hover button). The callbacks carry the
278
+ * whole behavior: the Host call, the notice a success raises, the
279
+ * stop-and-archive confirmation a Host refusal for running work raises, and
280
+ * the diagnostics for any other rejection.
281
+ */
282
+ export interface ArchiveSessionInjected {
283
+ hooks: {
284
+ /** Archived Session ids. */
285
+ archived: HostObservable<ReadonlySet<SessionId>>;
286
+ };
287
+ /**
288
+ * Archive a Session into the registry-global set: the row keeps its
289
+ * account position and shows per the archived filter; archiving the
290
+ * current session clears the selection into the New Session view state.
291
+ * A Session with running work is not archived by this call: the Host's
292
+ * refusal opens the stop-and-archive confirmation instead.
293
+ */
294
+ archiveSession: (sessionId: SessionId) => void;
295
+ /** Remove a Session from the registry-global archived set. */
296
+ unarchiveSession: (sessionId: SessionId) => void;
297
+ }
298
+ /**
299
+ * A stop-and-archive confirmation the archive action asked for: the Host
300
+ * refused the plain archive because this work still runs.
301
+ */
302
+ export interface SessionArchiveConfirmRequest {
303
+ /** Session to stop and archive. */
304
+ sessionId: SessionId;
305
+ /** The row's display title, named in the dialog. */
306
+ displayTitle: string;
307
+ /** What the Host reported running, in family order. */
308
+ activity: readonly SessionActivity[];
309
+ }
310
+ /**
311
+ * Stop-and-archive dialog share: the pending confirmation, its settlement,
312
+ * and the archive hop that asks the Host to stop the work first.
313
+ */
314
+ export interface SessionArchiveConfirmInjected {
315
+ hooks: {
316
+ /** The confirmation asked for, until the dialog consumes or cancels it. */
317
+ archiveRequest: HostObservable<SessionArchiveConfirmRequest | null>;
318
+ };
319
+ /** Consume or cancel the pending confirmation. */
320
+ settleSessionArchive: () => void;
321
+ /**
322
+ * Archive a Session after the Host stops its running work; resolves once
323
+ * the archive set is durable (the stops settle in the background) and
324
+ * raises the stopped-and-archived notice.
325
+ */
326
+ stopAndArchiveSession: (sessionId: SessionId) => Promise<void>;
327
+ }
328
+ /** Fork action share. */
329
+ export interface ForkSessionInjected {
330
+ /** Fork a Session at its last completed turn; the child arrives through the Host list. */
331
+ forkSession: (sessionId: SessionId) => void;
332
+ }
333
+ /** Rename action share: the row only raises the request; the dialog entry answers it. */
334
+ export interface RenameSessionInjected {
335
+ /** Ask for the rename dialog, seeded with the row's current title. */
336
+ requestSessionRename: (sessionId: SessionId, currentTitle: string) => void;
337
+ }
338
+ /** A Session rename the rename action asked for; the dialog entry opens on it. */
339
+ export interface SessionRenameTarget {
340
+ /** Session to rename. */
341
+ sessionId: SessionId;
342
+ /** Title the dialog seeds its draft from. */
343
+ currentTitle: string;
344
+ }
345
+ /** Rename dialog share: the pending request, its settlement, and the rename hop the dialog confirms with. */
346
+ export interface SessionRenameDialogInjected {
347
+ hooks: {
348
+ /** The rename asked for, until the dialog consumes or cancels it. */
349
+ renameRequest: HostObservable<SessionRenameTarget | null>;
350
+ };
351
+ /** Consume or cancel the pending request. */
352
+ settleSessionRename: () => void;
353
+ /** Rename a Session (explicit user title; resolves on host acceptance). */
354
+ renameSession: (sessionId: SessionId, title: string) => Promise<void>;
355
+ }
356
+ /** Row toast share: the notice on display, its dismissal, and the two actions the archived notice offers. */
357
+ export interface RowToastInjected {
358
+ hooks: {
359
+ /** The notice on display, or none. */
360
+ toast: HostObservable<RowToastState | null>;
361
+ };
362
+ /** Take the notice down. */
363
+ dismissToast: () => void;
364
+ /** Undo an archive from its notice. */
365
+ undoArchive: (sessionId: SessionId) => void;
366
+ /** Switch the archived filter to "show" so the archived row is back in view. */
367
+ showArchived: () => void;
368
+ }
369
+ /** Props of the rename dialog entry in `shell.overlay`. */
370
+ export type SessionRenameDialogProps = PropsRuntime<'shell.overlay'> & PropsLocale<'workspace'> & Omit<SessionRenameDialogInjected, 'hooks'> & PropsHooks<SessionRenameDialogInjected['hooks']>;
371
+ /** Props of the stop-and-archive dialog entry in `shell.overlay`. */
372
+ export type SessionArchiveConfirmProps = PropsRuntime<'shell.overlay'> & PropsLocale<'workspace'> & Omit<SessionArchiveConfirmInjected, 'hooks'> & PropsHooks<SessionArchiveConfirmInjected['hooks']>;
373
+ /** Props of the row toast entry in `shell.overlay`. */
374
+ export type RowToastProps = PropsRuntime<'shell.overlay'> & PropsLocale<'workspace'> & Omit<RowToastInjected, 'hooks'> & PropsHooks<RowToastInjected['hooks']>;
138
375
  /** Full browser props: shell owner share + viewing store + injected actions + the locale seat. */
139
- export type WorkspaceBrowserProps = PropsRuntime<'sidebar.workspaces'> & PropsRenderSlots<'sidebar.workspaces.directoryFlow'> & PropsStore<ReturnType<typeof createWorkspaceViewStore>> & Omit<WorkspaceBrowserInjected, 'hooks'> & PropsHooks<WorkspaceBrowserInjected['hooks']> & PropsLocale<'workspace'>;
376
+ export type WorkspaceBrowserProps = PropsRuntime<'sidebar.workspaces'> & PropsRenderSlots<'sidebar.workspaces.directoryFlow' | 'sidebar.workspaces.session.menu.item' | 'sidebar.workspaces.session.row.action'> & PropsStore<WorkspaceViewStoreHandle> & Omit<WorkspaceBrowserInjected, 'hooks'> & PropsHooks<WorkspaceBrowserInjected['hooks']> & PropsLocale<'workspace'>;
140
377
  /**
141
378
  * Picker-private injected share. Pick semantics remain in the owner's onPick
142
379
  * callback; this callback creates only the real Host Workspace. A type alias
@@ -153,5 +390,5 @@ export type WorkspacePickerInjected = DirectoryPickingInjected & {
153
390
  * locale seat. The two picker holes (blank-session hero / New-Session view)
154
391
  * share one owner currency, so one composed type serves both registrations.
155
392
  */
156
- export type WorkspacePickerProps = PropsRuntime<'conversation.hero.workspace'> & PropsRenderSlots<'conversation.hero.workspace.directoryFlow'> & Omit<WorkspacePickerInjected, 'hooks'> & DirectoryPickingHooks & PropsLocale<'workspace'>;
393
+ export type WorkspacePickerProps = PropsRuntime<'conversation.hero.workspace'> & PropsRenderSlots<'conversation.hero.workspace.directoryFlow'> & Omit<WorkspacePickerInjected, 'hooks'> & PropsHooks<WorkspacePickerInjected['hooks']> & PropsLocale<'workspace'>;
157
394
  //# sourceMappingURL=slots.d.ts.map
@@ -5,7 +5,11 @@
5
5
  * (`conversation.hero.workspace` — both hero forms). Both read real Host
6
6
  * Workspaces through the global useWorkspaces hook, and each declares its
7
7
  * own `single` directory-flow child hole for the composed picker package's
8
- * client half (see the contract module doc). Export discipline:
8
+ * client half. WorkspaceBrowser additionally declares the two Session row
9
+ * action lists, and this apply registers the shipped actions — pin, rename,
10
+ * fork, archive — into them the way any client plugin would, each with its
11
+ * own behavior, plus the rename dialog and the row-action notice into
12
+ * `shell.overlay` (see the contract module doc). Export discipline:
9
13
  * packages/client/AGENTS.md.
10
14
  */
11
15
  import type { Context } from '@deepseek-ai/cordis';
@@ -13,7 +17,7 @@ import type { WorkspaceSnapshot } from '@deepseek-ai/dsh-api-workspace-controlle
13
17
  import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
14
18
  import { type WorkspaceKey } from './locales.ts';
15
19
  export type { UiWorkspace } from './navigation.ts';
16
- export type { DirectoryFlowOwnerProps, DirectoryFlowSlotName, DirectoryPickingHooks, DirectoryPickingInjected, WorkspaceBrowserInjected, WorkspaceBrowserProps, WorkspacePickerInjected, WorkspacePickerProps, } from './contract/slots.ts';
20
+ export type { DirectoryFlowOwnerProps, DirectoryFlowSlotName, DirectoryPickingHooks, DirectoryPickingInjected, MenuOpenState, RowToast, SessionRenameTarget, SessionRowOwnerProps, UseMenuOpenState, WorkspaceBrowserInjected, WorkspaceBrowserProps, WorkspacePickerInjected, WorkspacePickerProps, } from './contract/slots.ts';
17
21
  export type { WorkspaceKey } from './locales.ts';
18
22
  declare module '@deepseek-ai/dsh-client-ui-slots' {
19
23
  interface GlobalStandardProps {
@@ -5,6 +5,8 @@
5
5
  */
6
6
  /** Simplified Chinese dictionary (the key-set source of truth). */
7
7
  export declare const zh: {
8
+ 'defaultWorkspace.failed': string;
9
+ 'defaultWorkspace.title': string;
8
10
  'group.ungrouped': string;
9
11
  'session.new': string;
10
12
  'section.workspaces': string;
@@ -17,6 +19,9 @@ export declare const zh: {
17
19
  'orderBy.label': string;
18
20
  'orderBy.manual': string;
19
21
  'orderBy.updated': string;
22
+ 'filterBy.label': string;
23
+ 'viewOptions.showArchived': string;
24
+ 'viewOptions.onlyArchived': string;
20
25
  'sessions.expand': string;
21
26
  'sessions.collapse': string;
22
27
  'empty.none': string;
@@ -27,7 +32,6 @@ export declare const zh: {
27
32
  'search.clear': string;
28
33
  'search.results.aria': string;
29
34
  'search.pending': string;
30
- 'search.unavailable': string;
31
35
  'search.noMatches': string;
32
36
  'search.hasMore': string;
33
37
  'menu.addWorkspace': string;
@@ -45,10 +49,44 @@ export declare const zh: {
45
49
  'delete.pending': string;
46
50
  'menu.fork': string;
47
51
  'menu.archiveSession': string;
52
+ 'menu.unarchiveSession': string;
53
+ 'menu.pinSession': string;
54
+ 'menu.unpinSession': string;
55
+ 'row.archived': string;
56
+ 'row.pinned': string;
57
+ 'toast.archivedNotOpenable': string;
58
+ 'toast.archived': string;
59
+ 'toast.stoppedAndArchived': string;
60
+ 'archive.confirm.title': string;
61
+ 'archive.confirm.desc': string;
62
+ 'archive.confirm.activity': string;
63
+ 'archive.confirm.turn': string;
64
+ 'archive.confirm.subagents.one': string;
65
+ 'archive.confirm.subagents.other': string;
66
+ 'archive.confirm.jobs.one': string;
67
+ 'archive.confirm.jobs.other': string;
68
+ 'archive.confirm.schedules.one': string;
69
+ 'archive.confirm.schedules.other': string;
70
+ 'archive.confirm.other.one': string;
71
+ 'archive.confirm.other.other': string;
72
+ 'archive.confirm.listSeparator': string;
73
+ 'archive.confirm.action': string;
74
+ 'archive.confirm.pending': string;
75
+ 'toast.archivedUndo': string;
76
+ 'toast.archivedOr': string;
77
+ 'toast.archivedFilter': string;
78
+ 'toast.pinFailed': string;
79
+ 'toast.unpinFailed': string;
80
+ 'toast.createFailed': string;
48
81
  'sessions.count.one': string;
49
82
  'sessions.count.other': string;
50
83
  'actions.workspace.aria': string;
51
84
  'actions.session.aria': string;
85
+ 'actions.archive': string;
86
+ 'actions.unarchive': string;
87
+ 'actions.pin': string;
88
+ 'actions.unpin': string;
89
+ 'actions.newSession': string;
52
90
  'actions.newSession.aria': string;
53
91
  'status.running': string;
54
92
  'status.subagentsRunning.one': string;
@@ -57,6 +95,9 @@ export declare const zh: {
57
95
  'status.waitingApproval': string;
58
96
  'status.planReview': string;
59
97
  'status.waitingAnswer': string;
98
+ 'status.compact.approval': string;
99
+ 'status.compact.planReview': string;
100
+ 'status.compact.answer': string;
60
101
  'status.completed': string;
61
102
  'schedule.active': string;
62
103
  'hover.created': string;
@@ -74,6 +115,8 @@ export declare const zh: {
74
115
  export type WorkspaceKey = keyof typeof zh;
75
116
  /** English dictionary, checked complete against the zh key set. */
76
117
  export declare const en: {
118
+ 'defaultWorkspace.failed': string;
119
+ 'defaultWorkspace.title': string;
77
120
  'group.ungrouped': string;
78
121
  'session.new': string;
79
122
  'section.workspaces': string;
@@ -86,6 +129,9 @@ export declare const en: {
86
129
  'orderBy.label': string;
87
130
  'orderBy.manual': string;
88
131
  'orderBy.updated': string;
132
+ 'filterBy.label': string;
133
+ 'viewOptions.showArchived': string;
134
+ 'viewOptions.onlyArchived': string;
89
135
  'sessions.expand': string;
90
136
  'sessions.collapse': string;
91
137
  'empty.none': string;
@@ -96,7 +142,6 @@ export declare const en: {
96
142
  'search.clear': string;
97
143
  'search.results.aria': string;
98
144
  'search.pending': string;
99
- 'search.unavailable': string;
100
145
  'search.noMatches': string;
101
146
  'search.hasMore': string;
102
147
  'menu.addWorkspace': string;
@@ -114,10 +159,44 @@ export declare const en: {
114
159
  'delete.pending': string;
115
160
  'menu.fork': string;
116
161
  'menu.archiveSession': string;
162
+ 'menu.unarchiveSession': string;
163
+ 'menu.pinSession': string;
164
+ 'menu.unpinSession': string;
165
+ 'row.archived': string;
166
+ 'row.pinned': string;
167
+ 'toast.archivedNotOpenable': string;
168
+ 'toast.archived': string;
169
+ 'toast.stoppedAndArchived': string;
170
+ 'archive.confirm.title': string;
171
+ 'archive.confirm.desc': string;
172
+ 'archive.confirm.activity': string;
173
+ 'archive.confirm.turn': string;
174
+ 'archive.confirm.subagents.one': string;
175
+ 'archive.confirm.subagents.other': string;
176
+ 'archive.confirm.jobs.one': string;
177
+ 'archive.confirm.jobs.other': string;
178
+ 'archive.confirm.schedules.one': string;
179
+ 'archive.confirm.schedules.other': string;
180
+ 'archive.confirm.other.one': string;
181
+ 'archive.confirm.other.other': string;
182
+ 'archive.confirm.listSeparator': string;
183
+ 'archive.confirm.action': string;
184
+ 'archive.confirm.pending': string;
185
+ 'toast.archivedUndo': string;
186
+ 'toast.archivedOr': string;
187
+ 'toast.archivedFilter': string;
188
+ 'toast.pinFailed': string;
189
+ 'toast.unpinFailed': string;
190
+ 'toast.createFailed': string;
117
191
  'sessions.count.one': string;
118
192
  'sessions.count.other': string;
119
193
  'actions.workspace.aria': string;
120
194
  'actions.session.aria': string;
195
+ 'actions.archive': string;
196
+ 'actions.unarchive': string;
197
+ 'actions.pin': string;
198
+ 'actions.unpin': string;
199
+ 'actions.newSession': string;
121
200
  'actions.newSession.aria': string;
122
201
  'status.running': string;
123
202
  'status.subagentsRunning.one': string;
@@ -126,6 +205,9 @@ export declare const en: {
126
205
  'status.waitingApproval': string;
127
206
  'status.planReview': string;
128
207
  'status.waitingAnswer': string;
208
+ 'status.compact.approval': string;
209
+ 'status.compact.planReview': string;
210
+ 'status.compact.answer': string;
129
211
  'status.completed': string;
130
212
  'schedule.active': string;
131
213
  'hover.created': string;
@@ -4,6 +4,8 @@ import type { ClientRemote, DirectoryListing, RemoteFailure } from '@deepseek-ai
4
4
  import type { ISessions, SessionTarget } from '@deepseek-ai/dsh-api-session-controller/client';
5
5
  import type { IWorkspaces, WorkspaceId } from '@deepseek-ai/dsh-api-workspace-controller/client';
6
6
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
7
+ import type { RowToast } from './contract/slots.ts';
8
+ import type { WorkspaceViewStoreActions } from './stores.ts';
7
9
  /** Workspace archive and directory operations consumed by Client UI domains. */
8
10
  export interface UiWorkspace {
9
11
  /**
@@ -16,12 +18,14 @@ export interface UiWorkspace {
16
18
  * @param workspaceId - target Workspace.
17
19
  * @param beforeOpen - optional synchronous preparation for the selected Session, skipped after supersession.
18
20
  * @returns completion; a superseded request may create a Session but does not open it.
21
+ * @throws on failure; a refused creation is also shown through the Workspace
22
+ * notice unless a later navigation or disposal superseded the request.
19
23
  */
20
24
  openWorkspace(workspaceId: WorkspaceId, beforeOpen?: (sessionId: SessionId) => void): Promise<void>;
21
25
  /**
22
- * Fork a Session and open the child unless a later navigation supersedes it.
26
+ * Fork a Session without changing the current selection.
23
27
  * @param sessionId - source Session.
24
- * @returns completion; a superseded request leaves its child available without selecting it.
28
+ * @returns completion after child creation and inherited-title increment.
25
29
  */
26
30
  forkSession(sessionId: SessionId): Promise<void>;
27
31
  /**
@@ -31,20 +35,37 @@ export interface UiWorkspace {
31
35
  */
32
36
  connectWorkspace(workspaceId: WorkspaceId): Promise<SessionId>;
33
37
  /**
34
- * Start a New Session flow and navigate to its Session.
38
+ * Start a New Session flow and navigate to its Session; a creation the Host
39
+ * refuses is shown through the Workspace notice and leaves the selection as it was.
35
40
  * @param workspaceId - explicit target; absent inherits the current or most recent Workspace.
36
41
  */
37
42
  startSession(workspaceId?: WorkspaceId): void;
38
43
  /**
39
44
  * Archive a Session and clear it when it is the current selection.
40
45
  * @param sessionId - Session to archive.
46
+ * @param options - `stopActivity` asks the Host to stop the Session's running work instead of refusing.
41
47
  */
42
- archiveSession(sessionId: SessionId): Promise<void>;
48
+ archiveSession(sessionId: SessionId, options?: {
49
+ readonly stopActivity?: boolean;
50
+ }): Promise<void>;
43
51
  /**
44
52
  * Unarchive a Session, restoring it to its recorded Workspace position.
45
53
  * @param sessionId - Session to unarchive.
46
54
  */
47
55
  unarchiveSession(sessionId: SessionId): Promise<void>;
56
+ /**
57
+ * Pin a Session on the Host, then lead it in its accounts' saved orders
58
+ * (its Workspace group or Ungrouped, and the flat list). The order write
59
+ * reads the memberships current at completion, so reorders that landed
60
+ * while the Host call was pending keep their positions.
61
+ * @param sessionId - Session to pin.
62
+ */
63
+ pinSession(sessionId: SessionId): Promise<void>;
64
+ /**
65
+ * Unpin a Session on the Host; saved positions stay as they are.
66
+ * @param sessionId - Session to unpin.
67
+ */
68
+ unpinSession(sessionId: SessionId): Promise<void>;
48
69
  /**
49
70
  * Open the Host-native directory picker.
50
71
  * @returns the selected directory, or null when cancelled.
@@ -83,6 +104,8 @@ declare class UiWorkspaceService extends Service implements UiWorkspace {
83
104
  private readonly directoryPicker;
84
105
  private readonly workspaces;
85
106
  private readonly sessions;
107
+ private readonly view;
108
+ private readonly notify;
86
109
  private readonly connecting;
87
110
  private readonly lifetime;
88
111
  private readonly selection;
@@ -92,19 +115,29 @@ declare class UiWorkspaceService extends Service implements UiWorkspace {
92
115
  * @param directoryPicker - the directory-picking Remote namespace.
93
116
  * @param workspaces - pure Workspace Controller.
94
117
  * @param sessions - pure Session Controller.
118
+ * @param view - the browser's viewing-store write set (one instance shared with its registration).
119
+ * @param notify - show one notice through the Workspace notice channel.
95
120
  */
96
- constructor(ctx: Context, directoryPicker: ClientRemote['directoryPicker'], workspaces: IWorkspaces, sessions: ISessions);
121
+ constructor(ctx: Context, directoryPicker: ClientRemote['directoryPicker'], workspaces: IWorkspaces, sessions: ISessions, view: Pick<WorkspaceViewStoreActions, 'pinSessionOrder'>, notify: (toast: RowToast) => void);
97
122
  connectWorkspace(workspaceId: WorkspaceId): Promise<SessionId>;
123
+ private reuseOrCreateBlank;
124
+ private reuseBlank;
98
125
  openSession(target: SessionTarget): void;
99
126
  openWorkspace(workspaceId: WorkspaceId, beforeOpen?: (sessionId: SessionId) => void): Promise<void>;
100
127
  forkSession(sessionId: SessionId): Promise<void>;
101
128
  startSession(workspaceId?: WorkspaceId): void;
102
- archiveSession(sessionId: SessionId): Promise<void>;
129
+ archiveSession(sessionId: SessionId, options?: {
130
+ readonly stopActivity?: boolean;
131
+ }): Promise<void>;
103
132
  unarchiveSession(sessionId: SessionId): Promise<void>;
133
+ pinSession(sessionId: SessionId): Promise<void>;
134
+ unpinSession(sessionId: SessionId): Promise<void>;
104
135
  pickDirectory(): Promise<string | null>;
105
136
  listDirectory(path?: string, signal?: AbortSignal): Promise<DirectoryListing>;
106
137
  createDirectory(path: string, name: string): Promise<string>;
107
138
  private watchNavigation;
139
+ private restoreSelection;
140
+ private initializeDefaultWorkspace;
108
141
  /** @returns true when an archived current selection was cleared. */
109
142
  private clearArchivedCurrent;
110
143
  private clearMain;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The complete account memberships a Pin order write reconciles against,
3
+ * derived from the same Host snapshots the browser orders by, so the
4
+ * UiWorkspace service can complete the write without the browser in the loop.
5
+ */
6
+ import type { SessionListState } from '@deepseek-ai/dsh-api-session-controller/client';
7
+ import type { WorkspaceView } from '@deepseek-ai/dsh-api-workspace-controller/client';
8
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
9
+ import { type SessionRowState } from './tree.ts';
10
+ /** What `pinSessionOrder` reconciles a pinned Session's accounts against. */
11
+ export interface PinOrderSource {
12
+ members: Readonly<Record<string, readonly SessionId[]>>;
13
+ summaries: SessionListState['byId'];
14
+ rowState: Pick<SessionRowState, 'pinnedSessionIds' | 'archivedSessionIds'>;
15
+ }
16
+ /**
17
+ * Every account's complete membership: each Workspace, Ungrouped, and the flat list.
18
+ * @param workspaces - current Host Workspaces.
19
+ * @param list - current Session list snapshot.
20
+ * @param rowState - registry-global pin and archive sets.
21
+ * @returns the order source for one pin write.
22
+ */
23
+ export declare function pinOrderSource(workspaces: readonly WorkspaceView[], list: SessionListState, rowState: PinOrderSource['rowState']): PinOrderSource;
24
+ /**
25
+ * The accounts a pinned Session leads: its group (or Ungrouped) and the flat list.
26
+ * @param workspaces - current Host Workspaces.
27
+ * @param sessionId - the Session being pinned.
28
+ * @returns the account keys `pinSessionOrder` fronts.
29
+ */
30
+ export declare function pinOrderAccounts(workspaces: readonly WorkspaceView[], sessionId: SessionId): readonly string[];
31
+ //# sourceMappingURL=pin-order.d.ts.map