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