@qodalis/vue-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
@@ -275,38 +275,97 @@ var CliPanel = (0, import_vue3.defineComponent)({
275
275
  type: Function,
276
276
  default: void 0
277
277
  },
278
+ collapsed: { type: Boolean, default: void 0 },
279
+ hidden: { type: Boolean, default: void 0 },
280
+ maximized: { type: Boolean, default: void 0 },
281
+ activeTabId: { type: Number, default: void 0 },
282
+ position: { type: String, default: void 0 },
283
+ height: { type: Number, default: void 0 },
284
+ width: { type: Number, default: void 0 },
278
285
  style: {
279
286
  type: Object,
280
287
  default: void 0
281
288
  },
282
289
  class: { type: String, default: void 0 }
283
290
  },
284
- setup(props) {
291
+ emits: [
292
+ "update:collapsed",
293
+ "update:hidden",
294
+ "update:maximized",
295
+ "update:activeTabId",
296
+ "update:position",
297
+ "update:height",
298
+ "update:width",
299
+ "close",
300
+ "tab-added",
301
+ "tab-closed",
302
+ "pane-split",
303
+ "pane-closed"
304
+ ],
305
+ setup(props, { expose, emit }) {
285
306
  const config = (0, import_vue3.inject)(CliConfigKey, null);
286
- const mergedOptions = (0, import_vue3.computed)(() => props.options ?? config?.options);
307
+ const mergedOptions = (0, import_vue3.computed)(() => props.options ? { ...config?.options, ...props.options } : config?.options);
287
308
  const mergedModules = (0, import_vue3.computed)(() => props.modules ?? config?.modules);
288
309
  const mergedProcessors = (0, import_vue3.computed)(() => props.processors ?? config?.processors);
289
310
  const mergedServices = (0, import_vue3.computed)(() => props.services ?? config?.services);
290
311
  const visible = (0, import_vue3.ref)(true);
291
- const collapsed = (0, import_vue3.ref)(mergedOptions.value?.isCollapsed ?? true);
292
- const maximized = (0, import_vue3.ref)(false);
293
- const panelHeight = (0, import_vue3.ref)(600);
294
- const panelWidth = (0, import_vue3.ref)(400);
312
+ const internalCollapsed = (0, import_vue3.ref)(mergedOptions.value?.isCollapsed ?? true);
313
+ const internalMaximized = (0, import_vue3.ref)(false);
314
+ const internalHeight = (0, import_vue3.ref)(600);
315
+ const internalWidth = (0, import_vue3.ref)(400);
295
316
  const prevHeight = (0, import_vue3.ref)(600);
296
317
  const prevWidth = (0, import_vue3.ref)(400);
297
318
  const initialized = (0, import_vue3.ref)(false);
298
- const hidden = (0, import_vue3.ref)(props.options?.isHidden ?? false);
319
+ const internalHidden = (0, import_vue3.ref)(props.options?.isHidden ?? false);
299
320
  let preHideCollapsed = true;
300
- const position = (0, import_vue3.ref)((0, import_cli_core.loadPanelPosition)() ?? mergedOptions.value?.position ?? "bottom");
321
+ const internalActiveTabId = (0, import_vue3.ref)(0);
322
+ const internalPosition = (0, import_vue3.ref)(
323
+ (0, import_cli_core.loadPanelPosition)() ?? mergedOptions.value?.position ?? "bottom"
324
+ );
325
+ const resolvedCollapsed = (0, import_vue3.computed)(() => props.collapsed !== void 0 ? props.collapsed : internalCollapsed.value);
326
+ const resolvedHidden = (0, import_vue3.computed)(() => props.hidden !== void 0 ? props.hidden : internalHidden.value);
327
+ const resolvedMaximized = (0, import_vue3.computed)(() => props.maximized !== void 0 ? props.maximized : internalMaximized.value);
328
+ const resolvedActiveTabId = (0, import_vue3.computed)(() => props.activeTabId !== void 0 ? props.activeTabId : internalActiveTabId.value);
329
+ const resolvedHeight = (0, import_vue3.computed)(() => props.height !== void 0 ? props.height : internalHeight.value);
330
+ const resolvedWidth = (0, import_vue3.computed)(() => props.width !== void 0 ? props.width : internalWidth.value);
331
+ const resolvedPosition = (0, import_vue3.computed)(() => props.position !== void 0 ? props.position : internalPosition.value);
332
+ function updateCollapsed(value) {
333
+ internalCollapsed.value = value;
334
+ emit("update:collapsed", value);
335
+ }
336
+ function updateHidden(value) {
337
+ internalHidden.value = value;
338
+ emit("update:hidden", value);
339
+ }
340
+ function updateMaximized(value) {
341
+ internalMaximized.value = value;
342
+ emit("update:maximized", value);
343
+ }
344
+ function updateActiveTabId(value) {
345
+ internalActiveTabId.value = value;
346
+ emit("update:activeTabId", value);
347
+ }
348
+ function updateHeight(value) {
349
+ internalHeight.value = value;
350
+ emit("update:height", value);
351
+ }
352
+ function updateWidth(value) {
353
+ internalWidth.value = value;
354
+ emit("update:width", value);
355
+ }
356
+ function updatePosition(value) {
357
+ internalPosition.value = value;
358
+ (0, import_cli_core.savePanelPosition)(value);
359
+ emit("update:position", value);
360
+ }
301
361
  const closable = (0, import_vue3.computed)(() => mergedOptions.value?.closable ?? true);
302
362
  const resizable = (0, import_vue3.computed)(() => mergedOptions.value?.resizable ?? true);
303
- const isHorizontal = (0, import_vue3.computed)(() => position.value === "left" || position.value === "right");
363
+ const isHorizontal = (0, import_vue3.computed)(() => resolvedPosition.value === "left" || resolvedPosition.value === "right");
304
364
  const hideable = (0, import_vue3.computed)(() => mergedOptions.value?.hideable ?? true);
305
365
  const hideAlignment = (0, import_vue3.computed)(() => mergedOptions.value?.hideAlignment ?? "center");
306
366
  const syncTheme = (0, import_vue3.computed)(() => mergedOptions.value?.syncTheme ?? false);
307
367
  const themeStyles = (0, import_vue3.ref)({});
308
368
  const tabs = (0, import_vue3.ref)([]);
309
- const activeTabId = (0, import_vue3.ref)(0);
310
369
  const activePaneId = (0, import_vue3.ref)(0);
311
370
  let nextTabId = 1;
312
371
  let nextPaneId = 1;
@@ -327,19 +386,120 @@ var CliPanel = (0, import_vue3.defineComponent)({
327
386
  };
328
387
  const panelResizing = (0, import_vue3.ref)(false);
329
388
  let panelResizeState = { startY: 0, startHeight: 0, startX: 0, startWidth: 0 };
330
- function addTab() {
331
- const paneId = nextPaneId++;
389
+ const statusExecutionState = (0, import_vue3.ref)("idle");
390
+ const statusLastCommand = (0, import_vue3.ref)(null);
391
+ const statusServiceCount = (0, import_vue3.ref)({ running: 0, total: 0 });
392
+ const statusServiceDetails = (0, import_vue3.ref)([]);
393
+ const statusServerState = (0, import_vue3.ref)("none");
394
+ const statusServerDetails = (0, import_vue3.ref)([]);
395
+ const statusUptime = (0, import_vue3.ref)(0);
396
+ const statusText = (0, import_vue3.ref)(null);
397
+ const servicesDropdownOpen = (0, import_vue3.ref)(false);
398
+ const serversDropdownOpen = (0, import_vue3.ref)(false);
399
+ const servicesDropdownTriggerRect = (0, import_vue3.ref)(null);
400
+ const serversDropdownTriggerRect = (0, import_vue3.ref)(null);
401
+ const formattedUptime = (0, import_vue3.computed)(() => {
402
+ const mins = Math.floor(statusUptime.value / 6e4);
403
+ if (mins < 60) return `${mins}m`;
404
+ const hrs = Math.floor(mins / 60);
405
+ return `${hrs}h${mins % 60}m`;
406
+ });
407
+ let execPollTimer;
408
+ let globalPollTimer;
409
+ function startStatusPolling() {
410
+ stopStatusPolling();
411
+ execPollTimer = setInterval(() => {
412
+ const tab = tabs.value.find((t) => t.id === resolvedActiveTabId.value);
413
+ if (!tab) return;
414
+ const engine = engineMap.get(tab.panes[0]?.id);
415
+ if (!engine) return;
416
+ const context = engine.getContext();
417
+ if (!context) return;
418
+ const running = !!context.isExecuting || !!context.contextProcessor;
419
+ statusExecutionState.value = running ? "running" : "idle";
420
+ const result = context.lastCommandResult;
421
+ statusLastCommand.value = result ? { name: result.command, success: result.success } : null;
422
+ const text = context.getStatusText?.();
423
+ statusText.value = text || null;
424
+ }, 300);
425
+ globalPollTimer = setInterval(() => {
426
+ const tab = tabs.value.find((t) => t.id === resolvedActiveTabId.value);
427
+ if (!tab) return;
428
+ const engine = engineMap.get(tab.panes[0]?.id);
429
+ if (!engine) return;
430
+ const context = engine.getContext();
431
+ if (!context) return;
432
+ try {
433
+ const services = context.backgroundServices?.list() ?? [];
434
+ statusServiceCount.value = { running: services.filter((s) => s.status === "running").length, total: services.length };
435
+ statusServiceDetails.value = services.map((s) => ({ name: s.name, status: s.status, description: s.description }));
436
+ } catch {
437
+ }
438
+ try {
439
+ const serverManager = context.services?.get?.("cli-server-manager");
440
+ const details = [];
441
+ if (serverManager?.connections?.size > 0) {
442
+ let anyConnected = false;
443
+ for (const [name, conn] of serverManager.connections) {
444
+ if (conn.connected) anyConnected = true;
445
+ details.push({
446
+ name,
447
+ url: conn.config?.url ?? "",
448
+ connected: !!conn.connected,
449
+ apiVersion: conn.connected ? conn.apiVersion : void 0,
450
+ commandCount: conn.connected ? conn.commands?.length : void 0
451
+ });
452
+ }
453
+ statusServerState.value = anyConnected ? "connected" : "disconnected";
454
+ } else {
455
+ const configuredServers = engine.options?.servers;
456
+ if (Array.isArray(configuredServers) && configuredServers.length > 0) {
457
+ for (const srv of configuredServers) {
458
+ if (srv.enabled === false) continue;
459
+ details.push({
460
+ name: srv.name,
461
+ url: srv.url ?? "",
462
+ connected: false
463
+ });
464
+ }
465
+ statusServerState.value = "disconnected";
466
+ } else {
467
+ statusServerState.value = "none";
468
+ }
469
+ }
470
+ statusServerDetails.value = details;
471
+ } catch {
472
+ }
473
+ statusUptime.value = engine.startedAt ? Date.now() - engine.startedAt : 0;
474
+ }, 2e3);
475
+ }
476
+ function stopStatusPolling() {
477
+ if (execPollTimer) {
478
+ clearInterval(execPollTimer);
479
+ execPollTimer = void 0;
480
+ }
481
+ if (globalPollTimer) {
482
+ clearInterval(globalPollTimer);
483
+ globalPollTimer = void 0;
484
+ }
485
+ }
486
+ (0, import_vue3.watch)(initialized, (val) => {
487
+ if (val) startStatusPolling();
488
+ else stopStatusPolling();
489
+ });
490
+ function addTab(title) {
332
491
  const tabId = nextTabId++;
333
- const pane = { id: paneId, widthPercent: 100 };
334
- const tab = {
492
+ const paneId = nextPaneId++;
493
+ tabs.value = [...tabs.value, {
335
494
  id: tabId,
336
- title: `Terminal ${tabId}`,
495
+ title: title ?? `Terminal ${tabId}`,
337
496
  isEditing: false,
338
- panes: [pane]
339
- };
340
- tabs.value.push(tab);
341
- activeTabId.value = tabId;
497
+ panes: [{ id: paneId, widthPercent: 100 }]
498
+ }];
499
+ updateActiveTabId(tabId);
342
500
  activePaneId.value = paneId;
501
+ emit("tab-added", { tabId });
502
+ return tabId;
343
503
  }
344
504
  let themeObserver;
345
505
  function syncThemeFromEngine() {
@@ -367,8 +527,9 @@ var CliPanel = (0, import_vue3.defineComponent)({
367
527
  document.removeEventListener("click", closePositionDropdown);
368
528
  });
369
529
  function toggle() {
370
- collapsed.value = !collapsed.value;
371
- if (!collapsed.value && !initialized.value) {
530
+ const newVal = !resolvedCollapsed.value;
531
+ updateCollapsed(newVal);
532
+ if (!newVal && !initialized.value) {
372
533
  initialized.value = true;
373
534
  addTab();
374
535
  setupThemeSync();
@@ -378,58 +539,65 @@ var CliPanel = (0, import_vue3.defineComponent)({
378
539
  const idx = tabs.value.findIndex((t) => t.id === id);
379
540
  if (idx === -1) return;
380
541
  tabs.value.splice(idx, 1);
542
+ emit("tab-closed", { tabId: id });
381
543
  if (tabs.value.length === 0) {
382
544
  initialized.value = false;
383
- collapsed.value = true;
545
+ updateCollapsed(true);
384
546
  return;
385
547
  }
386
- if (activeTabId.value === id) {
548
+ if (resolvedActiveTabId.value === id) {
387
549
  const newIdx = Math.min(idx, tabs.value.length - 1);
388
550
  selectTab(tabs.value[newIdx].id);
389
551
  }
390
552
  }
391
553
  function selectTab(id) {
392
- activeTabId.value = id;
554
+ updateActiveTabId(id);
393
555
  const tab = tabs.value.find((t) => t.id === id);
394
556
  if (tab && tab.panes.length > 0) {
395
557
  activePaneId.value = tab.panes[0].id;
396
558
  }
397
559
  }
398
- function splitRight(tabId) {
399
- const tab = tabs.value.find(
400
- (t) => t.id === (tabId ?? activeTabId.value)
401
- );
402
- if (!tab) return;
403
- const idx = tab.panes.findIndex((p) => p.id === activePaneId.value);
404
- const insertIdx = idx === -1 ? tab.panes.length : idx + 1;
405
- const newPaneId = nextPaneId++;
406
- tab.panes.splice(insertIdx, 0, { id: newPaneId, widthPercent: 0 });
407
- const evenWidth = 100 / tab.panes.length;
408
- tab.panes.forEach((p) => {
409
- p.widthPercent = evenWidth;
560
+ function splitPane(tabId) {
561
+ const targetTabId = tabId ?? resolvedActiveTabId.value;
562
+ const paneId = nextPaneId++;
563
+ tabs.value = tabs.value.map((t) => {
564
+ if (t.id !== targetTabId) return t;
565
+ const newPanes = [...t.panes, { id: paneId, widthPercent: 0 }];
566
+ const evenWidth = 100 / newPanes.length;
567
+ newPanes.forEach((p) => {
568
+ p.widthPercent = evenWidth;
569
+ });
570
+ normalizePanes(newPanes);
571
+ return { ...t, panes: newPanes };
410
572
  });
411
- normalizePanes(tab.panes);
412
- activePaneId.value = newPaneId;
573
+ activePaneId.value = paneId;
574
+ emit("pane-split", { paneId, tabId: targetTabId });
575
+ return paneId;
413
576
  }
414
- function closePane(tabId, paneId) {
415
- const tab = tabs.value.find((t) => t.id === tabId);
416
- if (!tab) return;
417
- if (tab.panes.length <= 1) {
418
- closeTab(tabId);
577
+ function closePane(paneId) {
578
+ for (const tab of tabs.value) {
579
+ const idx = tab.panes.findIndex((p) => p.id === paneId);
580
+ if (idx === -1) continue;
581
+ if (tab.panes.length <= 1) {
582
+ closeTab(tab.id);
583
+ return;
584
+ }
585
+ engineMap.get(paneId)?.destroy();
586
+ engineMap.delete(paneId);
587
+ const newPanes = tab.panes.filter((p) => p.id !== paneId);
588
+ normalizePanes(newPanes);
589
+ if (activePaneId.value === paneId) {
590
+ activePaneId.value = newPanes[Math.min(idx, newPanes.length - 1)].id;
591
+ }
592
+ emit("pane-closed", { paneId });
593
+ tabs.value = tabs.value.map(
594
+ (t) => t.id === tab.id ? { ...t, panes: newPanes } : t
595
+ );
419
596
  return;
420
597
  }
421
- const idx = tab.panes.findIndex((p) => p.id === paneId);
422
- if (idx === -1) return;
423
- tab.panes.splice(idx, 1);
424
- const total = tab.panes.reduce((s, p) => s + p.widthPercent, 0);
425
- if (total > 0)
426
- tab.panes.forEach((p) => {
427
- p.widthPercent = p.widthPercent / total * 100;
428
- });
429
- normalizePanes(tab.panes);
430
- if (activePaneId.value === paneId) {
431
- activePaneId.value = tab.panes[Math.min(idx, tab.panes.length - 1)].id;
432
- }
598
+ }
599
+ function renameTab(tabId, title) {
600
+ tabs.value = tabs.value.map((t) => t.id === tabId ? { ...t, title } : t);
433
601
  }
434
602
  function startRename(tabId) {
435
603
  tabs.value.forEach((t) => {
@@ -457,27 +625,27 @@ var CliPanel = (0, import_vue3.defineComponent)({
457
625
  function onPanelResizeStart(e) {
458
626
  if (!resizable.value) return;
459
627
  e.preventDefault();
460
- if (collapsed.value) toggle();
628
+ if (resolvedCollapsed.value) toggle();
461
629
  panelResizing.value = true;
462
630
  panelResizeState = {
463
631
  startY: e.clientY,
464
- startHeight: panelHeight.value,
632
+ startHeight: resolvedHeight.value,
465
633
  startX: e.clientX,
466
- startWidth: panelWidth.value
634
+ startWidth: resolvedWidth.value
467
635
  };
468
636
  }
469
637
  function onPanelResizeMove(e) {
470
638
  if (!panelResizing.value) return;
471
639
  if (isHorizontal.value) {
472
- const deltaX = position.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
640
+ const deltaX = resolvedPosition.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
473
641
  let next = Math.max(100, panelResizeState.startWidth + deltaX);
474
642
  if (next > window.innerWidth) next = window.innerWidth;
475
- panelWidth.value = next;
643
+ updateWidth(next);
476
644
  } else {
477
- const deltaY = position.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
645
+ const deltaY = resolvedPosition.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
478
646
  let next = Math.max(100, panelResizeState.startHeight + deltaY);
479
647
  if (next > window.innerHeight) next = window.innerHeight;
480
- panelHeight.value = next;
648
+ updateHeight(next);
481
649
  }
482
650
  }
483
651
  function onPanelResizeEnd() {
@@ -541,40 +709,44 @@ var CliPanel = (0, import_vue3.defineComponent)({
541
709
  (0, import_vue3.onBeforeUnmount)(() => {
542
710
  document.removeEventListener("mousemove", onPaneResizeMove);
543
711
  document.removeEventListener("mouseup", onPaneResizeEnd);
712
+ stopStatusPolling();
544
713
  });
545
714
  function toggleMaximize() {
546
- if (!maximized.value) {
715
+ if (!resolvedMaximized.value) {
547
716
  if (isHorizontal.value) {
548
- prevWidth.value = panelWidth.value;
549
- panelWidth.value = window.innerWidth;
717
+ prevWidth.value = resolvedWidth.value;
718
+ updateWidth(window.innerWidth);
550
719
  } else {
551
- prevHeight.value = panelHeight.value;
552
- panelHeight.value = window.innerHeight;
720
+ prevHeight.value = resolvedHeight.value;
721
+ updateHeight(window.innerHeight);
553
722
  }
554
723
  } else {
555
724
  if (isHorizontal.value) {
556
- panelWidth.value = prevWidth.value;
725
+ updateWidth(prevWidth.value);
557
726
  } else {
558
- panelHeight.value = prevHeight.value;
727
+ updateHeight(prevHeight.value);
559
728
  }
560
729
  }
561
- maximized.value = !maximized.value;
730
+ updateMaximized(!resolvedMaximized.value);
562
731
  }
563
732
  function handleClose() {
564
733
  visible.value = false;
734
+ emit("close");
565
735
  props.onClose?.();
566
736
  }
567
737
  function handleHide() {
568
- preHideCollapsed = collapsed.value;
569
- hidden.value = true;
738
+ preHideCollapsed = resolvedCollapsed.value;
739
+ updateHidden(true);
570
740
  }
571
741
  function handleUnhide() {
572
- hidden.value = false;
573
- collapsed.value = preHideCollapsed;
742
+ updateHidden(false);
743
+ updateCollapsed(preHideCollapsed);
574
744
  }
575
745
  const positionDropdownOpen = (0, import_vue3.ref)(false);
576
746
  function closePositionDropdown() {
577
747
  positionDropdownOpen.value = false;
748
+ servicesDropdownOpen.value = false;
749
+ serversDropdownOpen.value = false;
578
750
  }
579
751
  function togglePositionDropdown(e) {
580
752
  e.stopPropagation();
@@ -582,21 +754,106 @@ var CliPanel = (0, import_vue3.defineComponent)({
582
754
  }
583
755
  function selectPosition(pos) {
584
756
  positionDropdownOpen.value = false;
585
- position.value = pos;
586
- (0, import_cli_core.savePanelPosition)(pos);
757
+ updatePosition(pos);
587
758
  }
759
+ function getEngine(paneId) {
760
+ const targetId = paneId ?? activePaneId.value;
761
+ return engineMap.get(targetId);
762
+ }
763
+ function getState() {
764
+ return {
765
+ collapsed: resolvedCollapsed.value,
766
+ hidden: resolvedHidden.value,
767
+ maximized: resolvedMaximized.value,
768
+ position: resolvedPosition.value,
769
+ height: resolvedHeight.value,
770
+ width: resolvedWidth.value,
771
+ activeTabId: resolvedActiveTabId.value,
772
+ activePaneId: activePaneId.value,
773
+ tabs: tabs.value.map((t) => ({
774
+ id: t.id,
775
+ title: t.title,
776
+ panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent }))
777
+ }))
778
+ };
779
+ }
780
+ expose({
781
+ open: () => {
782
+ if (!visible.value) {
783
+ visible.value = true;
784
+ }
785
+ if (resolvedHidden.value) {
786
+ updateHidden(false);
787
+ }
788
+ if (resolvedCollapsed.value) {
789
+ updateCollapsed(false);
790
+ if (!initialized.value) {
791
+ initialized.value = true;
792
+ addTab();
793
+ setupThemeSync();
794
+ }
795
+ }
796
+ },
797
+ collapse: () => {
798
+ if (!resolvedCollapsed.value) updateCollapsed(true);
799
+ },
800
+ toggleCollapse: () => {
801
+ if (resolvedCollapsed.value) {
802
+ updateCollapsed(false);
803
+ if (!initialized.value) {
804
+ initialized.value = true;
805
+ addTab();
806
+ setupThemeSync();
807
+ }
808
+ } else {
809
+ updateCollapsed(true);
810
+ }
811
+ },
812
+ hide: () => {
813
+ if (!resolvedHidden.value) updateHidden(true);
814
+ },
815
+ unhide: () => {
816
+ if (resolvedHidden.value) updateHidden(false);
817
+ },
818
+ toggleHide: () => {
819
+ updateHidden(!resolvedHidden.value);
820
+ },
821
+ close: handleClose,
822
+ maximize: () => {
823
+ if (!resolvedMaximized.value) updateMaximized(true);
824
+ },
825
+ restore: () => {
826
+ if (resolvedMaximized.value) updateMaximized(false);
827
+ },
828
+ toggleMaximize: () => {
829
+ updateMaximized(!resolvedMaximized.value);
830
+ },
831
+ resize: (dims) => {
832
+ if (dims.height !== void 0) updateHeight(dims.height);
833
+ if (dims.width !== void 0) updateWidth(dims.width);
834
+ },
835
+ setPosition: updatePosition,
836
+ addTab,
837
+ closeTab,
838
+ selectTab,
839
+ renameTab,
840
+ splitPane,
841
+ closePane,
842
+ getEngine,
843
+ getState
844
+ });
588
845
  return () => {
589
846
  if (!visible.value) return null;
590
847
  const wrapperClass = [
591
848
  "cli-panel-wrapper",
592
- collapsed.value && "collapsed",
593
- maximized.value && "maximized",
849
+ resolvedCollapsed.value && "collapsed",
850
+ resolvedMaximized.value && "maximized",
594
851
  panelResizing.value && "resizing",
595
852
  props.class
596
853
  ].filter(Boolean).join(" ");
597
854
  const wrapperStyle = {
598
- ...isHorizontal.value ? { width: `${panelWidth.value}px` } : { height: `${panelHeight.value}px` },
599
- ...hidden.value ? { display: "none" } : {},
855
+ ...isHorizontal.value ? { width: `${resolvedWidth.value}px` } : { height: `${resolvedHeight.value}px` },
856
+ ...resolvedHidden.value ? { display: "none" } : {},
600
857
  ...themeStyles.value,
601
858
  ...props.style
602
859
  };
@@ -616,6 +873,54 @@ var CliPanel = (0, import_vue3.defineComponent)({
616
873
  ]),
617
874
  "CLI"
618
875
  ]),
876
+ // Status indicators (bottom/top only)
877
+ resolvedPosition.value === "bottom" || resolvedPosition.value === "top" ? (0, import_vue3.h)("div", { class: "cli-panel-status-indicators" }, [
878
+ // Execution state
879
+ (0, import_vue3.h)("span", { class: ["cli-panel-status-item", statusExecutionState.value === "running" ? "status-running" : ""].filter(Boolean).join(" ") }, [
880
+ (0, import_vue3.h)("span", { class: ["cli-panel-status-dot", statusExecutionState.value === "running" ? "dot-running" : "dot-idle"].join(" ") }),
881
+ (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, statusExecutionState.value)
882
+ ]),
883
+ // Background services
884
+ statusServiceCount.value.total > 0 ? (0, import_vue3.h)("span", {
885
+ class: "cli-panel-status-item status-clickable",
886
+ onClick: (e) => {
887
+ e.stopPropagation();
888
+ servicesDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
889
+ servicesDropdownOpen.value = !servicesDropdownOpen.value;
890
+ serversDropdownOpen.value = false;
891
+ }
892
+ }, [
893
+ (0, import_vue3.h)("span", { class: "cli-panel-status-icon", innerHTML: "&#9881;" }),
894
+ (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, `${statusServiceCount.value.running}/${statusServiceCount.value.total} services`)
895
+ ]) : null,
896
+ // Last command
897
+ statusLastCommand.value ? (0, import_vue3.h)("span", { class: "cli-panel-status-item" }, [
898
+ (0, import_vue3.h)("span", { class: ["cli-panel-status-icon", statusLastCommand.value.success ? "status-success" : "status-error"].join(" "), innerHTML: statusLastCommand.value.success ? "&#10003;" : "&#10005;" }),
899
+ (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, statusLastCommand.value.name)
900
+ ]) : null,
901
+ // Server connection
902
+ statusServerState.value !== "none" ? (0, import_vue3.h)("span", {
903
+ class: "cli-panel-status-item status-clickable",
904
+ onClick: (e) => {
905
+ e.stopPropagation();
906
+ serversDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
907
+ serversDropdownOpen.value = !serversDropdownOpen.value;
908
+ servicesDropdownOpen.value = false;
909
+ }
910
+ }, [
911
+ (0, import_vue3.h)("span", { class: ["cli-panel-status-dot", statusServerState.value === "connected" ? "dot-idle" : "dot-error"].join(" ") }),
912
+ (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, `${statusServerDetails.value.filter((s) => s.connected).length}/${statusServerDetails.value.length} servers`)
913
+ ]) : null,
914
+ // Uptime
915
+ statusUptime.value > 0 ? (0, import_vue3.h)("span", { class: "cli-panel-status-item status-muted" }, [
916
+ (0, import_vue3.h)("span", { class: "cli-panel-status-icon", innerHTML: "&uarr;" }),
917
+ (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, formattedUptime.value)
918
+ ]) : null,
919
+ // Custom status text
920
+ statusText.value ? (0, import_vue3.h)("span", { class: "cli-panel-status-item status-text" }, [
921
+ (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, statusText.value)
922
+ ]) : null
923
+ ].filter(Boolean)) : null,
619
924
  (0, import_vue3.h)("div", { class: "cli-panel-action-buttons" }, [
620
925
  (0, import_vue3.h)("div", {
621
926
  class: "cli-panel-btn-position-wrapper",
@@ -625,13 +930,13 @@ var CliPanel = (0, import_vue3.defineComponent)({
625
930
  class: "cli-panel-btn cli-panel-btn-position",
626
931
  title: "Move panel",
627
932
  onClick: togglePositionDropdown
628
- }, [positionIcon(position.value)]),
933
+ }, [positionIcon(resolvedPosition.value)]),
629
934
  positionDropdownOpen.value ? (0, import_vue3.h)(
630
935
  "div",
631
936
  { class: "cli-panel-position-dropdown" },
632
937
  ["bottom", "top", "left", "right"].map(
633
938
  (pos) => (0, import_vue3.h)("button", {
634
- class: ["cli-panel-position-dropdown-item", position.value === pos ? "active" : ""].filter(Boolean).join(" "),
939
+ class: ["cli-panel-position-dropdown-item", resolvedPosition.value === pos ? "active" : ""].filter(Boolean).join(" "),
635
940
  onClick: () => selectPosition(pos)
636
941
  }, [
637
942
  positionIcon(pos, 16),
@@ -649,20 +954,20 @@ var CliPanel = (0, import_vue3.defineComponent)({
649
954
  "button",
650
955
  {
651
956
  class: "cli-panel-btn",
652
- title: maximized.value ? "Restore" : "Maximize",
653
- disabled: collapsed.value,
957
+ title: resolvedMaximized.value ? "Restore" : "Maximize",
958
+ disabled: resolvedCollapsed.value,
654
959
  onClick: toggleMaximize
655
960
  },
656
- [maximized.value ? restoreIcon() : maximizeIcon()]
961
+ [resolvedMaximized.value ? restoreIcon() : maximizeIcon()]
657
962
  ),
658
963
  (0, import_vue3.h)(
659
964
  "button",
660
965
  {
661
966
  class: "cli-panel-btn",
662
- title: collapsed.value ? "Expand" : "Collapse",
967
+ title: resolvedCollapsed.value ? "Expand" : "Collapse",
663
968
  onClick: toggle
664
969
  },
665
- [collapseChevron(position.value, collapsed.value)]
970
+ [collapseChevron(resolvedPosition.value, resolvedCollapsed.value)]
666
971
  ),
667
972
  closable.value ? (0, import_vue3.h)(
668
973
  "button",
@@ -674,9 +979,10 @@ var CliPanel = (0, import_vue3.defineComponent)({
674
979
  [closeIcon()]
675
980
  ) : null
676
981
  ])
677
- ])
982
+ ]),
983
+ (0, import_vue3.h)("div", { class: ["cli-panel-glow-line", statusExecutionState.value === "running" ? "glow-active" : ""].filter(Boolean).join(" ") })
678
984
  ]);
679
- const contentEl = initialized.value ? (0, import_vue3.h)("div", { class: "cli-panel-content", style: collapsed.value ? { display: "none" } : void 0 }, [
985
+ const contentEl = initialized.value ? (0, import_vue3.h)("div", { class: "cli-panel-content", style: resolvedCollapsed.value ? { display: "none" } : void 0 }, [
680
986
  // Tab bar
681
987
  (0, import_vue3.h)("div", { class: "cli-panel-tabs" }, [
682
988
  (0, import_vue3.h)(
@@ -689,7 +995,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
689
995
  key: tab.id,
690
996
  class: [
691
997
  "cli-panel-tab",
692
- tab.id === activeTabId.value && "active"
998
+ tab.id === resolvedActiveTabId.value && "active"
693
999
  ].filter(Boolean).join(" "),
694
1000
  onClick: () => selectTab(tab.id),
695
1001
  onDblclick: () => startRename(tab.id),
@@ -727,6 +1033,12 @@ var CliPanel = (0, import_vue3.defineComponent)({
727
1033
  onDblclick: (e) => e.stopPropagation()
728
1034
  })
729
1035
  ] : [
1036
+ (0, import_vue3.h)("span", { class: ["cli-panel-tab-dot", (() => {
1037
+ if (tab.id !== resolvedActiveTabId.value) return "dot-idle";
1038
+ if (statusExecutionState.value === "running") return "dot-running";
1039
+ if (statusLastCommand.value && !statusLastCommand.value.success) return "dot-error";
1040
+ return "dot-idle";
1041
+ })()].join(" ") }),
730
1042
  (0, import_vue3.h)(
731
1043
  "span",
732
1044
  {
@@ -757,7 +1069,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
757
1069
  {
758
1070
  class: "cli-panel-add-tab",
759
1071
  title: "New terminal",
760
- onClick: addTab
1072
+ onClick: () => addTab()
761
1073
  },
762
1074
  "+"
763
1075
  )
@@ -773,7 +1085,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
773
1085
  key: tab.id,
774
1086
  class: "cli-panel-instance",
775
1087
  style: {
776
- display: tab.id === activeTabId.value ? void 0 : "none"
1088
+ display: tab.id === resolvedActiveTabId.value ? void 0 : "none"
777
1089
  }
778
1090
  },
779
1091
  [
@@ -813,7 +1125,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
813
1125
  {
814
1126
  class: [
815
1127
  "cli-panel-pane",
816
- pane.id === activePaneId.value && tab.id === activeTabId.value && "active-pane"
1128
+ pane.id === activePaneId.value && tab.id === resolvedActiveTabId.value && "active-pane"
817
1129
  ].filter(
818
1130
  Boolean
819
1131
  ).join(" "),
@@ -822,7 +1134,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
822
1134
  },
823
1135
  onClick: () => {
824
1136
  activePaneId.value = pane.id;
825
- activeTabId.value = tab.id;
1137
+ updateActiveTabId(tab.id);
826
1138
  }
827
1139
  },
828
1140
  [
@@ -834,7 +1146,6 @@ var CliPanel = (0, import_vue3.defineComponent)({
834
1146
  onClick: (e) => {
835
1147
  e.stopPropagation();
836
1148
  closePane(
837
- tab.id,
838
1149
  pane.id
839
1150
  );
840
1151
  }
@@ -910,7 +1221,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
910
1221
  { id: paneId, widthPercent: 100, snapshot }
911
1222
  ]
912
1223
  });
913
- activeTabId.value = tabId;
1224
+ updateActiveTabId(tabId);
914
1225
  activePaneId.value = paneId;
915
1226
  }
916
1227
  },
@@ -922,7 +1233,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
922
1233
  class: "cli-panel-context-menu-item",
923
1234
  onClick: () => {
924
1235
  closeContextMenu();
925
- splitRight(contextMenu.value.tabId);
1236
+ splitPane(contextMenu.value.tabId);
926
1237
  }
927
1238
  },
928
1239
  "Split Right"
@@ -952,7 +1263,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
952
1263
  tabs.value = tabs.value.filter(
953
1264
  (t) => t.id === id
954
1265
  );
955
- activeTabId.value = id;
1266
+ updateActiveTabId(id);
956
1267
  }
957
1268
  },
958
1269
  "Close Others"
@@ -987,16 +1298,16 @@ var CliPanel = (0, import_vue3.defineComponent)({
987
1298
  closeContextMenu();
988
1299
  tabs.value = [];
989
1300
  initialized.value = false;
990
- collapsed.value = true;
1301
+ updateCollapsed(true);
991
1302
  }
992
1303
  },
993
1304
  "Close All"
994
1305
  )
995
1306
  ]
996
1307
  ) : null;
997
- const hideTabEl = hidden.value ? (0, import_vue3.h)("button", {
1308
+ const hideTabEl = resolvedHidden.value ? (0, import_vue3.h)("button", {
998
1309
  class: "cli-panel-hide-tab",
999
- "data-position": position.value,
1310
+ "data-position": resolvedPosition.value,
1000
1311
  "data-hide-align": hideAlignment.value,
1001
1312
  style: themeStyles.value,
1002
1313
  title: "Show CLI",
@@ -1007,8 +1318,64 @@ var CliPanel = (0, import_vue3.defineComponent)({
1007
1318
  (0, import_vue3.h)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
1008
1319
  ]),
1009
1320
  (0, import_vue3.h)("span", { class: "cli-panel-hide-tab-label" }, "CLI"),
1010
- hideTabChevron(position.value)
1321
+ hideTabChevron(resolvedPosition.value)
1011
1322
  ]) : null;
1323
+ const servicesDropdownEl = servicesDropdownOpen.value && statusServiceCount.value.total > 0 ? (0, import_vue3.h)("div", {
1324
+ class: "cli-panel-services-dropdown",
1325
+ style: (() => {
1326
+ const rect = servicesDropdownTriggerRect.value;
1327
+ if (!rect) return {};
1328
+ return resolvedPosition.value === "top" ? { position: "fixed", top: `${rect.bottom + 4}px`, left: `${rect.left}px`, zIndex: 1100 } : { position: "fixed", bottom: `${window.innerHeight - rect.top + 4}px`, left: `${rect.left}px`, zIndex: 1100 };
1329
+ })(),
1330
+ onClick: (e) => e.stopPropagation()
1331
+ }, [
1332
+ (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-header" }, "Background Services"),
1333
+ (0, import_vue3.h)(
1334
+ "div",
1335
+ { class: "cli-panel-services-dropdown-list" },
1336
+ statusServiceDetails.value.map(
1337
+ (svc, i) => (0, import_vue3.h)("div", { key: i, class: "cli-panel-services-dropdown-item" }, [
1338
+ (0, import_vue3.h)("span", { class: ["cli-panel-svc-dot", svc.status === "running" ? "svc-running" : "svc-stopped"].join(" ") }),
1339
+ (0, import_vue3.h)("div", { class: "cli-panel-svc-info" }, [
1340
+ (0, import_vue3.h)("span", { class: "cli-panel-svc-name" }, svc.name),
1341
+ svc.description ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, svc.description) : null
1342
+ ].filter(Boolean)),
1343
+ (0, import_vue3.h)("span", { class: "cli-panel-svc-status" }, svc.status)
1344
+ ])
1345
+ )
1346
+ ),
1347
+ statusServiceDetails.value.length === 0 ? (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-empty" }, "No services registered") : null
1348
+ ].filter(Boolean)) : null;
1349
+ const serversDropdownEl = serversDropdownOpen.value && statusServerState.value !== "none" ? (0, import_vue3.h)("div", {
1350
+ class: "cli-panel-services-dropdown",
1351
+ style: (() => {
1352
+ const rect = serversDropdownTriggerRect.value;
1353
+ if (!rect) return {};
1354
+ return resolvedPosition.value === "top" ? { position: "fixed", top: `${rect.bottom + 4}px`, left: `${rect.left}px`, zIndex: 1100 } : { position: "fixed", bottom: `${window.innerHeight - rect.top + 4}px`, left: `${rect.left}px`, zIndex: 1100 };
1355
+ })(),
1356
+ onClick: (e) => e.stopPropagation()
1357
+ }, [
1358
+ (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-header" }, "Server Connections"),
1359
+ (0, import_vue3.h)(
1360
+ "div",
1361
+ { class: "cli-panel-services-dropdown-list" },
1362
+ statusServerDetails.value.map(
1363
+ (srv, i) => (0, import_vue3.h)("div", { key: i, class: "cli-panel-services-dropdown-item" }, [
1364
+ (0, import_vue3.h)("span", { class: ["cli-panel-svc-dot", srv.connected ? "svc-running" : "svc-stopped"].join(" ") }),
1365
+ (0, import_vue3.h)("div", { class: "cli-panel-svc-info" }, [
1366
+ (0, import_vue3.h)("span", { class: "cli-panel-svc-name" }, srv.name),
1367
+ srv.url ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, srv.url) : null
1368
+ ].filter(Boolean)),
1369
+ (0, import_vue3.h)("div", { class: "cli-panel-srv-meta" }, [
1370
+ (0, import_vue3.h)("span", { class: "cli-panel-svc-status" }, srv.connected ? "connected" : "disconnected"),
1371
+ srv.apiVersion ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, `v${srv.apiVersion}`) : null,
1372
+ srv.commandCount ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, `${srv.commandCount} cmds`) : null
1373
+ ].filter(Boolean))
1374
+ ])
1375
+ )
1376
+ ),
1377
+ statusServerDetails.value.length === 0 ? (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-empty" }, "No servers configured") : null
1378
+ ].filter(Boolean)) : null;
1012
1379
  return (0, import_vue3.h)("div", {}, [
1013
1380
  hideTabEl,
1014
1381
  (0, import_vue3.h)(
@@ -1016,14 +1383,16 @@ var CliPanel = (0, import_vue3.defineComponent)({
1016
1383
  {
1017
1384
  class: wrapperClass,
1018
1385
  style: wrapperStyle,
1019
- "data-position": position.value,
1386
+ "data-position": resolvedPosition.value,
1020
1387
  "data-resizable": String(resizable.value),
1021
1388
  "data-closable": String(closable.value),
1022
1389
  "data-hideable": String(hideable.value)
1023
1390
  },
1024
1391
  [headerEl, contentEl]
1025
1392
  ),
1026
- contextMenuEl
1393
+ contextMenuEl,
1394
+ servicesDropdownEl,
1395
+ serversDropdownEl
1027
1396
  ]);
1028
1397
  };
1029
1398
  }