@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.mjs CHANGED
@@ -129,6 +129,7 @@ import {
129
129
  ref as ref2,
130
130
  computed,
131
131
  inject as inject2,
132
+ watch,
132
133
  h as h2,
133
134
  onMounted as onMounted2,
134
135
  onBeforeUnmount as onBeforeUnmount2
@@ -256,38 +257,97 @@ var CliPanel = defineComponent3({
256
257
  type: Function,
257
258
  default: void 0
258
259
  },
260
+ collapsed: { type: Boolean, default: void 0 },
261
+ hidden: { type: Boolean, default: void 0 },
262
+ maximized: { type: Boolean, default: void 0 },
263
+ activeTabId: { type: Number, default: void 0 },
264
+ position: { type: String, default: void 0 },
265
+ height: { type: Number, default: void 0 },
266
+ width: { type: Number, default: void 0 },
259
267
  style: {
260
268
  type: Object,
261
269
  default: void 0
262
270
  },
263
271
  class: { type: String, default: void 0 }
264
272
  },
265
- setup(props) {
273
+ emits: [
274
+ "update:collapsed",
275
+ "update:hidden",
276
+ "update:maximized",
277
+ "update:activeTabId",
278
+ "update:position",
279
+ "update:height",
280
+ "update:width",
281
+ "close",
282
+ "tab-added",
283
+ "tab-closed",
284
+ "pane-split",
285
+ "pane-closed"
286
+ ],
287
+ setup(props, { expose, emit }) {
266
288
  const config = inject2(CliConfigKey, null);
267
- const mergedOptions = computed(() => props.options ?? config?.options);
289
+ const mergedOptions = computed(() => props.options ? { ...config?.options, ...props.options } : config?.options);
268
290
  const mergedModules = computed(() => props.modules ?? config?.modules);
269
291
  const mergedProcessors = computed(() => props.processors ?? config?.processors);
270
292
  const mergedServices = computed(() => props.services ?? config?.services);
271
293
  const visible = ref2(true);
272
- const collapsed = ref2(mergedOptions.value?.isCollapsed ?? true);
273
- const maximized = ref2(false);
274
- const panelHeight = ref2(600);
275
- const panelWidth = ref2(400);
294
+ const internalCollapsed = ref2(mergedOptions.value?.isCollapsed ?? true);
295
+ const internalMaximized = ref2(false);
296
+ const internalHeight = ref2(600);
297
+ const internalWidth = ref2(400);
276
298
  const prevHeight = ref2(600);
277
299
  const prevWidth = ref2(400);
278
300
  const initialized = ref2(false);
279
- const hidden = ref2(props.options?.isHidden ?? false);
301
+ const internalHidden = ref2(props.options?.isHidden ?? false);
280
302
  let preHideCollapsed = true;
281
- const position = ref2(loadPanelPosition() ?? mergedOptions.value?.position ?? "bottom");
303
+ const internalActiveTabId = ref2(0);
304
+ const internalPosition = ref2(
305
+ loadPanelPosition() ?? mergedOptions.value?.position ?? "bottom"
306
+ );
307
+ const resolvedCollapsed = computed(() => props.collapsed !== void 0 ? props.collapsed : internalCollapsed.value);
308
+ const resolvedHidden = computed(() => props.hidden !== void 0 ? props.hidden : internalHidden.value);
309
+ const resolvedMaximized = computed(() => props.maximized !== void 0 ? props.maximized : internalMaximized.value);
310
+ const resolvedActiveTabId = computed(() => props.activeTabId !== void 0 ? props.activeTabId : internalActiveTabId.value);
311
+ const resolvedHeight = computed(() => props.height !== void 0 ? props.height : internalHeight.value);
312
+ const resolvedWidth = computed(() => props.width !== void 0 ? props.width : internalWidth.value);
313
+ const resolvedPosition = computed(() => props.position !== void 0 ? props.position : internalPosition.value);
314
+ function updateCollapsed(value) {
315
+ internalCollapsed.value = value;
316
+ emit("update:collapsed", value);
317
+ }
318
+ function updateHidden(value) {
319
+ internalHidden.value = value;
320
+ emit("update:hidden", value);
321
+ }
322
+ function updateMaximized(value) {
323
+ internalMaximized.value = value;
324
+ emit("update:maximized", value);
325
+ }
326
+ function updateActiveTabId(value) {
327
+ internalActiveTabId.value = value;
328
+ emit("update:activeTabId", value);
329
+ }
330
+ function updateHeight(value) {
331
+ internalHeight.value = value;
332
+ emit("update:height", value);
333
+ }
334
+ function updateWidth(value) {
335
+ internalWidth.value = value;
336
+ emit("update:width", value);
337
+ }
338
+ function updatePosition(value) {
339
+ internalPosition.value = value;
340
+ savePanelPosition(value);
341
+ emit("update:position", value);
342
+ }
282
343
  const closable = computed(() => mergedOptions.value?.closable ?? true);
283
344
  const resizable = computed(() => mergedOptions.value?.resizable ?? true);
284
- const isHorizontal = computed(() => position.value === "left" || position.value === "right");
345
+ const isHorizontal = computed(() => resolvedPosition.value === "left" || resolvedPosition.value === "right");
285
346
  const hideable = computed(() => mergedOptions.value?.hideable ?? true);
286
347
  const hideAlignment = computed(() => mergedOptions.value?.hideAlignment ?? "center");
287
348
  const syncTheme = computed(() => mergedOptions.value?.syncTheme ?? false);
288
349
  const themeStyles = ref2({});
289
350
  const tabs = ref2([]);
290
- const activeTabId = ref2(0);
291
351
  const activePaneId = ref2(0);
292
352
  let nextTabId = 1;
293
353
  let nextPaneId = 1;
@@ -308,19 +368,120 @@ var CliPanel = defineComponent3({
308
368
  };
309
369
  const panelResizing = ref2(false);
310
370
  let panelResizeState = { startY: 0, startHeight: 0, startX: 0, startWidth: 0 };
311
- function addTab() {
312
- const paneId = nextPaneId++;
371
+ const statusExecutionState = ref2("idle");
372
+ const statusLastCommand = ref2(null);
373
+ const statusServiceCount = ref2({ running: 0, total: 0 });
374
+ const statusServiceDetails = ref2([]);
375
+ const statusServerState = ref2("none");
376
+ const statusServerDetails = ref2([]);
377
+ const statusUptime = ref2(0);
378
+ const statusText = ref2(null);
379
+ const servicesDropdownOpen = ref2(false);
380
+ const serversDropdownOpen = ref2(false);
381
+ const servicesDropdownTriggerRect = ref2(null);
382
+ const serversDropdownTriggerRect = ref2(null);
383
+ const formattedUptime = computed(() => {
384
+ const mins = Math.floor(statusUptime.value / 6e4);
385
+ if (mins < 60) return `${mins}m`;
386
+ const hrs = Math.floor(mins / 60);
387
+ return `${hrs}h${mins % 60}m`;
388
+ });
389
+ let execPollTimer;
390
+ let globalPollTimer;
391
+ function startStatusPolling() {
392
+ stopStatusPolling();
393
+ execPollTimer = setInterval(() => {
394
+ const tab = tabs.value.find((t) => t.id === resolvedActiveTabId.value);
395
+ if (!tab) return;
396
+ const engine = engineMap.get(tab.panes[0]?.id);
397
+ if (!engine) return;
398
+ const context = engine.getContext();
399
+ if (!context) return;
400
+ const running = !!context.isExecuting || !!context.contextProcessor;
401
+ statusExecutionState.value = running ? "running" : "idle";
402
+ const result = context.lastCommandResult;
403
+ statusLastCommand.value = result ? { name: result.command, success: result.success } : null;
404
+ const text = context.getStatusText?.();
405
+ statusText.value = text || null;
406
+ }, 300);
407
+ globalPollTimer = setInterval(() => {
408
+ const tab = tabs.value.find((t) => t.id === resolvedActiveTabId.value);
409
+ if (!tab) return;
410
+ const engine = engineMap.get(tab.panes[0]?.id);
411
+ if (!engine) return;
412
+ const context = engine.getContext();
413
+ if (!context) return;
414
+ try {
415
+ const services = context.backgroundServices?.list() ?? [];
416
+ statusServiceCount.value = { running: services.filter((s) => s.status === "running").length, total: services.length };
417
+ statusServiceDetails.value = services.map((s) => ({ name: s.name, status: s.status, description: s.description }));
418
+ } catch {
419
+ }
420
+ try {
421
+ const serverManager = context.services?.get?.("cli-server-manager");
422
+ const details = [];
423
+ if (serverManager?.connections?.size > 0) {
424
+ let anyConnected = false;
425
+ for (const [name, conn] of serverManager.connections) {
426
+ if (conn.connected) anyConnected = true;
427
+ details.push({
428
+ name,
429
+ url: conn.config?.url ?? "",
430
+ connected: !!conn.connected,
431
+ apiVersion: conn.connected ? conn.apiVersion : void 0,
432
+ commandCount: conn.connected ? conn.commands?.length : void 0
433
+ });
434
+ }
435
+ statusServerState.value = anyConnected ? "connected" : "disconnected";
436
+ } else {
437
+ const configuredServers = engine.options?.servers;
438
+ if (Array.isArray(configuredServers) && configuredServers.length > 0) {
439
+ for (const srv of configuredServers) {
440
+ if (srv.enabled === false) continue;
441
+ details.push({
442
+ name: srv.name,
443
+ url: srv.url ?? "",
444
+ connected: false
445
+ });
446
+ }
447
+ statusServerState.value = "disconnected";
448
+ } else {
449
+ statusServerState.value = "none";
450
+ }
451
+ }
452
+ statusServerDetails.value = details;
453
+ } catch {
454
+ }
455
+ statusUptime.value = engine.startedAt ? Date.now() - engine.startedAt : 0;
456
+ }, 2e3);
457
+ }
458
+ function stopStatusPolling() {
459
+ if (execPollTimer) {
460
+ clearInterval(execPollTimer);
461
+ execPollTimer = void 0;
462
+ }
463
+ if (globalPollTimer) {
464
+ clearInterval(globalPollTimer);
465
+ globalPollTimer = void 0;
466
+ }
467
+ }
468
+ watch(initialized, (val) => {
469
+ if (val) startStatusPolling();
470
+ else stopStatusPolling();
471
+ });
472
+ function addTab(title) {
313
473
  const tabId = nextTabId++;
314
- const pane = { id: paneId, widthPercent: 100 };
315
- const tab = {
474
+ const paneId = nextPaneId++;
475
+ tabs.value = [...tabs.value, {
316
476
  id: tabId,
317
- title: `Terminal ${tabId}`,
477
+ title: title ?? `Terminal ${tabId}`,
318
478
  isEditing: false,
319
- panes: [pane]
320
- };
321
- tabs.value.push(tab);
322
- activeTabId.value = tabId;
479
+ panes: [{ id: paneId, widthPercent: 100 }]
480
+ }];
481
+ updateActiveTabId(tabId);
323
482
  activePaneId.value = paneId;
483
+ emit("tab-added", { tabId });
484
+ return tabId;
324
485
  }
325
486
  let themeObserver;
326
487
  function syncThemeFromEngine() {
@@ -348,8 +509,9 @@ var CliPanel = defineComponent3({
348
509
  document.removeEventListener("click", closePositionDropdown);
349
510
  });
350
511
  function toggle() {
351
- collapsed.value = !collapsed.value;
352
- if (!collapsed.value && !initialized.value) {
512
+ const newVal = !resolvedCollapsed.value;
513
+ updateCollapsed(newVal);
514
+ if (!newVal && !initialized.value) {
353
515
  initialized.value = true;
354
516
  addTab();
355
517
  setupThemeSync();
@@ -359,58 +521,65 @@ var CliPanel = defineComponent3({
359
521
  const idx = tabs.value.findIndex((t) => t.id === id);
360
522
  if (idx === -1) return;
361
523
  tabs.value.splice(idx, 1);
524
+ emit("tab-closed", { tabId: id });
362
525
  if (tabs.value.length === 0) {
363
526
  initialized.value = false;
364
- collapsed.value = true;
527
+ updateCollapsed(true);
365
528
  return;
366
529
  }
367
- if (activeTabId.value === id) {
530
+ if (resolvedActiveTabId.value === id) {
368
531
  const newIdx = Math.min(idx, tabs.value.length - 1);
369
532
  selectTab(tabs.value[newIdx].id);
370
533
  }
371
534
  }
372
535
  function selectTab(id) {
373
- activeTabId.value = id;
536
+ updateActiveTabId(id);
374
537
  const tab = tabs.value.find((t) => t.id === id);
375
538
  if (tab && tab.panes.length > 0) {
376
539
  activePaneId.value = tab.panes[0].id;
377
540
  }
378
541
  }
379
- function splitRight(tabId) {
380
- const tab = tabs.value.find(
381
- (t) => t.id === (tabId ?? activeTabId.value)
382
- );
383
- if (!tab) return;
384
- const idx = tab.panes.findIndex((p) => p.id === activePaneId.value);
385
- const insertIdx = idx === -1 ? tab.panes.length : idx + 1;
386
- const newPaneId = nextPaneId++;
387
- tab.panes.splice(insertIdx, 0, { id: newPaneId, widthPercent: 0 });
388
- const evenWidth = 100 / tab.panes.length;
389
- tab.panes.forEach((p) => {
390
- p.widthPercent = evenWidth;
542
+ function splitPane(tabId) {
543
+ const targetTabId = tabId ?? resolvedActiveTabId.value;
544
+ const paneId = nextPaneId++;
545
+ tabs.value = tabs.value.map((t) => {
546
+ if (t.id !== targetTabId) return t;
547
+ const newPanes = [...t.panes, { id: paneId, widthPercent: 0 }];
548
+ const evenWidth = 100 / newPanes.length;
549
+ newPanes.forEach((p) => {
550
+ p.widthPercent = evenWidth;
551
+ });
552
+ normalizePanes(newPanes);
553
+ return { ...t, panes: newPanes };
391
554
  });
392
- normalizePanes(tab.panes);
393
- activePaneId.value = newPaneId;
555
+ activePaneId.value = paneId;
556
+ emit("pane-split", { paneId, tabId: targetTabId });
557
+ return paneId;
394
558
  }
395
- function closePane(tabId, paneId) {
396
- const tab = tabs.value.find((t) => t.id === tabId);
397
- if (!tab) return;
398
- if (tab.panes.length <= 1) {
399
- closeTab(tabId);
559
+ function closePane(paneId) {
560
+ for (const tab of tabs.value) {
561
+ const idx = tab.panes.findIndex((p) => p.id === paneId);
562
+ if (idx === -1) continue;
563
+ if (tab.panes.length <= 1) {
564
+ closeTab(tab.id);
565
+ return;
566
+ }
567
+ engineMap.get(paneId)?.destroy();
568
+ engineMap.delete(paneId);
569
+ const newPanes = tab.panes.filter((p) => p.id !== paneId);
570
+ normalizePanes(newPanes);
571
+ if (activePaneId.value === paneId) {
572
+ activePaneId.value = newPanes[Math.min(idx, newPanes.length - 1)].id;
573
+ }
574
+ emit("pane-closed", { paneId });
575
+ tabs.value = tabs.value.map(
576
+ (t) => t.id === tab.id ? { ...t, panes: newPanes } : t
577
+ );
400
578
  return;
401
579
  }
402
- const idx = tab.panes.findIndex((p) => p.id === paneId);
403
- if (idx === -1) return;
404
- tab.panes.splice(idx, 1);
405
- const total = tab.panes.reduce((s, p) => s + p.widthPercent, 0);
406
- if (total > 0)
407
- tab.panes.forEach((p) => {
408
- p.widthPercent = p.widthPercent / total * 100;
409
- });
410
- normalizePanes(tab.panes);
411
- if (activePaneId.value === paneId) {
412
- activePaneId.value = tab.panes[Math.min(idx, tab.panes.length - 1)].id;
413
- }
580
+ }
581
+ function renameTab(tabId, title) {
582
+ tabs.value = tabs.value.map((t) => t.id === tabId ? { ...t, title } : t);
414
583
  }
415
584
  function startRename(tabId) {
416
585
  tabs.value.forEach((t) => {
@@ -438,27 +607,27 @@ var CliPanel = defineComponent3({
438
607
  function onPanelResizeStart(e) {
439
608
  if (!resizable.value) return;
440
609
  e.preventDefault();
441
- if (collapsed.value) toggle();
610
+ if (resolvedCollapsed.value) toggle();
442
611
  panelResizing.value = true;
443
612
  panelResizeState = {
444
613
  startY: e.clientY,
445
- startHeight: panelHeight.value,
614
+ startHeight: resolvedHeight.value,
446
615
  startX: e.clientX,
447
- startWidth: panelWidth.value
616
+ startWidth: resolvedWidth.value
448
617
  };
449
618
  }
450
619
  function onPanelResizeMove(e) {
451
620
  if (!panelResizing.value) return;
452
621
  if (isHorizontal.value) {
453
- const deltaX = position.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
622
+ const deltaX = resolvedPosition.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
454
623
  let next = Math.max(100, panelResizeState.startWidth + deltaX);
455
624
  if (next > window.innerWidth) next = window.innerWidth;
456
- panelWidth.value = next;
625
+ updateWidth(next);
457
626
  } else {
458
- const deltaY = position.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
627
+ const deltaY = resolvedPosition.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
459
628
  let next = Math.max(100, panelResizeState.startHeight + deltaY);
460
629
  if (next > window.innerHeight) next = window.innerHeight;
461
- panelHeight.value = next;
630
+ updateHeight(next);
462
631
  }
463
632
  }
464
633
  function onPanelResizeEnd() {
@@ -522,40 +691,44 @@ var CliPanel = defineComponent3({
522
691
  onBeforeUnmount2(() => {
523
692
  document.removeEventListener("mousemove", onPaneResizeMove);
524
693
  document.removeEventListener("mouseup", onPaneResizeEnd);
694
+ stopStatusPolling();
525
695
  });
526
696
  function toggleMaximize() {
527
- if (!maximized.value) {
697
+ if (!resolvedMaximized.value) {
528
698
  if (isHorizontal.value) {
529
- prevWidth.value = panelWidth.value;
530
- panelWidth.value = window.innerWidth;
699
+ prevWidth.value = resolvedWidth.value;
700
+ updateWidth(window.innerWidth);
531
701
  } else {
532
- prevHeight.value = panelHeight.value;
533
- panelHeight.value = window.innerHeight;
702
+ prevHeight.value = resolvedHeight.value;
703
+ updateHeight(window.innerHeight);
534
704
  }
535
705
  } else {
536
706
  if (isHorizontal.value) {
537
- panelWidth.value = prevWidth.value;
707
+ updateWidth(prevWidth.value);
538
708
  } else {
539
- panelHeight.value = prevHeight.value;
709
+ updateHeight(prevHeight.value);
540
710
  }
541
711
  }
542
- maximized.value = !maximized.value;
712
+ updateMaximized(!resolvedMaximized.value);
543
713
  }
544
714
  function handleClose() {
545
715
  visible.value = false;
716
+ emit("close");
546
717
  props.onClose?.();
547
718
  }
548
719
  function handleHide() {
549
- preHideCollapsed = collapsed.value;
550
- hidden.value = true;
720
+ preHideCollapsed = resolvedCollapsed.value;
721
+ updateHidden(true);
551
722
  }
552
723
  function handleUnhide() {
553
- hidden.value = false;
554
- collapsed.value = preHideCollapsed;
724
+ updateHidden(false);
725
+ updateCollapsed(preHideCollapsed);
555
726
  }
556
727
  const positionDropdownOpen = ref2(false);
557
728
  function closePositionDropdown() {
558
729
  positionDropdownOpen.value = false;
730
+ servicesDropdownOpen.value = false;
731
+ serversDropdownOpen.value = false;
559
732
  }
560
733
  function togglePositionDropdown(e) {
561
734
  e.stopPropagation();
@@ -563,21 +736,106 @@ var CliPanel = defineComponent3({
563
736
  }
564
737
  function selectPosition(pos) {
565
738
  positionDropdownOpen.value = false;
566
- position.value = pos;
567
- savePanelPosition(pos);
739
+ updatePosition(pos);
568
740
  }
741
+ function getEngine(paneId) {
742
+ const targetId = paneId ?? activePaneId.value;
743
+ return engineMap.get(targetId);
744
+ }
745
+ function getState() {
746
+ return {
747
+ collapsed: resolvedCollapsed.value,
748
+ hidden: resolvedHidden.value,
749
+ maximized: resolvedMaximized.value,
750
+ position: resolvedPosition.value,
751
+ height: resolvedHeight.value,
752
+ width: resolvedWidth.value,
753
+ activeTabId: resolvedActiveTabId.value,
754
+ activePaneId: activePaneId.value,
755
+ tabs: tabs.value.map((t) => ({
756
+ id: t.id,
757
+ title: t.title,
758
+ panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent }))
759
+ }))
760
+ };
761
+ }
762
+ expose({
763
+ open: () => {
764
+ if (!visible.value) {
765
+ visible.value = true;
766
+ }
767
+ if (resolvedHidden.value) {
768
+ updateHidden(false);
769
+ }
770
+ if (resolvedCollapsed.value) {
771
+ updateCollapsed(false);
772
+ if (!initialized.value) {
773
+ initialized.value = true;
774
+ addTab();
775
+ setupThemeSync();
776
+ }
777
+ }
778
+ },
779
+ collapse: () => {
780
+ if (!resolvedCollapsed.value) updateCollapsed(true);
781
+ },
782
+ toggleCollapse: () => {
783
+ if (resolvedCollapsed.value) {
784
+ updateCollapsed(false);
785
+ if (!initialized.value) {
786
+ initialized.value = true;
787
+ addTab();
788
+ setupThemeSync();
789
+ }
790
+ } else {
791
+ updateCollapsed(true);
792
+ }
793
+ },
794
+ hide: () => {
795
+ if (!resolvedHidden.value) updateHidden(true);
796
+ },
797
+ unhide: () => {
798
+ if (resolvedHidden.value) updateHidden(false);
799
+ },
800
+ toggleHide: () => {
801
+ updateHidden(!resolvedHidden.value);
802
+ },
803
+ close: handleClose,
804
+ maximize: () => {
805
+ if (!resolvedMaximized.value) updateMaximized(true);
806
+ },
807
+ restore: () => {
808
+ if (resolvedMaximized.value) updateMaximized(false);
809
+ },
810
+ toggleMaximize: () => {
811
+ updateMaximized(!resolvedMaximized.value);
812
+ },
813
+ resize: (dims) => {
814
+ if (dims.height !== void 0) updateHeight(dims.height);
815
+ if (dims.width !== void 0) updateWidth(dims.width);
816
+ },
817
+ setPosition: updatePosition,
818
+ addTab,
819
+ closeTab,
820
+ selectTab,
821
+ renameTab,
822
+ splitPane,
823
+ closePane,
824
+ getEngine,
825
+ getState
826
+ });
569
827
  return () => {
570
828
  if (!visible.value) return null;
571
829
  const wrapperClass = [
572
830
  "cli-panel-wrapper",
573
- collapsed.value && "collapsed",
574
- maximized.value && "maximized",
831
+ resolvedCollapsed.value && "collapsed",
832
+ resolvedMaximized.value && "maximized",
575
833
  panelResizing.value && "resizing",
576
834
  props.class
577
835
  ].filter(Boolean).join(" ");
578
836
  const wrapperStyle = {
579
- ...isHorizontal.value ? { width: `${panelWidth.value}px` } : { height: `${panelHeight.value}px` },
580
- ...hidden.value ? { display: "none" } : {},
837
+ ...isHorizontal.value ? { width: `${resolvedWidth.value}px` } : { height: `${resolvedHeight.value}px` },
838
+ ...resolvedHidden.value ? { display: "none" } : {},
581
839
  ...themeStyles.value,
582
840
  ...props.style
583
841
  };
@@ -597,6 +855,54 @@ var CliPanel = defineComponent3({
597
855
  ]),
598
856
  "CLI"
599
857
  ]),
858
+ // Status indicators (bottom/top only)
859
+ resolvedPosition.value === "bottom" || resolvedPosition.value === "top" ? h2("div", { class: "cli-panel-status-indicators" }, [
860
+ // Execution state
861
+ h2("span", { class: ["cli-panel-status-item", statusExecutionState.value === "running" ? "status-running" : ""].filter(Boolean).join(" ") }, [
862
+ h2("span", { class: ["cli-panel-status-dot", statusExecutionState.value === "running" ? "dot-running" : "dot-idle"].join(" ") }),
863
+ h2("span", { class: "cli-panel-status-label" }, statusExecutionState.value)
864
+ ]),
865
+ // Background services
866
+ statusServiceCount.value.total > 0 ? h2("span", {
867
+ class: "cli-panel-status-item status-clickable",
868
+ onClick: (e) => {
869
+ e.stopPropagation();
870
+ servicesDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
871
+ servicesDropdownOpen.value = !servicesDropdownOpen.value;
872
+ serversDropdownOpen.value = false;
873
+ }
874
+ }, [
875
+ h2("span", { class: "cli-panel-status-icon", innerHTML: "&#9881;" }),
876
+ h2("span", { class: "cli-panel-status-label" }, `${statusServiceCount.value.running}/${statusServiceCount.value.total} services`)
877
+ ]) : null,
878
+ // Last command
879
+ statusLastCommand.value ? h2("span", { class: "cli-panel-status-item" }, [
880
+ h2("span", { class: ["cli-panel-status-icon", statusLastCommand.value.success ? "status-success" : "status-error"].join(" "), innerHTML: statusLastCommand.value.success ? "&#10003;" : "&#10005;" }),
881
+ h2("span", { class: "cli-panel-status-label" }, statusLastCommand.value.name)
882
+ ]) : null,
883
+ // Server connection
884
+ statusServerState.value !== "none" ? h2("span", {
885
+ class: "cli-panel-status-item status-clickable",
886
+ onClick: (e) => {
887
+ e.stopPropagation();
888
+ serversDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
889
+ serversDropdownOpen.value = !serversDropdownOpen.value;
890
+ servicesDropdownOpen.value = false;
891
+ }
892
+ }, [
893
+ h2("span", { class: ["cli-panel-status-dot", statusServerState.value === "connected" ? "dot-idle" : "dot-error"].join(" ") }),
894
+ h2("span", { class: "cli-panel-status-label" }, `${statusServerDetails.value.filter((s) => s.connected).length}/${statusServerDetails.value.length} servers`)
895
+ ]) : null,
896
+ // Uptime
897
+ statusUptime.value > 0 ? h2("span", { class: "cli-panel-status-item status-muted" }, [
898
+ h2("span", { class: "cli-panel-status-icon", innerHTML: "&uarr;" }),
899
+ h2("span", { class: "cli-panel-status-label" }, formattedUptime.value)
900
+ ]) : null,
901
+ // Custom status text
902
+ statusText.value ? h2("span", { class: "cli-panel-status-item status-text" }, [
903
+ h2("span", { class: "cli-panel-status-label" }, statusText.value)
904
+ ]) : null
905
+ ].filter(Boolean)) : null,
600
906
  h2("div", { class: "cli-panel-action-buttons" }, [
601
907
  h2("div", {
602
908
  class: "cli-panel-btn-position-wrapper",
@@ -606,13 +912,13 @@ var CliPanel = defineComponent3({
606
912
  class: "cli-panel-btn cli-panel-btn-position",
607
913
  title: "Move panel",
608
914
  onClick: togglePositionDropdown
609
- }, [positionIcon(position.value)]),
915
+ }, [positionIcon(resolvedPosition.value)]),
610
916
  positionDropdownOpen.value ? h2(
611
917
  "div",
612
918
  { class: "cli-panel-position-dropdown" },
613
919
  ["bottom", "top", "left", "right"].map(
614
920
  (pos) => h2("button", {
615
- class: ["cli-panel-position-dropdown-item", position.value === pos ? "active" : ""].filter(Boolean).join(" "),
921
+ class: ["cli-panel-position-dropdown-item", resolvedPosition.value === pos ? "active" : ""].filter(Boolean).join(" "),
616
922
  onClick: () => selectPosition(pos)
617
923
  }, [
618
924
  positionIcon(pos, 16),
@@ -630,20 +936,20 @@ var CliPanel = defineComponent3({
630
936
  "button",
631
937
  {
632
938
  class: "cli-panel-btn",
633
- title: maximized.value ? "Restore" : "Maximize",
634
- disabled: collapsed.value,
939
+ title: resolvedMaximized.value ? "Restore" : "Maximize",
940
+ disabled: resolvedCollapsed.value,
635
941
  onClick: toggleMaximize
636
942
  },
637
- [maximized.value ? restoreIcon() : maximizeIcon()]
943
+ [resolvedMaximized.value ? restoreIcon() : maximizeIcon()]
638
944
  ),
639
945
  h2(
640
946
  "button",
641
947
  {
642
948
  class: "cli-panel-btn",
643
- title: collapsed.value ? "Expand" : "Collapse",
949
+ title: resolvedCollapsed.value ? "Expand" : "Collapse",
644
950
  onClick: toggle
645
951
  },
646
- [collapseChevron(position.value, collapsed.value)]
952
+ [collapseChevron(resolvedPosition.value, resolvedCollapsed.value)]
647
953
  ),
648
954
  closable.value ? h2(
649
955
  "button",
@@ -655,9 +961,10 @@ var CliPanel = defineComponent3({
655
961
  [closeIcon()]
656
962
  ) : null
657
963
  ])
658
- ])
964
+ ]),
965
+ h2("div", { class: ["cli-panel-glow-line", statusExecutionState.value === "running" ? "glow-active" : ""].filter(Boolean).join(" ") })
659
966
  ]);
660
- const contentEl = initialized.value ? h2("div", { class: "cli-panel-content", style: collapsed.value ? { display: "none" } : void 0 }, [
967
+ const contentEl = initialized.value ? h2("div", { class: "cli-panel-content", style: resolvedCollapsed.value ? { display: "none" } : void 0 }, [
661
968
  // Tab bar
662
969
  h2("div", { class: "cli-panel-tabs" }, [
663
970
  h2(
@@ -670,7 +977,7 @@ var CliPanel = defineComponent3({
670
977
  key: tab.id,
671
978
  class: [
672
979
  "cli-panel-tab",
673
- tab.id === activeTabId.value && "active"
980
+ tab.id === resolvedActiveTabId.value && "active"
674
981
  ].filter(Boolean).join(" "),
675
982
  onClick: () => selectTab(tab.id),
676
983
  onDblclick: () => startRename(tab.id),
@@ -708,6 +1015,12 @@ var CliPanel = defineComponent3({
708
1015
  onDblclick: (e) => e.stopPropagation()
709
1016
  })
710
1017
  ] : [
1018
+ h2("span", { class: ["cli-panel-tab-dot", (() => {
1019
+ if (tab.id !== resolvedActiveTabId.value) return "dot-idle";
1020
+ if (statusExecutionState.value === "running") return "dot-running";
1021
+ if (statusLastCommand.value && !statusLastCommand.value.success) return "dot-error";
1022
+ return "dot-idle";
1023
+ })()].join(" ") }),
711
1024
  h2(
712
1025
  "span",
713
1026
  {
@@ -738,7 +1051,7 @@ var CliPanel = defineComponent3({
738
1051
  {
739
1052
  class: "cli-panel-add-tab",
740
1053
  title: "New terminal",
741
- onClick: addTab
1054
+ onClick: () => addTab()
742
1055
  },
743
1056
  "+"
744
1057
  )
@@ -754,7 +1067,7 @@ var CliPanel = defineComponent3({
754
1067
  key: tab.id,
755
1068
  class: "cli-panel-instance",
756
1069
  style: {
757
- display: tab.id === activeTabId.value ? void 0 : "none"
1070
+ display: tab.id === resolvedActiveTabId.value ? void 0 : "none"
758
1071
  }
759
1072
  },
760
1073
  [
@@ -794,7 +1107,7 @@ var CliPanel = defineComponent3({
794
1107
  {
795
1108
  class: [
796
1109
  "cli-panel-pane",
797
- pane.id === activePaneId.value && tab.id === activeTabId.value && "active-pane"
1110
+ pane.id === activePaneId.value && tab.id === resolvedActiveTabId.value && "active-pane"
798
1111
  ].filter(
799
1112
  Boolean
800
1113
  ).join(" "),
@@ -803,7 +1116,7 @@ var CliPanel = defineComponent3({
803
1116
  },
804
1117
  onClick: () => {
805
1118
  activePaneId.value = pane.id;
806
- activeTabId.value = tab.id;
1119
+ updateActiveTabId(tab.id);
807
1120
  }
808
1121
  },
809
1122
  [
@@ -815,7 +1128,6 @@ var CliPanel = defineComponent3({
815
1128
  onClick: (e) => {
816
1129
  e.stopPropagation();
817
1130
  closePane(
818
- tab.id,
819
1131
  pane.id
820
1132
  );
821
1133
  }
@@ -891,7 +1203,7 @@ var CliPanel = defineComponent3({
891
1203
  { id: paneId, widthPercent: 100, snapshot }
892
1204
  ]
893
1205
  });
894
- activeTabId.value = tabId;
1206
+ updateActiveTabId(tabId);
895
1207
  activePaneId.value = paneId;
896
1208
  }
897
1209
  },
@@ -903,7 +1215,7 @@ var CliPanel = defineComponent3({
903
1215
  class: "cli-panel-context-menu-item",
904
1216
  onClick: () => {
905
1217
  closeContextMenu();
906
- splitRight(contextMenu.value.tabId);
1218
+ splitPane(contextMenu.value.tabId);
907
1219
  }
908
1220
  },
909
1221
  "Split Right"
@@ -933,7 +1245,7 @@ var CliPanel = defineComponent3({
933
1245
  tabs.value = tabs.value.filter(
934
1246
  (t) => t.id === id
935
1247
  );
936
- activeTabId.value = id;
1248
+ updateActiveTabId(id);
937
1249
  }
938
1250
  },
939
1251
  "Close Others"
@@ -968,16 +1280,16 @@ var CliPanel = defineComponent3({
968
1280
  closeContextMenu();
969
1281
  tabs.value = [];
970
1282
  initialized.value = false;
971
- collapsed.value = true;
1283
+ updateCollapsed(true);
972
1284
  }
973
1285
  },
974
1286
  "Close All"
975
1287
  )
976
1288
  ]
977
1289
  ) : null;
978
- const hideTabEl = hidden.value ? h2("button", {
1290
+ const hideTabEl = resolvedHidden.value ? h2("button", {
979
1291
  class: "cli-panel-hide-tab",
980
- "data-position": position.value,
1292
+ "data-position": resolvedPosition.value,
981
1293
  "data-hide-align": hideAlignment.value,
982
1294
  style: themeStyles.value,
983
1295
  title: "Show CLI",
@@ -988,8 +1300,64 @@ var CliPanel = defineComponent3({
988
1300
  h2("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
989
1301
  ]),
990
1302
  h2("span", { class: "cli-panel-hide-tab-label" }, "CLI"),
991
- hideTabChevron(position.value)
1303
+ hideTabChevron(resolvedPosition.value)
992
1304
  ]) : null;
1305
+ const servicesDropdownEl = servicesDropdownOpen.value && statusServiceCount.value.total > 0 ? h2("div", {
1306
+ class: "cli-panel-services-dropdown",
1307
+ style: (() => {
1308
+ const rect = servicesDropdownTriggerRect.value;
1309
+ if (!rect) return {};
1310
+ 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 };
1311
+ })(),
1312
+ onClick: (e) => e.stopPropagation()
1313
+ }, [
1314
+ h2("div", { class: "cli-panel-services-dropdown-header" }, "Background Services"),
1315
+ h2(
1316
+ "div",
1317
+ { class: "cli-panel-services-dropdown-list" },
1318
+ statusServiceDetails.value.map(
1319
+ (svc, i) => h2("div", { key: i, class: "cli-panel-services-dropdown-item" }, [
1320
+ h2("span", { class: ["cli-panel-svc-dot", svc.status === "running" ? "svc-running" : "svc-stopped"].join(" ") }),
1321
+ h2("div", { class: "cli-panel-svc-info" }, [
1322
+ h2("span", { class: "cli-panel-svc-name" }, svc.name),
1323
+ svc.description ? h2("span", { class: "cli-panel-svc-desc" }, svc.description) : null
1324
+ ].filter(Boolean)),
1325
+ h2("span", { class: "cli-panel-svc-status" }, svc.status)
1326
+ ])
1327
+ )
1328
+ ),
1329
+ statusServiceDetails.value.length === 0 ? h2("div", { class: "cli-panel-services-dropdown-empty" }, "No services registered") : null
1330
+ ].filter(Boolean)) : null;
1331
+ const serversDropdownEl = serversDropdownOpen.value && statusServerState.value !== "none" ? h2("div", {
1332
+ class: "cli-panel-services-dropdown",
1333
+ style: (() => {
1334
+ const rect = serversDropdownTriggerRect.value;
1335
+ if (!rect) return {};
1336
+ 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 };
1337
+ })(),
1338
+ onClick: (e) => e.stopPropagation()
1339
+ }, [
1340
+ h2("div", { class: "cli-panel-services-dropdown-header" }, "Server Connections"),
1341
+ h2(
1342
+ "div",
1343
+ { class: "cli-panel-services-dropdown-list" },
1344
+ statusServerDetails.value.map(
1345
+ (srv, i) => h2("div", { key: i, class: "cli-panel-services-dropdown-item" }, [
1346
+ h2("span", { class: ["cli-panel-svc-dot", srv.connected ? "svc-running" : "svc-stopped"].join(" ") }),
1347
+ h2("div", { class: "cli-panel-svc-info" }, [
1348
+ h2("span", { class: "cli-panel-svc-name" }, srv.name),
1349
+ srv.url ? h2("span", { class: "cli-panel-svc-desc" }, srv.url) : null
1350
+ ].filter(Boolean)),
1351
+ h2("div", { class: "cli-panel-srv-meta" }, [
1352
+ h2("span", { class: "cli-panel-svc-status" }, srv.connected ? "connected" : "disconnected"),
1353
+ srv.apiVersion ? h2("span", { class: "cli-panel-svc-desc" }, `v${srv.apiVersion}`) : null,
1354
+ srv.commandCount ? h2("span", { class: "cli-panel-svc-desc" }, `${srv.commandCount} cmds`) : null
1355
+ ].filter(Boolean))
1356
+ ])
1357
+ )
1358
+ ),
1359
+ statusServerDetails.value.length === 0 ? h2("div", { class: "cli-panel-services-dropdown-empty" }, "No servers configured") : null
1360
+ ].filter(Boolean)) : null;
993
1361
  return h2("div", {}, [
994
1362
  hideTabEl,
995
1363
  h2(
@@ -997,14 +1365,16 @@ var CliPanel = defineComponent3({
997
1365
  {
998
1366
  class: wrapperClass,
999
1367
  style: wrapperStyle,
1000
- "data-position": position.value,
1368
+ "data-position": resolvedPosition.value,
1001
1369
  "data-resizable": String(resizable.value),
1002
1370
  "data-closable": String(closable.value),
1003
1371
  "data-hideable": String(hideable.value)
1004
1372
  },
1005
1373
  [headerEl, contentEl]
1006
1374
  ),
1007
- contextMenuEl
1375
+ contextMenuEl,
1376
+ servicesDropdownEl,
1377
+ serversDropdownEl
1008
1378
  ]);
1009
1379
  };
1010
1380
  }