@vibesdotdev/terminal-client 0.0.0-stage → 1.0.0-rc.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.
- package/LICENSE +18 -0
- package/README.md +102 -2
- package/dist/ansi/index.d.ts +11 -0
- package/dist/ansi/index.d.ts.map +1 -0
- package/dist/ansi/index.js +11 -0
- package/dist/ansi/index.js.map +1 -0
- package/dist/ansi/keymap.d.ts +77 -0
- package/dist/ansi/keymap.d.ts.map +1 -0
- package/dist/ansi/keymap.js +257 -0
- package/dist/ansi/keymap.js.map +1 -0
- package/dist/api-client-plugin.d.ts +4 -0
- package/dist/api-client-plugin.d.ts.map +1 -0
- package/dist/api-client-plugin.js +16 -0
- package/dist/api-client-plugin.js.map +1 -0
- package/dist/client/http-session.d.ts +44 -0
- package/dist/client/http-session.d.ts.map +1 -0
- package/dist/client/http-session.js +457 -0
- package/dist/client/http-session.js.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client.d.ts +56 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +169 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/pty-bridge/index.d.ts +15 -0
- package/dist/pty-bridge/index.d.ts.map +1 -0
- package/dist/pty-bridge/index.js +15 -0
- package/dist/pty-bridge/index.js.map +1 -0
- package/dist/pty-bridge/protocol.d.ts +6 -0
- package/dist/pty-bridge/protocol.d.ts.map +1 -0
- package/dist/pty-bridge/protocol.js +6 -0
- package/dist/pty-bridge/protocol.js.map +1 -0
- package/dist/pty-bridge/server.d.ts +7 -0
- package/dist/pty-bridge/server.d.ts.map +1 -0
- package/dist/pty-bridge/server.js +9 -0
- package/dist/pty-bridge/server.js.map +1 -0
- package/dist/remote-session.d.ts +51 -0
- package/dist/remote-session.d.ts.map +1 -0
- package/dist/remote-session.js +326 -0
- package/dist/remote-session.js.map +1 -0
- package/dist/schemas/http.d.ts +61 -0
- package/dist/schemas/http.d.ts.map +1 -0
- package/dist/schemas/http.js +45 -0
- package/dist/schemas/http.js.map +1 -0
- package/dist/schemas/remote.d.ts +25 -0
- package/dist/schemas/remote.d.ts.map +1 -0
- package/dist/schemas/remote.js +23 -0
- package/dist/schemas/remote.js.map +1 -0
- package/dist/schemas/session.d.ts +26 -0
- package/dist/schemas/session.d.ts.map +1 -0
- package/dist/schemas/session.js +17 -0
- package/dist/schemas/session.js.map +1 -0
- package/dist/schemas/store.d.ts +29 -0
- package/dist/schemas/store.d.ts.map +1 -0
- package/dist/schemas/store.js +22 -0
- package/dist/schemas/store.js.map +1 -0
- package/dist/schemas/terminal.d.ts +296 -0
- package/dist/schemas/terminal.d.ts.map +1 -0
- package/dist/schemas/terminal.js +243 -0
- package/dist/schemas/terminal.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/session-store.d.ts +176 -0
- package/dist/server/session-store.d.ts.map +1 -0
- package/dist/server/session-store.js +462 -0
- package/dist/server/session-store.js.map +1 -0
- package/dist/server/sse-stream.d.ts +32 -0
- package/dist/server/sse-stream.d.ts.map +1 -0
- package/dist/server/sse-stream.js +87 -0
- package/dist/server/sse-stream.js.map +1 -0
- package/dist/svelte/HostPicker.svelte +256 -0
- package/dist/svelte/HostPicker.svelte.d.ts +30 -0
- package/dist/svelte/HostPicker.svelte.d.ts.map +1 -0
- package/dist/svelte/README.md +52 -0
- package/dist/svelte/ReconnectBanner.svelte +88 -0
- package/dist/svelte/ReconnectBanner.svelte.d.ts +16 -0
- package/dist/svelte/ReconnectBanner.svelte.d.ts.map +1 -0
- package/dist/svelte/SessionTabs.svelte +192 -0
- package/dist/svelte/SessionTabs.svelte.d.ts +17 -0
- package/dist/svelte/SessionTabs.svelte.d.ts.map +1 -0
- package/dist/svelte/Terminal.svelte +567 -0
- package/dist/svelte/Terminal.svelte.d.ts +25 -0
- package/dist/svelte/Terminal.svelte.d.ts.map +1 -0
- package/dist/svelte/TerminalMobileToolbar.svelte +56 -0
- package/dist/svelte/TerminalMobileToolbar.svelte.d.ts +14 -0
- package/dist/svelte/TerminalMobileToolbar.svelte.d.ts.map +1 -0
- package/dist/svelte/TerminalWorkspace.svelte +562 -0
- package/dist/svelte/TerminalWorkspace.svelte.d.ts +25 -0
- package/dist/svelte/TerminalWorkspace.svelte.d.ts.map +1 -0
- package/dist/svelte/ambient.d.ts +9 -0
- package/dist/svelte/docs/terminal-svelte.docs.descriptor.d.ts +4 -0
- package/dist/svelte/docs/terminal-svelte.docs.descriptor.d.ts.map +1 -0
- package/dist/svelte/docs/terminal-svelte.docs.descriptor.js +74 -0
- package/dist/svelte/index.d.ts +27 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +26 -0
- package/dist/svelte/styles.css +8 -0
- package/dist/svelte/terminal-session-authority.d.ts +17 -0
- package/dist/svelte/terminal-session-authority.d.ts.map +1 -0
- package/dist/svelte/terminal-session-authority.js +25 -0
- package/dist/svelte/terminal-svelte.plugin.d.ts +19 -0
- package/dist/svelte/terminal-svelte.plugin.d.ts.map +1 -0
- package/dist/svelte/terminal-svelte.plugin.js +42 -0
- package/dist/svelte/terminal-theme.d.ts +11 -0
- package/dist/svelte/terminal-theme.d.ts.map +1 -0
- package/dist/svelte/terminal-theme.js +116 -0
- package/dist/svelte/tool-widgets/TerminalToolCard.svelte +65 -0
- package/dist/svelte/tool-widgets/TerminalToolCard.svelte.d.ts +5 -0
- package/dist/svelte/tool-widgets/TerminalToolCard.svelte.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/terminal-tool-card.d.ts +25 -0
- package/dist/svelte/tool-widgets/terminal-tool-card.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/terminal-tool-card.js +116 -0
- package/dist/svelte/types.d.ts +160 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +51 -0
- package/dist/svelte/workspace.d.ts +3 -0
- package/dist/svelte/workspace.d.ts.map +1 -0
- package/dist/svelte/workspace.js +2 -0
- package/dist/terminal-client.plugin.d.ts +3 -0
- package/dist/terminal-client.plugin.d.ts.map +1 -0
- package/dist/terminal-client.plugin.js +3 -0
- package/dist/terminal-client.plugin.js.map +1 -0
- package/dist/terminal-runtime.d.ts +30 -0
- package/dist/terminal-runtime.d.ts.map +1 -0
- package/dist/terminal-runtime.js +673 -0
- package/dist/terminal-runtime.js.map +1 -0
- package/dist/terminal.consumer.plugin.d.ts +14 -0
- package/dist/terminal.consumer.plugin.d.ts.map +1 -0
- package/dist/terminal.consumer.plugin.js +31 -0
- package/dist/terminal.consumer.plugin.js.map +1 -0
- package/dist/tools/terminal.impl.consumer.d.ts +11 -0
- package/dist/tools/terminal.impl.consumer.d.ts.map +1 -0
- package/dist/tools/terminal.impl.consumer.js +20 -0
- package/dist/tools/terminal.impl.consumer.js.map +1 -0
- package/dist/tools/terminal.schemas.d.ts +3 -0
- package/dist/tools/terminal.schemas.d.ts.map +1 -0
- package/dist/tools/terminal.schemas.js +3 -0
- package/dist/tools/terminal.schemas.js.map +1 -0
- package/dist/tools/terminal.unified.descriptor.d.ts +218 -0
- package/dist/tools/terminal.unified.descriptor.d.ts.map +1 -0
- package/dist/tools/terminal.unified.descriptor.js +36 -0
- package/dist/tools/terminal.unified.descriptor.js.map +1 -0
- package/dist/tui/index.d.ts +9 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +8 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/terminal-open.descriptor.d.ts +9 -0
- package/dist/tui/terminal-open.descriptor.d.ts.map +1 -0
- package/dist/tui/terminal-open.descriptor.js +18 -0
- package/dist/tui/terminal-open.descriptor.js.map +1 -0
- package/dist/tui/terminal-open.impl.consumer.d.ts +9 -0
- package/dist/tui/terminal-open.impl.consumer.d.ts.map +1 -0
- package/dist/tui/terminal-open.impl.consumer.js +13 -0
- package/dist/tui/terminal-open.impl.consumer.js.map +1 -0
- package/dist/tui/terminal-registry.descriptor.d.ts +11 -0
- package/dist/tui/terminal-registry.descriptor.d.ts.map +1 -0
- package/dist/tui/terminal-registry.descriptor.js +17 -0
- package/dist/tui/terminal-registry.descriptor.js.map +1 -0
- package/dist/tui/terminal-registry.impl.consumer.d.ts +71 -0
- package/dist/tui/terminal-registry.impl.consumer.d.ts.map +1 -0
- package/dist/tui/terminal-registry.impl.consumer.js +190 -0
- package/dist/tui/terminal-registry.impl.consumer.js.map +1 -0
- package/dist/tui/terminal-tui.plugin.d.ts +10 -0
- package/dist/tui/terminal-tui.plugin.d.ts.map +1 -0
- package/dist/tui/terminal-tui.plugin.js +32 -0
- package/dist/tui/terminal-tui.plugin.js.map +1 -0
- package/dist/tui/terminal.descriptor.d.ts +10 -0
- package/dist/tui/terminal.descriptor.d.ts.map +1 -0
- package/dist/tui/terminal.descriptor.js +25 -0
- package/dist/tui/terminal.descriptor.js.map +1 -0
- package/dist/tui/terminal.impl.consumer.d.ts +14 -0
- package/dist/tui/terminal.impl.consumer.d.ts.map +1 -0
- package/dist/tui/terminal.impl.consumer.js +189 -0
- package/dist/tui/terminal.impl.consumer.js.map +1 -0
- package/dist/tui/terminal.state-key.d.ts +30 -0
- package/dist/tui/terminal.state-key.d.ts.map +1 -0
- package/dist/tui/terminal.state-key.js +9 -0
- package/dist/tui/terminal.state-key.js.map +1 -0
- package/dist/tui/terminal.state.d.ts +152 -0
- package/dist/tui/terminal.state.d.ts.map +1 -0
- package/dist/tui/terminal.state.js +149 -0
- package/dist/tui/terminal.state.js.map +1 -0
- package/dist/workspace.d.ts +155 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +292 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +228 -4
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
import { CapabilityError, CaughtFailureSchema } from '@vibesdotdev/kernel';
|
|
4
|
+
import { getRuntimeSvelteContext } from '@vibesdotdev/surface-sveltekit/svelte';
|
|
5
|
+
import { Button } from '@vibesdotdev/kit/components/ui/button';
|
|
6
|
+
import * as Select from '@vibesdotdev/kit/components/ui/select';
|
|
7
|
+
import * as AlertDialog from '@vibesdotdev/kit/components/ui/alert-dialog';
|
|
8
|
+
import {
|
|
9
|
+
CircleHelp,
|
|
10
|
+
Eraser,
|
|
11
|
+
Info,
|
|
12
|
+
OctagonX,
|
|
13
|
+
PanelsTopLeft,
|
|
14
|
+
Play,
|
|
15
|
+
RotateCw,
|
|
16
|
+
TriangleAlert
|
|
17
|
+
} from '@lucide/svelte';
|
|
18
|
+
import type {
|
|
19
|
+
ExecutionCapabilities,
|
|
20
|
+
ExecutionSession,
|
|
21
|
+
ProcessHandle
|
|
22
|
+
} from '@vibesdotdev/kernel/execution';
|
|
23
|
+
import Terminal from './Terminal.svelte';
|
|
24
|
+
import {
|
|
25
|
+
openTerminalWorkspaceTargetSession,
|
|
26
|
+
terminalWorkspaceCapabilityBanner,
|
|
27
|
+
terminalWorkspacePlaceDefaultCapabilities,
|
|
28
|
+
terminalWorkspaceQuickActions,
|
|
29
|
+
terminalWorkspaceConnectionNotice,
|
|
30
|
+
terminalWorkspaceDefaultCommand,
|
|
31
|
+
terminalWorkspaceError,
|
|
32
|
+
terminalWorkspaceExitedEvent,
|
|
33
|
+
terminalWorkspaceLaunchSignature,
|
|
34
|
+
terminalWorkspacePlaceLabel,
|
|
35
|
+
terminalWorkspaceStatusLabel,
|
|
36
|
+
type TerminalWorkspaceLifecycleEvent,
|
|
37
|
+
type TerminalWorkspaceOpenedReceipt,
|
|
38
|
+
type TerminalWorkspaceQuickActionId,
|
|
39
|
+
type TerminalWorkspaceRuntimePlaceOption,
|
|
40
|
+
type TerminalWorkspaceStatus
|
|
41
|
+
} from '../workspace.js';
|
|
42
|
+
import type { TerminalTheme } from './types.js';
|
|
43
|
+
|
|
44
|
+
interface Props {
|
|
45
|
+
/** Trusted host transport; session lifecycle and rendering remain owner-managed. */
|
|
46
|
+
sessionOpener?: typeof openTerminalWorkspaceTargetSession;
|
|
47
|
+
executionId?: string;
|
|
48
|
+
runtimePlaceId?: string;
|
|
49
|
+
command?: string;
|
|
50
|
+
args?: string[];
|
|
51
|
+
cwd?: string;
|
|
52
|
+
title?: string;
|
|
53
|
+
class?: string;
|
|
54
|
+
compact?: boolean;
|
|
55
|
+
theme?: TerminalTheme;
|
|
56
|
+
/** Host-supplied, executable runtime places plus honest unavailable inventory. */
|
|
57
|
+
loadRuntimePlaces?: () => Promise<readonly TerminalWorkspaceRuntimePlaceOption[]>;
|
|
58
|
+
/** Opens a separate Terminal window; the current session never migrates silently. */
|
|
59
|
+
onOpenRuntimePlace?: (place: TerminalWorkspaceRuntimePlaceOption) => void;
|
|
60
|
+
/** Receives real execution outcomes; `opened` is emitted only after `session()` resolves. */
|
|
61
|
+
onLifecycle?: (event: TerminalWorkspaceLifecycleEvent) => void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let {
|
|
65
|
+
sessionOpener = openTerminalWorkspaceTargetSession,
|
|
66
|
+
executionId = 'browser-shell',
|
|
67
|
+
runtimePlaceId = 'browser:this-profile',
|
|
68
|
+
command,
|
|
69
|
+
args = [],
|
|
70
|
+
cwd = '/',
|
|
71
|
+
title = 'Terminal',
|
|
72
|
+
class: className = '',
|
|
73
|
+
compact = false,
|
|
74
|
+
theme,
|
|
75
|
+
loadRuntimePlaces,
|
|
76
|
+
onOpenRuntimePlace,
|
|
77
|
+
onLifecycle
|
|
78
|
+
}: Props = $props();
|
|
79
|
+
|
|
80
|
+
const runtimeStore = getRuntimeSvelteContext();
|
|
81
|
+
|
|
82
|
+
let status = $state<TerminalWorkspaceStatus>('idle');
|
|
83
|
+
let notice = $state('Opening terminal...');
|
|
84
|
+
let session = $state<ExecutionSession | null>(null);
|
|
85
|
+
let lastDimensions = $state({ cols: 80, rows: 24 });
|
|
86
|
+
let inputReady = $state(false);
|
|
87
|
+
let replaceSessionOpen = $state(false);
|
|
88
|
+
function requestNewSession(): void {
|
|
89
|
+
if (status === 'live') replaceSessionOpen = true;
|
|
90
|
+
else void openSession();
|
|
91
|
+
}
|
|
92
|
+
let activeCwd = $state('/');
|
|
93
|
+
let activeLaunchSignature = $state('');
|
|
94
|
+
let openedReceipt = $state<TerminalWorkspaceOpenedReceipt | null>(null);
|
|
95
|
+
let terminalReady = $state(false);
|
|
96
|
+
let openGeneration = 0;
|
|
97
|
+
let placeLoadGeneration = 0;
|
|
98
|
+
let runtimePlaces = $state<TerminalWorkspaceRuntimePlaceOption[]>([]);
|
|
99
|
+
let runtimePlacesLoading = $state(false);
|
|
100
|
+
let runtimePlacesError = $state<string | null>(null);
|
|
101
|
+
/** Live capability mask from the opened place; overrides the pre-open expectation. */
|
|
102
|
+
let openedCapabilities = $state<ExecutionCapabilities | null>(null);
|
|
103
|
+
/** Accumulated exec-mode output, appended live as stdout/stderr bytes arrive. */
|
|
104
|
+
let execResultText = $state<string | null>(null);
|
|
105
|
+
/** The live exec-mode process; killed on re-open/destroy. */
|
|
106
|
+
let execProcess = $state<ProcessHandle | null>(null);
|
|
107
|
+
|
|
108
|
+
const statusLabel = $derived(terminalWorkspaceStatusLabel(status));
|
|
109
|
+
const placeLabel = $derived(terminalWorkspacePlaceLabel(runtimePlaceId));
|
|
110
|
+
const currentRuntimePlace = $derived(
|
|
111
|
+
runtimePlaces.find((place) => place.id === runtimePlaceId) ?? {
|
|
112
|
+
id: runtimePlaceId,
|
|
113
|
+
label: placeLabel,
|
|
114
|
+
status: 'available' as const,
|
|
115
|
+
executionId
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
const encoder = new TextEncoder();
|
|
119
|
+
const quickActionIcons = { help: CircleHelp, clear: Eraser, interrupt: OctagonX, end: Play };
|
|
120
|
+
// Honest capability gate: the live mask if a place has opened, else the place's
|
|
121
|
+
// advertised mask, else the pre-resolution expectation derived from the id.
|
|
122
|
+
const caps = $derived(
|
|
123
|
+
openedCapabilities ??
|
|
124
|
+
currentRuntimePlace.capabilities ??
|
|
125
|
+
terminalWorkspacePlaceDefaultCapabilities(runtimePlaceId)
|
|
126
|
+
);
|
|
127
|
+
const isExecMode = $derived(caps.pty === false);
|
|
128
|
+
const capabilityBanner = $derived(terminalWorkspaceCapabilityBanner(caps, runtimePlaceId));
|
|
129
|
+
|
|
130
|
+
function fallbackRuntimePlace(): TerminalWorkspaceRuntimePlaceOption {
|
|
131
|
+
return {
|
|
132
|
+
id: runtimePlaceId,
|
|
133
|
+
label: terminalWorkspacePlaceLabel(runtimePlaceId),
|
|
134
|
+
status: 'available',
|
|
135
|
+
executionId
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
onDestroy(() => {
|
|
140
|
+
openGeneration += 1;
|
|
141
|
+
placeLoadGeneration += 1;
|
|
142
|
+
session?.kill();
|
|
143
|
+
session = null;
|
|
144
|
+
execProcess?.kill();
|
|
145
|
+
execProcess = null;
|
|
146
|
+
openedReceipt = null;
|
|
147
|
+
inputReady = false;
|
|
148
|
+
terminalReady = false;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
$effect(() => {
|
|
152
|
+
const loader = loadRuntimePlaces;
|
|
153
|
+
void runtimePlaceId;
|
|
154
|
+
void executionId;
|
|
155
|
+
const generation = ++placeLoadGeneration;
|
|
156
|
+
if (!loader) {
|
|
157
|
+
runtimePlaces = [fallbackRuntimePlace()];
|
|
158
|
+
runtimePlacesLoading = false;
|
|
159
|
+
runtimePlacesError = null;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
runtimePlacesLoading = true;
|
|
163
|
+
runtimePlacesError = null;
|
|
164
|
+
void loader()
|
|
165
|
+
.then(
|
|
166
|
+
(places) => ({ kind: 'loaded' as const, places }),
|
|
167
|
+
(cause) => ({
|
|
168
|
+
kind: 'failure' as const,
|
|
169
|
+
failure: CapabilityError.from(CaughtFailureSchema.parse(cause))
|
|
170
|
+
})
|
|
171
|
+
)
|
|
172
|
+
.then((outcome) => {
|
|
173
|
+
if (generation !== placeLoadGeneration) return;
|
|
174
|
+
if (outcome.kind === 'failure') {
|
|
175
|
+
runtimePlaces = [fallbackRuntimePlace()];
|
|
176
|
+
runtimePlacesError = outcome.failure.message;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const unique = new Map(outcome.places.map((place) => [place.id, place]));
|
|
180
|
+
if (!unique.has(runtimePlaceId)) unique.set(runtimePlaceId, fallbackRuntimePlace());
|
|
181
|
+
runtimePlaces = [...unique.values()];
|
|
182
|
+
})
|
|
183
|
+
.finally(() => {
|
|
184
|
+
if (generation === placeLoadGeneration) runtimePlacesLoading = false;
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
function openRuntimePlace(place: TerminalWorkspaceRuntimePlaceOption): void {
|
|
189
|
+
if (place.status !== 'available') return;
|
|
190
|
+
onOpenRuntimePlace?.(place);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function handleRuntimePlaceSelection(value: string | undefined): void {
|
|
194
|
+
if (!value || value === runtimePlaceId) return;
|
|
195
|
+
const place = runtimePlaces.find((candidate) => candidate.id === value);
|
|
196
|
+
if (place) openRuntimePlace(place);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function handleReady(dimensions: { cols: number; rows: number }): void {
|
|
200
|
+
lastDimensions = dimensions;
|
|
201
|
+
terminalReady = true;
|
|
202
|
+
if (!session && status === 'idle') {
|
|
203
|
+
void openSession(dimensions);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function openSession(dimensions = lastDimensions): Promise<void> {
|
|
208
|
+
const generation = ++openGeneration;
|
|
209
|
+
const sessionExecutionId = executionId;
|
|
210
|
+
const sessionCommand = command ?? terminalWorkspaceDefaultCommand(runtimePlaceId);
|
|
211
|
+
const sessionArgs = [...args];
|
|
212
|
+
const sessionCwd = cwd;
|
|
213
|
+
activeLaunchSignature = terminalWorkspaceLaunchSignature({
|
|
214
|
+
runtimePlaceId,
|
|
215
|
+
executionId: sessionExecutionId,
|
|
216
|
+
command: sessionCommand,
|
|
217
|
+
args: sessionArgs,
|
|
218
|
+
cwd: sessionCwd
|
|
219
|
+
});
|
|
220
|
+
let lifecycleEvent: TerminalWorkspaceOpenedReceipt | null = null;
|
|
221
|
+
session?.kill();
|
|
222
|
+
session = null;
|
|
223
|
+
execProcess?.kill();
|
|
224
|
+
execProcess = null;
|
|
225
|
+
openedReceipt = null;
|
|
226
|
+
openedCapabilities = null;
|
|
227
|
+
execResultText = null;
|
|
228
|
+
inputReady = false;
|
|
229
|
+
status = 'opening';
|
|
230
|
+
notice = isExecMode ? 'Preparing cloud exec…' : 'Opening terminal...';
|
|
231
|
+
const opening = runtimeStore.load().then((runtime) => sessionOpener({
|
|
232
|
+
runtime,
|
|
233
|
+
executionId: sessionExecutionId,
|
|
234
|
+
command: sessionCommand,
|
|
235
|
+
args: sessionArgs,
|
|
236
|
+
runtimePlaceId,
|
|
237
|
+
cwd: sessionCwd,
|
|
238
|
+
cols: dimensions.cols,
|
|
239
|
+
rows: dimensions.rows
|
|
240
|
+
}));
|
|
241
|
+
const outcome = await opening.then(
|
|
242
|
+
(opened) => ({ kind: 'opened' as const, opened }),
|
|
243
|
+
(cause) => ({
|
|
244
|
+
kind: 'failure' as const,
|
|
245
|
+
failure: CapabilityError.from(CaughtFailureSchema.parse(cause))
|
|
246
|
+
})
|
|
247
|
+
);
|
|
248
|
+
if (outcome.kind === 'failure') {
|
|
249
|
+
if (generation !== openGeneration) return;
|
|
250
|
+
if (isExecMode) {
|
|
251
|
+
status = 'idle';
|
|
252
|
+
notice = 'Cloud exec — connect a cloud runtime to run commands.';
|
|
253
|
+
} else {
|
|
254
|
+
status = 'error';
|
|
255
|
+
notice = terminalWorkspaceError(outcome.failure, runtimePlaceId);
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const { opened } = outcome;
|
|
260
|
+
if (generation !== openGeneration) {
|
|
261
|
+
opened.session?.kill();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
openedCapabilities = opened.capabilities;
|
|
265
|
+
openedReceipt = opened.receipt;
|
|
266
|
+
activeCwd = sessionCwd;
|
|
267
|
+
if (opened.mode === 'exec' || !opened.session) {
|
|
268
|
+
// Honest non-PTY path: NO interactive PTY, NO faked keystroke stream.
|
|
269
|
+
// But output STREAMS live — drain the ProcessHandle into the panel as
|
|
270
|
+
// bytes arrive, then settle when the process exits.
|
|
271
|
+
session = null;
|
|
272
|
+
execProcess = opened.execProcess ?? null;
|
|
273
|
+
execResultText = '';
|
|
274
|
+
status = 'live';
|
|
275
|
+
notice = 'Cloud exec — streaming output…';
|
|
276
|
+
if (opened.execProcess) {
|
|
277
|
+
void streamExecProcess(opened.execProcess, generation, opened.receipt);
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
session = opened.session;
|
|
281
|
+
status = 'live';
|
|
282
|
+
notice = terminalWorkspaceConnectionNotice(sessionCwd, runtimePlaceId);
|
|
283
|
+
}
|
|
284
|
+
lifecycleEvent = opened.receipt;
|
|
285
|
+
if (lifecycleEvent) onLifecycle?.(lifecycleEvent);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function handleDisconnect(reason: string): void {
|
|
289
|
+
status = 'error';
|
|
290
|
+
inputReady = false;
|
|
291
|
+
notice = reason || 'Connection lost. Start a new session to reconnect.';
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function handleExit(result: { exitCode: number | null }): void {
|
|
295
|
+
const receipt = openedReceipt;
|
|
296
|
+
status = 'exited';
|
|
297
|
+
notice = 'Session ended.';
|
|
298
|
+
inputReady = false;
|
|
299
|
+
if (receipt) onLifecycle?.(terminalWorkspaceExitedEvent(receipt, result));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Drain a live exec-mode ProcessHandle into the panel. Reads stdout+stderr
|
|
304
|
+
* concurrently, appending decoded text as bytes arrive (chunk 1 shows before
|
|
305
|
+
* exit), then settles status + emits the exited lifecycle event on exit. A
|
|
306
|
+
* stale generation (re-open/destroy) aborts the drain and kills the process.
|
|
307
|
+
*/
|
|
308
|
+
async function streamExecProcess(
|
|
309
|
+
handle: ProcessHandle,
|
|
310
|
+
generation: number,
|
|
311
|
+
receipt: TerminalWorkspaceOpenedReceipt
|
|
312
|
+
): Promise<void> {
|
|
313
|
+
const pump = async (stream: ReadableStream<Uint8Array> | null): Promise<void> => {
|
|
314
|
+
if (!stream) return;
|
|
315
|
+
const reader = stream.getReader();
|
|
316
|
+
const decoder = new TextDecoder();
|
|
317
|
+
try {
|
|
318
|
+
for (;;) {
|
|
319
|
+
const { done, value } = await reader.read();
|
|
320
|
+
if (done) break;
|
|
321
|
+
if (generation !== openGeneration) return;
|
|
322
|
+
execResultText = (execResultText ?? '') + decoder.decode(value, { stream: true });
|
|
323
|
+
}
|
|
324
|
+
const tail = decoder.decode();
|
|
325
|
+
if (tail && generation === openGeneration) execResultText = (execResultText ?? '') + tail;
|
|
326
|
+
} finally {
|
|
327
|
+
reader.releaseLock();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const outcome = await Promise.all([
|
|
331
|
+
pump(handle.stdout),
|
|
332
|
+
pump(handle.stderr),
|
|
333
|
+
handle.exited
|
|
334
|
+
])
|
|
335
|
+
.then(
|
|
336
|
+
([, , exit]) => ({ kind: 'exit' as const, exit }),
|
|
337
|
+
(cause) => ({
|
|
338
|
+
kind: 'failure' as const,
|
|
339
|
+
failure: CapabilityError.from(CaughtFailureSchema.parse(cause))
|
|
340
|
+
})
|
|
341
|
+
);
|
|
342
|
+
if (generation !== openGeneration) return;
|
|
343
|
+
if (outcome.kind === 'failure') {
|
|
344
|
+
status = 'error';
|
|
345
|
+
notice = terminalWorkspaceError(outcome.failure, runtimePlaceId);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if ((execResultText ?? '').length === 0) execResultText = '(command produced no output)';
|
|
349
|
+
status = 'exited';
|
|
350
|
+
const { exit } = outcome;
|
|
351
|
+
notice = exit.code === 0 || exit.code === null ? 'Command finished.' : `Command exited (${exit.code}).`;
|
|
352
|
+
onLifecycle?.(terminalWorkspaceExitedEvent(receipt, { exitCode: exit.code }));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function handleInputReady(): void {
|
|
356
|
+
inputReady = true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
$effect(() => {
|
|
360
|
+
const requestedLaunchSignature = terminalWorkspaceLaunchSignature({
|
|
361
|
+
runtimePlaceId,
|
|
362
|
+
executionId,
|
|
363
|
+
command: command ?? terminalWorkspaceDefaultCommand(runtimePlaceId),
|
|
364
|
+
args,
|
|
365
|
+
cwd
|
|
366
|
+
});
|
|
367
|
+
if (requestedLaunchSignature === activeLaunchSignature) return;
|
|
368
|
+
// Exec-mode places have no interactive terminal to become ready, so open as
|
|
369
|
+
// soon as the place resolves; PTY places still wait for onReady dimensions.
|
|
370
|
+
if (isExecMode) {
|
|
371
|
+
void openSession();
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (!terminalReady) return;
|
|
375
|
+
void openSession();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
function runQuickAction(action: {
|
|
379
|
+
id: TerminalWorkspaceQuickActionId;
|
|
380
|
+
command?: string;
|
|
381
|
+
signal?: string;
|
|
382
|
+
}): void {
|
|
383
|
+
if (!session || status !== 'live' || !inputReady) return;
|
|
384
|
+
if (action.signal) {
|
|
385
|
+
// PTY input delivers SIGINT to the foreground process group. Signaling
|
|
386
|
+
// only the shell PID can leave its current command running.
|
|
387
|
+
if (action.signal === 'SIGINT' && caps.pty) session.write(new Uint8Array([3]));
|
|
388
|
+
else session.signal(action.signal);
|
|
389
|
+
notice = 'Interrupt requested.';
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (action.command) {
|
|
393
|
+
session.write(encoder.encode(`${action.command}\r`));
|
|
394
|
+
if (action.id === 'help') notice = 'Showing available commands.';
|
|
395
|
+
if (action.id === 'clear') notice = 'Cleared terminal output.';
|
|
396
|
+
if (action.id === 'end') notice = 'Ending session...';
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
</script>
|
|
400
|
+
|
|
401
|
+
<section
|
|
402
|
+
class={`grid h-full min-h-0 min-w-0 grid-rows-[auto_minmax(0,1fr)] ${compact ? 'gap-1' : 'gap-3'} overflow-hidden ${className}`}
|
|
403
|
+
aria-label={title}
|
|
404
|
+
data-terminal-workspace
|
|
405
|
+
data-terminal-status={status}
|
|
406
|
+
data-terminal-active-cwd={activeCwd}
|
|
407
|
+
data-terminal-runtime-place={runtimePlaceId}
|
|
408
|
+
>
|
|
409
|
+
<header class={`flex flex-wrap items-center justify-between ${compact ? 'gap-1 border-b px-3 py-1' : 'gap-3'}`}>
|
|
410
|
+
<div class="min-w-0">
|
|
411
|
+
<div class="flex items-center gap-2">
|
|
412
|
+
<h2 class="truncate text-lg font-semibold">{title}</h2>
|
|
413
|
+
<span
|
|
414
|
+
class="rounded-full border border-border bg-background/70 px-2 py-0.5 text-xs font-medium"
|
|
415
|
+
title={runtimePlaceId}
|
|
416
|
+
data-runtime-place-id={runtimePlaceId}
|
|
417
|
+
>
|
|
418
|
+
{placeLabel}
|
|
419
|
+
</span>
|
|
420
|
+
<span class="rounded-full border border-border bg-muted px-2 py-0.5 text-xs font-medium">
|
|
421
|
+
{statusLabel}
|
|
422
|
+
</span>
|
|
423
|
+
</div>
|
|
424
|
+
<p class={compact && status === 'live' ? 'sr-only' : 'mt-1 truncate text-sm text-muted-foreground'}>{notice}</p>
|
|
425
|
+
</div>
|
|
426
|
+
<div class="terminal-workspace-actions flex flex-wrap items-center justify-end gap-2">
|
|
427
|
+
{#if onOpenRuntimePlace}
|
|
428
|
+
<Select.Root type="single" value={runtimePlaceId} onValueChange={handleRuntimePlaceSelection}>
|
|
429
|
+
<Select.Trigger
|
|
430
|
+
class="h-8 max-w-40 min-w-0 rounded-full"
|
|
431
|
+
aria-label="Open another terminal on a runtime place"
|
|
432
|
+
>
|
|
433
|
+
{runtimePlacesLoading ? 'Finding machines…' : currentRuntimePlace.label}
|
|
434
|
+
</Select.Trigger>
|
|
435
|
+
<Select.Content>
|
|
436
|
+
{#each runtimePlaces as place (place.id)}
|
|
437
|
+
<Select.Item
|
|
438
|
+
value={place.id}
|
|
439
|
+
label={place.label}
|
|
440
|
+
disabled={place.status !== 'available'}
|
|
441
|
+
>
|
|
442
|
+
<span class="flex min-w-0 flex-col">
|
|
443
|
+
<span class="truncate">{place.label}</span>
|
|
444
|
+
{#if place.detail}
|
|
445
|
+
<span class="truncate text-xs text-muted-foreground">{place.detail}</span>
|
|
446
|
+
{/if}
|
|
447
|
+
</span>
|
|
448
|
+
</Select.Item>
|
|
449
|
+
{/each}
|
|
450
|
+
</Select.Content>
|
|
451
|
+
</Select.Root>
|
|
452
|
+
<Button
|
|
453
|
+
variant="outline"
|
|
454
|
+
size="sm"
|
|
455
|
+
class="gap-2 rounded-full"
|
|
456
|
+
aria-label={`Open another terminal on ${currentRuntimePlace.label}`}
|
|
457
|
+
disabled={currentRuntimePlace.status !== 'available'}
|
|
458
|
+
onclick={() => openRuntimePlace(currentRuntimePlace)}
|
|
459
|
+
>
|
|
460
|
+
<PanelsTopLeft class="size-3.5" />
|
|
461
|
+
New window
|
|
462
|
+
</Button>
|
|
463
|
+
{/if}
|
|
464
|
+
{#each terminalWorkspaceQuickActions as action (action.id)}
|
|
465
|
+
{@const Icon = quickActionIcons[action.id]}
|
|
466
|
+
<Button
|
|
467
|
+
variant="outline"
|
|
468
|
+
size="sm"
|
|
469
|
+
class="gap-2 rounded-full"
|
|
470
|
+
disabled={status !== 'live' || !inputReady || isExecMode}
|
|
471
|
+
aria-label={action.label}
|
|
472
|
+
title={action.description}
|
|
473
|
+
onclick={() => runQuickAction(action)}
|
|
474
|
+
>
|
|
475
|
+
<Icon class="size-3.5" />
|
|
476
|
+
{#if !compact}{action.label}{/if}
|
|
477
|
+
</Button>
|
|
478
|
+
{/each}
|
|
479
|
+
<Button
|
|
480
|
+
variant="outline"
|
|
481
|
+
size="sm"
|
|
482
|
+
class="gap-2 rounded-full"
|
|
483
|
+
disabled={status === 'opening'}
|
|
484
|
+
onclick={requestNewSession}
|
|
485
|
+
>
|
|
486
|
+
<RotateCw class="size-3.5" />
|
|
487
|
+
New session
|
|
488
|
+
</Button>
|
|
489
|
+
</div>
|
|
490
|
+
</header>
|
|
491
|
+
{#if runtimePlacesError}
|
|
492
|
+
<p class="-mt-2 text-xs text-destructive" role="status">{runtimePlacesError}</p>
|
|
493
|
+
{/if}
|
|
494
|
+
|
|
495
|
+
{#if capabilityBanner}
|
|
496
|
+
<div
|
|
497
|
+
class="flex items-start gap-2 rounded-lg border border-border bg-muted px-3 py-2 text-sm text-foreground"
|
|
498
|
+
role="status"
|
|
499
|
+
data-capability-banner
|
|
500
|
+
data-terminal-exec-mode={isExecMode ? 'true' : 'false'}
|
|
501
|
+
>
|
|
502
|
+
{#if isExecMode}
|
|
503
|
+
<TriangleAlert class="mt-0.5 size-4 shrink-0" />
|
|
504
|
+
{:else}
|
|
505
|
+
<Info class="mt-0.5 size-4 shrink-0" />
|
|
506
|
+
{/if}
|
|
507
|
+
<span>{capabilityBanner}</span>
|
|
508
|
+
</div>
|
|
509
|
+
{/if}
|
|
510
|
+
|
|
511
|
+
{#if isExecMode}
|
|
512
|
+
<div
|
|
513
|
+
class="grid min-h-64 place-items-center rounded-xl border border-dashed border-border bg-muted/30 p-6 text-center"
|
|
514
|
+
data-terminal-exec-panel
|
|
515
|
+
>
|
|
516
|
+
<div class="max-w-md">
|
|
517
|
+
<h3 class="text-base font-semibold">Exec session — live output, no keystrokes</h3>
|
|
518
|
+
<p class="mt-2 text-sm leading-6 text-muted-foreground">{notice}</p>
|
|
519
|
+
{#if execResultText}
|
|
520
|
+
<pre
|
|
521
|
+
class="mt-4 max-h-64 overflow-auto rounded-lg bg-background/70 p-3 text-left text-xs">{execResultText}</pre>
|
|
522
|
+
{/if}
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
{:else if status === 'error'}
|
|
526
|
+
<div
|
|
527
|
+
class="grid min-h-64 place-items-center rounded-xl border border-dashed border-border bg-muted/30 p-6 text-center"
|
|
528
|
+
>
|
|
529
|
+
<div>
|
|
530
|
+
<h3 class="text-base font-semibold">Terminal is not available</h3>
|
|
531
|
+
<p class="mt-2 max-w-sm text-sm leading-6 text-muted-foreground">{notice}</p>
|
|
532
|
+
<Button class="mt-4 rounded-full" variant="outline" onclick={() => void openSession()}>
|
|
533
|
+
Try again
|
|
534
|
+
</Button>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
537
|
+
{:else}
|
|
538
|
+
<Terminal
|
|
539
|
+
{session}
|
|
540
|
+
{theme}
|
|
541
|
+
onReady={handleReady}
|
|
542
|
+
onInputReady={handleInputReady}
|
|
543
|
+
onExit={handleExit}
|
|
544
|
+
onDisconnect={handleDisconnect}
|
|
545
|
+
onNewSession={requestNewSession}
|
|
546
|
+
class="min-h-0"
|
|
547
|
+
/>
|
|
548
|
+
{/if}
|
|
549
|
+
</section>
|
|
550
|
+
|
|
551
|
+
<AlertDialog.Root bind:open={replaceSessionOpen}>
|
|
552
|
+
<AlertDialog.Content>
|
|
553
|
+
<AlertDialog.Header>
|
|
554
|
+
<AlertDialog.Title>Replace this terminal session?</AlertDialog.Title>
|
|
555
|
+
<AlertDialog.Description>This ends the current shell and its running commands. Use another terminal tab if you want to keep them running.</AlertDialog.Description>
|
|
556
|
+
</AlertDialog.Header>
|
|
557
|
+
<AlertDialog.Footer>
|
|
558
|
+
<AlertDialog.Cancel>Keep session</AlertDialog.Cancel>
|
|
559
|
+
<AlertDialog.Action onclick={() => void openSession()}>Replace session</AlertDialog.Action>
|
|
560
|
+
</AlertDialog.Footer>
|
|
561
|
+
</AlertDialog.Content>
|
|
562
|
+
</AlertDialog.Root>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { openTerminalWorkspaceTargetSession, type TerminalWorkspaceLifecycleEvent, type TerminalWorkspaceRuntimePlaceOption } from '../workspace.ts';
|
|
2
|
+
import type { TerminalTheme } from './types.ts';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Trusted host transport; session lifecycle and rendering remain owner-managed. */
|
|
5
|
+
sessionOpener?: typeof openTerminalWorkspaceTargetSession;
|
|
6
|
+
executionId?: string;
|
|
7
|
+
runtimePlaceId?: string;
|
|
8
|
+
command?: string;
|
|
9
|
+
args?: string[];
|
|
10
|
+
cwd?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
compact?: boolean;
|
|
14
|
+
theme?: TerminalTheme;
|
|
15
|
+
/** Host-supplied, executable runtime places plus honest unavailable inventory. */
|
|
16
|
+
loadRuntimePlaces?: () => Promise<readonly TerminalWorkspaceRuntimePlaceOption[]>;
|
|
17
|
+
/** Opens a separate Terminal window; the current session never migrates silently. */
|
|
18
|
+
onOpenRuntimePlace?: (place: TerminalWorkspaceRuntimePlaceOption) => void;
|
|
19
|
+
/** Receives real execution outcomes; `opened` is emitted only after `session()` resolves. */
|
|
20
|
+
onLifecycle?: (event: TerminalWorkspaceLifecycleEvent) => void;
|
|
21
|
+
}
|
|
22
|
+
declare const TerminalWorkspace: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type TerminalWorkspace = ReturnType<typeof TerminalWorkspace>;
|
|
24
|
+
export default TerminalWorkspace;
|
|
25
|
+
//# sourceMappingURL=TerminalWorkspace.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TerminalWorkspace.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/TerminalWorkspace.svelte.ts"],"names":[],"mappings":"AAyBA,OAAO,EACL,kCAAkC,EAWlC,KAAK,+BAA+B,EAGpC,KAAK,mCAAmC,EAExC,MAAM,iBAAiB,CAAC;AAC1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG/C,UAAU,KAAK;IACd,oFAAoF;IACpF,aAAa,CAAC,EAAE,OAAO,kCAAkC,CAAC;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,mCAAmC,EAAE,CAAC,CAAC;IAClF,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,mCAAmC,KAAK,IAAI,CAAC;IAC1E,6FAA6F;IAC7F,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,+BAA+B,KAAK,IAAI,CAAC;CAC/D;AA0cF,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient module declarations for terminal-svelte.
|
|
3
|
+
*
|
|
4
|
+
* The component dynamically imports xterm's stylesheet (`@xterm/xterm/css/
|
|
5
|
+
* xterm.css`) inside `onMount` so the browser bundler injects the styles. CSS
|
|
6
|
+
* modules carry no TypeScript types, so declare them as side-effecting imports.
|
|
7
|
+
*/
|
|
8
|
+
declare module '*.css';
|
|
9
|
+
declare module '@xterm/xterm/css/xterm.css';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-svelte.docs.descriptor.d.ts","sourceRoot":"","sources":["../../../src/svelte/docs/terminal-svelte.docs.descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAEvE,QAAA,MAAM,UAAU,EAAE,mBAwEjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const descriptor = {
|
|
2
|
+
kind: 'docs/topic',
|
|
3
|
+
id: 'terminal-svelte',
|
|
4
|
+
parent: 'runtime',
|
|
5
|
+
order: 44,
|
|
6
|
+
title: 'Terminal Svelte',
|
|
7
|
+
summary: 'Svelte terminal surfaces over runtime-resolved interactive execution sessions.',
|
|
8
|
+
tags: ['terminal', 'svelte', 'execution', 'runtime', 'vessel'],
|
|
9
|
+
surfaces: ['web', 'in-app'],
|
|
10
|
+
hardware: ['consumer', 'cloud'],
|
|
11
|
+
enabled: true,
|
|
12
|
+
body: {
|
|
13
|
+
type: 'markdown',
|
|
14
|
+
sourceType: 'raw',
|
|
15
|
+
source: `---
|
|
16
|
+
title: Terminal Svelte
|
|
17
|
+
summary: Svelte terminal surfaces over runtime-resolved interactive execution sessions
|
|
18
|
+
tags: [terminal, svelte, execution, runtime, vessel]
|
|
19
|
+
parent: runtime
|
|
20
|
+
order: 44
|
|
21
|
+
surfaces: [web, in-app]
|
|
22
|
+
hardware: [consumer, cloud]
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Terminal Svelte
|
|
26
|
+
|
|
27
|
+
\`@vibesdotdev/terminal-client/svelte\` presents interactive terminal sessions without
|
|
28
|
+
owning execution. \`TerminalWorkspace\` resolves \`runtime/execution\`, awaits a
|
|
29
|
+
real \`ExecutionSession\`, and then mounts the xterm renderer over that session's
|
|
30
|
+
byte stream.
|
|
31
|
+
|
|
32
|
+
## Entrypoints
|
|
33
|
+
|
|
34
|
+
- \`@vibesdotdev/terminal-client/svelte/Terminal.svelte\` - xterm renderer for an
|
|
35
|
+
injected \`ExecutionSession\`.
|
|
36
|
+
- \`@vibesdotdev/terminal-client/svelte/TerminalWorkspace.svelte\` - runtime-backed
|
|
37
|
+
terminal workspace and lifecycle boundary.
|
|
38
|
+
- \`@vibesdotdev/terminal-client/workspace\` - rune-free lifecycle helpers and
|
|
39
|
+
receipt types.
|
|
40
|
+
- \`@vibesdotdev/terminal-client/svelte/plugin\` - docs descriptor registration and
|
|
41
|
+
the designed \`terminal\` Kit ToolCard (\`tools/ui\`, web).
|
|
42
|
+
- \`@vibesdotdev/vessel/apps/terminal.vessel.plugin\` - Vessel-owned
|
|
43
|
+
\`terminal.workspace\` registration and browser loader for this owner component.
|
|
44
|
+
|
|
45
|
+
## Terminal tool card
|
|
46
|
+
|
|
47
|
+
The Svelte plugin registers a designed Kit \`ToolCard\` for \`tools/tool:terminal\`.
|
|
48
|
+
The card shows cwd, command/session, and a bounded output excerpt through Kit
|
|
49
|
+
\`CodeViewer\`. First-party terminal calls do not fall through to JSON
|
|
50
|
+
\`DefaultToolCard\`.
|
|
51
|
+
|
|
52
|
+
## Lifecycle boundary
|
|
53
|
+
|
|
54
|
+
\`phase: 'opened'\` is emitted only after \`runtime/execution.session()\`
|
|
55
|
+
resolves. Its receipt carries the resolved session, environment, and execution
|
|
56
|
+
ids. A request to open Terminal is intent; this execution-backed receipt is the
|
|
57
|
+
success signal.
|
|
58
|
+
|
|
59
|
+
## Vessel capability
|
|
60
|
+
|
|
61
|
+
The owner-declared Vessel surface requires \`runtime/execution\`. That metadata
|
|
62
|
+
lets a host explain why Terminal is unavailable before it attempts to compose
|
|
63
|
+
the surface, while the owner plugin keeps its Svelte module private behind a
|
|
64
|
+
browser-scoped loader.
|
|
65
|
+
|
|
66
|
+
## Boundary
|
|
67
|
+
|
|
68
|
+
This package does not allocate PTYs, choose execution environments, manage
|
|
69
|
+
filesystem adapters, or persist Vessel return points. Those capabilities remain
|
|
70
|
+
with their runtime and feature owners.
|
|
71
|
+
`
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export default descriptor;
|