@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.js CHANGED
@@ -251,7 +251,7 @@ var CliPanel = import_react5.default.forwardRef(
251
251
  function CliPanel2(props, ref) {
252
252
  const { options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className } = props;
253
253
  const config = useCliConfig();
254
- const options = optionsProp ?? config.options;
254
+ const options = optionsProp ? { ...config.options, ...optionsProp } : config.options;
255
255
  const modules = modulesProp ?? config.modules;
256
256
  const processors = processorsProp ?? config.processors;
257
257
  const services = servicesProp ?? config.services;
@@ -326,6 +326,18 @@ var CliPanel = import_react5.default.forwardRef(
326
326
  const panelResizeRef = (0, import_react5.useRef)({ startY: 0, startHeight: 0, startX: 0, startWidth: 0 });
327
327
  const prevHeightRef = (0, import_react5.useRef)(600);
328
328
  const prevWidthRef = (0, import_react5.useRef)(400);
329
+ const [statusExecutionState, setStatusExecutionState] = (0, import_react5.useState)("idle");
330
+ const [statusLastCommand, setStatusLastCommand] = (0, import_react5.useState)(null);
331
+ const [statusServiceCount, setStatusServiceCount] = (0, import_react5.useState)({ running: 0, total: 0 });
332
+ const [statusServiceDetails, setStatusServiceDetails] = (0, import_react5.useState)([]);
333
+ const [statusServerState, setStatusServerState] = (0, import_react5.useState)("none");
334
+ const [statusServerDetails, setStatusServerDetails] = (0, import_react5.useState)([]);
335
+ const [statusUptime, setStatusUptime] = (0, import_react5.useState)(0);
336
+ const [notification, setNotification] = (0, import_react5.useState)(null);
337
+ const [servicesDropdownOpen, setServicesDropdownOpen] = (0, import_react5.useState)(false);
338
+ const [serversDropdownOpen, setServersDropdownOpen] = (0, import_react5.useState)(false);
339
+ const servicesDropdownRef = (0, import_react5.useRef)(null);
340
+ const serversDropdownRef = (0, import_react5.useRef)(null);
329
341
  const normalizePanes = (0, import_react5.useCallback)((panes) => {
330
342
  const total = panes.reduce((s, p) => s + p.widthPercent, 0);
331
343
  if (total === 0) return;
@@ -408,6 +420,10 @@ var CliPanel = import_react5.default.forwardRef(
408
420
  setTabs((prev) => prev.map((t) => {
409
421
  if (t.id !== targetTabId) return t;
410
422
  const newPanes = [...t.panes.map((p) => ({ ...p })), { id: paneId, widthPercent: 0 }];
423
+ const evenWidth = 100 / newPanes.length;
424
+ newPanes.forEach((p) => {
425
+ p.widthPercent = evenWidth;
426
+ });
411
427
  normalizePanes(newPanes);
412
428
  return { ...t, panes: newPanes };
413
429
  }));
@@ -557,6 +573,81 @@ var CliPanel = import_react5.default.forwardRef(
557
573
  }, 200);
558
574
  return () => clearTimeout(timer);
559
575
  }, [syncTheme, initialized]);
576
+ (0, import_react5.useEffect)(() => {
577
+ if (!initialized) return;
578
+ const pollExec = setInterval(() => {
579
+ const tab = tabs.find((t) => t.id === activeTabIdRef.current);
580
+ if (!tab) return;
581
+ const engine = engineMapRef.current.get(tab.panes[0]?.id);
582
+ if (!engine) return;
583
+ const context = engine.getContext();
584
+ if (!context) return;
585
+ const running = !!context.isExecuting || !!context.contextProcessor;
586
+ setStatusExecutionState(running ? "running" : "idle");
587
+ const result = context.lastCommandResult;
588
+ setStatusLastCommand(result ? { name: result.command, success: result.success } : null);
589
+ const notif = context.notifier?.current;
590
+ setNotification(notif || null);
591
+ }, 300);
592
+ const pollGlobal = setInterval(() => {
593
+ const tab = tabs.find((t) => t.id === activeTabIdRef.current);
594
+ if (!tab) return;
595
+ const engine = engineMapRef.current.get(tab.panes[0]?.id);
596
+ if (!engine) return;
597
+ const context = engine.getContext();
598
+ if (!context) return;
599
+ try {
600
+ const services2 = context.backgroundServices?.list() ?? [];
601
+ setStatusServiceCount({ running: services2.filter((s) => s.status === "running").length, total: services2.length });
602
+ setStatusServiceDetails(services2.map((s) => ({ name: s.name, status: s.status, description: s.description })));
603
+ } catch {
604
+ }
605
+ try {
606
+ let serverManager;
607
+ try {
608
+ serverManager = context.services?.get?.("cli-server-manager");
609
+ } catch {
610
+ }
611
+ const details = [];
612
+ if (serverManager?.connections?.size > 0) {
613
+ let anyConnected = false;
614
+ for (const [name, conn] of serverManager.connections) {
615
+ if (conn.connected) anyConnected = true;
616
+ details.push({
617
+ name,
618
+ url: conn.config?.url ?? "",
619
+ connected: !!conn.connected,
620
+ apiVersion: conn.connected ? conn.apiVersion : void 0,
621
+ commandCount: conn.connected ? conn.commands?.length : void 0
622
+ });
623
+ }
624
+ setStatusServerState(anyConnected ? "connected" : "disconnected");
625
+ } else {
626
+ const configuredServers = engine.options?.servers;
627
+ if (Array.isArray(configuredServers) && configuredServers.length > 0) {
628
+ for (const srv of configuredServers) {
629
+ if (srv.enabled === false) continue;
630
+ details.push({
631
+ name: srv.name,
632
+ url: srv.url ?? "",
633
+ connected: false
634
+ });
635
+ }
636
+ setStatusServerState("disconnected");
637
+ } else {
638
+ setStatusServerState("none");
639
+ }
640
+ }
641
+ setStatusServerDetails(details);
642
+ } catch {
643
+ }
644
+ setStatusUptime(engine.startedAt ? Date.now() - engine.startedAt : 0);
645
+ }, 2e3);
646
+ return () => {
647
+ clearInterval(pollExec);
648
+ clearInterval(pollGlobal);
649
+ };
650
+ }, [initialized, tabs]);
560
651
  const toggleMaximize = (0, import_react5.useCallback)(() => {
561
652
  const nextMaximized = !maximized;
562
653
  if (nextMaximized) {
@@ -595,6 +686,15 @@ var CliPanel = import_react5.default.forwardRef(
595
686
  document.addEventListener("click", closePositionDropdown);
596
687
  return () => document.removeEventListener("click", closePositionDropdown);
597
688
  }, [positionDropdownOpen, closePositionDropdown]);
689
+ (0, import_react5.useEffect)(() => {
690
+ if (!servicesDropdownOpen && !serversDropdownOpen) return;
691
+ const handler = () => {
692
+ setServicesDropdownOpen(false);
693
+ setServersDropdownOpen(false);
694
+ };
695
+ document.addEventListener("click", handler);
696
+ return () => document.removeEventListener("click", handler);
697
+ }, [servicesDropdownOpen, serversDropdownOpen]);
598
698
  const handlePositionButtonClick = (0, import_react5.useCallback)((e) => {
599
699
  e.stopPropagation();
600
700
  setPositionDropdownOpen((prev) => !prev);
@@ -705,6 +805,12 @@ var CliPanel = import_react5.default.forwardRef(
705
805
  getEngine,
706
806
  getState
707
807
  ]);
808
+ const formattedUptime = (() => {
809
+ const mins = Math.floor(statusUptime / 6e4);
810
+ if (mins < 60) return `${mins}m`;
811
+ const hrs = Math.floor(mins / 60);
812
+ return `${hrs}h${mins % 60}m`;
813
+ })();
708
814
  if (!visible) return null;
709
815
  const wrapperStyle = {
710
816
  ...isHorizontal ? { width: `${panelWidth}px` } : { height: `${panelHeight}px` },
@@ -750,6 +856,97 @@ var CliPanel = import_react5.default.forwardRef(
750
856
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TerminalIcon, {}) }),
751
857
  "CLI"
752
858
  ] }),
859
+ (position === "bottom" || position === "top") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-status-indicators", children: [
860
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: `cli-panel-status-item${statusExecutionState === "running" ? " status-running" : ""}`, children: [
861
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-dot ${statusExecutionState === "running" ? "dot-running" : "dot-idle"}` }),
862
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-label", children: statusExecutionState })
863
+ ] }),
864
+ statusServiceCount.total > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
865
+ "span",
866
+ {
867
+ ref: servicesDropdownRef,
868
+ className: "cli-panel-status-item status-clickable",
869
+ onClick: (e) => {
870
+ e.stopPropagation();
871
+ setServicesDropdownOpen((prev) => !prev);
872
+ setServersDropdownOpen(false);
873
+ },
874
+ children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-icon", children: "\u2699" }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-status-label", children: [
877
+ statusServiceCount.running,
878
+ "/",
879
+ statusServiceCount.total,
880
+ " services"
881
+ ] })
882
+ ]
883
+ }
884
+ ),
885
+ statusLastCommand && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-status-item", children: [
886
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-icon ${statusLastCommand.success ? "status-success" : "status-error"}`, children: statusLastCommand.success ? "\u2713" : "\u2717" }),
887
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-label", children: statusLastCommand.name })
888
+ ] }),
889
+ statusServerState !== "none" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
890
+ "span",
891
+ {
892
+ ref: serversDropdownRef,
893
+ className: "cli-panel-status-item status-clickable",
894
+ onClick: (e) => {
895
+ e.stopPropagation();
896
+ setServersDropdownOpen((prev) => !prev);
897
+ setServicesDropdownOpen(false);
898
+ },
899
+ children: [
900
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-dot ${statusServerState === "connected" ? "dot-idle" : "dot-error"}` }),
901
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-status-label", children: [
902
+ statusServerDetails.filter((s) => s.connected).length,
903
+ "/",
904
+ statusServerDetails.length,
905
+ " servers"
906
+ ] })
907
+ ]
908
+ }
909
+ ),
910
+ statusUptime > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-status-item status-muted", children: [
911
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-icon", children: "\u2191" }),
912
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-label", children: formattedUptime })
913
+ ] }),
914
+ notification && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-item status-text level-${notification.level}`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-label", children: notification.message }) })
915
+ ] }),
916
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-status-indicators-compact", children: [
917
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-compact-item", title: statusExecutionState, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-dot ${statusExecutionState === "running" ? "dot-running" : "dot-idle"}` }) }),
918
+ statusServiceCount.total > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
919
+ "span",
920
+ {
921
+ ref: servicesDropdownRef,
922
+ className: "cli-panel-status-compact-item status-clickable",
923
+ title: `${statusServiceCount.running}/${statusServiceCount.total} services`,
924
+ onClick: (e) => {
925
+ e.stopPropagation();
926
+ setServicesDropdownOpen((prev) => !prev);
927
+ setServersDropdownOpen(false);
928
+ },
929
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-icon", children: "\u2699" })
930
+ }
931
+ ),
932
+ statusLastCommand && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-compact-item", title: `${statusLastCommand.success ? "\u2713" : "\u2717"} ${statusLastCommand.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-icon ${statusLastCommand.success ? "status-success" : "status-error"}`, children: statusLastCommand.success ? "\u2713" : "\u2717" }) }),
933
+ statusServerState !== "none" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
934
+ "span",
935
+ {
936
+ ref: serversDropdownRef,
937
+ className: "cli-panel-status-compact-item status-clickable",
938
+ title: `${statusServerDetails.filter((s) => s.connected).length}/${statusServerDetails.length} servers`,
939
+ onClick: (e) => {
940
+ e.stopPropagation();
941
+ setServersDropdownOpen((prev) => !prev);
942
+ setServicesDropdownOpen(false);
943
+ },
944
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-dot ${statusServerState === "connected" ? "dot-idle" : "dot-error"}` })
945
+ }
946
+ ),
947
+ statusUptime > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-compact-item status-muted", title: formattedUptime, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-icon", children: "\u2191" }) }),
948
+ notification && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-compact-item", title: notification.message, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-compact-dot level-${notification.level}` }) })
949
+ ] }),
753
950
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-action-buttons", children: [
754
951
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
755
952
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-position", title: "Move panel", onClick: handlePositionButtonClick, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position }) }),
@@ -771,7 +968,8 @@ var CliPanel = import_react5.default.forwardRef(
771
968
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn", title: collapsed ? "Expand" : "Collapse", onClick: toggle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CollapseChevron, { position, isCollapsed: collapsed }) }),
772
969
  closable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-close", title: "Close", onClick: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}) })
773
970
  ] })
774
- ] })
971
+ ] }),
972
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `cli-panel-glow-line${statusExecutionState === "running" ? " glow-active" : ""}` })
775
973
  ] }),
776
974
  initialized && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
777
975
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-tabs", children: [
@@ -802,6 +1000,12 @@ var CliPanel = import_react5.default.forwardRef(
802
1000
  onDoubleClick: (e) => e.stopPropagation()
803
1001
  }
804
1002
  ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1003
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-tab-dot ${(() => {
1004
+ if (tab.id !== activeTabId) return "dot-idle";
1005
+ if (statusExecutionState === "running") return "dot-running";
1006
+ if (statusLastCommand && !statusLastCommand.success) return "dot-error";
1007
+ return "dot-idle";
1008
+ })()}` }),
805
1009
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-tab-title", children: tab.title }),
806
1010
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
1011
  "button",
@@ -927,6 +1131,86 @@ var CliPanel = import_react5.default.forwardRef(
927
1131
  }, children: "Close All" })
928
1132
  ]
929
1133
  }
1134
+ ),
1135
+ servicesDropdownOpen && statusServiceCount.total > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1136
+ "div",
1137
+ {
1138
+ className: "cli-panel-services-dropdown",
1139
+ style: (() => {
1140
+ const el = servicesDropdownRef.current;
1141
+ if (!el) return {};
1142
+ const rect = el.getBoundingClientRect();
1143
+ const base = { position: "fixed", zIndex: 1100 };
1144
+ switch (position) {
1145
+ case "top":
1146
+ return { ...base, top: rect.bottom + 4, left: rect.left };
1147
+ case "left":
1148
+ return { ...base, top: rect.top, left: rect.right + 4 };
1149
+ case "right":
1150
+ return { ...base, top: rect.top, right: window.innerWidth - rect.left + 4 };
1151
+ default:
1152
+ return { ...base, bottom: window.innerHeight - rect.top + 4, left: rect.left };
1153
+ }
1154
+ })(),
1155
+ onClick: (e) => e.stopPropagation(),
1156
+ children: [
1157
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-header", children: "Background Services" }),
1158
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-list", children: statusServiceDetails.map((svc, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-services-dropdown-item", children: [
1159
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-svc-dot ${svc.status === "running" ? "svc-running" : "svc-stopped"}` }),
1160
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-svc-info", children: [
1161
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-name", children: svc.name }),
1162
+ svc.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-desc", children: svc.description })
1163
+ ] }),
1164
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-status", children: svc.status })
1165
+ ] }, i)) }),
1166
+ statusServiceDetails.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-empty", children: "No services registered" })
1167
+ ]
1168
+ }
1169
+ ),
1170
+ serversDropdownOpen && statusServerState !== "none" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1171
+ "div",
1172
+ {
1173
+ className: "cli-panel-services-dropdown",
1174
+ style: (() => {
1175
+ const el = serversDropdownRef.current;
1176
+ if (!el) return {};
1177
+ const rect = el.getBoundingClientRect();
1178
+ const base = { position: "fixed", zIndex: 1100 };
1179
+ switch (position) {
1180
+ case "top":
1181
+ return { ...base, top: rect.bottom + 4, left: rect.left };
1182
+ case "left":
1183
+ return { ...base, top: rect.top, left: rect.right + 4 };
1184
+ case "right":
1185
+ return { ...base, top: rect.top, right: window.innerWidth - rect.left + 4 };
1186
+ default:
1187
+ return { ...base, bottom: window.innerHeight - rect.top + 4, left: rect.left };
1188
+ }
1189
+ })(),
1190
+ onClick: (e) => e.stopPropagation(),
1191
+ children: [
1192
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-header", children: "Server Connections" }),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-list", children: statusServerDetails.map((srv, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-services-dropdown-item", children: [
1194
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-svc-dot ${srv.connected ? "svc-running" : "svc-stopped"}` }),
1195
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-svc-info", children: [
1196
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-name", children: srv.name }),
1197
+ srv.url && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-desc", children: srv.url })
1198
+ ] }),
1199
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-srv-meta", children: [
1200
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-status", children: srv.connected ? "connected" : "disconnected" }),
1201
+ srv.apiVersion && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-svc-desc", children: [
1202
+ "v",
1203
+ srv.apiVersion
1204
+ ] }),
1205
+ srv.commandCount && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cli-panel-svc-desc", children: [
1206
+ srv.commandCount,
1207
+ " cmds"
1208
+ ] })
1209
+ ] })
1210
+ ] }, i)) }),
1211
+ statusServerDetails.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-empty", children: "No servers configured" })
1212
+ ]
1213
+ }
930
1214
  )
931
1215
  ] });
932
1216
  }