@silo-code/sdk 0.31.0 → 0.32.0

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.
Files changed (55) hide show
  1. package/dist/ActivityGlyph.d.ts +25 -0
  2. package/dist/ActivityGlyph.d.ts.map +1 -0
  3. package/dist/ActivityGlyph.js +25 -0
  4. package/dist/ActivityGlyph.js.map +1 -0
  5. package/dist/IconButton.d.ts +23 -6
  6. package/dist/IconButton.d.ts.map +1 -1
  7. package/dist/IconButton.js +14 -5
  8. package/dist/IconButton.js.map +1 -1
  9. package/dist/activity.d.ts +36 -0
  10. package/dist/activity.d.ts.map +1 -0
  11. package/dist/activity.js +33 -0
  12. package/dist/activity.js.map +1 -0
  13. package/dist/editor-service.d.ts +5 -1
  14. package/dist/editor-service.d.ts.map +1 -1
  15. package/dist/icon-button-classes.d.ts +10 -2
  16. package/dist/icon-button-classes.d.ts.map +1 -1
  17. package/dist/icon-button-classes.js +9 -5
  18. package/dist/icon-button-classes.js.map +1 -1
  19. package/dist/index.d.ts +7 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/phosphor-icon.d.ts +15 -0
  24. package/dist/phosphor-icon.d.ts.map +1 -0
  25. package/dist/phosphor-icon.js +2 -0
  26. package/dist/phosphor-icon.js.map +1 -0
  27. package/dist/tab-adornment.d.ts +193 -0
  28. package/dist/tab-adornment.d.ts.map +1 -0
  29. package/dist/tab-adornment.js +2 -0
  30. package/dist/tab-adornment.js.map +1 -0
  31. package/dist/terminal-service.d.ts +18 -35
  32. package/dist/terminal-service.d.ts.map +1 -1
  33. package/dist/toolbar-items.d.ts +171 -0
  34. package/dist/toolbar-items.d.ts.map +1 -0
  35. package/dist/toolbar-items.js +2 -0
  36. package/dist/toolbar-items.js.map +1 -0
  37. package/dist/types.d.ts +15 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/workspace-service.d.ts +66 -45
  40. package/dist/workspace-service.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/src/ActivityGlyph.tsx +43 -0
  43. package/src/IconButton.tsx +28 -6
  44. package/src/activity.test.ts +35 -0
  45. package/src/activity.ts +57 -0
  46. package/src/editor-service.ts +5 -1
  47. package/src/icon-button-classes.test.ts +10 -0
  48. package/src/icon-button-classes.ts +18 -5
  49. package/src/index.ts +32 -1
  50. package/src/phosphor-icon.ts +14 -0
  51. package/src/tab-adornment.ts +208 -0
  52. package/src/terminal-service.ts +21 -35
  53. package/src/toolbar-items.ts +190 -0
  54. package/src/types.ts +17 -0
  55. package/src/workspace-service.ts +72 -45
@@ -0,0 +1,190 @@
1
+ import type { ContextKeys } from "./context-keys";
2
+ import type { PhosphorIconName } from "./phosphor-icon";
3
+ import type { MenuEntry } from "./ui-service";
4
+
5
+ /**
6
+ * Built-in CenterDock surfaces that accept
7
+ * {@link ToolbarItemContribution}s. One surface per registration.
8
+ *
9
+ * @category Registration
10
+ * @public
11
+ */
12
+ export type ToolbarSurface = "editor" | "terminal";
13
+
14
+ /**
15
+ * The typed target each {@link ToolbarSurface} passes to an invoked command
16
+ * and to `when` / `checked` / {@link ToolbarMenuItemContribution.menu} builders.
17
+ *
18
+ * @category Registration
19
+ * @public
20
+ */
21
+ export interface ToolbarItemContext {
22
+ editor: { editorId: string };
23
+ terminal: { terminalId: string };
24
+ }
25
+
26
+ /**
27
+ * Shared fields for interactive toolbar contributions. Render chrome is driven
28
+ * by {@link ToolbarItemFields.icon | icon} + {@link ToolbarItemFields.title | title}:
29
+ *
30
+ * | icon | title | Control |
31
+ * | ---- | ----- | -------------- |
32
+ * | ✓ | — | icon-only |
33
+ * | — | ✓ | text-only |
34
+ * | ✓ | ✓ | icon + text |
35
+ *
36
+ * Icons are Phosphor export names ({@link PhosphorIconName}); the host
37
+ * resolves and paints them bold at 1em so they match local-web-viewer and
38
+ * track UI zoom. Pass a React node is no longer supported — use the name.
39
+ *
40
+ * @category Registration
41
+ * @public
42
+ */
43
+ export interface ToolbarItemFields<S extends ToolbarSurface = ToolbarSurface> {
44
+ /** Unique id for this contribution. */
45
+ id: string;
46
+ /** Which toolbar to contribute to. */
47
+ surface: S;
48
+ /**
49
+ * Leading glyph as a {@link PhosphorIconName} (e.g. `"Flag"`). Omit for a
50
+ * text-only control (requires {@link ToolbarItemFields.title | title}).
51
+ */
52
+ icon?: PhosphorIconName;
53
+ /**
54
+ * Visible label painted in the control. Omit for icon-only. When both
55
+ * `icon` and `title` are set, the host renders icon + text.
56
+ */
57
+ title?: string;
58
+ /** Hover tooltip (falls back to title / label / command label). */
59
+ tooltip?: string;
60
+ /**
61
+ * Accessible name (falls back to title / the command's label). Always used
62
+ * for `aria-label`; not painted unless {@link ToolbarItemFields.title | title}
63
+ * is also set.
64
+ */
65
+ label?: string;
66
+ /** Ordering within the trailing cluster; lower sorts first. */
67
+ order?: number;
68
+ /**
69
+ * Visibility predicate. Returning false hides the item for this target.
70
+ */
71
+ when?: (ctx: ContextKeys, target: ToolbarItemContext[S]) => boolean;
72
+ /**
73
+ * Toggle-state predicate for command items. When provided, the host renders
74
+ * the control in a pressed/checked visual state whenever this returns true.
75
+ * Menu items put checks on individual {@link MenuEntry} rows instead.
76
+ */
77
+ checked?: (ctx: ContextKeys, target: ToolbarItemContext[S]) => boolean;
78
+ }
79
+
80
+ /**
81
+ * Command-backed toolbar control — click runs {@link ToolbarCommandItemContribution.command}.
82
+ *
83
+ * @category Registration
84
+ * @public
85
+ */
86
+ export interface ToolbarCommandItemContribution<
87
+ S extends ToolbarSurface = ToolbarSurface,
88
+ > extends ToolbarItemFields<S> {
89
+ /** The command to run; receives {@link ToolbarItemContext}[S] as its first arg. */
90
+ command: string;
91
+ menu?: undefined;
92
+ type?: undefined;
93
+ }
94
+
95
+ /**
96
+ * Menu-backed toolbar control — click opens a host dropdown via
97
+ * {@link UiService.showMenu} with entries from
98
+ * {@link ToolbarMenuItemContribution.menu}.
99
+ *
100
+ * @category Registration
101
+ * @public
102
+ */
103
+ export interface ToolbarMenuItemContribution<
104
+ S extends ToolbarSurface = ToolbarSurface,
105
+ > extends ToolbarItemFields<S> {
106
+ /**
107
+ * Build the dropdown for this target. May be sync or async. The host
108
+ * anchors the menu on the toolbar control (`align: "end"`, toggle on).
109
+ */
110
+ menu: (target: ToolbarItemContext[S]) => MenuEntry[] | Promise<MenuEntry[]>;
111
+ command?: undefined;
112
+ type?: undefined;
113
+ }
114
+
115
+ /**
116
+ * Shared placement fields for non-interactive toolbar chrome
117
+ * ({@link ToolbarSeparatorContribution} / {@link ToolbarSpacerContribution}).
118
+ *
119
+ * @category Registration
120
+ * @public
121
+ */
122
+ export interface ToolbarChromeFields<
123
+ S extends ToolbarSurface = ToolbarSurface,
124
+ > {
125
+ id: string;
126
+ surface: S;
127
+ order?: number;
128
+ when?: (ctx: ContextKeys, target: ToolbarItemContext[S]) => boolean;
129
+ }
130
+
131
+ /**
132
+ * A light vertical rule between toolbar controls. Softer than the Text |
133
+ * Preview pipe — host paints it with a low-opacity mix of toolbar text, not
134
+ * `--silo-color-toolbar-text-disabled`.
135
+ *
136
+ * @category Registration
137
+ * @public
138
+ */
139
+ export interface ToolbarSeparatorContribution<
140
+ S extends ToolbarSurface = ToolbarSurface,
141
+ > extends ToolbarChromeFields<S> {
142
+ type: "separator";
143
+ }
144
+
145
+ /**
146
+ * Gap size for {@link ToolbarSpacerContribution}. Maps to em of the toolbar
147
+ * font (`sm` 0.25em / `md` 0.5em / `lg` 0.75em).
148
+ *
149
+ * @category Registration
150
+ * @public
151
+ */
152
+ export type ToolbarSpacerSize = "sm" | "md" | "lg";
153
+
154
+ /**
155
+ * An empty gap between toolbar controls (no rule). Use to group without a
156
+ * hard split; prefer {@link ToolbarSeparatorContribution} when a hairline helps.
157
+ *
158
+ * @category Registration
159
+ * @public
160
+ */
161
+ export interface ToolbarSpacerContribution<
162
+ S extends ToolbarSurface = ToolbarSurface,
163
+ > extends ToolbarChromeFields<S> {
164
+ type: "spacer";
165
+ /** Default `"md"`. */
166
+ size?: ToolbarSpacerSize;
167
+ }
168
+
169
+ /**
170
+ * Adds a control or chrome element to the trailing cluster of a built-in
171
+ * editor or terminal toolbar. Register via
172
+ * {@link ExtensionContext.registerToolbarItem}.
173
+ *
174
+ * Interactive items set exactly one of
175
+ * {@link ToolbarCommandItemContribution.command | command} or
176
+ * {@link ToolbarMenuItemContribution.menu | menu}. Separators and spacers use
177
+ * `type: "separator" | "spacer"`.
178
+ *
179
+ * Contributions are independent of context-menu items — an extension may
180
+ * register either, both, or neither. Hosts only render items while that
181
+ * surface's breadcrumbs setting is on.
182
+ *
183
+ * @category Registration
184
+ * @public
185
+ */
186
+ export type ToolbarItemContribution<S extends ToolbarSurface = ToolbarSurface> =
187
+ | ToolbarCommandItemContribution<S>
188
+ | ToolbarMenuItemContribution<S>
189
+ | ToolbarSeparatorContribution<S>
190
+ | ToolbarSpacerContribution<S>;
package/src/types.ts CHANGED
@@ -47,6 +47,7 @@ import type {
47
47
  ExtensionStorage,
48
48
  ExtensionStorageScopes,
49
49
  } from "./extension-storage";
50
+ import type { ToolbarItemContribution, ToolbarSurface } from "./toolbar-items";
50
51
 
51
52
  /**
52
53
  * The teardown handle returned by every `register*` call on
@@ -630,6 +631,22 @@ export interface ExtensionContext {
630
631
  registerContextMenuItem<S extends MenuSurface>(
631
632
  item: ContextMenuContribution<S>,
632
633
  ): Disposable;
634
+ /**
635
+ * Register a {@link ToolbarItemContribution} (icon-only, text-only,
636
+ * icon+text, or dropdown) in the trailing cluster of an editor or terminal
637
+ * toolbar. Independent of {@link ExtensionContext.registerContextMenuItem}
638
+ * — register either, both, or neither. Hosts only show items while that
639
+ * surface's breadcrumbs setting is on. See
640
+ * {@link ExtensionContext.invalidateToolbarItems}.
641
+ */
642
+ registerToolbarItem<S extends ToolbarSurface>(
643
+ item: ToolbarItemContribution<S>,
644
+ ): Disposable;
645
+ /**
646
+ * Signal that toolbar-item `when` / `checked` data changed. Causes editor
647
+ * and terminal toolbars to re-query contributions and re-render.
648
+ */
649
+ invalidateToolbarItems(): void;
633
650
  /** Register a {@link Keybinding} (bind a shortcut to a command). */
634
651
  registerKeybinding(binding: Keybinding): Disposable;
635
652
  /** Register a {@link SidePanel} (a left/right column panel). */
@@ -1,5 +1,6 @@
1
1
  import type { Disposable } from "./types";
2
2
  import type { Workspace } from "./domain-types";
3
+ import type { Activity } from "./activity";
3
4
 
4
5
  // Re-export the workspace domain types so consumers can name them from the SDK.
5
6
  export type { Workspace, TerminalRecord } from "./domain-types";
@@ -14,8 +15,11 @@ export type { Workspace, TerminalRecord } from "./domain-types";
14
15
  export interface WorkspaceStatusRow {
15
16
  /** Stable key unique within this provider's results; used for reconciliation. */
16
17
  id: string;
17
- /** Semantic status dot shown to the left of the label. */
18
- status?: "ok" | "warn" | "busy" | "error";
18
+ /**
19
+ * Host-owned {@link Activity} glyph to the left of the label. Omit for the
20
+ * neutral gray fallback (same as today’s omitted `status`). See ADR 0030.
21
+ */
22
+ activity?: Activity;
19
23
  /** Short label — the host truncates with an ellipsis when space is tight. */
20
24
  label: string;
21
25
  /**
@@ -72,22 +76,23 @@ export interface WorkspaceBadge {
72
76
  /** Short text rendered inside the badge. */
73
77
  text: string;
74
78
  /**
75
- * CSS color applied to both the badge border and text. Falls back to the
76
- * muted text color (`--silo-color-text-lo`) when omitted.
79
+ * Solid chip fill color. Text paints white on top for contrast. Falls back
80
+ * to a muted solid background + hi text when omitted.
77
81
  */
78
82
  color?: string;
79
83
  }
80
84
 
81
85
  /**
82
- * A badge provider that contributes {@link WorkspaceBadge}s next to the
83
- * workspace name in the Workspaces side panel. Register via
84
- * {@link WorkspaceService.registerBadge}.
86
+ * A badge binder that contributes {@link WorkspaceBadge}s next to the
87
+ * workspace name in the Workspaces side panel. Prefer
88
+ * {@link WorkspaceService.bindBadge}; {@link WorkspaceService.registerBadge}
89
+ * remains as a deprecated alias.
85
90
  *
86
91
  * @category Consumer Services
87
92
  * @public
88
93
  */
89
94
  export interface WorkspaceBadgeProvider {
90
- /** Unique id for this provider — conventionally `"<extension-id>.badges"`. */
95
+ /** Unique id for this binder — conventionally `"<extension-id>.badges"`. */
91
96
  id: string;
92
97
  /**
93
98
  * Called synchronously for each workspace during render. Return an empty
@@ -97,15 +102,19 @@ export interface WorkspaceBadgeProvider {
97
102
  }
98
103
 
99
104
  /**
100
- * A status provider that contributes {@link WorkspaceStatusRow}s to
101
- * workspace rows in the Workspaces side panel. Register via
102
- * {@link WorkspaceService.registerStatus}.
105
+ * A status binder that contributes {@link WorkspaceStatusRow}s to
106
+ * workspace rows in the Workspaces side panel. Prefer
107
+ * {@link WorkspaceService.bindStatus}; {@link WorkspaceService.registerStatus}
108
+ * remains as a deprecated alias.
109
+ *
110
+ * Unlike CenterDock tab `bindIndicator` (single adornment | null), status
111
+ * and badge binders return an **array** — one projection may emit many rows.
103
112
  *
104
113
  * @category Consumer Services
105
114
  * @public
106
115
  */
107
116
  export interface WorkspaceStatusProvider {
108
- /** Unique id for this provider — conventionally `"<extension-id>.status"`. */
117
+ /** Unique id for this binder — conventionally `"<extension-id>.status"`. */
109
118
  id: string;
110
119
  /**
111
120
  * Called synchronously for each workspace during render. Return an empty
@@ -260,50 +269,62 @@ export interface WorkspaceService {
260
269
  delete(id: string): Promise<void>;
261
270
 
262
271
  /**
263
- * Register a status provider that contributes status rows to workspace
264
- * rows in the Workspaces side panel. Multiple providers may be registered;
265
- * their rows are concatenated in registration order. Returns a
266
- * {@link Disposable} that unregisters the provider.
272
+ * Imperatively adorn a workspace row with a status line. `row.id` is the
273
+ * adornment key for {@link WorkspaceService.clearStatus}.
274
+ */
275
+ setStatus(workspaceId: string, row: WorkspaceStatusRow): void;
276
+
277
+ /** Remove an imperative status row previously set via {@link WorkspaceService.setStatus}. */
278
+ clearStatus(workspaceId: string, rowId: string): void;
279
+
280
+ /**
281
+ * Keep a status projection in sync for every workspace. `provide` returns
282
+ * an array of rows (may be empty). Prefer this over repeatedly calling
283
+ * {@link WorkspaceService.setStatus}.
267
284
  *
268
285
  * @example
269
286
  * ```ts
270
287
  * ctx.subscriptions.push(
271
- * ctx.workspaces.registerStatus({
288
+ * ctx.workspaces.bindStatus({
272
289
  * id: "my-ext.status",
273
290
  * provide(workspaceId) {
274
291
  * const running = getRunningTasks(workspaceId);
275
- * return running.map(t => ({ id: t.id, status: "busy", label: t.name, startedAt: t.startedAt }));
292
+ * return running.map(t => ({ id: t.id, activity: "working", label: t.name, startedAt: t.startedAt }));
276
293
  * },
277
294
  * }),
278
- * ctx.workspaces.subscribeStatus(() => ctx.workspaces.invalidateStatus()),
279
295
  * );
280
296
  * ```
281
297
  */
298
+ bindStatus(binder: WorkspaceStatusProvider): Disposable;
299
+
300
+ /**
301
+ * @deprecated Prefer {@link WorkspaceService.bindStatus}.
302
+ */
282
303
  registerStatus(provider: WorkspaceStatusProvider): Disposable;
283
304
 
284
305
  /**
285
- * Concatenate all registered providers' rows for one workspace (in
286
- * registration order). Called synchronously during panel render — providers
287
- * must be fast and side-effect-free.
306
+ * Concatenate imperative rows and all binders' rows for one workspace (in
307
+ * binder registration order). Called synchronously during panel render —
308
+ * binders must be fast and side-effect-free.
288
309
  */
289
310
  getStatus(workspaceId: string): WorkspaceStatusRow[];
290
311
 
291
312
  /**
292
- * Signal that status data has changed. Fires all listeners registered
313
+ * Signal that binder data has changed. Fires all listeners registered
293
314
  * via {@link WorkspaceService.subscribeStatus}, causing the Workspaces
294
- * panel to re-query providers and re-render the status rows.
315
+ * panel to re-query binders and re-render the status rows.
295
316
  *
296
317
  * Call this after any mutation to the state your `provide` function reads.
318
+ * Imperative {@link WorkspaceService.setStatus} / {@link WorkspaceService.clearStatus}
319
+ * already notify listeners.
297
320
  */
298
321
  invalidateStatus(): void;
299
322
 
300
323
  /**
301
324
  * Subscribe to status invalidations. The listener is called whenever
302
- * {@link WorkspaceService.invalidateStatus} is invoked. Returns a
303
- * {@link Disposable} that cancels the subscription.
304
- *
305
- * The Workspaces panel subscribes internally; extensions may also subscribe
306
- * to observe invalidations.
325
+ * {@link WorkspaceService.invalidateStatus} is invoked (or imperative
326
+ * set/clear runs). Returns a {@link Disposable} that cancels the
327
+ * subscription.
307
328
  */
308
329
  subscribeStatus(listener: () => void): Disposable;
309
330
 
@@ -374,15 +395,22 @@ export interface WorkspaceService {
374
395
  registerPropertyPage(page: WorkspacePropertyPage): Disposable;
375
396
 
376
397
  /**
377
- * Register a badge provider that contributes {@link WorkspaceBadge}s next to
378
- * the workspace name in the Workspaces side panel. Multiple providers may be
379
- * registered; their badges are concatenated in registration order. Returns a
380
- * {@link Disposable} that unregisters the provider.
398
+ * Imperatively adorn a workspace name with a badge. `badge.id` is the
399
+ * adornment key for {@link WorkspaceService.clearBadge}.
400
+ */
401
+ setBadge(workspaceId: string, badge: WorkspaceBadge): void;
402
+
403
+ /** Remove an imperative badge previously set via {@link WorkspaceService.setBadge}. */
404
+ clearBadge(workspaceId: string, badgeId: string): void;
405
+
406
+ /**
407
+ * Keep a badge projection in sync for every workspace. `provide` returns
408
+ * an array of badges (may be empty).
381
409
  *
382
410
  * @example
383
411
  * ```ts
384
412
  * ctx.subscriptions.push(
385
- * ctx.workspaces.registerBadge({
413
+ * ctx.workspaces.bindBadge({
386
414
  * id: "my-ext.badges",
387
415
  * provide(workspaceId) {
388
416
  * const status = getStatus(workspaceId);
@@ -393,28 +421,27 @@ export interface WorkspaceService {
393
421
  * );
394
422
  * ```
395
423
  */
424
+ bindBadge(binder: WorkspaceBadgeProvider): Disposable;
425
+
426
+ /**
427
+ * @deprecated Prefer {@link WorkspaceService.bindBadge}.
428
+ */
396
429
  registerBadge(provider: WorkspaceBadgeProvider): Disposable;
397
430
 
398
431
  /**
399
- * Concatenate all registered providers' badges for one workspace (in
400
- * registration order). Called synchronously during panel render — providers
401
- * must be fast and side-effect-free.
432
+ * Concatenate imperative badges and all binders' badges for one workspace
433
+ * (in binder registration order).
402
434
  */
403
435
  getBadges(workspaceId: string): WorkspaceBadge[];
404
436
 
405
437
  /**
406
- * Signal that badge data has changed. Fires all listeners registered via
407
- * {@link WorkspaceService.subscribeBadges}, causing the Workspaces panel to
408
- * re-query providers and re-render the name row.
409
- *
410
- * Call this after any mutation to the state your `provide` function reads.
438
+ * Signal that badge binder data has changed. Imperative set/clear already
439
+ * notify listeners.
411
440
  */
412
441
  invalidateBadges(): void;
413
442
 
414
443
  /**
415
- * Subscribe to badge invalidations. The listener is called whenever
416
- * {@link WorkspaceService.invalidateBadges} is invoked. Returns a
417
- * {@link Disposable} that cancels the subscription.
444
+ * Subscribe to badge invalidations.
418
445
  */
419
446
  subscribeBadges(listener: () => void): Disposable;
420
447
  }