@axiom-lattice/react-sdk 2.1.97 → 2.1.98

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
@@ -220,8 +220,8 @@ var AuthProvider = ({
220
220
  onLoginSuccess?.(userData, tenantList || []);
221
221
  return { requiresTenantSelection, hasTenants };
222
222
  } catch (err) {
223
- const message28 = err instanceof Error ? err.message : "Login failed";
224
- setError(message28);
223
+ const message30 = err instanceof Error ? err.message : "Login failed";
224
+ setError(message30);
225
225
  throw err;
226
226
  } finally {
227
227
  setIsLoading(false);
@@ -245,8 +245,8 @@ var AuthProvider = ({
245
245
  }
246
246
  return { message: data.message || "Registration successful!", token: data.data?.token };
247
247
  } catch (err) {
248
- const message28 = err instanceof Error ? err.message : "Registration failed";
249
- setError(message28);
248
+ const message30 = err instanceof Error ? err.message : "Registration failed";
249
+ setError(message30);
250
250
  throw err;
251
251
  } finally {
252
252
  setIsLoading(false);
@@ -305,8 +305,8 @@ var AuthProvider = ({
305
305
  }
306
306
  onTenantSelected?.(tenantData);
307
307
  } catch (err) {
308
- const message28 = err instanceof Error ? err.message : "Failed to select tenant";
309
- setError(message28);
308
+ const message30 = err instanceof Error ? err.message : "Failed to select tenant";
309
+ setError(message30);
310
310
  throw err;
311
311
  } finally {
312
312
  setIsLoading(false);
@@ -345,8 +345,8 @@ var AuthProvider = ({
345
345
  setTenants(tenantList);
346
346
  sessionStorage.setItem("lattice_tenants", JSON.stringify(tenantList));
347
347
  } catch (err) {
348
- const message28 = err instanceof Error ? err.message : "Failed to fetch tenants";
349
- setError(message28);
348
+ const message30 = err instanceof Error ? err.message : "Failed to fetch tenants";
349
+ setError(message30);
350
350
  } finally {
351
351
  setIsLoading(false);
352
352
  }
@@ -376,8 +376,8 @@ var AuthProvider = ({
376
376
  setUser(data);
377
377
  sessionStorage.setItem("lattice_user", JSON.stringify(data));
378
378
  } catch (err) {
379
- const message28 = err instanceof Error ? err.message : "Failed to refresh user";
380
- setError(message28);
379
+ const message30 = err instanceof Error ? err.message : "Failed to refresh user";
380
+ setError(message30);
381
381
  } finally {
382
382
  setIsLoading(false);
383
383
  }
@@ -413,8 +413,8 @@ var AuthProvider = ({
413
413
  const result = await response.json();
414
414
  return result;
415
415
  } catch (err) {
416
- const message28 = err instanceof Error ? err.message : "Failed to change password";
417
- setError(message28);
416
+ const message30 = err instanceof Error ? err.message : "Failed to change password";
417
+ setError(message30);
418
418
  throw err;
419
419
  } finally {
420
420
  setIsLoading(false);
@@ -633,7 +633,7 @@ function useChat(threadId, assistantId, options = {}) {
633
633
  throw new Error("Thread ID is required to send messages");
634
634
  }
635
635
  const { input, command, streaming = true } = data;
636
- const { message: message28, files, ...rest } = input || {};
636
+ const { message: message30, files, ...rest } = input || {};
637
637
  setState((prev) => ({
638
638
  ...prev,
639
639
  isLoading: true,
@@ -642,7 +642,7 @@ function useChat(threadId, assistantId, options = {}) {
642
642
  }));
643
643
  const userMessage = {
644
644
  id: (0, import_uuid.v4)(),
645
- content: message28 || command?.resume?.message || "",
645
+ content: message30 || command?.resume?.message || "",
646
646
  files,
647
647
  role: "human"
648
648
  };
@@ -1649,8 +1649,8 @@ function convertThreadToConversationThread(thread, label) {
1649
1649
  function getThreadMetadata(label) {
1650
1650
  return label ? { label } : {};
1651
1651
  }
1652
- function generateLabelFromMessage(message28) {
1653
- const cleanMessage = message28.replace(/```attachments[\s\S]*?```/g, "").trim();
1652
+ function generateLabelFromMessage(message30) {
1653
+ const cleanMessage = message30.replace(/```attachments[\s\S]*?```/g, "").trim();
1654
1654
  if (!cleanMessage) {
1655
1655
  return "";
1656
1656
  }
@@ -2123,7 +2123,7 @@ function AgentThreadProvider({
2123
2123
  throw new Error("Thread ID is required to send messages");
2124
2124
  }
2125
2125
  const { input, command, streaming = true, mode } = data;
2126
- const { message: message28, files, ...rest } = input || {};
2126
+ const { message: message30, files, ...rest } = input || {};
2127
2127
  setState((prev) => ({
2128
2128
  ...prev,
2129
2129
  isLoading: true,
@@ -2132,7 +2132,7 @@ function AgentThreadProvider({
2132
2132
  }));
2133
2133
  const userMessage = {
2134
2134
  id: (0, import_uuid2.v4)(),
2135
- content: message28 || command?.resume?.message || "",
2135
+ content: message30 || command?.resume?.message || "",
2136
2136
  files,
2137
2137
  role: "human"
2138
2138
  };
@@ -2150,8 +2150,8 @@ function AgentThreadProvider({
2150
2150
  ]
2151
2151
  }));
2152
2152
  const isFirstMessage = messageCountRef.current === 0;
2153
- if (isFirstMessage && message28 && conversationContext) {
2154
- const label = generateLabelFromMessage(message28);
2153
+ if (isFirstMessage && message30 && conversationContext) {
2154
+ const label = generateLabelFromMessage(message30);
2155
2155
  if (label) {
2156
2156
  conversationContext.updateThread({
2157
2157
  id: threadId,
@@ -4048,8 +4048,8 @@ function useTenants(options) {
4048
4048
  const { data } = await response.json();
4049
4049
  setTenants(data || []);
4050
4050
  } catch (err) {
4051
- const message28 = err instanceof Error ? err.message : "Failed to fetch tenants";
4052
- setError(message28);
4051
+ const message30 = err instanceof Error ? err.message : "Failed to fetch tenants";
4052
+ setError(message30);
4053
4053
  } finally {
4054
4054
  setIsLoading(false);
4055
4055
  }
@@ -4091,8 +4091,8 @@ function useUsers(options) {
4091
4091
  const { data } = await response.json();
4092
4092
  setUsers(data || []);
4093
4093
  } catch (err) {
4094
- const message28 = err instanceof Error ? err.message : "Failed to fetch users";
4095
- setError(message28);
4094
+ const message30 = err instanceof Error ? err.message : "Failed to fetch users";
4095
+ setError(message30);
4096
4096
  } finally {
4097
4097
  setIsLoading(false);
4098
4098
  }
@@ -4338,7 +4338,7 @@ var import_react108 = __toESM(require("react"));
4338
4338
  var import_client_sdk8 = require("@axiom-lattice/client-sdk");
4339
4339
 
4340
4340
  // src/components/Chat/WorkspaceResourceManager.tsx
4341
- var import_react107 = require("react");
4341
+ var import_react107 = __toESM(require("react"));
4342
4342
  var import_lucide_react37 = require("lucide-react");
4343
4343
  var import_antd97 = require("antd");
4344
4344
 
@@ -5470,7 +5470,8 @@ var Menu = ({
5470
5470
  onCollapsedChange,
5471
5471
  className,
5472
5472
  style,
5473
- footer
5473
+ footer,
5474
+ getContextMenuItems
5474
5475
  }) => {
5475
5476
  const { styles: menuStyles } = useMenuStyles();
5476
5477
  const { styles } = useStyle();
@@ -5537,17 +5538,21 @@ var Menu = ({
5537
5538
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: styles.iconModeLogo, children: logo || logoIcon || /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react6.Cpu, { size: 28, color: "#1677ff" }) }),
5538
5539
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: styles.iconModeContainer, children: groupedItems.map(({ group, items: items2 }, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react22.default.Fragment, { children: [
5539
5540
  groupIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: styles.iconModeDivider }),
5540
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: styles.iconModeGroup, children: items2.map((item) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5541
- "button",
5542
- {
5543
- className: styles.iconModeButton,
5544
- onClick: () => handleItemClick(item),
5545
- title: item.name,
5546
- "aria-label": item.name,
5547
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%" }, children: item.icon })
5548
- },
5549
- item.id
5550
- )) })
5541
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: styles.iconModeGroup, children: items2.map((item) => {
5542
+ const ctxItems = getContextMenuItems?.(item);
5543
+ const btn = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5544
+ "button",
5545
+ {
5546
+ className: styles.iconModeButton,
5547
+ onClick: () => handleItemClick(item),
5548
+ title: item.name,
5549
+ "aria-label": item.name,
5550
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%" }, children: item.icon })
5551
+ },
5552
+ item.id
5553
+ );
5554
+ return ctxItems ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_antd6.Dropdown, { menu: { items: ctxItems }, trigger: ["contextMenu"], children: btn }, item.id) : btn;
5555
+ }) })
5551
5556
  ] }, group || `group-${groupIndex}`)) }),
5552
5557
  showToggle && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5553
5558
  "button",
@@ -5586,22 +5591,25 @@ var Menu = ({
5586
5591
  items2.map((item) => {
5587
5592
  const isInline = item.inline ?? false;
5588
5593
  const isExpanded2 = expandedInlineDrawers.has(item.id);
5594
+ const ctxItems = getContextMenuItems?.(item);
5595
+ const btn = /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5596
+ "button",
5597
+ {
5598
+ className: `${styles.menuItemExpanded} ${isCollapsed ? "collapsed" : ""} ${isExpanded2 ? styles.menuItemExpandedActive : ""}`,
5599
+ onClick: () => handleItemClick(item),
5600
+ title: isCollapsed ? item.name : void 0,
5601
+ "aria-label": item.name,
5602
+ "aria-expanded": isExpanded2,
5603
+ children: [
5604
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "menu-icon", children: item.icon }),
5605
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "menu-label", children: item.name }),
5606
+ isInline && !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: styles.inlineDrawerIndicator, children: isExpanded2 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react6.ChevronDown, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react6.ChevronRight, { size: 16 }) })
5607
+ ]
5608
+ }
5609
+ );
5610
+ const buttonWithContextMenu = ctxItems ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_antd6.Dropdown, { menu: { items: ctxItems }, trigger: ["contextMenu"], children: btn }, item.id) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react22.default.Fragment, { children: btn }, item.id);
5589
5611
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react22.default.Fragment, { children: [
5590
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5591
- "button",
5592
- {
5593
- className: `${styles.menuItemExpanded} ${isCollapsed ? "collapsed" : ""} ${isExpanded2 ? styles.menuItemExpandedActive : ""}`,
5594
- onClick: () => handleItemClick(item),
5595
- title: isCollapsed ? item.name : void 0,
5596
- "aria-label": item.name,
5597
- "aria-expanded": isExpanded2,
5598
- children: [
5599
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "menu-icon", children: item.icon }),
5600
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "menu-label", children: item.name }),
5601
- isInline && !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: styles.inlineDrawerIndicator, children: isExpanded2 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react6.ChevronDown, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react6.ChevronRight, { size: 16 }) })
5602
- ]
5603
- }
5604
- ),
5612
+ buttonWithContextMenu,
5605
5613
  isInline && isExpanded2 && !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: styles.inlineDrawerContainer, children: item.content })
5606
5614
  ] }, item.id);
5607
5615
  })
@@ -5764,7 +5772,8 @@ function encodeId(contentType, contentConfig, itemId) {
5764
5772
  const projectId = contentConfig.projectId || "";
5765
5773
  const assistantId = contentConfig.assistantId || "";
5766
5774
  const fileName = encodeURIComponent(contentConfig.fileName || "");
5767
- return `${CUSTOM_MENU_ID_PREFIX}:file:${resourcePath}:${workspaceId}:${projectId}:${assistantId}:${fileName}:${itemId}`;
5775
+ const tenantId = contentConfig.tenantId || "";
5776
+ return `${CUSTOM_MENU_ID_PREFIX}:file:${resourcePath}:${workspaceId}:${projectId}:${assistantId}:${fileName}:${tenantId}:${itemId}`;
5768
5777
  }
5769
5778
  return `${CUSTOM_MENU_ID_PREFIX}:unknown::${itemId}`;
5770
5779
  }
@@ -5783,14 +5792,23 @@ function parseCustomMenuId(id) {
5783
5792
  };
5784
5793
  }
5785
5794
  if (contentType === "file" && parts.length >= 8) {
5795
+ const payload = decodeURIComponent(parts[2]);
5796
+ const workspaceId = parts[3] ?? void 0;
5797
+ const projectId = parts[4] ?? void 0;
5798
+ const assistantId = parts[5] ?? void 0;
5799
+ const fileName = decodeURIComponent(parts[6] || "");
5800
+ const hasTenantId = parts.length >= 9;
5801
+ const tenantId = hasTenantId ? parts[7] ?? void 0 : void 0;
5802
+ const dbId = parts.slice(hasTenantId ? 8 : 7).join(":");
5786
5803
  return {
5787
5804
  contentType,
5788
- payload: decodeURIComponent(parts[2]),
5789
- workspaceId: parts[3] ?? void 0,
5790
- projectId: parts[4] ?? void 0,
5791
- assistantId: parts[5] ?? void 0,
5792
- fileName: decodeURIComponent(parts[6] || ""),
5793
- dbId: parts.slice(7).join(":")
5805
+ payload,
5806
+ workspaceId,
5807
+ projectId,
5808
+ assistantId,
5809
+ fileName,
5810
+ tenantId,
5811
+ dbId
5794
5812
  };
5795
5813
  }
5796
5814
  return {
@@ -5800,7 +5818,8 @@ function parseCustomMenuId(id) {
5800
5818
  };
5801
5819
  }
5802
5820
  function menuItemToSideMenuItemConfig(item) {
5803
- const encodedId = encodeId(item.contentType, item.contentConfig, item.id);
5821
+ const contentConfigWithTenant = item.tenantId ? { ...item.contentConfig, tenantId: item.tenantId } : item.contentConfig;
5822
+ const encodedId = encodeId(item.contentType, contentConfigWithTenant, item.id);
5804
5823
  return {
5805
5824
  id: encodedId,
5806
5825
  type: "route",
@@ -13451,11 +13470,11 @@ var ConfirmFeedback = ({
13451
13470
  data,
13452
13471
  interactive = true
13453
13472
  }) => {
13454
- const { message: message28, type, config, feedback, options } = data ?? {};
13473
+ const { message: message30, type, config, feedback, options } = data ?? {};
13455
13474
  const { sendMessage } = useAgentChat();
13456
13475
  const [clicked, setClicked] = (0, import_react42.useState)(false);
13457
13476
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_antd23.Space, { direction: "vertical", style: { width: "100%" }, children: [
13458
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(MDResponse, { content: message28 }),
13477
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(MDResponse, { content: message30 }),
13459
13478
  options ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_antd23.Space, { style: { justifyContent: "flex-end", width: "100%" }, children: options?.map((option) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
13460
13479
  import_antd23.Button,
13461
13480
  {
@@ -13778,7 +13797,7 @@ var useSideAppOpener = () => {
13778
13797
  var import_jsx_runtime33 = require("react/jsx-runtime");
13779
13798
  var { Text: Text16 } = import_antd25.Typography;
13780
13799
  var GenericDataTable = ({ data, interactive = true, default_open_in_side_app = true }) => {
13781
- const { dataSource, message: message28 } = data ?? {};
13800
+ const { dataSource, message: message30 } = data ?? {};
13782
13801
  const [expandedRowKeys, setExpandedRowKeys] = (0, import_react45.useState)([]);
13783
13802
  const openSideApp = useSideAppOpener();
13784
13803
  const processedData = dataSource?.map((item, index) => ({
@@ -13877,7 +13896,7 @@ var GenericDataTable = ({ data, interactive = true, default_open_in_side_app = t
13877
13896
  type: "text/csv;charset=utf-8;"
13878
13897
  });
13879
13898
  const link = document.createElement("a");
13880
- const filename = `${message28 || "data"}_${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`;
13899
+ const filename = `${message30 || "data"}_${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`;
13881
13900
  link.href = URL.createObjectURL(blob);
13882
13901
  link.download = filename;
13883
13902
  document.body.appendChild(link);
@@ -13898,7 +13917,7 @@ var GenericDataTable = ({ data, interactive = true, default_open_in_side_app = t
13898
13917
  tableLayout: "fixed",
13899
13918
  style: { width: "100% !important" },
13900
13919
  title: () => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_antd25.Flex, { justify: "space-between", align: "center", children: [
13901
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_antd25.Space, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Text16, { strong: true, style: { fontSize: 16 }, children: message28 || "" }) }),
13920
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_antd25.Space, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Text16, { strong: true, style: { fontSize: 16 }, children: message30 || "" }) }),
13902
13921
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_antd25.Space, { children: [
13903
13922
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13904
13923
  import_antd25.Button,
@@ -13920,8 +13939,8 @@ var GenericDataTable = ({ data, interactive = true, default_open_in_side_app = t
13920
13939
  onClick: () => {
13921
13940
  openSideApp({
13922
13941
  component_key: "generic_data_table",
13923
- message: message28 || "",
13924
- data: { dataSource, message: message28 }
13942
+ message: message30 || "",
13943
+ data: { dataSource, message: message30 }
13925
13944
  });
13926
13945
  },
13927
13946
  children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_icons12.ExpandAltOutlined, {})
@@ -16528,7 +16547,7 @@ var import_ErrorBoundary = __toESM(require("antd/es/alert/ErrorBoundary"));
16528
16547
  var import_react54 = require("react");
16529
16548
  var import_jsx_runtime53 = require("react/jsx-runtime");
16530
16549
  var LazyBubble = ({
16531
- message: message28,
16550
+ message: message30,
16532
16551
  renderContent,
16533
16552
  autoLoadRightPanel
16534
16553
  }) => {
@@ -16559,10 +16578,10 @@ var LazyBubble = ({
16559
16578
  autoLoadRightPanel?.();
16560
16579
  }, []);
16561
16580
  const getPlaceholder = () => {
16562
- const estimatedHeight = message28.content ? Math.min(100, message28.content.length / 5) : 100;
16581
+ const estimatedHeight = message30.content ? Math.min(100, message30.content.length / 5) : 100;
16563
16582
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { style: { height: `${estimatedHeight}px`, minHeight: "50px" } });
16564
16583
  };
16565
- return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_ErrorBoundary.default, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { ref, style: { width: "100%" }, children: isVisible || wasEverVisible ? renderContent(message28) : getPlaceholder() }) });
16584
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_ErrorBoundary.default, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { ref, style: { width: "100%" }, children: isVisible || wasEverVisible ? renderContent(message30) : getPlaceholder() }) });
16566
16585
  };
16567
16586
  var MemoizedBubbleList = (0, import_react54.memo)(
16568
16587
  ({
@@ -16593,8 +16612,8 @@ var MessageList = ({
16593
16612
  messageLengthRef.current = messages?.length;
16594
16613
  }
16595
16614
  }, [messages?.length]);
16596
- const renderContent = (0, import_react54.useCallback)((message28) => {
16597
- const { content } = message28;
16615
+ const renderContent = (0, import_react54.useCallback)((message30) => {
16616
+ const { content } = message30;
16598
16617
  try {
16599
16618
  const json = JSON.parse(content);
16600
16619
  if (json.action && json.message) {
@@ -16602,7 +16621,7 @@ var MessageList = ({
16602
16621
  }
16603
16622
  } catch (error) {
16604
16623
  }
16605
- const tool_calls_md = message28.tool_calls?.map((tool_call) => {
16624
+ const tool_calls_md = message30.tool_calls?.map((tool_call) => {
16606
16625
  return `\`\`\`tool_call
16607
16626
  ${JSON.stringify(tool_call)}
16608
16627
  \`\`\``;
@@ -16611,17 +16630,17 @@ ${JSON.stringify(tool_call)}
16611
16630
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_antd40.Space, { direction: "vertical", style: { width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(MDResponse, { content: content_md }) });
16612
16631
  }, []);
16613
16632
  const items = (0, import_react54.useMemo)(
16614
- () => messages.map((message28, index) => ({
16615
- key: message28.id,
16616
- role: message28.role,
16633
+ () => messages.map((message30, index) => ({
16634
+ key: message30.id,
16635
+ role: message30.role,
16617
16636
  typing: false,
16618
16637
  content: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
16619
16638
  LazyBubble,
16620
16639
  {
16621
- message: message28,
16640
+ message: message30,
16622
16641
  renderContent,
16623
16642
  autoLoadRightPanel: () => {
16624
- const { content, role: role2 } = message28;
16643
+ const { content, role: role2 } = message30;
16625
16644
  const isNewAddedMessage = messageLengthRef.current > 1 && messageLengthRef.current + 1 === messages.length;
16626
16645
  if (index === messages.length - 1 && isNewAddedMessage && role2 === "ai") {
16627
16646
  try {
@@ -22997,12 +23016,12 @@ var formatDate3 = (timestamp) => {
22997
23016
  });
22998
23017
  };
22999
23018
  var MailboxDetailModal = ({
23000
- message: message28,
23019
+ message: message30,
23001
23020
  visible,
23002
23021
  onClose
23003
23022
  }) => {
23004
23023
  const { styles } = useStyles14();
23005
- if (!message28) return null;
23024
+ if (!message30) return null;
23006
23025
  return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
23007
23026
  import_antd71.Modal,
23008
23027
  {
@@ -23025,12 +23044,12 @@ var MailboxDetailModal = ({
23025
23044
  children: /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.modalContent, children: [
23026
23045
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.header, children: [
23027
23046
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: styles.messageIcon, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react24.Mail, { size: 20 }) }),
23028
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text36, { className: styles.messageId, children: message28.id }),
23029
- !message28.read && /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_antd71.Tag, { color: "red", children: "Unread" })
23047
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text36, { className: styles.messageId, children: message30.id }),
23048
+ !message30.read && /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_antd71.Tag, { color: "red", children: "Unread" })
23030
23049
  ] }),
23031
23050
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Title12, { level: 4, className: styles.title, children: [
23032
23051
  "Message from ",
23033
- message28.from
23052
+ message30.from
23034
23053
  ] }),
23035
23054
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.metaSection, children: [
23036
23055
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.metaItem, children: [
@@ -23040,11 +23059,11 @@ var MailboxDetailModal = ({
23040
23059
  "div",
23041
23060
  {
23042
23061
  className: styles.avatar,
23043
- style: { background: getAvatarColor7(message28.from) },
23044
- children: getInitials7(message28.from)
23062
+ style: { background: getAvatarColor7(message30.from) },
23063
+ children: getInitials7(message30.from)
23045
23064
  }
23046
23065
  ),
23047
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: message28.from })
23066
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: message30.from })
23048
23067
  ] })
23049
23068
  ] }),
23050
23069
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.metaItem, children: [
@@ -23054,29 +23073,29 @@ var MailboxDetailModal = ({
23054
23073
  "div",
23055
23074
  {
23056
23075
  className: styles.avatar,
23057
- style: { background: getAvatarColor7(message28.to) },
23058
- children: getInitials7(message28.to)
23076
+ style: { background: getAvatarColor7(message30.to) },
23077
+ children: getInitials7(message30.to)
23059
23078
  }
23060
23079
  ),
23061
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: message28.to })
23080
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: message30.to })
23062
23081
  ] })
23063
23082
  ] }),
23064
23083
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.metaItem, children: [
23065
23084
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text36, { className: styles.metaLabel, children: "Time" }),
23066
23085
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.metaValue, children: [
23067
23086
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react24.Calendar, { size: 14 }),
23068
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: formatDate3(message28.timestamp) })
23087
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: formatDate3(message30.timestamp) })
23069
23088
  ] })
23070
23089
  ] }),
23071
23090
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.metaItem, children: [
23072
23091
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text36, { className: styles.metaLabel, children: "Type" }),
23073
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: styles.metaValue, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_antd71.Tag, { color: message28.type === "broadcast" ? "blue" : "default", children: message28.type }) })
23092
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: styles.metaValue, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_antd71.Tag, { color: message30.type === "broadcast" ? "blue" : "default", children: message30.type }) })
23074
23093
  ] })
23075
23094
  ] }),
23076
23095
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_antd71.Divider, {}),
23077
23096
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: styles.contentSection, children: [
23078
23097
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text36, { className: styles.contentLabel, children: "Message Content" }),
23079
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: styles.contentBox, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Paragraph2, { className: styles.messageContent, children: message28.content }) })
23098
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: styles.contentBox, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Paragraph2, { className: styles.messageContent, children: message30.content }) })
23080
23099
  ] })
23081
23100
  ] })
23082
23101
  }
@@ -23305,26 +23324,26 @@ var MessageGroupComponent = ({ group, styles, defaultExpanded = true, onMessageC
23305
23324
  ]
23306
23325
  }
23307
23326
  ),
23308
- isExpanded && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.listGroupContent, children: group.messages.map((message28) => /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
23327
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.listGroupContent, children: group.messages.map((message30) => /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
23309
23328
  "div",
23310
23329
  {
23311
- className: `${styles.listItem} ${!message28.read ? styles.listItemUnread : ""}`,
23312
- onClick: () => onMessageClick(message28),
23330
+ className: `${styles.listItem} ${!message30.read ? styles.listItemUnread : ""}`,
23331
+ onClick: () => onMessageClick(message30),
23313
23332
  children: [
23314
- /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.listItemIcon, children: !message28.read ? /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.unreadBadge }) : /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react25.Circle, { size: 8, style: { color: "#d9d9d9" } }) }),
23333
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.listItemIcon, children: !message30.read ? /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.unreadBadge }) : /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react25.Circle, { size: 8, style: { color: "#d9d9d9" } }) }),
23315
23334
  /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: styles.listItemContent, children: [
23316
- /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: styles.listItemPreview, children: getMessagePreview(message28.content) }),
23335
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: styles.listItemPreview, children: getMessagePreview(message30.content) }),
23317
23336
  /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("span", { className: styles.listItemMeta, children: [
23318
23337
  "To: ",
23319
- message28.to,
23338
+ message30.to,
23320
23339
  " \u2022 ",
23321
- message28.type
23340
+ message30.type
23322
23341
  ] })
23323
23342
  ] }),
23324
- /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.listItemRight, children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: styles.listItemDate, children: formatDate4(message28.timestamp) }) })
23343
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: styles.listItemRight, children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: styles.listItemDate, children: formatDate4(message30.timestamp) }) })
23325
23344
  ]
23326
23345
  },
23327
- message28.id
23346
+ message30.id
23328
23347
  )) })
23329
23348
  ] });
23330
23349
  };
@@ -23335,12 +23354,12 @@ var MailboxPanel = ({ data }) => {
23335
23354
  const { teamMailbox = [] } = data || {};
23336
23355
  const messageGroups = (0, import_react73.useMemo)(() => {
23337
23356
  const groupsMap = /* @__PURE__ */ new Map();
23338
- teamMailbox.forEach((message28) => {
23339
- const sender = message28.from;
23357
+ teamMailbox.forEach((message30) => {
23358
+ const sender = message30.from;
23340
23359
  if (!groupsMap.has(sender)) {
23341
23360
  groupsMap.set(sender, []);
23342
23361
  }
23343
- groupsMap.get(sender).push(message28);
23362
+ groupsMap.get(sender).push(message30);
23344
23363
  });
23345
23364
  const groups = Array.from(groupsMap.entries()).map(([sender, messages]) => ({
23346
23365
  sender,
@@ -23357,8 +23376,8 @@ var MailboxPanel = ({ data }) => {
23357
23376
  return groups;
23358
23377
  }, [teamMailbox]);
23359
23378
  const totalUnread = teamMailbox.filter((m) => !m.read).length;
23360
- const handleMessageClick = (message28) => {
23361
- setSelectedMessage(message28);
23379
+ const handleMessageClick = (message30) => {
23380
+ setSelectedMessage(message30);
23362
23381
  setModalVisible(true);
23363
23382
  };
23364
23383
  const handleCloseModal = () => {
@@ -25202,11 +25221,11 @@ var StreamingHTMLRenderer = ({
25202
25221
  if (!iframe || event.source !== iframe.contentWindow) {
25203
25222
  return;
25204
25223
  }
25205
- const message28 = event.data;
25206
- if (!message28 || typeof message28 !== "object") {
25224
+ const message30 = event.data;
25225
+ if (!message30 || typeof message30 !== "object") {
25207
25226
  return;
25208
25227
  }
25209
- switch (message28.type) {
25228
+ switch (message30.type) {
25210
25229
  case "iframe-ready":
25211
25230
  console.log("[StreamingHTMLRenderer] Iframe ready");
25212
25231
  isReadyRef.current = true;
@@ -25221,26 +25240,26 @@ var StreamingHTMLRenderer = ({
25221
25240
  }
25222
25241
  break;
25223
25242
  case "iframe-height":
25224
- if (typeof message28.height === "number" && message28.height > 0) {
25225
- setIframeHeight(message28.height);
25243
+ if (typeof message30.height === "number" && message30.height > 0) {
25244
+ setIframeHeight(message30.height);
25226
25245
  }
25227
25246
  break;
25228
25247
  case "iframe-error": {
25229
25248
  const streamingError = {
25230
25249
  type: "RENDER_ERROR",
25231
- message: message28.error || "Unknown iframe error"
25250
+ message: message30.error || "Unknown iframe error"
25232
25251
  };
25233
25252
  onError?.(streamingError);
25234
25253
  break;
25235
25254
  }
25236
25255
  case "widget-prompt":
25237
- if (typeof message28.text === "string") {
25238
- onPrompt?.(message28.text);
25256
+ if (typeof message30.text === "string") {
25257
+ onPrompt?.(message30.text);
25239
25258
  }
25240
25259
  break;
25241
25260
  case "widget-open-link":
25242
- if (typeof message28.url === "string") {
25243
- window.open(message28.url, "_blank", "noopener,noreferrer");
25261
+ if (typeof message30.url === "string") {
25262
+ window.open(message30.url, "_blank", "noopener,noreferrer");
25244
25263
  }
25245
25264
  break;
25246
25265
  }
@@ -28355,8 +28374,8 @@ var TopologyRuntimeView = () => {
28355
28374
  setRuns((prev) => prev.filter((r) => r.id !== runId));
28356
28375
  setSelectedRun((prev) => prev?.id === runId ? null : prev);
28357
28376
  } catch (err) {
28358
- const message28 = err instanceof Error ? err.message : "Failed to delete run";
28359
- setError(message28);
28377
+ const message30 = err instanceof Error ? err.message : "Failed to delete run";
28378
+ setError(message30);
28360
28379
  }
28361
28380
  }, [del]);
28362
28381
  const refreshRuns = (0, import_react91.useCallback)(async () => {
@@ -31206,6 +31225,7 @@ var PersonalAssistantChannelModal = ({
31206
31225
  const [selectedId, setSelectedId] = (0, import_react102.useState)(null);
31207
31226
  const [formMode, setFormMode] = (0, import_react102.useState)("view");
31208
31227
  const [selectedChannel, setSelectedChannel] = (0, import_react102.useState)("lark");
31228
+ const [hasPickedChannel, setHasPickedChannel] = (0, import_react102.useState)(false);
31209
31229
  const [form] = import_antd91.Form.useForm();
31210
31230
  const [allowOthers, setAllowOthers] = (0, import_react102.useState)(true);
31211
31231
  const [saving, setSaving] = (0, import_react102.useState)(false);
@@ -31220,6 +31240,7 @@ var PersonalAssistantChannelModal = ({
31220
31240
  const [qrCountdown, setQrCountdown] = (0, import_react102.useState)(0);
31221
31241
  const qrCodeRef = (0, import_react102.useRef)(null);
31222
31242
  const qrCountdownRef = (0, import_react102.useRef)(null);
31243
+ const bindingsLoadIdRef = (0, import_react102.useRef)(null);
31223
31244
  const clearQrCountdown = (0, import_react102.useCallback)(() => {
31224
31245
  if (qrCountdownRef.current) {
31225
31246
  clearInterval(qrCountdownRef.current);
@@ -31318,19 +31339,25 @@ var PersonalAssistantChannelModal = ({
31318
31339
  }
31319
31340
  }, [get]);
31320
31341
  const loadBindings = (0, import_react102.useCallback)(async (installationId) => {
31342
+ const requestId = installationId;
31343
+ bindingsLoadIdRef.current = requestId;
31321
31344
  setBindingsLoading(true);
31322
31345
  try {
31323
31346
  const res = await get(`/api/channel-bindings?channelInstallationId=${installationId}`);
31347
+ if (bindingsLoadIdRef.current !== requestId) return;
31324
31348
  if (res.success) {
31325
31349
  setBindings(res.data?.records ?? []);
31326
31350
  } else {
31327
31351
  import_antd91.message.error(res.message || "Failed to load bindings");
31328
31352
  }
31329
31353
  } catch (error) {
31354
+ if (bindingsLoadIdRef.current !== requestId) return;
31330
31355
  console.error("Failed to load bindings:", error);
31331
31356
  import_antd91.message.error("Failed to load bindings");
31332
31357
  } finally {
31333
- setBindingsLoading(false);
31358
+ if (bindingsLoadIdRef.current === requestId) {
31359
+ setBindingsLoading(false);
31360
+ }
31334
31361
  }
31335
31362
  }, [get]);
31336
31363
  (0, import_react102.useEffect)(() => {
@@ -31343,6 +31370,7 @@ var PersonalAssistantChannelModal = ({
31343
31370
  setSelectedId(null);
31344
31371
  setFormMode("view");
31345
31372
  setSelectedChannel("lark");
31373
+ setHasPickedChannel(false);
31346
31374
  form.resetFields();
31347
31375
  bindingForm.resetFields();
31348
31376
  setBindings([]);
@@ -31376,7 +31404,8 @@ var PersonalAssistantChannelModal = ({
31376
31404
  setSelectedId(installation.id);
31377
31405
  setFormMode("edit");
31378
31406
  setSelectedChannel(installation.channel);
31379
- setAllowOthers(!installation.rejectWhenNoBinding);
31407
+ setAllowOthers(installation.channel === "wechat" ? true : !installation.rejectWhenNoBinding);
31408
+ form.resetFields();
31380
31409
  if (installation.channel === "lark") {
31381
31410
  form.setFieldsValue({
31382
31411
  name: installation.name,
@@ -31398,6 +31427,7 @@ var PersonalAssistantChannelModal = ({
31398
31427
  setSelectedId(null);
31399
31428
  setFormMode("create");
31400
31429
  setSelectedChannel("lark");
31430
+ setHasPickedChannel(false);
31401
31431
  setBindings([]);
31402
31432
  setAllowOthers(true);
31403
31433
  form.resetFields();
@@ -31410,6 +31440,7 @@ var PersonalAssistantChannelModal = ({
31410
31440
  setSelectedId(null);
31411
31441
  }
31412
31442
  setFormMode("view");
31443
+ setHasPickedChannel(false);
31413
31444
  setBindings([]);
31414
31445
  form.resetFields();
31415
31446
  bindingForm.resetFields();
@@ -31495,7 +31526,7 @@ var PersonalAssistantChannelModal = ({
31495
31526
  const res = await post(
31496
31527
  "/api/channel-installations",
31497
31528
  {
31498
- channel: "lark",
31529
+ channel: selectedChannel,
31499
31530
  name: values.name,
31500
31531
  config,
31501
31532
  rejectWhenNoBinding: !allowOthers,
@@ -31508,6 +31539,7 @@ var PersonalAssistantChannelModal = ({
31508
31539
  setSelectedId(res.data.id);
31509
31540
  setFormMode("edit");
31510
31541
  setBindings([]);
31542
+ loadInstallations();
31511
31543
  import_antd91.message.info("Now add users who can access this channel");
31512
31544
  } else {
31513
31545
  cancelForm();
@@ -31608,20 +31640,54 @@ var PersonalAssistantChannelModal = ({
31608
31640
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Text49, { style: { fontSize: 14, color: "var(--color-text-secondary, #666)" }, children: "Select a channel from the left or add a new one" })
31609
31641
  ] });
31610
31642
  }
31643
+ if (formMode === "create" && !hasPickedChannel) {
31644
+ return /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { className: styles.rightBody, children: [
31645
+ /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { className: styles.formSectionTitle, children: "Choose Channel Type" }),
31646
+ /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: channelOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)(
31647
+ "div",
31648
+ {
31649
+ onClick: () => {
31650
+ setSelectedChannel(opt.value);
31651
+ setHasPickedChannel(true);
31652
+ form.resetFields();
31653
+ },
31654
+ style: {
31655
+ padding: "16px 20px",
31656
+ border: "2px solid var(--color-border, #d9d9d9)",
31657
+ borderRadius: 12,
31658
+ cursor: "pointer",
31659
+ display: "flex",
31660
+ alignItems: "center",
31661
+ gap: 12,
31662
+ transition: "border-color 0.2s, background 0.2s"
31663
+ },
31664
+ onMouseEnter: (e) => {
31665
+ e.currentTarget.style.borderColor = "var(--color-primary, #6366f1)";
31666
+ e.currentTarget.style.background = "var(--color-primary-bg, #f0f0ff)";
31667
+ },
31668
+ onMouseLeave: (e) => {
31669
+ e.currentTarget.style.borderColor = "var(--color-border, #d9d9d9)";
31670
+ e.currentTarget.style.background = "transparent";
31671
+ },
31672
+ children: [
31673
+ /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_lucide_react35.MessagesSquare, { size: 22, style: { opacity: 0.6 } }),
31674
+ /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Text49, { strong: true, style: { fontSize: 14 }, children: opt.label }) })
31675
+ ]
31676
+ },
31677
+ opt.value
31678
+ )) })
31679
+ ] });
31680
+ }
31611
31681
  const isEdit = formMode === "edit" && selectedId !== null;
31612
31682
  return /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { className: styles.rightBody, children: [
31613
- /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { className: styles.formSectionTitle, children: isEdit ? "Edit Channel" : "New Channel" }),
31683
+ /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { className: styles.formSectionTitle, children: isEdit ? "Edit Channel" : `New ${CHANNEL_LABEL_MAP[selectedChannel] || selectedChannel}` }),
31614
31684
  /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)(import_antd91.Form, { form, layout: "vertical", children: [
31615
- /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Form.Item, { label: "Channel Type", children: /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
31685
+ isEdit && /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Form.Item, { label: "Channel Type", children: /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
31616
31686
  import_antd91.Select,
31617
31687
  {
31618
31688
  options: channelOptions,
31619
31689
  value: selectedChannel,
31620
- onChange: (val) => {
31621
- setSelectedChannel(val);
31622
- form.resetFields();
31623
- },
31624
- disabled: isEdit
31690
+ disabled: true
31625
31691
  }
31626
31692
  ) }),
31627
31693
  selectedChannel === "lark" && /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)(import_jsx_runtime119.Fragment, { children: [
@@ -31766,7 +31832,7 @@ var PersonalAssistantChannelModal = ({
31766
31832
  }
31767
31833
  )
31768
31834
  ] }),
31769
- /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 16 }, children: [
31835
+ selectedChannel !== "wechat" && /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 16 }, children: [
31770
31836
  /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { children: [
31771
31837
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Text49, { strong: true, style: { fontSize: 14 }, children: "Anyone can message" }),
31772
31838
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("br", {}),
@@ -31775,7 +31841,7 @@ var PersonalAssistantChannelModal = ({
31775
31841
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Switch, { checked: allowOthers, onChange: setAllowOthers })
31776
31842
  ] })
31777
31843
  ] }),
31778
- !allowOthers && isEdit && /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { className: styles.bindingSection, children: [
31844
+ selectedChannel !== "wechat" && !allowOthers && isEdit && /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { className: styles.bindingSection, children: [
31779
31845
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { className: styles.bindingHeader, children: "Who can chat" }),
31780
31846
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { className: styles.bindingHint, children: "Only people you add here can message your assistant" }),
31781
31847
  bindingsLoading ? /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Spin, { size: "small", style: { display: "block", margin: "12px 0" } }) : bindings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Text49, { type: "secondary", style: { fontSize: 12 }, children: "No contacts added yet" }) : /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: bindings.map((b) => /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { className: styles.contactItem, children: [
@@ -31809,7 +31875,7 @@ var PersonalAssistantChannelModal = ({
31809
31875
  name: "senderId",
31810
31876
  rules: [{ required: true, message: "Required" }],
31811
31877
  style: { flex: 1, marginBottom: 0 },
31812
- children: /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Input, { size: "small", placeholder: "Feishu Open ID" })
31878
+ children: /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Input, { size: "small", placeholder: selectedChannel === "wechat" ? "WeChat Open ID" : "Feishu Open ID" })
31813
31879
  }
31814
31880
  ),
31815
31881
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
@@ -31847,12 +31913,13 @@ var PersonalAssistantChannelModal = ({
31847
31913
  }
31848
31914
  )
31849
31915
  ] }),
31850
- !allowOthers && !isEdit && /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { className: styles.bindingSection, children: /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Text49, { type: "secondary", style: { fontSize: 12 }, children: "Once saved, come back here to add the people you'd like to chat with." }) })
31916
+ selectedChannel !== "wechat" && !allowOthers && !isEdit && /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { className: styles.bindingSection, children: /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Text49, { type: "secondary", style: { fontSize: 12 }, children: "Once saved, come back here to add the people you'd like to chat with." }) })
31851
31917
  ] });
31852
31918
  };
31853
31919
  const renderFooter = () => {
31854
31920
  if (formMode === "view") return null;
31855
31921
  const isEdit = formMode === "edit" && selectedId !== null;
31922
+ const isPicking = formMode === "create" && !hasPickedChannel;
31856
31923
  return /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { className: styles.footer, children: [
31857
31924
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)("div", { children: isEdit && /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
31858
31925
  import_antd91.Popconfirm,
@@ -31868,7 +31935,7 @@ var PersonalAssistantChannelModal = ({
31868
31935
  ) }),
31869
31936
  /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
31870
31937
  /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Button, { onClick: cancelForm, children: "Cancel" }),
31871
- /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Button, { type: "primary", loading: saving, onClick: handleSave, children: isEdit ? "Save" : "Create" })
31938
+ !isPicking && /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(import_antd91.Button, { type: "primary", loading: saving, onClick: handleSave, children: isEdit ? "Save" : "Create" })
31872
31939
  ] })
31873
31940
  ] });
31874
31941
  };
@@ -32275,15 +32342,10 @@ var FileView = ({ parsed }) => {
32275
32342
  path: resourcePath.startsWith("/") ? resourcePath : `/${resourcePath}`
32276
32343
  });
32277
32344
  if (parsed.assistantId) params.set("assistantId", parsed.assistantId);
32345
+ if (parsed.tenantId) params.set("tenantId", parsed.tenantId);
32278
32346
  const url = `${baseURL}/api/workspaces/${parsed.workspaceId}/projects/${parsed.projectId}/viewfile?${params}`;
32279
- return /* @__PURE__ */ (0, import_jsx_runtime122.jsx)(
32280
- "iframe",
32281
- {
32282
- src: url,
32283
- style: IFRAME_STYLE,
32284
- title: parsed.fileName || resourcePath.split("/").pop() || "File"
32285
- }
32286
- );
32347
+ const fileName = parsed.fileName || resourcePath.split("/").pop() || "File";
32348
+ return /* @__PURE__ */ (0, import_jsx_runtime122.jsx)("div", { style: { width: "100%", height: "100%", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime122.jsx)(FileRenderer, { fileUrl: url, fileName, showHeader: true }) });
32287
32349
  };
32288
32350
  var CustomMenuPage = ({ component_key }) => {
32289
32351
  const parsed = parseCustomMenuId(component_key);
@@ -32989,19 +33051,27 @@ var WorkspaceResourceManager = ({
32989
33051
  const [tenantModalOpen, setTenantModalOpen] = (0, import_react107.useState)(false);
32990
33052
  const [changePasswordOpen, setChangePasswordOpen] = (0, import_react107.useState)(false);
32991
33053
  const [fetchedCustomMenuItems, setFetchedCustomMenuItems] = (0, import_react107.useState)([]);
32992
- (0, import_react107.useEffect)(() => {
32993
- const fetchCustomMenu = async () => {
32994
- try {
32995
- const json = await get("/api/menu-items?menuTarget=workspace");
32996
- if (json.success && json.data?.records) {
32997
- const items = json.data.records.map(menuItemToSideMenuItemConfig);
32998
- setFetchedCustomMenuItems(items);
32999
- }
33000
- } catch {
33054
+ const { unpin: unpinMenuItem } = usePinMenuActions();
33055
+ const fetchCustomMenu = (0, import_react107.useCallback)(async () => {
33056
+ try {
33057
+ const json = await get("/api/menu-items?menuTarget=workspace");
33058
+ if (json.success && json.data?.records) {
33059
+ const items = json.data.records.map(menuItemToSideMenuItemConfig);
33060
+ setFetchedCustomMenuItems(items);
33001
33061
  }
33002
- };
33003
- fetchCustomMenu();
33062
+ } catch {
33063
+ }
33004
33064
  }, [get]);
33065
+ (0, import_react107.useEffect)(() => {
33066
+ fetchCustomMenu();
33067
+ }, [fetchCustomMenu]);
33068
+ (0, import_react107.useEffect)(() => {
33069
+ const handler = () => {
33070
+ fetchCustomMenu();
33071
+ };
33072
+ window.addEventListener("menu:refresh", handler);
33073
+ return () => window.removeEventListener("menu:refresh", handler);
33074
+ }, [fetchCustomMenu]);
33005
33075
  (0, import_react107.useEffect)(() => {
33006
33076
  for (const item of fetchedCustomMenuItems) {
33007
33077
  regsiterElement(item.id, {
@@ -33018,6 +33088,31 @@ var WorkspaceResourceManager = ({
33018
33088
  const merged = fetchedCustomMenuItems.length > 0 ? mergeMenuItems(DEFAULT_WORKSPACE_MENU_ITEMS, fetchedCustomMenuItems) : [...DEFAULT_WORKSPACE_MENU_ITEMS];
33019
33089
  return merged;
33020
33090
  }, [config.workspaceMenuItems, fetchedCustomMenuItems]);
33091
+ const getContextMenuItems = (0, import_react107.useCallback)(
33092
+ (item) => {
33093
+ if (!item.id.startsWith(CUSTOM_MENU_ID_PREFIX)) return null;
33094
+ const parts = item.id.split(":");
33095
+ const dbId = parts[parts.length - 1];
33096
+ if (!dbId) return null;
33097
+ return [
33098
+ {
33099
+ key: "unpin",
33100
+ icon: import_react107.default.createElement(import_lucide_react37.PinOff, { size: 14 }),
33101
+ label: "Unpin from Menu",
33102
+ onClick: async () => {
33103
+ try {
33104
+ await unpinMenuItem(dbId);
33105
+ import_antd97.message.success("Unpinned from menu");
33106
+ window.dispatchEvent(new CustomEvent("menu:refresh"));
33107
+ } catch (err) {
33108
+ import_antd97.message.error(err.message || "Failed to unpin");
33109
+ }
33110
+ }
33111
+ }
33112
+ ];
33113
+ },
33114
+ [unpinMenuItem]
33115
+ );
33021
33116
  const handleItemClick = (item) => {
33022
33117
  if (item.type === "action" && item.builtin === "switch-tenant") {
33023
33118
  setTenantModalOpen(true);
@@ -33107,6 +33202,7 @@ var WorkspaceResourceManager = ({
33107
33202
  {
33108
33203
  items: menuItems,
33109
33204
  onItemClick: handleItemClick,
33205
+ getContextMenuItems,
33110
33206
  logo,
33111
33207
  logoText: workspaceName || "Workspace",
33112
33208
  showToggle: true,
@@ -33601,6 +33697,122 @@ var WorkspaceContextProvider = ({
33601
33697
 
33602
33698
  // src/components/Chat/PinToMenuButton.tsx
33603
33699
  var import_jsx_runtime127 = require("react/jsx-runtime");
33700
+ function buildPinContentConfig(params) {
33701
+ const { contentType, assistantId, contentConfig, workspaceId, projectId } = params;
33702
+ if (contentType === "agent") {
33703
+ return {
33704
+ agentId: assistantId,
33705
+ workspaceId: workspaceId || void 0,
33706
+ projectId: projectId || void 0
33707
+ };
33708
+ }
33709
+ return {
33710
+ ...contentConfig,
33711
+ workspaceId: workspaceId || void 0,
33712
+ projectId: projectId || void 0
33713
+ };
33714
+ }
33715
+ async function checkPinStatus(get, params, enableCustomMenu) {
33716
+ const { contentType, assistantId, contentConfig, isMatch } = params;
33717
+ const hasTarget = contentType === "agent" ? !!assistantId : !!contentConfig;
33718
+ if (!hasTarget || !enableCustomMenu) {
33719
+ return { isPinned: false, pinnedId: null };
33720
+ }
33721
+ const json = await get("/api/menu-items?menuTarget=workspace");
33722
+ if (json.success && json.data?.records) {
33723
+ const matchFn = isMatch ?? ((cfg) => contentType === "agent" && cfg.agentId === assistantId);
33724
+ const pinned = json.data.records.find(
33725
+ (item) => item.contentType === contentType && matchFn(item.contentConfig)
33726
+ );
33727
+ if (pinned) {
33728
+ return { isPinned: true, pinnedId: pinned.id };
33729
+ }
33730
+ }
33731
+ return { isPinned: false, pinnedId: null };
33732
+ }
33733
+ async function pinItem(post, params) {
33734
+ const { contentType, name, group } = params;
33735
+ const contentConfig = buildPinContentConfig(params);
33736
+ const body = {
33737
+ menuTarget: "workspace",
33738
+ contentType,
33739
+ contentConfig,
33740
+ group: group?.trim() || void 0,
33741
+ name: name.trim(),
33742
+ sortOrder: 0
33743
+ };
33744
+ const json = await post(
33745
+ "/api/menu-items",
33746
+ body
33747
+ );
33748
+ if (!json.success) {
33749
+ throw new Error(json.message || "Failed to pin");
33750
+ }
33751
+ return { id: json.data.id };
33752
+ }
33753
+ async function unpinItem(del, pinnedId) {
33754
+ const json = await del(`/api/menu-items/${pinnedId}`);
33755
+ if (!json.success) {
33756
+ throw new Error(json.message || "Failed to unpin");
33757
+ }
33758
+ }
33759
+ function usePinMenuActions() {
33760
+ const workspaceCtx = import_react109.default.useContext(WorkspaceContext);
33761
+ const { config } = useLatticeChatShellContext();
33762
+ const { post, get, del } = useApi();
33763
+ const enableCustomMenu = config.enableCustomMenu ?? false;
33764
+ const check = (0, import_react109.useCallback)(
33765
+ async (params) => checkPinStatus(get, params, enableCustomMenu),
33766
+ [get, enableCustomMenu]
33767
+ );
33768
+ const pin = (0, import_react109.useCallback)(
33769
+ async (params) => pinItem(post, {
33770
+ ...params,
33771
+ workspaceId: workspaceCtx?.workspaceId,
33772
+ projectId: workspaceCtx?.projectId
33773
+ }),
33774
+ [post, workspaceCtx]
33775
+ );
33776
+ const unpin = (0, import_react109.useCallback)(
33777
+ async (pinnedId) => unpinItem(del, pinnedId),
33778
+ [del]
33779
+ );
33780
+ return { check, pin, unpin, enableCustomMenu, get };
33781
+ }
33782
+ function extractGroups(items) {
33783
+ const groups = /* @__PURE__ */ new Set();
33784
+ for (const item of items) {
33785
+ if (item.group?.trim()) {
33786
+ groups.add(item.group.trim());
33787
+ }
33788
+ }
33789
+ return Array.from(groups).sort();
33790
+ }
33791
+ function useMenuGroups() {
33792
+ const { config } = useLatticeChatShellContext();
33793
+ const defaultGroups = import_react109.default.useMemo(() => extractGroups(DEFAULT_WORKSPACE_MENU_ITEMS), []);
33794
+ const configGroups = import_react109.default.useMemo(
33795
+ () => extractGroups(config.workspaceMenuItems ?? []),
33796
+ [config.workspaceMenuItems]
33797
+ );
33798
+ return import_react109.default.useMemo(
33799
+ () => Array.from(/* @__PURE__ */ new Set([...defaultGroups, ...configGroups])).sort(),
33800
+ [defaultGroups, configGroups]
33801
+ );
33802
+ }
33803
+ async function fetchExistingGroups(get) {
33804
+ const json = await get("/api/menu-items?menuTarget=workspace");
33805
+ if (json.success && json.data?.records) {
33806
+ const groups = /* @__PURE__ */ new Set();
33807
+ for (const item of json.data.records) {
33808
+ if (item.group?.trim()) {
33809
+ groups.add(item.group.trim());
33810
+ }
33811
+ }
33812
+ return Array.from(groups).sort();
33813
+ }
33814
+ return [];
33815
+ }
33604
33816
  var MODAL_BODY_STYLE = {
33605
33817
  display: "flex",
33606
33818
  flexDirection: "column",
@@ -33615,9 +33827,7 @@ var PinToMenuButton = ({
33615
33827
  label,
33616
33828
  block
33617
33829
  }) => {
33618
- const workspaceCtx = import_react109.default.useContext(WorkspaceContext);
33619
- const { config } = useLatticeChatShellContext();
33620
- const { post, get, del } = useApi();
33830
+ const { check, pin, unpin, enableCustomMenu, get } = usePinMenuActions();
33621
33831
  const [open, setOpen] = (0, import_react109.useState)(false);
33622
33832
  const [group, setGroup] = (0, import_react109.useState)("");
33623
33833
  const [name, setName] = (0, import_react109.useState)("");
@@ -33625,41 +33835,38 @@ var PinToMenuButton = ({
33625
33835
  const [isPinned, setIsPinned] = (0, import_react109.useState)(false);
33626
33836
  const [pinnedItemId, setPinnedItemId] = (0, import_react109.useState)(null);
33627
33837
  const [checking, setChecking] = (0, import_react109.useState)(true);
33838
+ const staticGroups = useMenuGroups();
33839
+ const [existingGroups, setExistingGroups] = (0, import_react109.useState)(staticGroups);
33840
+ (0, import_react109.useEffect)(() => {
33841
+ setExistingGroups(staticGroups);
33842
+ }, [staticGroups]);
33843
+ (0, import_react109.useEffect)(() => {
33844
+ if (open) {
33845
+ fetchExistingGroups(get).then((apiGroups) => {
33846
+ setExistingGroups(Array.from(/* @__PURE__ */ new Set([...staticGroups, ...apiGroups])).sort());
33847
+ });
33848
+ }
33849
+ }, [open]);
33628
33850
  const hasTarget = contentType === "agent" ? !!assistantId : !!externalContentConfig;
33629
33851
  const checkPinned = (0, import_react109.useCallback)(async () => {
33630
- if (!hasTarget || !config.enableCustomMenu) {
33852
+ if (!hasTarget || !enableCustomMenu) {
33631
33853
  setChecking(false);
33632
33854
  return;
33633
33855
  }
33634
33856
  setChecking(true);
33635
33857
  try {
33636
- const json = await get(
33637
- "/api/menu-items?menuTarget=workspace"
33638
- );
33639
- if (json.success && json.data?.records) {
33640
- const matchFn = isMatch ?? ((cfg) => {
33641
- return contentType === "agent" && cfg.agentId === assistantId;
33642
- });
33643
- const pinned = json.data.records.find(
33644
- (item) => item.contentType === contentType && matchFn(item.contentConfig)
33645
- );
33646
- if (pinned) {
33647
- setIsPinned(true);
33648
- setPinnedItemId(pinned.id);
33649
- } else {
33650
- setIsPinned(false);
33651
- setPinnedItemId(null);
33652
- }
33653
- }
33858
+ const result = await check({ contentType, assistantId, contentConfig: externalContentConfig, isMatch });
33859
+ setIsPinned(result.isPinned);
33860
+ setPinnedItemId(result.pinnedId);
33654
33861
  } catch {
33655
33862
  } finally {
33656
33863
  setChecking(false);
33657
33864
  }
33658
- }, [hasTarget, config.enableCustomMenu, get, contentType, assistantId, isMatch]);
33865
+ }, [hasTarget, enableCustomMenu, check, contentType, assistantId, externalContentConfig, isMatch]);
33659
33866
  (0, import_react109.useEffect)(() => {
33660
33867
  checkPinned();
33661
33868
  }, [checkPinned]);
33662
- if (!hasTarget || !config.enableCustomMenu) return null;
33869
+ if (!hasTarget || !enableCustomMenu) return null;
33663
33870
  const handlePin = async () => {
33664
33871
  if (!name.trim()) {
33665
33872
  import_antd98.message.warning("Please enter a menu name");
@@ -33667,35 +33874,22 @@ var PinToMenuButton = ({
33667
33874
  }
33668
33875
  setLoading(true);
33669
33876
  try {
33670
- const contentConfig = contentType === "agent" ? {
33671
- agentId: assistantId,
33672
- workspaceId: workspaceCtx?.workspaceId || void 0,
33673
- projectId: workspaceCtx?.projectId || void 0
33674
- } : {
33675
- ...externalContentConfig,
33676
- workspaceId: workspaceCtx?.workspaceId || void 0,
33677
- projectId: workspaceCtx?.projectId || void 0
33678
- };
33679
- const body = {
33680
- menuTarget: "workspace",
33877
+ await pin({
33681
33878
  contentType,
33682
- contentConfig,
33683
- group: group.trim() || void 0,
33879
+ assistantId,
33880
+ contentConfig: externalContentConfig,
33881
+ isMatch,
33684
33882
  name: name.trim(),
33685
- sortOrder: 0
33686
- };
33687
- const json = await post("/api/menu-items", body);
33688
- if (json.success) {
33689
- import_antd98.message.success("Pinned to menu");
33690
- setOpen(false);
33691
- setGroup("");
33692
- setName("");
33693
- checkPinned();
33694
- } else {
33695
- import_antd98.message.error(json.message || "Failed to pin");
33696
- }
33697
- } catch {
33698
- import_antd98.message.error("Failed to pin to menu");
33883
+ group: group.trim() || void 0
33884
+ });
33885
+ import_antd98.message.success("Pinned to menu");
33886
+ window.dispatchEvent(new CustomEvent("menu:refresh"));
33887
+ setOpen(false);
33888
+ setGroup("");
33889
+ setName("");
33890
+ checkPinned();
33891
+ } catch (err) {
33892
+ import_antd98.message.error(err.message || "Failed to pin to menu");
33699
33893
  } finally {
33700
33894
  setLoading(false);
33701
33895
  }
@@ -33704,16 +33898,13 @@ var PinToMenuButton = ({
33704
33898
  if (!pinnedItemId) return;
33705
33899
  setLoading(true);
33706
33900
  try {
33707
- const json = await del(`/api/menu-items/${pinnedItemId}`);
33708
- if (json.success) {
33709
- import_antd98.message.success("Unpinned from menu");
33710
- setIsPinned(false);
33711
- setPinnedItemId(null);
33712
- } else {
33713
- import_antd98.message.error(json.message || "Failed to unpin");
33714
- }
33715
- } catch {
33716
- import_antd98.message.error("Failed to unpin from menu");
33901
+ await unpin(pinnedItemId);
33902
+ import_antd98.message.success("Unpinned from menu");
33903
+ window.dispatchEvent(new CustomEvent("menu:refresh"));
33904
+ setIsPinned(false);
33905
+ setPinnedItemId(null);
33906
+ } catch (err) {
33907
+ import_antd98.message.error(err.message || "Failed to unpin from menu");
33717
33908
  } finally {
33718
33909
  setLoading(false);
33719
33910
  }
@@ -33767,11 +33958,13 @@ var PinToMenuButton = ({
33767
33958
  }
33768
33959
  ),
33769
33960
  /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(
33770
- import_antd98.Input,
33961
+ import_antd98.AutoComplete,
33771
33962
  {
33772
- placeholder: "Group (optional, e.g. Process)",
33963
+ placeholder: "Group (optional, select or type new)",
33773
33964
  value: group,
33774
- onChange: (e) => setGroup(e.target.value)
33965
+ options: existingGroups.map((g) => ({ value: g })),
33966
+ onChange: (val) => setGroup(val),
33967
+ filterOption: (inputValue, option) => option?.value?.toUpperCase().includes(inputValue.toUpperCase()) ?? false
33775
33968
  }
33776
33969
  )
33777
33970
  ] })
@@ -34418,6 +34611,11 @@ var getFileName = (path) => {
34418
34611
  return parts[parts.length - 1];
34419
34612
  };
34420
34613
  var getEntryName = (entry) => getFileName(entry.name || entry.path);
34614
+ var MODAL_BODY_STYLE2 = {
34615
+ display: "flex",
34616
+ flexDirection: "column",
34617
+ gap: 12
34618
+ };
34421
34619
  var FileDirectoryPanel = ({
34422
34620
  workspaceId,
34423
34621
  projectId,
@@ -34437,6 +34635,116 @@ var FileDirectoryPanel = ({
34437
34635
  const { currentAssistant } = useAssistantContext();
34438
34636
  const { getFileViewUrl } = useWorkspaceContext();
34439
34637
  const [sharePath, setSharePath] = (0, import_react111.useState)(null);
34638
+ const { check: checkPin, pin: doPin, unpin: doUnpin, enableCustomMenu, get } = usePinMenuActions();
34639
+ const staticGroups = useMenuGroups();
34640
+ const [existingGroups, setExistingGroups] = (0, import_react111.useState)(staticGroups);
34641
+ (0, import_react111.useEffect)(() => {
34642
+ setExistingGroups(staticGroups);
34643
+ }, [staticGroups]);
34644
+ const [pinStates, setPinStates] = (0, import_react111.useState)({});
34645
+ const [pinModalPath, setPinModalPath] = (0, import_react111.useState)(null);
34646
+ const [pinName, setPinName] = (0, import_react111.useState)("");
34647
+ const [pinGroup, setPinGroup] = (0, import_react111.useState)("");
34648
+ const [pinLoading, setPinLoading] = (0, import_react111.useState)(false);
34649
+ (0, import_react111.useEffect)(() => {
34650
+ if (pinModalPath) {
34651
+ fetchExistingGroups(get).then((apiGroups) => {
34652
+ setExistingGroups(Array.from(/* @__PURE__ */ new Set([...staticGroups, ...apiGroups])).sort());
34653
+ });
34654
+ }
34655
+ }, [pinModalPath]);
34656
+ const handleDropdownOpen = (0, import_react111.useCallback)(
34657
+ (filePath, open) => {
34658
+ if (!open || !enableCustomMenu) return;
34659
+ setPinStates((prev) => {
34660
+ if (prev[filePath]) return prev;
34661
+ return {
34662
+ ...prev,
34663
+ [filePath]: { isPinned: false, pinnedId: null, loading: true }
34664
+ };
34665
+ });
34666
+ const params = {
34667
+ contentType: "file",
34668
+ contentConfig: {
34669
+ resourcePath: filePath,
34670
+ assistantId: currentAssistant?.id || "",
34671
+ fileName: filePath.split("/").pop() || filePath
34672
+ },
34673
+ isMatch: (cfg) => cfg.resourcePath === filePath
34674
+ };
34675
+ checkPin(params).then((result) => {
34676
+ setPinStates((prev) => ({
34677
+ ...prev,
34678
+ [filePath]: { ...result, loading: false }
34679
+ }));
34680
+ }).catch(() => {
34681
+ setPinStates((prev) => ({
34682
+ ...prev,
34683
+ [filePath]: { isPinned: false, pinnedId: null, loading: false }
34684
+ }));
34685
+ });
34686
+ },
34687
+ [enableCustomMenu, checkPin, currentAssistant?.id]
34688
+ );
34689
+ const handlePinSubmit = async () => {
34690
+ if (!pinModalPath || !pinName.trim()) {
34691
+ import_antd100.message.warning("Please enter a menu name");
34692
+ return;
34693
+ }
34694
+ setPinLoading(true);
34695
+ try {
34696
+ const fileName = pinModalPath.split("/").pop() || pinModalPath;
34697
+ await doPin({
34698
+ contentType: "file",
34699
+ name: pinName.trim(),
34700
+ group: pinGroup.trim() || void 0,
34701
+ contentConfig: {
34702
+ resourcePath: pinModalPath,
34703
+ assistantId: currentAssistant?.id || "",
34704
+ fileName
34705
+ },
34706
+ isMatch: (cfg) => cfg.resourcePath === pinModalPath
34707
+ });
34708
+ import_antd100.message.success("Pinned to menu");
34709
+ window.dispatchEvent(new CustomEvent("menu:refresh"));
34710
+ setPinModalPath(null);
34711
+ setPinName("");
34712
+ setPinGroup("");
34713
+ setPinStates((prev) => {
34714
+ const copy = { ...prev };
34715
+ delete copy[pinModalPath];
34716
+ return copy;
34717
+ });
34718
+ } catch (err) {
34719
+ import_antd100.message.error(err.message || "Failed to pin");
34720
+ } finally {
34721
+ setPinLoading(false);
34722
+ }
34723
+ };
34724
+ const handleUnpin = (0, import_react111.useCallback)(
34725
+ async (filePath, pinnedId) => {
34726
+ setPinStates((prev) => ({
34727
+ ...prev,
34728
+ [filePath]: { ...prev[filePath], loading: true }
34729
+ }));
34730
+ try {
34731
+ await doUnpin(pinnedId);
34732
+ import_antd100.message.success("Unpinned from menu");
34733
+ window.dispatchEvent(new CustomEvent("menu:refresh"));
34734
+ setPinStates((prev) => ({
34735
+ ...prev,
34736
+ [filePath]: { isPinned: false, pinnedId: null, loading: false }
34737
+ }));
34738
+ } catch (err) {
34739
+ import_antd100.message.error(err.message || "Failed to unpin");
34740
+ setPinStates((prev) => ({
34741
+ ...prev,
34742
+ [filePath]: { ...prev[filePath], loading: false }
34743
+ }));
34744
+ }
34745
+ },
34746
+ [doUnpin]
34747
+ );
34440
34748
  const renderEntry = import_react111.default.useCallback((entry, depth) => {
34441
34749
  const entryName = getEntryName(entry);
34442
34750
  if (entry.is_dir) {
@@ -34494,24 +34802,35 @@ var FileDirectoryPanel = ({
34494
34802
  ]
34495
34803
  }
34496
34804
  ),
34497
- /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(
34498
- PinToMenuButton,
34499
- {
34500
- contentType: "file",
34501
- contentConfig: {
34502
- resourcePath: entry.path,
34503
- assistantId: currentAssistant?.id || "",
34504
- fileName: entry.path.split("/").pop() || entry.path
34505
- },
34506
- isMatch: (cfg) => cfg.resourcePath === entry.path
34507
- }
34508
- ),
34509
34805
  /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(
34510
34806
  import_antd100.Dropdown,
34511
34807
  {
34512
34808
  trigger: ["click"],
34809
+ onOpenChange: (open) => handleDropdownOpen(entry.path, open),
34513
34810
  menu: {
34514
34811
  items: [
34812
+ ...enableCustomMenu ? pinStates[entry.path]?.isPinned ? [
34813
+ {
34814
+ key: "unpin",
34815
+ icon: /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(import_lucide_react40.Pin, { size: 14 }),
34816
+ label: "Unpin from Menu",
34817
+ onClick: () => {
34818
+ const state = pinStates[entry.path];
34819
+ if (state?.pinnedId) handleUnpin(entry.path, state.pinnedId);
34820
+ }
34821
+ }
34822
+ ] : [
34823
+ {
34824
+ key: "pin",
34825
+ icon: /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(import_lucide_react40.Pin, { size: 14 }),
34826
+ label: "Pin to Menu",
34827
+ onClick: () => {
34828
+ setPinModalPath(entry.path);
34829
+ setPinName("");
34830
+ setPinGroup("");
34831
+ }
34832
+ }
34833
+ ] : [],
34515
34834
  {
34516
34835
  key: "preview",
34517
34836
  icon: /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(import_icons46.EyeOutlined, {}),
@@ -34549,7 +34868,7 @@ var FileDirectoryPanel = ({
34549
34868
  }
34550
34869
  )
34551
34870
  ] }, entry.path);
34552
- }, [directoryChildren, directoryExpanded, directoryLoading, onAssetClick, onToggleDirectory, styles, getFileViewUrl, currentAssistant?.id]);
34871
+ }, [directoryChildren, directoryExpanded, directoryLoading, onAssetClick, onToggleDirectory, styles, getFileViewUrl, currentAssistant?.id, enableCustomMenu, pinStates, handleDropdownOpen, handleUnpin]);
34553
34872
  if (!projectId) {
34554
34873
  return /* @__PURE__ */ (0, import_jsx_runtime129.jsx)("div", { className: styles.emptyState, children: "Please select a project to browse files" });
34555
34874
  }
@@ -34606,6 +34925,37 @@ var FileDirectoryPanel = ({
34606
34925
  fileName: sharePath.split("/").pop(),
34607
34926
  onClose: () => setSharePath(null)
34608
34927
  }
34928
+ ),
34929
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(
34930
+ import_antd100.Modal,
34931
+ {
34932
+ title: "Pin to Menu",
34933
+ open: !!pinModalPath,
34934
+ onOk: handlePinSubmit,
34935
+ onCancel: () => setPinModalPath(null),
34936
+ confirmLoading: pinLoading,
34937
+ okText: "Pin",
34938
+ children: /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)("div", { style: MODAL_BODY_STYLE2, children: [
34939
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(
34940
+ import_antd100.Input,
34941
+ {
34942
+ placeholder: "Menu name (required)",
34943
+ value: pinName,
34944
+ onChange: (e) => setPinName(e.target.value)
34945
+ }
34946
+ ),
34947
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(
34948
+ import_antd100.AutoComplete,
34949
+ {
34950
+ placeholder: "Group (optional, select or type new)",
34951
+ value: pinGroup,
34952
+ options: existingGroups.map((g) => ({ value: g })),
34953
+ onChange: (val) => setPinGroup(val),
34954
+ filterOption: (inputValue, option) => option?.value?.toUpperCase().includes(inputValue.toUpperCase()) ?? false
34955
+ }
34956
+ )
34957
+ ] })
34958
+ }
34609
34959
  )
34610
34960
  ] });
34611
34961
  };