@silo-code/sdk 0.20.0 → 0.22.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 (62) hide show
  1. package/dist/context-keys.d.ts +18 -3
  2. package/dist/context-keys.d.ts.map +1 -1
  3. package/dist/domain-types.d.ts +8 -32
  4. package/dist/domain-types.d.ts.map +1 -1
  5. package/dist/editor-service.d.ts +135 -3
  6. package/dist/editor-service.d.ts.map +1 -1
  7. package/dist/event.d.ts +28 -0
  8. package/dist/event.d.ts.map +1 -0
  9. package/dist/event.js +2 -0
  10. package/dist/event.js.map +1 -0
  11. package/dist/extension-storage.d.ts +7 -3
  12. package/dist/extension-storage.d.ts.map +1 -1
  13. package/dist/file-service.d.ts +48 -4
  14. package/dist/file-service.d.ts.map +1 -1
  15. package/dist/index.d.ts +17 -8
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +17 -5
  18. package/dist/index.js.map +1 -1
  19. package/dist/layout-service.d.ts +13 -13
  20. package/dist/layout-service.d.ts.map +1 -1
  21. package/dist/network-service.d.ts +70 -4
  22. package/dist/network-service.d.ts.map +1 -1
  23. package/dist/network-service.js +35 -1
  24. package/dist/network-service.js.map +1 -1
  25. package/dist/path.d.ts +62 -0
  26. package/dist/path.d.ts.map +1 -0
  27. package/dist/path.js +150 -0
  28. package/dist/path.js.map +1 -0
  29. package/dist/process-service.d.ts +24 -4
  30. package/dist/process-service.d.ts.map +1 -1
  31. package/dist/processes-service.d.ts +1 -1
  32. package/dist/search-service.d.ts +11 -0
  33. package/dist/search-service.d.ts.map +1 -1
  34. package/dist/system-service.d.ts +2 -2
  35. package/dist/terminal-service.d.ts +66 -0
  36. package/dist/terminal-service.d.ts.map +1 -1
  37. package/dist/theme-service.d.ts +23 -7
  38. package/dist/theme-service.d.ts.map +1 -1
  39. package/dist/types.d.ts +102 -24
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/workspace-service.d.ts +13 -0
  42. package/dist/workspace-service.d.ts.map +1 -1
  43. package/package.json +2 -4
  44. package/src/context-keys.ts +18 -3
  45. package/src/domain-types.ts +8 -29
  46. package/src/editor-service.ts +141 -5
  47. package/src/event.ts +28 -0
  48. package/src/extension-storage.ts +8 -3
  49. package/src/file-service.ts +49 -4
  50. package/src/index.ts +31 -6
  51. package/src/layout-service.ts +15 -13
  52. package/src/network-service.ts +85 -4
  53. package/src/path.test.ts +135 -0
  54. package/src/path.ts +188 -0
  55. package/src/process-service.ts +24 -4
  56. package/src/processes-service.ts +1 -1
  57. package/src/search-service.ts +11 -0
  58. package/src/system-service.ts +2 -2
  59. package/src/terminal-service.ts +71 -0
  60. package/src/theme-service.ts +23 -7
  61. package/src/types.ts +104 -25
  62. package/src/workspace-service.ts +13 -0
@@ -67,6 +67,24 @@ export interface ProcessExecOptions {
67
67
  * {@link Permission}. First-party (bundled) extensions are unscoped.
68
68
  */
69
69
  cwd?: string;
70
+ /**
71
+ * Extra environment variables, **merged over** the host's environment (the
72
+ * command inherits the host env; these keys add to or override it). Use it to
73
+ * set things like `GIT_PAGER=cat` or a locale without clobbering `PATH`.
74
+ */
75
+ env?: Record<string, string>;
76
+ /**
77
+ * Kill the process and reject after this many milliseconds. The whole process
78
+ * group is terminated (not just the direct child), so shell wrappers don't
79
+ * leak orphans. The rejection is an `Error` whose `name` is `"AbortError"`.
80
+ */
81
+ timeoutMs?: number;
82
+ /**
83
+ * Abort handle. Aborting kills the process (and its group) and rejects the
84
+ * `exec` promise with an `Error` whose `name` is `"AbortError"` — the same
85
+ * shape as a `timeoutMs` expiry, so callers branch on `err.name`.
86
+ */
87
+ signal?: AbortSignal;
70
88
  }
71
89
 
72
90
  /**
@@ -116,10 +134,12 @@ export interface ProcessService {
116
134
  * interactive sessions instead.
117
135
  *
118
136
  * Runs **off the UI thread**, so a slow or network-bound command never
119
- * stutters the app. The returned promise rejects only if the process could
120
- * not be spawned (e.g. the command was not found); a command that runs but
121
- * exits non-zero **resolves** check {@link ProcessExecResult.code} and
122
- * {@link ProcessExecResult.stderr}.
137
+ * stutters the app. The returned promise rejects if the process could not be
138
+ * spawned (e.g. the command was not found), or if a
139
+ * {@link ProcessExecOptions.timeoutMs | timeout} / {@link ProcessExecOptions.signal | abort}
140
+ * fires (an `Error` with `name === "AbortError"`); a command that runs to
141
+ * completion but exits non-zero **resolves** — check
142
+ * {@link ProcessExecResult.code} and {@link ProcessExecResult.stderr}.
123
143
  *
124
144
  * @param command - Executable to run (resolved via `PATH`), e.g. `"git"`.
125
145
  * @param args - Arguments passed verbatim — not shell-interpreted, so no
@@ -88,7 +88,7 @@ export interface ProcessInfo {
88
88
  * // Notify when all agents in the workspace are idle.
89
89
  * const sub = ctx.processes.subscribe((procs) => {
90
90
  * const allIdle = procs.every((p) => p.atPrompt);
91
- * if (allIdle) ctx.ui.notify({ title: "All agents finished" });
91
+ * if (allIdle) ctx.ui.notify("info", "All agents finished");
92
92
  * });
93
93
  * ctx.subscriptions.push(sub);
94
94
  *
@@ -43,6 +43,17 @@ export interface SearchOptions {
43
43
  * is hit, the search stops early and {@link SearchResponse.truncated} is true.
44
44
  */
45
45
  maxResults?: number;
46
+ /**
47
+ * Cancel the search. When the signal aborts, the promise returned by
48
+ * {@link SearchService.search} rejects with an `Error` whose `name` is
49
+ * `"AbortError"` (the `fetch` convention — branch on `err.name`).
50
+ *
51
+ * Cancellation is observable immediately, but the native search may still run
52
+ * to completion in the background — its result is simply discarded. Use this
53
+ * to abandon a stale query (e.g. superseded by the next keystroke) rather than
54
+ * to reclaim native CPU the instant you abort.
55
+ */
56
+ signal?: AbortSignal;
46
57
  }
47
58
 
48
59
  /**
@@ -53,13 +53,13 @@ export interface SystemInfo {
53
53
  * ctx.subscriptions.push(
54
54
  * ctx.registerCommand({
55
55
  * id: "my.reveal-in-finder",
56
- * title: "Reveal in Finder",
56
+ * label: "Reveal in Finder",
57
57
  * run() { ... },
58
58
  * }),
59
59
  * );
60
60
  * }
61
61
  *
62
- * ctx.ui.notify({ title: `Running Silo ${siloVersion} on ${os}/${arch}` });
62
+ * ctx.ui.notify("info", `Running Silo ${siloVersion} on ${os}/${arch}`);
63
63
  * },
64
64
  * };
65
65
  * ```
@@ -98,11 +98,51 @@ export interface TerminalService {
98
98
  * Open a new terminal in a workspace (defaults to the active one). Returns the
99
99
  * created {@link TerminalRecord}; the PTY session spawns lazily when its tab
100
100
  * mounts.
101
+ *
102
+ * Returns `undefined` only when `input.workspaceId` is not given and there is
103
+ * no active workspace at the time of the call — in normal use this does not
104
+ * happen because activating any workspace happens before extensions run.
101
105
  */
102
106
  create(input?: CreateTerminalInput): TerminalRecord | undefined;
103
107
  /** Close and kill every terminal in a workspace (e.g. on workspace delete). */
104
108
  closeWorkspace(workspaceId: string): void;
105
109
 
110
+ /**
111
+ * Write text to a terminal's PTY as if the user typed it. By default a
112
+ * carriage return is appended so the line executes; pass `addNewline: false`
113
+ * to stage text without running it.
114
+ *
115
+ * Works even if the terminal tab has never been shown: the PTY spawns lazily
116
+ * on first mount, and `sendText` force-spawns it on demand (a later mount then
117
+ * attaches to that same session). No-op for an unknown `terminalId`.
118
+ *
119
+ * @param terminalId - The {@link TerminalRecord.id} to write to.
120
+ * @param text - The text to send.
121
+ * @param addNewline - Append a carriage return to execute. Defaults to `true`.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const term = ctx.terminals.create({ cwd: workspaceFolder });
126
+ * if (term) ctx.terminals.sendText(term.id, "npm run build");
127
+ * ```
128
+ */
129
+ sendText(terminalId: string, text: string, addNewline?: boolean): void;
130
+
131
+ /**
132
+ * Close one terminal tab and kill its PTY session. No-op if the id is unknown.
133
+ * To reap every terminal in a workspace at once use
134
+ * {@link TerminalService.closeWorkspace}.
135
+ */
136
+ close(terminalId: string): void;
137
+
138
+ /**
139
+ * Set a terminal's user-facing name ({@link TerminalRecord.customName}),
140
+ * shown on its tab and persisted across restarts. Passing an empty string
141
+ * clears the custom name, letting the PTY-derived title take over again.
142
+ * No-op for an unknown `terminalId`.
143
+ */
144
+ rename(terminalId: string, name: string): void;
145
+
106
146
  /**
107
147
  * Switch to the workspace containing this terminal and activate its tab in
108
148
  * the center dock. No-ops if the terminal id is unknown.
@@ -168,4 +208,35 @@ export interface TerminalService {
168
208
  terminalId: string,
169
209
  handler: (event: OscEvent) => void,
170
210
  ): Disposable;
211
+
212
+ /**
213
+ * The record id of the terminal tab that is currently active in the active
214
+ * workspace's center dock, or `null` when an editor tab (or nothing) is
215
+ * active. "Active" is the dock's single active panel — the tab the user is
216
+ * looking at and typing into — so a terminal merely visible in a non-active
217
+ * split does not count.
218
+ */
219
+ getActive(): string | null;
220
+
221
+ /**
222
+ * Subscribe to active-terminal changes. The listener receives the terminal
223
+ * record id whenever a terminal tab becomes the active center-dock panel,
224
+ * and `null` when activation moves elsewhere (an editor tab, or no panel —
225
+ * including transiently during a workspace switch, before the incoming
226
+ * workspace's active tab is published).
227
+ *
228
+ * Fires on tab activation, group activation, and workspace switches.
229
+ * Returns a {@link Disposable} that cancels the subscription.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * // Clear a "needs attention" marker once the user views the terminal.
234
+ * ctx.subscriptions.push(
235
+ * ctx.terminals.subscribeActive((terminalId) => {
236
+ * if (terminalId) attention.delete(terminalId);
237
+ * }),
238
+ * );
239
+ * ```
240
+ */
241
+ subscribeActive(listener: (terminalId: string | null) => void): Disposable;
171
242
  }
@@ -15,11 +15,11 @@ export type {
15
15
  } from "./domain-types";
16
16
 
17
17
  /**
18
- * A selectable theme contributed via
19
- * {@link ExtensionContext.registerThemePreset}. Built-in presets (Tokyo Night,
20
- * Solarized Light, Gruvbox Dark, …) are registered by the `theme-presets`
21
- * extension; core ships only Dark and Light. A preset's {@link ThemePreset.vars}
22
- * are injected as CSS custom properties when it is the active theme.
18
+ * A selectable theme contributed via {@link ThemeService.registerPreset}.
19
+ * Built-in presets (Tokyo Night, Solarized Light, Gruvbox Dark, …) are
20
+ * registered by the `theme-presets` extension; core ships only Dark and Light.
21
+ * A preset's {@link ThemePreset.vars} are injected as CSS custom properties
22
+ * when it is the active theme.
23
23
  *
24
24
  * @category Registration
25
25
  * @public
@@ -74,8 +74,7 @@ export interface ThemeState {
74
74
  * Read via {@link ThemeService.getState | getState} /
75
75
  * {@link ThemeService.subscribe | subscribe} (e.g. with `useSyncExternalStore`);
76
76
  * drive via {@link ThemeService.setActive | setActive} and the custom-theme
77
- * methods. Contributing a *new* preset is a separate concern —
78
- * {@link ExtensionContext.registerThemePreset}.
77
+ * methods. Contribute a new preset via {@link ThemeService.registerPreset}.
79
78
  *
80
79
  * @category Consumer Services
81
80
  * @public
@@ -99,4 +98,21 @@ export interface ThemeService {
99
98
  exportTheme(theme: CustomTheme): ThemeExport;
100
99
  /** Validate/parse imported JSON into a custom theme (assigns a fresh id). */
101
100
  importTheme(data: unknown): CustomTheme;
101
+ /**
102
+ * Register a {@link ThemePreset} (a selectable theme in the picker). The
103
+ * preset appears immediately and is removed when the returned
104
+ * {@link Disposable} is disposed (typically when the extension deactivates).
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * ctx.theme.registerPreset({
109
+ * id: "my-theme",
110
+ * name: "My Theme",
111
+ * base: "dark",
112
+ * colorScheme: "dark",
113
+ * vars: { "--silo-color-bg": "#1a1a2e" },
114
+ * });
115
+ * ```
116
+ */
117
+ registerPreset(preset: ThemePreset): Disposable;
102
118
  }
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
- * The Silo extension API — types only.
2
+ * The Silo extension API — types-first contract plus a small set of blessed
3
+ * runtime helpers.
3
4
  *
4
5
  * Everything an extension author can see at compile time lives here (plus the
5
6
  * service interfaces in the sibling `*-service` files, which this module
@@ -8,6 +9,12 @@
8
9
  * never import the host. This is the VS Code (`vscode.d.ts`) / Obsidian
9
10
  * (`obsidian-api`) model.
10
11
  *
12
+ * **Runtime exports:** the SDK also ships a small set of blessed runtime
13
+ * helpers (`Tooltip`, `useFocusGroup`, `useServiceState`, `DND_MIME`,
14
+ * `PathDeniedError`, `NetworkError`) and peer-depends on React 19. Changes to
15
+ * these can be breaking even when the types are unchanged — treat them with
16
+ * the same care as the type surface.
17
+ *
11
18
  * Start with {@link Extension} (the unit you export) and
12
19
  * {@link ExtensionContext} (the `ctx` the host hands you).
13
20
  *
@@ -18,7 +25,6 @@
18
25
  * @packageDocumentation
19
26
  */
20
27
  import type React from "react";
21
- import type { IDockviewPanelProps } from "dockview";
22
28
  import type { ContextKeys } from "./context-keys";
23
29
  import type { WorkspaceService } from "./workspace-service";
24
30
  import type { EditorService } from "./editor-service";
@@ -54,26 +60,70 @@ export interface Disposable {
54
60
  }
55
61
 
56
62
  /**
57
- * The dockview panel API handed to a {@link DockPanelKind} component used to
58
- * drive the panel's own tab (title, close, focus). Re-exported from `dockview`
59
- * so extensions don't take a direct dependency on it.
63
+ * The panel API handed to a {@link DockPanelKind} component. Use these methods
64
+ * to drive the panel's own tab (title, close, focus) and update its stored
65
+ * parameters. The host provides the implementation; extensions never construct
66
+ * this object directly.
60
67
  *
61
68
  * @category Core Types
62
69
  * @public
63
70
  */
64
- export type DockPanelApi = IDockviewPanelProps["api"];
71
+ export interface DockPanelApi {
72
+ /** Update the title shown in the panel's tab. */
73
+ setTitle(title: string): void;
74
+ /** Programmatically close this panel. */
75
+ close(): void;
76
+ /** Bring this panel to focus (make it the active panel in its group). */
77
+ setActive(): void;
78
+ /** `true` while this panel is the active one in its dock group. */
79
+ readonly isActive: boolean;
80
+ /**
81
+ * Subscribe to active-state transitions. The listener is called whenever
82
+ * the panel gains or loses active status, with an event carrying the new
83
+ * state. Returns a {@link Disposable} that cancels the subscription.
84
+ */
85
+ onDidActiveChange(
86
+ listener: (event: { readonly isActive: boolean }) => void,
87
+ ): Disposable;
88
+ /**
89
+ * `true` while this panel is visible — its tab is the selected one in its
90
+ * group. Distinct from {@link DockPanelApi.isActive | isActive}: with split
91
+ * groups, every group's selected tab is visible but only one panel in the
92
+ * whole dock is active.
93
+ */
94
+ readonly isVisible: boolean;
95
+ /**
96
+ * Subscribe to visibility transitions (the panel's tab being selected or
97
+ * deselected in its group). Use to pause expensive work while hidden, or to
98
+ * re-measure on reveal (e.g. the terminal refits xterm when its tab becomes
99
+ * visible again). Returns a {@link Disposable} that cancels the subscription.
100
+ */
101
+ onDidVisibilityChange(
102
+ listener: (event: { readonly isVisible: boolean }) => void,
103
+ ): Disposable;
104
+ /**
105
+ * Shallow-merge `params` into this panel's stored parameters. Keys absent
106
+ * from `params` are left unchanged. Useful for keeping tabs-serializable
107
+ * state (e.g. the open URL in a web-viewer panel) consistent with the UI.
108
+ */
109
+ updateParameters(params: object): void;
110
+ }
65
111
 
66
112
  /**
67
113
  * Props handed to a {@link DockPanelKind} component. Use this type to annotate
68
- * your component instead of importing `IDockviewPanelProps` from `dockview`
69
- * directly — the SDK wraps it so extensions remain insulated from dockview
70
- * version changes. The optional generic `T` narrows the shape of `params`.
114
+ * your component instead of importing from the underlying dock framework
115
+ * directly — the SDK owns this surface so extensions remain insulated from
116
+ * host implementation details. The optional generic `T` narrows `params`.
71
117
  *
72
118
  * @category Core Types
73
119
  * @public
74
120
  */
75
- export type DockPanelProps<T extends object = Record<string, unknown>> =
76
- IDockviewPanelProps<T>;
121
+ export interface DockPanelProps<T extends object = Record<string, unknown>> {
122
+ /** The panel API — drives the tab (title, close, focus, params). */
123
+ api: DockPanelApi;
124
+ /** Serializable parameters forwarded to the panel at open time. */
125
+ params: T;
126
+ }
77
127
 
78
128
  /**
79
129
  * Props passed to an {@link Editor} component. An editor renders the contents of
@@ -164,10 +214,10 @@ export interface NewFileTemplate {
164
214
 
165
215
  /**
166
216
  * Declarative metadata about a file extension — the open-ended counterpart to
167
- * Viewer (which is purely a renderer). A single source of truth that "New File"
168
- * surfaces (and, later, tab/explorer icons) can enumerate. Registering a
169
- * FileType does not register a viewer; the two are matched independently by
170
- * extension at dispatch time.
217
+ * {@link Editor} (which is purely a presenter/renderer). A single source of
218
+ * truth that "New File" surfaces (and, later, tab/explorer icons) can
219
+ * enumerate. Registering a FileType does not register an Editor; the two are
220
+ * matched independently by extension at dispatch time.
171
221
  *
172
222
  * @category Registration
173
223
  * @public
@@ -196,8 +246,15 @@ export interface Command {
196
246
  id: string;
197
247
  /** Human-readable label (shown where the command surfaces in UI). */
198
248
  label: string;
199
- /** The action. Runs synchronously; do async work inside if needed. */
200
- run: () => void;
249
+ /**
250
+ * The action. May accept arguments passed through from
251
+ * {@link ExtensionContext.executeCommand} and may return a value (sync or
252
+ * async); `executeCommand` resolves with whatever this returns.
253
+ *
254
+ * Zero-argument, void-returning commands are still valid — `() => void`
255
+ * satisfies this type, so existing registrations compile unchanged.
256
+ */
257
+ run: (...args: unknown[]) => unknown | Promise<unknown>;
201
258
  }
202
259
 
203
260
  /**
@@ -325,16 +382,19 @@ export interface SidePanel {
325
382
 
326
383
  /**
327
384
  * Registers a kind of dock panel (a tab that can live in the center dock area,
328
- * e.g. the terminal). Workspaces open panels of registered kinds by id.
385
+ * e.g. the terminal). Workspaces open panels of registered kinds by id. The
386
+ * optional generic `T` is the shape of the params this kind's panels are
387
+ * opened with — annotate your component with `DockPanelProps<T>` and
388
+ * {@link ExtensionContext.registerDockPanelKind} infers it, no casts needed.
329
389
  *
330
390
  * @category Registration
331
391
  * @public
332
392
  */
333
- export interface DockPanelKind {
393
+ export interface DockPanelKind<T extends object = Record<string, unknown>> {
334
394
  /** Unique id for this panel kind. */
335
395
  id: string;
336
- /** The React component; receives the raw dockview panel props. */
337
- component: React.ComponentType<IDockviewPanelProps>;
396
+ /** The React component that renders this panel; receives {@link DockPanelProps}. */
397
+ component: React.ComponentType<DockPanelProps<T>>;
338
398
  /**
339
399
  * When set, this kind appears as an entry in the center dock's **+** add
340
400
  * menu (the per-group header button). Omit to keep the kind internal.
@@ -464,20 +524,39 @@ export interface ExtensionContext {
464
524
  registerKeybinding(binding: Keybinding): Disposable;
465
525
  /** Register a {@link SidePanel} (a left/right column panel). */
466
526
  registerSidePanel(panel: SidePanel): Disposable;
467
- /** Register a {@link DockPanelKind} (a center-dock tab kind). */
468
- registerDockPanelKind(kind: DockPanelKind): Disposable;
527
+ /**
528
+ * Register a {@link DockPanelKind} (a center-dock tab kind). The params
529
+ * generic `T` is inferred from the component's {@link DockPanelProps}
530
+ * annotation, so kinds with typed params register without casts.
531
+ */
532
+ registerDockPanelKind<T extends object = Record<string, unknown>>(
533
+ kind: DockPanelKind<T>,
534
+ ): Disposable;
469
535
  /** Register a {@link StatusItem} (a status-bar widget). */
470
536
  registerStatusItem(item: StatusItem): Disposable;
471
537
  /** Register a {@link SettingsPage} (a page in the Settings dialog). */
472
538
  registerSettingsPage(page: SettingsPage): Disposable;
473
- /** Register a {@link ThemePreset} (a selectable theme in the picker). */
539
+ /**
540
+ * Register a {@link ThemePreset} (a selectable theme in the picker).
541
+ * @deprecated Use {@link ThemeService.registerPreset | ctx.theme.registerPreset()} instead.
542
+ * This method will be removed in a future release.
543
+ */
474
544
  registerThemePreset(preset: ThemePreset): Disposable;
475
545
  /**
476
546
  * Invoke a registered command by id — including commands contributed by
477
547
  * other extensions. The minimal "operate" primitive; pairs with the typed
478
548
  * services for read access.
549
+ *
550
+ * Optional positional `args` are forwarded to the command's
551
+ * {@link Command.run} function. The returned `Promise` resolves with the
552
+ * command's return value, or rejects if the command throws, is async and
553
+ * rejects, or the id is not registered. Sync commands dispatch synchronously
554
+ * before the promise settles, so callers that read state the command mutates
555
+ * immediately after `await executeCommand(…)` see the updated state.
556
+ *
557
+ * @typeParam T - Expected return type of the command (defaults to `unknown`).
479
558
  */
480
- executeCommand(id: string): void;
559
+ executeCommand<T = unknown>(id: string, ...args: unknown[]): Promise<T>;
481
560
  /**
482
561
  * Consumer API for driving workspace state — create, rename, reorder,
483
562
  * activate, soft close/reopen, and hard delete. Subscribe to a frozen
@@ -170,6 +170,12 @@ export interface WorkspaceService {
170
170
  createFromFolderPicker(): Promise<Workspace | null>;
171
171
  create(input: CreateWorkspaceInput): Workspace;
172
172
  rename(id: string, name: string): void;
173
+ /**
174
+ * Move a workspace to a new position relative to a reference workspace.
175
+ * @param from - Id of the workspace being dragged / moved.
176
+ * @param to - Id of the reference workspace (the insertion anchor).
177
+ * @param position - Whether to place `from` before or after `to`.
178
+ */
173
179
  reorder(from: string, to: string, position: "before" | "after"): void;
174
180
  /** Activate (and reopen if closed). */
175
181
  activate(id: string): void;
@@ -263,6 +269,13 @@ export interface WorkspaceService {
263
269
  *
264
270
  * The Workspaces panel subscribes internally to re-render when providers
265
271
  * are added or removed.
272
+ *
273
+ * **No `invalidateSection` by design.** Unlike status rows and badges, a
274
+ * section is a live React component that re-renders on its own internal or
275
+ * context-driven state changes — it is not a snapshot returned from a
276
+ * `provide()` call, so there is nothing for the host to re-query. If a
277
+ * section needs to trigger a full workspace-panel refresh (rare), it should
278
+ * update its own state directly.
266
279
  */
267
280
  subscribeSection(listener: () => void): Disposable;
268
281