@axiom-lattice/react-sdk 2.1.24 → 2.1.25

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
@@ -6373,7 +6373,7 @@ var AgentConversations = () => {
6373
6373
  };
6374
6374
 
6375
6375
  // src/components/Chat/ChatSidebar.tsx
6376
- import { useState as useState27, useEffect as useEffect20, useRef as useRef13 } from "react";
6376
+ import { useState as useState28, useEffect as useEffect20, useRef as useRef13 } from "react";
6377
6377
  import { Divider as Divider4, Tooltip as Tooltip8, Modal as Modal3, Button as Button20 } from "antd";
6378
6378
  import {
6379
6379
  MenuFoldOutlined,
@@ -6427,7 +6427,7 @@ var AssistantList = () => {
6427
6427
  import { createStyles as createStyles12 } from "antd-style";
6428
6428
 
6429
6429
  // src/components/Chat/AssistantFlow.tsx
6430
- import { useMemo as useMemo10, useEffect as useEffect17 } from "react";
6430
+ import { useMemo as useMemo10, useEffect as useEffect17, useState as useState24 } from "react";
6431
6431
  import {
6432
6432
  ReactFlow,
6433
6433
  Background,
@@ -7215,18 +7215,44 @@ var AssistantNode_default = AssistantNode;
7215
7215
 
7216
7216
  // src/components/Chat/AssistantFlow.tsx
7217
7217
  import { jsx as jsx44, jsxs as jsxs24 } from "react/jsx-runtime";
7218
- var AssistantFlowInner = ({
7219
- assistants,
7220
- onNodeClick
7221
- }) => {
7218
+ var AssistantFlowInner = ({ onNodeClick }) => {
7222
7219
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
7223
7220
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
7221
+ const [assistants, setAssistants] = useState24([]);
7222
+ const [assistantsLoading, setAssistantsLoading] = useState24(false);
7223
+ const { config } = useLatticeChatShellContext();
7224
7224
  const nodeTypes = useMemo10(
7225
7225
  () => ({
7226
7226
  assistant: AssistantNode_default
7227
7227
  }),
7228
7228
  []
7229
7229
  );
7230
+ const fetchAssistants = async () => {
7231
+ setAssistantsLoading(true);
7232
+ try {
7233
+ const headers = {};
7234
+ if (config.apiKey) {
7235
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
7236
+ }
7237
+ const response = await fetch(`${config.baseURL}/api/assistants`, {
7238
+ headers
7239
+ });
7240
+ if (response.ok) {
7241
+ const data = await response.json();
7242
+ if (data.success && data.data && Array.isArray(data.data.records)) {
7243
+ setAssistants(data.data.records);
7244
+ }
7245
+ }
7246
+ } catch (error) {
7247
+ console.error("Failed to fetch assistants:", error);
7248
+ setAssistants([]);
7249
+ } finally {
7250
+ setAssistantsLoading(false);
7251
+ }
7252
+ };
7253
+ useEffect17(() => {
7254
+ fetchAssistants();
7255
+ }, []);
7230
7256
  useEffect17(() => {
7231
7257
  if (!assistants || assistants.length === 0) {
7232
7258
  setNodes([]);
@@ -7416,6 +7442,22 @@ var AssistantFlowInner = ({
7416
7442
  setNodes(initialNodes);
7417
7443
  setEdges(initialEdges);
7418
7444
  }, [assistants, setNodes, setEdges, onNodeClick]);
7445
+ if (assistantsLoading) {
7446
+ return /* @__PURE__ */ jsx44(
7447
+ "div",
7448
+ {
7449
+ style: {
7450
+ width: "100%",
7451
+ height: "100%",
7452
+ display: "flex",
7453
+ justifyContent: "center",
7454
+ alignItems: "center",
7455
+ background: "#fafafa"
7456
+ },
7457
+ children: "Loading assistants..."
7458
+ }
7459
+ );
7460
+ }
7419
7461
  return /* @__PURE__ */ jsx44("div", { style: { width: "100%", height: "100%", background: "#fafafa" }, children: /* @__PURE__ */ jsxs24(
7420
7462
  ReactFlow,
7421
7463
  {
@@ -7436,7 +7478,7 @@ var AssistantFlow = (props) => /* @__PURE__ */ jsx44(ReactFlowProvider, { childr
7436
7478
  var AssistantFlow_default = AssistantFlow;
7437
7479
 
7438
7480
  // src/components/Chat/SkillFlow.tsx
7439
- import { useMemo as useMemo12, useEffect as useEffect19, useState as useState26 } from "react";
7481
+ import { useMemo as useMemo12, useEffect as useEffect19, useState as useState27 } from "react";
7440
7482
  import {
7441
7483
  ReactFlow as ReactFlow2,
7442
7484
  Background as Background2,
@@ -7450,7 +7492,7 @@ import { Button as Button19, theme as theme6 } from "antd";
7450
7492
  import { PlusOutlined as PlusOutlined3 } from "@ant-design/icons";
7451
7493
 
7452
7494
  // src/components/Chat/SkillNode.tsx
7453
- import { useEffect as useEffect18, useMemo as useMemo11, useState as useState24 } from "react";
7495
+ import { useEffect as useEffect18, useMemo as useMemo11, useState as useState25 } from "react";
7454
7496
  import { Handle as Handle2, Position as Position2 } from "@xyflow/react";
7455
7497
  import {
7456
7498
  Avatar as Avatar6,
@@ -7474,6 +7516,7 @@ import {
7474
7516
  CopyOutlined as CopyOutlined3,
7475
7517
  PlusOutlined
7476
7518
  } from "@ant-design/icons";
7519
+ import { Client as Client4 } from "@axiom-lattice/client-sdk";
7477
7520
  import { Fragment as Fragment5, jsx as jsx45, jsxs as jsxs25 } from "react/jsx-runtime";
7478
7521
  var { Text: Text15 } = Typography17;
7479
7522
  var { TextArea } = Input;
@@ -7505,14 +7548,24 @@ var getBadgeColor2 = (name) => {
7505
7548
  var SkillNode = ({ data }) => {
7506
7549
  const { token } = theme4.useToken();
7507
7550
  const { config } = useLatticeChatShellContext();
7551
+ const client = useMemo11(
7552
+ () => new Client4({
7553
+ baseURL: config.baseURL,
7554
+ apiKey: config.apiKey || "",
7555
+ assistantId: "",
7556
+ // Not needed for skills
7557
+ transport: "sse"
7558
+ }),
7559
+ [config.baseURL, config.apiKey]
7560
+ );
7508
7561
  if (!data) {
7509
7562
  return null;
7510
7563
  }
7511
- const { skill, onClick } = data;
7564
+ const { skill, onClick, onDelete } = data;
7512
7565
  if (!skill) {
7513
7566
  return null;
7514
7567
  }
7515
- const [currentSkill, setCurrentSkill] = useState24(skill);
7568
+ const [currentSkill, setCurrentSkill] = useState25(skill);
7516
7569
  const badgeColor = getBadgeColor2(currentSkill.name);
7517
7570
  const initials = currentSkill.name?.split("-").map((n) => n.charAt(0)).join("").toUpperCase().slice(0, 2) || "SK";
7518
7571
  const hasSubSkills = currentSkill.subSkills && currentSkill.subSkills.length > 0;
@@ -7534,19 +7587,19 @@ var SkillNode = ({ data }) => {
7534
7587
  const handleCopy = (text) => {
7535
7588
  navigator.clipboard.writeText(text);
7536
7589
  };
7537
- const [isEditing, setIsEditing] = useState24(false);
7538
- const [draftSkill, setDraftSkill] = useState24({
7590
+ const [isEditing, setIsEditing] = useState25(false);
7591
+ const [draftSkill, setDraftSkill] = useState25({
7539
7592
  ...skill,
7540
7593
  id: skill.name
7541
7594
  });
7542
- const [metadataRows, setMetadataRows] = useState24(
7595
+ const [metadataRows, setMetadataRows] = useState25(
7543
7596
  metadataEntries.map(([key, value]) => ({
7544
7597
  key,
7545
7598
  value
7546
7599
  }))
7547
7600
  );
7548
- const [isModalOpen, setIsModalOpen] = useState24(false);
7549
- const [saving, setSaving] = useState24(false);
7601
+ const [isModalOpen, setIsModalOpen] = useState25(false);
7602
+ const [saving, setSaving] = useState25(false);
7550
7603
  useEffect18(() => {
7551
7604
  setIsEditing(false);
7552
7605
  setCurrentSkill(skill);
@@ -7572,45 +7625,26 @@ var SkillNode = ({ data }) => {
7572
7625
  [field]: value
7573
7626
  }));
7574
7627
  };
7575
- const handleDelete = () => {
7576
- Modal.confirm({
7577
- title: "Delete skill",
7578
- content: `Are you sure you want to delete "${currentSkill.name}"? This action cannot be undone.`,
7579
- okText: "Delete",
7580
- okType: "danger",
7581
- cancelText: "Cancel",
7582
- onOk: async () => {
7583
- try {
7584
- const headers = {};
7585
- if (config.apiKey) {
7586
- headers["Authorization"] = `Bearer ${config.apiKey}`;
7587
- }
7588
- const response = await fetch(
7589
- `${config.baseURL}/api/skills/${currentSkill.id}`,
7590
- {
7591
- method: "DELETE",
7592
- headers
7593
- }
7594
- );
7595
- let data2 = {};
7596
- try {
7597
- data2 = await response.json();
7598
- } catch {
7599
- }
7600
- if (response.ok && (data2.success ?? true)) {
7601
- message5.success("Skill deleted successfully");
7602
- setIsModalOpen(false);
7603
- onClick?.(currentSkill);
7604
- } else {
7605
- message5.error(data2.message || "Failed to delete skill");
7606
- }
7607
- } catch (error) {
7608
- message5.error(
7609
- error?.message || "An error occurred while deleting the skill"
7610
- );
7611
- }
7628
+ const handleDelete = async (e) => {
7629
+ e?.stopPropagation();
7630
+ e?.preventDefault();
7631
+ try {
7632
+ const skillId = currentSkill.id || currentSkill.name;
7633
+ if (!skillId) {
7634
+ message5.error("Skill ID is missing");
7635
+ return;
7612
7636
  }
7613
- });
7637
+ await client.skills.delete(skillId);
7638
+ message5.success("Skill deleted successfully");
7639
+ setIsModalOpen(false);
7640
+ if (onDelete) {
7641
+ await onDelete();
7642
+ }
7643
+ } catch (error) {
7644
+ message5.error(
7645
+ error?.message || "An error occurred while deleting the skill"
7646
+ );
7647
+ }
7614
7648
  };
7615
7649
  const syncMetadataRows = (rows) => {
7616
7650
  setMetadataRows(rows);
@@ -8255,7 +8289,17 @@ var SkillNode = ({ data }) => {
8255
8289
  children: "Edit Skill"
8256
8290
  }
8257
8291
  ),
8258
- /* @__PURE__ */ jsx45(Button17, { danger: true, onClick: handleDelete, children: "Delete" }),
8292
+ /* @__PURE__ */ jsx45(
8293
+ Button17,
8294
+ {
8295
+ danger: true,
8296
+ onClick: (e) => {
8297
+ e.stopPropagation();
8298
+ handleDelete(e);
8299
+ },
8300
+ children: "Delete"
8301
+ }
8302
+ ),
8259
8303
  /* @__PURE__ */ jsx45(
8260
8304
  Button17,
8261
8305
  {
@@ -8629,7 +8673,7 @@ var SkillNode = ({ data }) => {
8629
8673
  var SkillNode_default = SkillNode;
8630
8674
 
8631
8675
  // src/components/Chat/CreateSkillModal.tsx
8632
- import { useState as useState25 } from "react";
8676
+ import { useState as useState26 } from "react";
8633
8677
  import {
8634
8678
  Modal as Modal2,
8635
8679
  Form,
@@ -8650,11 +8694,11 @@ var CreateSkillModal = ({
8650
8694
  onSuccess
8651
8695
  }) => {
8652
8696
  const [form] = Form.useForm();
8653
- const [loading, setLoading] = useState25(false);
8697
+ const [loading, setLoading] = useState26(false);
8654
8698
  const { config } = useLatticeChatShellContext();
8655
8699
  const { token } = theme5.useToken();
8656
8700
  const defaultMetadataRows = [{ key: "category", value: "global" }];
8657
- const [metadataRows, setMetadataRows] = useState25(defaultMetadataRows);
8701
+ const [metadataRows, setMetadataRows] = useState26(defaultMetadataRows);
8658
8702
  const handleSubmit = async () => {
8659
8703
  try {
8660
8704
  const values = await form.validateFields();
@@ -8929,21 +8973,46 @@ var CreateSkillModal_default = CreateSkillModal;
8929
8973
 
8930
8974
  // src/components/Chat/SkillFlow.tsx
8931
8975
  import { jsx as jsx47, jsxs as jsxs27 } from "react/jsx-runtime";
8932
- var SkillFlowInner = ({
8933
- skills,
8934
- onNodeClick,
8935
- onRefresh
8936
- }) => {
8976
+ var SkillFlowInner = ({ onNodeClick }) => {
8937
8977
  const [nodes, setNodes, onNodesChange] = useNodesState2([]);
8938
8978
  const [edges, setEdges, onEdgesChange] = useEdgesState2([]);
8939
- const [isCreateModalOpen, setIsCreateModalOpen] = useState26(false);
8979
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState27(false);
8980
+ const [skills, setSkills] = useState27([]);
8981
+ const [skillsLoading, setSkillsLoading] = useState27(false);
8940
8982
  const { token } = theme6.useToken();
8983
+ const { config } = useLatticeChatShellContext();
8941
8984
  const nodeTypes = useMemo12(
8942
8985
  () => ({
8943
8986
  skill: SkillNode_default
8944
8987
  }),
8945
8988
  []
8946
8989
  );
8990
+ const fetchSkills = async () => {
8991
+ setSkillsLoading(true);
8992
+ try {
8993
+ const headers = {};
8994
+ if (config.apiKey) {
8995
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
8996
+ }
8997
+ const response = await fetch(`${config.baseURL}/api/skills`, {
8998
+ headers
8999
+ });
9000
+ if (response.ok) {
9001
+ const data = await response.json();
9002
+ if (data.success && data.data && Array.isArray(data.data.records)) {
9003
+ setSkills(data.data.records);
9004
+ }
9005
+ }
9006
+ } catch (error) {
9007
+ console.error("Failed to fetch skills:", error);
9008
+ setSkills([]);
9009
+ } finally {
9010
+ setSkillsLoading(false);
9011
+ }
9012
+ };
9013
+ useEffect19(() => {
9014
+ fetchSkills();
9015
+ }, []);
8947
9016
  useEffect19(() => {
8948
9017
  if (!skills || skills.length === 0) {
8949
9018
  setNodes([]);
@@ -9033,7 +9102,8 @@ var SkillFlowInner = ({
9033
9102
  type: "skill",
9034
9103
  data: {
9035
9104
  skill,
9036
- onClick: () => onNodeClick?.(skill)
9105
+ onClick: () => onNodeClick?.(skill),
9106
+ onDelete: fetchSkills
9037
9107
  },
9038
9108
  position: { x, y }
9039
9109
  });
@@ -9072,10 +9142,24 @@ var SkillFlowInner = ({
9072
9142
  setEdges(initialEdges);
9073
9143
  }, [skills, setNodes, setEdges, onNodeClick]);
9074
9144
  const handleSkillCreated = async () => {
9075
- if (onRefresh) {
9076
- await onRefresh();
9077
- }
9145
+ await fetchSkills();
9078
9146
  };
9147
+ if (skillsLoading) {
9148
+ return /* @__PURE__ */ jsx47(
9149
+ "div",
9150
+ {
9151
+ style: {
9152
+ width: "100%",
9153
+ height: "100%",
9154
+ display: "flex",
9155
+ justifyContent: "center",
9156
+ alignItems: "center",
9157
+ background: "#fafafa"
9158
+ },
9159
+ children: "Loading skills..."
9160
+ }
9161
+ );
9162
+ }
9079
9163
  return /* @__PURE__ */ jsxs27("div", { style: { width: "100%", height: "100%", background: "#fafafa", position: "relative" }, children: [
9080
9164
  /* @__PURE__ */ jsxs27(
9081
9165
  ReactFlow2,
@@ -9097,7 +9181,7 @@ var SkillFlowInner = ({
9097
9181
  {
9098
9182
  style: {
9099
9183
  position: "absolute",
9100
- bottom: 20,
9184
+ top: 20,
9101
9185
  right: 20,
9102
9186
  zIndex: 10
9103
9187
  },
@@ -9361,13 +9445,11 @@ var ChatSidebar = ({
9361
9445
  const { styles } = useStyles4();
9362
9446
  const { setMenuCollapsed, menuCollapsed, sideAppVisible } = useChatUIContext();
9363
9447
  const { setSettingsModalOpen, config } = useLatticeChatShellContext();
9364
- const { assistants, selectAssistant } = useAssistantContext();
9365
- const [isHovered, setIsHovered] = useState27(false);
9366
- const [isFirstCollapse, setIsFirstCollapse] = useState27(false);
9367
- const [isFlowModalOpen, setIsFlowModalOpen] = useState27(false);
9368
- const [isSkillFlowModalOpen, setIsSkillFlowModalOpen] = useState27(false);
9369
- const [skills, setSkills] = useState27([]);
9370
- const [skillsLoading, setSkillsLoading] = useState27(false);
9448
+ const { selectAssistant } = useAssistantContext();
9449
+ const [isHovered, setIsHovered] = useState28(false);
9450
+ const [isFirstCollapse, setIsFirstCollapse] = useState28(false);
9451
+ const [isFlowModalOpen, setIsFlowModalOpen] = useState28(false);
9452
+ const [isSkillFlowModalOpen, setIsSkillFlowModalOpen] = useState28(false);
9371
9453
  const prevIsCollapsedRef = useRef13(false);
9372
9454
  const handleToggleCollapse = () => {
9373
9455
  setMenuCollapsed(!menuCollapsed);
@@ -9376,34 +9458,8 @@ var ChatSidebar = ({
9376
9458
  setSettingsModalOpen(true);
9377
9459
  onSettingsClick?.();
9378
9460
  };
9379
- const handleSkillFlowClick = async () => {
9461
+ const handleSkillFlowClick = () => {
9380
9462
  setIsSkillFlowModalOpen(true);
9381
- if (skills.length === 0 && !skillsLoading) {
9382
- await fetchSkills();
9383
- }
9384
- };
9385
- const fetchSkills = async () => {
9386
- setSkillsLoading(true);
9387
- try {
9388
- const headers = {};
9389
- if (config.apiKey) {
9390
- headers["Authorization"] = `Bearer ${config.apiKey}`;
9391
- }
9392
- const response = await fetch(`${config.baseURL}/api/skills`, {
9393
- headers
9394
- });
9395
- if (response.ok) {
9396
- const data = await response.json();
9397
- if (data.success && data.data && Array.isArray(data.data.records)) {
9398
- setSkills(data.data.records);
9399
- }
9400
- }
9401
- } catch (error) {
9402
- console.error("Failed to fetch skills:", error);
9403
- setSkills([]);
9404
- } finally {
9405
- setSkillsLoading(false);
9406
- }
9407
9463
  };
9408
9464
  const isCollapsed = menuCollapsed || sideAppVisible;
9409
9465
  useEffect20(() => {
@@ -9582,6 +9638,7 @@ var ChatSidebar = ({
9582
9638
  /* @__PURE__ */ jsx48(
9583
9639
  Modal3,
9584
9640
  {
9641
+ destroyOnHidden: true,
9585
9642
  title: "Assistant Overview",
9586
9643
  open: isFlowModalOpen,
9587
9644
  onCancel: () => setIsFlowModalOpen(false),
@@ -9595,7 +9652,6 @@ var ChatSidebar = ({
9595
9652
  children: /* @__PURE__ */ jsx48("div", { style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx48(
9596
9653
  AssistantFlow_default,
9597
9654
  {
9598
- assistants,
9599
9655
  onNodeClick: (assistant) => {
9600
9656
  selectAssistant(assistant.id);
9601
9657
  setIsFlowModalOpen(false);
@@ -9607,6 +9663,7 @@ var ChatSidebar = ({
9607
9663
  /* @__PURE__ */ jsx48(
9608
9664
  Modal3,
9609
9665
  {
9666
+ destroyOnHidden: true,
9610
9667
  title: "Skill Overview",
9611
9668
  open: isSkillFlowModalOpen,
9612
9669
  onCancel: () => setIsSkillFlowModalOpen(false),
@@ -9617,25 +9674,12 @@ var ChatSidebar = ({
9617
9674
  height: "calc(100vh - 120px)",
9618
9675
  padding: 0
9619
9676
  },
9620
- children: /* @__PURE__ */ jsx48("div", { style: { width: "100%", height: "100%" }, children: skillsLoading ? /* @__PURE__ */ jsx48(
9621
- "div",
9622
- {
9623
- style: {
9624
- display: "flex",
9625
- justifyContent: "center",
9626
- alignItems: "center",
9627
- height: "100%"
9628
- },
9629
- children: "Loading skills..."
9630
- }
9631
- ) : /* @__PURE__ */ jsx48(
9677
+ children: /* @__PURE__ */ jsx48("div", { style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx48(
9632
9678
  SkillFlow_default,
9633
9679
  {
9634
- skills,
9635
9680
  onNodeClick: (skill) => {
9636
9681
  console.log("Skill clicked:", skill);
9637
- },
9638
- onRefresh: fetchSkills
9682
+ }
9639
9683
  }
9640
9684
  ) })
9641
9685
  }
@@ -9675,7 +9719,7 @@ var LatticeChatView = (props) => {
9675
9719
  };
9676
9720
 
9677
9721
  // src/components/Chat/SettingsModal.tsx
9678
- import { useState as useState28, useEffect as useEffect21, useRef as useRef14 } from "react";
9722
+ import { useState as useState29, useEffect as useEffect21, useRef as useRef14 } from "react";
9679
9723
  import {
9680
9724
  Modal as Modal4,
9681
9725
  Input as Input3,
@@ -10064,7 +10108,7 @@ var SettingsModal = ({
10064
10108
  }) => {
10065
10109
  const { styles } = useStyles5();
10066
10110
  const { config: shellConfig, updateConfigValue } = useLatticeChatShellContext();
10067
- const [connections, setConnections] = useState28(() => {
10111
+ const [connections, setConnections] = useState29(() => {
10068
10112
  if (typeof window !== "undefined") {
10069
10113
  try {
10070
10114
  const stored = localStorage.getItem("lattice_server_connections");
@@ -10087,21 +10131,21 @@ var SettingsModal = ({
10087
10131
  }
10088
10132
  return [];
10089
10133
  });
10090
- const [serverConfigs, setServerConfigs] = useState28({});
10134
+ const [serverConfigs, setServerConfigs] = useState29({});
10091
10135
  const connectionsRef = useRef14(connections);
10092
10136
  useEffect21(() => {
10093
10137
  connectionsRef.current = connections;
10094
10138
  }, [connections]);
10095
- const [activeTabKey, setActiveTabKey] = useState28(
10139
+ const [activeTabKey, setActiveTabKey] = useState29(
10096
10140
  connections.length > 0 ? connections[0].id : ""
10097
10141
  );
10098
- const [activeMenu, setActiveMenu] = useState28("environment");
10099
- const [loading, setLoading] = useState28(false);
10100
- const [showAddServerModal, setShowAddServerModal] = useState28(false);
10101
- const [newServerUrl, setNewServerUrl] = useState28("");
10102
- const [newServerName, setNewServerName] = useState28("");
10103
- const [newServerApiKey, setNewServerApiKey] = useState28("");
10104
- const [addingServer, setAddingServer] = useState28(false);
10142
+ const [activeMenu, setActiveMenu] = useState29("environment");
10143
+ const [loading, setLoading] = useState29(false);
10144
+ const [showAddServerModal, setShowAddServerModal] = useState29(false);
10145
+ const [newServerUrl, setNewServerUrl] = useState29("");
10146
+ const [newServerName, setNewServerName] = useState29("");
10147
+ const [newServerApiKey, setNewServerApiKey] = useState29("");
10148
+ const [addingServer, setAddingServer] = useState29(false);
10105
10149
  const saveConnections = (newConnections) => {
10106
10150
  setConnections(newConnections);
10107
10151
  if (typeof window !== "undefined") {