@qodalis/react-cli 2.0.0-beta.10 → 2.0.0-beta.12

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
@@ -247,562 +247,920 @@ function PositionIcon({ position, size = 20 }) {
247
247
  fillRect
248
248
  ] });
249
249
  }
250
- function CliPanel({ options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className }) {
251
- const config = useCliConfig();
252
- const options = optionsProp ?? config.options;
253
- const modules = modulesProp ?? config.modules;
254
- const processors = processorsProp ?? config.processors;
255
- const services = servicesProp ?? config.services;
256
- const [position, setPosition] = (0, import_react5.useState)(
257
- () => (0, import_cli_core.loadPanelPosition)() ?? options?.position ?? "bottom"
258
- );
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] = (0, import_react5.useState)(true);
266
- const [collapsed, setCollapsed] = (0, import_react5.useState)(options?.isCollapsed ?? true);
267
- const [maximized, setMaximized] = (0, import_react5.useState)(false);
268
- const [panelHeight, setPanelHeight] = (0, import_react5.useState)(600);
269
- const [panelWidth, setPanelWidth] = (0, import_react5.useState)(400);
270
- const [initialized, setInitialized] = (0, import_react5.useState)(false);
271
- const [hidden, setHidden] = (0, import_react5.useState)(options?.isHidden ?? false);
272
- const preHideCollapsedRef = (0, import_react5.useRef)(true);
273
- const [themeStyles, setThemeStyles] = (0, import_react5.useState)({});
274
- const wrapperRef = (0, import_react5.useRef)(null);
275
- const [tabs, setTabs] = (0, import_react5.useState)([]);
276
- const [activeTabId, setActiveTabId] = (0, import_react5.useState)(0);
277
- const [activePaneId, setActivePaneId] = (0, import_react5.useState)(0);
278
- const nextIdRef = (0, import_react5.useRef)({ tab: 1, pane: 1 });
279
- const [contextMenu, setContextMenu] = (0, import_react5.useState)({ visible: false, x: 0, y: 0, tabId: 0 });
280
- const engineMapRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
281
- const [paneResizing, setPaneResizing] = (0, import_react5.useState)(false);
282
- const paneResizeRef = (0, import_react5.useRef)({ tabId: 0, dividerIndex: 0, startX: 0, startWidths: [], containerWidth: 0 });
283
- const [panelResizing, setPanelResizing] = (0, import_react5.useState)(false);
284
- const panelResizeRef = (0, import_react5.useRef)({ startY: 0, startHeight: 0, startX: 0, startWidth: 0 });
285
- const prevHeightRef = (0, import_react5.useRef)(600);
286
- const prevWidthRef = (0, import_react5.useRef)(400);
287
- const addTab = (0, import_react5.useCallback)(() => {
288
- const paneId = nextIdRef.current.pane++;
289
- const tabId = nextIdRef.current.tab++;
290
- const pane = { id: paneId, widthPercent: 100 };
291
- const tab = { id: tabId, title: `Terminal ${tabId}`, isEditing: false, panes: [pane] };
292
- setTabs((prev) => [...prev, tab]);
293
- setActiveTabId(tabId);
294
- setActivePaneId(paneId);
295
- }, []);
296
- const toggle = (0, import_react5.useCallback)(() => {
297
- setCollapsed((prev) => {
298
- const next = !prev;
299
- if (!next && !initialized) {
300
- setInitialized(true);
301
- const paneId = nextIdRef.current.pane++;
302
- const tabId = nextIdRef.current.tab++;
303
- const pane = { id: paneId, widthPercent: 100 };
304
- const tab = { id: tabId, title: `Terminal ${tabId}`, isEditing: false, panes: [pane] };
305
- setTabs([tab]);
306
- setActiveTabId(tabId);
307
- setActivePaneId(paneId);
308
- }
309
- return next;
310
- });
311
- }, [initialized]);
312
- const closeTab = (0, import_react5.useCallback)((id) => {
313
- setTabs((prev) => {
314
- const tab = prev.find((t) => t.id === id);
315
- if (tab) {
316
- tab.panes.forEach((p) => engineMapRef.current.delete(p.id));
317
- }
318
- const next = prev.filter((t) => t.id !== id);
319
- if (next.length === 0) {
320
- setInitialized(false);
321
- setCollapsed(true);
322
- return [];
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 [statusText, setStatusText] = (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);
323
369
  }
324
- setActiveTabId((current) => {
325
- if (current === id) {
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) {
326
387
  const oldIdx = prev.findIndex((t) => t.id === id);
327
388
  const newIdx = Math.min(oldIdx, next.length - 1);
328
- return next[newIdx].id;
389
+ updateActiveTabId(next[newIdx].id);
329
390
  }
330
- return current;
331
- });
332
- return next;
333
- });
334
- }, []);
335
- const selectTab = (0, import_react5.useCallback)((id) => {
336
- setActiveTabId(id);
337
- setTabs((prev) => {
338
- const tab = prev.find((t) => t.id === id);
339
- if (tab && tab.panes.length > 0) {
340
- setActivePaneId(tab.panes[0].id);
341
- }
342
- return prev;
343
- });
344
- }, []);
345
- const normalizePanes = (panes) => {
346
- const total = panes.reduce((s, p) => s + p.widthPercent, 0);
347
- if (total === 0) return;
348
- const scale = 100 / total;
349
- panes.forEach((p) => {
350
- p.widthPercent *= scale;
351
- });
352
- };
353
- const splitRight = (0, import_react5.useCallback)((tabId, afterPaneId) => {
354
- setTabs((prev) => {
355
- const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
356
- const tab = next.find((t) => t.id === (tabId ?? activeTabId));
357
- if (!tab) return prev;
358
- const targetPaneId = afterPaneId ?? activePaneId;
359
- const idx = tab.panes.findIndex((p) => p.id === targetPaneId);
360
- const insertIdx = idx === -1 ? tab.panes.length : idx + 1;
361
- const newPaneId = nextIdRef.current.pane++;
362
- const newPane = { id: newPaneId, widthPercent: 0 };
363
- tab.panes.splice(insertIdx, 0, newPane);
364
- const evenWidth = 100 / tab.panes.length;
365
- tab.panes.forEach((p) => {
366
- p.widthPercent = evenWidth;
391
+ return next;
367
392
  });
368
- normalizePanes(tab.panes);
369
- setActivePaneId(newPaneId);
370
- return next;
371
- });
372
- }, [activeTabId, activePaneId]);
373
- const closePane = (0, import_react5.useCallback)((tabId, paneId) => {
374
- setTabs((prev) => {
375
- const tab = prev.find((t) => t.id === tabId);
376
- if (!tab) return prev;
377
- if (tab.panes.length <= 1) {
378
- const next2 = prev.filter((t) => t.id !== tabId);
379
- if (next2.length === 0) {
380
- setInitialized(false);
381
- setCollapsed(true);
382
- return [];
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);
383
401
  }
384
- return next2;
385
- }
386
- const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
387
- const updatedTab = next.find((t) => t.id === tabId);
388
- const idx = updatedTab.panes.findIndex((p) => p.id === paneId);
389
- if (idx === -1) return prev;
390
- updatedTab.panes.splice(idx, 1);
391
- const totalRemaining = updatedTab.panes.reduce((s, p) => s + p.widthPercent, 0);
392
- if (totalRemaining > 0) {
393
- updatedTab.panes.forEach((p) => {
394
- p.widthPercent = p.widthPercent / totalRemaining * 100;
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;
395
426
  });
396
- }
397
- normalizePanes(updatedTab.panes);
398
- setActivePaneId((curr) => {
399
- if (curr === paneId) {
400
- const newIdx = Math.min(idx, updatedTab.panes.length - 1);
401
- return updatedTab.panes[newIdx].id;
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;
402
455
  }
403
- return curr;
456
+ return prev;
404
457
  });
405
- return next;
406
- });
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 closeContextMenu = (0, import_react5.useCallback)(() => {
418
- setContextMenu((prev) => ({ ...prev, visible: false }));
419
- }, []);
420
- (0, import_react5.useEffect)(() => {
421
- if (!contextMenu.visible) return;
422
- const handler = () => closeContextMenu();
423
- document.addEventListener("click", handler);
424
- return () => document.removeEventListener("click", handler);
425
- }, [contextMenu.visible, closeContextMenu]);
426
- const onPanelResizeStart = (0, import_react5.useCallback)((e) => {
427
- if (!resizable) return;
428
- e.preventDefault();
429
- if (collapsed) {
430
- toggle();
431
- }
432
- setPanelResizing(true);
433
- panelResizeRef.current = {
434
- startY: e.clientY,
435
- startHeight: panelHeight,
436
- startX: e.clientX,
437
- startWidth: panelWidth
438
- };
439
- }, [collapsed, toggle, panelHeight, panelWidth, resizable]);
440
- (0, import_react5.useEffect)(() => {
441
- if (!panelResizing) return;
442
- const onMove = (e) => {
443
- if (isHorizontal) {
444
- const deltaX = position === "left" ? e.clientX - panelResizeRef.current.startX : panelResizeRef.current.startX - e.clientX;
445
- let next = Math.max(100, panelResizeRef.current.startWidth + deltaX);
446
- if (next > window.innerWidth) next = window.innerWidth;
447
- setPanelWidth(next);
448
- } else {
449
- const deltaY = position === "top" ? e.clientY - panelResizeRef.current.startY : panelResizeRef.current.startY - e.clientY;
450
- let next = Math.max(100, panelResizeRef.current.startHeight + deltaY);
451
- if (next > window.innerHeight) next = window.innerHeight;
452
- setPanelHeight(next);
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();
453
473
  }
454
- };
455
- const onUp = () => setPanelResizing(false);
456
- document.addEventListener("mousemove", onMove);
457
- document.addEventListener("mouseup", onUp);
458
- return () => {
459
- document.removeEventListener("mousemove", onMove);
460
- document.removeEventListener("mouseup", onUp);
461
- };
462
- }, [panelResizing, isHorizontal, position]);
463
- const onPaneResizeStart = (0, import_react5.useCallback)((e, tabId, dividerIndex) => {
464
- e.preventDefault();
465
- const tab = tabs.find((t) => t.id === tabId);
466
- if (!tab) return;
467
- setPaneResizing(true);
468
- const container = e.target.closest(".cli-panel-panes-container");
469
- paneResizeRef.current = {
470
- tabId,
471
- dividerIndex,
472
- startX: e.clientX,
473
- startWidths: tab.panes.map((p) => p.widthPercent),
474
- containerWidth: container ? container.clientWidth : 1
475
- };
476
- }, [tabs]);
477
- (0, import_react5.useEffect)(() => {
478
- if (!paneResizing) return;
479
- const onMove = (e) => {
480
- const ref = paneResizeRef.current;
481
- const deltaX = e.clientX - ref.startX;
482
- const deltaPct = deltaX / ref.containerWidth * 100;
483
- const i = ref.dividerIndex;
484
- let leftWidth = ref.startWidths[i] + deltaPct;
485
- let rightWidth = ref.startWidths[i + 1] - deltaPct;
486
- if (leftWidth < MIN_PANE_WIDTH_PERCENT) {
487
- leftWidth = MIN_PANE_WIDTH_PERCENT;
488
- rightWidth = ref.startWidths[i] + ref.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
489
- }
490
- if (rightWidth < MIN_PANE_WIDTH_PERCENT) {
491
- rightWidth = MIN_PANE_WIDTH_PERCENT;
492
- leftWidth = ref.startWidths[i] + ref.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
493
- }
494
- setTabs((prev) => {
495
- const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
496
- const tab = next.find((t) => t.id === ref.tabId);
497
- if (tab) {
498
- tab.panes[i].widthPercent = leftWidth;
499
- tab.panes[i + 1].widthPercent = rightWidth;
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);
500
495
  }
501
- return next;
502
- });
503
- };
504
- const onUp = () => {
505
- setPaneResizing(false);
506
- document.body.classList.remove("cli-pane-resizing");
507
- };
508
- document.body.classList.add("cli-pane-resizing");
509
- document.addEventListener("mousemove", onMove);
510
- document.addEventListener("mouseup", onUp);
511
- return () => {
512
- document.removeEventListener("mousemove", onMove);
513
- document.removeEventListener("mouseup", onUp);
514
- };
515
- }, [paneResizing]);
516
- (0, import_react5.useEffect)(() => {
517
- if (!syncTheme || !initialized) return;
518
- const syncFromEngine = () => {
519
- const engine = engineMapRef.current.values().next().value;
520
- if (!engine) return;
521
- const theme = engine.getTerminal().options.theme;
522
- if (theme) setThemeStyles((0, import_cli_core.derivePanelThemeStyles)(theme));
523
- };
524
- const timer = setTimeout(() => {
525
- syncFromEngine();
526
- const container = wrapperRef.current?.querySelector(".terminal-container");
527
- if (!container) return;
528
- const observer = new MutationObserver(syncFromEngine);
529
- observer.observe(container, { attributes: true, attributeFilter: ["style"] });
530
- return () => observer.disconnect();
531
- }, 200);
532
- return () => clearTimeout(timer);
533
- }, [syncTheme, initialized]);
534
- const toggleMaximize = (0, import_react5.useCallback)(() => {
535
- setMaximized((prev) => {
536
- if (!prev) {
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 text = context.getStatusText?.();
590
+ setStatusText(text || 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) {
537
654
  if (isHorizontal) {
538
655
  prevWidthRef.current = panelWidth;
539
- setPanelWidth(window.innerWidth);
656
+ updateWidth(window.innerWidth);
540
657
  } else {
541
658
  prevHeightRef.current = panelHeight;
542
- setPanelHeight(window.innerHeight);
659
+ updateHeight(window.innerHeight);
543
660
  }
544
661
  } else {
545
662
  if (isHorizontal) {
546
- setPanelWidth(prevWidthRef.current);
663
+ updateWidth(prevWidthRef.current);
547
664
  } else {
548
- setPanelHeight(prevHeightRef.current);
665
+ updateHeight(prevHeightRef.current);
549
666
  }
550
667
  }
551
- return !prev;
552
- });
553
- }, [panelHeight, panelWidth, isHorizontal]);
554
- const handleClose = (0, import_react5.useCallback)(() => {
555
- setVisible(false);
556
- onClose?.();
557
- }, [onClose]);
558
- const handleHide = (0, import_react5.useCallback)(() => {
559
- preHideCollapsedRef.current = collapsed;
560
- setHidden(true);
561
- }, [collapsed]);
562
- const handleUnhide = (0, import_react5.useCallback)(() => {
563
- setHidden(false);
564
- setCollapsed(preHideCollapsedRef.current);
565
- }, []);
566
- const [positionDropdownOpen, setPositionDropdownOpen] = (0, import_react5.useState)(false);
567
- const closePositionDropdown = (0, import_react5.useCallback)(() => setPositionDropdownOpen(false), []);
568
- (0, import_react5.useEffect)(() => {
569
- if (!positionDropdownOpen) return;
570
- document.addEventListener("click", closePositionDropdown);
571
- return () => document.removeEventListener("click", closePositionDropdown);
572
- }, [positionDropdownOpen, closePositionDropdown]);
573
- const handlePositionButtonClick = (0, import_react5.useCallback)((e) => {
574
- e.stopPropagation();
575
- setPositionDropdownOpen((prev) => !prev);
576
- }, []);
577
- const selectPosition = (0, import_react5.useCallback)((pos) => {
578
- setPositionDropdownOpen(false);
579
- setPosition(pos);
580
- (0, import_cli_core.savePanelPosition)(pos);
581
- }, []);
582
- if (!visible) return null;
583
- const wrapperStyle = {
584
- ...isHorizontal ? { width: `${panelWidth}px` } : { height: `${panelHeight}px` },
585
- ...hidden ? { display: "none" } : {},
586
- ...themeStyles,
587
- ...style
588
- };
589
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
590
- hidden && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
591
- "button",
592
- {
593
- className: "cli-panel-hide-tab",
594
- "data-position": position,
595
- "data-hide-align": hideAlignment,
596
- style: themeStyles,
597
- title: "Show CLI",
598
- onClick: handleUnhide,
599
- children: [
600
- /* @__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: [
601
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 17 10 11 4 5" }),
602
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
603
- ] }),
604
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-hide-tab-label", children: "CLI" }),
605
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HideTabChevron, { position })
606
- ]
607
- }
608
- ),
609
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
610
- "div",
611
- {
612
- ref: wrapperRef,
613
- className: `cli-panel-wrapper ${collapsed ? "collapsed" : ""} ${maximized ? "maximized" : ""} ${panelResizing ? "resizing" : ""} ${className ?? ""}`,
614
- style: wrapperStyle,
615
- "data-position": position,
616
- "data-resizable": String(resizable),
617
- "data-closable": String(closable),
618
- "data-hideable": String(hideable),
619
- children: [
620
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header", children: [
621
- /* @__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" }) }),
622
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header-content", children: [
623
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "cli-panel-title", children: [
624
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TerminalIcon, {}) }),
625
- "CLI"
626
- ] }),
627
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-action-buttons", children: [
628
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
629
- /* @__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 }) }),
630
- 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)(
631
- "button",
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",
632
866
  {
633
- className: `cli-panel-position-dropdown-item${position === pos ? " active" : ""}`,
634
- onClick: () => selectPosition(pos),
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
+ },
635
874
  children: [
636
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position: pos, size: 16 }),
637
- pos.charAt(0).toUpperCase() + pos.slice(1)
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
+ ] })
638
882
  ]
639
- },
640
- pos
641
- )) })
642
- ] }),
643
- 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, {}) }),
644
- /* @__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, {}) }),
645
- /* @__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 }) }),
646
- 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, {}) })
647
- ] })
648
- ] })
649
- ] }),
650
- initialized && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
651
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-tabs", children: [
652
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "cli-panel-tab-list", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
653
- "li",
654
- {
655
- className: `cli-panel-tab ${tab.id === activeTabId ? "active" : ""}`,
656
- onClick: () => selectTab(tab.id),
657
- onDoubleClick: () => startRename(tab.id),
658
- onContextMenu: (e) => {
659
- e.preventDefault();
660
- e.stopPropagation();
661
- setContextMenu({ visible: true, x: e.clientX, y: e.clientY, tabId: tab.id });
662
- },
663
- children: tab.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
664
- "input",
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",
665
891
  {
666
- className: "cli-panel-tab-rename-input",
667
- type: "text",
668
- defaultValue: tab.title,
669
- autoFocus: true,
670
- onKeyDown: (e) => {
671
- if (e.key === "Enter") commitRename(tab.id, e.target.value);
672
- else if (e.key === "Escape") setTabs((prev) => prev.map((t) => ({ ...t, isEditing: false })));
892
+ ref: serversDropdownRef,
893
+ className: "cli-panel-status-item status-clickable",
894
+ onClick: (e) => {
895
+ e.stopPropagation();
896
+ setServersDropdownOpen((prev) => !prev);
897
+ setServicesDropdownOpen(false);
673
898
  },
674
- onBlur: (e) => commitRename(tab.id, e.target.value),
675
- onClick: (e) => e.stopPropagation(),
676
- onDoubleClick: (e) => e.stopPropagation()
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
+ ]
677
908
  }
678
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
679
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-tab-title", children: tab.title }),
680
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
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
+ statusText && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-item status-text", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-label", children: statusText }) })
915
+ ] }),
916
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-action-buttons", children: [
917
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
918
+ /* @__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 }) }),
919
+ 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)(
681
920
  "button",
682
921
  {
683
- className: "cli-panel-tab-close-btn",
684
- title: "Close tab",
685
- onClick: (e) => {
686
- e.stopPropagation();
687
- closeTab(tab.id);
688
- },
689
- children: "\xD7"
690
- }
691
- )
692
- ] })
693
- },
694
- tab.id
695
- )) }),
696
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-add-tab", title: "New terminal", onClick: addTab, children: "+" })
922
+ className: `cli-panel-position-dropdown-item${position === pos ? " active" : ""}`,
923
+ onClick: () => selectPosition(pos),
924
+ children: [
925
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position: pos, size: 16 }),
926
+ pos.charAt(0).toUpperCase() + pos.slice(1)
927
+ ]
928
+ },
929
+ pos
930
+ )) })
931
+ ] }),
932
+ 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, {}) }),
933
+ /* @__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, {}) }),
934
+ /* @__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 }) }),
935
+ 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, {}) })
936
+ ] })
937
+ ] }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `cli-panel-glow-line${statusExecutionState === "running" ? " glow-active" : ""}` })
697
939
  ] }),
698
- /* @__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: [
699
- 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" }) }),
700
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
701
- "div",
702
- {
703
- className: `cli-panel-pane ${pane.id === activePaneId && tab.id === activeTabId ? "active-pane" : ""}`,
704
- style: { flex: `${pane.widthPercent} 1 0` },
705
- onClick: () => {
706
- setActivePaneId(pane.id);
707
- setActiveTabId(tab.id);
708
- },
709
- children: [
710
- tab.panes.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
711
- "button",
940
+ initialized && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
941
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-tabs", children: [
942
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "cli-panel-tab-list", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
943
+ "li",
944
+ {
945
+ className: `cli-panel-tab ${tab.id === activeTabId ? "active" : ""}`,
946
+ onClick: () => selectTab(tab.id),
947
+ onDoubleClick: () => startRename(tab.id),
948
+ onContextMenu: (e) => {
949
+ e.preventDefault();
950
+ e.stopPropagation();
951
+ setContextMenu({ visible: true, x: e.clientX, y: e.clientY, tabId: tab.id });
952
+ },
953
+ children: tab.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
954
+ "input",
712
955
  {
713
- className: "cli-panel-pane-close-btn",
714
- onClick: (e) => {
715
- e.stopPropagation();
716
- closePane(tab.id, pane.id);
956
+ className: "cli-panel-tab-rename-input",
957
+ type: "text",
958
+ defaultValue: tab.title,
959
+ autoFocus: true,
960
+ onKeyDown: (e) => {
961
+ if (e.key === "Enter") commitRename(tab.id, e.target.value);
962
+ else if (e.key === "Escape") setTabs((prev) => prev.map((t) => ({ ...t, isEditing: false })));
717
963
  },
718
- title: "Close pane",
719
- children: "\xD7"
964
+ onBlur: (e) => commitRename(tab.id, e.target.value),
965
+ onClick: (e) => e.stopPropagation(),
966
+ onDoubleClick: (e) => e.stopPropagation()
720
967
  }
721
- ),
722
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
723
- Cli,
724
- {
725
- options,
726
- modules,
727
- processors,
728
- services,
729
- snapshot: pane.snapshot,
730
- onReady: (engine) => engineMapRef.current.set(pane.id, engine)
731
- }
732
- ) })
733
- ]
734
- }
735
- )
736
- ] }, pane.id)) }) }, tab.id)) })
737
- ] })
738
- ]
739
- }
740
- ),
741
- contextMenu.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
742
- "div",
743
- {
744
- className: "cli-panel-context-menu",
745
- style: { left: contextMenu.x, top: contextMenu.y },
746
- onClick: (e) => e.stopPropagation(),
747
- children: [
748
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
749
- closeContextMenu();
750
- startRename(contextMenu.tabId);
751
- }, children: "Rename" }),
752
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
753
- closeContextMenu();
754
- const tab = tabs.find((t) => t.id === contextMenu.tabId);
755
- if (!tab) return;
756
- const sourceEngine = engineMapRef.current.get(tab.panes[0]?.id);
757
- const snapshot = sourceEngine?.snapshot();
758
- const paneId = nextIdRef.current.pane++;
759
- const tabId = nextIdRef.current.tab++;
760
- const pane = { id: paneId, widthPercent: 100, snapshot };
761
- const newTab = { id: tabId, title: `${tab.title} (copy)`, isEditing: false, panes: [pane] };
762
- const idx = tabs.indexOf(tab);
763
- setTabs((prev) => {
764
- const next = [...prev];
765
- next.splice(idx + 1, 0, newTab);
766
- return next;
767
- });
768
- setActiveTabId(tabId);
769
- setActivePaneId(paneId);
770
- }, children: "Duplicate" }),
771
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
772
- closeContextMenu();
773
- splitRight(contextMenu.tabId);
774
- }, children: "Split Right" }),
775
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
776
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
777
- closeContextMenu();
778
- closeTab(contextMenu.tabId);
779
- }, children: "Close" }),
780
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", disabled: tabs.length <= 1, onClick: () => {
781
- closeContextMenu();
782
- setTabs((prev) => prev.filter((t) => t.id === contextMenu.tabId));
783
- setActiveTabId(contextMenu.tabId);
784
- }, children: "Close Others" }),
785
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
786
- const id = contextMenu.tabId;
787
- closeContextMenu();
788
- setTabs((prev) => {
789
- const idx = prev.findIndex((t) => t.id === id);
790
- if (idx === -1) return prev;
791
- return prev.slice(0, idx + 1);
792
- });
793
- }, children: "Close to the Right" }),
794
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
795
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item destructive", onClick: () => {
796
- closeContextMenu();
797
- setTabs([]);
798
- setInitialized(false);
799
- setCollapsed(true);
800
- }, children: "Close All" })
801
- ]
802
- }
803
- )
804
- ] });
805
- }
968
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
969
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-tab-dot ${(() => {
970
+ if (tab.id !== activeTabId) return "dot-idle";
971
+ if (statusExecutionState === "running") return "dot-running";
972
+ if (statusLastCommand && !statusLastCommand.success) return "dot-error";
973
+ return "dot-idle";
974
+ })()}` }),
975
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-tab-title", children: tab.title }),
976
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
977
+ "button",
978
+ {
979
+ className: "cli-panel-tab-close-btn",
980
+ title: "Close tab",
981
+ onClick: (e) => {
982
+ e.stopPropagation();
983
+ closeTab(tab.id);
984
+ },
985
+ children: "\xD7"
986
+ }
987
+ )
988
+ ] })
989
+ },
990
+ tab.id
991
+ )) }),
992
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-add-tab", title: "New terminal", onClick: () => addTab(), children: "+" })
993
+ ] }),
994
+ /* @__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: [
995
+ 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" }) }),
996
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
997
+ "div",
998
+ {
999
+ className: `cli-panel-pane ${pane.id === activePaneId && tab.id === activeTabId ? "active-pane" : ""}`,
1000
+ style: { flex: `${pane.widthPercent} 1 0` },
1001
+ onClick: () => {
1002
+ setActivePaneId(pane.id);
1003
+ updateActiveTabId(tab.id);
1004
+ },
1005
+ children: [
1006
+ tab.panes.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1007
+ "button",
1008
+ {
1009
+ className: "cli-panel-pane-close-btn",
1010
+ onClick: (e) => {
1011
+ e.stopPropagation();
1012
+ closePane(pane.id);
1013
+ },
1014
+ title: "Close pane",
1015
+ children: "\xD7"
1016
+ }
1017
+ ),
1018
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1019
+ Cli,
1020
+ {
1021
+ options,
1022
+ modules,
1023
+ processors,
1024
+ services,
1025
+ snapshot: pane.snapshot,
1026
+ onReady: (engine) => engineMapRef.current.set(pane.id, engine)
1027
+ }
1028
+ ) })
1029
+ ]
1030
+ }
1031
+ )
1032
+ ] }, pane.id)) }) }, tab.id)) })
1033
+ ] })
1034
+ ]
1035
+ }
1036
+ ),
1037
+ contextMenu.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1038
+ "div",
1039
+ {
1040
+ className: "cli-panel-context-menu",
1041
+ style: { left: contextMenu.x, top: contextMenu.y },
1042
+ onClick: (e) => e.stopPropagation(),
1043
+ children: [
1044
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
1045
+ closeContextMenu();
1046
+ startRename(contextMenu.tabId);
1047
+ }, children: "Rename" }),
1048
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
1049
+ closeContextMenu();
1050
+ const tab = tabs.find((t) => t.id === contextMenu.tabId);
1051
+ if (!tab) return;
1052
+ const sourceEngine = engineMapRef.current.get(tab.panes[0]?.id);
1053
+ const snapshot = sourceEngine?.snapshot();
1054
+ const paneId = nextIdRef.current.pane++;
1055
+ const tabId = nextIdRef.current.tab++;
1056
+ const pane = { id: paneId, widthPercent: 100, snapshot };
1057
+ const newTab = { id: tabId, title: `${tab.title} (copy)`, isEditing: false, panes: [pane] };
1058
+ const idx = tabs.indexOf(tab);
1059
+ setTabs((prev) => {
1060
+ const next = [...prev];
1061
+ next.splice(idx + 1, 0, newTab);
1062
+ return next;
1063
+ });
1064
+ updateActiveTabId(tabId);
1065
+ setActivePaneId(paneId);
1066
+ props.onTabAdded?.({ tabId });
1067
+ }, children: "Duplicate" }),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
1069
+ closeContextMenu();
1070
+ splitPane(contextMenu.tabId);
1071
+ }, children: "Split Right" }),
1072
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
1073
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
1074
+ closeContextMenu();
1075
+ closeTab(contextMenu.tabId);
1076
+ }, children: "Close" }),
1077
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", disabled: tabs.length <= 1, onClick: () => {
1078
+ closeContextMenu();
1079
+ setTabs((prev) => prev.filter((t) => t.id === contextMenu.tabId));
1080
+ updateActiveTabId(contextMenu.tabId);
1081
+ }, children: "Close Others" }),
1082
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
1083
+ const id = contextMenu.tabId;
1084
+ closeContextMenu();
1085
+ setTabs((prev) => {
1086
+ const idx = prev.findIndex((t) => t.id === id);
1087
+ if (idx === -1) return prev;
1088
+ return prev.slice(0, idx + 1);
1089
+ });
1090
+ }, children: "Close to the Right" }),
1091
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
1092
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item destructive", onClick: () => {
1093
+ closeContextMenu();
1094
+ setTabs([]);
1095
+ setInitialized(false);
1096
+ updateCollapsed(true);
1097
+ }, children: "Close All" })
1098
+ ]
1099
+ }
1100
+ ),
1101
+ servicesDropdownOpen && statusServiceCount.total > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1102
+ "div",
1103
+ {
1104
+ className: "cli-panel-services-dropdown",
1105
+ style: (() => {
1106
+ const el = servicesDropdownRef.current;
1107
+ if (!el) return {};
1108
+ const rect = el.getBoundingClientRect();
1109
+ return position === "top" ? { position: "fixed", top: rect.bottom + 4, left: rect.left, zIndex: 1100 } : { position: "fixed", bottom: window.innerHeight - rect.top + 4, left: rect.left, zIndex: 1100 };
1110
+ })(),
1111
+ onClick: (e) => e.stopPropagation(),
1112
+ children: [
1113
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-header", children: "Background Services" }),
1114
+ /* @__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: [
1115
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-svc-dot ${svc.status === "running" ? "svc-running" : "svc-stopped"}` }),
1116
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-svc-info", children: [
1117
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-name", children: svc.name }),
1118
+ svc.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-desc", children: svc.description })
1119
+ ] }),
1120
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-status", children: svc.status })
1121
+ ] }, i)) }),
1122
+ statusServiceDetails.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-empty", children: "No services registered" })
1123
+ ]
1124
+ }
1125
+ ),
1126
+ serversDropdownOpen && statusServerState !== "none" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1127
+ "div",
1128
+ {
1129
+ className: "cli-panel-services-dropdown",
1130
+ style: (() => {
1131
+ const el = serversDropdownRef.current;
1132
+ if (!el) return {};
1133
+ const rect = el.getBoundingClientRect();
1134
+ return position === "top" ? { position: "fixed", top: rect.bottom + 4, left: rect.left, zIndex: 1100 } : { position: "fixed", bottom: window.innerHeight - rect.top + 4, left: rect.left, zIndex: 1100 };
1135
+ })(),
1136
+ onClick: (e) => e.stopPropagation(),
1137
+ children: [
1138
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-header", children: "Server Connections" }),
1139
+ /* @__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: [
1140
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-svc-dot ${srv.connected ? "svc-running" : "svc-stopped"}` }),
1141
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-svc-info", children: [
1142
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-name", children: srv.name }),
1143
+ srv.url && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-desc", children: srv.url })
1144
+ ] }),
1145
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-srv-meta", children: [
1146
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-status", children: srv.connected ? "connected" : "disconnected" }),
1147
+ srv.apiVersion && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-svc-desc", children: [
1148
+ "v",
1149
+ srv.apiVersion
1150
+ ] }),
1151
+ srv.commandCount && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-svc-desc", children: [
1152
+ srv.commandCount,
1153
+ " cmds"
1154
+ ] })
1155
+ ] })
1156
+ ] }, i)) }),
1157
+ statusServerDetails.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-empty", children: "No servers configured" })
1158
+ ]
1159
+ }
1160
+ )
1161
+ ] });
1162
+ }
1163
+ );
806
1164
 
807
1165
  // src/CliProvider.tsx
808
1166
  var import_react6 = require("react");