@qodalis/react-cli 2.0.0-beta.11 → 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
@@ -207,7 +207,7 @@ var CliPanel = React3.forwardRef(
207
207
  function CliPanel2(props, ref) {
208
208
  const { options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className } = props;
209
209
  const config = useCliConfig();
210
- const options = optionsProp ?? config.options;
210
+ const options = optionsProp ? { ...config.options, ...optionsProp } : config.options;
211
211
  const modules = modulesProp ?? config.modules;
212
212
  const processors = processorsProp ?? config.processors;
213
213
  const services = servicesProp ?? config.services;
@@ -282,6 +282,18 @@ var CliPanel = React3.forwardRef(
282
282
  const panelResizeRef = useRef3({ startY: 0, startHeight: 0, startX: 0, startWidth: 0 });
283
283
  const prevHeightRef = useRef3(600);
284
284
  const prevWidthRef = useRef3(400);
285
+ const [statusExecutionState, setStatusExecutionState] = useState2("idle");
286
+ const [statusLastCommand, setStatusLastCommand] = useState2(null);
287
+ const [statusServiceCount, setStatusServiceCount] = useState2({ running: 0, total: 0 });
288
+ const [statusServiceDetails, setStatusServiceDetails] = useState2([]);
289
+ const [statusServerState, setStatusServerState] = useState2("none");
290
+ const [statusServerDetails, setStatusServerDetails] = useState2([]);
291
+ const [statusUptime, setStatusUptime] = useState2(0);
292
+ const [statusText, setStatusText] = useState2(null);
293
+ const [servicesDropdownOpen, setServicesDropdownOpen] = useState2(false);
294
+ const [serversDropdownOpen, setServersDropdownOpen] = useState2(false);
295
+ const servicesDropdownRef = useRef3(null);
296
+ const serversDropdownRef = useRef3(null);
285
297
  const normalizePanes = useCallback((panes) => {
286
298
  const total = panes.reduce((s, p) => s + p.widthPercent, 0);
287
299
  if (total === 0) return;
@@ -364,6 +376,10 @@ var CliPanel = React3.forwardRef(
364
376
  setTabs((prev) => prev.map((t) => {
365
377
  if (t.id !== targetTabId) return t;
366
378
  const newPanes = [...t.panes.map((p) => ({ ...p })), { id: paneId, widthPercent: 0 }];
379
+ const evenWidth = 100 / newPanes.length;
380
+ newPanes.forEach((p) => {
381
+ p.widthPercent = evenWidth;
382
+ });
367
383
  normalizePanes(newPanes);
368
384
  return { ...t, panes: newPanes };
369
385
  }));
@@ -513,6 +529,81 @@ var CliPanel = React3.forwardRef(
513
529
  }, 200);
514
530
  return () => clearTimeout(timer);
515
531
  }, [syncTheme, initialized]);
532
+ useEffect3(() => {
533
+ if (!initialized) return;
534
+ const pollExec = setInterval(() => {
535
+ const tab = tabs.find((t) => t.id === activeTabIdRef.current);
536
+ if (!tab) return;
537
+ const engine = engineMapRef.current.get(tab.panes[0]?.id);
538
+ if (!engine) return;
539
+ const context = engine.getContext();
540
+ if (!context) return;
541
+ const running = !!context.isExecuting || !!context.contextProcessor;
542
+ setStatusExecutionState(running ? "running" : "idle");
543
+ const result = context.lastCommandResult;
544
+ setStatusLastCommand(result ? { name: result.command, success: result.success } : null);
545
+ const text = context.getStatusText?.();
546
+ setStatusText(text || null);
547
+ }, 300);
548
+ const pollGlobal = setInterval(() => {
549
+ const tab = tabs.find((t) => t.id === activeTabIdRef.current);
550
+ if (!tab) return;
551
+ const engine = engineMapRef.current.get(tab.panes[0]?.id);
552
+ if (!engine) return;
553
+ const context = engine.getContext();
554
+ if (!context) return;
555
+ try {
556
+ const services2 = context.backgroundServices?.list() ?? [];
557
+ setStatusServiceCount({ running: services2.filter((s) => s.status === "running").length, total: services2.length });
558
+ setStatusServiceDetails(services2.map((s) => ({ name: s.name, status: s.status, description: s.description })));
559
+ } catch {
560
+ }
561
+ try {
562
+ let serverManager;
563
+ try {
564
+ serverManager = context.services?.get?.("cli-server-manager");
565
+ } catch {
566
+ }
567
+ const details = [];
568
+ if (serverManager?.connections?.size > 0) {
569
+ let anyConnected = false;
570
+ for (const [name, conn] of serverManager.connections) {
571
+ if (conn.connected) anyConnected = true;
572
+ details.push({
573
+ name,
574
+ url: conn.config?.url ?? "",
575
+ connected: !!conn.connected,
576
+ apiVersion: conn.connected ? conn.apiVersion : void 0,
577
+ commandCount: conn.connected ? conn.commands?.length : void 0
578
+ });
579
+ }
580
+ setStatusServerState(anyConnected ? "connected" : "disconnected");
581
+ } else {
582
+ const configuredServers = engine.options?.servers;
583
+ if (Array.isArray(configuredServers) && configuredServers.length > 0) {
584
+ for (const srv of configuredServers) {
585
+ if (srv.enabled === false) continue;
586
+ details.push({
587
+ name: srv.name,
588
+ url: srv.url ?? "",
589
+ connected: false
590
+ });
591
+ }
592
+ setStatusServerState("disconnected");
593
+ } else {
594
+ setStatusServerState("none");
595
+ }
596
+ }
597
+ setStatusServerDetails(details);
598
+ } catch {
599
+ }
600
+ setStatusUptime(engine.startedAt ? Date.now() - engine.startedAt : 0);
601
+ }, 2e3);
602
+ return () => {
603
+ clearInterval(pollExec);
604
+ clearInterval(pollGlobal);
605
+ };
606
+ }, [initialized, tabs]);
516
607
  const toggleMaximize = useCallback(() => {
517
608
  const nextMaximized = !maximized;
518
609
  if (nextMaximized) {
@@ -551,6 +642,15 @@ var CliPanel = React3.forwardRef(
551
642
  document.addEventListener("click", closePositionDropdown);
552
643
  return () => document.removeEventListener("click", closePositionDropdown);
553
644
  }, [positionDropdownOpen, closePositionDropdown]);
645
+ useEffect3(() => {
646
+ if (!servicesDropdownOpen && !serversDropdownOpen) return;
647
+ const handler = () => {
648
+ setServicesDropdownOpen(false);
649
+ setServersDropdownOpen(false);
650
+ };
651
+ document.addEventListener("click", handler);
652
+ return () => document.removeEventListener("click", handler);
653
+ }, [servicesDropdownOpen, serversDropdownOpen]);
554
654
  const handlePositionButtonClick = useCallback((e) => {
555
655
  e.stopPropagation();
556
656
  setPositionDropdownOpen((prev) => !prev);
@@ -661,6 +761,12 @@ var CliPanel = React3.forwardRef(
661
761
  getEngine,
662
762
  getState
663
763
  ]);
764
+ const formattedUptime = (() => {
765
+ const mins = Math.floor(statusUptime / 6e4);
766
+ if (mins < 60) return `${mins}m`;
767
+ const hrs = Math.floor(mins / 60);
768
+ return `${hrs}h${mins % 60}m`;
769
+ })();
664
770
  if (!visible) return null;
665
771
  const wrapperStyle = {
666
772
  ...isHorizontal ? { width: `${panelWidth}px` } : { height: `${panelHeight}px` },
@@ -706,6 +812,63 @@ var CliPanel = React3.forwardRef(
706
812
  /* @__PURE__ */ jsx3("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ jsx3(TerminalIcon, {}) }),
707
813
  "CLI"
708
814
  ] }),
815
+ (position === "bottom" || position === "top") && /* @__PURE__ */ jsxs("div", { className: "cli-panel-status-indicators", children: [
816
+ /* @__PURE__ */ jsxs("span", { className: `cli-panel-status-item${statusExecutionState === "running" ? " status-running" : ""}`, children: [
817
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusExecutionState === "running" ? "dot-running" : "dot-idle"}` }),
818
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: statusExecutionState })
819
+ ] }),
820
+ statusServiceCount.total > 0 && /* @__PURE__ */ jsxs(
821
+ "span",
822
+ {
823
+ ref: servicesDropdownRef,
824
+ className: "cli-panel-status-item status-clickable",
825
+ onClick: (e) => {
826
+ e.stopPropagation();
827
+ setServicesDropdownOpen((prev) => !prev);
828
+ setServersDropdownOpen(false);
829
+ },
830
+ children: [
831
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2699" }),
832
+ /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-label", children: [
833
+ statusServiceCount.running,
834
+ "/",
835
+ statusServiceCount.total,
836
+ " services"
837
+ ] })
838
+ ]
839
+ }
840
+ ),
841
+ statusLastCommand && /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-item", children: [
842
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-icon ${statusLastCommand.success ? "status-success" : "status-error"}`, children: statusLastCommand.success ? "\u2713" : "\u2717" }),
843
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: statusLastCommand.name })
844
+ ] }),
845
+ statusServerState !== "none" && /* @__PURE__ */ jsxs(
846
+ "span",
847
+ {
848
+ ref: serversDropdownRef,
849
+ className: "cli-panel-status-item status-clickable",
850
+ onClick: (e) => {
851
+ e.stopPropagation();
852
+ setServersDropdownOpen((prev) => !prev);
853
+ setServicesDropdownOpen(false);
854
+ },
855
+ children: [
856
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusServerState === "connected" ? "dot-idle" : "dot-error"}` }),
857
+ /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-label", children: [
858
+ statusServerDetails.filter((s) => s.connected).length,
859
+ "/",
860
+ statusServerDetails.length,
861
+ " servers"
862
+ ] })
863
+ ]
864
+ }
865
+ ),
866
+ statusUptime > 0 && /* @__PURE__ */ jsxs("span", { className: "cli-panel-status-item status-muted", children: [
867
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2191" }),
868
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: formattedUptime })
869
+ ] }),
870
+ statusText && /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-item status-text", children: /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: statusText }) })
871
+ ] }),
709
872
  /* @__PURE__ */ jsxs("div", { className: "cli-panel-action-buttons", children: [
710
873
  /* @__PURE__ */ jsxs("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
711
874
  /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-position", title: "Move panel", onClick: handlePositionButtonClick, children: /* @__PURE__ */ jsx3(PositionIcon, { position }) }),
@@ -727,7 +890,8 @@ var CliPanel = React3.forwardRef(
727
890
  /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn", title: collapsed ? "Expand" : "Collapse", onClick: toggle, children: /* @__PURE__ */ jsx3(CollapseChevron, { position, isCollapsed: collapsed }) }),
728
891
  closable && /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-close", title: "Close", onClick: handleClose, children: /* @__PURE__ */ jsx3(CloseIcon, {}) })
729
892
  ] })
730
- ] })
893
+ ] }),
894
+ /* @__PURE__ */ jsx3("div", { className: `cli-panel-glow-line${statusExecutionState === "running" ? " glow-active" : ""}` })
731
895
  ] }),
732
896
  initialized && /* @__PURE__ */ jsxs("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
733
897
  /* @__PURE__ */ jsxs("div", { className: "cli-panel-tabs", children: [
@@ -758,6 +922,12 @@ var CliPanel = React3.forwardRef(
758
922
  onDoubleClick: (e) => e.stopPropagation()
759
923
  }
760
924
  ) : /* @__PURE__ */ jsxs(Fragment2, { children: [
925
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-tab-dot ${(() => {
926
+ if (tab.id !== activeTabId) return "dot-idle";
927
+ if (statusExecutionState === "running") return "dot-running";
928
+ if (statusLastCommand && !statusLastCommand.success) return "dot-error";
929
+ return "dot-idle";
930
+ })()}` }),
761
931
  /* @__PURE__ */ jsx3("span", { className: "cli-panel-tab-title", children: tab.title }),
762
932
  /* @__PURE__ */ jsx3(
763
933
  "button",
@@ -883,6 +1053,66 @@ var CliPanel = React3.forwardRef(
883
1053
  }, children: "Close All" })
884
1054
  ]
885
1055
  }
1056
+ ),
1057
+ servicesDropdownOpen && statusServiceCount.total > 0 && /* @__PURE__ */ jsxs(
1058
+ "div",
1059
+ {
1060
+ className: "cli-panel-services-dropdown",
1061
+ style: (() => {
1062
+ const el = servicesDropdownRef.current;
1063
+ if (!el) return {};
1064
+ const rect = el.getBoundingClientRect();
1065
+ 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 };
1066
+ })(),
1067
+ onClick: (e) => e.stopPropagation(),
1068
+ children: [
1069
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-header", children: "Background Services" }),
1070
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-list", children: statusServiceDetails.map((svc, i) => /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown-item", children: [
1071
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-svc-dot ${svc.status === "running" ? "svc-running" : "svc-stopped"}` }),
1072
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-svc-info", children: [
1073
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-name", children: svc.name }),
1074
+ svc.description && /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: svc.description })
1075
+ ] }),
1076
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: svc.status })
1077
+ ] }, i)) }),
1078
+ statusServiceDetails.length === 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-empty", children: "No services registered" })
1079
+ ]
1080
+ }
1081
+ ),
1082
+ serversDropdownOpen && statusServerState !== "none" && /* @__PURE__ */ jsxs(
1083
+ "div",
1084
+ {
1085
+ className: "cli-panel-services-dropdown",
1086
+ style: (() => {
1087
+ const el = serversDropdownRef.current;
1088
+ if (!el) return {};
1089
+ const rect = el.getBoundingClientRect();
1090
+ 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 };
1091
+ })(),
1092
+ onClick: (e) => e.stopPropagation(),
1093
+ children: [
1094
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-header", children: "Server Connections" }),
1095
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-list", children: statusServerDetails.map((srv, i) => /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown-item", children: [
1096
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-svc-dot ${srv.connected ? "svc-running" : "svc-stopped"}` }),
1097
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-svc-info", children: [
1098
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-name", children: srv.name }),
1099
+ srv.url && /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: srv.url })
1100
+ ] }),
1101
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-srv-meta", children: [
1102
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: srv.connected ? "connected" : "disconnected" }),
1103
+ srv.apiVersion && /* @__PURE__ */ jsxs("span", { className: "cli-panel-svc-desc", children: [
1104
+ "v",
1105
+ srv.apiVersion
1106
+ ] }),
1107
+ srv.commandCount && /* @__PURE__ */ jsxs("span", { className: "cli-panel-svc-desc", children: [
1108
+ srv.commandCount,
1109
+ " cmds"
1110
+ ] })
1111
+ ] })
1112
+ ] }, i)) }),
1113
+ statusServerDetails.length === 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-empty", children: "No servers configured" })
1114
+ ]
1115
+ }
886
1116
  )
887
1117
  ] });
888
1118
  }