@qodalis/react-cli 2.0.0-beta.1 → 2.0.0-beta.11

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/index.js ADDED
@@ -0,0 +1,974 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.tsx
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ Cli: () => Cli,
34
+ CliConfigProvider: () => CliConfigProvider,
35
+ CliContext: () => CliContext,
36
+ CliEngine: () => import_cli2.CliEngine,
37
+ CliPanel: () => CliPanel,
38
+ CliProvider: () => CliProvider,
39
+ useCli: () => useCli,
40
+ useCliConfig: () => useCliConfig,
41
+ useCliEngine: () => useCliEngine
42
+ });
43
+ module.exports = __toCommonJS(index_exports);
44
+
45
+ // src/Cli.tsx
46
+ var import_react4 = require("react");
47
+
48
+ // src/CliContext.tsx
49
+ var import_react = require("react");
50
+ var CliContext = (0, import_react.createContext)({ engine: null });
51
+ function useCli() {
52
+ return (0, import_react.useContext)(CliContext);
53
+ }
54
+
55
+ // src/CliConfigContext.tsx
56
+ var import_react2 = require("react");
57
+ var import_jsx_runtime = require("react/jsx-runtime");
58
+ var CliConfigCtx = (0, import_react2.createContext)({});
59
+ function useCliConfig() {
60
+ return (0, import_react2.useContext)(CliConfigCtx);
61
+ }
62
+ function CliConfigProvider({
63
+ modules,
64
+ processors,
65
+ options,
66
+ services,
67
+ children
68
+ }) {
69
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CliConfigCtx.Provider, { value: { modules, processors, options, services }, children });
70
+ }
71
+
72
+ // src/useCliEngine.ts
73
+ var import_react3 = require("react");
74
+ var import_cli = require("@qodalis/cli");
75
+ function useCliEngine(containerRef, config) {
76
+ const [engine, setEngine] = (0, import_react3.useState)(null);
77
+ const engineRef = (0, import_react3.useRef)(null);
78
+ const initTimerRef = (0, import_react3.useRef)(null);
79
+ (0, import_react3.useEffect)(() => {
80
+ if (config?.disabled || !containerRef.current) return;
81
+ const container = containerRef.current;
82
+ initTimerRef.current = setTimeout(() => {
83
+ initTimerRef.current = null;
84
+ if (!container) return;
85
+ const e = new import_cli.CliEngine(container, config?.options);
86
+ e.registerService("cli-framework", "React");
87
+ if (config?.services) {
88
+ for (const [token, value] of Object.entries(config.services)) {
89
+ e.registerService(token, value);
90
+ }
91
+ }
92
+ if (config?.modules) {
93
+ e.registerModules(config.modules);
94
+ }
95
+ if (config?.processors) {
96
+ e.registerProcessors(config.processors);
97
+ }
98
+ engineRef.current = e;
99
+ e.start().then(() => setEngine(e));
100
+ }, 0);
101
+ return () => {
102
+ if (initTimerRef.current !== null) {
103
+ clearTimeout(initTimerRef.current);
104
+ initTimerRef.current = null;
105
+ return;
106
+ }
107
+ if (engineRef.current) {
108
+ engineRef.current.destroy();
109
+ engineRef.current = null;
110
+ setEngine(null);
111
+ }
112
+ };
113
+ }, []);
114
+ return engine;
115
+ }
116
+
117
+ // src/Cli.tsx
118
+ var import_jsx_runtime2 = require("react/jsx-runtime");
119
+ function Cli({
120
+ modules,
121
+ processors,
122
+ options,
123
+ services,
124
+ snapshot,
125
+ onReady,
126
+ style,
127
+ className
128
+ }) {
129
+ const ctx = useCli();
130
+ const config = useCliConfig();
131
+ const containerRef = (0, import_react4.useRef)(null);
132
+ const hasProvider = !!ctx.engine;
133
+ const mergedModules = modules ?? config.modules;
134
+ const mergedProcessors = processors ?? config.processors;
135
+ const mergedOptions = options ?? config.options;
136
+ const mergedServices = services ?? config.services;
137
+ const optionsWithSnapshot = snapshot ? { ...mergedOptions, snapshot } : mergedOptions;
138
+ const standaloneEngine = useCliEngine(containerRef, {
139
+ modules: mergedModules,
140
+ processors: mergedProcessors,
141
+ options: optionsWithSnapshot,
142
+ services: mergedServices,
143
+ disabled: hasProvider
144
+ });
145
+ const engine = ctx.engine ?? standaloneEngine;
146
+ (0, import_react4.useEffect)(() => {
147
+ if (engine && onReady) {
148
+ onReady(engine);
149
+ }
150
+ }, [engine, onReady]);
151
+ if (hasProvider) {
152
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
153
+ }
154
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
155
+ "div",
156
+ {
157
+ ref: containerRef,
158
+ style: { height: "100%", ...style },
159
+ className
160
+ }
161
+ );
162
+ }
163
+
164
+ // src/CliPanel.tsx
165
+ var import_react5 = __toESM(require("react"));
166
+ var import_cli_core = require("@qodalis/cli-core");
167
+ var import_jsx_runtime3 = require("react/jsx-runtime");
168
+ var MIN_PANE_WIDTH_PERCENT = 10;
169
+ var TerminalIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
170
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 17 10 11 4 5" }),
171
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
172
+ ] });
173
+ var MaximizeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
174
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "15 3 21 3 21 9" }),
175
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "9 21 3 21 3 15" }),
176
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
177
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
178
+ ] });
179
+ var RestoreIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
180
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 14 10 14 10 20" }),
181
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "20 10 14 10 14 4" }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
184
+ ] });
185
+ var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
186
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
188
+ ] });
189
+ var HideIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
190
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "5", y1: "18", x2: "19", y2: "18" }),
191
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "9 14 12 17 15 14" })
192
+ ] });
193
+ function CollapseChevron({ position, isCollapsed }) {
194
+ let points;
195
+ switch (position) {
196
+ case "top":
197
+ points = isCollapsed ? "6 9 12 15 18 9" : "18 15 12 9 6 15";
198
+ break;
199
+ case "left":
200
+ points = isCollapsed ? "9 18 15 12 9 6" : "15 6 9 12 15 18";
201
+ break;
202
+ case "right":
203
+ points = isCollapsed ? "15 6 9 12 15 18" : "9 18 15 12 9 6";
204
+ break;
205
+ default:
206
+ points = isCollapsed ? "18 15 12 9 6 15" : "6 9 12 15 18 9";
207
+ break;
208
+ }
209
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points }) });
210
+ }
211
+ function HideTabChevron({ position }) {
212
+ let points;
213
+ switch (position) {
214
+ case "top":
215
+ points = "6 9 12 15 18 9";
216
+ break;
217
+ case "left":
218
+ points = "9 18 15 12 9 6";
219
+ break;
220
+ case "right":
221
+ points = "15 6 9 12 15 18";
222
+ break;
223
+ default:
224
+ points = "18 15 12 9 6 15";
225
+ break;
226
+ }
227
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "cli-panel-hide-tab-arrow", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points }) });
228
+ }
229
+ function PositionIcon({ position, size = 20 }) {
230
+ let fillRect;
231
+ switch (position) {
232
+ case "top":
233
+ fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "4", y: "4", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
234
+ break;
235
+ case "left":
236
+ fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "4", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
237
+ break;
238
+ case "right":
239
+ fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "15", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
240
+ break;
241
+ default:
242
+ fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "4", y: "15", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
243
+ break;
244
+ }
245
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
246
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
247
+ fillRect
248
+ ] });
249
+ }
250
+ var CliPanel = import_react5.default.forwardRef(
251
+ function CliPanel2(props, ref) {
252
+ const { options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className } = props;
253
+ const config = useCliConfig();
254
+ const options = optionsProp ?? config.options;
255
+ const modules = modulesProp ?? config.modules;
256
+ const processors = processorsProp ?? config.processors;
257
+ const services = servicesProp ?? config.services;
258
+ const [internalPosition, setInternalPosition] = (0, import_react5.useState)(
259
+ () => (0, import_cli_core.loadPanelPosition)() ?? options?.position ?? "bottom"
260
+ );
261
+ const [internalCollapsed, setInternalCollapsed] = (0, import_react5.useState)(options?.isCollapsed ?? true);
262
+ const [internalHidden, setInternalHidden] = (0, import_react5.useState)(options?.isHidden ?? false);
263
+ const [internalMaximized, setInternalMaximized] = (0, import_react5.useState)(false);
264
+ const [internalHeight, setInternalHeight] = (0, import_react5.useState)(600);
265
+ const [internalWidth, setInternalWidth] = (0, import_react5.useState)(400);
266
+ const [internalActiveTabId, setInternalActiveTabId] = (0, import_react5.useState)(0);
267
+ const position = props.position !== void 0 ? props.position : internalPosition;
268
+ const collapsed = props.collapsed !== void 0 ? props.collapsed : internalCollapsed;
269
+ const hidden = props.hidden !== void 0 ? props.hidden : internalHidden;
270
+ const maximized = props.maximized !== void 0 ? props.maximized : internalMaximized;
271
+ const panelHeight = props.height !== void 0 ? props.height : internalHeight;
272
+ const panelWidth = props.width !== void 0 ? props.width : internalWidth;
273
+ const activeTabId = props.activeTabId !== void 0 ? props.activeTabId : internalActiveTabId;
274
+ const updateCollapsed = (0, import_react5.useCallback)((value) => {
275
+ setInternalCollapsed(value);
276
+ props.onCollapsedChange?.(value);
277
+ }, [props.onCollapsedChange]);
278
+ const updateHidden = (0, import_react5.useCallback)((value) => {
279
+ setInternalHidden(value);
280
+ props.onHiddenChange?.(value);
281
+ }, [props.onHiddenChange]);
282
+ const updateMaximized = (0, import_react5.useCallback)((value) => {
283
+ setInternalMaximized(value);
284
+ props.onMaximizedChange?.(value);
285
+ }, [props.onMaximizedChange]);
286
+ const updateHeight = (0, import_react5.useCallback)((value) => {
287
+ setInternalHeight(value);
288
+ props.onHeightChange?.(value);
289
+ }, [props.onHeightChange]);
290
+ const updateWidth = (0, import_react5.useCallback)((value) => {
291
+ setInternalWidth(value);
292
+ props.onWidthChange?.(value);
293
+ }, [props.onWidthChange]);
294
+ const updateActiveTabId = (0, import_react5.useCallback)((value) => {
295
+ setInternalActiveTabId(value);
296
+ props.onActiveTabIdChange?.(value);
297
+ }, [props.onActiveTabIdChange]);
298
+ const updatePosition = (0, import_react5.useCallback)((value) => {
299
+ setInternalPosition(value);
300
+ (0, import_cli_core.savePanelPosition)(value);
301
+ props.onPositionChange?.(value);
302
+ }, [props.onPositionChange]);
303
+ const closable = options?.closable ?? true;
304
+ const resizable = options?.resizable ?? true;
305
+ const isHorizontal = position === "left" || position === "right";
306
+ const hideable = options?.hideable ?? true;
307
+ const hideAlignment = options?.hideAlignment ?? "center";
308
+ const syncTheme = options?.syncTheme ?? false;
309
+ const [visible, setVisible] = (0, import_react5.useState)(true);
310
+ const [initialized, setInitialized] = (0, import_react5.useState)(false);
311
+ const preHideCollapsedRef = (0, import_react5.useRef)(true);
312
+ const [themeStyles, setThemeStyles] = (0, import_react5.useState)({});
313
+ const wrapperRef = (0, import_react5.useRef)(null);
314
+ const [tabs, setTabs] = (0, import_react5.useState)([]);
315
+ const [activePaneId, setActivePaneId] = (0, import_react5.useState)(0);
316
+ const nextIdRef = (0, import_react5.useRef)({ tab: 1, pane: 1 });
317
+ const [contextMenu, setContextMenu] = (0, import_react5.useState)({ visible: false, x: 0, y: 0, tabId: 0 });
318
+ const engineMapRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
319
+ const activeTabIdRef = (0, import_react5.useRef)(activeTabId);
320
+ activeTabIdRef.current = activeTabId;
321
+ const activePaneIdRef = (0, import_react5.useRef)(activePaneId);
322
+ activePaneIdRef.current = activePaneId;
323
+ const [paneResizing, setPaneResizing] = (0, import_react5.useState)(false);
324
+ const paneResizeRef = (0, import_react5.useRef)({ tabId: 0, dividerIndex: 0, startX: 0, startWidths: [], containerWidth: 0 });
325
+ const [panelResizing, setPanelResizing] = (0, import_react5.useState)(false);
326
+ const panelResizeRef = (0, import_react5.useRef)({ startY: 0, startHeight: 0, startX: 0, startWidth: 0 });
327
+ const prevHeightRef = (0, import_react5.useRef)(600);
328
+ const prevWidthRef = (0, import_react5.useRef)(400);
329
+ const normalizePanes = (0, import_react5.useCallback)((panes) => {
330
+ const total = panes.reduce((s, p) => s + p.widthPercent, 0);
331
+ if (total === 0) return;
332
+ const scale = 100 / total;
333
+ panes.forEach((p) => {
334
+ p.widthPercent *= scale;
335
+ });
336
+ }, []);
337
+ const addTab = (0, import_react5.useCallback)((title) => {
338
+ const tabId = nextIdRef.current.tab++;
339
+ const paneId = nextIdRef.current.pane++;
340
+ const pane = { id: paneId, widthPercent: 100 };
341
+ const tab = { id: tabId, title: title ?? `Terminal ${tabId}`, isEditing: false, panes: [pane] };
342
+ setTabs((prev) => [...prev, tab]);
343
+ updateActiveTabId(tabId);
344
+ setActivePaneId(paneId);
345
+ props.onTabAdded?.({ tabId });
346
+ return tabId;
347
+ }, [updateActiveTabId, props.onTabAdded]);
348
+ const toggle = (0, import_react5.useCallback)(() => {
349
+ if (collapsed) {
350
+ updateCollapsed(false);
351
+ if (!initialized) {
352
+ setInitialized(true);
353
+ addTab();
354
+ }
355
+ } else {
356
+ updateCollapsed(true);
357
+ }
358
+ }, [collapsed, initialized, updateCollapsed, addTab]);
359
+ const closeTab = (0, import_react5.useCallback)((id) => {
360
+ setTabs((prev) => {
361
+ const tab = prev.find((t) => t.id === id);
362
+ if (tab) {
363
+ tab.panes.forEach((p) => {
364
+ engineMapRef.current.get(p.id)?.destroy();
365
+ engineMapRef.current.delete(p.id);
366
+ });
367
+ }
368
+ const next = prev.filter((t) => t.id !== id);
369
+ if (next.length === 0) {
370
+ setInitialized(false);
371
+ updateCollapsed(true);
372
+ return [];
373
+ }
374
+ if (activeTabIdRef.current === id) {
375
+ const oldIdx = prev.findIndex((t) => t.id === id);
376
+ const newIdx = Math.min(oldIdx, next.length - 1);
377
+ updateActiveTabId(next[newIdx].id);
378
+ }
379
+ return next;
380
+ });
381
+ props.onTabClosed?.({ tabId: id });
382
+ }, [updateCollapsed, updateActiveTabId, props.onTabClosed]);
383
+ const selectTab = (0, import_react5.useCallback)((id) => {
384
+ updateActiveTabId(id);
385
+ setTabs((prev) => {
386
+ const tab = prev.find((t) => t.id === id);
387
+ if (tab && tab.panes.length > 0) {
388
+ setActivePaneId(tab.panes[0].id);
389
+ }
390
+ return prev;
391
+ });
392
+ }, [updateActiveTabId]);
393
+ const renameTab = (0, import_react5.useCallback)((tabId, title) => {
394
+ setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, title } : t));
395
+ }, []);
396
+ const startRename = (0, import_react5.useCallback)((tabId) => {
397
+ setTabs((prev) => prev.map((t) => ({ ...t, isEditing: t.id === tabId })));
398
+ }, []);
399
+ const commitRename = (0, import_react5.useCallback)((tabId, value) => {
400
+ const trimmed = value.trim();
401
+ setTabs((prev) => prev.map(
402
+ (t) => t.id === tabId ? { ...t, title: trimmed || t.title, isEditing: false } : t
403
+ ));
404
+ }, []);
405
+ const splitPane = (0, import_react5.useCallback)((tabId) => {
406
+ const targetTabId = tabId ?? activeTabIdRef.current;
407
+ const paneId = nextIdRef.current.pane++;
408
+ setTabs((prev) => prev.map((t) => {
409
+ if (t.id !== targetTabId) return t;
410
+ const newPanes = [...t.panes.map((p) => ({ ...p })), { id: paneId, widthPercent: 0 }];
411
+ normalizePanes(newPanes);
412
+ return { ...t, panes: newPanes };
413
+ }));
414
+ setActivePaneId(paneId);
415
+ props.onPaneSplit?.({ paneId, tabId: targetTabId });
416
+ return paneId;
417
+ }, [normalizePanes, props.onPaneSplit]);
418
+ const closePane = (0, import_react5.useCallback)((paneId) => {
419
+ setTabs((prev) => {
420
+ for (let i = 0; i < prev.length; i++) {
421
+ const tab = prev[i];
422
+ const idx = tab.panes.findIndex((p) => p.id === paneId);
423
+ if (idx === -1) continue;
424
+ if (tab.panes.length <= 1) {
425
+ setTimeout(() => closeTab(tab.id), 0);
426
+ return prev;
427
+ }
428
+ const newPanes = tab.panes.filter((p) => p.id !== paneId);
429
+ normalizePanes(newPanes);
430
+ const newTabs = [...prev];
431
+ newTabs[i] = { ...tab, panes: newPanes };
432
+ engineMapRef.current.get(paneId)?.destroy();
433
+ engineMapRef.current.delete(paneId);
434
+ if (activePaneIdRef.current === paneId) {
435
+ setActivePaneId(newPanes[Math.min(idx, newPanes.length - 1)].id);
436
+ }
437
+ props.onPaneClosed?.({ paneId });
438
+ return newTabs;
439
+ }
440
+ return prev;
441
+ });
442
+ }, [closeTab, normalizePanes, props.onPaneClosed]);
443
+ const closeContextMenu = (0, import_react5.useCallback)(() => {
444
+ setContextMenu((prev) => ({ ...prev, visible: false }));
445
+ }, []);
446
+ (0, import_react5.useEffect)(() => {
447
+ if (!contextMenu.visible) return;
448
+ const handler = () => closeContextMenu();
449
+ document.addEventListener("click", handler);
450
+ return () => document.removeEventListener("click", handler);
451
+ }, [contextMenu.visible, closeContextMenu]);
452
+ const onPanelResizeStart = (0, import_react5.useCallback)((e) => {
453
+ if (!resizable) return;
454
+ e.preventDefault();
455
+ if (collapsed) {
456
+ toggle();
457
+ }
458
+ setPanelResizing(true);
459
+ panelResizeRef.current = {
460
+ startY: e.clientY,
461
+ startHeight: panelHeight,
462
+ startX: e.clientX,
463
+ startWidth: panelWidth
464
+ };
465
+ }, [collapsed, toggle, panelHeight, panelWidth, resizable]);
466
+ (0, import_react5.useEffect)(() => {
467
+ if (!panelResizing) return;
468
+ const onMove = (e) => {
469
+ if (isHorizontal) {
470
+ const deltaX = position === "left" ? e.clientX - panelResizeRef.current.startX : panelResizeRef.current.startX - e.clientX;
471
+ let next = Math.max(100, panelResizeRef.current.startWidth + deltaX);
472
+ if (next > window.innerWidth) next = window.innerWidth;
473
+ updateWidth(next);
474
+ } else {
475
+ const deltaY = position === "top" ? e.clientY - panelResizeRef.current.startY : panelResizeRef.current.startY - e.clientY;
476
+ let next = Math.max(100, panelResizeRef.current.startHeight + deltaY);
477
+ if (next > window.innerHeight) next = window.innerHeight;
478
+ updateHeight(next);
479
+ }
480
+ };
481
+ const onUp = () => setPanelResizing(false);
482
+ document.addEventListener("mousemove", onMove);
483
+ document.addEventListener("mouseup", onUp);
484
+ return () => {
485
+ document.removeEventListener("mousemove", onMove);
486
+ document.removeEventListener("mouseup", onUp);
487
+ };
488
+ }, [panelResizing, isHorizontal, position, updateWidth, updateHeight]);
489
+ const onPaneResizeStart = (0, import_react5.useCallback)((e, tabId, dividerIndex) => {
490
+ e.preventDefault();
491
+ const tab = tabs.find((t) => t.id === tabId);
492
+ if (!tab) return;
493
+ setPaneResizing(true);
494
+ const container = e.target.closest(".cli-panel-panes-container");
495
+ paneResizeRef.current = {
496
+ tabId,
497
+ dividerIndex,
498
+ startX: e.clientX,
499
+ startWidths: tab.panes.map((p) => p.widthPercent),
500
+ containerWidth: container ? container.clientWidth : 1
501
+ };
502
+ }, [tabs]);
503
+ (0, import_react5.useEffect)(() => {
504
+ if (!paneResizing) return;
505
+ const onMove = (e) => {
506
+ const ref2 = paneResizeRef.current;
507
+ const deltaX = e.clientX - ref2.startX;
508
+ const deltaPct = deltaX / ref2.containerWidth * 100;
509
+ const i = ref2.dividerIndex;
510
+ let leftWidth = ref2.startWidths[i] + deltaPct;
511
+ let rightWidth = ref2.startWidths[i + 1] - deltaPct;
512
+ if (leftWidth < MIN_PANE_WIDTH_PERCENT) {
513
+ leftWidth = MIN_PANE_WIDTH_PERCENT;
514
+ rightWidth = ref2.startWidths[i] + ref2.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
515
+ }
516
+ if (rightWidth < MIN_PANE_WIDTH_PERCENT) {
517
+ rightWidth = MIN_PANE_WIDTH_PERCENT;
518
+ leftWidth = ref2.startWidths[i] + ref2.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
519
+ }
520
+ setTabs((prev) => {
521
+ const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
522
+ const tab = next.find((t) => t.id === ref2.tabId);
523
+ if (tab) {
524
+ tab.panes[i].widthPercent = leftWidth;
525
+ tab.panes[i + 1].widthPercent = rightWidth;
526
+ }
527
+ return next;
528
+ });
529
+ };
530
+ const onUp = () => {
531
+ setPaneResizing(false);
532
+ document.body.classList.remove("cli-pane-resizing");
533
+ };
534
+ document.body.classList.add("cli-pane-resizing");
535
+ document.addEventListener("mousemove", onMove);
536
+ document.addEventListener("mouseup", onUp);
537
+ return () => {
538
+ document.removeEventListener("mousemove", onMove);
539
+ document.removeEventListener("mouseup", onUp);
540
+ };
541
+ }, [paneResizing]);
542
+ (0, import_react5.useEffect)(() => {
543
+ if (!syncTheme || !initialized) return;
544
+ const syncFromEngine = () => {
545
+ const engine = engineMapRef.current.values().next().value;
546
+ if (!engine) return;
547
+ const theme = engine.getTerminal().options.theme;
548
+ if (theme) setThemeStyles((0, import_cli_core.derivePanelThemeStyles)(theme));
549
+ };
550
+ const timer = setTimeout(() => {
551
+ syncFromEngine();
552
+ const container = wrapperRef.current?.querySelector(".terminal-container");
553
+ if (!container) return;
554
+ const observer = new MutationObserver(syncFromEngine);
555
+ observer.observe(container, { attributes: true, attributeFilter: ["style"] });
556
+ return () => observer.disconnect();
557
+ }, 200);
558
+ return () => clearTimeout(timer);
559
+ }, [syncTheme, initialized]);
560
+ const toggleMaximize = (0, import_react5.useCallback)(() => {
561
+ const nextMaximized = !maximized;
562
+ if (nextMaximized) {
563
+ if (isHorizontal) {
564
+ prevWidthRef.current = panelWidth;
565
+ updateWidth(window.innerWidth);
566
+ } else {
567
+ prevHeightRef.current = panelHeight;
568
+ updateHeight(window.innerHeight);
569
+ }
570
+ } else {
571
+ if (isHorizontal) {
572
+ updateWidth(prevWidthRef.current);
573
+ } else {
574
+ updateHeight(prevHeightRef.current);
575
+ }
576
+ }
577
+ updateMaximized(nextMaximized);
578
+ }, [maximized, panelHeight, panelWidth, isHorizontal, updateMaximized, updateWidth, updateHeight]);
579
+ const handleClose = (0, import_react5.useCallback)(() => {
580
+ setVisible(false);
581
+ onClose?.();
582
+ }, [onClose]);
583
+ const handleHide = (0, import_react5.useCallback)(() => {
584
+ preHideCollapsedRef.current = collapsed;
585
+ updateHidden(true);
586
+ }, [collapsed, updateHidden]);
587
+ const handleUnhide = (0, import_react5.useCallback)(() => {
588
+ updateHidden(false);
589
+ updateCollapsed(preHideCollapsedRef.current);
590
+ }, [updateHidden, updateCollapsed]);
591
+ const [positionDropdownOpen, setPositionDropdownOpen] = (0, import_react5.useState)(false);
592
+ const closePositionDropdown = (0, import_react5.useCallback)(() => setPositionDropdownOpen(false), []);
593
+ (0, import_react5.useEffect)(() => {
594
+ if (!positionDropdownOpen) return;
595
+ document.addEventListener("click", closePositionDropdown);
596
+ return () => document.removeEventListener("click", closePositionDropdown);
597
+ }, [positionDropdownOpen, closePositionDropdown]);
598
+ const handlePositionButtonClick = (0, import_react5.useCallback)((e) => {
599
+ e.stopPropagation();
600
+ setPositionDropdownOpen((prev) => !prev);
601
+ }, []);
602
+ const selectPosition = (0, import_react5.useCallback)((pos) => {
603
+ setPositionDropdownOpen(false);
604
+ updatePosition(pos);
605
+ }, [updatePosition]);
606
+ const getEngine = (0, import_react5.useCallback)((paneId) => {
607
+ const targetId = paneId ?? activePaneIdRef.current;
608
+ return engineMapRef.current.get(targetId);
609
+ }, []);
610
+ const getState = (0, import_react5.useCallback)(() => ({
611
+ collapsed,
612
+ hidden,
613
+ maximized,
614
+ position,
615
+ height: panelHeight,
616
+ width: panelWidth,
617
+ activeTabId,
618
+ activePaneId,
619
+ tabs: tabs.map((t) => ({
620
+ id: t.id,
621
+ title: t.title,
622
+ panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent }))
623
+ }))
624
+ }), [collapsed, hidden, maximized, position, panelHeight, panelWidth, activeTabId, activePaneId, tabs]);
625
+ import_react5.default.useImperativeHandle(ref, () => ({
626
+ open: () => {
627
+ if (!visible) {
628
+ setVisible(true);
629
+ }
630
+ if (hidden) {
631
+ updateHidden(false);
632
+ }
633
+ if (collapsed) {
634
+ updateCollapsed(false);
635
+ if (!initialized) {
636
+ setInitialized(true);
637
+ addTab();
638
+ }
639
+ }
640
+ },
641
+ collapse: () => {
642
+ if (!collapsed) updateCollapsed(true);
643
+ },
644
+ toggleCollapse: () => {
645
+ if (collapsed) {
646
+ updateCollapsed(false);
647
+ if (!initialized) {
648
+ setInitialized(true);
649
+ addTab();
650
+ }
651
+ } else {
652
+ updateCollapsed(true);
653
+ }
654
+ },
655
+ hide: () => {
656
+ if (!hidden) updateHidden(true);
657
+ },
658
+ unhide: () => {
659
+ if (hidden) updateHidden(false);
660
+ },
661
+ toggleHide: () => {
662
+ updateHidden(!hidden);
663
+ },
664
+ close: handleClose,
665
+ maximize: () => {
666
+ if (!maximized) updateMaximized(true);
667
+ },
668
+ restore: () => {
669
+ if (maximized) updateMaximized(false);
670
+ },
671
+ toggleMaximize: () => {
672
+ updateMaximized(!maximized);
673
+ },
674
+ resize: (dims) => {
675
+ if (dims.height !== void 0) updateHeight(dims.height);
676
+ if (dims.width !== void 0) updateWidth(dims.width);
677
+ },
678
+ setPosition: updatePosition,
679
+ addTab,
680
+ closeTab,
681
+ selectTab,
682
+ renameTab,
683
+ splitPane,
684
+ closePane,
685
+ getEngine,
686
+ getState
687
+ }), [
688
+ collapsed,
689
+ hidden,
690
+ maximized,
691
+ initialized,
692
+ updateCollapsed,
693
+ updateHidden,
694
+ updateMaximized,
695
+ updateHeight,
696
+ updateWidth,
697
+ updatePosition,
698
+ handleClose,
699
+ addTab,
700
+ closeTab,
701
+ selectTab,
702
+ renameTab,
703
+ splitPane,
704
+ closePane,
705
+ getEngine,
706
+ getState
707
+ ]);
708
+ if (!visible) return null;
709
+ const wrapperStyle = {
710
+ ...isHorizontal ? { width: `${panelWidth}px` } : { height: `${panelHeight}px` },
711
+ ...hidden ? { display: "none" } : {},
712
+ ...themeStyles,
713
+ ...style
714
+ };
715
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
716
+ hidden && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
717
+ "button",
718
+ {
719
+ className: "cli-panel-hide-tab",
720
+ "data-position": position,
721
+ "data-hide-align": hideAlignment,
722
+ style: themeStyles,
723
+ title: "Show CLI",
724
+ onClick: handleUnhide,
725
+ children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { className: "cli-panel-hide-tab-icon", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
727
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 17 10 11 4 5" }),
728
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
729
+ ] }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-hide-tab-label", children: "CLI" }),
731
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HideTabChevron, { position })
732
+ ]
733
+ }
734
+ ),
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
736
+ "div",
737
+ {
738
+ ref: wrapperRef,
739
+ className: `cli-panel-wrapper ${collapsed ? "collapsed" : ""} ${maximized ? "maximized" : ""} ${panelResizing ? "resizing" : ""} ${className ?? ""}`,
740
+ style: wrapperStyle,
741
+ "data-position": position,
742
+ "data-resizable": String(resizable),
743
+ "data-closable": String(closable),
744
+ "data-hideable": String(hideable),
745
+ children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header", children: [
747
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-resize-bar", onMouseDown: onPanelResizeStart, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-resize-grip" }) }),
748
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header-content", children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "cli-panel-title", children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TerminalIcon, {}) }),
751
+ "CLI"
752
+ ] }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-action-buttons", children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
755
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-position", title: "Move panel", onClick: handlePositionButtonClick, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position }) }),
756
+ positionDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-position-dropdown", children: ["bottom", "top", "left", "right"].map((pos) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
757
+ "button",
758
+ {
759
+ className: `cli-panel-position-dropdown-item${position === pos ? " active" : ""}`,
760
+ onClick: () => selectPosition(pos),
761
+ children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position: pos, size: 16 }),
763
+ pos.charAt(0).toUpperCase() + pos.slice(1)
764
+ ]
765
+ },
766
+ pos
767
+ )) })
768
+ ] }),
769
+ hideable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-hide", title: "Hide", onClick: handleHide, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HideIcon, {}) }),
770
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn", title: maximized ? "Restore" : "Maximize", disabled: collapsed, onClick: toggleMaximize, children: maximized ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MaximizeIcon, {}) }),
771
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn", title: collapsed ? "Expand" : "Collapse", onClick: toggle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CollapseChevron, { position, isCollapsed: collapsed }) }),
772
+ closable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-close", title: "Close", onClick: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}) })
773
+ ] })
774
+ ] })
775
+ ] }),
776
+ initialized && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
777
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-tabs", children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "cli-panel-tab-list", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
779
+ "li",
780
+ {
781
+ className: `cli-panel-tab ${tab.id === activeTabId ? "active" : ""}`,
782
+ onClick: () => selectTab(tab.id),
783
+ onDoubleClick: () => startRename(tab.id),
784
+ onContextMenu: (e) => {
785
+ e.preventDefault();
786
+ e.stopPropagation();
787
+ setContextMenu({ visible: true, x: e.clientX, y: e.clientY, tabId: tab.id });
788
+ },
789
+ children: tab.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
790
+ "input",
791
+ {
792
+ className: "cli-panel-tab-rename-input",
793
+ type: "text",
794
+ defaultValue: tab.title,
795
+ autoFocus: true,
796
+ onKeyDown: (e) => {
797
+ if (e.key === "Enter") commitRename(tab.id, e.target.value);
798
+ else if (e.key === "Escape") setTabs((prev) => prev.map((t) => ({ ...t, isEditing: false })));
799
+ },
800
+ onBlur: (e) => commitRename(tab.id, e.target.value),
801
+ onClick: (e) => e.stopPropagation(),
802
+ onDoubleClick: (e) => e.stopPropagation()
803
+ }
804
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
805
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-tab-title", children: tab.title }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
+ "button",
808
+ {
809
+ className: "cli-panel-tab-close-btn",
810
+ title: "Close tab",
811
+ onClick: (e) => {
812
+ e.stopPropagation();
813
+ closeTab(tab.id);
814
+ },
815
+ children: "\xD7"
816
+ }
817
+ )
818
+ ] })
819
+ },
820
+ tab.id
821
+ )) }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-add-tab", title: "New terminal", onClick: () => addTab(), children: "+" })
823
+ ] }),
824
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-instances", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-instance", style: { display: tab.id === activeTabId ? void 0 : "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `cli-panel-panes-container ${paneResizing ? "resizing" : ""}`, children: tab.panes.map((pane, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react5.default.Fragment, { children: [
825
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-pane-divider", onMouseDown: (e) => onPaneResizeStart(e, tab.id, i - 1), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-pane-divider-grip" }) }),
826
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
827
+ "div",
828
+ {
829
+ className: `cli-panel-pane ${pane.id === activePaneId && tab.id === activeTabId ? "active-pane" : ""}`,
830
+ style: { flex: `${pane.widthPercent} 1 0` },
831
+ onClick: () => {
832
+ setActivePaneId(pane.id);
833
+ updateActiveTabId(tab.id);
834
+ },
835
+ children: [
836
+ tab.panes.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
837
+ "button",
838
+ {
839
+ className: "cli-panel-pane-close-btn",
840
+ onClick: (e) => {
841
+ e.stopPropagation();
842
+ closePane(pane.id);
843
+ },
844
+ title: "Close pane",
845
+ children: "\xD7"
846
+ }
847
+ ),
848
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
849
+ Cli,
850
+ {
851
+ options,
852
+ modules,
853
+ processors,
854
+ services,
855
+ snapshot: pane.snapshot,
856
+ onReady: (engine) => engineMapRef.current.set(pane.id, engine)
857
+ }
858
+ ) })
859
+ ]
860
+ }
861
+ )
862
+ ] }, pane.id)) }) }, tab.id)) })
863
+ ] })
864
+ ]
865
+ }
866
+ ),
867
+ contextMenu.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
868
+ "div",
869
+ {
870
+ className: "cli-panel-context-menu",
871
+ style: { left: contextMenu.x, top: contextMenu.y },
872
+ onClick: (e) => e.stopPropagation(),
873
+ children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
875
+ closeContextMenu();
876
+ startRename(contextMenu.tabId);
877
+ }, children: "Rename" }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
879
+ closeContextMenu();
880
+ const tab = tabs.find((t) => t.id === contextMenu.tabId);
881
+ if (!tab) return;
882
+ const sourceEngine = engineMapRef.current.get(tab.panes[0]?.id);
883
+ const snapshot = sourceEngine?.snapshot();
884
+ const paneId = nextIdRef.current.pane++;
885
+ const tabId = nextIdRef.current.tab++;
886
+ const pane = { id: paneId, widthPercent: 100, snapshot };
887
+ const newTab = { id: tabId, title: `${tab.title} (copy)`, isEditing: false, panes: [pane] };
888
+ const idx = tabs.indexOf(tab);
889
+ setTabs((prev) => {
890
+ const next = [...prev];
891
+ next.splice(idx + 1, 0, newTab);
892
+ return next;
893
+ });
894
+ updateActiveTabId(tabId);
895
+ setActivePaneId(paneId);
896
+ props.onTabAdded?.({ tabId });
897
+ }, children: "Duplicate" }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
899
+ closeContextMenu();
900
+ splitPane(contextMenu.tabId);
901
+ }, children: "Split Right" }),
902
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
903
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
904
+ closeContextMenu();
905
+ closeTab(contextMenu.tabId);
906
+ }, children: "Close" }),
907
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", disabled: tabs.length <= 1, onClick: () => {
908
+ closeContextMenu();
909
+ setTabs((prev) => prev.filter((t) => t.id === contextMenu.tabId));
910
+ updateActiveTabId(contextMenu.tabId);
911
+ }, children: "Close Others" }),
912
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
913
+ const id = contextMenu.tabId;
914
+ closeContextMenu();
915
+ setTabs((prev) => {
916
+ const idx = prev.findIndex((t) => t.id === id);
917
+ if (idx === -1) return prev;
918
+ return prev.slice(0, idx + 1);
919
+ });
920
+ }, children: "Close to the Right" }),
921
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
922
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item destructive", onClick: () => {
923
+ closeContextMenu();
924
+ setTabs([]);
925
+ setInitialized(false);
926
+ updateCollapsed(true);
927
+ }, children: "Close All" })
928
+ ]
929
+ }
930
+ )
931
+ ] });
932
+ }
933
+ );
934
+
935
+ // src/CliProvider.tsx
936
+ var import_react6 = require("react");
937
+ var import_jsx_runtime4 = require("react/jsx-runtime");
938
+ function CliProvider({
939
+ modules,
940
+ processors,
941
+ options,
942
+ services,
943
+ children,
944
+ style
945
+ }) {
946
+ const config = useCliConfig();
947
+ const containerRef = (0, import_react6.useRef)(null);
948
+ const engine = useCliEngine(containerRef, {
949
+ modules: modules ?? config.modules,
950
+ processors: processors ?? config.processors,
951
+ options: options ?? config.options,
952
+ services: services ?? config.services
953
+ });
954
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(CliContext.Provider, { value: { engine }, children: [
955
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: containerRef, style: { height: "100%", ...style } }),
956
+ children
957
+ ] });
958
+ }
959
+
960
+ // src/index.tsx
961
+ var import_cli2 = require("@qodalis/cli");
962
+ // Annotate the CommonJS export names for ESM import in node:
963
+ 0 && (module.exports = {
964
+ Cli,
965
+ CliConfigProvider,
966
+ CliContext,
967
+ CliEngine,
968
+ CliPanel,
969
+ CliProvider,
970
+ useCli,
971
+ useCliConfig,
972
+ useCliEngine
973
+ });
974
+ //# sourceMappingURL=index.js.map