@qodalis/react-cli 2.0.0-beta.16 → 2.0.0-beta.18

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.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/Cli.tsx
2
2
  import { useRef as useRef2, useEffect as useEffect2 } from "react";
3
+ import { resolveCliHostConfig } from "@qodalis/cli";
3
4
 
4
5
  // src/CliContext.tsx
5
6
  import { createContext, useContext } from "react";
@@ -27,7 +28,7 @@ function CliConfigProvider({
27
28
 
28
29
  // src/useCliEngine.ts
29
30
  import { useRef, useEffect, useState } from "react";
30
- import { CliEngine } from "@qodalis/cli";
31
+ import { createCliEngine } from "@qodalis/cli";
31
32
  function useCliEngine(containerRef, config) {
32
33
  const [engine, setEngine] = useState(null);
33
34
  const engineRef = useRef(null);
@@ -38,19 +39,7 @@ function useCliEngine(containerRef, config) {
38
39
  initTimerRef.current = setTimeout(() => {
39
40
  initTimerRef.current = null;
40
41
  if (!container) return;
41
- const e = new CliEngine(container, config?.options);
42
- e.registerService("cli-framework", "React");
43
- if (config?.services) {
44
- for (const [token, value] of Object.entries(config.services)) {
45
- e.registerService(token, value);
46
- }
47
- }
48
- if (config?.modules) {
49
- e.registerModules(config.modules);
50
- }
51
- if (config?.processors) {
52
- e.registerProcessors(config.processors);
53
- }
42
+ const e = createCliEngine(container, { ...config, framework: "React" });
54
43
  engineRef.current = e;
55
44
  e.start().then(() => setEngine(e));
56
45
  }, 0);
@@ -86,16 +75,9 @@ function Cli({
86
75
  const config = useCliConfig();
87
76
  const containerRef = useRef2(null);
88
77
  const hasProvider = !!ctx.engine;
89
- const mergedModules = modules ?? config.modules;
90
- const mergedProcessors = processors ?? config.processors;
91
- const mergedOptions = options ?? config.options;
92
- const mergedServices = services ?? config.services;
93
- const optionsWithSnapshot = snapshot ? { ...mergedOptions, snapshot } : mergedOptions;
94
78
  const standaloneEngine = useCliEngine(containerRef, {
95
- modules: mergedModules,
96
- processors: mergedProcessors,
97
- options: optionsWithSnapshot,
98
- services: mergedServices,
79
+ ...resolveCliHostConfig({ modules, processors, options, services }, config),
80
+ snapshot,
99
81
  disabled: hasProvider
100
82
  });
101
83
  const engine = ctx.engine ?? standaloneEngine;
@@ -118,1062 +100,351 @@ function Cli({
118
100
  }
119
101
 
120
102
  // src/CliPanel.tsx
121
- import React3, { useState as useState2, useRef as useRef3, useEffect as useEffect3, useCallback } from "react";
122
- import { derivePanelThemeStyles, loadPanelPosition, savePanelPosition } from "@qodalis/cli-core";
103
+ import React3, { useEffect as useEffect3, useLayoutEffect, useState as useState2, useSyncExternalStore } from "react";
104
+ import {
105
+ CliPanelController,
106
+ CLOSE_GLYPH,
107
+ CLOSE_ICON,
108
+ HIDE_ICON,
109
+ HIDE_TAB_TERMINAL_ICON,
110
+ MAXIMIZE_ICON,
111
+ PANEL_LABELS,
112
+ RESTORE_ICON,
113
+ TERMINAL_ICON,
114
+ collapseChevronIcon,
115
+ contextMenuItems,
116
+ contextMenuStyle,
117
+ glowLineClass,
118
+ handleRenameKey,
119
+ hideTabChevronIcon,
120
+ joinClasses,
121
+ mergeCliOptions,
122
+ openStatusDropdown,
123
+ paneClass,
124
+ paneStyle,
125
+ panelTabClass,
126
+ panelTabDotClass,
127
+ panelWrapperClass,
128
+ panelWrapperStyle,
129
+ panesContainerClass,
130
+ panesContainerWidth,
131
+ positionIcon,
132
+ positionOptions,
133
+ resolveCliHostConfig as resolveCliHostConfig2,
134
+ runContextMenuAction,
135
+ statusIndicators,
136
+ svgAttrs
137
+ } from "@qodalis/cli";
123
138
  import { Fragment as Fragment2, jsx as jsx3, jsxs } from "react/jsx-runtime";
124
- var MIN_PANE_WIDTH_PERCENT = 10;
125
- var TerminalIcon = () => /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
126
- /* @__PURE__ */ jsx3("polyline", { points: "4 17 10 11 4 5" }),
127
- /* @__PURE__ */ jsx3("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
128
- ] });
129
- var MaximizeIcon = () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
130
- /* @__PURE__ */ jsx3("polyline", { points: "15 3 21 3 21 9" }),
131
- /* @__PURE__ */ jsx3("polyline", { points: "9 21 3 21 3 15" }),
132
- /* @__PURE__ */ jsx3("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
133
- /* @__PURE__ */ jsx3("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
134
- ] });
135
- var RestoreIcon = () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
136
- /* @__PURE__ */ jsx3("polyline", { points: "4 14 10 14 10 20" }),
137
- /* @__PURE__ */ jsx3("polyline", { points: "20 10 14 10 14 4" }),
138
- /* @__PURE__ */ jsx3("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
139
- /* @__PURE__ */ jsx3("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
140
- ] });
141
- var CloseIcon = () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
142
- /* @__PURE__ */ jsx3("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
143
- /* @__PURE__ */ jsx3("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
144
- ] });
145
- var HideIcon = () => /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
146
- /* @__PURE__ */ jsx3("line", { x1: "5", y1: "18", x2: "19", y2: "18" }),
147
- /* @__PURE__ */ jsx3("polyline", { points: "9 14 12 17 15 14" })
148
- ] });
149
- function CollapseChevron({ position, isCollapsed }) {
150
- let points;
151
- switch (position) {
152
- case "top":
153
- points = isCollapsed ? "6 9 12 15 18 9" : "18 15 12 9 6 15";
154
- break;
155
- case "left":
156
- points = isCollapsed ? "9 18 15 12 9 6" : "15 6 9 12 15 18";
157
- break;
158
- case "right":
159
- points = isCollapsed ? "15 6 9 12 15 18" : "9 18 15 12 9 6";
160
- break;
161
- default:
162
- points = isCollapsed ? "18 15 12 9 6 15" : "6 9 12 15 18 9";
163
- break;
139
+ function reactSvgProps(attrs) {
140
+ const props = {};
141
+ for (const [key, value] of Object.entries(attrs)) {
142
+ props[key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = value;
164
143
  }
165
- return /* @__PURE__ */ jsx3("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("polyline", { points }) });
144
+ return props;
166
145
  }
167
- function HideTabChevron({ position }) {
168
- let points;
169
- switch (position) {
170
- case "top":
171
- points = "6 9 12 15 18 9";
172
- break;
173
- case "left":
174
- points = "9 18 15 12 9 6";
175
- break;
176
- case "right":
177
- points = "15 6 9 12 15 18";
178
- break;
179
- default:
180
- points = "18 15 12 9 6 15";
181
- break;
182
- }
183
- return /* @__PURE__ */ jsx3("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__ */ jsx3("polyline", { points }) });
146
+ function Icon({ icon, className }) {
147
+ return /* @__PURE__ */ jsx3("svg", { className, ...reactSvgProps(svgAttrs(icon)), children: icon.shapes.map((shape, i) => React3.createElement(shape.tag, { key: i, ...reactSvgProps(shape.attrs) })) });
184
148
  }
185
- function PositionIcon({ position, size = 20 }) {
186
- let fillRect;
187
- switch (position) {
188
- case "top":
189
- fillRect = /* @__PURE__ */ jsx3("rect", { x: "4", y: "4", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
190
- break;
191
- case "left":
192
- fillRect = /* @__PURE__ */ jsx3("rect", { x: "4", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
193
- break;
194
- case "right":
195
- fillRect = /* @__PURE__ */ jsx3("rect", { x: "15", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
196
- break;
197
- default:
198
- fillRect = /* @__PURE__ */ jsx3("rect", { x: "4", y: "15", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
199
- break;
149
+ var focusAndSelect = (input) => {
150
+ if (input && document.activeElement !== input) {
151
+ input.focus();
152
+ input.select();
200
153
  }
201
- return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
202
- /* @__PURE__ */ jsx3("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
203
- fillRect
204
- ] });
205
- }
206
- var CliPanel = React3.forwardRef(
207
- function CliPanel2(props, ref) {
208
- const { options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className } = props;
209
- const config = useCliConfig();
210
- const options = optionsProp ? { ...config.options, ...optionsProp } : config.options;
211
- const modules = modulesProp ?? config.modules;
212
- const processors = processorsProp ?? config.processors;
213
- const services = servicesProp ?? config.services;
214
- const [internalPosition, setInternalPosition] = useState2(
215
- () => loadPanelPosition() ?? options?.position ?? "bottom"
216
- );
217
- const [internalCollapsed, setInternalCollapsed] = useState2(options?.isCollapsed ?? true);
218
- const [internalHidden, setInternalHidden] = useState2(options?.isHidden ?? false);
219
- const [internalMaximized, setInternalMaximized] = useState2(false);
220
- const [internalHeight, setInternalHeight] = useState2(600);
221
- const [internalWidth, setInternalWidth] = useState2(400);
222
- const [internalActiveTabId, setInternalActiveTabId] = useState2(0);
223
- const position = props.position !== void 0 ? props.position : internalPosition;
224
- const collapsed = props.collapsed !== void 0 ? props.collapsed : internalCollapsed;
225
- const hidden = props.hidden !== void 0 ? props.hidden : internalHidden;
226
- const maximized = props.maximized !== void 0 ? props.maximized : internalMaximized;
227
- const panelHeight = props.height !== void 0 ? props.height : internalHeight;
228
- const panelWidth = props.width !== void 0 ? props.width : internalWidth;
229
- const activeTabId = props.activeTabId !== void 0 ? props.activeTabId : internalActiveTabId;
230
- const updateCollapsed = useCallback((value) => {
231
- setInternalCollapsed(value);
232
- props.onCollapsedChange?.(value);
233
- }, [props.onCollapsedChange]);
234
- const updateHidden = useCallback((value) => {
235
- setInternalHidden(value);
236
- props.onHiddenChange?.(value);
237
- }, [props.onHiddenChange]);
238
- const updateMaximized = useCallback((value) => {
239
- setInternalMaximized(value);
240
- props.onMaximizedChange?.(value);
241
- }, [props.onMaximizedChange]);
242
- const updateHeight = useCallback((value) => {
243
- setInternalHeight(value);
244
- props.onHeightChange?.(value);
245
- }, [props.onHeightChange]);
246
- const updateWidth = useCallback((value) => {
247
- setInternalWidth(value);
248
- props.onWidthChange?.(value);
249
- }, [props.onWidthChange]);
250
- const updateActiveTabId = useCallback((value) => {
251
- setInternalActiveTabId(value);
252
- props.onActiveTabIdChange?.(value);
253
- }, [props.onActiveTabIdChange]);
254
- const updatePosition = useCallback((value) => {
255
- setInternalPosition(value);
256
- savePanelPosition(value);
257
- props.onPositionChange?.(value);
258
- }, [props.onPositionChange]);
259
- const closable = options?.closable ?? true;
260
- const resizable = options?.resizable ?? true;
261
- const isHorizontal = position === "left" || position === "right";
262
- const hideable = options?.hideable ?? true;
263
- const hideAlignment = options?.hideAlignment ?? "center";
264
- const syncTheme = options?.syncTheme ?? false;
265
- const [visible, setVisible] = useState2(true);
266
- const [initialized, setInitialized] = useState2(false);
267
- const preHideCollapsedRef = useRef3(true);
268
- const [themeStyles, setThemeStyles] = useState2({});
269
- const wrapperRef = useRef3(null);
270
- const [tabs, setTabs] = useState2([]);
271
- const [activePaneId, setActivePaneId] = useState2(0);
272
- const nextIdRef = useRef3({ tab: 1, pane: 1 });
273
- const [contextMenu, setContextMenu] = useState2({ visible: false, x: 0, y: 0, tabId: 0 });
274
- const engineMapRef = useRef3(/* @__PURE__ */ new Map());
275
- const activeTabIdRef = useRef3(activeTabId);
276
- activeTabIdRef.current = activeTabId;
277
- const activePaneIdRef = useRef3(activePaneId);
278
- activePaneIdRef.current = activePaneId;
279
- const [paneResizing, setPaneResizing] = useState2(false);
280
- const paneResizeRef = useRef3({ tabId: 0, dividerIndex: 0, startX: 0, startWidths: [], containerWidth: 0 });
281
- const [panelResizing, setPanelResizing] = useState2(false);
282
- const panelResizeRef = useRef3({ startY: 0, startHeight: 0, startX: 0, startWidth: 0 });
283
- const prevHeightRef = useRef3(600);
284
- const prevWidthRef = useRef3(400);
285
- const [statusExecutionState, setStatusExecutionState] = useState2("idle");
286
- const [statusLastCommand, setStatusLastCommand] = useState2(null);
287
- const [statusServiceCount, setStatusServiceCount] = useState2({ running: 0, total: 0 });
288
- const [statusServiceDetails, setStatusServiceDetails] = useState2([]);
289
- const [statusServerState, setStatusServerState] = useState2("none");
290
- const [statusServerDetails, setStatusServerDetails] = useState2([]);
291
- const [statusUptime, setStatusUptime] = useState2(0);
292
- const [notification, setNotification] = useState2(null);
293
- const [servicesDropdownOpen, setServicesDropdownOpen] = useState2(false);
294
- const [serversDropdownOpen, setServersDropdownOpen] = useState2(false);
295
- const servicesDropdownRef = useRef3(null);
296
- const serversDropdownRef = useRef3(null);
297
- const normalizePanes = useCallback((panes) => {
298
- const total = panes.reduce((s, p) => s + p.widthPercent, 0);
299
- if (total === 0) return;
300
- const scale = 100 / total;
301
- panes.forEach((p) => {
302
- p.widthPercent *= scale;
303
- });
304
- }, []);
305
- const addTab = useCallback((title) => {
306
- const tabId = nextIdRef.current.tab++;
307
- const paneId = nextIdRef.current.pane++;
308
- const pane = { id: paneId, widthPercent: 100 };
309
- const tab = { id: tabId, title: title ?? `Terminal ${tabId}`, isEditing: false, panes: [pane] };
310
- setTabs((prev) => [...prev, tab]);
311
- updateActiveTabId(tabId);
312
- setActivePaneId(paneId);
313
- props.onTabAdded?.({ tabId });
314
- return tabId;
315
- }, [updateActiveTabId, props.onTabAdded]);
316
- const toggle = useCallback(() => {
317
- if (collapsed) {
318
- updateCollapsed(false);
319
- if (!initialized) {
320
- setInitialized(true);
321
- addTab();
322
- }
323
- } else {
324
- updateCollapsed(true);
325
- }
326
- }, [collapsed, initialized, updateCollapsed, addTab]);
327
- const closeTab = useCallback((id) => {
328
- setTabs((prev) => {
329
- const tab = prev.find((t) => t.id === id);
330
- if (tab) {
331
- tab.panes.forEach((p) => {
332
- engineMapRef.current.get(p.id)?.destroy();
333
- engineMapRef.current.delete(p.id);
334
- });
335
- }
336
- const next = prev.filter((t) => t.id !== id);
337
- if (next.length === 0) {
338
- setInitialized(false);
339
- updateCollapsed(true);
340
- return [];
341
- }
342
- if (activeTabIdRef.current === id) {
343
- const oldIdx = prev.findIndex((t) => t.id === id);
344
- const newIdx = Math.min(oldIdx, next.length - 1);
345
- updateActiveTabId(next[newIdx].id);
346
- }
347
- return next;
348
- });
349
- props.onTabClosed?.({ tabId: id });
350
- }, [updateCollapsed, updateActiveTabId, props.onTabClosed]);
351
- const selectTab = useCallback((id) => {
352
- updateActiveTabId(id);
353
- setTabs((prev) => {
354
- const tab = prev.find((t) => t.id === id);
355
- if (tab && tab.panes.length > 0) {
356
- setActivePaneId(tab.panes[0].id);
357
- }
358
- return prev;
359
- });
360
- }, [updateActiveTabId]);
361
- const renameTab = useCallback((tabId, title) => {
362
- setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, title } : t));
363
- }, []);
364
- const startRename = useCallback((tabId) => {
365
- setTabs((prev) => prev.map((t) => ({ ...t, isEditing: t.id === tabId })));
366
- }, []);
367
- const commitRename = useCallback((tabId, value) => {
368
- const trimmed = value.trim();
369
- setTabs((prev) => prev.map(
370
- (t) => t.id === tabId ? { ...t, title: trimmed || t.title, isEditing: false } : t
371
- ));
372
- }, []);
373
- const splitPane = useCallback((tabId) => {
374
- const targetTabId = tabId ?? activeTabIdRef.current;
375
- const paneId = nextIdRef.current.pane++;
376
- setTabs((prev) => prev.map((t) => {
377
- if (t.id !== targetTabId) return t;
378
- const newPanes = [...t.panes.map((p) => ({ ...p })), { id: paneId, widthPercent: 0 }];
379
- const evenWidth = 100 / newPanes.length;
380
- newPanes.forEach((p) => {
381
- p.widthPercent = evenWidth;
382
- });
383
- normalizePanes(newPanes);
384
- return { ...t, panes: newPanes };
385
- }));
386
- setActivePaneId(paneId);
387
- props.onPaneSplit?.({ paneId, tabId: targetTabId });
388
- return paneId;
389
- }, [normalizePanes, props.onPaneSplit]);
390
- const closePane = useCallback((paneId) => {
391
- setTabs((prev) => {
392
- for (let i = 0; i < prev.length; i++) {
393
- const tab = prev[i];
394
- const idx = tab.panes.findIndex((p) => p.id === paneId);
395
- if (idx === -1) continue;
396
- if (tab.panes.length <= 1) {
397
- setTimeout(() => closeTab(tab.id), 0);
398
- return prev;
399
- }
400
- const newPanes = tab.panes.filter((p) => p.id !== paneId);
401
- normalizePanes(newPanes);
402
- const newTabs = [...prev];
403
- newTabs[i] = { ...tab, panes: newPanes };
404
- engineMapRef.current.get(paneId)?.destroy();
405
- engineMapRef.current.delete(paneId);
406
- if (activePaneIdRef.current === paneId) {
407
- setActivePaneId(newPanes[Math.min(idx, newPanes.length - 1)].id);
408
- }
409
- props.onPaneClosed?.({ paneId });
410
- return newTabs;
411
- }
412
- return prev;
413
- });
414
- }, [closeTab, normalizePanes, props.onPaneClosed]);
415
- const closeContextMenu = useCallback(() => {
416
- setContextMenu((prev) => ({ ...prev, visible: false }));
417
- }, []);
418
- useEffect3(() => {
419
- if (!contextMenu.visible) return;
420
- const handler = () => closeContextMenu();
421
- document.addEventListener("click", handler);
422
- return () => document.removeEventListener("click", handler);
423
- }, [contextMenu.visible, closeContextMenu]);
424
- const onPanelResizeStart = useCallback((e) => {
425
- if (!resizable) return;
426
- e.preventDefault();
427
- if (collapsed) {
428
- toggle();
154
+ };
155
+ var asStyle = (style) => style;
156
+ var CliPanel = React3.forwardRef(function CliPanel2(props, ref) {
157
+ const config = useCliConfig();
158
+ const options = mergeCliOptions(props.options, config.options);
159
+ const { modules, processors, services } = resolveCliHostConfig2(props, config);
160
+ const [controller] = useState2(
161
+ () => new CliPanelController({
162
+ config: options,
163
+ controlled: {
164
+ collapsed: props.collapsed,
165
+ hidden: props.hidden,
166
+ maximized: props.maximized,
167
+ activeTabId: props.activeTabId,
168
+ position: props.position,
169
+ height: props.height,
170
+ width: props.width
429
171
  }
430
- setPanelResizing(true);
431
- panelResizeRef.current = {
432
- startY: e.clientY,
433
- startHeight: panelHeight,
434
- startX: e.clientX,
435
- startWidth: panelWidth
436
- };
437
- }, [collapsed, toggle, panelHeight, panelWidth, resizable]);
438
- useEffect3(() => {
439
- if (!panelResizing) return;
440
- const onMove = (e) => {
441
- if (isHorizontal) {
442
- const deltaX = position === "left" ? e.clientX - panelResizeRef.current.startX : panelResizeRef.current.startX - e.clientX;
443
- let next = Math.max(100, panelResizeRef.current.startWidth + deltaX);
444
- if (next > window.innerWidth) next = window.innerWidth;
445
- updateWidth(next);
446
- } else {
447
- const deltaY = position === "top" ? e.clientY - panelResizeRef.current.startY : panelResizeRef.current.startY - e.clientY;
448
- let next = Math.max(100, panelResizeRef.current.startHeight + deltaY);
449
- if (next > window.innerHeight) next = window.innerHeight;
450
- updateHeight(next);
451
- }
452
- };
453
- const onUp = () => setPanelResizing(false);
454
- document.addEventListener("mousemove", onMove);
455
- document.addEventListener("mouseup", onUp);
456
- return () => {
457
- document.removeEventListener("mousemove", onMove);
458
- document.removeEventListener("mouseup", onUp);
459
- };
460
- }, [panelResizing, isHorizontal, position, updateWidth, updateHeight]);
461
- const onPaneResizeStart = useCallback((e, tabId, dividerIndex) => {
462
- e.preventDefault();
463
- const tab = tabs.find((t) => t.id === tabId);
464
- if (!tab) return;
465
- setPaneResizing(true);
466
- const container = e.target.closest(".cli-panel-panes-container");
467
- paneResizeRef.current = {
468
- tabId,
469
- dividerIndex,
470
- startX: e.clientX,
471
- startWidths: tab.panes.map((p) => p.widthPercent),
472
- containerWidth: container ? container.clientWidth : 1
473
- };
474
- }, [tabs]);
475
- useEffect3(() => {
476
- if (!paneResizing) return;
477
- const onMove = (e) => {
478
- const ref2 = paneResizeRef.current;
479
- const deltaX = e.clientX - ref2.startX;
480
- const deltaPct = deltaX / ref2.containerWidth * 100;
481
- const i = ref2.dividerIndex;
482
- let leftWidth = ref2.startWidths[i] + deltaPct;
483
- let rightWidth = ref2.startWidths[i + 1] - deltaPct;
484
- if (leftWidth < MIN_PANE_WIDTH_PERCENT) {
485
- leftWidth = MIN_PANE_WIDTH_PERCENT;
486
- rightWidth = ref2.startWidths[i] + ref2.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
487
- }
488
- if (rightWidth < MIN_PANE_WIDTH_PERCENT) {
489
- rightWidth = MIN_PANE_WIDTH_PERCENT;
490
- leftWidth = ref2.startWidths[i] + ref2.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
491
- }
492
- setTabs((prev) => {
493
- const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
494
- const tab = next.find((t) => t.id === ref2.tabId);
495
- if (tab) {
496
- tab.panes[i].widthPercent = leftWidth;
497
- tab.panes[i + 1].widthPercent = rightWidth;
498
- }
499
- return next;
500
- });
501
- };
502
- const onUp = () => {
503
- setPaneResizing(false);
504
- document.body.classList.remove("cli-pane-resizing");
505
- };
506
- document.body.classList.add("cli-pane-resizing");
507
- document.addEventListener("mousemove", onMove);
508
- document.addEventListener("mouseup", onUp);
509
- return () => {
510
- document.removeEventListener("mousemove", onMove);
511
- document.removeEventListener("mouseup", onUp);
512
- };
513
- }, [paneResizing]);
514
- useEffect3(() => {
515
- if (!syncTheme || !initialized) return;
516
- const syncFromEngine = () => {
517
- const engine = engineMapRef.current.values().next().value;
518
- if (!engine) return;
519
- const theme = engine.getTerminal().options.theme;
520
- if (theme) setThemeStyles(derivePanelThemeStyles(theme));
521
- };
522
- const timer = setTimeout(() => {
523
- syncFromEngine();
524
- const container = wrapperRef.current?.querySelector(".terminal-container");
525
- if (!container) return;
526
- const observer = new MutationObserver(syncFromEngine);
527
- observer.observe(container, { attributes: true, attributeFilter: ["style"] });
528
- return () => observer.disconnect();
529
- }, 200);
530
- return () => clearTimeout(timer);
531
- }, [syncTheme, initialized]);
532
- useEffect3(() => {
533
- if (!initialized) return;
534
- const pollExec = setInterval(() => {
535
- const tab = tabs.find((t) => t.id === activeTabIdRef.current);
536
- if (!tab) return;
537
- const engine = engineMapRef.current.get(tab.panes[0]?.id);
538
- if (!engine) return;
539
- const context = engine.getContext();
540
- if (!context) return;
541
- const running = !!context.isExecuting || !!context.contextProcessor;
542
- setStatusExecutionState(running ? "running" : "idle");
543
- const result = context.lastCommandResult;
544
- setStatusLastCommand(result ? { name: result.command, success: result.success } : null);
545
- const notif = context.notifier?.current;
546
- setNotification(notif || null);
547
- }, 300);
548
- const pollGlobal = setInterval(() => {
549
- const tab = tabs.find((t) => t.id === activeTabIdRef.current);
550
- if (!tab) return;
551
- const engine = engineMapRef.current.get(tab.panes[0]?.id);
552
- if (!engine) return;
553
- const context = engine.getContext();
554
- if (!context) return;
555
- try {
556
- const services2 = context.backgroundServices?.list() ?? [];
557
- setStatusServiceCount({ running: services2.filter((s) => s.status === "running").length, total: services2.length });
558
- setStatusServiceDetails(services2.map((s) => ({ name: s.name, status: s.status, description: s.description })));
559
- } catch {
560
- }
561
- try {
562
- let serverManager;
563
- try {
564
- serverManager = context.services?.get?.("cli-server-manager");
565
- } catch {
566
- }
567
- const details = [];
568
- if (serverManager?.connections?.size > 0) {
569
- let anyConnected = false;
570
- for (const [name, conn] of serverManager.connections) {
571
- if (conn.connected) anyConnected = true;
572
- details.push({
573
- name,
574
- url: conn.config?.url ?? "",
575
- connected: !!conn.connected,
576
- apiVersion: conn.connected ? conn.apiVersion : void 0,
577
- commandCount: conn.connected ? conn.commands?.length : void 0
578
- });
579
- }
580
- setStatusServerState(anyConnected ? "connected" : "disconnected");
581
- } else {
582
- const configuredServers = engine.options?.servers;
583
- if (Array.isArray(configuredServers) && configuredServers.length > 0) {
584
- for (const srv of configuredServers) {
585
- if (srv.enabled === false) continue;
586
- details.push({
587
- name: srv.name,
588
- url: srv.url ?? "",
589
- connected: false
590
- });
591
- }
592
- setStatusServerState("disconnected");
593
- } else {
594
- setStatusServerState("none");
595
- }
596
- }
597
- setStatusServerDetails(details);
598
- } catch {
599
- }
600
- setStatusUptime(engine.startedAt ? Date.now() - engine.startedAt : 0);
601
- }, 2e3);
602
- return () => {
603
- clearInterval(pollExec);
604
- clearInterval(pollGlobal);
605
- };
606
- }, [initialized, tabs]);
607
- const toggleMaximize = useCallback(() => {
608
- const nextMaximized = !maximized;
609
- if (nextMaximized) {
610
- if (isHorizontal) {
611
- prevWidthRef.current = panelWidth;
612
- updateWidth(window.innerWidth);
613
- } else {
614
- prevHeightRef.current = panelHeight;
615
- updateHeight(window.innerHeight);
616
- }
617
- } else {
618
- if (isHorizontal) {
619
- updateWidth(prevWidthRef.current);
620
- } else {
621
- updateHeight(prevHeightRef.current);
622
- }
172
+ })
173
+ );
174
+ const s = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
175
+ const [rootElement, setRootElement] = useState2(null);
176
+ useLayoutEffect(() => {
177
+ controller.setCallbacks({
178
+ onCollapsedChange: props.onCollapsedChange,
179
+ onHiddenChange: props.onHiddenChange,
180
+ onMaximizedChange: props.onMaximizedChange,
181
+ onActiveTabIdChange: props.onActiveTabIdChange,
182
+ onPositionChange: props.onPositionChange,
183
+ onHeightChange: props.onHeightChange,
184
+ onWidthChange: props.onWidthChange,
185
+ onClose: props.onClose,
186
+ onTabAdded: props.onTabAdded,
187
+ onTabClosed: props.onTabClosed,
188
+ onPaneSplit: props.onPaneSplit,
189
+ onPaneClosed: props.onPaneClosed
190
+ });
191
+ controller.setControlled({
192
+ collapsed: props.collapsed,
193
+ hidden: props.hidden,
194
+ maximized: props.maximized,
195
+ activeTabId: props.activeTabId,
196
+ position: props.position,
197
+ height: props.height,
198
+ width: props.width
199
+ });
200
+ controller.setOptions(options);
201
+ });
202
+ useEffect3(() => controller.setRootElement(rootElement), [controller, rootElement]);
203
+ useEffect3(() => () => controller.destroy(), [controller]);
204
+ React3.useImperativeHandle(ref, () => controller, [controller]);
205
+ if (!s.visible) return null;
206
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
207
+ s.hidden && /* @__PURE__ */ jsxs(
208
+ "button",
209
+ {
210
+ className: "cli-panel-hide-tab",
211
+ "data-position": s.position,
212
+ "data-hide-align": s.hideAlignment,
213
+ style: asStyle(s.themeStyles),
214
+ title: PANEL_LABELS.showCli,
215
+ onClick: () => controller.unhide(),
216
+ children: [
217
+ /* @__PURE__ */ jsx3(Icon, { icon: HIDE_TAB_TERMINAL_ICON, className: "cli-panel-hide-tab-icon" }),
218
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-hide-tab-label", children: PANEL_LABELS.title }),
219
+ /* @__PURE__ */ jsx3(Icon, { icon: hideTabChevronIcon(s.position), className: "cli-panel-hide-tab-arrow" })
220
+ ]
623
221
  }
624
- updateMaximized(nextMaximized);
625
- }, [maximized, panelHeight, panelWidth, isHorizontal, updateMaximized, updateWidth, updateHeight]);
626
- const handleClose = useCallback(() => {
627
- setVisible(false);
628
- onClose?.();
629
- }, [onClose]);
630
- const handleHide = useCallback(() => {
631
- preHideCollapsedRef.current = collapsed;
632
- updateHidden(true);
633
- }, [collapsed, updateHidden]);
634
- const handleUnhide = useCallback(() => {
635
- updateHidden(false);
636
- updateCollapsed(preHideCollapsedRef.current);
637
- }, [updateHidden, updateCollapsed]);
638
- const [positionDropdownOpen, setPositionDropdownOpen] = useState2(false);
639
- const closePositionDropdown = useCallback(() => setPositionDropdownOpen(false), []);
640
- useEffect3(() => {
641
- if (!positionDropdownOpen) return;
642
- document.addEventListener("click", closePositionDropdown);
643
- return () => document.removeEventListener("click", closePositionDropdown);
644
- }, [positionDropdownOpen, closePositionDropdown]);
645
- useEffect3(() => {
646
- if (!servicesDropdownOpen && !serversDropdownOpen) return;
647
- const handler = () => {
648
- setServicesDropdownOpen(false);
649
- setServersDropdownOpen(false);
650
- };
651
- document.addEventListener("click", handler);
652
- return () => document.removeEventListener("click", handler);
653
- }, [servicesDropdownOpen, serversDropdownOpen]);
654
- const handlePositionButtonClick = useCallback((e) => {
655
- e.stopPropagation();
656
- setPositionDropdownOpen((prev) => !prev);
657
- }, []);
658
- const selectPosition = useCallback((pos) => {
659
- setPositionDropdownOpen(false);
660
- updatePosition(pos);
661
- }, [updatePosition]);
662
- const getEngine = useCallback((paneId) => {
663
- const targetId = paneId ?? activePaneIdRef.current;
664
- return engineMapRef.current.get(targetId);
665
- }, []);
666
- const getState = useCallback(() => ({
667
- collapsed,
668
- hidden,
669
- maximized,
670
- position,
671
- height: panelHeight,
672
- width: panelWidth,
673
- activeTabId,
674
- activePaneId,
675
- tabs: tabs.map((t) => ({
676
- id: t.id,
677
- title: t.title,
678
- panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent }))
679
- }))
680
- }), [collapsed, hidden, maximized, position, panelHeight, panelWidth, activeTabId, activePaneId, tabs]);
681
- React3.useImperativeHandle(ref, () => ({
682
- open: () => {
683
- if (!visible) {
684
- setVisible(true);
685
- }
686
- if (hidden) {
687
- updateHidden(false);
688
- }
689
- if (collapsed) {
690
- updateCollapsed(false);
691
- if (!initialized) {
692
- setInitialized(true);
693
- addTab();
694
- }
695
- }
696
- },
697
- collapse: () => {
698
- if (!collapsed) updateCollapsed(true);
699
- },
700
- toggleCollapse: () => {
701
- if (collapsed) {
702
- updateCollapsed(false);
703
- if (!initialized) {
704
- setInitialized(true);
705
- addTab();
706
- }
707
- } else {
708
- updateCollapsed(true);
709
- }
710
- },
711
- hide: () => {
712
- if (!hidden) updateHidden(true);
713
- },
714
- unhide: () => {
715
- if (hidden) updateHidden(false);
716
- },
717
- toggleHide: () => {
718
- updateHidden(!hidden);
719
- },
720
- close: handleClose,
721
- maximize: () => {
722
- if (!maximized) updateMaximized(true);
723
- },
724
- restore: () => {
725
- if (maximized) updateMaximized(false);
726
- },
727
- toggleMaximize: () => {
728
- updateMaximized(!maximized);
729
- },
730
- resize: (dims) => {
731
- if (dims.height !== void 0) updateHeight(dims.height);
732
- if (dims.width !== void 0) updateWidth(dims.width);
733
- },
734
- setPosition: updatePosition,
735
- addTab,
736
- closeTab,
737
- selectTab,
738
- renameTab,
739
- splitPane,
740
- closePane,
741
- getEngine,
742
- getState
743
- }), [
744
- collapsed,
745
- hidden,
746
- maximized,
747
- initialized,
748
- updateCollapsed,
749
- updateHidden,
750
- updateMaximized,
751
- updateHeight,
752
- updateWidth,
753
- updatePosition,
754
- handleClose,
755
- addTab,
756
- closeTab,
757
- selectTab,
758
- renameTab,
759
- splitPane,
760
- closePane,
761
- getEngine,
762
- getState
763
- ]);
764
- const formattedUptime = (() => {
765
- const mins = Math.floor(statusUptime / 6e4);
766
- if (mins < 60) return `${mins}m`;
767
- const hrs = Math.floor(mins / 60);
768
- return `${hrs}h${mins % 60}m`;
769
- })();
770
- if (!visible) return null;
771
- const wrapperStyle = {
772
- ...isHorizontal ? { width: `${panelWidth}px` } : { height: `${panelHeight}px` },
773
- ...hidden ? { display: "none" } : {},
774
- ...themeStyles,
775
- ...style
776
- };
777
- return /* @__PURE__ */ jsxs(Fragment2, { children: [
778
- hidden && /* @__PURE__ */ jsxs(
779
- "button",
780
- {
781
- className: "cli-panel-hide-tab",
782
- "data-position": position,
783
- "data-hide-align": hideAlignment,
784
- style: themeStyles,
785
- title: "Show CLI",
786
- onClick: handleUnhide,
787
- children: [
788
- /* @__PURE__ */ 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: [
789
- /* @__PURE__ */ jsx3("polyline", { points: "4 17 10 11 4 5" }),
790
- /* @__PURE__ */ jsx3("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
791
- ] }),
792
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-hide-tab-label", children: "CLI" }),
793
- /* @__PURE__ */ jsx3(HideTabChevron, { position })
794
- ]
795
- }
796
- ),
797
- /* @__PURE__ */ jsxs(
798
- "div",
799
- {
800
- ref: wrapperRef,
801
- className: `cli-panel-wrapper ${collapsed ? "collapsed" : ""} ${maximized ? "maximized" : ""} ${panelResizing ? "resizing" : ""} ${className ?? ""}`,
802
- style: wrapperStyle,
803
- "data-position": position,
804
- "data-resizable": String(resizable),
805
- "data-closable": String(closable),
806
- "data-hideable": String(hideable),
807
- children: [
808
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-header", children: [
809
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-resize-bar", onMouseDown: onPanelResizeStart, children: /* @__PURE__ */ jsx3("div", { className: "cli-panel-resize-grip" }) }),
810
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-header-content", children: [
811
- /* @__PURE__ */ jsxs("p", { className: "cli-panel-title", children: [
812
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ jsx3(TerminalIcon, {}) }),
813
- "CLI"
814
- ] }),
815
- (position === "bottom" || position === "top") && /* @__PURE__ */ jsxs("div", { className: "cli-panel-status-indicators", children: [
816
- /* @__PURE__ */ jsxs("span", { className: `cli-panel-status-item${statusExecutionState === "running" ? " status-running" : ""}`, children: [
817
- /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusExecutionState === "running" ? "dot-running" : "dot-idle"}` }),
818
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: statusExecutionState })
819
- ] }),
820
- statusServiceCount.total > 0 && /* @__PURE__ */ jsxs(
821
- "span",
822
- {
823
- ref: servicesDropdownRef,
824
- className: "cli-panel-status-item status-clickable",
825
- onClick: (e) => {
826
- e.stopPropagation();
827
- setServicesDropdownOpen((prev) => !prev);
828
- setServersDropdownOpen(false);
829
- },
830
- children: [
831
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2699" }),
832
- /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-label", children: [
833
- statusServiceCount.running,
834
- "/",
835
- statusServiceCount.total,
836
- " services"
837
- ] })
838
- ]
839
- }
840
- ),
841
- statusLastCommand && /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-item", children: [
842
- /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-icon ${statusLastCommand.success ? "status-success" : "status-error"}`, children: statusLastCommand.success ? "\u2713" : "\u2717" }),
843
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: statusLastCommand.name })
844
- ] }),
845
- statusServerState !== "none" && /* @__PURE__ */ jsxs(
846
- "span",
847
- {
848
- ref: serversDropdownRef,
849
- className: "cli-panel-status-item status-clickable",
850
- onClick: (e) => {
851
- e.stopPropagation();
852
- setServersDropdownOpen((prev) => !prev);
853
- setServicesDropdownOpen(false);
854
- },
855
- children: [
856
- /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusServerState === "connected" ? "dot-idle" : "dot-error"}` }),
857
- /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-label", children: [
858
- statusServerDetails.filter((s) => s.connected).length,
859
- "/",
860
- statusServerDetails.length,
861
- " servers"
862
- ] })
863
- ]
864
- }
865
- ),
866
- statusUptime > 0 && /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-item status-muted", children: [
867
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2191" }),
868
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: formattedUptime })
869
- ] }),
870
- notification && /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-item status-text level-${notification.level}`, children: /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: notification.message }) })
871
- ] }),
872
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-status-indicators-compact", children: [
873
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item", title: statusExecutionState, children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusExecutionState === "running" ? "dot-running" : "dot-idle"}` }) }),
874
- statusServiceCount.total > 0 && /* @__PURE__ */ jsx3(
875
- "span",
876
- {
877
- ref: position === "left" || position === "right" ? servicesDropdownRef : void 0,
878
- className: "cli-panel-status-compact-item status-clickable",
879
- title: `${statusServiceCount.running}/${statusServiceCount.total} services`,
880
- onClick: (e) => {
881
- e.stopPropagation();
882
- setServicesDropdownOpen((prev) => !prev);
883
- setServersDropdownOpen(false);
884
- },
885
- children: /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2699" })
886
- }
887
- ),
888
- statusLastCommand && /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item", title: `${statusLastCommand.success ? "\u2713" : "\u2717"} ${statusLastCommand.name}`, children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-icon ${statusLastCommand.success ? "status-success" : "status-error"}`, children: statusLastCommand.success ? "\u2713" : "\u2717" }) }),
889
- statusServerState !== "none" && /* @__PURE__ */ jsx3(
890
- "span",
222
+ ),
223
+ /* @__PURE__ */ jsxs(
224
+ "div",
225
+ {
226
+ ref: setRootElement,
227
+ className: panelWrapperClass(s, props.className),
228
+ style: { ...asStyle(panelWrapperStyle(s)), ...props.style },
229
+ "data-position": s.position,
230
+ "data-resizable": String(s.resizable),
231
+ "data-closable": String(s.closable),
232
+ "data-hideable": String(s.hideable),
233
+ children: [
234
+ /* @__PURE__ */ jsx3(PanelHeader, { controller, s }),
235
+ s.initialized && /* @__PURE__ */ jsxs("div", { className: "cli-panel-content", style: s.collapsed ? { display: "none" } : void 0, children: [
236
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-tabs", children: [
237
+ /* @__PURE__ */ jsx3("ul", { className: "cli-panel-tab-list", children: s.tabs.map((tab) => /* @__PURE__ */ jsx3(
238
+ "li",
239
+ {
240
+ className: panelTabClass(s, tab),
241
+ onClick: () => controller.selectTab(tab.id),
242
+ onDoubleClick: () => controller.startRename(tab.id),
243
+ onContextMenu: (e) => {
244
+ e.preventDefault();
245
+ e.stopPropagation();
246
+ controller.openContextMenu(tab.id, e.clientX, e.clientY);
247
+ },
248
+ children: tab.isEditing ? /* @__PURE__ */ jsx3(
249
+ "input",
891
250
  {
892
- ref: position === "left" || position === "right" ? serversDropdownRef : void 0,
893
- className: "cli-panel-status-compact-item status-clickable",
894
- title: `${statusServerDetails.filter((s) => s.connected).length}/${statusServerDetails.length} servers`,
895
- onClick: (e) => {
896
- e.stopPropagation();
897
- setServersDropdownOpen((prev) => !prev);
898
- setServicesDropdownOpen(false);
899
- },
900
- children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusServerState === "connected" ? "dot-idle" : "dot-error"}` })
251
+ className: "cli-panel-tab-rename-input",
252
+ type: "text",
253
+ defaultValue: tab.title,
254
+ ref: focusAndSelect,
255
+ onKeyDown: (e) => handleRenameKey(controller, tab.id, e.key, e.currentTarget.value),
256
+ onBlur: (e) => controller.commitRename(tab.id, e.target.value),
257
+ onClick: (e) => e.stopPropagation(),
258
+ onDoubleClick: (e) => e.stopPropagation()
901
259
  }
902
- ),
903
- statusUptime > 0 && /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item status-muted", title: formattedUptime, children: /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2191" }) }),
904
- notification && /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item", title: notification.message, children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-compact-dot level-${notification.level}` }) })
905
- ] }),
906
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-action-buttons", children: [
907
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
908
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-position", title: "Move panel", onClick: handlePositionButtonClick, children: /* @__PURE__ */ jsx3(PositionIcon, { position }) }),
909
- positionDropdownOpen && /* @__PURE__ */ jsx3("div", { className: "cli-panel-position-dropdown", children: ["bottom", "top", "left", "right"].map((pos) => /* @__PURE__ */ jsxs(
260
+ ) : /* @__PURE__ */ jsxs(Fragment2, { children: [
261
+ /* @__PURE__ */ jsx3("span", { className: panelTabDotClass(s, tab) }),
262
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-tab-title", children: tab.title }),
263
+ /* @__PURE__ */ jsx3(
910
264
  "button",
911
265
  {
912
- className: `cli-panel-position-dropdown-item${position === pos ? " active" : ""}`,
913
- onClick: () => selectPosition(pos),
914
- children: [
915
- /* @__PURE__ */ jsx3(PositionIcon, { position: pos, size: 16 }),
916
- pos.charAt(0).toUpperCase() + pos.slice(1)
917
- ]
918
- },
919
- pos
920
- )) })
921
- ] }),
922
- hideable && /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-hide", title: "Hide", onClick: handleHide, children: /* @__PURE__ */ jsx3(HideIcon, {}) }),
923
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn", title: maximized ? "Restore" : "Maximize", disabled: collapsed, onClick: toggleMaximize, children: maximized ? /* @__PURE__ */ jsx3(RestoreIcon, {}) : /* @__PURE__ */ jsx3(MaximizeIcon, {}) }),
924
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn", title: collapsed ? "Expand" : "Collapse", onClick: toggle, children: /* @__PURE__ */ jsx3(CollapseChevron, { position, isCollapsed: collapsed }) }),
925
- closable && /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-close", title: "Close", onClick: handleClose, children: /* @__PURE__ */ jsx3(CloseIcon, {}) })
926
- ] })
927
- ] }),
928
- /* @__PURE__ */ jsx3("div", { className: `cli-panel-glow-line${statusExecutionState === "running" ? " glow-active" : ""}` })
266
+ className: "cli-panel-tab-close-btn",
267
+ title: PANEL_LABELS.closeTab,
268
+ onClick: (e) => {
269
+ e.stopPropagation();
270
+ controller.closeTab(tab.id);
271
+ },
272
+ children: CLOSE_GLYPH
273
+ }
274
+ )
275
+ ] })
276
+ },
277
+ tab.id
278
+ )) }),
279
+ /* @__PURE__ */ jsx3("button", { className: "cli-panel-add-tab", title: PANEL_LABELS.newTerminal, onClick: () => controller.addTab(), children: "+" })
929
280
  ] }),
930
- initialized && /* @__PURE__ */ jsxs("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
931
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-tabs", children: [
932
- /* @__PURE__ */ jsx3("ul", { className: "cli-panel-tab-list", children: tabs.map((tab) => /* @__PURE__ */ jsx3(
933
- "li",
934
- {
935
- className: `cli-panel-tab ${tab.id === activeTabId ? "active" : ""}`,
936
- onClick: () => selectTab(tab.id),
937
- onDoubleClick: () => startRename(tab.id),
938
- onContextMenu: (e) => {
939
- e.preventDefault();
940
- e.stopPropagation();
941
- setContextMenu({ visible: true, x: e.clientX, y: e.clientY, tabId: tab.id });
942
- },
943
- children: tab.isEditing ? /* @__PURE__ */ jsx3(
944
- "input",
281
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-instances", children: s.tabs.map((tab) => /* @__PURE__ */ jsx3("div", { className: "cli-panel-instance", style: { display: tab.id === s.activeTabId ? void 0 : "none" }, children: /* @__PURE__ */ jsx3("div", { className: panesContainerClass(s), children: tab.panes.map((pane, i) => /* @__PURE__ */ jsxs(React3.Fragment, { children: [
282
+ i > 0 && /* @__PURE__ */ jsx3(
283
+ "div",
284
+ {
285
+ className: "cli-panel-pane-divider",
286
+ onMouseDown: (e) => {
287
+ e.preventDefault();
288
+ controller.startPaneResize(tab.id, i - 1, e.clientX, panesContainerWidth(e.target));
289
+ },
290
+ children: /* @__PURE__ */ jsx3("div", { className: "cli-panel-pane-divider-grip" })
291
+ }
292
+ ),
293
+ /* @__PURE__ */ jsxs(
294
+ "div",
295
+ {
296
+ className: paneClass(s, tab.id, pane.id),
297
+ style: paneStyle(pane.widthPercent),
298
+ onClick: () => controller.activatePane(tab.id, pane.id),
299
+ children: [
300
+ tab.panes.length > 1 && /* @__PURE__ */ jsx3(
301
+ "button",
945
302
  {
946
- className: "cli-panel-tab-rename-input",
947
- type: "text",
948
- defaultValue: tab.title,
949
- autoFocus: true,
950
- onKeyDown: (e) => {
951
- if (e.key === "Enter") commitRename(tab.id, e.target.value);
952
- else if (e.key === "Escape") setTabs((prev) => prev.map((t) => ({ ...t, isEditing: false })));
303
+ className: "cli-panel-pane-close-btn",
304
+ title: PANEL_LABELS.closePane,
305
+ onClick: (e) => {
306
+ e.stopPropagation();
307
+ controller.closePane(pane.id);
953
308
  },
954
- onBlur: (e) => commitRename(tab.id, e.target.value),
955
- onClick: (e) => e.stopPropagation(),
956
- onDoubleClick: (e) => e.stopPropagation()
309
+ children: CLOSE_GLYPH
957
310
  }
958
- ) : /* @__PURE__ */ jsxs(Fragment2, { children: [
959
- /* @__PURE__ */ jsx3("span", { className: `cli-panel-tab-dot ${(() => {
960
- if (tab.id !== activeTabId) return "dot-idle";
961
- if (statusExecutionState === "running") return "dot-running";
962
- if (statusLastCommand && !statusLastCommand.success) return "dot-error";
963
- return "dot-idle";
964
- })()}` }),
965
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-tab-title", children: tab.title }),
966
- /* @__PURE__ */ jsx3(
967
- "button",
968
- {
969
- className: "cli-panel-tab-close-btn",
970
- title: "Close tab",
971
- onClick: (e) => {
972
- e.stopPropagation();
973
- closeTab(tab.id);
974
- },
975
- children: "\xD7"
976
- }
977
- )
978
- ] })
979
- },
980
- tab.id
981
- )) }),
982
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-add-tab", title: "New terminal", onClick: () => addTab(), children: "+" })
983
- ] }),
984
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-instances", children: tabs.map((tab) => /* @__PURE__ */ jsx3("div", { className: "cli-panel-instance", style: { display: tab.id === activeTabId ? void 0 : "none" }, children: /* @__PURE__ */ jsx3("div", { className: `cli-panel-panes-container ${paneResizing ? "resizing" : ""}`, children: tab.panes.map((pane, i) => /* @__PURE__ */ jsxs(React3.Fragment, { children: [
985
- i > 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-pane-divider", onMouseDown: (e) => onPaneResizeStart(e, tab.id, i - 1), children: /* @__PURE__ */ jsx3("div", { className: "cli-panel-pane-divider-grip" }) }),
986
- /* @__PURE__ */ jsxs(
987
- "div",
988
- {
989
- className: `cli-panel-pane ${pane.id === activePaneId && tab.id === activeTabId ? "active-pane" : ""}`,
990
- style: { flex: `${pane.widthPercent} 1 0` },
991
- onClick: () => {
992
- setActivePaneId(pane.id);
993
- updateActiveTabId(tab.id);
994
- },
995
- children: [
996
- tab.panes.length > 1 && /* @__PURE__ */ jsx3(
997
- "button",
998
- {
999
- className: "cli-panel-pane-close-btn",
1000
- onClick: (e) => {
1001
- e.stopPropagation();
1002
- closePane(pane.id);
1003
- },
1004
- title: "Close pane",
1005
- children: "\xD7"
1006
- }
1007
- ),
1008
- /* @__PURE__ */ jsx3(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ jsx3(
1009
- Cli,
1010
- {
1011
- options,
1012
- modules,
1013
- processors,
1014
- services,
1015
- snapshot: pane.snapshot,
1016
- onReady: (engine) => engineMapRef.current.set(pane.id, engine)
1017
- }
1018
- ) })
1019
- ]
1020
- }
1021
- )
1022
- ] }, pane.id)) }) }, tab.id)) })
1023
- ] })
1024
- ]
1025
- }
1026
- ),
1027
- contextMenu.visible && /* @__PURE__ */ jsxs(
1028
- "div",
311
+ ),
312
+ /* @__PURE__ */ jsx3(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ jsx3(
313
+ Cli,
314
+ {
315
+ options,
316
+ modules,
317
+ processors,
318
+ services,
319
+ snapshot: pane.snapshot,
320
+ onReady: (engine) => controller.registerEngine(tab.id, pane.id, engine)
321
+ }
322
+ ) })
323
+ ]
324
+ }
325
+ )
326
+ ] }, pane.id)) }) }, tab.id)) })
327
+ ] })
328
+ ]
329
+ }
330
+ ),
331
+ s.contextMenu.visible && /* @__PURE__ */ jsx3("div", { className: "cli-panel-context-menu", style: asStyle(contextMenuStyle(s)), onClick: (e) => e.stopPropagation(), children: contextMenuItems(s).map((item) => /* @__PURE__ */ jsxs(React3.Fragment, { children: [
332
+ item.separatorBefore && /* @__PURE__ */ jsx3("div", { className: "cli-panel-context-menu-separator" }),
333
+ /* @__PURE__ */ jsx3(
334
+ "button",
1029
335
  {
1030
- className: "cli-panel-context-menu",
1031
- style: { left: contextMenu.x, top: contextMenu.y },
1032
- onClick: (e) => e.stopPropagation(),
1033
- children: [
1034
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-context-menu-item", onClick: () => {
1035
- closeContextMenu();
1036
- startRename(contextMenu.tabId);
1037
- }, children: "Rename" }),
1038
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-context-menu-item", onClick: () => {
1039
- closeContextMenu();
1040
- const tab = tabs.find((t) => t.id === contextMenu.tabId);
1041
- if (!tab) return;
1042
- const sourceEngine = engineMapRef.current.get(tab.panes[0]?.id);
1043
- const snapshot = sourceEngine?.snapshot();
1044
- const paneId = nextIdRef.current.pane++;
1045
- const tabId = nextIdRef.current.tab++;
1046
- const pane = { id: paneId, widthPercent: 100, snapshot };
1047
- const newTab = { id: tabId, title: `${tab.title} (copy)`, isEditing: false, panes: [pane] };
1048
- const idx = tabs.indexOf(tab);
1049
- setTabs((prev) => {
1050
- const next = [...prev];
1051
- next.splice(idx + 1, 0, newTab);
1052
- return next;
1053
- });
1054
- updateActiveTabId(tabId);
1055
- setActivePaneId(paneId);
1056
- props.onTabAdded?.({ tabId });
1057
- }, children: "Duplicate" }),
1058
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-context-menu-item", onClick: () => {
1059
- closeContextMenu();
1060
- splitPane(contextMenu.tabId);
1061
- }, children: "Split Right" }),
1062
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-context-menu-separator" }),
1063
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-context-menu-item", onClick: () => {
1064
- closeContextMenu();
1065
- closeTab(contextMenu.tabId);
1066
- }, children: "Close" }),
1067
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-context-menu-item", disabled: tabs.length <= 1, onClick: () => {
1068
- closeContextMenu();
1069
- setTabs((prev) => prev.filter((t) => t.id === contextMenu.tabId));
1070
- updateActiveTabId(contextMenu.tabId);
1071
- }, children: "Close Others" }),
1072
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-context-menu-item", onClick: () => {
1073
- const id = contextMenu.tabId;
1074
- closeContextMenu();
1075
- setTabs((prev) => {
1076
- const idx = prev.findIndex((t) => t.id === id);
1077
- if (idx === -1) return prev;
1078
- return prev.slice(0, idx + 1);
1079
- });
1080
- }, children: "Close to the Right" }),
1081
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-context-menu-separator" }),
1082
- /* @__PURE__ */ jsx3("button", { className: "cli-panel-context-menu-item destructive", onClick: () => {
1083
- closeContextMenu();
1084
- setTabs([]);
1085
- setInitialized(false);
1086
- updateCollapsed(true);
1087
- }, children: "Close All" })
1088
- ]
336
+ className: joinClasses("cli-panel-context-menu-item", item.destructive && "destructive"),
337
+ disabled: item.disabled,
338
+ onClick: () => runContextMenuAction(controller, item.action),
339
+ children: item.label
1089
340
  }
1090
- ),
1091
- servicesDropdownOpen && statusServiceCount.total > 0 && /* @__PURE__ */ jsxs(
1092
- "div",
341
+ )
342
+ ] }, item.action)) }),
343
+ /* @__PURE__ */ jsx3(StatusDropdown, { s })
344
+ ] });
345
+ });
346
+ function StatusDropdown({ s }) {
347
+ const dropdown = openStatusDropdown(s);
348
+ if (!dropdown) return null;
349
+ return /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown", style: asStyle(dropdown.style), onClick: (e) => e.stopPropagation(), children: [
350
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-header", children: dropdown.header }),
351
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-list", children: dropdown.rows.map((row) => /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown-item", children: [
352
+ /* @__PURE__ */ jsx3("span", { className: row.dotClass }),
353
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-svc-info", children: [
354
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-name", children: row.name }),
355
+ row.description && /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: row.description })
356
+ ] }),
357
+ dropdown.kind === "servers" ? /* @__PURE__ */ jsxs("div", { className: "cli-panel-srv-meta", children: [
358
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: row.status }),
359
+ row.meta.map((m) => /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: m }, m))
360
+ ] }) : /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: row.status })
361
+ ] }, row.key)) }),
362
+ dropdown.rows.length === 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-empty", children: dropdown.emptyText })
363
+ ] });
364
+ }
365
+ function PanelHeader({ controller, s }) {
366
+ const indicators = statusIndicators(s);
367
+ const openDropdown = (e, dropdown) => {
368
+ if (!dropdown) return;
369
+ e.stopPropagation();
370
+ if (dropdown === "services") controller.toggleServicesDropdown(e.currentTarget);
371
+ else controller.toggleServersDropdown(e.currentTarget);
372
+ };
373
+ return /* @__PURE__ */ jsxs("div", { className: "cli-panel-header", children: [
374
+ /* @__PURE__ */ jsx3(
375
+ "div",
376
+ {
377
+ className: "cli-panel-resize-bar",
378
+ onMouseDown: (e) => {
379
+ if (!s.resizable) return;
380
+ e.preventDefault();
381
+ controller.startPanelResize(e.clientX, e.clientY);
382
+ },
383
+ children: /* @__PURE__ */ jsx3("div", { className: "cli-panel-resize-grip" })
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-header-content", children: [
387
+ /* @__PURE__ */ jsxs("p", { className: "cli-panel-title", children: [
388
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ jsx3(Icon, { icon: TERMINAL_ICON }) }),
389
+ PANEL_LABELS.title
390
+ ] }),
391
+ s.showStatusIndicators && /* @__PURE__ */ jsx3("div", { className: "cli-panel-status-indicators", children: indicators.map((item) => /* @__PURE__ */ jsxs(
392
+ "span",
1093
393
  {
1094
- className: "cli-panel-services-dropdown",
1095
- style: (() => {
1096
- const el = servicesDropdownRef.current;
1097
- if (!el) return {};
1098
- const rect = el.getBoundingClientRect();
1099
- const base = { position: "fixed", zIndex: 1100 };
1100
- switch (position) {
1101
- case "top":
1102
- return { ...base, top: rect.bottom + 4, left: rect.left };
1103
- case "left":
1104
- return { ...base, top: rect.top, left: rect.right + 4 };
1105
- case "right":
1106
- return { ...base, top: rect.top, right: window.innerWidth - rect.left + 4 };
1107
- default:
1108
- return { ...base, bottom: window.innerHeight - rect.top + 4, left: rect.left };
1109
- }
1110
- })(),
1111
- onClick: (e) => e.stopPropagation(),
394
+ className: joinClasses("cli-panel-status-item", item.itemClass),
395
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0,
1112
396
  children: [
1113
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-header", children: "Background Services" }),
1114
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-list", children: statusServiceDetails.map((svc, i) => /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown-item", children: [
1115
- /* @__PURE__ */ jsx3("span", { className: `cli-panel-svc-dot ${svc.status === "running" ? "svc-running" : "svc-stopped"}` }),
1116
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-svc-info", children: [
1117
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-name", children: svc.name }),
1118
- svc.description && /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: svc.description })
1119
- ] }),
1120
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: svc.status })
1121
- ] }, i)) }),
1122
- statusServiceDetails.length === 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-empty", children: "No services registered" })
397
+ item.dotClass && /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${item.dotClass}` }),
398
+ item.icon && /* @__PURE__ */ jsx3("span", { className: joinClasses("cli-panel-status-icon", item.iconClass), children: item.icon }),
399
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: item.label })
1123
400
  ]
1124
- }
1125
- ),
1126
- serversDropdownOpen && statusServerState !== "none" && /* @__PURE__ */ jsxs(
1127
- "div",
401
+ },
402
+ item.key
403
+ )) }),
404
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-status-indicators-compact", children: indicators.map((item) => /* @__PURE__ */ jsxs(
405
+ "span",
1128
406
  {
1129
- className: "cli-panel-services-dropdown",
1130
- style: (() => {
1131
- const el = serversDropdownRef.current;
1132
- if (!el) return {};
1133
- const rect = el.getBoundingClientRect();
1134
- const base = { position: "fixed", zIndex: 1100 };
1135
- switch (position) {
1136
- case "top":
1137
- return { ...base, top: rect.bottom + 4, left: rect.left };
1138
- case "left":
1139
- return { ...base, top: rect.top, left: rect.right + 4 };
1140
- case "right":
1141
- return { ...base, top: rect.top, right: window.innerWidth - rect.left + 4 };
1142
- default:
1143
- return { ...base, bottom: window.innerHeight - rect.top + 4, left: rect.left };
1144
- }
1145
- })(),
1146
- onClick: (e) => e.stopPropagation(),
407
+ className: joinClasses("cli-panel-status-compact-item", item.compactClass),
408
+ title: item.title,
409
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0,
1147
410
  children: [
1148
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-header", children: "Server Connections" }),
1149
- /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-list", children: statusServerDetails.map((srv, i) => /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown-item", children: [
1150
- /* @__PURE__ */ jsx3("span", { className: `cli-panel-svc-dot ${srv.connected ? "svc-running" : "svc-stopped"}` }),
1151
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-svc-info", children: [
1152
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-name", children: srv.name }),
1153
- srv.url && /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: srv.url })
1154
- ] }),
1155
- /* @__PURE__ */ jsxs("div", { className: "cli-panel-srv-meta", children: [
1156
- /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: srv.connected ? "connected" : "disconnected" }),
1157
- srv.apiVersion && /* @__PURE__ */ jsxs("span", { className: "cli-panel-svc-desc", children: [
1158
- "v",
1159
- srv.apiVersion
1160
- ] }),
1161
- srv.commandCount && /* @__PURE__ */ jsxs("span", { className: "cli-panel-svc-desc", children: [
1162
- srv.commandCount,
1163
- " cmds"
1164
- ] })
1165
- ] })
1166
- ] }, i)) }),
1167
- statusServerDetails.length === 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-empty", children: "No servers configured" })
411
+ item.compactDotClass && /* @__PURE__ */ jsx3("span", { className: item.compactDotClass }),
412
+ item.dotClass && /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${item.dotClass}` }),
413
+ item.icon && /* @__PURE__ */ jsx3("span", { className: joinClasses("cli-panel-status-icon", item.iconClass), children: item.icon })
1168
414
  ]
1169
- }
1170
- )
1171
- ] });
1172
- }
1173
- );
415
+ },
416
+ item.key
417
+ )) }),
418
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-action-buttons", children: [
419
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
420
+ /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-position", title: PANEL_LABELS.movePanel, onClick: () => controller.togglePositionDropdown(), children: /* @__PURE__ */ jsx3(Icon, { icon: positionIcon(s.position) }) }),
421
+ s.positionDropdownOpen && /* @__PURE__ */ jsx3("div", { className: "cli-panel-position-dropdown", children: positionOptions(s).map((opt) => /* @__PURE__ */ jsxs("button", { className: opt.className, onClick: () => controller.setPosition(opt.position), children: [
422
+ /* @__PURE__ */ jsx3(Icon, { icon: positionIcon(opt.position, 16) }),
423
+ opt.label
424
+ ] }, opt.position)) })
425
+ ] }),
426
+ s.hideable && /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-hide", title: PANEL_LABELS.hide, onClick: () => controller.hide(), children: /* @__PURE__ */ jsx3(Icon, { icon: HIDE_ICON }) }),
427
+ /* @__PURE__ */ jsx3(
428
+ "button",
429
+ {
430
+ className: "cli-panel-btn",
431
+ title: s.maximized ? PANEL_LABELS.restore : PANEL_LABELS.maximize,
432
+ disabled: s.collapsed,
433
+ onClick: () => controller.toggleMaximize(),
434
+ children: /* @__PURE__ */ jsx3(Icon, { icon: s.maximized ? RESTORE_ICON : MAXIMIZE_ICON })
435
+ }
436
+ ),
437
+ /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn", title: s.collapsed ? PANEL_LABELS.expand : PANEL_LABELS.collapse, onClick: () => controller.toggle(), children: /* @__PURE__ */ jsx3(Icon, { icon: collapseChevronIcon(s.position, s.collapsed) }) }),
438
+ s.closable && /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-close", title: PANEL_LABELS.close, onClick: () => controller.close(), children: /* @__PURE__ */ jsx3(Icon, { icon: CLOSE_ICON }) })
439
+ ] })
440
+ ] }),
441
+ /* @__PURE__ */ jsx3("div", { className: glowLineClass(s) })
442
+ ] });
443
+ }
1174
444
 
1175
445
  // src/CliProvider.tsx
1176
- import { useRef as useRef4 } from "react";
446
+ import { useRef as useRef3 } from "react";
447
+ import { resolveCliHostConfig as resolveCliHostConfig3 } from "@qodalis/cli";
1177
448
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1178
449
  function CliProvider({
1179
450
  modules,
@@ -1184,13 +455,8 @@ function CliProvider({
1184
455
  style
1185
456
  }) {
1186
457
  const config = useCliConfig();
1187
- const containerRef = useRef4(null);
1188
- const engine = useCliEngine(containerRef, {
1189
- modules: modules ?? config.modules,
1190
- processors: processors ?? config.processors,
1191
- options: options ?? config.options,
1192
- services: services ?? config.services
1193
- });
458
+ const containerRef = useRef3(null);
459
+ const engine = useCliEngine(containerRef, resolveCliHostConfig3({ modules, processors, options, services }, config));
1194
460
  return /* @__PURE__ */ jsxs2(CliContext.Provider, { value: { engine }, children: [
1195
461
  /* @__PURE__ */ jsx4("div", { ref: containerRef, style: { height: "100%", ...style } }),
1196
462
  children
@@ -1198,12 +464,12 @@ function CliProvider({
1198
464
  }
1199
465
 
1200
466
  // src/index.tsx
1201
- import { CliEngine as CliEngine2 } from "@qodalis/cli";
467
+ import { CliEngine as CliEngine4 } from "@qodalis/cli";
1202
468
  export {
1203
469
  Cli,
1204
470
  CliConfigProvider,
1205
471
  CliContext,
1206
- CliEngine2 as CliEngine,
472
+ CliEngine4 as CliEngine,
1207
473
  CliPanel,
1208
474
  CliProvider,
1209
475
  useCli,