@silo-code/sdk 0.6.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.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/context-keys.d.ts +19 -0
- package/dist/context-keys.d.ts.map +1 -0
- package/dist/context-keys.js +2 -0
- package/dist/context-keys.js.map +1 -0
- package/dist/dnd-service.d.ts +140 -0
- package/dist/dnd-service.d.ts.map +1 -0
- package/dist/dnd-service.js +17 -0
- package/dist/dnd-service.js.map +1 -0
- package/dist/domain-types.d.ts +237 -0
- package/dist/domain-types.d.ts.map +1 -0
- package/dist/domain-types.js +11 -0
- package/dist/domain-types.js.map +1 -0
- package/dist/editor-service.d.ts +175 -0
- package/dist/editor-service.d.ts.map +1 -0
- package/dist/editor-service.js +2 -0
- package/dist/editor-service.js.map +1 -0
- package/dist/extension-storage.d.ts +26 -0
- package/dist/extension-storage.d.ts.map +1 -0
- package/dist/extension-storage.js +2 -0
- package/dist/extension-storage.js.map +1 -0
- package/dist/file-service.d.ts +84 -0
- package/dist/file-service.d.ts.map +1 -0
- package/dist/file-service.js +2 -0
- package/dist/file-service.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/layout-service.d.ts +46 -0
- package/dist/layout-service.d.ts.map +1 -0
- package/dist/layout-service.js +2 -0
- package/dist/layout-service.js.map +1 -0
- package/dist/permissions.d.ts +41 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +40 -0
- package/dist/permissions.js.map +1 -0
- package/dist/process-service.d.ts +132 -0
- package/dist/process-service.d.ts.map +1 -0
- package/dist/process-service.js +2 -0
- package/dist/process-service.js.map +1 -0
- package/dist/terminal-service.d.ts +38 -0
- package/dist/terminal-service.d.ts.map +1 -0
- package/dist/terminal-service.js +2 -0
- package/dist/terminal-service.js.map +1 -0
- package/dist/theme-service.d.ts +87 -0
- package/dist/theme-service.d.ts.map +1 -0
- package/dist/theme-service.js +2 -0
- package/dist/theme-service.js.map +1 -0
- package/dist/types.d.ts +495 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui-service.d.ts +469 -0
- package/dist/ui-service.d.ts.map +1 -0
- package/dist/ui-service.js +2 -0
- package/dist/ui-service.js.map +1 -0
- package/dist/use-focus-group.d.ts +202 -0
- package/dist/use-focus-group.d.ts.map +1 -0
- package/dist/use-focus-group.js +236 -0
- package/dist/use-focus-group.js.map +1 -0
- package/dist/use-service-state.d.ts +36 -0
- package/dist/use-service-state.d.ts.map +1 -0
- package/dist/use-service-state.js +25 -0
- package/dist/use-service-state.js.map +1 -0
- package/dist/workspace-service.d.ts +72 -0
- package/dist/workspace-service.d.ts.map +1 -0
- package/dist/workspace-service.js +2 -0
- package/dist/workspace-service.js.map +1 -0
- package/package.json +54 -0
- package/src/context-keys.ts +18 -0
- package/src/dnd-service.ts +151 -0
- package/src/domain-types.ts +252 -0
- package/src/editor-service.ts +196 -0
- package/src/extension-storage.ts +25 -0
- package/src/file-service.ts +90 -0
- package/src/index.ts +151 -0
- package/src/layout-service.ts +49 -0
- package/src/permissions.ts +55 -0
- package/src/process-service.ts +143 -0
- package/src/terminal-service.ts +41 -0
- package/src/theme-service.ts +102 -0
- package/src/types.ts +513 -0
- package/src/ui-service.ts +487 -0
- package/src/use-focus-group.test.ts +168 -0
- package/src/use-focus-group.ts +382 -0
- package/src/use-service-state.ts +43 -0
- package/src/workspace-service.ts +76 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public Silo extension API surface — the single curated entry point an
|
|
3
|
+
* extension author imports from. This is the seed of the future `@silo-code/sdk`
|
|
4
|
+
* package: it re-exports **only** the blessed, permanently supported types.
|
|
5
|
+
* Anything not re-exported here is host-internal and may change without notice.
|
|
6
|
+
*
|
|
7
|
+
* It is also the entry point the API-reference generator (TypeDoc) reads, so
|
|
8
|
+
* the published reference is exactly this surface — no more, no less.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Core contract + contribution-point types.
|
|
14
|
+
export type {
|
|
15
|
+
Disposable,
|
|
16
|
+
DockPanelApi,
|
|
17
|
+
EditorProps,
|
|
18
|
+
EditorCapabilities,
|
|
19
|
+
Editor,
|
|
20
|
+
NewFileTemplate,
|
|
21
|
+
FileType,
|
|
22
|
+
Command,
|
|
23
|
+
MenuId,
|
|
24
|
+
MenuItemContribution,
|
|
25
|
+
Keybinding,
|
|
26
|
+
SidePanelProps,
|
|
27
|
+
SidePanel,
|
|
28
|
+
DockPanelKind,
|
|
29
|
+
StatusItem,
|
|
30
|
+
SettingsPage,
|
|
31
|
+
ExtensionContext,
|
|
32
|
+
Extension,
|
|
33
|
+
ExtensionHandle,
|
|
34
|
+
} from "./types";
|
|
35
|
+
|
|
36
|
+
// Public domain types (the persisted shapes surfaced through the services).
|
|
37
|
+
// TerminalKind/TerminalRecord and the theme types are re-exported via their
|
|
38
|
+
// service modules below; these are the remaining ones consumers can name.
|
|
39
|
+
export type {
|
|
40
|
+
Workspace,
|
|
41
|
+
EditorMode,
|
|
42
|
+
EditorRecord,
|
|
43
|
+
SidePanelSlot,
|
|
44
|
+
} from "./domain-types";
|
|
45
|
+
|
|
46
|
+
// Consumer services exposed on the ExtensionContext.
|
|
47
|
+
export type {
|
|
48
|
+
WorkspaceService,
|
|
49
|
+
WorkspaceState,
|
|
50
|
+
CreateWorkspaceInput,
|
|
51
|
+
} from "./workspace-service";
|
|
52
|
+
export type {
|
|
53
|
+
EditorService,
|
|
54
|
+
EditorSaveHandlers,
|
|
55
|
+
OpenFileOptions,
|
|
56
|
+
EditorViewInfo,
|
|
57
|
+
OpenDiffSpec,
|
|
58
|
+
DiffContent,
|
|
59
|
+
DiffContentRequest,
|
|
60
|
+
DiffContentProvider,
|
|
61
|
+
} from "./editor-service";
|
|
62
|
+
export type {
|
|
63
|
+
LayoutService,
|
|
64
|
+
LayoutState,
|
|
65
|
+
SidePanelColumnState,
|
|
66
|
+
SideLocation,
|
|
67
|
+
} from "./layout-service";
|
|
68
|
+
export type {
|
|
69
|
+
ProcessService,
|
|
70
|
+
ProcessSession,
|
|
71
|
+
ProcessSpawnOptions,
|
|
72
|
+
ProcessExecOptions,
|
|
73
|
+
ProcessExecResult,
|
|
74
|
+
} from "./process-service";
|
|
75
|
+
export type {
|
|
76
|
+
TerminalService,
|
|
77
|
+
CreateTerminalInput,
|
|
78
|
+
TerminalKind,
|
|
79
|
+
TerminalRecord,
|
|
80
|
+
} from "./terminal-service";
|
|
81
|
+
export type { FileService, FileMeta, FileChangeEvent } from "./file-service";
|
|
82
|
+
// The permission surface: the capability vocabulary an extension declares, and
|
|
83
|
+
// the error the host throws when an extension reaches outside the workspace
|
|
84
|
+
// without the matching grant. `PathDeniedError` is a class (a runtime value).
|
|
85
|
+
export type { Permission } from "./permissions";
|
|
86
|
+
export { PathDeniedError } from "./permissions";
|
|
87
|
+
// The theme domain: the consumer service + its state/resolve shapes, the
|
|
88
|
+
// `ThemePreset` contribution type (registered via ctx.registerThemePreset), and
|
|
89
|
+
// the underlying theme types (re-exported from ./domain-types via theme-service).
|
|
90
|
+
export type {
|
|
91
|
+
ThemeService,
|
|
92
|
+
ThemeState,
|
|
93
|
+
ThemePreset,
|
|
94
|
+
ResolvedTheme,
|
|
95
|
+
ThemeBase,
|
|
96
|
+
ThemeVars,
|
|
97
|
+
CustomTheme,
|
|
98
|
+
ThemeExport,
|
|
99
|
+
} from "./theme-service";
|
|
100
|
+
export type { ExtensionStorage } from "./extension-storage";
|
|
101
|
+
// The drag-and-drop domain: the consumer service + its payload/handler shapes.
|
|
102
|
+
// DND_MIME is a value (the well-known MIME vocabulary), so it's a runtime export.
|
|
103
|
+
export type {
|
|
104
|
+
DndService,
|
|
105
|
+
DndItem,
|
|
106
|
+
DndMime,
|
|
107
|
+
DragInit,
|
|
108
|
+
DndMode,
|
|
109
|
+
DropContext,
|
|
110
|
+
DropTargetHandlers,
|
|
111
|
+
} from "./dnd-service";
|
|
112
|
+
export { DND_MIME } from "./dnd-service";
|
|
113
|
+
// The user-interaction domain: native pickers + toast notifications + menus,
|
|
114
|
+
// plus the file-filter shape the pickers accept, the menu item/entry shapes
|
|
115
|
+
// `showMenu` accepts, and the modal-chrome options `showModal` accepts.
|
|
116
|
+
export type {
|
|
117
|
+
UiService,
|
|
118
|
+
FileFilter,
|
|
119
|
+
MenuItem,
|
|
120
|
+
MenuItemTrailing,
|
|
121
|
+
MenuSeparator,
|
|
122
|
+
MenuHeader,
|
|
123
|
+
MenuEntry,
|
|
124
|
+
ShowMenuOptions,
|
|
125
|
+
ConfirmOptions,
|
|
126
|
+
PromptOptions,
|
|
127
|
+
ModalOptions,
|
|
128
|
+
NotifyAction,
|
|
129
|
+
NotifyOptions,
|
|
130
|
+
} from "./ui-service";
|
|
131
|
+
|
|
132
|
+
// Context keys referenced by `when` predicates on menu items / keybindings.
|
|
133
|
+
export type { ContextKeys } from "./context-keys";
|
|
134
|
+
|
|
135
|
+
// Runtime helpers. The one blessed way for an extension to read a `ctx`
|
|
136
|
+
// service's reactive state in React — replaces hand-rolled useSyncExternalStore.
|
|
137
|
+
export { useServiceState } from "./use-service-state";
|
|
138
|
+
export type { ReactiveService } from "./use-service-state";
|
|
139
|
+
// Headless keyboard navigation for a focus group (list / menu / toolbar / …):
|
|
140
|
+
// one tab stop, arrow/Home/End movement, the WebKit-safe keyboard ring. The
|
|
141
|
+
// `focusGroupNextIndex` helper is the pure roving-index core for widgets that
|
|
142
|
+
// can't use the focus-driven hook (e.g. menus driven by a document listener).
|
|
143
|
+
export { useFocusGroup, focusGroupNextIndex } from "./use-focus-group";
|
|
144
|
+
export type {
|
|
145
|
+
FocusGroup,
|
|
146
|
+
FocusGroupOptions,
|
|
147
|
+
FocusGroupContainerProps,
|
|
148
|
+
FocusGroupItemProps,
|
|
149
|
+
FocusGroupOrientation,
|
|
150
|
+
FocusGroupNavQuery,
|
|
151
|
+
} from "./use-focus-group";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Disposable } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Which side column a layout operation targets.
|
|
5
|
+
*
|
|
6
|
+
* @category Consumer Services
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export type SideLocation = "left" | "right";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Collapse state for one side column.
|
|
13
|
+
*
|
|
14
|
+
* @category Consumer Services
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export interface SidePanelColumnState {
|
|
18
|
+
/** True when the column is collapsed (hidden). */
|
|
19
|
+
collapsed: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* An immutable view of side-column layout state.
|
|
24
|
+
*
|
|
25
|
+
* @category Consumer Services
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export interface LayoutState {
|
|
29
|
+
left: SidePanelColumnState;
|
|
30
|
+
right: SidePanelColumnState;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Consumer API for app layout, exposed as {@link ExtensionContext.layout}.
|
|
35
|
+
* Read side-panel collapse state and drive it.
|
|
36
|
+
*
|
|
37
|
+
* @category Consumer Services
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export interface LayoutService {
|
|
41
|
+
/** Current frozen layout state. */
|
|
42
|
+
getState(): LayoutState;
|
|
43
|
+
/** Subscribe to layout changes; dispose to stop. */
|
|
44
|
+
subscribe(listener: (s: LayoutState) => void): Disposable;
|
|
45
|
+
/** Toggle a side column between collapsed and expanded. */
|
|
46
|
+
toggleSidePanel(location: SideLocation): void;
|
|
47
|
+
/** Set a side column's collapsed state explicitly. */
|
|
48
|
+
setSidePanelCollapsed(location: SideLocation, collapsed: boolean): void;
|
|
49
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// The extension permission surface — what an extension may declare it needs
|
|
2
|
+
// (`silo.permissions` in its manifest) and the error the host throws when an
|
|
3
|
+
// extension reaches outside the workspace without the matching grant. See the
|
|
4
|
+
// "Permissions & access" guide and ADR 0015 (phased security model).
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A capability an extension declares in its manifest (`silo.permissions`) to
|
|
8
|
+
* request access **beyond the open workspace**. With none declared, an
|
|
9
|
+
* extension's {@link FileService} / {@link ProcessService} access is confined to
|
|
10
|
+
* the workspace folder(s); each permission lifts one part of that confinement,
|
|
11
|
+
* and the user consents to the set at install.
|
|
12
|
+
*
|
|
13
|
+
* - `fs:read` — read files outside the workspace.
|
|
14
|
+
* - `fs:write` — write files outside the workspace.
|
|
15
|
+
* - `process` — run commands with a working directory outside the workspace.
|
|
16
|
+
* - `network` — make outbound network requests. Declarative consent only until
|
|
17
|
+
* sandboxed execution lands (in-process code can reach the network directly);
|
|
18
|
+
* declare it so the capability is reviewable and shown at install.
|
|
19
|
+
*
|
|
20
|
+
* @category Extension Contract
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
export type Permission = "fs:read" | "fs:write" | "process" | "network";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Thrown by {@link FileService} and {@link ProcessService} when an extension
|
|
27
|
+
* touches a path — or runs a process with a working directory — outside the open
|
|
28
|
+
* workspace without the matching {@link Permission}. Catch it and degrade
|
|
29
|
+
* gracefully, the same way you'd handle a missing file:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* try {
|
|
33
|
+
* const text = await ctx.files.readText(path);
|
|
34
|
+
* } catch (err) {
|
|
35
|
+
* if (err instanceof PathDeniedError) showMessage("That file is outside the workspace.");
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @category Core Types
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export class PathDeniedError extends Error {
|
|
43
|
+
/** The offending path, exactly as the extension passed it. */
|
|
44
|
+
readonly path: string;
|
|
45
|
+
|
|
46
|
+
constructor(path: string, message?: string) {
|
|
47
|
+
super(message ?? `Path is outside the workspace: ${path}`);
|
|
48
|
+
this.name = "PathDeniedError";
|
|
49
|
+
this.path = path;
|
|
50
|
+
// Restore the prototype chain so `instanceof` works across the down-leveled
|
|
51
|
+
// class output the SDK ships (and across the host↔extension boundary, where
|
|
52
|
+
// there's a single shared SDK instance).
|
|
53
|
+
Object.setPrototypeOf(this, PathDeniedError.prototype);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Disposable } from "./types";
|
|
2
|
+
|
|
3
|
+
// `ctx.process` — persistent process / PTY sessions that survive app restarts.
|
|
4
|
+
// The core primitive under the terminal (and future task runners / REPLs).
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for spawning a process session. Today sessions are shell PTYs, so
|
|
8
|
+
* `cwd` is required (the webview has no ambient working directory).
|
|
9
|
+
*
|
|
10
|
+
* @category Consumer Services
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export interface ProcessSpawnOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Working directory the session starts in. Must resolve inside the open
|
|
16
|
+
* workspace unless the extension declared the `process` {@link Permission}
|
|
17
|
+
* (first-party extensions are unscoped); otherwise throws
|
|
18
|
+
* {@link PathDeniedError}.
|
|
19
|
+
*/
|
|
20
|
+
cwd: string;
|
|
21
|
+
/** Initial column count. */
|
|
22
|
+
cols?: number;
|
|
23
|
+
/** Initial row count. */
|
|
24
|
+
rows?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A live handle to one persistent process session, returned by
|
|
29
|
+
* {@link ProcessService.spawn} / {@link ProcessService.attach}. The underlying
|
|
30
|
+
* session **survives app restarts** — re-`attach` by {@link ProcessSession.id}
|
|
31
|
+
* to reconnect to a still-running session.
|
|
32
|
+
*
|
|
33
|
+
* @category Consumer Services
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
export interface ProcessSession {
|
|
37
|
+
/** Stable session id; pass to {@link ProcessService.attach} to reconnect. */
|
|
38
|
+
readonly id: string;
|
|
39
|
+
/** Write input to the session (e.g. keystrokes). */
|
|
40
|
+
write(data: string): void;
|
|
41
|
+
/** Notify the session its viewport size changed. */
|
|
42
|
+
resize(cols: number, rows: number): void;
|
|
43
|
+
/** Terminate the session and release it. */
|
|
44
|
+
kill(): Promise<void>;
|
|
45
|
+
/** Fetch the persisted output buffer (to restore a view after re-attach). */
|
|
46
|
+
getBuffer(): Promise<string>;
|
|
47
|
+
/** Persist an output buffer for later restore. */
|
|
48
|
+
saveBuffer(data: string): Promise<void>;
|
|
49
|
+
/** Subscribe to output data. Dispose to stop listening. */
|
|
50
|
+
onData(listener: (data: string) => void): Disposable;
|
|
51
|
+
/** Subscribe to session exit. Dispose to stop listening. */
|
|
52
|
+
onExit(listener: (exitCode: number) => void): Disposable;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Options for {@link ProcessService.exec}.
|
|
57
|
+
*
|
|
58
|
+
* @category Consumer Services
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
export interface ProcessExecOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Working directory to run the command in. Defaults to the open **workspace
|
|
64
|
+
* folder** when omitted — the right cwd for CLI tools (git, formatters,
|
|
65
|
+
* linters) that operate on a repo. A `cwd` outside the workspace throws
|
|
66
|
+
* {@link PathDeniedError} unless the extension declared the `process`
|
|
67
|
+
* {@link Permission}. First-party (bundled) extensions are unscoped.
|
|
68
|
+
*/
|
|
69
|
+
cwd?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The captured result of a one-shot subprocess, returned by
|
|
74
|
+
* {@link ProcessService.exec}.
|
|
75
|
+
*
|
|
76
|
+
* @category Consumer Services
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export interface ProcessExecResult {
|
|
80
|
+
/** Everything the command wrote to standard output. */
|
|
81
|
+
stdout: string;
|
|
82
|
+
/** Everything the command wrote to standard error. */
|
|
83
|
+
stderr: string;
|
|
84
|
+
/**
|
|
85
|
+
* The process exit code (`0` conventionally means success), or `-1` if the
|
|
86
|
+
* process was terminated by a signal. A non-zero `code` is **not** an error —
|
|
87
|
+
* `exec` resolves regardless; inspect `code`/`stderr` to decide.
|
|
88
|
+
*/
|
|
89
|
+
code: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Persistent process / PTY sessions that **survive app restarts** — the core
|
|
94
|
+
* primitive under the terminal (and future task runners, REPLs) — plus one-shot
|
|
95
|
+
* {@link ProcessService.exec | exec} for fire-and-forget subprocess execution.
|
|
96
|
+
* Exposed as {@link ExtensionContext.process}.
|
|
97
|
+
*
|
|
98
|
+
* @category Consumer Services
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
export interface ProcessService {
|
|
102
|
+
/** Spawn a new session in `opts.cwd`. */
|
|
103
|
+
spawn(opts: ProcessSpawnOptions): Promise<ProcessSession>;
|
|
104
|
+
/**
|
|
105
|
+
* Re-attach to an existing session by id (e.g. after an app restart). Rejects
|
|
106
|
+
* with a 404-style error if the session no longer exists.
|
|
107
|
+
*/
|
|
108
|
+
attach(
|
|
109
|
+
id: string,
|
|
110
|
+
opts?: { cols?: number; rows?: number },
|
|
111
|
+
): Promise<ProcessSession>;
|
|
112
|
+
/**
|
|
113
|
+
* Run a one-shot command and resolve with its captured output — for
|
|
114
|
+
* extensions that wrap a CLI (git, formatters, linters) rather than drive an
|
|
115
|
+
* interactive shell. Use {@link ProcessService.spawn | spawn} for long-lived
|
|
116
|
+
* interactive sessions instead.
|
|
117
|
+
*
|
|
118
|
+
* 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}.
|
|
123
|
+
*
|
|
124
|
+
* @param command - Executable to run (resolved via `PATH`), e.g. `"git"`.
|
|
125
|
+
* @param args - Arguments passed verbatim — not shell-interpreted, so no
|
|
126
|
+
* quoting/escaping concerns and no shell-injection surface.
|
|
127
|
+
* @param options - Optional {@link ProcessExecOptions} (e.g. `cwd`).
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const { stdout, code } = await ctx.process.exec(
|
|
131
|
+
* "git",
|
|
132
|
+
* ["status", "--porcelain=v2"],
|
|
133
|
+
* { cwd: workspaceFolder },
|
|
134
|
+
* );
|
|
135
|
+
* if (code === 0) parseStatus(stdout);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
exec(
|
|
139
|
+
command: string,
|
|
140
|
+
args: string[],
|
|
141
|
+
options?: ProcessExecOptions,
|
|
142
|
+
): Promise<ProcessExecResult>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { TerminalKind, TerminalRecord } from "./domain-types";
|
|
2
|
+
|
|
3
|
+
// Re-export the terminal domain types so consumers can name them from the SDK.
|
|
4
|
+
export type { TerminalKind, TerminalRecord } from "./domain-types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Input for {@link TerminalService.create}.
|
|
8
|
+
*
|
|
9
|
+
* @category Consumer Services
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export interface CreateTerminalInput {
|
|
13
|
+
/** Terminal kind — `"shell"` (default), `"claude"`, or `"pi"`. */
|
|
14
|
+
kind?: TerminalKind;
|
|
15
|
+
/** Working directory; falls back to the workspace folder when absent. */
|
|
16
|
+
cwd?: string;
|
|
17
|
+
/** Target workspace; defaults to the active workspace. */
|
|
18
|
+
workspaceId?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Consumer API for the terminal domain, exposed as
|
|
23
|
+
* {@link ExtensionContext.terminals}. The terminal is a core feature — a
|
|
24
|
+
* built-in DockKind like the editor — so this mirrors {@link EditorService}:
|
|
25
|
+
* `create` opens a terminal tab in a workspace, and `closeWorkspace` reaps a
|
|
26
|
+
* workspace's terminals (used when a workspace is deleted). The tab itself is
|
|
27
|
+
* rendered by the core dock from the workspace's terminal records.
|
|
28
|
+
*
|
|
29
|
+
* @category Consumer Services
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export interface TerminalService {
|
|
33
|
+
/**
|
|
34
|
+
* Open a new terminal in a workspace (defaults to the active one). Returns the
|
|
35
|
+
* created {@link TerminalRecord}; the PTY session spawns lazily when its tab
|
|
36
|
+
* mounts.
|
|
37
|
+
*/
|
|
38
|
+
create(input?: CreateTerminalInput): TerminalRecord | undefined;
|
|
39
|
+
/** Close and kill every terminal in a workspace (e.g. on workspace delete). */
|
|
40
|
+
closeWorkspace(workspaceId: string): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Disposable } from "./types";
|
|
2
|
+
import type {
|
|
3
|
+
ThemeBase,
|
|
4
|
+
ThemeVars,
|
|
5
|
+
CustomTheme,
|
|
6
|
+
ThemeExport,
|
|
7
|
+
} from "./domain-types";
|
|
8
|
+
|
|
9
|
+
// Re-export the theme domain types so consumers can name them from the SDK.
|
|
10
|
+
export type {
|
|
11
|
+
ThemeBase,
|
|
12
|
+
ThemeVars,
|
|
13
|
+
CustomTheme,
|
|
14
|
+
ThemeExport,
|
|
15
|
+
} from "./domain-types";
|
|
16
|
+
|
|
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.
|
|
23
|
+
*
|
|
24
|
+
* @category Registration
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export interface ThemePreset {
|
|
28
|
+
/** Unique id (also the persisted `activeThemeId` when selected). */
|
|
29
|
+
id: string;
|
|
30
|
+
/** Display name shown in the theme picker. */
|
|
31
|
+
name: string;
|
|
32
|
+
/** Dark or light — drives the Monaco/xterm base and the `data-theme` attribute. */
|
|
33
|
+
base: ThemeBase;
|
|
34
|
+
/** The CSS `color-scheme` for native controls. */
|
|
35
|
+
colorScheme: "dark" | "light";
|
|
36
|
+
/** CSS variable overrides applied on top of the base palette in `theme.css`. */
|
|
37
|
+
vars: Partial<ThemeVars>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A theme id resolved to its base + effective variables — what the theme picker
|
|
42
|
+
* renders swatches from, returned by {@link ThemeService.resolve}.
|
|
43
|
+
*
|
|
44
|
+
* @category Consumer Services
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export interface ResolvedTheme {
|
|
48
|
+
base: ThemeBase;
|
|
49
|
+
colorScheme: "dark" | "light";
|
|
50
|
+
vars: Partial<ThemeVars>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* An immutable, frozen view of theme state, returned by
|
|
55
|
+
* {@link ThemeService.getState} and delivered to subscribers — read access
|
|
56
|
+
* without a Valtio dependency. `presets` is part of the state (not a static
|
|
57
|
+
* read) because presets are dynamic: extensions register and unregister them at
|
|
58
|
+
* runtime.
|
|
59
|
+
*
|
|
60
|
+
* @category Consumer Services
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
export interface ThemeState {
|
|
64
|
+
/** The active theme's id (a preset id or a custom theme id). */
|
|
65
|
+
activeId: string;
|
|
66
|
+
/** Core Dark/Light followed by every registered preset, in registration order. */
|
|
67
|
+
presets: readonly ThemePreset[];
|
|
68
|
+
/** The user's custom themes, loaded from disk. */
|
|
69
|
+
customThemes: readonly CustomTheme[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Consumer API for the theme domain, exposed as {@link ExtensionContext.theme}.
|
|
74
|
+
* Read via {@link ThemeService.getState | getState} /
|
|
75
|
+
* {@link ThemeService.subscribe | subscribe} (e.g. with `useSyncExternalStore`);
|
|
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}.
|
|
79
|
+
*
|
|
80
|
+
* @category Consumer Services
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
export interface ThemeService {
|
|
84
|
+
/** Current frozen view of theme state. */
|
|
85
|
+
getState(): ThemeState;
|
|
86
|
+
/** Subscribe to theme-state changes (active theme, custom themes, or presets). */
|
|
87
|
+
subscribe(listener: (s: ThemeState) => void): Disposable;
|
|
88
|
+
/** Set the active theme by id (a built-in preset, registered preset, or custom). */
|
|
89
|
+
setActive(id: string): void;
|
|
90
|
+
/** Resolve a theme id to its base + effective vars (for previews/swatches). */
|
|
91
|
+
resolve(id: string): ResolvedTheme;
|
|
92
|
+
/** Persist a custom theme to disk and refresh the in-memory list. */
|
|
93
|
+
saveCustom(theme: CustomTheme): Promise<void>;
|
|
94
|
+
/** Delete a custom theme from disk and refresh the in-memory list. */
|
|
95
|
+
deleteCustom(id: string): Promise<void>;
|
|
96
|
+
/** Reload custom themes from disk into the store. */
|
|
97
|
+
reloadCustom(): Promise<void>;
|
|
98
|
+
/** Strip the id from a custom theme for sharing/serialization. */
|
|
99
|
+
exportTheme(theme: CustomTheme): ThemeExport;
|
|
100
|
+
/** Validate/parse imported JSON into a custom theme (assigns a fresh id). */
|
|
101
|
+
importTheme(data: unknown): CustomTheme;
|
|
102
|
+
}
|