@burtson-labs/bandit-engine 2.0.152 → 2.0.153

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.
Files changed (47) hide show
  1. package/dist/chat-4UZ5QT7B.mjs +16 -0
  2. package/dist/chat-provider.js +6 -30
  3. package/dist/chat-provider.js.map +1 -1
  4. package/dist/chat-provider.mjs +5 -5
  5. package/dist/{chunk-6STVJJSG.mjs → chunk-2HGBMNX4.mjs} +17 -3
  6. package/dist/chunk-2HGBMNX4.mjs.map +1 -0
  7. package/dist/{chunk-CVFFNX7H.mjs → chunk-6KOEXQJN.mjs} +8 -32
  8. package/dist/chunk-6KOEXQJN.mjs.map +1 -0
  9. package/dist/{chunk-7ESJJN6D.mjs → chunk-6NYYYKYZ.mjs} +262 -75
  10. package/dist/chunk-6NYYYKYZ.mjs.map +1 -0
  11. package/dist/{chunk-QLATFSSO.mjs → chunk-AJL3JOD6.mjs} +5 -5
  12. package/dist/{chunk-RRCCSN3N.mjs → chunk-B2TJZM4Z.mjs} +2 -2
  13. package/dist/{chunk-LPSM62OE.mjs → chunk-DADHPWB7.mjs} +19 -14
  14. package/dist/chunk-DADHPWB7.mjs.map +1 -0
  15. package/dist/{chunk-U33KBX3Y.mjs → chunk-GQ3X6VPR.mjs} +4 -4
  16. package/dist/{chunk-TYRYAAP2.mjs → chunk-RN7FRSMX.mjs} +3 -3
  17. package/dist/{chunk-EORIRPPX.mjs → chunk-ZOCXFQID.mjs} +2 -2
  18. package/dist/{chunk-EORIRPPX.mjs.map → chunk-ZOCXFQID.mjs.map} +1 -1
  19. package/dist/{chunk-VLUXQE24.mjs → chunk-ZW3YNAD7.mjs} +2 -2
  20. package/dist/index.d.mts +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +788 -591
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +10 -10
  25. package/dist/management/management.js +487 -290
  26. package/dist/management/management.js.map +1 -1
  27. package/dist/management/management.mjs +8 -8
  28. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  29. package/dist/modals/chat-modal/chat-modal.mjs +4 -4
  30. package/dist/{modelStore-U57LUIIB.mjs → modelStore-KBUW3XAV.mjs} +2 -2
  31. package/dist/{public-20My32EB.d.mts → public-0EYmtpGD.d.mts} +8 -0
  32. package/dist/{public-20My32EB.d.ts → public-0EYmtpGD.d.ts} +8 -0
  33. package/dist/public-types.d.mts +1 -1
  34. package/dist/public-types.d.ts +1 -1
  35. package/package.json +1 -1
  36. package/dist/chat-Y3WOTIKI.mjs +0 -16
  37. package/dist/chunk-6STVJJSG.mjs.map +0 -1
  38. package/dist/chunk-7ESJJN6D.mjs.map +0 -1
  39. package/dist/chunk-CVFFNX7H.mjs.map +0 -1
  40. package/dist/chunk-LPSM62OE.mjs.map +0 -1
  41. /package/dist/{chat-Y3WOTIKI.mjs.map → chat-4UZ5QT7B.mjs.map} +0 -0
  42. /package/dist/{chunk-QLATFSSO.mjs.map → chunk-AJL3JOD6.mjs.map} +0 -0
  43. /package/dist/{chunk-RRCCSN3N.mjs.map → chunk-B2TJZM4Z.mjs.map} +0 -0
  44. /package/dist/{chunk-U33KBX3Y.mjs.map → chunk-GQ3X6VPR.mjs.map} +0 -0
  45. /package/dist/{chunk-TYRYAAP2.mjs.map → chunk-RN7FRSMX.mjs.map} +0 -0
  46. /package/dist/{chunk-VLUXQE24.mjs.map → chunk-ZW3YNAD7.mjs.map} +0 -0
  47. /package/dist/{modelStore-U57LUIIB.mjs.map → modelStore-KBUW3XAV.mjs.map} +0 -0
@@ -18998,6 +18998,24 @@ var init_themeMap = __esm({
18998
18998
  }
18999
18999
  });
19000
19000
 
19001
+ // src/utils/installedApp.ts
19002
+ var isNativeShell, isStandalonePwa2;
19003
+ var init_installedApp = __esm({
19004
+ "src/utils/installedApp.ts"() {
19005
+ "use strict";
19006
+ isNativeShell = () => {
19007
+ if (typeof window === "undefined") return false;
19008
+ const cap = window.Capacitor;
19009
+ return typeof cap?.isNativePlatform === "function" && cap.isNativePlatform() === true;
19010
+ };
19011
+ isStandalonePwa2 = () => {
19012
+ if (typeof window === "undefined") return false;
19013
+ const nav = window.navigator;
19014
+ return window.matchMedia?.("(display-mode: standalone)").matches || nav.standalone === true;
19015
+ };
19016
+ }
19017
+ });
19018
+
19001
19019
  // src/components/StreamingMarkdown.tsx
19002
19020
  var import_react33, import_material25, import_react_markdown2, import_remark_gfm2, import_rehype_raw2, import_rehype_sanitize3, import_styles19, import_jsx_runtime24, StreamingMarkdown, arePropsEqual, StreamingMarkdown_default;
19003
19021
  var init_StreamingMarkdown = __esm({
@@ -19552,7 +19570,7 @@ var init_mcpServersService = __esm({
19552
19570
  });
19553
19571
 
19554
19572
  // src/store/mcpToolsStore.ts
19555
- var import_zustand14, sanitizeMcpToolName, healthCheckTool, webSearchTool, webFetchTool, imageGenerationTool, createFileTool, defaultTools, useMCPToolsStore;
19573
+ var import_zustand14, sanitizeMcpToolName, healthCheckTool, webSearchTool, webFetchTool, createFileTool, defaultTools, useMCPToolsStore;
19556
19574
  var init_mcpToolsStore = __esm({
19557
19575
  "src/store/mcpToolsStore.ts"() {
19558
19576
  "use strict";
@@ -19624,47 +19642,23 @@ var init_mcpToolsStore = __esm({
19624
19642
  method: "GET",
19625
19643
  isBuiltIn: true
19626
19644
  };
19627
- imageGenerationTool = {
19628
- id: "image-generation",
19629
- name: "image_generation",
19630
- description: "Generate high-quality images using DALL-E 3 from text prompts",
19631
- enabled: true,
19632
- type: "function",
19633
- function: {
19634
- name: "image_generation",
19635
- description: "Generate high-quality images using DALL-E 3 from text prompts",
19636
- parameters: {
19637
- type: "object",
19638
- properties: {
19639
- prompt: { type: "string", description: "Text description of the image to generate" },
19640
- size: { type: "string", enum: ["1024x1024", "1024x1792", "1792x1024"], description: "Image dimensions" },
19641
- quality: { type: "string", enum: ["standard", "hd"], description: "Image quality" },
19642
- style: { type: "string", enum: ["vivid", "natural"], description: "Style preference" }
19643
- },
19644
- required: ["prompt"]
19645
- }
19646
- },
19647
- endpoint: "/mcp/generate-image",
19648
- method: "POST",
19649
- isBuiltIn: true
19650
- };
19651
19645
  createFileTool = {
19652
19646
  id: "create-file",
19653
19647
  name: "create_file",
19654
- description: "Create a downloadable file for the user (md, txt, csv, json, html, docx, pptx). Returns a temporary (~1 hour) download link.",
19648
+ description: "Create a downloadable file for the user (md, txt, csv, json, html, docx, pptx, pdf). Returns a temporary (~1 hour) download link.",
19655
19649
  enabled: true,
19656
19650
  type: "function",
19657
19651
  function: {
19658
19652
  name: "create_file",
19659
- description: "Generate a file the user can download. For docx/pptx write well-structured Markdown (headings, lists, tables; use '## ' headings to start each slide). Returns a short-lived (~1 hour) download URL \u2014 tell the user it expires.",
19653
+ description: "Generate a file the user can download. For docx/pptx write well-structured Markdown (headings, lists, tables; use '## ' headings to start each slide). For pdf, prefer complete HTML with inline CSS (branded proposals, print layouts) \u2014 Chromium renders it without browser headers/footers; Markdown also works. Returns a short-lived (~1 hour) download URL \u2014 tell the user it expires.",
19660
19654
  parameters: {
19661
19655
  type: "object",
19662
19656
  properties: {
19663
- content: { type: "string", description: "The file content (Markdown for docx/pptx; raw text for others)." },
19664
- filename: { type: "string", description: "Desired filename, e.g. 'report.docx' or 'notes.md'." },
19657
+ content: { type: "string", description: "The file content (HTML preferred for pdf; Markdown for docx/pptx/pdf; raw text for others)." },
19658
+ filename: { type: "string", description: "Desired filename, e.g. 'report.pdf' or 'notes.md'." },
19665
19659
  format: {
19666
19660
  type: "string",
19667
- enum: ["md", "txt", "csv", "json", "html", "xml", "yaml", "docx", "pptx"],
19661
+ enum: ["md", "txt", "csv", "json", "html", "xml", "yaml", "docx", "pptx", "pdf"],
19668
19662
  description: "File format. Defaults to md."
19669
19663
  }
19670
19664
  },
@@ -19675,7 +19669,7 @@ var init_mcpToolsStore = __esm({
19675
19669
  method: "POST",
19676
19670
  isBuiltIn: true
19677
19671
  };
19678
- defaultTools = [healthCheckTool, webSearchTool, webFetchTool, imageGenerationTool, createFileTool];
19672
+ defaultTools = [healthCheckTool, webSearchTool, webFetchTool, createFileTool];
19679
19673
  useMCPToolsStore = (0, import_zustand14.create)((set, get) => ({
19680
19674
  tools: defaultTools,
19681
19675
  isLoaded: false,
@@ -20767,6 +20761,7 @@ var init_engine_picker = __esm({
20767
20761
  const tier = String(getCurrentTier());
20768
20762
  return tier !== "trial" && tier !== "expired" && tier !== "free" && tier !== "basic";
20769
20763
  })();
20764
+ const hideUpgradePrompts = usePackageSettingsStore((s) => s.settings?.hideUpgradePrompts === true);
20770
20765
  const plansPath = typeof window !== "undefined" && !(window.location.hostname === "banditailabs.com" || window.location.hostname.endsWith(".banditailabs.com") || window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") ? "https://banditailabs.com/plans" : "/plans";
20771
20766
  if (engines.length === 0) return null;
20772
20767
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
@@ -20845,8 +20840,8 @@ var init_engine_picker = __esm({
20845
20840
  ),
20846
20841
  engine.id === resolvedEngineId && !isGated && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_material37.Box, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: theme.palette.primary.main } })
20847
20842
  ] }),
20848
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_material37.Typography, { variant: "caption", sx: { color: theme.palette.text.secondary }, children: isGated ? "Premium engine \u2014 available on a paid plan." : engine.available ? engine.description : engine.unavailableReason || "Unavailable" }),
20849
- isGated && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
20843
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_material37.Typography, { variant: "caption", sx: { color: theme.palette.text.secondary }, children: isGated ? hideUpgradePrompts ? "Not available on your current plan." : "Premium engine \u2014 available on a paid plan." : engine.available ? engine.description : engine.unavailableReason || "Unavailable" }),
20844
+ isGated && !hideUpgradePrompts && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
20850
20845
  import_material37.Button,
20851
20846
  {
20852
20847
  size: "small",
@@ -22208,12 +22203,14 @@ var init_rate_limit_prompt = __esm({
22208
22203
  import_react_router_dom2 = require("react-router-dom");
22209
22204
  init_lucide_icons();
22210
22205
  init_rateLimitStore();
22206
+ init_packageSettingsStore();
22211
22207
  init_debugLogger();
22212
22208
  import_jsx_runtime41 = require("react/jsx-runtime");
22213
22209
  RateLimitPrompt = () => {
22214
22210
  const theme = (0, import_material41.useTheme)();
22215
22211
  const prompt = useRateLimitStore((s) => s.prompt);
22216
22212
  const dismiss = useRateLimitStore((s) => s.dismiss);
22213
+ const hideUpgradePrompts = usePackageSettingsStore((s) => s.settings?.hideUpgradePrompts === true);
22217
22214
  const hasLoggedRouterWarningRef = (0, import_react48.useRef)(false);
22218
22215
  let navigate = null;
22219
22216
  try {
@@ -22243,6 +22240,12 @@ var init_rate_limit_prompt = __esm({
22243
22240
  const { scope, frontier } = prompt;
22244
22241
  const title = scope === "weekly" ? "You've reached your weekly limit" : "You've hit your session limit";
22245
22242
  const body = (() => {
22243
+ if (hideUpgradePrompts) {
22244
+ if (frontier) {
22245
+ return scope === "weekly" ? "You've used your weekly allowance on premium engines. It resets with your weekly cycle." : "You've hit your session limit on premium engines. It resets soon.";
22246
+ }
22247
+ return scope === "weekly" ? "Your weekly chat allowance is used up. It resets with your weekly cycle." : "It resets shortly \u2014 check back in a bit.";
22248
+ }
22246
22249
  if (frontier) {
22247
22250
  return scope === "weekly" ? "You've used your weekly allowance on premium engines. Upgrade to keep chatting on the most capable models." : "You've hit your session limit on premium engines. It resets soon \u2014 or upgrade for higher limits and uninterrupted access.";
22248
22251
  }
@@ -22287,7 +22290,7 @@ var init_rate_limit_prompt = __esm({
22287
22290
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_material41.Typography, { variant: "subtitle2", sx: { fontWeight: 600, color: theme.palette.text.primary }, children: title }),
22288
22291
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_material41.Typography, { variant: "body2", sx: { color: theme.palette.text.secondary, mt: 0.25 }, children: body }),
22289
22292
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_material41.Box, { sx: { display: "flex", gap: 1, mt: 1, flexWrap: "wrap" }, children: [
22290
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
22293
+ !hideUpgradePrompts && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
22291
22294
  import_material41.Button,
22292
22295
  {
22293
22296
  size: "small",
@@ -24159,7 +24162,6 @@ TOOL USAGE PROTOCOL (conservative approach)
24159
24162
  - Only call tools for SPECIFIC, CURRENT information that requires real-time data or a source you don't already have:
24160
24163
  * web_search() - when asked about recent/current events, breaking news, live information (weather, prices, sports scores), or when you need to look up documentation, libraries, APIs, error messages, or verify a specific fact
24161
24164
  * web_fetch() - to read the FULL contents of a specific URL you already have. Reach for this when the user wants to "tell me more", "go deeper", "read/open that article", or asks for details about a specific source, link, or article from an EARLIER answer: take that item's URL from the previous Sources list in this conversation and fetch it, then answer from the page's actual content (not just the prior summary)
24162
- * image_generation() - ONLY when explicitly asked to create or generate an image
24163
24165
  * create_file({"content": "...", "filename": "report.docx", "format": "docx"}) - when the user asks for a downloadable FILE (a document, spreadsheet, slides, markdown, code, etc.) or to "export"/"download"/"save" something. Formats: md, txt, csv, json, html, xml, yaml, docx, pptx. PDF is NOT a supported format \u2014 if the user asks for a PDF, create a docx instead. For docx/pptx write well-structured Markdown (use "## " headings to start each slide for pptx). It returns a temporary download link \u2014 ALWAYS tell the user the file expires (~1 hour). If it is unclear whether they want it shown inline vs. as a downloadable file, use ask_user first.
24164
24166
  * ask_user({"questions": [{"question": "...", "header": "Format", "options": [{"label": "Inline (Recommended)"}, {"label": "Download a file"}]}]}) - when the request is AMBIGUOUS or missing a detail you need to do a good job (unclear scope, audience, format, tone, which of several interpretations they meant), or you are blocked on a decision that is the user's to make and cannot resolve from the request, context, or sensible defaults. Renders clickable options the user answers in one step \u2014 ALWAYS prefer this over asking a clarifying question in prose and ending your turn. Give 1-4 questions, each with 2-4 short options; if one is clearly best, list it first and append " (Recommended)". The user may also type their own answer; act on it directly. Don't overuse it \u2014 when a sensible default exists, take it and note the assumption instead.
24165
24167
  - For general questions about concepts, definitions, explanations, or how-to topics, use your built-in knowledge WITHOUT calling tools.
@@ -25252,12 +25254,49 @@ var init_useNetworkStatus = __esm({
25252
25254
  }
25253
25255
  });
25254
25256
 
25257
+ // src/hooks/useSafeNavigate.ts
25258
+ var import_react53, import_react_router_dom3, useSafeNavigate;
25259
+ var init_useSafeNavigate = __esm({
25260
+ "src/hooks/useSafeNavigate.ts"() {
25261
+ "use strict";
25262
+ import_react53 = require("react");
25263
+ import_react_router_dom3 = require("react-router-dom");
25264
+ init_debugLogger();
25265
+ useSafeNavigate = () => {
25266
+ const warned = (0, import_react53.useRef)(false);
25267
+ let navigate = null;
25268
+ try {
25269
+ navigate = (0, import_react_router_dom3.useNavigate)();
25270
+ } catch (error) {
25271
+ if (!warned.current) {
25272
+ debugLogger.warn("useSafeNavigate: no Router context; falling back to location", { error });
25273
+ warned.current = true;
25274
+ }
25275
+ navigate = null;
25276
+ }
25277
+ return (to) => {
25278
+ if (typeof window !== "undefined" && /^[a-z]+:\/\//i.test(to)) {
25279
+ window.location.href = to;
25280
+ return;
25281
+ }
25282
+ if (navigate) {
25283
+ navigate(to);
25284
+ return;
25285
+ }
25286
+ if (typeof window !== "undefined") {
25287
+ window.location.href = to;
25288
+ }
25289
+ };
25290
+ };
25291
+ }
25292
+ });
25293
+
25255
25294
  // src/chat/project-management-modal.tsx
25256
- var import_react53, import_material42, import_lucide_react9, import_styles26, import_jsx_runtime42, DEFAULT_COLORS2, ProjectManagementModal, project_management_modal_default;
25295
+ var import_react54, import_material42, import_lucide_react9, import_styles26, import_jsx_runtime42, DEFAULT_COLORS2, ProjectManagementModal, project_management_modal_default;
25257
25296
  var init_project_management_modal = __esm({
25258
25297
  "src/chat/project-management-modal.tsx"() {
25259
25298
  "use strict";
25260
- import_react53 = require("react");
25299
+ import_react54 = require("react");
25261
25300
  import_material42 = require("@mui/material");
25262
25301
  import_lucide_react9 = require("lucide-react");
25263
25302
  import_styles26 = require("@mui/material/styles");
@@ -25293,25 +25332,25 @@ var init_project_management_modal = __esm({
25293
25332
  hydrate
25294
25333
  } = useProjectStore();
25295
25334
  const { getConversationsByProject } = useConversationStore();
25296
- const [showCreateForm, setShowCreateForm] = (0, import_react53.useState)(false);
25297
- const [editingProject, setEditingProject] = (0, import_react53.useState)(null);
25298
- const [formData, setFormData] = (0, import_react53.useState)({
25335
+ const [showCreateForm, setShowCreateForm] = (0, import_react54.useState)(false);
25336
+ const [editingProject, setEditingProject] = (0, import_react54.useState)(null);
25337
+ const [formData, setFormData] = (0, import_react54.useState)({
25299
25338
  name: "",
25300
25339
  description: "",
25301
25340
  instructions: "",
25302
25341
  color: DEFAULT_COLORS2[0]
25303
25342
  });
25304
- const [menuAnchor, setMenuAnchor] = (0, import_react53.useState)(null);
25305
- const [selectedProject, setSelectedProject] = (0, import_react53.useState)(null);
25306
- const [loading, setLoading] = (0, import_react53.useState)(false);
25307
- const [error, setError] = (0, import_react53.useState)(null);
25308
- const modalContainerRef = (0, import_react53.useRef)(null);
25309
- (0, import_react53.useEffect)(() => {
25343
+ const [menuAnchor, setMenuAnchor] = (0, import_react54.useState)(null);
25344
+ const [selectedProject, setSelectedProject] = (0, import_react54.useState)(null);
25345
+ const [loading, setLoading] = (0, import_react54.useState)(false);
25346
+ const [error, setError] = (0, import_react54.useState)(null);
25347
+ const modalContainerRef = (0, import_react54.useRef)(null);
25348
+ (0, import_react54.useEffect)(() => {
25310
25349
  if (open && !_hasHydrated) {
25311
25350
  hydrate();
25312
25351
  }
25313
25352
  }, [open, _hasHydrated, hydrate]);
25314
- (0, import_react53.useEffect)(() => {
25353
+ (0, import_react54.useEffect)(() => {
25315
25354
  if (!open) {
25316
25355
  setMenuAnchor(null);
25317
25356
  setSelectedProject(null);
@@ -25846,11 +25885,11 @@ var init_project_management_modal = __esm({
25846
25885
  });
25847
25886
 
25848
25887
  // src/chat/move-conversation-modal.tsx
25849
- var import_react54, import_material43, import_lucide_react10, import_styles27, import_jsx_runtime43, MoveConversationModal, move_conversation_modal_default;
25888
+ var import_react55, import_material43, import_lucide_react10, import_styles27, import_jsx_runtime43, MoveConversationModal, move_conversation_modal_default;
25850
25889
  var init_move_conversation_modal = __esm({
25851
25890
  "src/chat/move-conversation-modal.tsx"() {
25852
25891
  "use strict";
25853
- import_react54 = require("react");
25892
+ import_react55 = require("react");
25854
25893
  import_material43 = require("@mui/material");
25855
25894
  import_lucide_react10 = require("lucide-react");
25856
25895
  import_styles27 = require("@mui/material/styles");
@@ -25867,15 +25906,15 @@ var init_move_conversation_modal = __esm({
25867
25906
  const theme = (0, import_styles27.useTheme)();
25868
25907
  const { projects, _hasHydrated, hydrate } = useProjectStore();
25869
25908
  const { moveConversationToProject } = useConversationStore();
25870
- const [selectedProjectId, setSelectedProjectId] = (0, import_react54.useState)(
25909
+ const [selectedProjectId, setSelectedProjectId] = (0, import_react55.useState)(
25871
25910
  currentProjectId
25872
25911
  );
25873
- (0, import_react54.useEffect)(() => {
25912
+ (0, import_react55.useEffect)(() => {
25874
25913
  if (open && !_hasHydrated) {
25875
25914
  hydrate();
25876
25915
  }
25877
25916
  }, [open, _hasHydrated, hydrate]);
25878
- (0, import_react54.useEffect)(() => {
25917
+ (0, import_react55.useEffect)(() => {
25879
25918
  setSelectedProjectId(currentProjectId);
25880
25919
  }, [currentProjectId, open]);
25881
25920
  const handleMove = async () => {
@@ -26017,11 +26056,11 @@ var init_move_conversation_modal = __esm({
26017
26056
  });
26018
26057
 
26019
26058
  // src/chat/simple-conversation-item.tsx
26020
- var import_react55, import_material44, import_lucide_react11, import_styles28, import_jsx_runtime44, SimpleConversationItem, simple_conversation_item_default;
26059
+ var import_react56, import_material44, import_lucide_react11, import_styles28, import_jsx_runtime44, SimpleConversationItem, simple_conversation_item_default;
26021
26060
  var init_simple_conversation_item = __esm({
26022
26061
  "src/chat/simple-conversation-item.tsx"() {
26023
26062
  "use strict";
26024
- import_react55 = require("react");
26063
+ import_react56 = require("react");
26025
26064
  import_material44 = require("@mui/material");
26026
26065
  import_lucide_react11 = require("lucide-react");
26027
26066
  import_styles28 = require("@mui/material/styles");
@@ -26043,16 +26082,16 @@ var init_simple_conversation_item = __esm({
26043
26082
  }) => {
26044
26083
  const theme = (0, import_styles28.useTheme)();
26045
26084
  const isMobile = (0, import_material44.useMediaQuery)(theme.breakpoints.down("sm"));
26046
- const [anchorEl, setAnchorEl] = (0, import_react55.useState)(null);
26047
- const [isEditing, setIsEditing] = (0, import_react55.useState)(false);
26048
- const [editName, setEditName] = (0, import_react55.useState)(conversation.name);
26049
- const [isDragging, setIsDragging] = (0, import_react55.useState)(false);
26050
- const [isTouchDragging, setIsTouchDragging] = (0, import_react55.useState)(false);
26051
- const [showRenameDialog, setShowRenameDialog] = (0, import_react55.useState)(false);
26052
- const longPressTimeoutRef = (0, import_react55.useRef)(null);
26053
- const touchStartPointRef = (0, import_react55.useRef)(null);
26054
- const activeTouchIdRef = (0, import_react55.useRef)(null);
26055
- const suppressClickRef = (0, import_react55.useRef)(false);
26085
+ const [anchorEl, setAnchorEl] = (0, import_react56.useState)(null);
26086
+ const [isEditing, setIsEditing] = (0, import_react56.useState)(false);
26087
+ const [editName, setEditName] = (0, import_react56.useState)(conversation.name);
26088
+ const [isDragging, setIsDragging] = (0, import_react56.useState)(false);
26089
+ const [isTouchDragging, setIsTouchDragging] = (0, import_react56.useState)(false);
26090
+ const [showRenameDialog, setShowRenameDialog] = (0, import_react56.useState)(false);
26091
+ const longPressTimeoutRef = (0, import_react56.useRef)(null);
26092
+ const touchStartPointRef = (0, import_react56.useRef)(null);
26093
+ const activeTouchIdRef = (0, import_react56.useRef)(null);
26094
+ const suppressClickRef = (0, import_react56.useRef)(false);
26056
26095
  const highlightText = (text, query) => {
26057
26096
  if (!query || !query.trim()) {
26058
26097
  return text;
@@ -26191,7 +26230,7 @@ var init_simple_conversation_item = __esm({
26191
26230
  if (!isMobile) return;
26192
26231
  finalizeTouchDrag();
26193
26232
  };
26194
- (0, import_react55.useEffect)(() => {
26233
+ (0, import_react56.useEffect)(() => {
26195
26234
  if (!isTouchDragActive && isTouchDragging) {
26196
26235
  setIsTouchDragging(false);
26197
26236
  }
@@ -26457,11 +26496,11 @@ var init_simple_conversation_item = __esm({
26457
26496
  });
26458
26497
 
26459
26498
  // src/chat/project-header.tsx
26460
- var import_react56, import_material45, import_lucide_react12, import_styles29, import_jsx_runtime45, ProjectHeader, project_header_default;
26499
+ var import_react57, import_material45, import_lucide_react12, import_styles29, import_jsx_runtime45, ProjectHeader, project_header_default;
26461
26500
  var init_project_header = __esm({
26462
26501
  "src/chat/project-header.tsx"() {
26463
26502
  "use strict";
26464
- import_react56 = require("react");
26503
+ import_react57 = require("react");
26465
26504
  import_material45 = require("@mui/material");
26466
26505
  import_lucide_react12 = require("lucide-react");
26467
26506
  import_styles29 = require("@mui/material/styles");
@@ -26485,10 +26524,10 @@ var init_project_header = __esm({
26485
26524
  const theme = (0, import_styles29.useTheme)();
26486
26525
  const { createNewConversation } = useConversationStore();
26487
26526
  const { renameProject } = useProjectStore();
26488
- const [isHovered, setIsHovered] = (0, import_react56.useState)(false);
26489
- const [isDragOver, setIsDragOver] = (0, import_react56.useState)(false);
26490
- const [renameDraft, setRenameDraft] = (0, import_react56.useState)(projectName);
26491
- const renameActionRef = (0, import_react56.useRef)("none");
26527
+ const [isHovered, setIsHovered] = (0, import_react57.useState)(false);
26528
+ const [isDragOver, setIsDragOver] = (0, import_react57.useState)(false);
26529
+ const [renameDraft, setRenameDraft] = (0, import_react57.useState)(projectName);
26530
+ const renameActionRef = (0, import_react57.useRef)("none");
26492
26531
  const isUngrouped = projectId === null;
26493
26532
  const Icon = isCollapsed ? import_lucide_react12.Folder : import_lucide_react12.FolderOpen;
26494
26533
  const handleAddConversation = (e) => {
@@ -26762,11 +26801,11 @@ var init_tooltips = __esm({
26762
26801
  });
26763
26802
 
26764
26803
  // src/chat/conversation-drawer.tsx
26765
- var import_react57, import_material46, import_lucide_react13, import_styles30, import_jsx_runtime46, BANDIT_AVATAR, coerceOptionalString, deriveInitials, ConversationDrawer, conversation_drawer_default;
26804
+ var import_react58, import_material46, import_lucide_react13, import_styles30, import_jsx_runtime46, BANDIT_AVATAR, coerceOptionalString, deriveInitials, ConversationDrawer, conversation_drawer_default;
26766
26805
  var init_conversation_drawer = __esm({
26767
26806
  "src/chat/conversation-drawer.tsx"() {
26768
26807
  "use strict";
26769
- import_react57 = require("react");
26808
+ import_react58 = require("react");
26770
26809
  import_material46 = require("@mui/material");
26771
26810
  import_lucide_react13 = require("lucide-react");
26772
26811
  init_FeedbackModal();
@@ -26775,6 +26814,8 @@ var init_conversation_drawer = __esm({
26775
26814
  init_projectStore();
26776
26815
  init_authenticationStore();
26777
26816
  init_packageSettingsStore();
26817
+ init_installedApp();
26818
+ init_useSafeNavigate();
26778
26819
  init_brandingService();
26779
26820
  init_authenticationService();
26780
26821
  init_project_management_modal();
@@ -26838,23 +26879,23 @@ var init_conversation_drawer = __esm({
26838
26879
  createProject,
26839
26880
  deleteProject
26840
26881
  } = useProjectStore();
26841
- const [projectManagementOpen, setProjectManagementOpen] = (0, import_react57.useState)(false);
26842
- const [memoryModalOpen, setMemoryModalOpen] = (0, import_react57.useState)(false);
26843
- const [collapsedProjects, setCollapsedProjects] = (0, import_react57.useState)(/* @__PURE__ */ new Set());
26844
- const didInitCollapseRef = (0, import_react57.useRef)(false);
26845
- const [searchQuery, setSearchQuery] = (0, import_react57.useState)("");
26846
- const [menuAnchorEl, setMenuAnchorEl] = (0, import_react57.useState)(null);
26847
- const [clearConfirmOpen, setClearConfirmOpen] = (0, import_react57.useState)(false);
26848
- const [feedbackOpen, setFeedbackOpen] = (0, import_react57.useState)(false);
26849
- const [moveModalOpen, setMoveModalOpen] = (0, import_react57.useState)(false);
26850
- const [conversationToMove, setConversationToMove] = (0, import_react57.useState)(null);
26851
- const [renameProjectId, setRenameProjectId] = (0, import_react57.useState)(null);
26852
- const getCustomClaim = (0, import_react57.useCallback)((key) => {
26882
+ const [projectManagementOpen, setProjectManagementOpen] = (0, import_react58.useState)(false);
26883
+ const [memoryModalOpen, setMemoryModalOpen] = (0, import_react58.useState)(false);
26884
+ const [collapsedProjects, setCollapsedProjects] = (0, import_react58.useState)(/* @__PURE__ */ new Set());
26885
+ const didInitCollapseRef = (0, import_react58.useRef)(false);
26886
+ const [searchQuery, setSearchQuery] = (0, import_react58.useState)("");
26887
+ const [menuAnchorEl, setMenuAnchorEl] = (0, import_react58.useState)(null);
26888
+ const [clearConfirmOpen, setClearConfirmOpen] = (0, import_react58.useState)(false);
26889
+ const [feedbackOpen, setFeedbackOpen] = (0, import_react58.useState)(false);
26890
+ const [moveModalOpen, setMoveModalOpen] = (0, import_react58.useState)(false);
26891
+ const [conversationToMove, setConversationToMove] = (0, import_react58.useState)(null);
26892
+ const [renameProjectId, setRenameProjectId] = (0, import_react58.useState)(null);
26893
+ const getCustomClaim = (0, import_react58.useCallback)((key) => {
26853
26894
  if (!user2) return void 0;
26854
26895
  const record = user2;
26855
26896
  return coerceOptionalString(record[key]);
26856
26897
  }, [user2]);
26857
- const userDisplayName = (0, import_react57.useMemo)(() => {
26898
+ const userDisplayName = (0, import_react58.useMemo)(() => {
26858
26899
  if (!user2) return void 0;
26859
26900
  const candidateFields = [
26860
26901
  coerceOptionalString(user2.name),
@@ -26869,7 +26910,7 @@ var init_conversation_drawer = __esm({
26869
26910
  if (trimmedEmail) return trimmedEmail;
26870
26911
  return user2.sub;
26871
26912
  }, [user2, getCustomClaim]);
26872
- const userSecondaryText = (0, import_react57.useMemo)(() => {
26913
+ const userSecondaryText = (0, import_react58.useMemo)(() => {
26873
26914
  if (!user2) return void 0;
26874
26915
  const trimmedEmail = coerceOptionalString(user2.email);
26875
26916
  if (trimmedEmail && trimmedEmail !== userDisplayName) {
@@ -26881,13 +26922,13 @@ var init_conversation_drawer = __esm({
26881
26922
  }
26882
26923
  return void 0;
26883
26924
  }, [user2, userDisplayName]);
26884
- const [avatarImage, setAvatarImage] = (0, import_react57.useState)(BANDIT_AVATAR);
26885
- (0, import_react57.useEffect)(() => {
26925
+ const [avatarImage, setAvatarImage] = (0, import_react58.useState)(BANDIT_AVATAR);
26926
+ (0, import_react58.useEffect)(() => {
26886
26927
  const fresh = readPersistedToken();
26887
26928
  const store = useAuthenticationStore.getState();
26888
26929
  if (fresh && fresh !== store.token) store.setToken(fresh);
26889
26930
  }, []);
26890
- (0, import_react57.useEffect)(() => {
26931
+ (0, import_react58.useEffect)(() => {
26891
26932
  let active2 = true;
26892
26933
  let objectUrl = null;
26893
26934
  const resolveAvatar2 = async () => {
@@ -26928,13 +26969,13 @@ var init_conversation_drawer = __esm({
26928
26969
  };
26929
26970
  }, [getCustomClaim]);
26930
26971
  const avatarLabel = userDisplayName || user2?.email || "Bandit";
26931
- const avatarInitials = (0, import_react57.useMemo)(() => deriveInitials(avatarLabel), [avatarLabel]);
26932
- (0, import_react57.useEffect)(() => {
26972
+ const avatarInitials = (0, import_react58.useMemo)(() => deriveInitials(avatarLabel), [avatarLabel]);
26973
+ (0, import_react58.useEffect)(() => {
26933
26974
  if (!projectsHydrated) {
26934
26975
  hydrateProjects();
26935
26976
  }
26936
26977
  }, [projectsHydrated, hydrateProjects]);
26937
- (0, import_react57.useEffect)(() => {
26978
+ (0, import_react58.useEffect)(() => {
26938
26979
  if (projectsHydrated && !didInitCollapseRef.current) {
26939
26980
  didInitCollapseRef.current = true;
26940
26981
  if (projects && projects.length > 0) {
@@ -26947,7 +26988,7 @@ var init_conversation_drawer = __esm({
26947
26988
  const end = Math.min(text.length, idx + query.length + 60);
26948
26989
  return text.slice(start, end).replace(/\s+/g, " ").trim();
26949
26990
  };
26950
- const projectGroups = (0, import_react57.useMemo)(() => {
26991
+ const projectGroups = (0, import_react58.useMemo)(() => {
26951
26992
  const listable = conversations.filter((conversation) => !conversation.incognito);
26952
26993
  const groups = projects.map((project) => ({
26953
26994
  id: project.id,
@@ -26971,7 +27012,7 @@ var init_conversation_drawer = __esm({
26971
27012
  }
26972
27013
  return groups.filter((group) => group.conversations.length > 0 || group.id !== null);
26973
27014
  }, [projects, conversations, collapsedProjects]);
26974
- const filteredProjectGroups = (0, import_react57.useMemo)(() => {
27015
+ const filteredProjectGroups = (0, import_react58.useMemo)(() => {
26975
27016
  if (!searchQuery.trim()) return projectGroups;
26976
27017
  const query = searchQuery.toLowerCase();
26977
27018
  return projectGroups.map((group) => {
@@ -27019,15 +27060,18 @@ var init_conversation_drawer = __esm({
27019
27060
  const syncEnabled = useConversationSyncStore((s) => s.syncEnabled);
27020
27061
  const syncStatus = useConversationSyncStore((s) => s.status);
27021
27062
  const runSync = useConversationSyncStore((s) => s.runSync);
27063
+ const safeNavigate = useSafeNavigate();
27022
27064
  const goToHome = () => {
27023
27065
  const resolvedHomeUrl = preferences.homeUrl?.trim() || usePackageSettingsStore.getState().settings?.homeUrl?.trim() || "";
27024
27066
  if (resolvedHomeUrl) {
27025
27067
  window.location.href = resolvedHomeUrl;
27026
27068
  return;
27027
27069
  }
27028
- const navigatorWithStandalone = window.navigator;
27029
- const isStandalone = window.matchMedia?.("(display-mode: standalone)").matches || navigatorWithStandalone.standalone === true;
27030
- if (isStandalone) {
27070
+ if (isNativeShell()) {
27071
+ safeNavigate("/chat");
27072
+ return;
27073
+ }
27074
+ if (isStandalonePwa2()) {
27031
27075
  try {
27032
27076
  sessionStorage.setItem("banditPwaStayOnHome", "true");
27033
27077
  } catch {
@@ -27498,7 +27542,7 @@ var init_conversation_drawer = __esm({
27498
27542
  import_material46.Box,
27499
27543
  {
27500
27544
  onClick: user2 ? () => {
27501
- window.location.href = "/profile";
27545
+ safeNavigate("/profile");
27502
27546
  } : void 0,
27503
27547
  title: user2 ? "Profile & settings" : void 0,
27504
27548
  sx: {
@@ -27591,7 +27635,7 @@ var init_conversation_drawer = __esm({
27591
27635
  title: "Management & settings",
27592
27636
  onClick: (event) => {
27593
27637
  event.stopPropagation();
27594
- window.location.href = managementPath;
27638
+ safeNavigate(managementPath);
27595
27639
  },
27596
27640
  sx: {
27597
27641
  flexShrink: 0,
@@ -27751,11 +27795,11 @@ var init_conversation_drawer = __esm({
27751
27795
  });
27752
27796
 
27753
27797
  // src/chat/enhanced-mobile-conversations-modal.tsx
27754
- var import_react58, import_material47, import_lucide_react14, import_styles31, import_jsx_runtime47, BANDIT_AVATAR2, coerceOptionalString2, deriveInitials2, EnhancedMobileConversationsModal, enhanced_mobile_conversations_modal_default;
27798
+ var import_react59, import_material47, import_lucide_react14, import_styles31, import_jsx_runtime47, BANDIT_AVATAR2, coerceOptionalString2, deriveInitials2, EnhancedMobileConversationsModal, enhanced_mobile_conversations_modal_default;
27755
27799
  var init_enhanced_mobile_conversations_modal = __esm({
27756
27800
  "src/chat/enhanced-mobile-conversations-modal.tsx"() {
27757
27801
  "use strict";
27758
- import_react58 = require("react");
27802
+ import_react59 = require("react");
27759
27803
  import_material47 = require("@mui/material");
27760
27804
  import_lucide_react14 = require("lucide-react");
27761
27805
  import_styles31 = require("@mui/material/styles");
@@ -27763,6 +27807,8 @@ var init_enhanced_mobile_conversations_modal = __esm({
27763
27807
  init_projectStore();
27764
27808
  init_authenticationStore();
27765
27809
  init_packageSettingsStore();
27810
+ init_installedApp();
27811
+ init_useSafeNavigate();
27766
27812
  init_memory_modal();
27767
27813
  init_brandingService();
27768
27814
  init_authenticationService();
@@ -27803,7 +27849,8 @@ var init_enhanced_mobile_conversations_modal = __esm({
27803
27849
  };
27804
27850
  EnhancedMobileConversationsModal = ({
27805
27851
  open,
27806
- onClose
27852
+ onClose,
27853
+ drag
27807
27854
  }) => {
27808
27855
  const theme = (0, import_styles31.useTheme)();
27809
27856
  const { user: user2 } = useAuthenticationStore();
@@ -27830,15 +27877,18 @@ var init_enhanced_mobile_conversations_modal = __esm({
27830
27877
  const syncEnabled = useConversationSyncStore((s) => s.syncEnabled);
27831
27878
  const syncStatus = useConversationSyncStore((s) => s.status);
27832
27879
  const runSync = useConversationSyncStore((s) => s.runSync);
27880
+ const safeNavigate = useSafeNavigate();
27833
27881
  const goToHome = () => {
27834
27882
  const resolvedHomeUrl = preferences.homeUrl?.trim() || usePackageSettingsStore.getState().settings?.homeUrl?.trim() || "";
27835
27883
  if (resolvedHomeUrl) {
27836
27884
  window.location.href = resolvedHomeUrl;
27837
27885
  return;
27838
27886
  }
27839
- const navigatorWithStandalone = window.navigator;
27840
- const isStandalone = window.matchMedia?.("(display-mode: standalone)").matches || navigatorWithStandalone.standalone === true;
27841
- if (isStandalone) {
27887
+ if (isNativeShell()) {
27888
+ safeNavigate("/chat");
27889
+ return;
27890
+ }
27891
+ if (isStandalonePwa2()) {
27842
27892
  try {
27843
27893
  sessionStorage.setItem("banditPwaStayOnHome", "true");
27844
27894
  } catch {
@@ -27849,25 +27899,25 @@ var init_enhanced_mobile_conversations_modal = __esm({
27849
27899
  window.location.href = "/";
27850
27900
  };
27851
27901
  const managementPath = typeof window !== "undefined" && window.location.pathname.startsWith("/playground") ? "/playground/management" : "/management";
27852
- const [projectManagementOpen, setProjectManagementOpen] = (0, import_react58.useState)(false);
27853
- const [memoryModalOpen, setMemoryModalOpen] = (0, import_react58.useState)(false);
27854
- const [collapsedProjects, setCollapsedProjects] = (0, import_react58.useState)(/* @__PURE__ */ new Set());
27855
- const didInitCollapseRef = (0, import_react58.useRef)(false);
27856
- const [searchQuery, setSearchQuery] = (0, import_react58.useState)("");
27857
- const [menuAnchorEl, setMenuAnchorEl] = (0, import_react58.useState)(null);
27858
- const [clearConfirmOpen, setClearConfirmOpen] = (0, import_react58.useState)(false);
27859
- const [moveModalOpen, setMoveModalOpen] = (0, import_react58.useState)(false);
27860
- const [conversationToMove, setConversationToMove] = (0, import_react58.useState)(null);
27861
- const [renameProjectId, setRenameProjectId] = (0, import_react58.useState)(null);
27862
- const [deletedConversationIds, setDeletedConversationIds] = (0, import_react58.useState)(/* @__PURE__ */ new Set());
27863
- const [touchDragState, setTouchDragState] = (0, import_react58.useState)({ conversationId: null, originProjectId: null, hoverProjectId: null });
27864
- const [avatarImage, setAvatarImage] = (0, import_react58.useState)(BANDIT_AVATAR2);
27865
- const getCustomClaim = (0, import_react58.useCallback)((key) => {
27902
+ const [projectManagementOpen, setProjectManagementOpen] = (0, import_react59.useState)(false);
27903
+ const [memoryModalOpen, setMemoryModalOpen] = (0, import_react59.useState)(false);
27904
+ const [collapsedProjects, setCollapsedProjects] = (0, import_react59.useState)(/* @__PURE__ */ new Set());
27905
+ const didInitCollapseRef = (0, import_react59.useRef)(false);
27906
+ const [searchQuery, setSearchQuery] = (0, import_react59.useState)("");
27907
+ const [menuAnchorEl, setMenuAnchorEl] = (0, import_react59.useState)(null);
27908
+ const [clearConfirmOpen, setClearConfirmOpen] = (0, import_react59.useState)(false);
27909
+ const [moveModalOpen, setMoveModalOpen] = (0, import_react59.useState)(false);
27910
+ const [conversationToMove, setConversationToMove] = (0, import_react59.useState)(null);
27911
+ const [renameProjectId, setRenameProjectId] = (0, import_react59.useState)(null);
27912
+ const [deletedConversationIds, setDeletedConversationIds] = (0, import_react59.useState)(/* @__PURE__ */ new Set());
27913
+ const [touchDragState, setTouchDragState] = (0, import_react59.useState)({ conversationId: null, originProjectId: null, hoverProjectId: null });
27914
+ const [avatarImage, setAvatarImage] = (0, import_react59.useState)(BANDIT_AVATAR2);
27915
+ const getCustomClaim = (0, import_react59.useCallback)((key) => {
27866
27916
  if (!user2) return void 0;
27867
27917
  const record = user2;
27868
27918
  return coerceOptionalString2(record[key]);
27869
27919
  }, [user2]);
27870
- const userDisplayName = (0, import_react58.useMemo)(() => {
27920
+ const userDisplayName = (0, import_react59.useMemo)(() => {
27871
27921
  if (!user2) return void 0;
27872
27922
  const candidateFields = [
27873
27923
  coerceOptionalString2(user2.name),
@@ -27882,7 +27932,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
27882
27932
  if (trimmedEmail) return trimmedEmail;
27883
27933
  return user2.sub;
27884
27934
  }, [user2, getCustomClaim]);
27885
- const userSecondaryText = (0, import_react58.useMemo)(() => {
27935
+ const userSecondaryText = (0, import_react59.useMemo)(() => {
27886
27936
  if (!user2) return void 0;
27887
27937
  const trimmedEmail = coerceOptionalString2(user2.email);
27888
27938
  if (trimmedEmail && trimmedEmail !== userDisplayName) {
@@ -27894,7 +27944,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
27894
27944
  }
27895
27945
  return void 0;
27896
27946
  }, [user2, userDisplayName]);
27897
- (0, import_react58.useEffect)(() => {
27947
+ (0, import_react59.useEffect)(() => {
27898
27948
  if (!open) return;
27899
27949
  const fresh = readPersistedToken();
27900
27950
  const authStore = useAuthenticationStore.getState();
@@ -27939,18 +27989,18 @@ var init_enhanced_mobile_conversations_modal = __esm({
27939
27989
  };
27940
27990
  }, [open, getCustomClaim]);
27941
27991
  const avatarLabel = userDisplayName || user2?.email || "Bandit";
27942
- const avatarInitials = (0, import_react58.useMemo)(() => deriveInitials2(avatarLabel), [avatarLabel]);
27992
+ const avatarInitials = (0, import_react59.useMemo)(() => deriveInitials2(avatarLabel), [avatarLabel]);
27943
27993
  const buildSnippet = (text, query, idx) => {
27944
27994
  const start = Math.max(0, idx - 40);
27945
27995
  const end = Math.min(text.length, idx + query.length + 60);
27946
27996
  return text.slice(start, end).replace(/\s+/g, " ").trim();
27947
27997
  };
27948
- (0, import_react58.useEffect)(() => {
27998
+ (0, import_react59.useEffect)(() => {
27949
27999
  if (open && !projectsHydrated) {
27950
28000
  hydrateProjects();
27951
28001
  }
27952
28002
  }, [open, projectsHydrated, hydrateProjects]);
27953
- (0, import_react58.useEffect)(() => {
28003
+ (0, import_react59.useEffect)(() => {
27954
28004
  if (projectsHydrated && !didInitCollapseRef.current) {
27955
28005
  didInitCollapseRef.current = true;
27956
28006
  if (projects && projects.length > 0) {
@@ -27958,7 +28008,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
27958
28008
  }
27959
28009
  }
27960
28010
  }, [projectsHydrated, projects]);
27961
- const projectGroups = (0, import_react58.useMemo)(() => {
28011
+ const projectGroups = (0, import_react59.useMemo)(() => {
27962
28012
  const visibleConversations = conversations.filter(
27963
28013
  (conversation) => !deletedConversationIds.has(conversation.id) && !conversation.incognito
27964
28014
  );
@@ -27984,11 +28034,11 @@ var init_enhanced_mobile_conversations_modal = __esm({
27984
28034
  }
27985
28035
  return groups.filter((group) => group.conversations.length > 0 || group.id !== null);
27986
28036
  }, [projects, conversations, collapsedProjects, deletedConversationIds]);
27987
- const visibleConversationCount = (0, import_react58.useMemo)(
28037
+ const visibleConversationCount = (0, import_react59.useMemo)(
27988
28038
  () => projectGroups.reduce((total, group) => total + group.conversations.length, 0),
27989
28039
  [projectGroups]
27990
28040
  );
27991
- const filteredProjectGroups = (0, import_react58.useMemo)(() => {
28041
+ const filteredProjectGroups = (0, import_react59.useMemo)(() => {
27992
28042
  if (!searchQuery.trim()) return projectGroups;
27993
28043
  const query = searchQuery.toLowerCase();
27994
28044
  return projectGroups.map((group) => {
@@ -28013,14 +28063,14 @@ var init_enhanced_mobile_conversations_modal = __esm({
28013
28063
  }).filter((group) => group.conversations.length > 0);
28014
28064
  }, [projectGroups, searchQuery]);
28015
28065
  const touchDragActive = Boolean(touchDragState.conversationId);
28016
- const getProjectIdFromPoint = (0, import_react58.useCallback)((clientX, clientY) => {
28066
+ const getProjectIdFromPoint = (0, import_react59.useCallback)((clientX, clientY) => {
28017
28067
  if (typeof document === "undefined") return null;
28018
28068
  const element = document.elementFromPoint(clientX, clientY);
28019
28069
  if (!element) return null;
28020
28070
  const projectNode = element.closest("[data-project-id]");
28021
28071
  return projectNode?.getAttribute("data-project-id") ?? null;
28022
28072
  }, []);
28023
- const handleTouchDragStart = (0, import_react58.useCallback)((conversation, touch) => {
28073
+ const handleTouchDragStart = (0, import_react59.useCallback)((conversation, touch) => {
28024
28074
  const initialHover = getProjectIdFromPoint(touch.clientX, touch.clientY);
28025
28075
  setTouchDragState({
28026
28076
  conversationId: conversation.id,
@@ -28028,7 +28078,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28028
28078
  hoverProjectId: initialHover ?? (conversation.projectId ?? null)
28029
28079
  });
28030
28080
  }, [getProjectIdFromPoint]);
28031
- const handleTouchDragMove = (0, import_react58.useCallback)((touch) => {
28081
+ const handleTouchDragMove = (0, import_react59.useCallback)((touch) => {
28032
28082
  setTouchDragState((prev) => {
28033
28083
  if (!prev.conversationId) return prev;
28034
28084
  const hoverId = getProjectIdFromPoint(touch.clientX, touch.clientY);
@@ -28036,7 +28086,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28036
28086
  return { ...prev, hoverProjectId: hoverId };
28037
28087
  });
28038
28088
  }, [getProjectIdFromPoint]);
28039
- const handleTouchDragEnd = (0, import_react58.useCallback)((touch) => {
28089
+ const handleTouchDragEnd = (0, import_react59.useCallback)((touch) => {
28040
28090
  setTouchDragState((prev) => {
28041
28091
  if (!prev.conversationId) {
28042
28092
  return { conversationId: null, originProjectId: null, hoverProjectId: null };
@@ -28064,11 +28114,11 @@ var init_enhanced_mobile_conversations_modal = __esm({
28064
28114
  return { conversationId: null, originProjectId: null, hoverProjectId: null };
28065
28115
  });
28066
28116
  }, [getProjectIdFromPoint, moveConversationToProject]);
28067
- const activeDragConversation = (0, import_react58.useMemo)(() => {
28117
+ const activeDragConversation = (0, import_react59.useMemo)(() => {
28068
28118
  if (!touchDragState.conversationId) return null;
28069
28119
  return conversations.find((conv) => conv.id === touchDragState.conversationId) || null;
28070
28120
  }, [touchDragState.conversationId, conversations]);
28071
- const activeHoverLabel = (0, import_react58.useMemo)(() => {
28121
+ const activeHoverLabel = (0, import_react59.useMemo)(() => {
28072
28122
  if (!touchDragState.hoverProjectId) return "";
28073
28123
  if (touchDragState.hoverProjectId === "__ungrouped") return "Ungrouped";
28074
28124
  const project = projects.find((p) => p.id === touchDragState.hoverProjectId);
@@ -28117,7 +28167,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28117
28167
  setMoveModalOpen(false);
28118
28168
  setConversationToMove(null);
28119
28169
  };
28120
- (0, import_react58.useEffect)(() => {
28170
+ (0, import_react59.useEffect)(() => {
28121
28171
  setDeletedConversationIds((prev) => {
28122
28172
  let changed = false;
28123
28173
  const active2 = new Set(conversations.map((conv) => conv.id));
@@ -28142,6 +28192,11 @@ var init_enhanced_mobile_conversations_modal = __esm({
28142
28192
  children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_material47.Slide, { direction: "right", in: open, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
28143
28193
  import_material47.Box,
28144
28194
  {
28195
+ style: drag?.style,
28196
+ onTouchStart: drag?.onTouchStart,
28197
+ onTouchMove: drag?.onTouchMove,
28198
+ onTouchEnd: drag?.onTouchEnd,
28199
+ onTouchCancel: drag?.onTouchEnd,
28145
28200
  sx: {
28146
28201
  position: "fixed",
28147
28202
  top: 0,
@@ -28649,7 +28704,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28649
28704
  import_material47.Box,
28650
28705
  {
28651
28706
  onClick: user2 ? () => {
28652
- window.location.href = "/profile";
28707
+ safeNavigate("/profile");
28653
28708
  } : void 0,
28654
28709
  sx: {
28655
28710
  mt: "auto",
@@ -28725,7 +28780,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28725
28780
  title: "Management & settings",
28726
28781
  onClick: (event) => {
28727
28782
  event.stopPropagation();
28728
- window.location.href = managementPath;
28783
+ safeNavigate(managementPath);
28729
28784
  },
28730
28785
  sx: {
28731
28786
  flexShrink: 0,
@@ -28887,15 +28942,15 @@ var init_enhanced_mobile_conversations_modal = __esm({
28887
28942
  });
28888
28943
 
28889
28944
  // src/chat/chat-app-bar.tsx
28890
- var import_material48, import_react59, import_material49, import_react_router_dom3, import_shallow2, import_jsx_runtime48, CDN_BASE2, banditHead4, modelAvatars3, ChatAppBar, chat_app_bar_default;
28945
+ var import_material48, import_react60, import_material49, import_react_router_dom4, import_shallow2, import_jsx_runtime48, CDN_BASE2, banditHead4, modelAvatars3, ChatAppBar, chat_app_bar_default;
28891
28946
  var init_chat_app_bar = __esm({
28892
28947
  "src/chat/chat-app-bar.tsx"() {
28893
28948
  "use strict";
28894
28949
  import_material48 = require("@mui/material");
28895
28950
  init_modelStore();
28896
- import_react59 = require("react");
28951
+ import_react60 = require("react");
28897
28952
  import_material49 = require("@mui/material");
28898
- import_react_router_dom3 = require("react-router-dom");
28953
+ import_react_router_dom4 = require("react-router-dom");
28899
28954
  init_lucide_icons();
28900
28955
  init_conversation_drawer();
28901
28956
  init_enhanced_mobile_conversations_modal();
@@ -28927,14 +28982,15 @@ var init_chat_app_bar = __esm({
28927
28982
  drawerOpen,
28928
28983
  setDrawerOpen,
28929
28984
  mobileConversationsOpen,
28930
- onMobileConversationsOpenChange
28985
+ onMobileConversationsOpenChange,
28986
+ mobileConversationsDrag
28931
28987
  }) => {
28932
28988
  const theme = (0, import_material49.useTheme)();
28933
28989
  const isMobile = (0, import_material49.useMediaQuery)(theme.breakpoints.down("sm"));
28934
- const hasLoggedRouterWarningRef = (0, import_react59.useRef)(false);
28990
+ const hasLoggedRouterWarningRef = (0, import_react60.useRef)(false);
28935
28991
  let navigate = null;
28936
28992
  try {
28937
- navigate = (0, import_react_router_dom3.useNavigate)();
28993
+ navigate = (0, import_react_router_dom4.useNavigate)();
28938
28994
  } catch (error) {
28939
28995
  if (!hasLoggedRouterWarningRef.current) {
28940
28996
  debugLogger.warn("ChatAppBar: Navigation not available (missing Router context)", { error });
@@ -28961,17 +29017,17 @@ var init_chat_app_bar = __esm({
28961
29017
  menuBackground,
28962
29018
  menuText
28963
29019
  } = theme.palette.chat.appBar;
28964
- const [modelAnchorEl, setModelAnchorEl] = (0, import_react59.useState)(null);
28965
- const [engineAnchorEl, setEngineAnchorEl] = (0, import_react59.useState)(null);
28966
- const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react59.useState)(null);
28967
- const [internalModalOpen, setInternalModalOpen] = (0, import_react59.useState)(false);
29020
+ const [modelAnchorEl, setModelAnchorEl] = (0, import_react60.useState)(null);
29021
+ const [engineAnchorEl, setEngineAnchorEl] = (0, import_react60.useState)(null);
29022
+ const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react60.useState)(null);
29023
+ const [internalModalOpen, setInternalModalOpen] = (0, import_react60.useState)(false);
28968
29024
  const modalOpen = mobileConversationsOpen ?? internalModalOpen;
28969
29025
  const setModalOpen = (open) => {
28970
29026
  setInternalModalOpen(open);
28971
29027
  onMobileConversationsOpenChange?.(open);
28972
29028
  };
28973
- const [confirmModelChangeOpen, setConfirmModelChangeOpen] = (0, import_react59.useState)(false);
28974
- const [pendingModel, setPendingModel] = (0, import_react59.useState)(null);
29029
+ const [confirmModelChangeOpen, setConfirmModelChangeOpen] = (0, import_react60.useState)(false);
29030
+ const [pendingModel, setPendingModel] = (0, import_react60.useState)(null);
28975
29031
  const { conversations, currentId, createNewConversation, toggleIncognito, _hasHydrated } = useConversationStore();
28976
29032
  const { preferences } = usePreferencesStore();
28977
29033
  const { settings: packageSettings } = usePackageSettingsStore();
@@ -28981,7 +29037,7 @@ var init_chat_app_bar = __esm({
28981
29037
  syncEnabled: state.syncEnabled,
28982
29038
  setSyncEnabled: state.setSyncEnabled
28983
29039
  }), import_shallow2.shallow);
28984
- (0, import_react59.useEffect)(() => {
29040
+ (0, import_react60.useEffect)(() => {
28985
29041
  if (isPlaygroundMode3 && syncEnabled) {
28986
29042
  void setSyncEnabled(false).catch((error) => {
28987
29043
  debugLogger.warn("ChatAppBar: Failed to disable sync in playground", {
@@ -29036,7 +29092,7 @@ var init_chat_app_bar = __esm({
29036
29092
  const resolvedEngineId = currentEngine?.id ?? effectiveEngineId;
29037
29093
  const cleanEngineName = (name) => (name || "").replace(/\s*\([^)]*\)\s*$/, "").trim();
29038
29094
  const engineDisplay = cleanEngineName(currentEngine?.displayName) || "Engine";
29039
- (0, import_react59.useEffect)(() => {
29095
+ (0, import_react60.useEffect)(() => {
29040
29096
  useEngineStore.getState().fetchEngines();
29041
29097
  }, []);
29042
29098
  const pendingModelAvatar = useModelStore.getState().availableModels.find((m) => m.name === pendingModel)?.avatarBase64 || modelAvatars3[pendingModel || ""] || banditHead4;
@@ -29279,7 +29335,7 @@ var init_chat_app_bar = __esm({
29279
29335
  }
29280
29336
  ),
29281
29337
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(conversation_drawer_default, { open: drawerOpen, onClose: () => setDrawerOpen(false) }),
29282
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(enhanced_mobile_conversations_modal_default, { open: modalOpen, onClose: () => setModalOpen(false) }),
29338
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(enhanced_mobile_conversations_modal_default, { open: modalOpen, onClose: () => setModalOpen(false), drag: mobileConversationsDrag }),
29283
29339
  /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
29284
29340
  import_material49.Dialog,
29285
29341
  {
@@ -29419,11 +29475,11 @@ Respond with just the title and nothing else.
29419
29475
  });
29420
29476
 
29421
29477
  // src/chat/query-suggestion-picker.tsx
29422
- var import_react60, import_material50, import_styles32, import_react_markdown3, import_remark_gfm3, import_rehype_raw3, import_jsx_runtime49, markdownComponents, QuerySuggestionPicker;
29478
+ var import_react61, import_material50, import_styles32, import_react_markdown3, import_remark_gfm3, import_rehype_raw3, import_jsx_runtime49, markdownComponents, QuerySuggestionPicker;
29423
29479
  var init_query_suggestion_picker = __esm({
29424
29480
  "src/chat/query-suggestion-picker.tsx"() {
29425
29481
  "use strict";
29426
- import_react60 = require("react");
29482
+ import_react61 = require("react");
29427
29483
  import_material50 = require("@mui/material");
29428
29484
  import_styles32 = require("@mui/material/styles");
29429
29485
  import_react_markdown3 = __toESM(require("react-markdown"));
@@ -29448,16 +29504,16 @@ var init_query_suggestion_picker = __esm({
29448
29504
  onSend,
29449
29505
  inputHeight
29450
29506
  }) => {
29451
- const hasGenerated = (0, import_react60.useRef)(false);
29452
- const [hasSentPrompt, setHasSentPrompt] = (0, import_react60.useState)(false);
29453
- const [examplePrompts, setExamplePrompts] = (0, import_react60.useState)([]);
29454
- const [visiblePrompts, setVisiblePrompts] = (0, import_react60.useState)([]);
29455
- const scrollRef = (0, import_react60.useRef)(null);
29507
+ const hasGenerated = (0, import_react61.useRef)(false);
29508
+ const [hasSentPrompt, setHasSentPrompt] = (0, import_react61.useState)(false);
29509
+ const [examplePrompts, setExamplePrompts] = (0, import_react61.useState)([]);
29510
+ const [visiblePrompts, setVisiblePrompts] = (0, import_react61.useState)([]);
29511
+ const scrollRef = (0, import_react61.useRef)(null);
29456
29512
  const theme = (0, import_styles32.useTheme)();
29457
29513
  const isMobile = (0, import_material50.useMediaQuery)((theme2) => theme2.breakpoints.down("sm"));
29458
29514
  const { background, text, border, hoverBackground, hoverBorder } = theme.palette.chat.suggestion;
29459
29515
  const { getCurrentModel, isLoading } = useModelStore();
29460
- (0, import_react60.useEffect)(() => {
29516
+ (0, import_react61.useEffect)(() => {
29461
29517
  if (hasGenerated.current || isLoading) return;
29462
29518
  hasGenerated.current = true;
29463
29519
  const currentModel = getCurrentModel();
@@ -29490,12 +29546,12 @@ var init_query_suggestion_picker = __esm({
29490
29546
  hasGenerated.current = false;
29491
29547
  });
29492
29548
  }, [getCurrentModel, isLoading]);
29493
- (0, import_react60.useEffect)(() => {
29549
+ (0, import_react61.useEffect)(() => {
29494
29550
  if (!isLoading) {
29495
29551
  hasGenerated.current = false;
29496
29552
  }
29497
29553
  }, [isLoading]);
29498
- (0, import_react60.useEffect)(() => {
29554
+ (0, import_react61.useEffect)(() => {
29499
29555
  if (hasSentPrompt || examplePrompts.length === 0) return;
29500
29556
  const interval = setInterval(() => {
29501
29557
  setExamplePrompts((prev) => {
@@ -29612,17 +29668,17 @@ var init_query_suggestion_picker = __esm({
29612
29668
  });
29613
29669
 
29614
29670
  // src/components/UnderReview.tsx
29615
- var import_material51, import_react_router_dom4, import_jsx_runtime50, UnderReview, UnderReview_default;
29671
+ var import_material51, import_react_router_dom5, import_jsx_runtime50, UnderReview, UnderReview_default;
29616
29672
  var init_UnderReview = __esm({
29617
29673
  "src/components/UnderReview.tsx"() {
29618
29674
  "use strict";
29619
29675
  import_material51 = require("@mui/material");
29620
- import_react_router_dom4 = require("react-router-dom");
29676
+ import_react_router_dom5 = require("react-router-dom");
29621
29677
  init_authenticationService();
29622
29678
  import_jsx_runtime50 = require("react/jsx-runtime");
29623
29679
  UnderReview = () => {
29624
29680
  const theme = (0, import_material51.useTheme)();
29625
- const navigate = (0, import_react_router_dom4.useNavigate)();
29681
+ const navigate = (0, import_react_router_dom5.useNavigate)();
29626
29682
  const handleResetSession = () => {
29627
29683
  authenticationService2.clearToken();
29628
29684
  navigate("/login");
@@ -29686,11 +29742,11 @@ var init_UnderReview = __esm({
29686
29742
  });
29687
29743
 
29688
29744
  // src/components/feedback/FeedbackPulse.tsx
29689
- var import_react61, import_material52, import_styles33, import_lucide_react15, import_jsx_runtime51, STORAGE_KEY3, COOLDOWN_DAYS, MIN_SESSION_MS, MIN_MESSAGES_SENT, CHECK_INTERVAL_MS, readLastInteraction, recordInteraction, FeedbackPulse, FeedbackPulse_default;
29745
+ var import_react62, import_material52, import_styles33, import_lucide_react15, import_jsx_runtime51, STORAGE_KEY3, COOLDOWN_DAYS, MIN_SESSION_MS, MIN_MESSAGES_SENT, CHECK_INTERVAL_MS, readLastInteraction, recordInteraction, FeedbackPulse, FeedbackPulse_default;
29690
29746
  var init_FeedbackPulse = __esm({
29691
29747
  "src/components/feedback/FeedbackPulse.tsx"() {
29692
29748
  "use strict";
29693
- import_react61 = require("react");
29749
+ import_react62 = require("react");
29694
29750
  import_material52 = require("@mui/material");
29695
29751
  import_styles33 = require("@mui/material/styles");
29696
29752
  import_lucide_react15 = require("lucide-react");
@@ -29719,21 +29775,21 @@ var init_FeedbackPulse = __esm({
29719
29775
  };
29720
29776
  FeedbackPulse = ({ isMobile, feedbackEmail }) => {
29721
29777
  const theme = (0, import_styles33.useTheme)();
29722
- const [visible, setVisible] = (0, import_react61.useState)(false);
29723
- const [thanked, setThanked] = (0, import_react61.useState)(false);
29724
- const [modalOpen, setModalOpen] = (0, import_react61.useState)(false);
29725
- const mountedAtRef = (0, import_react61.useRef)(Date.now());
29726
- const baselineMessagesRef = (0, import_react61.useRef)(null);
29778
+ const [visible, setVisible] = (0, import_react62.useState)(false);
29779
+ const [thanked, setThanked] = (0, import_react62.useState)(false);
29780
+ const [modalOpen, setModalOpen] = (0, import_react62.useState)(false);
29781
+ const mountedAtRef = (0, import_react62.useRef)(Date.now());
29782
+ const baselineMessagesRef = (0, import_react62.useRef)(null);
29727
29783
  const totalMessages = useConversationStore(
29728
29784
  (s) => s.conversations.reduce((n, c) => n + c.history.length, 0)
29729
29785
  );
29730
29786
  const hydrated = useConversationStore((s) => s._hasHydrated);
29731
- (0, import_react61.useEffect)(() => {
29787
+ (0, import_react62.useEffect)(() => {
29732
29788
  if (hydrated && baselineMessagesRef.current === null) {
29733
29789
  baselineMessagesRef.current = totalMessages;
29734
29790
  }
29735
29791
  }, [hydrated, totalMessages]);
29736
- (0, import_react61.useEffect)(() => {
29792
+ (0, import_react62.useEffect)(() => {
29737
29793
  const cooldownMs = COOLDOWN_DAYS * 24 * 60 * 60 * 1e3;
29738
29794
  const forced = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("feedbackPulse");
29739
29795
  const check = () => {
@@ -29943,11 +29999,11 @@ var init_ConnectionStatus = __esm({
29943
29999
  });
29944
30000
 
29945
30001
  // src/hooks/useVoiceMode.ts
29946
- var import_react62, RMS_BASELINE, RMS_NORMALIZER, computeRms, pickSupportedMimeType, useVoiceMode;
30002
+ var import_react63, RMS_BASELINE, RMS_NORMALIZER, computeRms, pickSupportedMimeType, useVoiceMode;
29947
30003
  var init_useVoiceMode = __esm({
29948
30004
  "src/hooks/useVoiceMode.ts"() {
29949
30005
  "use strict";
29950
- import_react62 = require("react");
30006
+ import_react63 = require("react");
29951
30007
  init_stt_client();
29952
30008
  init_debugLogger();
29953
30009
  init_voiceModeStore();
@@ -29980,11 +30036,11 @@ var init_useVoiceMode = __esm({
29980
30036
  const setError = useVoiceModeStore((state) => state.setError);
29981
30037
  const resetTransientState = useVoiceModeStore((state) => state.resetTransientState);
29982
30038
  const setLastTranscript = useVoiceModeStore((state) => state.setLastTranscript);
29983
- const configRef = (0, import_react62.useRef)(config);
29984
- (0, import_react62.useEffect)(() => {
30039
+ const configRef = (0, import_react63.useRef)(config);
30040
+ (0, import_react63.useEffect)(() => {
29985
30041
  configRef.current = config;
29986
30042
  }, [config]);
29987
- (0, import_react62.useEffect)(() => {
30043
+ (0, import_react63.useEffect)(() => {
29988
30044
  if (!enabled) {
29989
30045
  return () => void 0;
29990
30046
  }
@@ -30245,11 +30301,11 @@ var chat_exports = {};
30245
30301
  __export(chat_exports, {
30246
30302
  default: () => chat_default
30247
30303
  });
30248
- var import_react63, import_material54, import_styles34, import_react_router_dom5, import_lucide_react16, import_jsx_runtime53, ChatContent, Chat, chat_default;
30304
+ var import_react64, import_material54, import_styles34, import_react_router_dom6, import_lucide_react16, import_jsx_runtime53, ChatContent, Chat, chat_default;
30249
30305
  var init_chat2 = __esm({
30250
30306
  "src/chat/chat.tsx"() {
30251
30307
  "use strict";
30252
- import_react63 = require("react");
30308
+ import_react64 = require("react");
30253
30309
  init_custom_logo();
30254
30310
  init_indexedDBService();
30255
30311
  import_material54 = require("@mui/material");
@@ -30258,7 +30314,7 @@ var init_chat2 = __esm({
30258
30314
  init_modelStore();
30259
30315
  init_canvasStore();
30260
30316
  init_canvas_panel();
30261
- import_react_router_dom5 = require("react-router-dom");
30317
+ import_react_router_dom6 = require("react-router-dom");
30262
30318
  init_chat_scroll_to_bottom_button();
30263
30319
  init_bandit_chat_logo();
30264
30320
  init_chat_messages();
@@ -30291,6 +30347,8 @@ var init_chat2 = __esm({
30291
30347
  init_voiceModeStore();
30292
30348
  init_ttsSanitizer();
30293
30349
  init_FeatureFlagContext();
30350
+ init_knowledgeStore();
30351
+ init_lucide_icons();
30294
30352
  import_lucide_react16 = require("lucide-react");
30295
30353
  import_jsx_runtime53 = require("react/jsx-runtime");
30296
30354
  ChatContent = () => {
@@ -30301,12 +30359,12 @@ var init_chat2 = __esm({
30301
30359
  const ossMode = isOSSMode() || !packageSettings?.featureFlags?.subscriptionType;
30302
30360
  const playgroundBypassAccess = packageSettings?.playgroundBypassAuth || typeof window !== "undefined" && window.location.pathname.includes("/playground");
30303
30361
  const notificationService2 = useNotificationService();
30304
- const [selectedTheme, setSelectedTheme] = (0, import_react63.useState)(null);
30305
- const [themeLoading, setThemeLoading] = (0, import_react63.useState)(true);
30362
+ const [selectedTheme, setSelectedTheme] = (0, import_react64.useState)(null);
30363
+ const [themeLoading, setThemeLoading] = (0, import_react64.useState)(true);
30306
30364
  const token = authenticationService2.getToken();
30307
30365
  const claims = token ? authenticationService2.parseJwtClaims(token) : null;
30308
30366
  const baseTheme = themeMap_default[selectedTheme ?? "bandit-dark"] || banditDarkTheme;
30309
- const banditTheme = (0, import_react63.useMemo)(() => {
30367
+ const banditTheme = (0, import_react64.useMemo)(() => {
30310
30368
  return (0, import_styles34.createTheme)(baseTheme, {
30311
30369
  components: {
30312
30370
  MuiInputBase: {
@@ -30354,12 +30412,12 @@ var init_chat2 = __esm({
30354
30412
  } = useVoiceStore();
30355
30413
  const isVoiceModeEnabled = useVoiceModeStore((state) => state.enabled);
30356
30414
  const canvasOpen = useCanvasStore((state) => state.open);
30357
- const previousVoiceModeEnabledRef = (0, import_react63.useRef)(isVoiceModeEnabled);
30358
- const historyRef = (0, import_react63.useRef)(history);
30359
- (0, import_react63.useEffect)(() => {
30415
+ const previousVoiceModeEnabledRef = (0, import_react64.useRef)(isVoiceModeEnabled);
30416
+ const historyRef = (0, import_react64.useRef)(history);
30417
+ (0, import_react64.useEffect)(() => {
30360
30418
  historyRef.current = history;
30361
30419
  }, [history]);
30362
- (0, import_react63.useEffect)(() => {
30420
+ (0, import_react64.useEffect)(() => {
30363
30421
  if (!hydrated) return;
30364
30422
  const params = new URLSearchParams(window.location.search);
30365
30423
  const title = params.get("title") || "";
@@ -30375,7 +30433,7 @@ var init_chat2 = __esm({
30375
30433
  stop: ttsStop,
30376
30434
  isAvailable: isTTSAvailable
30377
30435
  } = useTTS();
30378
- (0, import_react63.useEffect)(() => {
30436
+ (0, import_react64.useEffect)(() => {
30379
30437
  const timer = setTimeout(() => {
30380
30438
  const isAuthenticated = authenticationService2.isAuthenticated();
30381
30439
  if (!initialized || availableVoices.length === 0) {
@@ -30389,7 +30447,7 @@ var init_chat2 = __esm({
30389
30447
  }, 500);
30390
30448
  return () => clearTimeout(timer);
30391
30449
  }, [initialized, availableVoices.length, loadVoicesFromAPI, token]);
30392
- (0, import_react63.useEffect)(() => {
30450
+ (0, import_react64.useEffect)(() => {
30393
30451
  const isAuthenticated = authenticationService2.isAuthenticated();
30394
30452
  if (packageSettings?.gatewayApiUrl && availableVoices.length === 0 && !initialized) {
30395
30453
  if (token && isAuthenticated) {
@@ -30401,19 +30459,82 @@ var init_chat2 = __esm({
30401
30459
  }
30402
30460
  }, [packageSettings?.gatewayApiUrl, availableVoices.length, initialized, loadVoicesFromAPI, token]);
30403
30461
  const provider = useAIProviderStore((state) => state.provider);
30404
- const inputRef = (0, import_react63.useRef)(null);
30405
- const [pastedImages, setPastedImages] = (0, import_react63.useState)([]);
30406
- const inputContainerRef = (0, import_react63.useRef)(null);
30407
- const [inputHeight, setInputHeight] = (0, import_react63.useState)(80);
30408
- const [isSubmitting, setIsSubmitting] = (0, import_react63.useState)(false);
30409
- const [pendingMessage, setPendingMessage] = (0, import_react63.useState)(null);
30462
+ const inputRef = (0, import_react64.useRef)(null);
30463
+ const [pastedImages, setPastedImages] = (0, import_react64.useState)([]);
30464
+ const inputContainerRef = (0, import_react64.useRef)(null);
30465
+ const [inputHeight, setInputHeight] = (0, import_react64.useState)(80);
30466
+ const [isSubmitting, setIsSubmitting] = (0, import_react64.useState)(false);
30467
+ const [pendingMessage, setPendingMessage] = (0, import_react64.useState)(null);
30410
30468
  const { conversations, currentId, _hasHydrated, hydrate, toggleIncognito } = useConversationStore();
30411
30469
  const isTemporaryChat = Boolean(conversations.find((c) => c.id === currentId)?.incognito);
30412
- const [isMobile, setIsMobile] = (0, import_react63.useState)(false);
30413
- const [drawerOpen, setDrawerOpen] = (0, import_react63.useState)(false);
30414
- const [mobileConversationsOpen, setMobileConversationsOpen] = (0, import_react63.useState)(false);
30415
- const cardSwipeStartXRef = (0, import_react63.useRef)(null);
30416
- (0, import_react63.useEffect)(() => {
30470
+ const [isMobile, setIsMobile] = (0, import_react64.useState)(false);
30471
+ const [drawerOpen, setDrawerOpen] = (0, import_react64.useState)(false);
30472
+ const [mobileConversationsOpen, setMobileConversationsOpen] = (0, import_react64.useState)(false);
30473
+ const cardSwipeStartXRef = (0, import_react64.useRef)(null);
30474
+ const dragStartRef = (0, import_react64.useRef)(null);
30475
+ const [drawerDragX, setDrawerDragX] = (0, import_react64.useState)(null);
30476
+ const [drawerSettling, setDrawerSettling] = (0, import_react64.useState)(false);
30477
+ const [drawerSwap, setDrawerSwap] = (0, import_react64.useState)(false);
30478
+ const drawerOpenOffset = () => (typeof window === "undefined" ? 360 : Math.min(window.innerWidth - 56, 360)) - 12;
30479
+ const drawerDragStart = (e) => {
30480
+ const t = e.touches[0];
30481
+ if (!t) return;
30482
+ dragStartRef.current = { x: t.clientX, y: t.clientY, t: performance.now(), axis: null };
30483
+ };
30484
+ const drawerDragMove = (e) => {
30485
+ const s = dragStartRef.current;
30486
+ const t = e.touches[0];
30487
+ if (!s || !t) return;
30488
+ const dx = t.clientX - s.x;
30489
+ const dy = t.clientY - s.y;
30490
+ if (s.axis === null) {
30491
+ if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return;
30492
+ s.axis = Math.abs(dx) > Math.abs(dy) ? "x" : "y";
30493
+ }
30494
+ if (s.axis !== "x") return;
30495
+ setDrawerDragX(dx < 0 ? Math.max(dx, -drawerOpenOffset()) : dx * 0.15);
30496
+ };
30497
+ const drawerDragEnd = () => {
30498
+ const s = dragStartRef.current;
30499
+ dragStartRef.current = null;
30500
+ if (!s || s.axis !== "x" || drawerDragX === null) {
30501
+ setDrawerDragX(null);
30502
+ return;
30503
+ }
30504
+ const dt = Math.max(1, performance.now() - s.t);
30505
+ const velocity = drawerDragX / dt;
30506
+ const offset = drawerOpenOffset();
30507
+ const shouldClose = drawerDragX < -offset * 0.35 || velocity < -0.45;
30508
+ if (shouldClose) {
30509
+ setDrawerSettling(true);
30510
+ setDrawerDragX(-offset);
30511
+ window.setTimeout(() => {
30512
+ setDrawerSwap(true);
30513
+ setMobileConversationsOpen(false);
30514
+ requestAnimationFrame(() => requestAnimationFrame(() => setDrawerSwap(false)));
30515
+ window.setTimeout(() => {
30516
+ setDrawerDragX(null);
30517
+ setDrawerSettling(false);
30518
+ }, 320);
30519
+ }, 220);
30520
+ } else {
30521
+ setDrawerSettling(true);
30522
+ setDrawerDragX(0);
30523
+ window.setTimeout(() => {
30524
+ setDrawerDragX(null);
30525
+ setDrawerSettling(false);
30526
+ }, 220);
30527
+ }
30528
+ };
30529
+ const drawerDragStyle = drawerSwap ? { translate: "0px 0", transition: "none" } : isMobile && mobileConversationsOpen && drawerDragX !== null ? {
30530
+ translate: `${drawerDragX}px 0`,
30531
+ transition: drawerSettling ? "translate 0.22s cubic-bezier(0.2, 0.8, 0.2, 1)" : "none"
30532
+ } : void 0;
30533
+ const drawerPanelDragStyle = isMobile && drawerDragX !== null ? {
30534
+ translate: `${drawerDragX}px 0`,
30535
+ transition: drawerSettling ? "translate 0.22s cubic-bezier(0.2, 0.8, 0.2, 1)" : "none"
30536
+ } : void 0;
30537
+ (0, import_react64.useEffect)(() => {
30417
30538
  if (typeof document === "undefined") return;
30418
30539
  const meta = document.querySelector('meta[name="theme-color"]');
30419
30540
  if (!meta) return;
@@ -30431,27 +30552,50 @@ var init_chat2 = __esm({
30431
30552
  const chatContainerEl = chatContainerRef.current;
30432
30553
  const scrollTargetEl = scrollTargetRef.current;
30433
30554
  const { isSlowConnection, connectionQuality, trackRequestStart, trackRequestEnd } = useNetworkStatus();
30434
- const [showScrollToBottom, setShowScrollToBottom] = (0, import_react63.useState)(false);
30435
- const [streamBuffer, setStreamBuffer] = (0, import_react63.useState)("");
30436
- const [responseStarted, setResponseStarted] = (0, import_react63.useState)(false);
30437
- const [isStreaming, setIsStreaming] = (0, import_react63.useState)(false);
30438
- const [isThinking, setIsThinking] = (0, import_react63.useState)(false);
30555
+ const [showScrollToBottom, setShowScrollToBottom] = (0, import_react64.useState)(false);
30556
+ const [streamBuffer, setStreamBuffer] = (0, import_react64.useState)("");
30557
+ const [responseStarted, setResponseStarted] = (0, import_react64.useState)(false);
30558
+ const [isStreaming, setIsStreaming] = (0, import_react64.useState)(false);
30559
+ const [isThinking, setIsThinking] = (0, import_react64.useState)(false);
30439
30560
  const initialLogoState = history.length === 0;
30440
- const [logoVisible, setLogoVisible] = (0, import_react63.useState)(initialLogoState);
30441
- const [logoShouldRender, setLogoShouldRender] = (0, import_react63.useState)(initialLogoState);
30442
- const streamingGraceUntilRef = (0, import_react63.useRef)(0);
30561
+ const [logoVisible, setLogoVisible] = (0, import_react64.useState)(initialLogoState);
30562
+ const [logoShouldRender, setLogoShouldRender] = (0, import_react64.useState)(initialLogoState);
30563
+ const streamingGraceUntilRef = (0, import_react64.useRef)(0);
30443
30564
  const GRACE_MS = 450;
30444
30565
  const GRACE_OFFSET_DESKTOP = 28;
30445
30566
  const GRACE_OFFSET_MOBILE = 20;
30446
- const followStreamRef = (0, import_react63.useRef)(true);
30447
- const lastSpokenResponseRef = (0, import_react63.useRef)(null);
30448
- const previousConversationIdRef = (0, import_react63.useRef)(null);
30449
- const logoFadeTimeoutRef = (0, import_react63.useRef)(null);
30450
- const [branding, setBranding] = (0, import_react63.useState)(null);
30451
- const [brandingLoading, setBrandingLoading] = (0, import_react63.useState)(true);
30452
- const isBrandingLoadInProgressRef = (0, import_react63.useRef)(false);
30567
+ const followStreamRef = (0, import_react64.useRef)(true);
30568
+ const lastSpokenResponseRef = (0, import_react64.useRef)(null);
30569
+ const previousConversationIdRef = (0, import_react64.useRef)(null);
30570
+ const logoFadeTimeoutRef = (0, import_react64.useRef)(null);
30571
+ const [branding, setBranding] = (0, import_react64.useState)(null);
30572
+ const [brandingLoading, setBrandingLoading] = (0, import_react64.useState)(true);
30573
+ const isBrandingLoadInProgressRef = (0, import_react64.useRef)(false);
30453
30574
  const logoOnly = history.length === 0 && !brandingLoading;
30454
- (0, import_react63.useEffect)(() => {
30575
+ const knowledgeDocs = useKnowledgeStore((s) => s.docs);
30576
+ const knowledgeLoaded = useKnowledgeStore((s) => s.isLoaded);
30577
+ const [knowledgeNudgeDismissed, setKnowledgeNudgeDismissed] = (0, import_react64.useState)(() => {
30578
+ try {
30579
+ return localStorage.getItem("bandit.knowledgeNudge.dismissed") === "true";
30580
+ } catch {
30581
+ return false;
30582
+ }
30583
+ });
30584
+ const dismissKnowledgeNudge = (0, import_react64.useCallback)(() => {
30585
+ setKnowledgeNudgeDismissed(true);
30586
+ try {
30587
+ localStorage.setItem("bandit.knowledgeNudge.dismissed", "true");
30588
+ } catch {
30589
+ }
30590
+ }, []);
30591
+ const showKnowledgeNudge = logoOnly && !knowledgeNudgeDismissed && knowledgeLoaded && knowledgeDocs.length === 0 && featureFlag.hasFeature("documentKnowledge");
30592
+ const knowledgePath = typeof window !== "undefined" && window.location.pathname.startsWith("/playground") ? "/playground/management" : "/management";
30593
+ const safeNavigateTo = (to) => {
30594
+ if (typeof window !== "undefined") {
30595
+ window.location.assign(to);
30596
+ }
30597
+ };
30598
+ (0, import_react64.useEffect)(() => {
30455
30599
  const isEmptyConversation = (history?.length ?? 0) === 0;
30456
30600
  const isSending = Boolean(pendingMessage);
30457
30601
  const shouldShowLogo = isEmptyConversation && !isSending;
@@ -30481,7 +30625,7 @@ var init_chat2 = __esm({
30481
30625
  }, 500);
30482
30626
  }
30483
30627
  }, [history, pendingMessage]);
30484
- (0, import_react63.useEffect)(() => {
30628
+ (0, import_react64.useEffect)(() => {
30485
30629
  if (!brandingLoading && !themeLoading) {
30486
30630
  return;
30487
30631
  }
@@ -30497,18 +30641,18 @@ var init_chat2 = __esm({
30497
30641
  }, 2500);
30498
30642
  return () => window.clearTimeout(timeoutId);
30499
30643
  }, [brandingLoading, themeLoading, selectedTheme]);
30500
- (0, import_react63.useEffect)(() => () => {
30644
+ (0, import_react64.useEffect)(() => () => {
30501
30645
  if (logoFadeTimeoutRef.current) {
30502
30646
  window.clearTimeout(logoFadeTimeoutRef.current);
30503
30647
  logoFadeTimeoutRef.current = null;
30504
30648
  }
30505
30649
  }, []);
30506
- (0, import_react63.useEffect)(() => {
30650
+ (0, import_react64.useEffect)(() => {
30507
30651
  if (isStreaming && streamBuffer.trim() === "") {
30508
30652
  streamingGraceUntilRef.current = Date.now() + GRACE_MS;
30509
30653
  }
30510
30654
  }, [isStreaming, streamBuffer]);
30511
- (0, import_react63.useEffect)(() => {
30655
+ (0, import_react64.useEffect)(() => {
30512
30656
  if (!isStreaming) return;
30513
30657
  const container = chatContainerRef.current;
30514
30658
  if (!container) return;
@@ -30519,13 +30663,13 @@ var init_chat2 = __esm({
30519
30663
  container.scrollTo({ top: targetTop, behavior: "smooth" });
30520
30664
  }
30521
30665
  }, [streamBuffer, isStreaming, isMobile, chatContainerRef]);
30522
- (0, import_react63.useEffect)(() => {
30666
+ (0, import_react64.useEffect)(() => {
30523
30667
  if (!_hasHydrated) {
30524
30668
  debugLogger.info("Chat component triggering conversation store hydration");
30525
30669
  hydrate();
30526
30670
  }
30527
30671
  }, [_hasHydrated, hydrate]);
30528
- (0, import_react63.useEffect)(() => {
30672
+ (0, import_react64.useEffect)(() => {
30529
30673
  const loadBrandingAndTheme = async () => {
30530
30674
  if (isBrandingLoadInProgressRef.current) {
30531
30675
  debugLogger.warn("Branding loading already in progress, skipping");
@@ -30714,7 +30858,7 @@ var init_chat2 = __esm({
30714
30858
  window.removeEventListener("bandit-theme-changed", handleThemeChange);
30715
30859
  };
30716
30860
  }, []);
30717
- (0, import_react63.useEffect)(() => {
30861
+ (0, import_react64.useEffect)(() => {
30718
30862
  if (!chatContainerEl) return;
30719
30863
  const blurInputOnTouchScroll = () => {
30720
30864
  if (document.activeElement === inputRef.current) inputRef.current?.blur();
@@ -30722,13 +30866,13 @@ var init_chat2 = __esm({
30722
30866
  chatContainerEl.addEventListener("touchmove", blurInputOnTouchScroll, { passive: true });
30723
30867
  return () => chatContainerEl.removeEventListener("touchmove", blurInputOnTouchScroll);
30724
30868
  }, [chatContainerEl]);
30725
- (0, import_react63.useEffect)(() => {
30869
+ (0, import_react64.useEffect)(() => {
30726
30870
  const handleResize = () => setIsMobile(window.innerWidth <= 768);
30727
30871
  handleResize();
30728
30872
  window.addEventListener("resize", handleResize);
30729
30873
  return () => window.removeEventListener("resize", handleResize);
30730
30874
  }, []);
30731
- (0, import_react63.useEffect)(() => {
30875
+ (0, import_react64.useEffect)(() => {
30732
30876
  const setViewportHeight = () => {
30733
30877
  document.documentElement.style.setProperty(
30734
30878
  "--vh",
@@ -30739,7 +30883,7 @@ var init_chat2 = __esm({
30739
30883
  window.addEventListener("resize", setViewportHeight);
30740
30884
  return () => window.removeEventListener("resize", setViewportHeight);
30741
30885
  }, []);
30742
- (0, import_react63.useEffect)(() => {
30886
+ (0, import_react64.useEffect)(() => {
30743
30887
  if (!chatContainerEl) return;
30744
30888
  let rafId = null;
30745
30889
  const update = () => {
@@ -30758,7 +30902,7 @@ var init_chat2 = __esm({
30758
30902
  chatContainerEl.removeEventListener(SCROLL_STATE_CHANGED_EVENT, update);
30759
30903
  };
30760
30904
  }, [chatContainerEl, getScrollState]);
30761
- (0, import_react63.useEffect)(() => {
30905
+ (0, import_react64.useEffect)(() => {
30762
30906
  if (!chatContainerEl) return;
30763
30907
  let observer = null;
30764
30908
  let rafId = null;
@@ -30793,7 +30937,7 @@ var init_chat2 = __esm({
30793
30937
  if (observer) observer.disconnect();
30794
30938
  };
30795
30939
  }, [chatContainerEl, scrollTargetEl, scrollTargetRef, getScrollState, inputHeight, history.length]);
30796
- (0, import_react63.useEffect)(() => {
30940
+ (0, import_react64.useEffect)(() => {
30797
30941
  const isTransitioning = isStreaming || streamBuffer.trim() === "";
30798
30942
  const delay = isTransitioning ? 400 : 100;
30799
30943
  const timer = setTimeout(() => {
@@ -30802,7 +30946,7 @@ var init_chat2 = __esm({
30802
30946
  }, delay);
30803
30947
  return () => clearTimeout(timer);
30804
30948
  }, [history, streamBuffer, getScrollState, isStreaming]);
30805
- (0, import_react63.useLayoutEffect)(() => {
30949
+ (0, import_react64.useLayoutEffect)(() => {
30806
30950
  const scrollState = getScrollState();
30807
30951
  const now = Date.now();
30808
30952
  if (isStreaming && scrollState.shouldAutoScroll && followStreamRef.current) {
@@ -30836,7 +30980,7 @@ var init_chat2 = __esm({
30836
30980
  return () => clearTimeout(scrollTimer);
30837
30981
  }
30838
30982
  }, [history, isStreaming, scrollToBottom, getScrollState, isMobile, chatContainerRef]);
30839
- (0, import_react63.useEffect)(() => {
30983
+ (0, import_react64.useEffect)(() => {
30840
30984
  const observer = new ResizeObserver(() => {
30841
30985
  if (inputContainerRef.current)
30842
30986
  setInputHeight(inputContainerRef.current.offsetHeight);
@@ -30847,7 +30991,7 @@ var init_chat2 = __esm({
30847
30991
  }
30848
30992
  return () => observer.disconnect();
30849
30993
  }, []);
30850
- (0, import_react63.useEffect)(() => {
30994
+ (0, import_react64.useEffect)(() => {
30851
30995
  if (!hydrated || !_hasHydrated) return;
30852
30996
  if (currentId === null) {
30853
30997
  useAIQueryStore.setState({ history: [] });
@@ -30896,7 +31040,7 @@ var init_chat2 = __esm({
30896
31040
  setResponse,
30897
31041
  setComponentStatus
30898
31042
  ]);
30899
- (0, import_react63.useEffect)(() => {
31043
+ (0, import_react64.useEffect)(() => {
30900
31044
  debugLogger.info("Chat component conversation state", {
30901
31045
  hasHydrated: _hasHydrated,
30902
31046
  conversationCount: conversations.length,
@@ -30905,7 +31049,7 @@ var init_chat2 = __esm({
30905
31049
  storeState: "main-chat"
30906
31050
  });
30907
31051
  }, [_hasHydrated, conversations, currentId]);
30908
- (0, import_react63.useEffect)(() => {
31052
+ (0, import_react64.useEffect)(() => {
30909
31053
  if (!_hasHydrated || !chatContainerEl) return;
30910
31054
  let rafId = null;
30911
31055
  const sync = () => {
@@ -30940,7 +31084,7 @@ var init_chat2 = __esm({
30940
31084
  notificationService2?.handleHttpError(error);
30941
31085
  }
30942
31086
  });
30943
- const handleStop = (0, import_react63.useCallback)(() => {
31087
+ const handleStop = (0, import_react64.useCallback)(() => {
30944
31088
  try {
30945
31089
  aiProvider.cancel();
30946
31090
  } catch (error) {
@@ -30949,7 +31093,7 @@ var init_chat2 = __esm({
30949
31093
  });
30950
31094
  }
30951
31095
  }, [aiProvider]);
30952
- const handleVoiceInterrupt = (0, import_react63.useCallback)(() => {
31096
+ const handleVoiceInterrupt = (0, import_react64.useCallback)(() => {
30953
31097
  try {
30954
31098
  ttsStop();
30955
31099
  } catch (error) {
@@ -30968,7 +31112,7 @@ var init_chat2 = __esm({
30968
31112
  handleStop();
30969
31113
  }
30970
31114
  }, [ttsStop, isStreaming, handleStop]);
30971
- const handleSend = (0, import_react63.useCallback)(
31115
+ const handleSend = (0, import_react64.useCallback)(
30972
31116
  (question, images, displayQuestion) => {
30973
31117
  const requestStartTime = trackRequestStart();
30974
31118
  const questionForDisplay = displayQuestion || question;
@@ -31078,7 +31222,7 @@ var init_chat2 = __esm({
31078
31222
  trackRequestStart
31079
31223
  ]
31080
31224
  );
31081
- const handleVoiceTranscription = (0, import_react63.useCallback)(
31225
+ const handleVoiceTranscription = (0, import_react64.useCallback)(
31082
31226
  (text) => {
31083
31227
  const cleaned = text.trim();
31084
31228
  if (!cleaned) {
@@ -31095,7 +31239,7 @@ var init_chat2 = __esm({
31095
31239
  onInterrupt: handleVoiceInterrupt,
31096
31240
  onError: (message) => notificationService2?.showError?.(message)
31097
31241
  });
31098
- (0, import_react63.useEffect)(() => {
31242
+ (0, import_react64.useEffect)(() => {
31099
31243
  const previouslyEnabled = previousVoiceModeEnabledRef.current;
31100
31244
  previousVoiceModeEnabledRef.current = isVoiceModeEnabled;
31101
31245
  if (!previouslyEnabled && isVoiceModeEnabled) {
@@ -31120,7 +31264,7 @@ var init_chat2 = __esm({
31120
31264
  }
31121
31265
  }
31122
31266
  }, [isVoiceModeEnabled, ttsStop]);
31123
- (0, import_react63.useEffect)(() => {
31267
+ (0, import_react64.useEffect)(() => {
31124
31268
  if (!isVoiceModeEnabled || !isStreaming) {
31125
31269
  return;
31126
31270
  }
@@ -31140,7 +31284,7 @@ var init_chat2 = __esm({
31140
31284
  }
31141
31285
  lastSpokenResponseRef.current = null;
31142
31286
  }, [isStreaming, isVoiceModeEnabled, ttsStop]);
31143
- (0, import_react63.useEffect)(() => {
31287
+ (0, import_react64.useEffect)(() => {
31144
31288
  if (!isVoiceModeEnabled) {
31145
31289
  lastSpokenResponseRef.current = null;
31146
31290
  return;
@@ -31181,13 +31325,13 @@ var init_chat2 = __esm({
31181
31325
  cancelled = true;
31182
31326
  };
31183
31327
  }, [history, isStreaming, isVoiceModeEnabled, isTTSAvailable, ttsSpeak]);
31184
- const handleModelChange = (0, import_react63.useCallback)(
31328
+ const handleModelChange = (0, import_react64.useCallback)(
31185
31329
  (modelId) => {
31186
31330
  setSelectedModel(modelId);
31187
31331
  },
31188
31332
  [setSelectedModel]
31189
31333
  );
31190
- const handleVoiceChange = (0, import_react63.useCallback)(async (voiceId) => {
31334
+ const handleVoiceChange = (0, import_react64.useCallback)(async (voiceId) => {
31191
31335
  ttsStop();
31192
31336
  stopTTS();
31193
31337
  setSelectedVoice(voiceId);
@@ -31297,27 +31441,23 @@ var init_chat2 = __esm({
31297
31441
  // is the same near-black as the shell.
31298
31442
  border: isMobile && mobileConversationsOpen ? `1px solid ${theme.palette.mode === "dark" ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.12)"}` : "none",
31299
31443
  boxShadow: isMobile && mobileConversationsOpen ? `0 12px 40px ${theme.palette.mode === "dark" ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.25)"}` : "none",
31300
- transition: "left 0.3s ease-in-out, width 0.3s ease-in-out, transform 0.3s ease-in-out, border-radius 0.3s ease-in-out"
31444
+ transition: drawerSwap ? "none" : "left 0.3s ease-in-out, width 0.3s ease-in-out, transform 0.3s ease-in-out, border-radius 0.3s ease-in-out"
31301
31445
  }),
31302
31446
  onClick: isMobile && mobileConversationsOpen ? (e) => {
31303
31447
  if (e.currentTarget.contains(e.target)) {
31304
31448
  setMobileConversationsOpen(false);
31305
31449
  }
31306
31450
  } : void 0,
31451
+ style: drawerDragStyle,
31307
31452
  onTouchStart: isMobile && mobileConversationsOpen ? (e) => {
31308
31453
  if (e.currentTarget.contains(e.target)) {
31309
31454
  cardSwipeStartXRef.current = e.touches[0]?.clientX ?? null;
31455
+ drawerDragStart(e);
31310
31456
  }
31311
31457
  } : void 0,
31312
- onTouchMove: isMobile && mobileConversationsOpen ? (e) => {
31313
- const start = cardSwipeStartXRef.current;
31314
- if (start === null) return;
31315
- const dx = (e.touches[0]?.clientX ?? start) - start;
31316
- if (Math.abs(dx) > 40) {
31317
- cardSwipeStartXRef.current = null;
31318
- setMobileConversationsOpen(false);
31319
- }
31320
- } : void 0,
31458
+ onTouchMove: isMobile && mobileConversationsOpen ? drawerDragMove : void 0,
31459
+ onTouchEnd: isMobile && mobileConversationsOpen ? drawerDragEnd : void 0,
31460
+ onTouchCancel: isMobile && mobileConversationsOpen ? drawerDragEnd : void 0,
31321
31461
  children: [
31322
31462
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31323
31463
  chat_app_bar_default,
@@ -31330,7 +31470,13 @@ var init_chat2 = __esm({
31330
31470
  drawerOpen,
31331
31471
  setDrawerOpen,
31332
31472
  mobileConversationsOpen,
31333
- onMobileConversationsOpenChange: setMobileConversationsOpen
31473
+ onMobileConversationsOpenChange: setMobileConversationsOpen,
31474
+ mobileConversationsDrag: isMobile ? {
31475
+ style: drawerPanelDragStyle,
31476
+ onTouchStart: drawerDragStart,
31477
+ onTouchMove: drawerDragMove,
31478
+ onTouchEnd: drawerDragEnd
31479
+ } : void 0
31334
31480
  }
31335
31481
  ),
31336
31482
  isTemporaryChat && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
@@ -31486,6 +31632,53 @@ var init_chat2 = __esm({
31486
31632
  }
31487
31633
  return branding?.logoBase64 ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(custom_logo_default, { visible: logoVisible, atTop: true }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(bandit_chat_logo_default, { visible: logoVisible, atTop: true });
31488
31634
  })(),
31635
+ showKnowledgeNudge && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
31636
+ import_material54.Box,
31637
+ {
31638
+ sx: (theme) => ({
31639
+ mx: "auto",
31640
+ mt: 2,
31641
+ mb: 1,
31642
+ px: 2,
31643
+ py: 1.25,
31644
+ maxWidth: 420,
31645
+ display: "flex",
31646
+ alignItems: "center",
31647
+ gap: 1.25,
31648
+ borderRadius: 2.5,
31649
+ border: `1px solid ${theme.palette.divider}`,
31650
+ bgcolor: theme.palette.action.hover,
31651
+ opacity: logoVisible ? 1 : 0,
31652
+ transition: "opacity 0.5s ease"
31653
+ }),
31654
+ children: [
31655
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_material54.Box, { sx: { flex: 1, minWidth: 0 }, children: [
31656
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_material54.Typography, { variant: "body2", sx: { fontWeight: 600 }, children: "Add your documents" }),
31657
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_material54.Typography, { variant: "caption", color: "text.secondary", children: "Upload your policies, handbooks or notes and Bandit answers from them, with sources." })
31658
+ ] }),
31659
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31660
+ import_material54.Button,
31661
+ {
31662
+ size: "small",
31663
+ variant: "outlined",
31664
+ onClick: () => safeNavigateTo(knowledgePath),
31665
+ sx: { textTransform: "none", fontWeight: 600, flexShrink: 0 },
31666
+ children: "Upload"
31667
+ }
31668
+ ),
31669
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31670
+ import_material54.IconButton,
31671
+ {
31672
+ size: "small",
31673
+ "aria-label": "Dismiss",
31674
+ onClick: dismissKnowledgeNudge,
31675
+ sx: { flexShrink: 0 },
31676
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(CloseIcon, { fontSize: "small" })
31677
+ }
31678
+ )
31679
+ ]
31680
+ }
31681
+ ),
31489
31682
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31490
31683
  import_material54.Box,
31491
31684
  {
@@ -31622,7 +31815,7 @@ var init_chat2 = __esm({
31622
31815
  });
31623
31816
  if (!allowUnauthenticated && !bypassAuth && !authenticationService2.isAuthenticated()) {
31624
31817
  debugLogger.debug("User is not authenticated, redirecting to login");
31625
- return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_router_dom5.Navigate, { to: "/login", replace: true });
31818
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_router_dom6.Navigate, { to: "/login", replace: true });
31626
31819
  }
31627
31820
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ChatContent, {});
31628
31821
  };
@@ -31636,13 +31829,13 @@ __export(management_exports, {
31636
31829
  default: () => management_default
31637
31830
  });
31638
31831
  module.exports = __toCommonJS(management_exports);
31639
- var import_react64 = require("react");
31832
+ var import_react65 = require("react");
31640
31833
  var import_useMediaQuery2 = __toESM(require("@mui/material/useMediaQuery"));
31641
31834
  var import_styles35 = require("@mui/material/styles");
31642
31835
  init_useKnowledgeStore();
31643
31836
  init_indexedDBService();
31644
31837
  var import_material55 = require("@mui/material");
31645
- var import_react_router_dom6 = require("react-router-dom");
31838
+ var import_react_router_dom7 = require("react-router-dom");
31646
31839
 
31647
31840
  // src/modals/chat-modal/chat-modal.tsx
31648
31841
  var import_react23 = require("react");
@@ -36463,6 +36656,7 @@ var useGatewayHealth = (options) => {
36463
36656
  };
36464
36657
 
36465
36658
  // src/management/components/PreferencesTab.tsx
36659
+ init_installedApp();
36466
36660
  init_lucide_icons();
36467
36661
  var import_jsx_runtime19 = require("react/jsx-runtime");
36468
36662
  var PreferencesTab = ({
@@ -37319,9 +37513,8 @@ var ViewportDiagnostics = () => {
37319
37513
  probe.style.cssText = "position:fixed;top:0;bottom:0;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px);visibility:hidden;pointer-events:none;";
37320
37514
  document.body.appendChild(probe);
37321
37515
  const cs = getComputedStyle(probe);
37322
- const standalone = window.matchMedia?.("(display-mode: standalone)").matches || window.navigator.standalone === true;
37323
37516
  setMetrics({
37324
- mode: standalone ? "standalone" : "browser",
37517
+ mode: isNativeShell() ? "native" : isStandalonePwa2() ? "standalone" : "browser",
37325
37518
  "screen.height": `${window.screen.height}`,
37326
37519
  innerHeight: `${window.innerHeight}`,
37327
37520
  "docElement.clientHeight": `${document.documentElement.clientHeight}`,
@@ -44335,10 +44528,10 @@ var buildCapabilitiesUrl = (gatewayApiUrl) => {
44335
44528
  return `${trimmed}/api/capabilities`;
44336
44529
  };
44337
44530
  var Management = () => {
44338
- const navigate = (0, import_react_router_dom6.useNavigate)();
44531
+ const navigate = (0, import_react_router_dom7.useNavigate)();
44339
44532
  const notificationService2 = useNotificationService();
44340
44533
  const isMobile = (0, import_useMediaQuery2.default)("(max-width:900px)");
44341
- const [sidebarOpen, setSidebarOpen] = (0, import_react64.useState)(false);
44534
+ const [sidebarOpen, setSidebarOpen] = (0, import_react65.useState)(false);
44342
44535
  const getOptimalFabLogo = async () => {
44343
44536
  const banditHead6 = "https://cdn.burtson.ai/images/bandit-head.png";
44344
44537
  try {
@@ -44378,17 +44571,17 @@ var Management = () => {
44378
44571
  hasTransparentLogo,
44379
44572
  setHasTransparentLogo
44380
44573
  } = useModelStore();
44381
- const [modalOpen, setModalOpen] = (0, import_react64.useState)(false);
44574
+ const [modalOpen, setModalOpen] = (0, import_react65.useState)(false);
44382
44575
  const banditHead5 = "https://cdn.burtson.ai/images/bandit-head.png";
44383
- const [fabLogo, setFabLogo] = (0, import_react64.useState)(banditHead5);
44384
- const [tabIndex, setTabIndex] = (0, import_react64.useState)(4);
44385
- const [logoFile, setLogoFile] = (0, import_react64.useState)(null);
44386
- const [logoBase64, setLogoBase64] = (0, import_react64.useState)(null);
44387
- const [brandingText, setBrandingText] = (0, import_react64.useState)("");
44388
- const [headerMode, setHeaderMode] = (0, import_react64.useState)("logo");
44389
- const [theme, setTheme] = (0, import_react64.useState)("bandit-dark");
44390
- const [customAvatarBase64, setCustomAvatarBase64] = (0, import_react64.useState)(null);
44391
- const [presetAvatar, setPresetAvatar] = (0, import_react64.useState)(null);
44576
+ const [fabLogo, setFabLogo] = (0, import_react65.useState)(banditHead5);
44577
+ const [tabIndex, setTabIndex] = (0, import_react65.useState)(4);
44578
+ const [logoFile, setLogoFile] = (0, import_react65.useState)(null);
44579
+ const [logoBase64, setLogoBase64] = (0, import_react65.useState)(null);
44580
+ const [brandingText, setBrandingText] = (0, import_react65.useState)("");
44581
+ const [headerMode, setHeaderMode] = (0, import_react65.useState)("logo");
44582
+ const [theme, setTheme] = (0, import_react65.useState)("bandit-dark");
44583
+ const [customAvatarBase64, setCustomAvatarBase64] = (0, import_react65.useState)(null);
44584
+ const [presetAvatar, setPresetAvatar] = (0, import_react65.useState)(null);
44392
44585
  const showSnackbarMessage = (message, severity = "success") => {
44393
44586
  if (severity === "success") {
44394
44587
  notificationService2?.showSuccess(message);
@@ -44396,9 +44589,9 @@ var Management = () => {
44396
44589
  notificationService2?.showError(message);
44397
44590
  }
44398
44591
  };
44399
- const [restoreDialogOpen, setRestoreDialogOpen] = (0, import_react64.useState)(false);
44400
- const [brandingLoaded, setBrandingLoaded] = (0, import_react64.useState)(false);
44401
- const [isLoadingBranding, setIsLoadingBranding] = (0, import_react64.useState)(false);
44592
+ const [restoreDialogOpen, setRestoreDialogOpen] = (0, import_react65.useState)(false);
44593
+ const [brandingLoaded, setBrandingLoaded] = (0, import_react65.useState)(false);
44594
+ const [isLoadingBranding, setIsLoadingBranding] = (0, import_react65.useState)(false);
44402
44595
  const { initModels } = useModelStore();
44403
44596
  const { settings: packageSettings } = usePackageSettingsStore();
44404
44597
  const authToken = useAuthenticationStore((state) => state.token);
@@ -44406,8 +44599,8 @@ var Management = () => {
44406
44599
  const { hasAdminDashboard, hasLimitedAdminDashboard, getCurrentTier, hasAdvancedSearch } = useFeatures();
44407
44600
  const { showAdminPanel, showLimitedAdminPanel } = useFeatureVisibility();
44408
44601
  const { provider: currentProvider, config: currentProviderConfig } = useAIProviderStore();
44409
- const [seedPacksEnabled, setSeedPacksEnabled] = (0, import_react64.useState)(false);
44410
- const [localSelectedModel, setLocalSelectedModel] = (0, import_react64.useState)({
44602
+ const [seedPacksEnabled, setSeedPacksEnabled] = (0, import_react65.useState)(false);
44603
+ const [localSelectedModel, setLocalSelectedModel] = (0, import_react65.useState)({
44411
44604
  name: "",
44412
44605
  tagline: "",
44413
44606
  systemPrompt: "",
@@ -44420,7 +44613,7 @@ var Management = () => {
44420
44613
  loadDocuments,
44421
44614
  clearAllDocuments
44422
44615
  } = useKnowledgeStore2();
44423
- (0, import_react64.useEffect)(() => {
44616
+ (0, import_react65.useEffect)(() => {
44424
44617
  if (selectedModel) {
44425
44618
  const selected = availableModels.find((m) => m.name === selectedModel);
44426
44619
  if (selected) {
@@ -44444,7 +44637,7 @@ var Management = () => {
44444
44637
  }
44445
44638
  }
44446
44639
  }, [selectedModel, availableModels]);
44447
- const loadBrandingConfig = (0, import_react64.useCallback)(async () => {
44640
+ const loadBrandingConfig = (0, import_react65.useCallback)(async () => {
44448
44641
  if (isLoadingBranding || brandingLoaded) {
44449
44642
  debugLogger.warn("Branding loading already in progress or completed, skipping");
44450
44643
  return;
@@ -44561,15 +44754,15 @@ var Management = () => {
44561
44754
  setTagline,
44562
44755
  setTheme
44563
44756
  ]);
44564
- (0, import_react64.useEffect)(() => {
44757
+ (0, import_react65.useEffect)(() => {
44565
44758
  void loadBrandingConfig();
44566
44759
  }, [loadBrandingConfig]);
44567
44760
  const handleOpenModal = () => setModalOpen(true);
44568
44761
  const handleCloseModal = () => setModalOpen(false);
44569
- (0, import_react64.useEffect)(() => {
44762
+ (0, import_react65.useEffect)(() => {
44570
44763
  getOptimalFabLogo().then(setFabLogo);
44571
44764
  }, []);
44572
- (0, import_react64.useEffect)(() => {
44765
+ (0, import_react65.useEffect)(() => {
44573
44766
  if (logoBase64) {
44574
44767
  setFabLogo(logoBase64);
44575
44768
  } else {
@@ -45022,7 +45215,7 @@ var Management = () => {
45022
45215
  reader.readAsText(file);
45023
45216
  }
45024
45217
  };
45025
- (0, import_react64.useEffect)(() => {
45218
+ (0, import_react65.useEffect)(() => {
45026
45219
  if (localSelectedModel.selectedModel && !availableModels.some((m) => m.name === localSelectedModel.selectedModel)) {
45027
45220
  setLocalSelectedModel((prev) => ({
45028
45221
  ...prev,
@@ -45030,10 +45223,10 @@ var Management = () => {
45030
45223
  }));
45031
45224
  }
45032
45225
  }, [availableModels, localSelectedModel.selectedModel]);
45033
- (0, import_react64.useEffect)(() => {
45226
+ (0, import_react65.useEffect)(() => {
45034
45227
  loadDocuments();
45035
45228
  }, [loadDocuments]);
45036
- (0, import_react64.useEffect)(() => {
45229
+ (0, import_react65.useEffect)(() => {
45037
45230
  const gatewayApiUrl = packageSettings?.gatewayApiUrl;
45038
45231
  if (!gatewayApiUrl || gatewayApiUrl.toLowerCase().startsWith("playground://")) {
45039
45232
  setSeedPacksEnabled(false);
@@ -45076,7 +45269,7 @@ var Management = () => {
45076
45269
  isActive = false;
45077
45270
  };
45078
45271
  }, [packageSettings?.gatewayApiUrl, authToken]);
45079
- const currentTheme = (0, import_react64.useMemo)(() => {
45272
+ const currentTheme = (0, import_react65.useMemo)(() => {
45080
45273
  const baseTheme = predefinedThemes[theme] || banditDarkTheme;
45081
45274
  return (0, import_styles35.createTheme)(baseTheme, {
45082
45275
  // Management-scoped density: condenses every settings tab at once. This
@@ -45197,7 +45390,7 @@ var Management = () => {
45197
45390
  });
45198
45391
  const preferredDefaultTabIndex = navTabs.findIndex((tab) => tab.label === "Preferences");
45199
45392
  const defaultTabIndex = preferredDefaultTabIndex >= 0 ? preferredDefaultTabIndex : 0;
45200
- (0, import_react64.useEffect)(() => {
45393
+ (0, import_react65.useEffect)(() => {
45201
45394
  setTabIndex((current) => {
45202
45395
  if (current < 0 || current >= navTabs.length) {
45203
45396
  return defaultTabIndex;
@@ -45354,6 +45547,10 @@ var Management = () => {
45354
45547
  justifyContent: "space-between",
45355
45548
  px: 3,
45356
45549
  py: 1.5,
45550
+ // The native shell and installed PWAs run edge-to-edge, so the
45551
+ // sticky bar starts underneath the status bar / notch unless it
45552
+ // reserves the inset itself (chat-app-bar does the same).
45553
+ pt: "calc(12px + env(safe-area-inset-top, 0px))",
45357
45554
  bgcolor: (theme2) => theme2.palette.mode === "dark" ? "rgba(24,28,40,0.95)" : "rgba(255,255,255,0.95)",
45358
45555
  borderBottom: (theme2) => `1px solid ${theme2.palette.divider}`,
45359
45556
  backdropFilter: "blur(12px)",
@@ -45649,7 +45846,7 @@ var Management = () => {
45649
45846
  ]
45650
45847
  }
45651
45848
  ),
45652
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
45849
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
45653
45850
  import_material55.Fab,
45654
45851
  {
45655
45852
  "aria-label": "AI",
@@ -45685,7 +45882,7 @@ var Management = () => {
45685
45882
  )
45686
45883
  }
45687
45884
  ),
45688
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(chat_modal_default, { open: modalOpen, onClose: handleCloseModal })
45885
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(chat_modal_default, { open: modalOpen, onClose: handleCloseModal })
45689
45886
  ]
45690
45887
  }
45691
45888
  )