@tutti-os/workspace-terminal 0.0.6 → 0.0.8

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.
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, HTMLAttributes } from 'react';
3
- import { TerminalNodeExternalState, TerminalHeaderAccessoryRenderer } from '../contracts/index.js';
3
+ import { TerminalNodeExternalState, TerminalHeaderAccessoryRenderer, TerminalPreviewChangeHandler, TerminalPreviewSnapshot, TerminalTheme } from '../contracts/index.js';
4
4
  import { T as TerminalNodeFeature } from '../feature-sh6KDO7B.js';
5
5
  import '@tutti-os/ui-i18n-runtime';
6
6
  import '../i18n/index.js';
@@ -13,6 +13,7 @@ interface TerminalNodeProps {
13
13
  headerAccessory?: TerminalHeaderAccessoryRenderer;
14
14
  nodeId: string;
15
15
  onFocusRequest?: () => void;
16
+ onPreviewChange?: TerminalPreviewChangeHandler;
16
17
  sessionId?: string | null;
17
18
  showHeader?: boolean;
18
19
  }
@@ -32,8 +33,19 @@ interface TerminalCloseGuardDialogProps {
32
33
  onConfirm: () => void;
33
34
  open: boolean;
34
35
  }
35
- declare function TerminalNode({ children, controllerLeaseRetainedExternally, externalState, feature, headerAccessory, nodeId, onFocusRequest, sessionId, showHeader }: TerminalNodeProps): react_jsx_runtime.JSX.Element;
36
+ declare function TerminalNode({ children, controllerLeaseRetainedExternally, externalState, feature, headerAccessory, nodeId, onFocusRequest, onPreviewChange, sessionId, showHeader }: TerminalNodeProps): react_jsx_runtime.JSX.Element;
36
37
  declare function TerminalNodeHeader({ className, defaultActions, externalState, feature, headerAccessory, onCloseRequest, sessionId, ...headerProps }: TerminalNodeHeaderProps): react_jsx_runtime.JSX.Element;
37
38
  declare function TerminalCloseGuardDialog({ feature, leaderCommand, onCancel, onConfirm, open }: TerminalCloseGuardDialogProps): react_jsx_runtime.JSX.Element | null;
38
39
 
39
- export { TerminalCloseGuardDialog, type TerminalCloseGuardDialogProps, TerminalNode, TerminalNodeHeader, type TerminalNodeHeaderProps, type TerminalNodeProps };
40
+ interface TerminalDockPreviewProps {
41
+ frame?: TerminalDockPreviewFrame | null;
42
+ snapshot: TerminalPreviewSnapshot;
43
+ theme?: TerminalTheme | null;
44
+ }
45
+ interface TerminalDockPreviewFrame {
46
+ height: number;
47
+ width: number;
48
+ }
49
+ declare function TerminalDockPreview({ frame, snapshot, theme }: TerminalDockPreviewProps): react_jsx_runtime.JSX.Element;
50
+
51
+ export { TerminalCloseGuardDialog, type TerminalCloseGuardDialogProps, TerminalDockPreview, type TerminalDockPreviewProps, TerminalNode, TerminalNodeHeader, type TerminalNodeHeaderProps, type TerminalNodeProps };
@@ -2,11 +2,195 @@ import {
2
2
  TerminalCloseGuardDialog,
3
3
  TerminalNode,
4
4
  TerminalNodeHeader
5
- } from "../chunk-FEJT6FJ4.js";
5
+ } from "../chunk-UWPZ5O4V.js";
6
6
  import "../chunk-56B5GYMU.js";
7
7
  import "../chunk-65BWWZQV.js";
8
+
9
+ // src/react/TerminalDockPreview.tsx
10
+ import { useLayoutEffect, useRef, useState } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var terminalDockPreviewLineHeight = 1.35;
13
+ var terminalDockPreviewCharacterWidth = 0.62;
14
+ var terminalDockPreviewFontSize = 13;
15
+ var terminalDockPreviewPadding = 16;
16
+ function TerminalDockPreview({
17
+ frame = null,
18
+ snapshot,
19
+ theme = null
20
+ }) {
21
+ const containerRef = useRef(null);
22
+ const [layout, setLayout] = useState({
23
+ height: 0,
24
+ scale: 1,
25
+ sourceHeight: 0,
26
+ sourceWidth: 0,
27
+ width: 0
28
+ });
29
+ const style = {};
30
+ if (theme?.background) {
31
+ style["--workspace-terminal-dock-preview-background"] = theme.background;
32
+ }
33
+ if (theme?.foreground) {
34
+ style["--workspace-terminal-dock-preview-foreground"] = theme.foreground;
35
+ }
36
+ if (layout.width > 0 && layout.height > 0) {
37
+ style["--workspace-terminal-dock-preview-height"] = `${layout.height}px`;
38
+ style["--workspace-terminal-dock-preview-width"] = `${layout.width}px`;
39
+ style["--workspace-terminal-dock-preview-scale"] = String(layout.scale);
40
+ style["--workspace-terminal-dock-preview-source-height"] = `${layout.sourceHeight}px`;
41
+ style["--workspace-terminal-dock-preview-source-width"] = `${layout.sourceWidth}px`;
42
+ }
43
+ useLayoutEffect(() => {
44
+ const container = containerRef.current;
45
+ if (!container || typeof ResizeObserver === "undefined") {
46
+ return;
47
+ }
48
+ const updateFontSize = () => {
49
+ const availableSize = readTerminalDockPreviewAvailableSize(container);
50
+ const availableWidth = availableSize.width;
51
+ const availableHeight = availableSize.height;
52
+ const previewRatio = resolveTerminalDockPreviewRatio(frame, snapshot);
53
+ const sourceSize = resolveTerminalDockPreviewSourceSize(frame, snapshot);
54
+ const fittedSize = fitTerminalDockPreviewSize({
55
+ availableHeight,
56
+ availableWidth,
57
+ ratio: previewRatio
58
+ });
59
+ const nextScale = sourceSize.width > 0 && sourceSize.height > 0 ? Math.min(
60
+ fittedSize.width / sourceSize.width,
61
+ fittedSize.height / sourceSize.height
62
+ ) : 1;
63
+ setLayout((current) => {
64
+ if (Math.abs(current.height - fittedSize.height) < 0.5 && Math.abs(current.scale - nextScale) < 1e-3 && Math.abs(current.sourceHeight - sourceSize.height) < 0.5 && Math.abs(current.sourceWidth - sourceSize.width) < 0.5 && Math.abs(current.width - fittedSize.width) < 0.5) {
65
+ return current;
66
+ }
67
+ return {
68
+ height: fittedSize.height,
69
+ scale: nextScale,
70
+ sourceHeight: sourceSize.height,
71
+ sourceWidth: sourceSize.width,
72
+ width: fittedSize.width
73
+ };
74
+ });
75
+ };
76
+ updateFontSize();
77
+ const observer = new ResizeObserver(updateFontSize);
78
+ observer.observe(container);
79
+ return () => {
80
+ observer.disconnect();
81
+ };
82
+ }, [frame, snapshot.cols, snapshot.lines.length]);
83
+ return /* @__PURE__ */ jsx(
84
+ "span",
85
+ {
86
+ className: "workspace-terminal__dock-preview-frame",
87
+ "data-terminal-dock-preview": "",
88
+ ref: containerRef,
89
+ children: /* @__PURE__ */ jsx("span", { className: "workspace-terminal__dock-preview-viewport", style, children: /* @__PURE__ */ jsx("span", { className: "workspace-terminal__dock-preview", children: snapshot.lines.map((line, index) => /* @__PURE__ */ jsx("span", { className: "workspace-terminal__dock-preview-line", children: line.segments.length > 0 ? line.segments.map((segment, segmentIndex) => /* @__PURE__ */ jsx(
90
+ "span",
91
+ {
92
+ className: "workspace-terminal__dock-preview-segment",
93
+ style: resolveTerminalDockPreviewSegmentStyle(
94
+ segment.style
95
+ ),
96
+ children: segment.text
97
+ },
98
+ segmentIndex
99
+ )) : " " }, index)) }) })
100
+ }
101
+ );
102
+ }
103
+ function readTerminalDockPreviewAvailableSize(element) {
104
+ const computedStyle = window.getComputedStyle(element);
105
+ const computedWidth = Number.parseFloat(computedStyle.width);
106
+ const computedHeight = Number.parseFloat(computedStyle.height);
107
+ if (Number.isFinite(computedWidth) && computedWidth > 0 && Number.isFinite(computedHeight) && computedHeight > 0) {
108
+ return {
109
+ height: computedHeight,
110
+ width: computedWidth
111
+ };
112
+ }
113
+ return {
114
+ height: Math.max(0, element.clientHeight),
115
+ width: Math.max(0, element.clientWidth)
116
+ };
117
+ }
118
+ function resolveTerminalDockPreviewRatio(frame, snapshot) {
119
+ if (frame && frame.width > 0 && frame.height > 0) {
120
+ return frame.width / frame.height;
121
+ }
122
+ const rowCount = Math.max(1, snapshot.lines.length);
123
+ const colCount = Math.max(1, Math.min(snapshot.cols, 160));
124
+ return colCount * terminalDockPreviewCharacterWidth / (rowCount * terminalDockPreviewLineHeight);
125
+ }
126
+ function resolveTerminalDockPreviewSourceSize(frame, snapshot) {
127
+ if (frame && frame.width > 0 && frame.height > 0) {
128
+ return {
129
+ height: frame.height,
130
+ width: frame.width
131
+ };
132
+ }
133
+ const rowCount = Math.max(1, snapshot.lines.length);
134
+ const colCount = Math.max(1, Math.min(snapshot.cols, 160));
135
+ return {
136
+ height: rowCount * terminalDockPreviewFontSize * terminalDockPreviewLineHeight + terminalDockPreviewPadding,
137
+ width: colCount * terminalDockPreviewFontSize * terminalDockPreviewCharacterWidth + terminalDockPreviewPadding
138
+ };
139
+ }
140
+ function fitTerminalDockPreviewSize(input) {
141
+ if (input.availableHeight <= 0 || input.availableWidth <= 0 || !Number.isFinite(input.ratio) || input.ratio <= 0) {
142
+ return { height: 0, width: 0 };
143
+ }
144
+ const widthFromFullHeight = input.availableHeight * input.ratio;
145
+ if (widthFromFullHeight <= input.availableWidth) {
146
+ return {
147
+ height: input.availableHeight,
148
+ width: widthFromFullHeight
149
+ };
150
+ }
151
+ return {
152
+ height: input.availableWidth / input.ratio,
153
+ width: input.availableWidth
154
+ };
155
+ }
156
+ function resolveTerminalDockPreviewSegmentStyle(segmentStyle) {
157
+ if (!segmentStyle) {
158
+ return void 0;
159
+ }
160
+ const style = {};
161
+ if (segmentStyle.background) {
162
+ style.backgroundColor = segmentStyle.background;
163
+ }
164
+ if (segmentStyle.color) {
165
+ style.color = segmentStyle.color;
166
+ }
167
+ if (segmentStyle.bold) {
168
+ style.fontWeight = 700;
169
+ }
170
+ if (segmentStyle.dim) {
171
+ style.opacity = 0.64;
172
+ }
173
+ if (segmentStyle.italic) {
174
+ style.fontStyle = "italic";
175
+ }
176
+ const decorations = [];
177
+ if (segmentStyle.underline) {
178
+ decorations.push("underline");
179
+ }
180
+ if (segmentStyle.strikethrough) {
181
+ decorations.push("line-through");
182
+ }
183
+ if (segmentStyle.overline) {
184
+ decorations.push("overline");
185
+ }
186
+ if (decorations.length > 0) {
187
+ style.textDecorationLine = decorations.join(" ");
188
+ }
189
+ return style;
190
+ }
8
191
  export {
9
192
  TerminalCloseGuardDialog,
193
+ TerminalDockPreview,
10
194
  TerminalNode,
11
195
  TerminalNodeHeader
12
196
  };
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../../src/react/TerminalDockPreview.tsx"],"sourcesContent":["import { useLayoutEffect, useRef, useState } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport type {\n TerminalPreviewSegmentStyle,\n TerminalPreviewSnapshot,\n TerminalTheme\n} from \"../contracts/index.ts\";\n\nexport interface TerminalDockPreviewProps {\n frame?: TerminalDockPreviewFrame | null;\n snapshot: TerminalPreviewSnapshot;\n theme?: TerminalTheme | null;\n}\n\nexport interface TerminalDockPreviewFrame {\n height: number;\n width: number;\n}\n\ninterface TerminalDockPreviewStyle extends CSSProperties {\n \"--workspace-terminal-dock-preview-background\"?: string;\n \"--workspace-terminal-dock-preview-foreground\"?: string;\n \"--workspace-terminal-dock-preview-scale\"?: string;\n \"--workspace-terminal-dock-preview-source-height\"?: string;\n \"--workspace-terminal-dock-preview-source-width\"?: string;\n \"--workspace-terminal-dock-preview-height\"?: string;\n \"--workspace-terminal-dock-preview-width\"?: string;\n}\n\nconst terminalDockPreviewLineHeight = 1.35;\nconst terminalDockPreviewCharacterWidth = 0.62;\nconst terminalDockPreviewFontSize = 13;\nconst terminalDockPreviewPadding = 16;\n\nexport function TerminalDockPreview({\n frame = null,\n snapshot,\n theme = null\n}: TerminalDockPreviewProps) {\n const containerRef = useRef<HTMLSpanElement | null>(null);\n const [layout, setLayout] = useState({\n height: 0,\n scale: 1,\n sourceHeight: 0,\n sourceWidth: 0,\n width: 0\n });\n const style: TerminalDockPreviewStyle = {};\n if (theme?.background) {\n style[\"--workspace-terminal-dock-preview-background\"] = theme.background;\n }\n if (theme?.foreground) {\n style[\"--workspace-terminal-dock-preview-foreground\"] = theme.foreground;\n }\n if (layout.width > 0 && layout.height > 0) {\n style[\"--workspace-terminal-dock-preview-height\"] = `${layout.height}px`;\n style[\"--workspace-terminal-dock-preview-width\"] = `${layout.width}px`;\n style[\"--workspace-terminal-dock-preview-scale\"] = String(layout.scale);\n style[\"--workspace-terminal-dock-preview-source-height\"] =\n `${layout.sourceHeight}px`;\n style[\"--workspace-terminal-dock-preview-source-width\"] =\n `${layout.sourceWidth}px`;\n }\n\n useLayoutEffect(() => {\n const container = containerRef.current;\n if (!container || typeof ResizeObserver === \"undefined\") {\n return;\n }\n const updateFontSize = () => {\n const availableSize = readTerminalDockPreviewAvailableSize(container);\n const availableWidth = availableSize.width;\n const availableHeight = availableSize.height;\n const previewRatio = resolveTerminalDockPreviewRatio(frame, snapshot);\n const sourceSize = resolveTerminalDockPreviewSourceSize(frame, snapshot);\n const fittedSize = fitTerminalDockPreviewSize({\n availableHeight,\n availableWidth,\n ratio: previewRatio\n });\n const nextScale =\n sourceSize.width > 0 && sourceSize.height > 0\n ? Math.min(\n fittedSize.width / sourceSize.width,\n fittedSize.height / sourceSize.height\n )\n : 1;\n setLayout((current) => {\n if (\n Math.abs(current.height - fittedSize.height) < 0.5 &&\n Math.abs(current.scale - nextScale) < 0.001 &&\n Math.abs(current.sourceHeight - sourceSize.height) < 0.5 &&\n Math.abs(current.sourceWidth - sourceSize.width) < 0.5 &&\n Math.abs(current.width - fittedSize.width) < 0.5\n ) {\n return current;\n }\n return {\n height: fittedSize.height,\n scale: nextScale,\n sourceHeight: sourceSize.height,\n sourceWidth: sourceSize.width,\n width: fittedSize.width\n };\n });\n };\n updateFontSize();\n const observer = new ResizeObserver(updateFontSize);\n observer.observe(container);\n return () => {\n observer.disconnect();\n };\n }, [frame, snapshot.cols, snapshot.lines.length]);\n\n return (\n <span\n className=\"workspace-terminal__dock-preview-frame\"\n data-terminal-dock-preview=\"\"\n ref={containerRef}\n >\n <span className=\"workspace-terminal__dock-preview-viewport\" style={style}>\n <span className=\"workspace-terminal__dock-preview\">\n {snapshot.lines.map((line, index) => (\n <span className=\"workspace-terminal__dock-preview-line\" key={index}>\n {line.segments.length > 0\n ? line.segments.map((segment, segmentIndex) => (\n <span\n className=\"workspace-terminal__dock-preview-segment\"\n key={segmentIndex}\n style={resolveTerminalDockPreviewSegmentStyle(\n segment.style\n )}\n >\n {segment.text}\n </span>\n ))\n : \" \"}\n </span>\n ))}\n </span>\n </span>\n </span>\n );\n}\n\nfunction readTerminalDockPreviewAvailableSize(element: HTMLElement) {\n const computedStyle = window.getComputedStyle(element);\n const computedWidth = Number.parseFloat(computedStyle.width);\n const computedHeight = Number.parseFloat(computedStyle.height);\n if (\n Number.isFinite(computedWidth) &&\n computedWidth > 0 &&\n Number.isFinite(computedHeight) &&\n computedHeight > 0\n ) {\n return {\n height: computedHeight,\n width: computedWidth\n };\n }\n\n return {\n height: Math.max(0, element.clientHeight),\n width: Math.max(0, element.clientWidth)\n };\n}\n\nfunction resolveTerminalDockPreviewRatio(\n frame: TerminalDockPreviewFrame | null,\n snapshot: TerminalPreviewSnapshot\n) {\n if (frame && frame.width > 0 && frame.height > 0) {\n return frame.width / frame.height;\n }\n const rowCount = Math.max(1, snapshot.lines.length);\n const colCount = Math.max(1, Math.min(snapshot.cols, 160));\n return (\n (colCount * terminalDockPreviewCharacterWidth) /\n (rowCount * terminalDockPreviewLineHeight)\n );\n}\n\nfunction resolveTerminalDockPreviewSourceSize(\n frame: TerminalDockPreviewFrame | null,\n snapshot: TerminalPreviewSnapshot\n) {\n if (frame && frame.width > 0 && frame.height > 0) {\n return {\n height: frame.height,\n width: frame.width\n };\n }\n\n const rowCount = Math.max(1, snapshot.lines.length);\n const colCount = Math.max(1, Math.min(snapshot.cols, 160));\n return {\n height:\n rowCount * terminalDockPreviewFontSize * terminalDockPreviewLineHeight +\n terminalDockPreviewPadding,\n width:\n colCount *\n terminalDockPreviewFontSize *\n terminalDockPreviewCharacterWidth +\n terminalDockPreviewPadding\n };\n}\n\nfunction fitTerminalDockPreviewSize(input: {\n availableHeight: number;\n availableWidth: number;\n ratio: number;\n}) {\n if (\n input.availableHeight <= 0 ||\n input.availableWidth <= 0 ||\n !Number.isFinite(input.ratio) ||\n input.ratio <= 0\n ) {\n return { height: 0, width: 0 };\n }\n\n const widthFromFullHeight = input.availableHeight * input.ratio;\n if (widthFromFullHeight <= input.availableWidth) {\n return {\n height: input.availableHeight,\n width: widthFromFullHeight\n };\n }\n\n return {\n height: input.availableWidth / input.ratio,\n width: input.availableWidth\n };\n}\n\nfunction resolveTerminalDockPreviewSegmentStyle(\n segmentStyle: TerminalPreviewSegmentStyle | undefined\n): CSSProperties | undefined {\n if (!segmentStyle) {\n return undefined;\n }\n\n const style: CSSProperties = {};\n if (segmentStyle.background) {\n style.backgroundColor = segmentStyle.background;\n }\n if (segmentStyle.color) {\n style.color = segmentStyle.color;\n }\n if (segmentStyle.bold) {\n style.fontWeight = 700;\n }\n if (segmentStyle.dim) {\n style.opacity = 0.64;\n }\n if (segmentStyle.italic) {\n style.fontStyle = \"italic\";\n }\n\n const decorations: string[] = [];\n if (segmentStyle.underline) {\n decorations.push(\"underline\");\n }\n if (segmentStyle.strikethrough) {\n decorations.push(\"line-through\");\n }\n if (segmentStyle.overline) {\n decorations.push(\"overline\");\n }\n if (decorations.length > 0) {\n style.textDecorationLine = decorations.join(\" \");\n }\n\n return style;\n}\n"],"mappings":"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","names":[]}
@@ -301,3 +301,51 @@
301
301
  "Courier New", monospace;
302
302
  font-weight: 700;
303
303
  }
304
+
305
+ .workspace-terminal__dock-preview-frame {
306
+ display: grid;
307
+ width: 100%;
308
+ height: 100%;
309
+ min-width: 0;
310
+ min-height: 0;
311
+ overflow: hidden;
312
+ place-items: center;
313
+ }
314
+
315
+ .workspace-terminal__dock-preview-viewport {
316
+ display: block;
317
+ width: var(--workspace-terminal-dock-preview-width, 100%);
318
+ height: var(--workspace-terminal-dock-preview-height, 100%);
319
+ overflow: hidden;
320
+ background: var(--workspace-terminal-dock-preview-background, #111);
321
+ }
322
+
323
+ .workspace-terminal__dock-preview {
324
+ display: block;
325
+ width: var(--workspace-terminal-dock-preview-source-width, 100%);
326
+ height: var(--workspace-terminal-dock-preview-source-height, 100%);
327
+ overflow: hidden;
328
+ background: var(--workspace-terminal-dock-preview-background, #111);
329
+ color: var(--workspace-terminal-dock-preview-foreground, #f4f4f5);
330
+ font-family:
331
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
332
+ "Courier New", monospace;
333
+ font-size: 13px;
334
+ line-height: 1.35;
335
+ padding: 8px;
336
+ pointer-events: none;
337
+ transform: scale(var(--workspace-terminal-dock-preview-scale, 1));
338
+ transform-origin: top left;
339
+ user-select: none;
340
+ }
341
+
342
+ .workspace-terminal__dock-preview-line {
343
+ display: block;
344
+ min-height: 1.35em;
345
+ overflow: hidden;
346
+ white-space: pre;
347
+ }
348
+
349
+ .workspace-terminal__dock-preview-segment {
350
+ white-space: pre;
351
+ }
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { WorkbenchHostExternalStateSource, WorkbenchFrame, WorkbenchHostLaunchRequest, WorkbenchHostDockEntry, WorkbenchContribution, WorkbenchHostLaunchResult, WorkbenchHostNodeDefinition } from '@tutti-os/workbench-surface';
3
- import { TerminalNodeExternalState, TerminalHeaderAccessoryRenderer, TerminalCloseGuardResult, TerminalLaunchInput } from '../contracts/index.js';
3
+ import { TerminalNodeExternalState, TerminalHeaderAccessoryRenderer, TerminalPreviewChangeHandler, TerminalCloseGuardResult, TerminalLaunchInput } from '../contracts/index.js';
4
4
  import { T as TerminalNodeFeature } from '../feature-sh6KDO7B.js';
5
5
  import '@tutti-os/ui-i18n-runtime';
6
6
  import '../i18n/index.js';
@@ -14,6 +14,7 @@ interface CreateTerminalWorkbenchNodeDefinitionInput {
14
14
  feature: TerminalNodeFeature;
15
15
  frame?: WorkbenchFrame;
16
16
  headerAccessory?: TerminalHeaderAccessoryRenderer;
17
+ onPreviewChange?: TerminalPreviewChangeHandler;
17
18
  title?: string;
18
19
  typeId?: string;
19
20
  }
@@ -51,7 +52,7 @@ interface CreateTerminalWorkbenchContributionInput {
51
52
  typeId?: string;
52
53
  }
53
54
  declare const defaultTerminalWorkbenchTypeId = "workspace-terminal";
54
- declare function createTerminalWorkbenchNodeDefinition({ feature, frame, headerAccessory, title, typeId }: CreateTerminalWorkbenchNodeDefinitionInput): WorkbenchHostNodeDefinition<TerminalNodeExternalState>;
55
+ declare function createTerminalWorkbenchNodeDefinition({ feature, frame, headerAccessory, onPreviewChange, title, typeId }: CreateTerminalWorkbenchNodeDefinitionInput): WorkbenchHostNodeDefinition<TerminalNodeExternalState>;
55
56
  declare function createTerminalDockEntry({ dockIcon, feature, id, order, sectionId, typeId }: CreateTerminalDockEntryInput): WorkbenchHostDockEntry;
56
57
  declare function createTerminalWorkbenchLaunchHandler({ feature, frame, resolveLaunchInput, typeId }: CreateTerminalWorkbenchLaunchHandlerInput): (request: WorkbenchHostLaunchRequest) => Promise<WorkbenchHostLaunchResult | null>;
57
58
  declare function createTerminalWorkbenchContribution({ contributionId, dockEntry, externalStateSource, feature, getTerminalState, node, onCloseFailure, onConfirmClose, resolveLaunchInput, shouldCloseAfterCloseFailure, typeId }: CreateTerminalWorkbenchContributionInput): WorkbenchContribution;
@@ -2,7 +2,7 @@ import {
2
2
  TerminalNode,
3
3
  TerminalNodeHeader,
4
4
  acquireTerminalSessionController
5
- } from "../chunk-FEJT6FJ4.js";
5
+ } from "../chunk-UWPZ5O4V.js";
6
6
  import {
7
7
  closeTerminalSession,
8
8
  isTerminalSessionEndedStatus
@@ -15,13 +15,15 @@ import { createElement } from "react";
15
15
  // src/workbench/bodyProps.ts
16
16
  function resolveTerminalWorkbenchBodyProps({
17
17
  context,
18
- feature
18
+ feature,
19
+ onPreviewChange
19
20
  }) {
20
21
  return {
21
22
  externalState: context.externalNodeState,
22
23
  feature,
23
24
  nodeId: context.node.id,
24
25
  onFocusRequest: context.isFocused ? void 0 : () => context.focus(),
26
+ onPreviewChange,
25
27
  sessionId: context.node.data.instanceKey ?? null,
26
28
  showHeader: false
27
29
  };
@@ -94,6 +96,7 @@ function createTerminalWorkbenchNodeDefinition({
94
96
  feature,
95
97
  frame = defaultTerminalNodeFrame,
96
98
  headerAccessory,
99
+ onPreviewChange,
97
100
  title,
98
101
  typeId = defaultTerminalWorkbenchTypeId
99
102
  }) {
@@ -104,7 +107,7 @@ function createTerminalWorkbenchNodeDefinition({
104
107
  },
105
108
  renderBody: (context) => createElement(
106
109
  TerminalNode,
107
- resolveTerminalWorkbenchBodyProps({ context, feature })
110
+ resolveTerminalWorkbenchBodyProps({ context, feature, onPreviewChange })
108
111
  ),
109
112
  createLease: ({ node }) => {
110
113
  const sessionId = node.data.instanceKey ?? null;
@@ -182,10 +185,15 @@ function createTerminalDockEntry({
182
185
  launchBehavior: "enabled",
183
186
  matchNode: (node) => node.data.typeId === typeId,
184
187
  order,
185
- resolvePopupItem: ({ node }) => ({
186
- subtitle: node.data.instanceKey ?? node.data.instanceId,
187
- title: node.title
188
- }),
188
+ resolvePopupItem: ({ node }) => {
189
+ const subtitle = node.data.instanceKey ?? node.data.instanceId;
190
+ return {
191
+ revision: `${node.title}
192
+ ${subtitle}`,
193
+ subtitle,
194
+ title: node.title
195
+ };
196
+ },
189
197
  sectionId,
190
198
  typeId,
191
199
  visibility: "always"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/workbench/index.ts","../../src/workbench/bodyProps.ts","../../src/workbench/launchAnalytics.ts","../../src/workbench/windowCloseEffect.ts"],"sourcesContent":["import { createElement, type PointerEvent, type ReactNode } from \"react\";\nimport type {\n WorkbenchContribution,\n WorkbenchFrame,\n WorkbenchHostDockEntry,\n WorkbenchHostExternalStateSource,\n WorkbenchHostLaunchRequest,\n WorkbenchHostLaunchResult,\n WorkbenchHostNodeCloseDecision,\n WorkbenchHostNodeCloseRequest,\n WorkbenchHostNodeDefinition\n} from \"@tutti-os/workbench-surface\";\nimport type {\n TerminalCloseGuardResult,\n TerminalHeaderAccessoryRenderer,\n TerminalLaunchInput,\n TerminalNodeExternalState\n} from \"../contracts/index.ts\";\nimport { closeTerminalSession } from \"../core/index.ts\";\nimport type { TerminalNodeFeature } from \"../core/feature.ts\";\nimport { acquireTerminalSessionController } from \"../core/sessionController.ts\";\nimport { resolveTerminalWorkbenchBodyProps } from \"./bodyProps.ts\";\nimport { resolveTerminalLaunchAnalyticsTrigger } from \"./launchAnalytics.ts\";\nimport { resolveTerminalWindowCloseEffect } from \"./windowCloseEffect.ts\";\nimport { TerminalNode, TerminalNodeHeader } from \"../react/TerminalNode.tsx\";\n\nexport interface TerminalWorkbenchIntent {\n cwd?: string | null;\n initialInput?: string | null;\n profileId?: string | null;\n}\n\nexport interface CreateTerminalWorkbenchNodeDefinitionInput {\n feature: TerminalNodeFeature;\n frame?: WorkbenchFrame;\n headerAccessory?: TerminalHeaderAccessoryRenderer;\n title?: string;\n typeId?: string;\n}\n\nexport type TerminalWorkbenchLaunchInputResolver = (\n request: WorkbenchHostLaunchRequest\n) =>\n | Promise<Partial<Omit<TerminalLaunchInput, \"reason\" | \"workspaceId\">>>\n | Partial<Omit<TerminalLaunchInput, \"reason\" | \"workspaceId\">>;\n\nexport interface CreateTerminalWorkbenchLaunchHandlerInput {\n feature: TerminalNodeFeature;\n frame?: WorkbenchFrame;\n resolveLaunchInput?: TerminalWorkbenchLaunchInputResolver;\n typeId?: string;\n}\n\nexport interface CreateTerminalDockEntryInput {\n dockIcon?: ReactNode;\n feature: TerminalNodeFeature;\n id?: string;\n order?: number;\n sectionId?: string;\n typeId?: string;\n}\n\nexport interface TerminalWorkbenchContributionCloseFailure {\n error: unknown;\n sessionId: string;\n status?: TerminalNodeExternalState[\"status\"];\n}\n\nexport interface CreateTerminalWorkbenchContributionInput {\n contributionId?: string;\n dockEntry?: Omit<CreateTerminalDockEntryInput, \"feature\">;\n externalStateSource?: WorkbenchHostExternalStateSource<\n TerminalNodeExternalState | null,\n unknown\n >;\n feature: TerminalNodeFeature;\n getTerminalState?: (sessionId: string) => TerminalNodeExternalState | null;\n node?: Omit<CreateTerminalWorkbenchNodeDefinitionInput, \"feature\">;\n onCloseFailure?: (failure: TerminalWorkbenchContributionCloseFailure) => void;\n onConfirmClose?: (\n guard: TerminalCloseGuardResult\n ) => Promise<boolean> | boolean;\n resolveLaunchInput?: TerminalWorkbenchLaunchInputResolver;\n shouldCloseAfterCloseFailure?: (\n failure: TerminalWorkbenchContributionCloseFailure\n ) => boolean;\n typeId?: string;\n}\n\nexport const defaultTerminalWorkbenchTypeId = \"workspace-terminal\";\n\nconst defaultTerminalNodeFrame: WorkbenchFrame = {\n height: 520,\n width: 860,\n x: 260,\n y: 140\n};\n\nexport function createTerminalWorkbenchNodeDefinition({\n feature,\n frame = defaultTerminalNodeFrame,\n headerAccessory,\n title,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalWorkbenchNodeDefinitionInput): WorkbenchHostNodeDefinition<TerminalNodeExternalState> {\n return {\n frame,\n instance: {\n mode: \"multi\"\n },\n renderBody: (context) =>\n createElement(\n TerminalNode,\n resolveTerminalWorkbenchBodyProps({ context, feature })\n ),\n createLease: ({ node }) => {\n const sessionId = node.data.instanceKey ?? null;\n if (!sessionId) {\n return null;\n }\n const controller = acquireTerminalSessionController({\n feature,\n nodeId: node.id,\n sessionId\n });\n controller.retain();\n return {\n release() {\n controller.release();\n }\n };\n },\n getWindowCloseEffect: ({ externalNodeState, node }) =>\n resolveTerminalWindowCloseEffect({\n closeGuard: feature.closeGuard,\n description: feature.i18n.t(\"closeGuard.description\"),\n externalNodeState,\n nodeId: node.id,\n sessionId: node.data.instanceKey ?? null,\n title: node.title,\n typeId\n }),\n renderHeader: ({\n defaultActions,\n dragHandleProps,\n externalNodeState,\n isFocused,\n node,\n windowActions\n }) =>\n createElement(TerminalNodeHeader, {\n defaultActions,\n externalState: externalNodeState,\n feature,\n headerAccessory,\n onCloseRequest: () => windowActions.close(),\n sessionId: node.data.instanceKey ?? null,\n ...dragHandleProps,\n onPointerDown: (event: PointerEvent<HTMLElement>) => {\n dragHandleProps.onPointerDown?.(event);\n if (!isFocused) {\n windowActions.focus();\n }\n }\n }),\n title: title ?? feature.i18n.t(\"title\"),\n typeId,\n window: {\n closable: true,\n defaultOpen: false,\n minimizedDock: {\n kind: \"snapshot\"\n },\n minimizable: true,\n restoreOnLoad: true\n }\n };\n}\n\nexport function createTerminalDockEntry({\n dockIcon,\n feature,\n id,\n order,\n sectionId,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalDockEntryInput): WorkbenchHostDockEntry {\n return {\n icon: dockIcon ?? createElement(DefaultTerminalDockIcon),\n id: id ?? typeId,\n label: feature.i18n.t(\"dockLabel\"),\n launchBehavior: \"enabled\",\n matchNode: (node) => node.data.typeId === typeId,\n order,\n resolvePopupItem: ({ node }) => ({\n subtitle: node.data.instanceKey ?? node.data.instanceId,\n title: node.title\n }),\n sectionId,\n typeId,\n visibility: \"always\"\n };\n}\n\nexport function createTerminalWorkbenchLaunchHandler({\n feature,\n frame = defaultTerminalNodeFrame,\n resolveLaunchInput,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalWorkbenchLaunchHandlerInput): (\n request: WorkbenchHostLaunchRequest\n) => Promise<WorkbenchHostLaunchResult | null> {\n return async (request) => {\n if (request.typeId !== typeId) {\n return null;\n }\n\n const resolved = (await resolveLaunchInput?.(request)) ?? {};\n const descriptor = await feature.launchService.create({\n cwd: resolved.cwd,\n initialInput: resolved.initialInput,\n profileId: resolved.profileId,\n reason: request.reason === \"dock\" ? \"dock\" : \"intent\",\n workspaceId: request.workspaceId\n });\n\n return {\n activation: {\n payload: {\n trigger: resolveTerminalLaunchAnalyticsTrigger(request)\n },\n type: \"terminal-launch\"\n },\n defaultFrame: frame,\n dockEntryId: request.dockEntryId ?? typeId,\n framePolicy: \"cascade\",\n instanceId: descriptor.sessionId,\n instanceKey: descriptor.sessionId,\n title: descriptor.title,\n typeId\n };\n };\n}\n\nexport function createTerminalWorkbenchContribution({\n contributionId,\n dockEntry,\n externalStateSource,\n feature,\n getTerminalState,\n node,\n onCloseFailure,\n onConfirmClose,\n resolveLaunchInput,\n shouldCloseAfterCloseFailure,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalWorkbenchContributionInput): WorkbenchContribution {\n return {\n dockEntries: [\n createTerminalDockEntry({\n ...dockEntry,\n feature,\n typeId\n })\n ],\n externalStateSource,\n id: contributionId ?? typeId,\n nodes: [\n createTerminalWorkbenchNodeDefinition({\n ...node,\n feature,\n typeId\n })\n ],\n onLaunchRequest: createTerminalWorkbenchLaunchHandler({\n feature,\n frame: node?.frame,\n resolveLaunchInput,\n typeId\n }),\n onNodeCloseRequest: onConfirmClose\n ? (request) =>\n handleTerminalContributionNodeCloseRequest({\n feature,\n getTerminalState,\n onCloseFailure,\n onConfirmClose,\n request,\n shouldCloseAfterCloseFailure,\n typeId\n })\n : undefined\n };\n}\n\nasync function handleTerminalContributionNodeCloseRequest(input: {\n feature: TerminalNodeFeature;\n getTerminalState?: (sessionId: string) => TerminalNodeExternalState | null;\n onCloseFailure?: (failure: TerminalWorkbenchContributionCloseFailure) => void;\n onConfirmClose: (\n guard: TerminalCloseGuardResult\n ) => Promise<boolean> | boolean;\n request: WorkbenchHostNodeCloseRequest;\n shouldCloseAfterCloseFailure?: (\n failure: TerminalWorkbenchContributionCloseFailure\n ) => boolean;\n typeId: string;\n}): Promise<WorkbenchHostNodeCloseDecision | void> {\n if (input.request.typeId !== input.typeId) {\n return undefined;\n }\n\n const sessionId = input.request.instanceKey ?? input.request.instanceId;\n const terminalState = input.getTerminalState?.(sessionId) ?? null;\n try {\n const result = await closeTerminalSession({\n confirm: input.onConfirmClose,\n feature: input.feature,\n sessionId,\n status: terminalState?.status\n });\n return result === \"closed\" ? \"close\" : \"keep-open\";\n } catch (error) {\n const failure = {\n error,\n sessionId,\n status: terminalState?.status\n };\n input.onCloseFailure?.(failure);\n return input.shouldCloseAfterCloseFailure?.(failure)\n ? \"close\"\n : \"keep-open\";\n }\n}\n\nfunction DefaultTerminalDockIcon() {\n return createElement(\n \"span\",\n {\n \"aria-hidden\": \"true\",\n className: \"workspace-terminal__dock-icon\"\n },\n \">\"\n );\n}\n","import type { WorkbenchHostNodeBodyContext } from \"@tutti-os/workbench-surface\";\nimport type { TerminalNodeExternalState } from \"../contracts/index.ts\";\nimport type { TerminalNodeFeature } from \"../core/feature.ts\";\n\nexport interface TerminalWorkbenchBodyProps {\n externalState: TerminalNodeExternalState | null;\n feature: TerminalNodeFeature;\n nodeId: string;\n onFocusRequest?: () => void;\n sessionId: string | null;\n showHeader: boolean;\n}\n\nexport function resolveTerminalWorkbenchBodyProps({\n context,\n feature\n}: {\n context: WorkbenchHostNodeBodyContext<\n TerminalNodeExternalState | null,\n unknown\n >;\n feature: TerminalNodeFeature;\n}): TerminalWorkbenchBodyProps {\n // Keep this resolver narrower than TerminalNodeProps on purpose. The workbench\n // lease keeps the session alive while the node exists, but the mounted surface\n // must still retain the controller so snapshot hydration starts immediately.\n // Passing controllerLeaseRetainedExternally here makes a newly opened terminal\n // render blank until the first user input triggers controller.write().\n return {\n externalState: context.externalNodeState,\n feature,\n nodeId: context.node.id,\n onFocusRequest: context.isFocused ? undefined : () => context.focus(),\n sessionId: context.node.data.instanceKey ?? null,\n showHeader: false\n };\n}\n","import type { WorkbenchHostLaunchRequest } from \"@tutti-os/workbench-surface\";\n\nexport type TerminalLaunchAnalyticsTrigger =\n | \"agent_command\"\n | \"dock\"\n | \"keyboard\"\n | \"launchpad\";\n\nexport function resolveTerminalLaunchAnalyticsTrigger(\n request: WorkbenchHostLaunchRequest\n): TerminalLaunchAnalyticsTrigger {\n switch (request.reason) {\n case \"dock\":\n return \"dock\";\n case \"launchpad\":\n return \"launchpad\";\n case \"shortcut\":\n return \"keyboard\";\n case \"host\":\n return hasInitialInput(request.payload) ? \"agent_command\" : \"launchpad\";\n default:\n return \"launchpad\";\n }\n}\n\nfunction hasInitialInput(payload: unknown): boolean {\n if (!payload || typeof payload !== \"object\") {\n return false;\n }\n const typed = payload as { initialInput?: unknown };\n return (\n typeof typed.initialInput === \"string\" &&\n typed.initialInput.trim().length > 0\n );\n}\n","import type {\n TerminalCloseGuardService,\n TerminalNodeExternalState\n} from \"../contracts/index.ts\";\nimport { isTerminalSessionEndedStatus } from \"../core/sessionProjection.ts\";\n\nexport interface ResolveTerminalWindowCloseEffectInput {\n closeGuard: TerminalCloseGuardService;\n description: string;\n externalNodeState: TerminalNodeExternalState | null;\n nodeId: string;\n sessionId?: string | null;\n title: string;\n typeId: string;\n}\n\nexport async function resolveTerminalWindowCloseEffect({\n closeGuard,\n description,\n externalNodeState,\n nodeId,\n sessionId,\n title,\n typeId\n}: ResolveTerminalWindowCloseEffectInput) {\n const status = externalNodeState?.status ?? \"created\";\n if (status === \"created\" || isTerminalSessionEndedStatus(status)) {\n return null;\n }\n\n const resolvedSessionId = sessionId ?? externalNodeState?.sessionId ?? null;\n if (resolvedSessionId) {\n try {\n const guard = await closeGuard.check({ sessionId: resolvedSessionId });\n if (\n !guard.requiresConfirmation ||\n guard.reason === \"not-running\" ||\n isTerminalSessionEndedStatus(guard.status)\n ) {\n return null;\n }\n } catch {\n // Fall back to a conservative close effect when guard lookup fails.\n }\n }\n\n return {\n description,\n nodeId,\n title: externalNodeState?.title?.trim() || title,\n typeId\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/workbench/index.ts","../../src/workbench/bodyProps.ts","../../src/workbench/launchAnalytics.ts","../../src/workbench/windowCloseEffect.ts"],"sourcesContent":["import { createElement, type PointerEvent, type ReactNode } from \"react\";\nimport type {\n WorkbenchContribution,\n WorkbenchFrame,\n WorkbenchHostDockEntry,\n WorkbenchHostExternalStateSource,\n WorkbenchHostLaunchRequest,\n WorkbenchHostLaunchResult,\n WorkbenchHostNodeCloseDecision,\n WorkbenchHostNodeCloseRequest,\n WorkbenchHostNodeDefinition\n} from \"@tutti-os/workbench-surface\";\nimport type {\n TerminalCloseGuardResult,\n TerminalHeaderAccessoryRenderer,\n TerminalLaunchInput,\n TerminalNodeExternalState,\n TerminalPreviewChangeHandler\n} from \"../contracts/index.ts\";\nimport { closeTerminalSession } from \"../core/index.ts\";\nimport type { TerminalNodeFeature } from \"../core/feature.ts\";\nimport { acquireTerminalSessionController } from \"../core/sessionController.ts\";\nimport { resolveTerminalWorkbenchBodyProps } from \"./bodyProps.ts\";\nimport { resolveTerminalLaunchAnalyticsTrigger } from \"./launchAnalytics.ts\";\nimport { resolveTerminalWindowCloseEffect } from \"./windowCloseEffect.ts\";\nimport { TerminalNode, TerminalNodeHeader } from \"../react/TerminalNode.tsx\";\n\nexport interface TerminalWorkbenchIntent {\n cwd?: string | null;\n initialInput?: string | null;\n profileId?: string | null;\n}\n\nexport interface CreateTerminalWorkbenchNodeDefinitionInput {\n feature: TerminalNodeFeature;\n frame?: WorkbenchFrame;\n headerAccessory?: TerminalHeaderAccessoryRenderer;\n onPreviewChange?: TerminalPreviewChangeHandler;\n title?: string;\n typeId?: string;\n}\n\nexport type TerminalWorkbenchLaunchInputResolver = (\n request: WorkbenchHostLaunchRequest\n) =>\n | Promise<Partial<Omit<TerminalLaunchInput, \"reason\" | \"workspaceId\">>>\n | Partial<Omit<TerminalLaunchInput, \"reason\" | \"workspaceId\">>;\n\nexport interface CreateTerminalWorkbenchLaunchHandlerInput {\n feature: TerminalNodeFeature;\n frame?: WorkbenchFrame;\n resolveLaunchInput?: TerminalWorkbenchLaunchInputResolver;\n typeId?: string;\n}\n\nexport interface CreateTerminalDockEntryInput {\n dockIcon?: ReactNode;\n feature: TerminalNodeFeature;\n id?: string;\n order?: number;\n sectionId?: string;\n typeId?: string;\n}\n\nexport interface TerminalWorkbenchContributionCloseFailure {\n error: unknown;\n sessionId: string;\n status?: TerminalNodeExternalState[\"status\"];\n}\n\nexport interface CreateTerminalWorkbenchContributionInput {\n contributionId?: string;\n dockEntry?: Omit<CreateTerminalDockEntryInput, \"feature\">;\n externalStateSource?: WorkbenchHostExternalStateSource<\n TerminalNodeExternalState | null,\n unknown\n >;\n feature: TerminalNodeFeature;\n getTerminalState?: (sessionId: string) => TerminalNodeExternalState | null;\n node?: Omit<CreateTerminalWorkbenchNodeDefinitionInput, \"feature\">;\n onCloseFailure?: (failure: TerminalWorkbenchContributionCloseFailure) => void;\n onConfirmClose?: (\n guard: TerminalCloseGuardResult\n ) => Promise<boolean> | boolean;\n resolveLaunchInput?: TerminalWorkbenchLaunchInputResolver;\n shouldCloseAfterCloseFailure?: (\n failure: TerminalWorkbenchContributionCloseFailure\n ) => boolean;\n typeId?: string;\n}\n\nexport const defaultTerminalWorkbenchTypeId = \"workspace-terminal\";\n\nconst defaultTerminalNodeFrame: WorkbenchFrame = {\n height: 520,\n width: 860,\n x: 260,\n y: 140\n};\n\nexport function createTerminalWorkbenchNodeDefinition({\n feature,\n frame = defaultTerminalNodeFrame,\n headerAccessory,\n onPreviewChange,\n title,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalWorkbenchNodeDefinitionInput): WorkbenchHostNodeDefinition<TerminalNodeExternalState> {\n return {\n frame,\n instance: {\n mode: \"multi\"\n },\n renderBody: (context) =>\n createElement(\n TerminalNode,\n resolveTerminalWorkbenchBodyProps({ context, feature, onPreviewChange })\n ),\n createLease: ({ node }) => {\n const sessionId = node.data.instanceKey ?? null;\n if (!sessionId) {\n return null;\n }\n const controller = acquireTerminalSessionController({\n feature,\n nodeId: node.id,\n sessionId\n });\n controller.retain();\n return {\n release() {\n controller.release();\n }\n };\n },\n getWindowCloseEffect: ({ externalNodeState, node }) =>\n resolveTerminalWindowCloseEffect({\n closeGuard: feature.closeGuard,\n description: feature.i18n.t(\"closeGuard.description\"),\n externalNodeState,\n nodeId: node.id,\n sessionId: node.data.instanceKey ?? null,\n title: node.title,\n typeId\n }),\n renderHeader: ({\n defaultActions,\n dragHandleProps,\n externalNodeState,\n isFocused,\n node,\n windowActions\n }) =>\n createElement(TerminalNodeHeader, {\n defaultActions,\n externalState: externalNodeState,\n feature,\n headerAccessory,\n onCloseRequest: () => windowActions.close(),\n sessionId: node.data.instanceKey ?? null,\n ...dragHandleProps,\n onPointerDown: (event: PointerEvent<HTMLElement>) => {\n dragHandleProps.onPointerDown?.(event);\n if (!isFocused) {\n windowActions.focus();\n }\n }\n }),\n title: title ?? feature.i18n.t(\"title\"),\n typeId,\n window: {\n closable: true,\n defaultOpen: false,\n minimizedDock: {\n kind: \"snapshot\"\n },\n minimizable: true,\n restoreOnLoad: true\n }\n };\n}\n\nexport function createTerminalDockEntry({\n dockIcon,\n feature,\n id,\n order,\n sectionId,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalDockEntryInput): WorkbenchHostDockEntry {\n return {\n icon: dockIcon ?? createElement(DefaultTerminalDockIcon),\n id: id ?? typeId,\n label: feature.i18n.t(\"dockLabel\"),\n launchBehavior: \"enabled\",\n matchNode: (node) => node.data.typeId === typeId,\n order,\n resolvePopupItem: ({ node }) => {\n const subtitle = node.data.instanceKey ?? node.data.instanceId;\n return {\n revision: `${node.title}\\n${subtitle}`,\n subtitle,\n title: node.title\n };\n },\n sectionId,\n typeId,\n visibility: \"always\"\n };\n}\n\nexport function createTerminalWorkbenchLaunchHandler({\n feature,\n frame = defaultTerminalNodeFrame,\n resolveLaunchInput,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalWorkbenchLaunchHandlerInput): (\n request: WorkbenchHostLaunchRequest\n) => Promise<WorkbenchHostLaunchResult | null> {\n return async (request) => {\n if (request.typeId !== typeId) {\n return null;\n }\n\n const resolved = (await resolveLaunchInput?.(request)) ?? {};\n const descriptor = await feature.launchService.create({\n cwd: resolved.cwd,\n initialInput: resolved.initialInput,\n profileId: resolved.profileId,\n reason: request.reason === \"dock\" ? \"dock\" : \"intent\",\n workspaceId: request.workspaceId\n });\n\n return {\n activation: {\n payload: {\n trigger: resolveTerminalLaunchAnalyticsTrigger(request)\n },\n type: \"terminal-launch\"\n },\n defaultFrame: frame,\n dockEntryId: request.dockEntryId ?? typeId,\n framePolicy: \"cascade\",\n instanceId: descriptor.sessionId,\n instanceKey: descriptor.sessionId,\n title: descriptor.title,\n typeId\n };\n };\n}\n\nexport function createTerminalWorkbenchContribution({\n contributionId,\n dockEntry,\n externalStateSource,\n feature,\n getTerminalState,\n node,\n onCloseFailure,\n onConfirmClose,\n resolveLaunchInput,\n shouldCloseAfterCloseFailure,\n typeId = defaultTerminalWorkbenchTypeId\n}: CreateTerminalWorkbenchContributionInput): WorkbenchContribution {\n return {\n dockEntries: [\n createTerminalDockEntry({\n ...dockEntry,\n feature,\n typeId\n })\n ],\n externalStateSource,\n id: contributionId ?? typeId,\n nodes: [\n createTerminalWorkbenchNodeDefinition({\n ...node,\n feature,\n typeId\n })\n ],\n onLaunchRequest: createTerminalWorkbenchLaunchHandler({\n feature,\n frame: node?.frame,\n resolveLaunchInput,\n typeId\n }),\n onNodeCloseRequest: onConfirmClose\n ? (request) =>\n handleTerminalContributionNodeCloseRequest({\n feature,\n getTerminalState,\n onCloseFailure,\n onConfirmClose,\n request,\n shouldCloseAfterCloseFailure,\n typeId\n })\n : undefined\n };\n}\n\nasync function handleTerminalContributionNodeCloseRequest(input: {\n feature: TerminalNodeFeature;\n getTerminalState?: (sessionId: string) => TerminalNodeExternalState | null;\n onCloseFailure?: (failure: TerminalWorkbenchContributionCloseFailure) => void;\n onConfirmClose: (\n guard: TerminalCloseGuardResult\n ) => Promise<boolean> | boolean;\n request: WorkbenchHostNodeCloseRequest;\n shouldCloseAfterCloseFailure?: (\n failure: TerminalWorkbenchContributionCloseFailure\n ) => boolean;\n typeId: string;\n}): Promise<WorkbenchHostNodeCloseDecision | void> {\n if (input.request.typeId !== input.typeId) {\n return undefined;\n }\n\n const sessionId = input.request.instanceKey ?? input.request.instanceId;\n const terminalState = input.getTerminalState?.(sessionId) ?? null;\n try {\n const result = await closeTerminalSession({\n confirm: input.onConfirmClose,\n feature: input.feature,\n sessionId,\n status: terminalState?.status\n });\n return result === \"closed\" ? \"close\" : \"keep-open\";\n } catch (error) {\n const failure = {\n error,\n sessionId,\n status: terminalState?.status\n };\n input.onCloseFailure?.(failure);\n return input.shouldCloseAfterCloseFailure?.(failure)\n ? \"close\"\n : \"keep-open\";\n }\n}\n\nfunction DefaultTerminalDockIcon() {\n return createElement(\n \"span\",\n {\n \"aria-hidden\": \"true\",\n className: \"workspace-terminal__dock-icon\"\n },\n \">\"\n );\n}\n","import type { WorkbenchHostNodeBodyContext } from \"@tutti-os/workbench-surface\";\nimport type {\n TerminalNodeExternalState,\n TerminalPreviewChangeHandler\n} from \"../contracts/index.ts\";\nimport type { TerminalNodeFeature } from \"../core/feature.ts\";\n\nexport interface TerminalWorkbenchBodyProps {\n externalState: TerminalNodeExternalState | null;\n feature: TerminalNodeFeature;\n nodeId: string;\n onFocusRequest?: () => void;\n onPreviewChange?: TerminalPreviewChangeHandler;\n sessionId: string | null;\n showHeader: boolean;\n}\n\nexport function resolveTerminalWorkbenchBodyProps({\n context,\n feature,\n onPreviewChange\n}: {\n context: WorkbenchHostNodeBodyContext<\n TerminalNodeExternalState | null,\n unknown\n >;\n feature: TerminalNodeFeature;\n onPreviewChange?: TerminalPreviewChangeHandler;\n}): TerminalWorkbenchBodyProps {\n // Keep this resolver narrower than TerminalNodeProps on purpose. The workbench\n // lease keeps the session alive while the node exists, but the mounted surface\n // must still retain the controller so snapshot hydration starts immediately.\n // Passing controllerLeaseRetainedExternally here makes a newly opened terminal\n // render blank until the first user input triggers controller.write().\n return {\n externalState: context.externalNodeState,\n feature,\n nodeId: context.node.id,\n onFocusRequest: context.isFocused ? undefined : () => context.focus(),\n onPreviewChange,\n sessionId: context.node.data.instanceKey ?? null,\n showHeader: false\n };\n}\n","import type { WorkbenchHostLaunchRequest } from \"@tutti-os/workbench-surface\";\n\nexport type TerminalLaunchAnalyticsTrigger =\n | \"agent_command\"\n | \"dock\"\n | \"keyboard\"\n | \"launchpad\";\n\nexport function resolveTerminalLaunchAnalyticsTrigger(\n request: WorkbenchHostLaunchRequest\n): TerminalLaunchAnalyticsTrigger {\n switch (request.reason) {\n case \"dock\":\n return \"dock\";\n case \"launchpad\":\n return \"launchpad\";\n case \"shortcut\":\n return \"keyboard\";\n case \"host\":\n return hasInitialInput(request.payload) ? \"agent_command\" : \"launchpad\";\n default:\n return \"launchpad\";\n }\n}\n\nfunction hasInitialInput(payload: unknown): boolean {\n if (!payload || typeof payload !== \"object\") {\n return false;\n }\n const typed = payload as { initialInput?: unknown };\n return (\n typeof typed.initialInput === \"string\" &&\n typed.initialInput.trim().length > 0\n );\n}\n","import type {\n TerminalCloseGuardService,\n TerminalNodeExternalState\n} from \"../contracts/index.ts\";\nimport { isTerminalSessionEndedStatus } from \"../core/sessionProjection.ts\";\n\nexport interface ResolveTerminalWindowCloseEffectInput {\n closeGuard: TerminalCloseGuardService;\n description: string;\n externalNodeState: TerminalNodeExternalState | null;\n nodeId: string;\n sessionId?: string | null;\n title: string;\n typeId: string;\n}\n\nexport async function resolveTerminalWindowCloseEffect({\n closeGuard,\n description,\n externalNodeState,\n nodeId,\n sessionId,\n title,\n typeId\n}: ResolveTerminalWindowCloseEffectInput) {\n const status = externalNodeState?.status ?? \"created\";\n if (status === \"created\" || isTerminalSessionEndedStatus(status)) {\n return null;\n }\n\n const resolvedSessionId = sessionId ?? externalNodeState?.sessionId ?? null;\n if (resolvedSessionId) {\n try {\n const guard = await closeGuard.check({ sessionId: resolvedSessionId });\n if (\n !guard.requiresConfirmation ||\n guard.reason === \"not-running\" ||\n isTerminalSessionEndedStatus(guard.status)\n ) {\n return null;\n }\n } catch {\n // Fall back to a conservative close effect when guard lookup fails.\n }\n }\n\n return {\n description,\n nodeId,\n title: externalNodeState?.title?.trim() || title,\n typeId\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tutti-os/workspace-terminal",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -37,10 +37,10 @@
37
37
  "directory": "packages/workspace/terminal"
38
38
  },
39
39
  "dependencies": {
40
- "@tutti-os/ui-i18n-runtime": "0.0.6",
41
- "@tutti-os/ui-react-hooks": "0.0.6",
42
- "@tutti-os/ui-system": "0.0.6",
43
- "@tutti-os/workbench-surface": "0.0.6",
40
+ "@tutti-os/ui-i18n-runtime": "0.0.8",
41
+ "@tutti-os/ui-react-hooks": "0.0.8",
42
+ "@tutti-os/ui-system": "0.0.8",
43
+ "@tutti-os/workbench-surface": "0.0.8",
44
44
  "@xterm/addon-fit": "^0.11.0",
45
45
  "@xterm/addon-search": "^0.16.0",
46
46
  "@xterm/addon-serialize": "^0.14.0",