@qodalis/react-cli 2.0.0-beta.11 → 2.0.0-beta.13

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 [notification, setNotification] = 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 notif = context.notifier?.current;
546
+ setNotification(notif || 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,97 @@ 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
+ notification && /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-item status-text level-${notification.level}`, children: /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-label", children: notification.message }) })
871
+ ] }),
872
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-status-indicators-compact", children: [
873
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item", title: statusExecutionState, children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusExecutionState === "running" ? "dot-running" : "dot-idle"}` }) }),
874
+ statusServiceCount.total > 0 && /* @__PURE__ */ jsx3(
875
+ "span",
876
+ {
877
+ ref: servicesDropdownRef,
878
+ className: "cli-panel-status-compact-item status-clickable",
879
+ title: `${statusServiceCount.running}/${statusServiceCount.total} services`,
880
+ onClick: (e) => {
881
+ e.stopPropagation();
882
+ setServicesDropdownOpen((prev) => !prev);
883
+ setServersDropdownOpen(false);
884
+ },
885
+ children: /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2699" })
886
+ }
887
+ ),
888
+ statusLastCommand && /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item", title: `${statusLastCommand.success ? "\u2713" : "\u2717"} ${statusLastCommand.name}`, children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-icon ${statusLastCommand.success ? "status-success" : "status-error"}`, children: statusLastCommand.success ? "\u2713" : "\u2717" }) }),
889
+ statusServerState !== "none" && /* @__PURE__ */ jsx3(
890
+ "span",
891
+ {
892
+ ref: serversDropdownRef,
893
+ className: "cli-panel-status-compact-item status-clickable",
894
+ title: `${statusServerDetails.filter((s) => s.connected).length}/${statusServerDetails.length} servers`,
895
+ onClick: (e) => {
896
+ e.stopPropagation();
897
+ setServersDropdownOpen((prev) => !prev);
898
+ setServicesDropdownOpen(false);
899
+ },
900
+ children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-dot ${statusServerState === "connected" ? "dot-idle" : "dot-error"}` })
901
+ }
902
+ ),
903
+ statusUptime > 0 && /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item status-muted", title: formattedUptime, children: /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-icon", children: "\u2191" }) }),
904
+ notification && /* @__PURE__ */ jsx3("span", { className: "cli-panel-status-compact-item", title: notification.message, children: /* @__PURE__ */ jsx3("span", { className: `cli-panel-status-compact-dot level-${notification.level}` }) })
905
+ ] }),
709
906
  /* @__PURE__ */ jsxs("div", { className: "cli-panel-action-buttons", children: [
710
907
  /* @__PURE__ */ jsxs("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
711
908
  /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-position", title: "Move panel", onClick: handlePositionButtonClick, children: /* @__PURE__ */ jsx3(PositionIcon, { position }) }),
@@ -727,7 +924,8 @@ var CliPanel = React3.forwardRef(
727
924
  /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn", title: collapsed ? "Expand" : "Collapse", onClick: toggle, children: /* @__PURE__ */ jsx3(CollapseChevron, { position, isCollapsed: collapsed }) }),
728
925
  closable && /* @__PURE__ */ jsx3("button", { className: "cli-panel-btn cli-panel-btn-close", title: "Close", onClick: handleClose, children: /* @__PURE__ */ jsx3(CloseIcon, {}) })
729
926
  ] })
730
- ] })
927
+ ] }),
928
+ /* @__PURE__ */ jsx3("div", { className: `cli-panel-glow-line${statusExecutionState === "running" ? " glow-active" : ""}` })
731
929
  ] }),
732
930
  initialized && /* @__PURE__ */ jsxs("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
733
931
  /* @__PURE__ */ jsxs("div", { className: "cli-panel-tabs", children: [
@@ -758,6 +956,12 @@ var CliPanel = React3.forwardRef(
758
956
  onDoubleClick: (e) => e.stopPropagation()
759
957
  }
760
958
  ) : /* @__PURE__ */ jsxs(Fragment2, { children: [
959
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-tab-dot ${(() => {
960
+ if (tab.id !== activeTabId) return "dot-idle";
961
+ if (statusExecutionState === "running") return "dot-running";
962
+ if (statusLastCommand && !statusLastCommand.success) return "dot-error";
963
+ return "dot-idle";
964
+ })()}` }),
761
965
  /* @__PURE__ */ jsx3("span", { className: "cli-panel-tab-title", children: tab.title }),
762
966
  /* @__PURE__ */ jsx3(
763
967
  "button",
@@ -883,6 +1087,86 @@ var CliPanel = React3.forwardRef(
883
1087
  }, children: "Close All" })
884
1088
  ]
885
1089
  }
1090
+ ),
1091
+ servicesDropdownOpen && statusServiceCount.total > 0 && /* @__PURE__ */ jsxs(
1092
+ "div",
1093
+ {
1094
+ className: "cli-panel-services-dropdown",
1095
+ style: (() => {
1096
+ const el = servicesDropdownRef.current;
1097
+ if (!el) return {};
1098
+ const rect = el.getBoundingClientRect();
1099
+ const base = { position: "fixed", zIndex: 1100 };
1100
+ switch (position) {
1101
+ case "top":
1102
+ return { ...base, top: rect.bottom + 4, left: rect.left };
1103
+ case "left":
1104
+ return { ...base, top: rect.top, left: rect.right + 4 };
1105
+ case "right":
1106
+ return { ...base, top: rect.top, right: window.innerWidth - rect.left + 4 };
1107
+ default:
1108
+ return { ...base, bottom: window.innerHeight - rect.top + 4, left: rect.left };
1109
+ }
1110
+ })(),
1111
+ onClick: (e) => e.stopPropagation(),
1112
+ children: [
1113
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-header", children: "Background Services" }),
1114
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-list", children: statusServiceDetails.map((svc, i) => /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown-item", children: [
1115
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-svc-dot ${svc.status === "running" ? "svc-running" : "svc-stopped"}` }),
1116
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-svc-info", children: [
1117
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-name", children: svc.name }),
1118
+ svc.description && /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: svc.description })
1119
+ ] }),
1120
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: svc.status })
1121
+ ] }, i)) }),
1122
+ statusServiceDetails.length === 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-empty", children: "No services registered" })
1123
+ ]
1124
+ }
1125
+ ),
1126
+ serversDropdownOpen && statusServerState !== "none" && /* @__PURE__ */ 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
+ const base = { position: "fixed", zIndex: 1100 };
1135
+ switch (position) {
1136
+ case "top":
1137
+ return { ...base, top: rect.bottom + 4, left: rect.left };
1138
+ case "left":
1139
+ return { ...base, top: rect.top, left: rect.right + 4 };
1140
+ case "right":
1141
+ return { ...base, top: rect.top, right: window.innerWidth - rect.left + 4 };
1142
+ default:
1143
+ return { ...base, bottom: window.innerHeight - rect.top + 4, left: rect.left };
1144
+ }
1145
+ })(),
1146
+ onClick: (e) => e.stopPropagation(),
1147
+ children: [
1148
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-header", children: "Server Connections" }),
1149
+ /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-list", children: statusServerDetails.map((srv, i) => /* @__PURE__ */ jsxs("div", { className: "cli-panel-services-dropdown-item", children: [
1150
+ /* @__PURE__ */ jsx3("span", { className: `cli-panel-svc-dot ${srv.connected ? "svc-running" : "svc-stopped"}` }),
1151
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-svc-info", children: [
1152
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-name", children: srv.name }),
1153
+ srv.url && /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-desc", children: srv.url })
1154
+ ] }),
1155
+ /* @__PURE__ */ jsxs("div", { className: "cli-panel-srv-meta", children: [
1156
+ /* @__PURE__ */ jsx3("span", { className: "cli-panel-svc-status", children: srv.connected ? "connected" : "disconnected" }),
1157
+ srv.apiVersion && /* @__PURE__ */ jsxs("span", { className: "cli-panel-svc-desc", children: [
1158
+ "v",
1159
+ srv.apiVersion
1160
+ ] }),
1161
+ srv.commandCount && /* @__PURE__ */ jsxs("span", { className: "cli-panel-svc-desc", children: [
1162
+ srv.commandCount,
1163
+ " cmds"
1164
+ ] })
1165
+ ] })
1166
+ ] }, i)) }),
1167
+ statusServerDetails.length === 0 && /* @__PURE__ */ jsx3("div", { className: "cli-panel-services-dropdown-empty", children: "No servers configured" })
1168
+ ]
1169
+ }
886
1170
  )
887
1171
  ] });
888
1172
  }