@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.
- package/dist/{chunk-FEJT6FJ4.js → chunk-UWPZ5O4V.js} +233 -1
- package/dist/chunk-UWPZ5O4V.js.map +1 -0
- package/dist/contracts/index.d.ts +33 -1
- package/dist/react/index.d.ts +15 -3
- package/dist/react/index.js +185 -1
- package/dist/react/index.js.map +1 -1
- package/dist/styles/terminal.css +48 -0
- package/dist/workbench/index.d.ts +3 -2
- package/dist/workbench/index.js +15 -7
- package/dist/workbench/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-FEJT6FJ4.js.map +0 -1
package/dist/react/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 };
|
package/dist/react/index.js
CHANGED
|
@@ -2,11 +2,195 @@ import {
|
|
|
2
2
|
TerminalCloseGuardDialog,
|
|
3
3
|
TerminalNode,
|
|
4
4
|
TerminalNodeHeader
|
|
5
|
-
} from "../chunk-
|
|
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
|
};
|
package/dist/react/index.js.map
CHANGED
|
@@ -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":";;;;;;;;;AAAA,SAAS,iBAAiB,QAAQ,gBAAgB;AA8H9B;AAjGpB,IAAM,gCAAgC;AACtC,IAAM,oCAAoC;AAC1C,IAAM,8BAA8B;AACpC,IAAM,6BAA6B;AAE5B,SAAS,oBAAoB;AAAA,EAClC,QAAQ;AAAA,EACR;AAAA,EACA,QAAQ;AACV,GAA6B;AAC3B,QAAM,eAAe,OAA+B,IAAI;AACxD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS;AAAA,IACnC,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,aAAa;AAAA,IACb,OAAO;AAAA,EACT,CAAC;AACD,QAAM,QAAkC,CAAC;AACzC,MAAI,OAAO,YAAY;AACrB,UAAM,8CAA8C,IAAI,MAAM;AAAA,EAChE;AACA,MAAI,OAAO,YAAY;AACrB,UAAM,8CAA8C,IAAI,MAAM;AAAA,EAChE;AACA,MAAI,OAAO,QAAQ,KAAK,OAAO,SAAS,GAAG;AACzC,UAAM,0CAA0C,IAAI,GAAG,OAAO,MAAM;AACpE,UAAM,yCAAyC,IAAI,GAAG,OAAO,KAAK;AAClE,UAAM,yCAAyC,IAAI,OAAO,OAAO,KAAK;AACtE,UAAM,iDAAiD,IACrD,GAAG,OAAO,YAAY;AACxB,UAAM,gDAAgD,IACpD,GAAG,OAAO,WAAW;AAAA,EACzB;AAEA,kBAAgB,MAAM;AACpB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,aAAa,OAAO,mBAAmB,aAAa;AACvD;AAAA,IACF;AACA,UAAM,iBAAiB,MAAM;AAC3B,YAAM,gBAAgB,qCAAqC,SAAS;AACpE,YAAM,iBAAiB,cAAc;AACrC,YAAM,kBAAkB,cAAc;AACtC,YAAM,eAAe,gCAAgC,OAAO,QAAQ;AACpE,YAAM,aAAa,qCAAqC,OAAO,QAAQ;AACvE,YAAM,aAAa,2BAA2B;AAAA,QAC5C;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AACD,YAAM,YACJ,WAAW,QAAQ,KAAK,WAAW,SAAS,IACxC,KAAK;AAAA,QACH,WAAW,QAAQ,WAAW;AAAA,QAC9B,WAAW,SAAS,WAAW;AAAA,MACjC,IACA;AACN,gBAAU,CAAC,YAAY;AACrB,YACE,KAAK,IAAI,QAAQ,SAAS,WAAW,MAAM,IAAI,OAC/C,KAAK,IAAI,QAAQ,QAAQ,SAAS,IAAI,QACtC,KAAK,IAAI,QAAQ,eAAe,WAAW,MAAM,IAAI,OACrD,KAAK,IAAI,QAAQ,cAAc,WAAW,KAAK,IAAI,OACnD,KAAK,IAAI,QAAQ,QAAQ,WAAW,KAAK,IAAI,KAC7C;AACA,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,UACL,QAAQ,WAAW;AAAA,UACnB,OAAO;AAAA,UACP,cAAc,WAAW;AAAA,UACzB,aAAa,WAAW;AAAA,UACxB,OAAO,WAAW;AAAA,QACpB;AAAA,MACF,CAAC;AAAA,IACH;AACA,mBAAe;AACf,UAAM,WAAW,IAAI,eAAe,cAAc;AAClD,aAAS,QAAQ,SAAS;AAC1B,WAAO,MAAM;AACX,eAAS,WAAW;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,MAAM,SAAS,MAAM,MAAM,CAAC;AAEhD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,8BAA2B;AAAA,MAC3B,KAAK;AAAA,MAEL,8BAAC,UAAK,WAAU,6CAA4C,OAC1D,8BAAC,UAAK,WAAU,oCACb,mBAAS,MAAM,IAAI,CAAC,MAAM,UACzB,oBAAC,UAAK,WAAU,yCACb,eAAK,SAAS,SAAS,IACpB,KAAK,SAAS,IAAI,CAAC,SAAS,iBAC1B;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UAEV,OAAO;AAAA,YACL,QAAQ;AAAA,UACV;AAAA,UAEC,kBAAQ;AAAA;AAAA,QALJ;AAAA,MAMP,CACD,IACD,OAbuD,KAc7D,CACD,GACH,GACF;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,qCAAqC,SAAsB;AAClE,QAAM,gBAAgB,OAAO,iBAAiB,OAAO;AACrD,QAAM,gBAAgB,OAAO,WAAW,cAAc,KAAK;AAC3D,QAAM,iBAAiB,OAAO,WAAW,cAAc,MAAM;AAC7D,MACE,OAAO,SAAS,aAAa,KAC7B,gBAAgB,KAChB,OAAO,SAAS,cAAc,KAC9B,iBAAiB,GACjB;AACA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,KAAK,IAAI,GAAG,QAAQ,YAAY;AAAA,IACxC,OAAO,KAAK,IAAI,GAAG,QAAQ,WAAW;AAAA,EACxC;AACF;AAEA,SAAS,gCACP,OACA,UACA;AACA,MAAI,SAAS,MAAM,QAAQ,KAAK,MAAM,SAAS,GAAG;AAChD,WAAO,MAAM,QAAQ,MAAM;AAAA,EAC7B;AACA,QAAM,WAAW,KAAK,IAAI,GAAG,SAAS,MAAM,MAAM;AAClD,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,MAAM,GAAG,CAAC;AACzD,SACG,WAAW,qCACX,WAAW;AAEhB;AAEA,SAAS,qCACP,OACA,UACA;AACA,MAAI,SAAS,MAAM,QAAQ,KAAK,MAAM,SAAS,GAAG;AAChD,WAAO;AAAA,MACL,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,QAAM,WAAW,KAAK,IAAI,GAAG,SAAS,MAAM,MAAM;AAClD,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,MAAM,GAAG,CAAC;AACzD,SAAO;AAAA,IACL,QACE,WAAW,8BAA8B,gCACzC;AAAA,IACF,OACE,WACE,8BACA,oCACF;AAAA,EACJ;AACF;AAEA,SAAS,2BAA2B,OAIjC;AACD,MACE,MAAM,mBAAmB,KACzB,MAAM,kBAAkB,KACxB,CAAC,OAAO,SAAS,MAAM,KAAK,KAC5B,MAAM,SAAS,GACf;AACA,WAAO,EAAE,QAAQ,GAAG,OAAO,EAAE;AAAA,EAC/B;AAEA,QAAM,sBAAsB,MAAM,kBAAkB,MAAM;AAC1D,MAAI,uBAAuB,MAAM,gBAAgB;AAC/C,WAAO;AAAA,MACL,QAAQ,MAAM;AAAA,MACd,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,MAAM,iBAAiB,MAAM;AAAA,IACrC,OAAO,MAAM;AAAA,EACf;AACF;AAEA,SAAS,uCACP,cAC2B;AAC3B,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,QAAM,QAAuB,CAAC;AAC9B,MAAI,aAAa,YAAY;AAC3B,UAAM,kBAAkB,aAAa;AAAA,EACvC;AACA,MAAI,aAAa,OAAO;AACtB,UAAM,QAAQ,aAAa;AAAA,EAC7B;AACA,MAAI,aAAa,MAAM;AACrB,UAAM,aAAa;AAAA,EACrB;AACA,MAAI,aAAa,KAAK;AACpB,UAAM,UAAU;AAAA,EAClB;AACA,MAAI,aAAa,QAAQ;AACvB,UAAM,YAAY;AAAA,EACpB;AAEA,QAAM,cAAwB,CAAC;AAC/B,MAAI,aAAa,WAAW;AAC1B,gBAAY,KAAK,WAAW;AAAA,EAC9B;AACA,MAAI,aAAa,eAAe;AAC9B,gBAAY,KAAK,cAAc;AAAA,EACjC;AACA,MAAI,aAAa,UAAU;AACzB,gBAAY,KAAK,UAAU;AAAA,EAC7B;AACA,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,qBAAqB,YAAY,KAAK,GAAG;AAAA,EACjD;AAEA,SAAO;AACT;","names":[]}
|
package/dist/styles/terminal.css
CHANGED
|
@@ -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;
|
package/dist/workbench/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
TerminalNode,
|
|
3
3
|
TerminalNodeHeader,
|
|
4
4
|
acquireTerminalSessionController
|
|
5
|
-
} from "../chunk-
|
|
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
|
|
187
|
-
|
|
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.
|
|
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.
|
|
41
|
-
"@tutti-os/ui-react-hooks": "0.0.
|
|
42
|
-
"@tutti-os/ui-system": "0.0.
|
|
43
|
-
"@tutti-os/workbench-surface": "0.0.
|
|
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",
|