@burtson-labs/bandit-engine 2.0.152 → 2.0.154

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-E3VSYTAB.mjs +16 -0
  2. package/dist/chat-provider.js +8 -10
  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-LPSM62OE.mjs → chunk-2X46TMFE.mjs} +19 -14
  8. package/dist/chunk-2X46TMFE.mjs.map +1 -0
  9. package/dist/{chunk-7ESJJN6D.mjs → chunk-7AMLR6P4.mjs} +263 -75
  10. package/dist/chunk-7AMLR6P4.mjs.map +1 -0
  11. package/dist/{chunk-RRCCSN3N.mjs → chunk-B2TJZM4Z.mjs} +2 -2
  12. package/dist/{chunk-U33KBX3Y.mjs → chunk-GQ3X6VPR.mjs} +4 -4
  13. package/dist/{chunk-CVFFNX7H.mjs → chunk-IS2LWSBZ.mjs} +10 -12
  14. package/dist/chunk-IS2LWSBZ.mjs.map +1 -0
  15. package/dist/{chunk-TYRYAAP2.mjs → chunk-RN7FRSMX.mjs} +3 -3
  16. package/dist/{chunk-QLATFSSO.mjs → chunk-TVPYBRWF.mjs} +5 -5
  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 +790 -570
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +10 -10
  25. package/dist/management/management.js +489 -269
  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-E3VSYTAB.mjs.map} +0 -0
  42. /package/dist/{chunk-RRCCSN3N.mjs.map → chunk-B2TJZM4Z.mjs.map} +0 -0
  43. /package/dist/{chunk-U33KBX3Y.mjs.map → chunk-GQ3X6VPR.mjs.map} +0 -0
  44. /package/dist/{chunk-TYRYAAP2.mjs.map → chunk-RN7FRSMX.mjs.map} +0 -0
  45. /package/dist/{chunk-QLATFSSO.mjs.map → chunk-TVPYBRWF.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({
@@ -19627,19 +19645,17 @@ var init_mcpToolsStore = __esm({
19627
19645
  imageGenerationTool = {
19628
19646
  id: "image-generation",
19629
19647
  name: "image_generation",
19630
- description: "Generate high-quality images using DALL-E 3 from text prompts",
19648
+ description: "Generate an image from a text prompt on the Burtson Labs GPU",
19631
19649
  enabled: true,
19632
19650
  type: "function",
19633
19651
  function: {
19634
19652
  name: "image_generation",
19635
- description: "Generate high-quality images using DALL-E 3 from text prompts",
19653
+ description: "Generate an image from a text prompt on the Burtson Labs GPU",
19636
19654
  parameters: {
19637
19655
  type: "object",
19638
19656
  properties: {
19639
19657
  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" }
19658
+ size: { type: "string", enum: ["1024x1024", "1344x768", "768x1344"], description: "Canvas: square, landscape, or portrait" }
19643
19659
  },
19644
19660
  required: ["prompt"]
19645
19661
  }
@@ -19651,20 +19667,20 @@ var init_mcpToolsStore = __esm({
19651
19667
  createFileTool = {
19652
19668
  id: "create-file",
19653
19669
  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.",
19670
+ description: "Create a downloadable file for the user (md, txt, csv, json, html, docx, pptx, pdf). Returns a temporary (~1 hour) download link.",
19655
19671
  enabled: true,
19656
19672
  type: "function",
19657
19673
  function: {
19658
19674
  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.",
19675
+ 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
19676
  parameters: {
19661
19677
  type: "object",
19662
19678
  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'." },
19679
+ content: { type: "string", description: "The file content (HTML preferred for pdf; Markdown for docx/pptx/pdf; raw text for others)." },
19680
+ filename: { type: "string", description: "Desired filename, e.g. 'report.pdf' or 'notes.md'." },
19665
19681
  format: {
19666
19682
  type: "string",
19667
- enum: ["md", "txt", "csv", "json", "html", "xml", "yaml", "docx", "pptx"],
19683
+ enum: ["md", "txt", "csv", "json", "html", "xml", "yaml", "docx", "pptx", "pdf"],
19668
19684
  description: "File format. Defaults to md."
19669
19685
  }
19670
19686
  },
@@ -20767,6 +20783,7 @@ var init_engine_picker = __esm({
20767
20783
  const tier = String(getCurrentTier());
20768
20784
  return tier !== "trial" && tier !== "expired" && tier !== "free" && tier !== "basic";
20769
20785
  })();
20786
+ const hideUpgradePrompts = usePackageSettingsStore((s) => s.settings?.hideUpgradePrompts === true);
20770
20787
  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
20788
  if (engines.length === 0) return null;
20772
20789
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
@@ -20845,8 +20862,8 @@ var init_engine_picker = __esm({
20845
20862
  ),
20846
20863
  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
20864
  ] }),
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)(
20865
+ /* @__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" }),
20866
+ isGated && !hideUpgradePrompts && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
20850
20867
  import_material37.Button,
20851
20868
  {
20852
20869
  size: "small",
@@ -22208,12 +22225,14 @@ var init_rate_limit_prompt = __esm({
22208
22225
  import_react_router_dom2 = require("react-router-dom");
22209
22226
  init_lucide_icons();
22210
22227
  init_rateLimitStore();
22228
+ init_packageSettingsStore();
22211
22229
  init_debugLogger();
22212
22230
  import_jsx_runtime41 = require("react/jsx-runtime");
22213
22231
  RateLimitPrompt = () => {
22214
22232
  const theme = (0, import_material41.useTheme)();
22215
22233
  const prompt = useRateLimitStore((s) => s.prompt);
22216
22234
  const dismiss = useRateLimitStore((s) => s.dismiss);
22235
+ const hideUpgradePrompts = usePackageSettingsStore((s) => s.settings?.hideUpgradePrompts === true);
22217
22236
  const hasLoggedRouterWarningRef = (0, import_react48.useRef)(false);
22218
22237
  let navigate = null;
22219
22238
  try {
@@ -22243,6 +22262,12 @@ var init_rate_limit_prompt = __esm({
22243
22262
  const { scope, frontier } = prompt;
22244
22263
  const title = scope === "weekly" ? "You've reached your weekly limit" : "You've hit your session limit";
22245
22264
  const body = (() => {
22265
+ if (hideUpgradePrompts) {
22266
+ if (frontier) {
22267
+ 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.";
22268
+ }
22269
+ 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.";
22270
+ }
22246
22271
  if (frontier) {
22247
22272
  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
22273
  }
@@ -22287,7 +22312,7 @@ var init_rate_limit_prompt = __esm({
22287
22312
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_material41.Typography, { variant: "subtitle2", sx: { fontWeight: 600, color: theme.palette.text.primary }, children: title }),
22288
22313
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_material41.Typography, { variant: "body2", sx: { color: theme.palette.text.secondary, mt: 0.25 }, children: body }),
22289
22314
  /* @__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)(
22315
+ !hideUpgradePrompts && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
22291
22316
  import_material41.Button,
22292
22317
  {
22293
22318
  size: "small",
@@ -24159,7 +24184,7 @@ TOOL USAGE PROTOCOL (conservative approach)
24159
24184
  - Only call tools for SPECIFIC, CURRENT information that requires real-time data or a source you don't already have:
24160
24185
  * 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
24186
  * 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
24187
+ * image_generation() - ONLY when explicitly asked to create or generate an image. Runs on Burtson's local GPU; it can take a couple of minutes when the GPU is cold, and is limited to admin and partner accounts
24163
24188
  * 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
24189
  * 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
24190
  - For general questions about concepts, definitions, explanations, or how-to topics, use your built-in knowledge WITHOUT calling tools.
@@ -25252,12 +25277,49 @@ var init_useNetworkStatus = __esm({
25252
25277
  }
25253
25278
  });
25254
25279
 
25280
+ // src/hooks/useSafeNavigate.ts
25281
+ var import_react53, import_react_router_dom3, useSafeNavigate;
25282
+ var init_useSafeNavigate = __esm({
25283
+ "src/hooks/useSafeNavigate.ts"() {
25284
+ "use strict";
25285
+ import_react53 = require("react");
25286
+ import_react_router_dom3 = require("react-router-dom");
25287
+ init_debugLogger();
25288
+ useSafeNavigate = () => {
25289
+ const warned = (0, import_react53.useRef)(false);
25290
+ let navigate = null;
25291
+ try {
25292
+ navigate = (0, import_react_router_dom3.useNavigate)();
25293
+ } catch (error) {
25294
+ if (!warned.current) {
25295
+ debugLogger.warn("useSafeNavigate: no Router context; falling back to location", { error });
25296
+ warned.current = true;
25297
+ }
25298
+ navigate = null;
25299
+ }
25300
+ return (to) => {
25301
+ if (typeof window !== "undefined" && /^[a-z]+:\/\//i.test(to)) {
25302
+ window.location.href = to;
25303
+ return;
25304
+ }
25305
+ if (navigate) {
25306
+ navigate(to);
25307
+ return;
25308
+ }
25309
+ if (typeof window !== "undefined") {
25310
+ window.location.href = to;
25311
+ }
25312
+ };
25313
+ };
25314
+ }
25315
+ });
25316
+
25255
25317
  // 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;
25318
+ var import_react54, import_material42, import_lucide_react9, import_styles26, import_jsx_runtime42, DEFAULT_COLORS2, ProjectManagementModal, project_management_modal_default;
25257
25319
  var init_project_management_modal = __esm({
25258
25320
  "src/chat/project-management-modal.tsx"() {
25259
25321
  "use strict";
25260
- import_react53 = require("react");
25322
+ import_react54 = require("react");
25261
25323
  import_material42 = require("@mui/material");
25262
25324
  import_lucide_react9 = require("lucide-react");
25263
25325
  import_styles26 = require("@mui/material/styles");
@@ -25293,25 +25355,25 @@ var init_project_management_modal = __esm({
25293
25355
  hydrate
25294
25356
  } = useProjectStore();
25295
25357
  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)({
25358
+ const [showCreateForm, setShowCreateForm] = (0, import_react54.useState)(false);
25359
+ const [editingProject, setEditingProject] = (0, import_react54.useState)(null);
25360
+ const [formData, setFormData] = (0, import_react54.useState)({
25299
25361
  name: "",
25300
25362
  description: "",
25301
25363
  instructions: "",
25302
25364
  color: DEFAULT_COLORS2[0]
25303
25365
  });
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)(() => {
25366
+ const [menuAnchor, setMenuAnchor] = (0, import_react54.useState)(null);
25367
+ const [selectedProject, setSelectedProject] = (0, import_react54.useState)(null);
25368
+ const [loading, setLoading] = (0, import_react54.useState)(false);
25369
+ const [error, setError] = (0, import_react54.useState)(null);
25370
+ const modalContainerRef = (0, import_react54.useRef)(null);
25371
+ (0, import_react54.useEffect)(() => {
25310
25372
  if (open && !_hasHydrated) {
25311
25373
  hydrate();
25312
25374
  }
25313
25375
  }, [open, _hasHydrated, hydrate]);
25314
- (0, import_react53.useEffect)(() => {
25376
+ (0, import_react54.useEffect)(() => {
25315
25377
  if (!open) {
25316
25378
  setMenuAnchor(null);
25317
25379
  setSelectedProject(null);
@@ -25846,11 +25908,11 @@ var init_project_management_modal = __esm({
25846
25908
  });
25847
25909
 
25848
25910
  // 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;
25911
+ var import_react55, import_material43, import_lucide_react10, import_styles27, import_jsx_runtime43, MoveConversationModal, move_conversation_modal_default;
25850
25912
  var init_move_conversation_modal = __esm({
25851
25913
  "src/chat/move-conversation-modal.tsx"() {
25852
25914
  "use strict";
25853
- import_react54 = require("react");
25915
+ import_react55 = require("react");
25854
25916
  import_material43 = require("@mui/material");
25855
25917
  import_lucide_react10 = require("lucide-react");
25856
25918
  import_styles27 = require("@mui/material/styles");
@@ -25867,15 +25929,15 @@ var init_move_conversation_modal = __esm({
25867
25929
  const theme = (0, import_styles27.useTheme)();
25868
25930
  const { projects, _hasHydrated, hydrate } = useProjectStore();
25869
25931
  const { moveConversationToProject } = useConversationStore();
25870
- const [selectedProjectId, setSelectedProjectId] = (0, import_react54.useState)(
25932
+ const [selectedProjectId, setSelectedProjectId] = (0, import_react55.useState)(
25871
25933
  currentProjectId
25872
25934
  );
25873
- (0, import_react54.useEffect)(() => {
25935
+ (0, import_react55.useEffect)(() => {
25874
25936
  if (open && !_hasHydrated) {
25875
25937
  hydrate();
25876
25938
  }
25877
25939
  }, [open, _hasHydrated, hydrate]);
25878
- (0, import_react54.useEffect)(() => {
25940
+ (0, import_react55.useEffect)(() => {
25879
25941
  setSelectedProjectId(currentProjectId);
25880
25942
  }, [currentProjectId, open]);
25881
25943
  const handleMove = async () => {
@@ -26017,11 +26079,11 @@ var init_move_conversation_modal = __esm({
26017
26079
  });
26018
26080
 
26019
26081
  // 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;
26082
+ var import_react56, import_material44, import_lucide_react11, import_styles28, import_jsx_runtime44, SimpleConversationItem, simple_conversation_item_default;
26021
26083
  var init_simple_conversation_item = __esm({
26022
26084
  "src/chat/simple-conversation-item.tsx"() {
26023
26085
  "use strict";
26024
- import_react55 = require("react");
26086
+ import_react56 = require("react");
26025
26087
  import_material44 = require("@mui/material");
26026
26088
  import_lucide_react11 = require("lucide-react");
26027
26089
  import_styles28 = require("@mui/material/styles");
@@ -26043,16 +26105,16 @@ var init_simple_conversation_item = __esm({
26043
26105
  }) => {
26044
26106
  const theme = (0, import_styles28.useTheme)();
26045
26107
  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);
26108
+ const [anchorEl, setAnchorEl] = (0, import_react56.useState)(null);
26109
+ const [isEditing, setIsEditing] = (0, import_react56.useState)(false);
26110
+ const [editName, setEditName] = (0, import_react56.useState)(conversation.name);
26111
+ const [isDragging, setIsDragging] = (0, import_react56.useState)(false);
26112
+ const [isTouchDragging, setIsTouchDragging] = (0, import_react56.useState)(false);
26113
+ const [showRenameDialog, setShowRenameDialog] = (0, import_react56.useState)(false);
26114
+ const longPressTimeoutRef = (0, import_react56.useRef)(null);
26115
+ const touchStartPointRef = (0, import_react56.useRef)(null);
26116
+ const activeTouchIdRef = (0, import_react56.useRef)(null);
26117
+ const suppressClickRef = (0, import_react56.useRef)(false);
26056
26118
  const highlightText = (text, query) => {
26057
26119
  if (!query || !query.trim()) {
26058
26120
  return text;
@@ -26191,7 +26253,7 @@ var init_simple_conversation_item = __esm({
26191
26253
  if (!isMobile) return;
26192
26254
  finalizeTouchDrag();
26193
26255
  };
26194
- (0, import_react55.useEffect)(() => {
26256
+ (0, import_react56.useEffect)(() => {
26195
26257
  if (!isTouchDragActive && isTouchDragging) {
26196
26258
  setIsTouchDragging(false);
26197
26259
  }
@@ -26457,11 +26519,11 @@ var init_simple_conversation_item = __esm({
26457
26519
  });
26458
26520
 
26459
26521
  // src/chat/project-header.tsx
26460
- var import_react56, import_material45, import_lucide_react12, import_styles29, import_jsx_runtime45, ProjectHeader, project_header_default;
26522
+ var import_react57, import_material45, import_lucide_react12, import_styles29, import_jsx_runtime45, ProjectHeader, project_header_default;
26461
26523
  var init_project_header = __esm({
26462
26524
  "src/chat/project-header.tsx"() {
26463
26525
  "use strict";
26464
- import_react56 = require("react");
26526
+ import_react57 = require("react");
26465
26527
  import_material45 = require("@mui/material");
26466
26528
  import_lucide_react12 = require("lucide-react");
26467
26529
  import_styles29 = require("@mui/material/styles");
@@ -26485,10 +26547,10 @@ var init_project_header = __esm({
26485
26547
  const theme = (0, import_styles29.useTheme)();
26486
26548
  const { createNewConversation } = useConversationStore();
26487
26549
  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");
26550
+ const [isHovered, setIsHovered] = (0, import_react57.useState)(false);
26551
+ const [isDragOver, setIsDragOver] = (0, import_react57.useState)(false);
26552
+ const [renameDraft, setRenameDraft] = (0, import_react57.useState)(projectName);
26553
+ const renameActionRef = (0, import_react57.useRef)("none");
26492
26554
  const isUngrouped = projectId === null;
26493
26555
  const Icon = isCollapsed ? import_lucide_react12.Folder : import_lucide_react12.FolderOpen;
26494
26556
  const handleAddConversation = (e) => {
@@ -26762,11 +26824,11 @@ var init_tooltips = __esm({
26762
26824
  });
26763
26825
 
26764
26826
  // 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;
26827
+ var import_react58, import_material46, import_lucide_react13, import_styles30, import_jsx_runtime46, BANDIT_AVATAR, coerceOptionalString, deriveInitials, ConversationDrawer, conversation_drawer_default;
26766
26828
  var init_conversation_drawer = __esm({
26767
26829
  "src/chat/conversation-drawer.tsx"() {
26768
26830
  "use strict";
26769
- import_react57 = require("react");
26831
+ import_react58 = require("react");
26770
26832
  import_material46 = require("@mui/material");
26771
26833
  import_lucide_react13 = require("lucide-react");
26772
26834
  init_FeedbackModal();
@@ -26775,6 +26837,8 @@ var init_conversation_drawer = __esm({
26775
26837
  init_projectStore();
26776
26838
  init_authenticationStore();
26777
26839
  init_packageSettingsStore();
26840
+ init_installedApp();
26841
+ init_useSafeNavigate();
26778
26842
  init_brandingService();
26779
26843
  init_authenticationService();
26780
26844
  init_project_management_modal();
@@ -26838,23 +26902,23 @@ var init_conversation_drawer = __esm({
26838
26902
  createProject,
26839
26903
  deleteProject
26840
26904
  } = 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) => {
26905
+ const [projectManagementOpen, setProjectManagementOpen] = (0, import_react58.useState)(false);
26906
+ const [memoryModalOpen, setMemoryModalOpen] = (0, import_react58.useState)(false);
26907
+ const [collapsedProjects, setCollapsedProjects] = (0, import_react58.useState)(/* @__PURE__ */ new Set());
26908
+ const didInitCollapseRef = (0, import_react58.useRef)(false);
26909
+ const [searchQuery, setSearchQuery] = (0, import_react58.useState)("");
26910
+ const [menuAnchorEl, setMenuAnchorEl] = (0, import_react58.useState)(null);
26911
+ const [clearConfirmOpen, setClearConfirmOpen] = (0, import_react58.useState)(false);
26912
+ const [feedbackOpen, setFeedbackOpen] = (0, import_react58.useState)(false);
26913
+ const [moveModalOpen, setMoveModalOpen] = (0, import_react58.useState)(false);
26914
+ const [conversationToMove, setConversationToMove] = (0, import_react58.useState)(null);
26915
+ const [renameProjectId, setRenameProjectId] = (0, import_react58.useState)(null);
26916
+ const getCustomClaim = (0, import_react58.useCallback)((key) => {
26853
26917
  if (!user2) return void 0;
26854
26918
  const record = user2;
26855
26919
  return coerceOptionalString(record[key]);
26856
26920
  }, [user2]);
26857
- const userDisplayName = (0, import_react57.useMemo)(() => {
26921
+ const userDisplayName = (0, import_react58.useMemo)(() => {
26858
26922
  if (!user2) return void 0;
26859
26923
  const candidateFields = [
26860
26924
  coerceOptionalString(user2.name),
@@ -26869,7 +26933,7 @@ var init_conversation_drawer = __esm({
26869
26933
  if (trimmedEmail) return trimmedEmail;
26870
26934
  return user2.sub;
26871
26935
  }, [user2, getCustomClaim]);
26872
- const userSecondaryText = (0, import_react57.useMemo)(() => {
26936
+ const userSecondaryText = (0, import_react58.useMemo)(() => {
26873
26937
  if (!user2) return void 0;
26874
26938
  const trimmedEmail = coerceOptionalString(user2.email);
26875
26939
  if (trimmedEmail && trimmedEmail !== userDisplayName) {
@@ -26881,13 +26945,13 @@ var init_conversation_drawer = __esm({
26881
26945
  }
26882
26946
  return void 0;
26883
26947
  }, [user2, userDisplayName]);
26884
- const [avatarImage, setAvatarImage] = (0, import_react57.useState)(BANDIT_AVATAR);
26885
- (0, import_react57.useEffect)(() => {
26948
+ const [avatarImage, setAvatarImage] = (0, import_react58.useState)(BANDIT_AVATAR);
26949
+ (0, import_react58.useEffect)(() => {
26886
26950
  const fresh = readPersistedToken();
26887
26951
  const store = useAuthenticationStore.getState();
26888
26952
  if (fresh && fresh !== store.token) store.setToken(fresh);
26889
26953
  }, []);
26890
- (0, import_react57.useEffect)(() => {
26954
+ (0, import_react58.useEffect)(() => {
26891
26955
  let active2 = true;
26892
26956
  let objectUrl = null;
26893
26957
  const resolveAvatar2 = async () => {
@@ -26928,13 +26992,13 @@ var init_conversation_drawer = __esm({
26928
26992
  };
26929
26993
  }, [getCustomClaim]);
26930
26994
  const avatarLabel = userDisplayName || user2?.email || "Bandit";
26931
- const avatarInitials = (0, import_react57.useMemo)(() => deriveInitials(avatarLabel), [avatarLabel]);
26932
- (0, import_react57.useEffect)(() => {
26995
+ const avatarInitials = (0, import_react58.useMemo)(() => deriveInitials(avatarLabel), [avatarLabel]);
26996
+ (0, import_react58.useEffect)(() => {
26933
26997
  if (!projectsHydrated) {
26934
26998
  hydrateProjects();
26935
26999
  }
26936
27000
  }, [projectsHydrated, hydrateProjects]);
26937
- (0, import_react57.useEffect)(() => {
27001
+ (0, import_react58.useEffect)(() => {
26938
27002
  if (projectsHydrated && !didInitCollapseRef.current) {
26939
27003
  didInitCollapseRef.current = true;
26940
27004
  if (projects && projects.length > 0) {
@@ -26947,7 +27011,7 @@ var init_conversation_drawer = __esm({
26947
27011
  const end = Math.min(text.length, idx + query.length + 60);
26948
27012
  return text.slice(start, end).replace(/\s+/g, " ").trim();
26949
27013
  };
26950
- const projectGroups = (0, import_react57.useMemo)(() => {
27014
+ const projectGroups = (0, import_react58.useMemo)(() => {
26951
27015
  const listable = conversations.filter((conversation) => !conversation.incognito);
26952
27016
  const groups = projects.map((project) => ({
26953
27017
  id: project.id,
@@ -26971,7 +27035,7 @@ var init_conversation_drawer = __esm({
26971
27035
  }
26972
27036
  return groups.filter((group) => group.conversations.length > 0 || group.id !== null);
26973
27037
  }, [projects, conversations, collapsedProjects]);
26974
- const filteredProjectGroups = (0, import_react57.useMemo)(() => {
27038
+ const filteredProjectGroups = (0, import_react58.useMemo)(() => {
26975
27039
  if (!searchQuery.trim()) return projectGroups;
26976
27040
  const query = searchQuery.toLowerCase();
26977
27041
  return projectGroups.map((group) => {
@@ -27019,15 +27083,18 @@ var init_conversation_drawer = __esm({
27019
27083
  const syncEnabled = useConversationSyncStore((s) => s.syncEnabled);
27020
27084
  const syncStatus = useConversationSyncStore((s) => s.status);
27021
27085
  const runSync = useConversationSyncStore((s) => s.runSync);
27086
+ const safeNavigate = useSafeNavigate();
27022
27087
  const goToHome = () => {
27023
27088
  const resolvedHomeUrl = preferences.homeUrl?.trim() || usePackageSettingsStore.getState().settings?.homeUrl?.trim() || "";
27024
27089
  if (resolvedHomeUrl) {
27025
27090
  window.location.href = resolvedHomeUrl;
27026
27091
  return;
27027
27092
  }
27028
- const navigatorWithStandalone = window.navigator;
27029
- const isStandalone = window.matchMedia?.("(display-mode: standalone)").matches || navigatorWithStandalone.standalone === true;
27030
- if (isStandalone) {
27093
+ if (isNativeShell()) {
27094
+ safeNavigate("/chat");
27095
+ return;
27096
+ }
27097
+ if (isStandalonePwa2()) {
27031
27098
  try {
27032
27099
  sessionStorage.setItem("banditPwaStayOnHome", "true");
27033
27100
  } catch {
@@ -27498,7 +27565,7 @@ var init_conversation_drawer = __esm({
27498
27565
  import_material46.Box,
27499
27566
  {
27500
27567
  onClick: user2 ? () => {
27501
- window.location.href = "/profile";
27568
+ safeNavigate("/profile");
27502
27569
  } : void 0,
27503
27570
  title: user2 ? "Profile & settings" : void 0,
27504
27571
  sx: {
@@ -27591,7 +27658,7 @@ var init_conversation_drawer = __esm({
27591
27658
  title: "Management & settings",
27592
27659
  onClick: (event) => {
27593
27660
  event.stopPropagation();
27594
- window.location.href = managementPath;
27661
+ safeNavigate(managementPath);
27595
27662
  },
27596
27663
  sx: {
27597
27664
  flexShrink: 0,
@@ -27751,11 +27818,11 @@ var init_conversation_drawer = __esm({
27751
27818
  });
27752
27819
 
27753
27820
  // 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;
27821
+ var import_react59, import_material47, import_lucide_react14, import_styles31, import_jsx_runtime47, BANDIT_AVATAR2, coerceOptionalString2, deriveInitials2, EnhancedMobileConversationsModal, enhanced_mobile_conversations_modal_default;
27755
27822
  var init_enhanced_mobile_conversations_modal = __esm({
27756
27823
  "src/chat/enhanced-mobile-conversations-modal.tsx"() {
27757
27824
  "use strict";
27758
- import_react58 = require("react");
27825
+ import_react59 = require("react");
27759
27826
  import_material47 = require("@mui/material");
27760
27827
  import_lucide_react14 = require("lucide-react");
27761
27828
  import_styles31 = require("@mui/material/styles");
@@ -27763,6 +27830,8 @@ var init_enhanced_mobile_conversations_modal = __esm({
27763
27830
  init_projectStore();
27764
27831
  init_authenticationStore();
27765
27832
  init_packageSettingsStore();
27833
+ init_installedApp();
27834
+ init_useSafeNavigate();
27766
27835
  init_memory_modal();
27767
27836
  init_brandingService();
27768
27837
  init_authenticationService();
@@ -27803,7 +27872,8 @@ var init_enhanced_mobile_conversations_modal = __esm({
27803
27872
  };
27804
27873
  EnhancedMobileConversationsModal = ({
27805
27874
  open,
27806
- onClose
27875
+ onClose,
27876
+ drag
27807
27877
  }) => {
27808
27878
  const theme = (0, import_styles31.useTheme)();
27809
27879
  const { user: user2 } = useAuthenticationStore();
@@ -27830,15 +27900,18 @@ var init_enhanced_mobile_conversations_modal = __esm({
27830
27900
  const syncEnabled = useConversationSyncStore((s) => s.syncEnabled);
27831
27901
  const syncStatus = useConversationSyncStore((s) => s.status);
27832
27902
  const runSync = useConversationSyncStore((s) => s.runSync);
27903
+ const safeNavigate = useSafeNavigate();
27833
27904
  const goToHome = () => {
27834
27905
  const resolvedHomeUrl = preferences.homeUrl?.trim() || usePackageSettingsStore.getState().settings?.homeUrl?.trim() || "";
27835
27906
  if (resolvedHomeUrl) {
27836
27907
  window.location.href = resolvedHomeUrl;
27837
27908
  return;
27838
27909
  }
27839
- const navigatorWithStandalone = window.navigator;
27840
- const isStandalone = window.matchMedia?.("(display-mode: standalone)").matches || navigatorWithStandalone.standalone === true;
27841
- if (isStandalone) {
27910
+ if (isNativeShell()) {
27911
+ safeNavigate("/chat");
27912
+ return;
27913
+ }
27914
+ if (isStandalonePwa2()) {
27842
27915
  try {
27843
27916
  sessionStorage.setItem("banditPwaStayOnHome", "true");
27844
27917
  } catch {
@@ -27849,25 +27922,25 @@ var init_enhanced_mobile_conversations_modal = __esm({
27849
27922
  window.location.href = "/";
27850
27923
  };
27851
27924
  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) => {
27925
+ const [projectManagementOpen, setProjectManagementOpen] = (0, import_react59.useState)(false);
27926
+ const [memoryModalOpen, setMemoryModalOpen] = (0, import_react59.useState)(false);
27927
+ const [collapsedProjects, setCollapsedProjects] = (0, import_react59.useState)(/* @__PURE__ */ new Set());
27928
+ const didInitCollapseRef = (0, import_react59.useRef)(false);
27929
+ const [searchQuery, setSearchQuery] = (0, import_react59.useState)("");
27930
+ const [menuAnchorEl, setMenuAnchorEl] = (0, import_react59.useState)(null);
27931
+ const [clearConfirmOpen, setClearConfirmOpen] = (0, import_react59.useState)(false);
27932
+ const [moveModalOpen, setMoveModalOpen] = (0, import_react59.useState)(false);
27933
+ const [conversationToMove, setConversationToMove] = (0, import_react59.useState)(null);
27934
+ const [renameProjectId, setRenameProjectId] = (0, import_react59.useState)(null);
27935
+ const [deletedConversationIds, setDeletedConversationIds] = (0, import_react59.useState)(/* @__PURE__ */ new Set());
27936
+ const [touchDragState, setTouchDragState] = (0, import_react59.useState)({ conversationId: null, originProjectId: null, hoverProjectId: null });
27937
+ const [avatarImage, setAvatarImage] = (0, import_react59.useState)(BANDIT_AVATAR2);
27938
+ const getCustomClaim = (0, import_react59.useCallback)((key) => {
27866
27939
  if (!user2) return void 0;
27867
27940
  const record = user2;
27868
27941
  return coerceOptionalString2(record[key]);
27869
27942
  }, [user2]);
27870
- const userDisplayName = (0, import_react58.useMemo)(() => {
27943
+ const userDisplayName = (0, import_react59.useMemo)(() => {
27871
27944
  if (!user2) return void 0;
27872
27945
  const candidateFields = [
27873
27946
  coerceOptionalString2(user2.name),
@@ -27882,7 +27955,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
27882
27955
  if (trimmedEmail) return trimmedEmail;
27883
27956
  return user2.sub;
27884
27957
  }, [user2, getCustomClaim]);
27885
- const userSecondaryText = (0, import_react58.useMemo)(() => {
27958
+ const userSecondaryText = (0, import_react59.useMemo)(() => {
27886
27959
  if (!user2) return void 0;
27887
27960
  const trimmedEmail = coerceOptionalString2(user2.email);
27888
27961
  if (trimmedEmail && trimmedEmail !== userDisplayName) {
@@ -27894,7 +27967,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
27894
27967
  }
27895
27968
  return void 0;
27896
27969
  }, [user2, userDisplayName]);
27897
- (0, import_react58.useEffect)(() => {
27970
+ (0, import_react59.useEffect)(() => {
27898
27971
  if (!open) return;
27899
27972
  const fresh = readPersistedToken();
27900
27973
  const authStore = useAuthenticationStore.getState();
@@ -27939,18 +28012,18 @@ var init_enhanced_mobile_conversations_modal = __esm({
27939
28012
  };
27940
28013
  }, [open, getCustomClaim]);
27941
28014
  const avatarLabel = userDisplayName || user2?.email || "Bandit";
27942
- const avatarInitials = (0, import_react58.useMemo)(() => deriveInitials2(avatarLabel), [avatarLabel]);
28015
+ const avatarInitials = (0, import_react59.useMemo)(() => deriveInitials2(avatarLabel), [avatarLabel]);
27943
28016
  const buildSnippet = (text, query, idx) => {
27944
28017
  const start = Math.max(0, idx - 40);
27945
28018
  const end = Math.min(text.length, idx + query.length + 60);
27946
28019
  return text.slice(start, end).replace(/\s+/g, " ").trim();
27947
28020
  };
27948
- (0, import_react58.useEffect)(() => {
28021
+ (0, import_react59.useEffect)(() => {
27949
28022
  if (open && !projectsHydrated) {
27950
28023
  hydrateProjects();
27951
28024
  }
27952
28025
  }, [open, projectsHydrated, hydrateProjects]);
27953
- (0, import_react58.useEffect)(() => {
28026
+ (0, import_react59.useEffect)(() => {
27954
28027
  if (projectsHydrated && !didInitCollapseRef.current) {
27955
28028
  didInitCollapseRef.current = true;
27956
28029
  if (projects && projects.length > 0) {
@@ -27958,7 +28031,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
27958
28031
  }
27959
28032
  }
27960
28033
  }, [projectsHydrated, projects]);
27961
- const projectGroups = (0, import_react58.useMemo)(() => {
28034
+ const projectGroups = (0, import_react59.useMemo)(() => {
27962
28035
  const visibleConversations = conversations.filter(
27963
28036
  (conversation) => !deletedConversationIds.has(conversation.id) && !conversation.incognito
27964
28037
  );
@@ -27984,11 +28057,11 @@ var init_enhanced_mobile_conversations_modal = __esm({
27984
28057
  }
27985
28058
  return groups.filter((group) => group.conversations.length > 0 || group.id !== null);
27986
28059
  }, [projects, conversations, collapsedProjects, deletedConversationIds]);
27987
- const visibleConversationCount = (0, import_react58.useMemo)(
28060
+ const visibleConversationCount = (0, import_react59.useMemo)(
27988
28061
  () => projectGroups.reduce((total, group) => total + group.conversations.length, 0),
27989
28062
  [projectGroups]
27990
28063
  );
27991
- const filteredProjectGroups = (0, import_react58.useMemo)(() => {
28064
+ const filteredProjectGroups = (0, import_react59.useMemo)(() => {
27992
28065
  if (!searchQuery.trim()) return projectGroups;
27993
28066
  const query = searchQuery.toLowerCase();
27994
28067
  return projectGroups.map((group) => {
@@ -28013,14 +28086,14 @@ var init_enhanced_mobile_conversations_modal = __esm({
28013
28086
  }).filter((group) => group.conversations.length > 0);
28014
28087
  }, [projectGroups, searchQuery]);
28015
28088
  const touchDragActive = Boolean(touchDragState.conversationId);
28016
- const getProjectIdFromPoint = (0, import_react58.useCallback)((clientX, clientY) => {
28089
+ const getProjectIdFromPoint = (0, import_react59.useCallback)((clientX, clientY) => {
28017
28090
  if (typeof document === "undefined") return null;
28018
28091
  const element = document.elementFromPoint(clientX, clientY);
28019
28092
  if (!element) return null;
28020
28093
  const projectNode = element.closest("[data-project-id]");
28021
28094
  return projectNode?.getAttribute("data-project-id") ?? null;
28022
28095
  }, []);
28023
- const handleTouchDragStart = (0, import_react58.useCallback)((conversation, touch) => {
28096
+ const handleTouchDragStart = (0, import_react59.useCallback)((conversation, touch) => {
28024
28097
  const initialHover = getProjectIdFromPoint(touch.clientX, touch.clientY);
28025
28098
  setTouchDragState({
28026
28099
  conversationId: conversation.id,
@@ -28028,7 +28101,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28028
28101
  hoverProjectId: initialHover ?? (conversation.projectId ?? null)
28029
28102
  });
28030
28103
  }, [getProjectIdFromPoint]);
28031
- const handleTouchDragMove = (0, import_react58.useCallback)((touch) => {
28104
+ const handleTouchDragMove = (0, import_react59.useCallback)((touch) => {
28032
28105
  setTouchDragState((prev) => {
28033
28106
  if (!prev.conversationId) return prev;
28034
28107
  const hoverId = getProjectIdFromPoint(touch.clientX, touch.clientY);
@@ -28036,7 +28109,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28036
28109
  return { ...prev, hoverProjectId: hoverId };
28037
28110
  });
28038
28111
  }, [getProjectIdFromPoint]);
28039
- const handleTouchDragEnd = (0, import_react58.useCallback)((touch) => {
28112
+ const handleTouchDragEnd = (0, import_react59.useCallback)((touch) => {
28040
28113
  setTouchDragState((prev) => {
28041
28114
  if (!prev.conversationId) {
28042
28115
  return { conversationId: null, originProjectId: null, hoverProjectId: null };
@@ -28064,11 +28137,11 @@ var init_enhanced_mobile_conversations_modal = __esm({
28064
28137
  return { conversationId: null, originProjectId: null, hoverProjectId: null };
28065
28138
  });
28066
28139
  }, [getProjectIdFromPoint, moveConversationToProject]);
28067
- const activeDragConversation = (0, import_react58.useMemo)(() => {
28140
+ const activeDragConversation = (0, import_react59.useMemo)(() => {
28068
28141
  if (!touchDragState.conversationId) return null;
28069
28142
  return conversations.find((conv) => conv.id === touchDragState.conversationId) || null;
28070
28143
  }, [touchDragState.conversationId, conversations]);
28071
- const activeHoverLabel = (0, import_react58.useMemo)(() => {
28144
+ const activeHoverLabel = (0, import_react59.useMemo)(() => {
28072
28145
  if (!touchDragState.hoverProjectId) return "";
28073
28146
  if (touchDragState.hoverProjectId === "__ungrouped") return "Ungrouped";
28074
28147
  const project = projects.find((p) => p.id === touchDragState.hoverProjectId);
@@ -28117,7 +28190,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28117
28190
  setMoveModalOpen(false);
28118
28191
  setConversationToMove(null);
28119
28192
  };
28120
- (0, import_react58.useEffect)(() => {
28193
+ (0, import_react59.useEffect)(() => {
28121
28194
  setDeletedConversationIds((prev) => {
28122
28195
  let changed = false;
28123
28196
  const active2 = new Set(conversations.map((conv) => conv.id));
@@ -28142,6 +28215,11 @@ var init_enhanced_mobile_conversations_modal = __esm({
28142
28215
  children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_material47.Slide, { direction: "right", in: open, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
28143
28216
  import_material47.Box,
28144
28217
  {
28218
+ style: drag?.style,
28219
+ onTouchStart: drag?.onTouchStart,
28220
+ onTouchMove: drag?.onTouchMove,
28221
+ onTouchEnd: drag?.onTouchEnd,
28222
+ onTouchCancel: drag?.onTouchEnd,
28145
28223
  sx: {
28146
28224
  position: "fixed",
28147
28225
  top: 0,
@@ -28649,7 +28727,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28649
28727
  import_material47.Box,
28650
28728
  {
28651
28729
  onClick: user2 ? () => {
28652
- window.location.href = "/profile";
28730
+ safeNavigate("/profile");
28653
28731
  } : void 0,
28654
28732
  sx: {
28655
28733
  mt: "auto",
@@ -28725,7 +28803,7 @@ var init_enhanced_mobile_conversations_modal = __esm({
28725
28803
  title: "Management & settings",
28726
28804
  onClick: (event) => {
28727
28805
  event.stopPropagation();
28728
- window.location.href = managementPath;
28806
+ safeNavigate(managementPath);
28729
28807
  },
28730
28808
  sx: {
28731
28809
  flexShrink: 0,
@@ -28887,15 +28965,15 @@ var init_enhanced_mobile_conversations_modal = __esm({
28887
28965
  });
28888
28966
 
28889
28967
  // 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;
28968
+ 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
28969
  var init_chat_app_bar = __esm({
28892
28970
  "src/chat/chat-app-bar.tsx"() {
28893
28971
  "use strict";
28894
28972
  import_material48 = require("@mui/material");
28895
28973
  init_modelStore();
28896
- import_react59 = require("react");
28974
+ import_react60 = require("react");
28897
28975
  import_material49 = require("@mui/material");
28898
- import_react_router_dom3 = require("react-router-dom");
28976
+ import_react_router_dom4 = require("react-router-dom");
28899
28977
  init_lucide_icons();
28900
28978
  init_conversation_drawer();
28901
28979
  init_enhanced_mobile_conversations_modal();
@@ -28927,14 +29005,15 @@ var init_chat_app_bar = __esm({
28927
29005
  drawerOpen,
28928
29006
  setDrawerOpen,
28929
29007
  mobileConversationsOpen,
28930
- onMobileConversationsOpenChange
29008
+ onMobileConversationsOpenChange,
29009
+ mobileConversationsDrag
28931
29010
  }) => {
28932
29011
  const theme = (0, import_material49.useTheme)();
28933
29012
  const isMobile = (0, import_material49.useMediaQuery)(theme.breakpoints.down("sm"));
28934
- const hasLoggedRouterWarningRef = (0, import_react59.useRef)(false);
29013
+ const hasLoggedRouterWarningRef = (0, import_react60.useRef)(false);
28935
29014
  let navigate = null;
28936
29015
  try {
28937
- navigate = (0, import_react_router_dom3.useNavigate)();
29016
+ navigate = (0, import_react_router_dom4.useNavigate)();
28938
29017
  } catch (error) {
28939
29018
  if (!hasLoggedRouterWarningRef.current) {
28940
29019
  debugLogger.warn("ChatAppBar: Navigation not available (missing Router context)", { error });
@@ -28961,17 +29040,17 @@ var init_chat_app_bar = __esm({
28961
29040
  menuBackground,
28962
29041
  menuText
28963
29042
  } = 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);
29043
+ const [modelAnchorEl, setModelAnchorEl] = (0, import_react60.useState)(null);
29044
+ const [engineAnchorEl, setEngineAnchorEl] = (0, import_react60.useState)(null);
29045
+ const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react60.useState)(null);
29046
+ const [internalModalOpen, setInternalModalOpen] = (0, import_react60.useState)(false);
28968
29047
  const modalOpen = mobileConversationsOpen ?? internalModalOpen;
28969
29048
  const setModalOpen = (open) => {
28970
29049
  setInternalModalOpen(open);
28971
29050
  onMobileConversationsOpenChange?.(open);
28972
29051
  };
28973
- const [confirmModelChangeOpen, setConfirmModelChangeOpen] = (0, import_react59.useState)(false);
28974
- const [pendingModel, setPendingModel] = (0, import_react59.useState)(null);
29052
+ const [confirmModelChangeOpen, setConfirmModelChangeOpen] = (0, import_react60.useState)(false);
29053
+ const [pendingModel, setPendingModel] = (0, import_react60.useState)(null);
28975
29054
  const { conversations, currentId, createNewConversation, toggleIncognito, _hasHydrated } = useConversationStore();
28976
29055
  const { preferences } = usePreferencesStore();
28977
29056
  const { settings: packageSettings } = usePackageSettingsStore();
@@ -28981,7 +29060,7 @@ var init_chat_app_bar = __esm({
28981
29060
  syncEnabled: state.syncEnabled,
28982
29061
  setSyncEnabled: state.setSyncEnabled
28983
29062
  }), import_shallow2.shallow);
28984
- (0, import_react59.useEffect)(() => {
29063
+ (0, import_react60.useEffect)(() => {
28985
29064
  if (isPlaygroundMode3 && syncEnabled) {
28986
29065
  void setSyncEnabled(false).catch((error) => {
28987
29066
  debugLogger.warn("ChatAppBar: Failed to disable sync in playground", {
@@ -29036,7 +29115,7 @@ var init_chat_app_bar = __esm({
29036
29115
  const resolvedEngineId = currentEngine?.id ?? effectiveEngineId;
29037
29116
  const cleanEngineName = (name) => (name || "").replace(/\s*\([^)]*\)\s*$/, "").trim();
29038
29117
  const engineDisplay = cleanEngineName(currentEngine?.displayName) || "Engine";
29039
- (0, import_react59.useEffect)(() => {
29118
+ (0, import_react60.useEffect)(() => {
29040
29119
  useEngineStore.getState().fetchEngines();
29041
29120
  }, []);
29042
29121
  const pendingModelAvatar = useModelStore.getState().availableModels.find((m) => m.name === pendingModel)?.avatarBase64 || modelAvatars3[pendingModel || ""] || banditHead4;
@@ -29279,7 +29358,7 @@ var init_chat_app_bar = __esm({
29279
29358
  }
29280
29359
  ),
29281
29360
  /* @__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) }),
29361
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(enhanced_mobile_conversations_modal_default, { open: modalOpen, onClose: () => setModalOpen(false), drag: mobileConversationsDrag }),
29283
29362
  /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
29284
29363
  import_material49.Dialog,
29285
29364
  {
@@ -29419,11 +29498,11 @@ Respond with just the title and nothing else.
29419
29498
  });
29420
29499
 
29421
29500
  // 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;
29501
+ var import_react61, import_material50, import_styles32, import_react_markdown3, import_remark_gfm3, import_rehype_raw3, import_jsx_runtime49, markdownComponents, QuerySuggestionPicker;
29423
29502
  var init_query_suggestion_picker = __esm({
29424
29503
  "src/chat/query-suggestion-picker.tsx"() {
29425
29504
  "use strict";
29426
- import_react60 = require("react");
29505
+ import_react61 = require("react");
29427
29506
  import_material50 = require("@mui/material");
29428
29507
  import_styles32 = require("@mui/material/styles");
29429
29508
  import_react_markdown3 = __toESM(require("react-markdown"));
@@ -29448,16 +29527,16 @@ var init_query_suggestion_picker = __esm({
29448
29527
  onSend,
29449
29528
  inputHeight
29450
29529
  }) => {
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);
29530
+ const hasGenerated = (0, import_react61.useRef)(false);
29531
+ const [hasSentPrompt, setHasSentPrompt] = (0, import_react61.useState)(false);
29532
+ const [examplePrompts, setExamplePrompts] = (0, import_react61.useState)([]);
29533
+ const [visiblePrompts, setVisiblePrompts] = (0, import_react61.useState)([]);
29534
+ const scrollRef = (0, import_react61.useRef)(null);
29456
29535
  const theme = (0, import_styles32.useTheme)();
29457
29536
  const isMobile = (0, import_material50.useMediaQuery)((theme2) => theme2.breakpoints.down("sm"));
29458
29537
  const { background, text, border, hoverBackground, hoverBorder } = theme.palette.chat.suggestion;
29459
29538
  const { getCurrentModel, isLoading } = useModelStore();
29460
- (0, import_react60.useEffect)(() => {
29539
+ (0, import_react61.useEffect)(() => {
29461
29540
  if (hasGenerated.current || isLoading) return;
29462
29541
  hasGenerated.current = true;
29463
29542
  const currentModel = getCurrentModel();
@@ -29490,12 +29569,12 @@ var init_query_suggestion_picker = __esm({
29490
29569
  hasGenerated.current = false;
29491
29570
  });
29492
29571
  }, [getCurrentModel, isLoading]);
29493
- (0, import_react60.useEffect)(() => {
29572
+ (0, import_react61.useEffect)(() => {
29494
29573
  if (!isLoading) {
29495
29574
  hasGenerated.current = false;
29496
29575
  }
29497
29576
  }, [isLoading]);
29498
- (0, import_react60.useEffect)(() => {
29577
+ (0, import_react61.useEffect)(() => {
29499
29578
  if (hasSentPrompt || examplePrompts.length === 0) return;
29500
29579
  const interval = setInterval(() => {
29501
29580
  setExamplePrompts((prev) => {
@@ -29612,17 +29691,17 @@ var init_query_suggestion_picker = __esm({
29612
29691
  });
29613
29692
 
29614
29693
  // src/components/UnderReview.tsx
29615
- var import_material51, import_react_router_dom4, import_jsx_runtime50, UnderReview, UnderReview_default;
29694
+ var import_material51, import_react_router_dom5, import_jsx_runtime50, UnderReview, UnderReview_default;
29616
29695
  var init_UnderReview = __esm({
29617
29696
  "src/components/UnderReview.tsx"() {
29618
29697
  "use strict";
29619
29698
  import_material51 = require("@mui/material");
29620
- import_react_router_dom4 = require("react-router-dom");
29699
+ import_react_router_dom5 = require("react-router-dom");
29621
29700
  init_authenticationService();
29622
29701
  import_jsx_runtime50 = require("react/jsx-runtime");
29623
29702
  UnderReview = () => {
29624
29703
  const theme = (0, import_material51.useTheme)();
29625
- const navigate = (0, import_react_router_dom4.useNavigate)();
29704
+ const navigate = (0, import_react_router_dom5.useNavigate)();
29626
29705
  const handleResetSession = () => {
29627
29706
  authenticationService2.clearToken();
29628
29707
  navigate("/login");
@@ -29686,11 +29765,11 @@ var init_UnderReview = __esm({
29686
29765
  });
29687
29766
 
29688
29767
  // 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;
29768
+ 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
29769
  var init_FeedbackPulse = __esm({
29691
29770
  "src/components/feedback/FeedbackPulse.tsx"() {
29692
29771
  "use strict";
29693
- import_react61 = require("react");
29772
+ import_react62 = require("react");
29694
29773
  import_material52 = require("@mui/material");
29695
29774
  import_styles33 = require("@mui/material/styles");
29696
29775
  import_lucide_react15 = require("lucide-react");
@@ -29719,21 +29798,21 @@ var init_FeedbackPulse = __esm({
29719
29798
  };
29720
29799
  FeedbackPulse = ({ isMobile, feedbackEmail }) => {
29721
29800
  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);
29801
+ const [visible, setVisible] = (0, import_react62.useState)(false);
29802
+ const [thanked, setThanked] = (0, import_react62.useState)(false);
29803
+ const [modalOpen, setModalOpen] = (0, import_react62.useState)(false);
29804
+ const mountedAtRef = (0, import_react62.useRef)(Date.now());
29805
+ const baselineMessagesRef = (0, import_react62.useRef)(null);
29727
29806
  const totalMessages = useConversationStore(
29728
29807
  (s) => s.conversations.reduce((n, c) => n + c.history.length, 0)
29729
29808
  );
29730
29809
  const hydrated = useConversationStore((s) => s._hasHydrated);
29731
- (0, import_react61.useEffect)(() => {
29810
+ (0, import_react62.useEffect)(() => {
29732
29811
  if (hydrated && baselineMessagesRef.current === null) {
29733
29812
  baselineMessagesRef.current = totalMessages;
29734
29813
  }
29735
29814
  }, [hydrated, totalMessages]);
29736
- (0, import_react61.useEffect)(() => {
29815
+ (0, import_react62.useEffect)(() => {
29737
29816
  const cooldownMs = COOLDOWN_DAYS * 24 * 60 * 60 * 1e3;
29738
29817
  const forced = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("feedbackPulse");
29739
29818
  const check = () => {
@@ -29943,11 +30022,11 @@ var init_ConnectionStatus = __esm({
29943
30022
  });
29944
30023
 
29945
30024
  // src/hooks/useVoiceMode.ts
29946
- var import_react62, RMS_BASELINE, RMS_NORMALIZER, computeRms, pickSupportedMimeType, useVoiceMode;
30025
+ var import_react63, RMS_BASELINE, RMS_NORMALIZER, computeRms, pickSupportedMimeType, useVoiceMode;
29947
30026
  var init_useVoiceMode = __esm({
29948
30027
  "src/hooks/useVoiceMode.ts"() {
29949
30028
  "use strict";
29950
- import_react62 = require("react");
30029
+ import_react63 = require("react");
29951
30030
  init_stt_client();
29952
30031
  init_debugLogger();
29953
30032
  init_voiceModeStore();
@@ -29980,11 +30059,11 @@ var init_useVoiceMode = __esm({
29980
30059
  const setError = useVoiceModeStore((state) => state.setError);
29981
30060
  const resetTransientState = useVoiceModeStore((state) => state.resetTransientState);
29982
30061
  const setLastTranscript = useVoiceModeStore((state) => state.setLastTranscript);
29983
- const configRef = (0, import_react62.useRef)(config);
29984
- (0, import_react62.useEffect)(() => {
30062
+ const configRef = (0, import_react63.useRef)(config);
30063
+ (0, import_react63.useEffect)(() => {
29985
30064
  configRef.current = config;
29986
30065
  }, [config]);
29987
- (0, import_react62.useEffect)(() => {
30066
+ (0, import_react63.useEffect)(() => {
29988
30067
  if (!enabled) {
29989
30068
  return () => void 0;
29990
30069
  }
@@ -30245,11 +30324,11 @@ var chat_exports = {};
30245
30324
  __export(chat_exports, {
30246
30325
  default: () => chat_default
30247
30326
  });
30248
- var import_react63, import_material54, import_styles34, import_react_router_dom5, import_lucide_react16, import_jsx_runtime53, ChatContent, Chat, chat_default;
30327
+ var import_react64, import_material54, import_styles34, import_react_router_dom6, import_lucide_react16, import_jsx_runtime53, ChatContent, Chat, chat_default;
30249
30328
  var init_chat2 = __esm({
30250
30329
  "src/chat/chat.tsx"() {
30251
30330
  "use strict";
30252
- import_react63 = require("react");
30331
+ import_react64 = require("react");
30253
30332
  init_custom_logo();
30254
30333
  init_indexedDBService();
30255
30334
  import_material54 = require("@mui/material");
@@ -30258,7 +30337,7 @@ var init_chat2 = __esm({
30258
30337
  init_modelStore();
30259
30338
  init_canvasStore();
30260
30339
  init_canvas_panel();
30261
- import_react_router_dom5 = require("react-router-dom");
30340
+ import_react_router_dom6 = require("react-router-dom");
30262
30341
  init_chat_scroll_to_bottom_button();
30263
30342
  init_bandit_chat_logo();
30264
30343
  init_chat_messages();
@@ -30291,6 +30370,8 @@ var init_chat2 = __esm({
30291
30370
  init_voiceModeStore();
30292
30371
  init_ttsSanitizer();
30293
30372
  init_FeatureFlagContext();
30373
+ init_knowledgeStore();
30374
+ init_lucide_icons();
30294
30375
  import_lucide_react16 = require("lucide-react");
30295
30376
  import_jsx_runtime53 = require("react/jsx-runtime");
30296
30377
  ChatContent = () => {
@@ -30301,12 +30382,12 @@ var init_chat2 = __esm({
30301
30382
  const ossMode = isOSSMode() || !packageSettings?.featureFlags?.subscriptionType;
30302
30383
  const playgroundBypassAccess = packageSettings?.playgroundBypassAuth || typeof window !== "undefined" && window.location.pathname.includes("/playground");
30303
30384
  const notificationService2 = useNotificationService();
30304
- const [selectedTheme, setSelectedTheme] = (0, import_react63.useState)(null);
30305
- const [themeLoading, setThemeLoading] = (0, import_react63.useState)(true);
30385
+ const [selectedTheme, setSelectedTheme] = (0, import_react64.useState)(null);
30386
+ const [themeLoading, setThemeLoading] = (0, import_react64.useState)(true);
30306
30387
  const token = authenticationService2.getToken();
30307
30388
  const claims = token ? authenticationService2.parseJwtClaims(token) : null;
30308
30389
  const baseTheme = themeMap_default[selectedTheme ?? "bandit-dark"] || banditDarkTheme;
30309
- const banditTheme = (0, import_react63.useMemo)(() => {
30390
+ const banditTheme = (0, import_react64.useMemo)(() => {
30310
30391
  return (0, import_styles34.createTheme)(baseTheme, {
30311
30392
  components: {
30312
30393
  MuiInputBase: {
@@ -30354,12 +30435,12 @@ var init_chat2 = __esm({
30354
30435
  } = useVoiceStore();
30355
30436
  const isVoiceModeEnabled = useVoiceModeStore((state) => state.enabled);
30356
30437
  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)(() => {
30438
+ const previousVoiceModeEnabledRef = (0, import_react64.useRef)(isVoiceModeEnabled);
30439
+ const historyRef = (0, import_react64.useRef)(history);
30440
+ (0, import_react64.useEffect)(() => {
30360
30441
  historyRef.current = history;
30361
30442
  }, [history]);
30362
- (0, import_react63.useEffect)(() => {
30443
+ (0, import_react64.useEffect)(() => {
30363
30444
  if (!hydrated) return;
30364
30445
  const params = new URLSearchParams(window.location.search);
30365
30446
  const title = params.get("title") || "";
@@ -30375,7 +30456,7 @@ var init_chat2 = __esm({
30375
30456
  stop: ttsStop,
30376
30457
  isAvailable: isTTSAvailable
30377
30458
  } = useTTS();
30378
- (0, import_react63.useEffect)(() => {
30459
+ (0, import_react64.useEffect)(() => {
30379
30460
  const timer = setTimeout(() => {
30380
30461
  const isAuthenticated = authenticationService2.isAuthenticated();
30381
30462
  if (!initialized || availableVoices.length === 0) {
@@ -30389,7 +30470,7 @@ var init_chat2 = __esm({
30389
30470
  }, 500);
30390
30471
  return () => clearTimeout(timer);
30391
30472
  }, [initialized, availableVoices.length, loadVoicesFromAPI, token]);
30392
- (0, import_react63.useEffect)(() => {
30473
+ (0, import_react64.useEffect)(() => {
30393
30474
  const isAuthenticated = authenticationService2.isAuthenticated();
30394
30475
  if (packageSettings?.gatewayApiUrl && availableVoices.length === 0 && !initialized) {
30395
30476
  if (token && isAuthenticated) {
@@ -30401,19 +30482,82 @@ var init_chat2 = __esm({
30401
30482
  }
30402
30483
  }, [packageSettings?.gatewayApiUrl, availableVoices.length, initialized, loadVoicesFromAPI, token]);
30403
30484
  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);
30485
+ const inputRef = (0, import_react64.useRef)(null);
30486
+ const [pastedImages, setPastedImages] = (0, import_react64.useState)([]);
30487
+ const inputContainerRef = (0, import_react64.useRef)(null);
30488
+ const [inputHeight, setInputHeight] = (0, import_react64.useState)(80);
30489
+ const [isSubmitting, setIsSubmitting] = (0, import_react64.useState)(false);
30490
+ const [pendingMessage, setPendingMessage] = (0, import_react64.useState)(null);
30410
30491
  const { conversations, currentId, _hasHydrated, hydrate, toggleIncognito } = useConversationStore();
30411
30492
  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)(() => {
30493
+ const [isMobile, setIsMobile] = (0, import_react64.useState)(false);
30494
+ const [drawerOpen, setDrawerOpen] = (0, import_react64.useState)(false);
30495
+ const [mobileConversationsOpen, setMobileConversationsOpen] = (0, import_react64.useState)(false);
30496
+ const cardSwipeStartXRef = (0, import_react64.useRef)(null);
30497
+ const dragStartRef = (0, import_react64.useRef)(null);
30498
+ const [drawerDragX, setDrawerDragX] = (0, import_react64.useState)(null);
30499
+ const [drawerSettling, setDrawerSettling] = (0, import_react64.useState)(false);
30500
+ const [drawerSwap, setDrawerSwap] = (0, import_react64.useState)(false);
30501
+ const drawerOpenOffset = () => (typeof window === "undefined" ? 360 : Math.min(window.innerWidth - 56, 360)) - 12;
30502
+ const drawerDragStart = (e) => {
30503
+ const t = e.touches[0];
30504
+ if (!t) return;
30505
+ dragStartRef.current = { x: t.clientX, y: t.clientY, t: performance.now(), axis: null };
30506
+ };
30507
+ const drawerDragMove = (e) => {
30508
+ const s = dragStartRef.current;
30509
+ const t = e.touches[0];
30510
+ if (!s || !t) return;
30511
+ const dx = t.clientX - s.x;
30512
+ const dy = t.clientY - s.y;
30513
+ if (s.axis === null) {
30514
+ if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return;
30515
+ s.axis = Math.abs(dx) > Math.abs(dy) ? "x" : "y";
30516
+ }
30517
+ if (s.axis !== "x") return;
30518
+ setDrawerDragX(dx < 0 ? Math.max(dx, -drawerOpenOffset()) : dx * 0.15);
30519
+ };
30520
+ const drawerDragEnd = () => {
30521
+ const s = dragStartRef.current;
30522
+ dragStartRef.current = null;
30523
+ if (!s || s.axis !== "x" || drawerDragX === null) {
30524
+ setDrawerDragX(null);
30525
+ return;
30526
+ }
30527
+ const dt = Math.max(1, performance.now() - s.t);
30528
+ const velocity = drawerDragX / dt;
30529
+ const offset = drawerOpenOffset();
30530
+ const shouldClose = drawerDragX < -offset * 0.35 || velocity < -0.45;
30531
+ if (shouldClose) {
30532
+ setDrawerSettling(true);
30533
+ setDrawerDragX(-offset);
30534
+ window.setTimeout(() => {
30535
+ setDrawerSwap(true);
30536
+ setMobileConversationsOpen(false);
30537
+ requestAnimationFrame(() => requestAnimationFrame(() => setDrawerSwap(false)));
30538
+ window.setTimeout(() => {
30539
+ setDrawerDragX(null);
30540
+ setDrawerSettling(false);
30541
+ }, 320);
30542
+ }, 220);
30543
+ } else {
30544
+ setDrawerSettling(true);
30545
+ setDrawerDragX(0);
30546
+ window.setTimeout(() => {
30547
+ setDrawerDragX(null);
30548
+ setDrawerSettling(false);
30549
+ }, 220);
30550
+ }
30551
+ };
30552
+ const drawerDragStyle = drawerSwap ? { translate: "0px 0", transition: "none" } : isMobile && mobileConversationsOpen && drawerDragX !== null ? {
30553
+ translate: `${drawerDragX}px 0`,
30554
+ transition: drawerSettling ? "translate 0.22s cubic-bezier(0.2, 0.8, 0.2, 1)" : "none"
30555
+ } : void 0;
30556
+ const drawerPanelDragStyle = isMobile && drawerDragX !== null ? {
30557
+ translate: `${drawerDragX}px 0`,
30558
+ transition: drawerSettling ? "translate 0.22s cubic-bezier(0.2, 0.8, 0.2, 1)" : "none"
30559
+ } : void 0;
30560
+ (0, import_react64.useEffect)(() => {
30417
30561
  if (typeof document === "undefined") return;
30418
30562
  const meta = document.querySelector('meta[name="theme-color"]');
30419
30563
  if (!meta) return;
@@ -30431,27 +30575,50 @@ var init_chat2 = __esm({
30431
30575
  const chatContainerEl = chatContainerRef.current;
30432
30576
  const scrollTargetEl = scrollTargetRef.current;
30433
30577
  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);
30578
+ const [showScrollToBottom, setShowScrollToBottom] = (0, import_react64.useState)(false);
30579
+ const [streamBuffer, setStreamBuffer] = (0, import_react64.useState)("");
30580
+ const [responseStarted, setResponseStarted] = (0, import_react64.useState)(false);
30581
+ const [isStreaming, setIsStreaming] = (0, import_react64.useState)(false);
30582
+ const [isThinking, setIsThinking] = (0, import_react64.useState)(false);
30439
30583
  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);
30584
+ const [logoVisible, setLogoVisible] = (0, import_react64.useState)(initialLogoState);
30585
+ const [logoShouldRender, setLogoShouldRender] = (0, import_react64.useState)(initialLogoState);
30586
+ const streamingGraceUntilRef = (0, import_react64.useRef)(0);
30443
30587
  const GRACE_MS = 450;
30444
30588
  const GRACE_OFFSET_DESKTOP = 28;
30445
30589
  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);
30590
+ const followStreamRef = (0, import_react64.useRef)(true);
30591
+ const lastSpokenResponseRef = (0, import_react64.useRef)(null);
30592
+ const previousConversationIdRef = (0, import_react64.useRef)(null);
30593
+ const logoFadeTimeoutRef = (0, import_react64.useRef)(null);
30594
+ const [branding, setBranding] = (0, import_react64.useState)(null);
30595
+ const [brandingLoading, setBrandingLoading] = (0, import_react64.useState)(true);
30596
+ const isBrandingLoadInProgressRef = (0, import_react64.useRef)(false);
30453
30597
  const logoOnly = history.length === 0 && !brandingLoading;
30454
- (0, import_react63.useEffect)(() => {
30598
+ const knowledgeDocs = useKnowledgeStore((s) => s.docs);
30599
+ const knowledgeLoaded = useKnowledgeStore((s) => s.isLoaded);
30600
+ const [knowledgeNudgeDismissed, setKnowledgeNudgeDismissed] = (0, import_react64.useState)(() => {
30601
+ try {
30602
+ return localStorage.getItem("bandit.knowledgeNudge.dismissed") === "true";
30603
+ } catch {
30604
+ return false;
30605
+ }
30606
+ });
30607
+ const dismissKnowledgeNudge = (0, import_react64.useCallback)(() => {
30608
+ setKnowledgeNudgeDismissed(true);
30609
+ try {
30610
+ localStorage.setItem("bandit.knowledgeNudge.dismissed", "true");
30611
+ } catch {
30612
+ }
30613
+ }, []);
30614
+ const showKnowledgeNudge = logoOnly && !knowledgeNudgeDismissed && knowledgeLoaded && knowledgeDocs.length === 0 && featureFlag.hasFeature("documentKnowledge");
30615
+ const knowledgePath = typeof window !== "undefined" && window.location.pathname.startsWith("/playground") ? "/playground/management" : "/management";
30616
+ const safeNavigateTo = (to) => {
30617
+ if (typeof window !== "undefined") {
30618
+ window.location.assign(to);
30619
+ }
30620
+ };
30621
+ (0, import_react64.useEffect)(() => {
30455
30622
  const isEmptyConversation = (history?.length ?? 0) === 0;
30456
30623
  const isSending = Boolean(pendingMessage);
30457
30624
  const shouldShowLogo = isEmptyConversation && !isSending;
@@ -30481,7 +30648,7 @@ var init_chat2 = __esm({
30481
30648
  }, 500);
30482
30649
  }
30483
30650
  }, [history, pendingMessage]);
30484
- (0, import_react63.useEffect)(() => {
30651
+ (0, import_react64.useEffect)(() => {
30485
30652
  if (!brandingLoading && !themeLoading) {
30486
30653
  return;
30487
30654
  }
@@ -30497,18 +30664,18 @@ var init_chat2 = __esm({
30497
30664
  }, 2500);
30498
30665
  return () => window.clearTimeout(timeoutId);
30499
30666
  }, [brandingLoading, themeLoading, selectedTheme]);
30500
- (0, import_react63.useEffect)(() => () => {
30667
+ (0, import_react64.useEffect)(() => () => {
30501
30668
  if (logoFadeTimeoutRef.current) {
30502
30669
  window.clearTimeout(logoFadeTimeoutRef.current);
30503
30670
  logoFadeTimeoutRef.current = null;
30504
30671
  }
30505
30672
  }, []);
30506
- (0, import_react63.useEffect)(() => {
30673
+ (0, import_react64.useEffect)(() => {
30507
30674
  if (isStreaming && streamBuffer.trim() === "") {
30508
30675
  streamingGraceUntilRef.current = Date.now() + GRACE_MS;
30509
30676
  }
30510
30677
  }, [isStreaming, streamBuffer]);
30511
- (0, import_react63.useEffect)(() => {
30678
+ (0, import_react64.useEffect)(() => {
30512
30679
  if (!isStreaming) return;
30513
30680
  const container = chatContainerRef.current;
30514
30681
  if (!container) return;
@@ -30519,13 +30686,13 @@ var init_chat2 = __esm({
30519
30686
  container.scrollTo({ top: targetTop, behavior: "smooth" });
30520
30687
  }
30521
30688
  }, [streamBuffer, isStreaming, isMobile, chatContainerRef]);
30522
- (0, import_react63.useEffect)(() => {
30689
+ (0, import_react64.useEffect)(() => {
30523
30690
  if (!_hasHydrated) {
30524
30691
  debugLogger.info("Chat component triggering conversation store hydration");
30525
30692
  hydrate();
30526
30693
  }
30527
30694
  }, [_hasHydrated, hydrate]);
30528
- (0, import_react63.useEffect)(() => {
30695
+ (0, import_react64.useEffect)(() => {
30529
30696
  const loadBrandingAndTheme = async () => {
30530
30697
  if (isBrandingLoadInProgressRef.current) {
30531
30698
  debugLogger.warn("Branding loading already in progress, skipping");
@@ -30714,7 +30881,7 @@ var init_chat2 = __esm({
30714
30881
  window.removeEventListener("bandit-theme-changed", handleThemeChange);
30715
30882
  };
30716
30883
  }, []);
30717
- (0, import_react63.useEffect)(() => {
30884
+ (0, import_react64.useEffect)(() => {
30718
30885
  if (!chatContainerEl) return;
30719
30886
  const blurInputOnTouchScroll = () => {
30720
30887
  if (document.activeElement === inputRef.current) inputRef.current?.blur();
@@ -30722,13 +30889,13 @@ var init_chat2 = __esm({
30722
30889
  chatContainerEl.addEventListener("touchmove", blurInputOnTouchScroll, { passive: true });
30723
30890
  return () => chatContainerEl.removeEventListener("touchmove", blurInputOnTouchScroll);
30724
30891
  }, [chatContainerEl]);
30725
- (0, import_react63.useEffect)(() => {
30892
+ (0, import_react64.useEffect)(() => {
30726
30893
  const handleResize = () => setIsMobile(window.innerWidth <= 768);
30727
30894
  handleResize();
30728
30895
  window.addEventListener("resize", handleResize);
30729
30896
  return () => window.removeEventListener("resize", handleResize);
30730
30897
  }, []);
30731
- (0, import_react63.useEffect)(() => {
30898
+ (0, import_react64.useEffect)(() => {
30732
30899
  const setViewportHeight = () => {
30733
30900
  document.documentElement.style.setProperty(
30734
30901
  "--vh",
@@ -30739,7 +30906,7 @@ var init_chat2 = __esm({
30739
30906
  window.addEventListener("resize", setViewportHeight);
30740
30907
  return () => window.removeEventListener("resize", setViewportHeight);
30741
30908
  }, []);
30742
- (0, import_react63.useEffect)(() => {
30909
+ (0, import_react64.useEffect)(() => {
30743
30910
  if (!chatContainerEl) return;
30744
30911
  let rafId = null;
30745
30912
  const update = () => {
@@ -30758,7 +30925,7 @@ var init_chat2 = __esm({
30758
30925
  chatContainerEl.removeEventListener(SCROLL_STATE_CHANGED_EVENT, update);
30759
30926
  };
30760
30927
  }, [chatContainerEl, getScrollState]);
30761
- (0, import_react63.useEffect)(() => {
30928
+ (0, import_react64.useEffect)(() => {
30762
30929
  if (!chatContainerEl) return;
30763
30930
  let observer = null;
30764
30931
  let rafId = null;
@@ -30793,7 +30960,7 @@ var init_chat2 = __esm({
30793
30960
  if (observer) observer.disconnect();
30794
30961
  };
30795
30962
  }, [chatContainerEl, scrollTargetEl, scrollTargetRef, getScrollState, inputHeight, history.length]);
30796
- (0, import_react63.useEffect)(() => {
30963
+ (0, import_react64.useEffect)(() => {
30797
30964
  const isTransitioning = isStreaming || streamBuffer.trim() === "";
30798
30965
  const delay = isTransitioning ? 400 : 100;
30799
30966
  const timer = setTimeout(() => {
@@ -30802,7 +30969,7 @@ var init_chat2 = __esm({
30802
30969
  }, delay);
30803
30970
  return () => clearTimeout(timer);
30804
30971
  }, [history, streamBuffer, getScrollState, isStreaming]);
30805
- (0, import_react63.useLayoutEffect)(() => {
30972
+ (0, import_react64.useLayoutEffect)(() => {
30806
30973
  const scrollState = getScrollState();
30807
30974
  const now = Date.now();
30808
30975
  if (isStreaming && scrollState.shouldAutoScroll && followStreamRef.current) {
@@ -30836,7 +31003,7 @@ var init_chat2 = __esm({
30836
31003
  return () => clearTimeout(scrollTimer);
30837
31004
  }
30838
31005
  }, [history, isStreaming, scrollToBottom, getScrollState, isMobile, chatContainerRef]);
30839
- (0, import_react63.useEffect)(() => {
31006
+ (0, import_react64.useEffect)(() => {
30840
31007
  const observer = new ResizeObserver(() => {
30841
31008
  if (inputContainerRef.current)
30842
31009
  setInputHeight(inputContainerRef.current.offsetHeight);
@@ -30847,7 +31014,7 @@ var init_chat2 = __esm({
30847
31014
  }
30848
31015
  return () => observer.disconnect();
30849
31016
  }, []);
30850
- (0, import_react63.useEffect)(() => {
31017
+ (0, import_react64.useEffect)(() => {
30851
31018
  if (!hydrated || !_hasHydrated) return;
30852
31019
  if (currentId === null) {
30853
31020
  useAIQueryStore.setState({ history: [] });
@@ -30896,7 +31063,7 @@ var init_chat2 = __esm({
30896
31063
  setResponse,
30897
31064
  setComponentStatus
30898
31065
  ]);
30899
- (0, import_react63.useEffect)(() => {
31066
+ (0, import_react64.useEffect)(() => {
30900
31067
  debugLogger.info("Chat component conversation state", {
30901
31068
  hasHydrated: _hasHydrated,
30902
31069
  conversationCount: conversations.length,
@@ -30905,7 +31072,7 @@ var init_chat2 = __esm({
30905
31072
  storeState: "main-chat"
30906
31073
  });
30907
31074
  }, [_hasHydrated, conversations, currentId]);
30908
- (0, import_react63.useEffect)(() => {
31075
+ (0, import_react64.useEffect)(() => {
30909
31076
  if (!_hasHydrated || !chatContainerEl) return;
30910
31077
  let rafId = null;
30911
31078
  const sync = () => {
@@ -30940,7 +31107,7 @@ var init_chat2 = __esm({
30940
31107
  notificationService2?.handleHttpError(error);
30941
31108
  }
30942
31109
  });
30943
- const handleStop = (0, import_react63.useCallback)(() => {
31110
+ const handleStop = (0, import_react64.useCallback)(() => {
30944
31111
  try {
30945
31112
  aiProvider.cancel();
30946
31113
  } catch (error) {
@@ -30949,7 +31116,7 @@ var init_chat2 = __esm({
30949
31116
  });
30950
31117
  }
30951
31118
  }, [aiProvider]);
30952
- const handleVoiceInterrupt = (0, import_react63.useCallback)(() => {
31119
+ const handleVoiceInterrupt = (0, import_react64.useCallback)(() => {
30953
31120
  try {
30954
31121
  ttsStop();
30955
31122
  } catch (error) {
@@ -30968,7 +31135,7 @@ var init_chat2 = __esm({
30968
31135
  handleStop();
30969
31136
  }
30970
31137
  }, [ttsStop, isStreaming, handleStop]);
30971
- const handleSend = (0, import_react63.useCallback)(
31138
+ const handleSend = (0, import_react64.useCallback)(
30972
31139
  (question, images, displayQuestion) => {
30973
31140
  const requestStartTime = trackRequestStart();
30974
31141
  const questionForDisplay = displayQuestion || question;
@@ -31078,7 +31245,7 @@ var init_chat2 = __esm({
31078
31245
  trackRequestStart
31079
31246
  ]
31080
31247
  );
31081
- const handleVoiceTranscription = (0, import_react63.useCallback)(
31248
+ const handleVoiceTranscription = (0, import_react64.useCallback)(
31082
31249
  (text) => {
31083
31250
  const cleaned = text.trim();
31084
31251
  if (!cleaned) {
@@ -31095,7 +31262,7 @@ var init_chat2 = __esm({
31095
31262
  onInterrupt: handleVoiceInterrupt,
31096
31263
  onError: (message) => notificationService2?.showError?.(message)
31097
31264
  });
31098
- (0, import_react63.useEffect)(() => {
31265
+ (0, import_react64.useEffect)(() => {
31099
31266
  const previouslyEnabled = previousVoiceModeEnabledRef.current;
31100
31267
  previousVoiceModeEnabledRef.current = isVoiceModeEnabled;
31101
31268
  if (!previouslyEnabled && isVoiceModeEnabled) {
@@ -31120,7 +31287,7 @@ var init_chat2 = __esm({
31120
31287
  }
31121
31288
  }
31122
31289
  }, [isVoiceModeEnabled, ttsStop]);
31123
- (0, import_react63.useEffect)(() => {
31290
+ (0, import_react64.useEffect)(() => {
31124
31291
  if (!isVoiceModeEnabled || !isStreaming) {
31125
31292
  return;
31126
31293
  }
@@ -31140,7 +31307,7 @@ var init_chat2 = __esm({
31140
31307
  }
31141
31308
  lastSpokenResponseRef.current = null;
31142
31309
  }, [isStreaming, isVoiceModeEnabled, ttsStop]);
31143
- (0, import_react63.useEffect)(() => {
31310
+ (0, import_react64.useEffect)(() => {
31144
31311
  if (!isVoiceModeEnabled) {
31145
31312
  lastSpokenResponseRef.current = null;
31146
31313
  return;
@@ -31181,13 +31348,13 @@ var init_chat2 = __esm({
31181
31348
  cancelled = true;
31182
31349
  };
31183
31350
  }, [history, isStreaming, isVoiceModeEnabled, isTTSAvailable, ttsSpeak]);
31184
- const handleModelChange = (0, import_react63.useCallback)(
31351
+ const handleModelChange = (0, import_react64.useCallback)(
31185
31352
  (modelId) => {
31186
31353
  setSelectedModel(modelId);
31187
31354
  },
31188
31355
  [setSelectedModel]
31189
31356
  );
31190
- const handleVoiceChange = (0, import_react63.useCallback)(async (voiceId) => {
31357
+ const handleVoiceChange = (0, import_react64.useCallback)(async (voiceId) => {
31191
31358
  ttsStop();
31192
31359
  stopTTS();
31193
31360
  setSelectedVoice(voiceId);
@@ -31297,27 +31464,23 @@ var init_chat2 = __esm({
31297
31464
  // is the same near-black as the shell.
31298
31465
  border: isMobile && mobileConversationsOpen ? `1px solid ${theme.palette.mode === "dark" ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.12)"}` : "none",
31299
31466
  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"
31467
+ 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
31468
  }),
31302
31469
  onClick: isMobile && mobileConversationsOpen ? (e) => {
31303
31470
  if (e.currentTarget.contains(e.target)) {
31304
31471
  setMobileConversationsOpen(false);
31305
31472
  }
31306
31473
  } : void 0,
31474
+ style: drawerDragStyle,
31307
31475
  onTouchStart: isMobile && mobileConversationsOpen ? (e) => {
31308
31476
  if (e.currentTarget.contains(e.target)) {
31309
31477
  cardSwipeStartXRef.current = e.touches[0]?.clientX ?? null;
31478
+ drawerDragStart(e);
31310
31479
  }
31311
31480
  } : 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,
31481
+ onTouchMove: isMobile && mobileConversationsOpen ? drawerDragMove : void 0,
31482
+ onTouchEnd: isMobile && mobileConversationsOpen ? drawerDragEnd : void 0,
31483
+ onTouchCancel: isMobile && mobileConversationsOpen ? drawerDragEnd : void 0,
31321
31484
  children: [
31322
31485
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31323
31486
  chat_app_bar_default,
@@ -31330,7 +31493,13 @@ var init_chat2 = __esm({
31330
31493
  drawerOpen,
31331
31494
  setDrawerOpen,
31332
31495
  mobileConversationsOpen,
31333
- onMobileConversationsOpenChange: setMobileConversationsOpen
31496
+ onMobileConversationsOpenChange: setMobileConversationsOpen,
31497
+ mobileConversationsDrag: isMobile ? {
31498
+ style: drawerPanelDragStyle,
31499
+ onTouchStart: drawerDragStart,
31500
+ onTouchMove: drawerDragMove,
31501
+ onTouchEnd: drawerDragEnd
31502
+ } : void 0
31334
31503
  }
31335
31504
  ),
31336
31505
  isTemporaryChat && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
@@ -31486,6 +31655,53 @@ var init_chat2 = __esm({
31486
31655
  }
31487
31656
  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
31657
  })(),
31658
+ showKnowledgeNudge && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
31659
+ import_material54.Box,
31660
+ {
31661
+ sx: (theme) => ({
31662
+ mx: "auto",
31663
+ mt: 2,
31664
+ mb: 1,
31665
+ px: 2,
31666
+ py: 1.25,
31667
+ maxWidth: 420,
31668
+ display: "flex",
31669
+ alignItems: "center",
31670
+ gap: 1.25,
31671
+ borderRadius: 2.5,
31672
+ border: `1px solid ${theme.palette.divider}`,
31673
+ bgcolor: theme.palette.action.hover,
31674
+ opacity: logoVisible ? 1 : 0,
31675
+ transition: "opacity 0.5s ease"
31676
+ }),
31677
+ children: [
31678
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_material54.Box, { sx: { flex: 1, minWidth: 0 }, children: [
31679
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_material54.Typography, { variant: "body2", sx: { fontWeight: 600 }, children: "Add your documents" }),
31680
+ /* @__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." })
31681
+ ] }),
31682
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31683
+ import_material54.Button,
31684
+ {
31685
+ size: "small",
31686
+ variant: "outlined",
31687
+ onClick: () => safeNavigateTo(knowledgePath),
31688
+ sx: { textTransform: "none", fontWeight: 600, flexShrink: 0 },
31689
+ children: "Upload"
31690
+ }
31691
+ ),
31692
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31693
+ import_material54.IconButton,
31694
+ {
31695
+ size: "small",
31696
+ "aria-label": "Dismiss",
31697
+ onClick: dismissKnowledgeNudge,
31698
+ sx: { flexShrink: 0 },
31699
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(CloseIcon, { fontSize: "small" })
31700
+ }
31701
+ )
31702
+ ]
31703
+ }
31704
+ ),
31489
31705
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31490
31706
  import_material54.Box,
31491
31707
  {
@@ -31622,7 +31838,7 @@ var init_chat2 = __esm({
31622
31838
  });
31623
31839
  if (!allowUnauthenticated && !bypassAuth && !authenticationService2.isAuthenticated()) {
31624
31840
  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 });
31841
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_router_dom6.Navigate, { to: "/login", replace: true });
31626
31842
  }
31627
31843
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ChatContent, {});
31628
31844
  };
@@ -31636,13 +31852,13 @@ __export(management_exports, {
31636
31852
  default: () => management_default
31637
31853
  });
31638
31854
  module.exports = __toCommonJS(management_exports);
31639
- var import_react64 = require("react");
31855
+ var import_react65 = require("react");
31640
31856
  var import_useMediaQuery2 = __toESM(require("@mui/material/useMediaQuery"));
31641
31857
  var import_styles35 = require("@mui/material/styles");
31642
31858
  init_useKnowledgeStore();
31643
31859
  init_indexedDBService();
31644
31860
  var import_material55 = require("@mui/material");
31645
- var import_react_router_dom6 = require("react-router-dom");
31861
+ var import_react_router_dom7 = require("react-router-dom");
31646
31862
 
31647
31863
  // src/modals/chat-modal/chat-modal.tsx
31648
31864
  var import_react23 = require("react");
@@ -36463,6 +36679,7 @@ var useGatewayHealth = (options) => {
36463
36679
  };
36464
36680
 
36465
36681
  // src/management/components/PreferencesTab.tsx
36682
+ init_installedApp();
36466
36683
  init_lucide_icons();
36467
36684
  var import_jsx_runtime19 = require("react/jsx-runtime");
36468
36685
  var PreferencesTab = ({
@@ -37319,9 +37536,8 @@ var ViewportDiagnostics = () => {
37319
37536
  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
37537
  document.body.appendChild(probe);
37321
37538
  const cs = getComputedStyle(probe);
37322
- const standalone = window.matchMedia?.("(display-mode: standalone)").matches || window.navigator.standalone === true;
37323
37539
  setMetrics({
37324
- mode: standalone ? "standalone" : "browser",
37540
+ mode: isNativeShell() ? "native" : isStandalonePwa2() ? "standalone" : "browser",
37325
37541
  "screen.height": `${window.screen.height}`,
37326
37542
  innerHeight: `${window.innerHeight}`,
37327
37543
  "docElement.clientHeight": `${document.documentElement.clientHeight}`,
@@ -44335,10 +44551,10 @@ var buildCapabilitiesUrl = (gatewayApiUrl) => {
44335
44551
  return `${trimmed}/api/capabilities`;
44336
44552
  };
44337
44553
  var Management = () => {
44338
- const navigate = (0, import_react_router_dom6.useNavigate)();
44554
+ const navigate = (0, import_react_router_dom7.useNavigate)();
44339
44555
  const notificationService2 = useNotificationService();
44340
44556
  const isMobile = (0, import_useMediaQuery2.default)("(max-width:900px)");
44341
- const [sidebarOpen, setSidebarOpen] = (0, import_react64.useState)(false);
44557
+ const [sidebarOpen, setSidebarOpen] = (0, import_react65.useState)(false);
44342
44558
  const getOptimalFabLogo = async () => {
44343
44559
  const banditHead6 = "https://cdn.burtson.ai/images/bandit-head.png";
44344
44560
  try {
@@ -44378,17 +44594,17 @@ var Management = () => {
44378
44594
  hasTransparentLogo,
44379
44595
  setHasTransparentLogo
44380
44596
  } = useModelStore();
44381
- const [modalOpen, setModalOpen] = (0, import_react64.useState)(false);
44597
+ const [modalOpen, setModalOpen] = (0, import_react65.useState)(false);
44382
44598
  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);
44599
+ const [fabLogo, setFabLogo] = (0, import_react65.useState)(banditHead5);
44600
+ const [tabIndex, setTabIndex] = (0, import_react65.useState)(4);
44601
+ const [logoFile, setLogoFile] = (0, import_react65.useState)(null);
44602
+ const [logoBase64, setLogoBase64] = (0, import_react65.useState)(null);
44603
+ const [brandingText, setBrandingText] = (0, import_react65.useState)("");
44604
+ const [headerMode, setHeaderMode] = (0, import_react65.useState)("logo");
44605
+ const [theme, setTheme] = (0, import_react65.useState)("bandit-dark");
44606
+ const [customAvatarBase64, setCustomAvatarBase64] = (0, import_react65.useState)(null);
44607
+ const [presetAvatar, setPresetAvatar] = (0, import_react65.useState)(null);
44392
44608
  const showSnackbarMessage = (message, severity = "success") => {
44393
44609
  if (severity === "success") {
44394
44610
  notificationService2?.showSuccess(message);
@@ -44396,9 +44612,9 @@ var Management = () => {
44396
44612
  notificationService2?.showError(message);
44397
44613
  }
44398
44614
  };
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);
44615
+ const [restoreDialogOpen, setRestoreDialogOpen] = (0, import_react65.useState)(false);
44616
+ const [brandingLoaded, setBrandingLoaded] = (0, import_react65.useState)(false);
44617
+ const [isLoadingBranding, setIsLoadingBranding] = (0, import_react65.useState)(false);
44402
44618
  const { initModels } = useModelStore();
44403
44619
  const { settings: packageSettings } = usePackageSettingsStore();
44404
44620
  const authToken = useAuthenticationStore((state) => state.token);
@@ -44406,8 +44622,8 @@ var Management = () => {
44406
44622
  const { hasAdminDashboard, hasLimitedAdminDashboard, getCurrentTier, hasAdvancedSearch } = useFeatures();
44407
44623
  const { showAdminPanel, showLimitedAdminPanel } = useFeatureVisibility();
44408
44624
  const { provider: currentProvider, config: currentProviderConfig } = useAIProviderStore();
44409
- const [seedPacksEnabled, setSeedPacksEnabled] = (0, import_react64.useState)(false);
44410
- const [localSelectedModel, setLocalSelectedModel] = (0, import_react64.useState)({
44625
+ const [seedPacksEnabled, setSeedPacksEnabled] = (0, import_react65.useState)(false);
44626
+ const [localSelectedModel, setLocalSelectedModel] = (0, import_react65.useState)({
44411
44627
  name: "",
44412
44628
  tagline: "",
44413
44629
  systemPrompt: "",
@@ -44420,7 +44636,7 @@ var Management = () => {
44420
44636
  loadDocuments,
44421
44637
  clearAllDocuments
44422
44638
  } = useKnowledgeStore2();
44423
- (0, import_react64.useEffect)(() => {
44639
+ (0, import_react65.useEffect)(() => {
44424
44640
  if (selectedModel) {
44425
44641
  const selected = availableModels.find((m) => m.name === selectedModel);
44426
44642
  if (selected) {
@@ -44444,7 +44660,7 @@ var Management = () => {
44444
44660
  }
44445
44661
  }
44446
44662
  }, [selectedModel, availableModels]);
44447
- const loadBrandingConfig = (0, import_react64.useCallback)(async () => {
44663
+ const loadBrandingConfig = (0, import_react65.useCallback)(async () => {
44448
44664
  if (isLoadingBranding || brandingLoaded) {
44449
44665
  debugLogger.warn("Branding loading already in progress or completed, skipping");
44450
44666
  return;
@@ -44561,15 +44777,15 @@ var Management = () => {
44561
44777
  setTagline,
44562
44778
  setTheme
44563
44779
  ]);
44564
- (0, import_react64.useEffect)(() => {
44780
+ (0, import_react65.useEffect)(() => {
44565
44781
  void loadBrandingConfig();
44566
44782
  }, [loadBrandingConfig]);
44567
44783
  const handleOpenModal = () => setModalOpen(true);
44568
44784
  const handleCloseModal = () => setModalOpen(false);
44569
- (0, import_react64.useEffect)(() => {
44785
+ (0, import_react65.useEffect)(() => {
44570
44786
  getOptimalFabLogo().then(setFabLogo);
44571
44787
  }, []);
44572
- (0, import_react64.useEffect)(() => {
44788
+ (0, import_react65.useEffect)(() => {
44573
44789
  if (logoBase64) {
44574
44790
  setFabLogo(logoBase64);
44575
44791
  } else {
@@ -45022,7 +45238,7 @@ var Management = () => {
45022
45238
  reader.readAsText(file);
45023
45239
  }
45024
45240
  };
45025
- (0, import_react64.useEffect)(() => {
45241
+ (0, import_react65.useEffect)(() => {
45026
45242
  if (localSelectedModel.selectedModel && !availableModels.some((m) => m.name === localSelectedModel.selectedModel)) {
45027
45243
  setLocalSelectedModel((prev) => ({
45028
45244
  ...prev,
@@ -45030,10 +45246,10 @@ var Management = () => {
45030
45246
  }));
45031
45247
  }
45032
45248
  }, [availableModels, localSelectedModel.selectedModel]);
45033
- (0, import_react64.useEffect)(() => {
45249
+ (0, import_react65.useEffect)(() => {
45034
45250
  loadDocuments();
45035
45251
  }, [loadDocuments]);
45036
- (0, import_react64.useEffect)(() => {
45252
+ (0, import_react65.useEffect)(() => {
45037
45253
  const gatewayApiUrl = packageSettings?.gatewayApiUrl;
45038
45254
  if (!gatewayApiUrl || gatewayApiUrl.toLowerCase().startsWith("playground://")) {
45039
45255
  setSeedPacksEnabled(false);
@@ -45076,7 +45292,7 @@ var Management = () => {
45076
45292
  isActive = false;
45077
45293
  };
45078
45294
  }, [packageSettings?.gatewayApiUrl, authToken]);
45079
- const currentTheme = (0, import_react64.useMemo)(() => {
45295
+ const currentTheme = (0, import_react65.useMemo)(() => {
45080
45296
  const baseTheme = predefinedThemes[theme] || banditDarkTheme;
45081
45297
  return (0, import_styles35.createTheme)(baseTheme, {
45082
45298
  // Management-scoped density: condenses every settings tab at once. This
@@ -45197,7 +45413,7 @@ var Management = () => {
45197
45413
  });
45198
45414
  const preferredDefaultTabIndex = navTabs.findIndex((tab) => tab.label === "Preferences");
45199
45415
  const defaultTabIndex = preferredDefaultTabIndex >= 0 ? preferredDefaultTabIndex : 0;
45200
- (0, import_react64.useEffect)(() => {
45416
+ (0, import_react65.useEffect)(() => {
45201
45417
  setTabIndex((current) => {
45202
45418
  if (current < 0 || current >= navTabs.length) {
45203
45419
  return defaultTabIndex;
@@ -45354,6 +45570,10 @@ var Management = () => {
45354
45570
  justifyContent: "space-between",
45355
45571
  px: 3,
45356
45572
  py: 1.5,
45573
+ // The native shell and installed PWAs run edge-to-edge, so the
45574
+ // sticky bar starts underneath the status bar / notch unless it
45575
+ // reserves the inset itself (chat-app-bar does the same).
45576
+ pt: "calc(12px + env(safe-area-inset-top, 0px))",
45357
45577
  bgcolor: (theme2) => theme2.palette.mode === "dark" ? "rgba(24,28,40,0.95)" : "rgba(255,255,255,0.95)",
45358
45578
  borderBottom: (theme2) => `1px solid ${theme2.palette.divider}`,
45359
45579
  backdropFilter: "blur(12px)",
@@ -45649,7 +45869,7 @@ var Management = () => {
45649
45869
  ]
45650
45870
  }
45651
45871
  ),
45652
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
45872
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
45653
45873
  import_material55.Fab,
45654
45874
  {
45655
45875
  "aria-label": "AI",
@@ -45685,7 +45905,7 @@ var Management = () => {
45685
45905
  )
45686
45906
  }
45687
45907
  ),
45688
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(chat_modal_default, { open: modalOpen, onClose: handleCloseModal })
45908
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(chat_modal_default, { open: modalOpen, onClose: handleCloseModal })
45689
45909
  ]
45690
45910
  }
45691
45911
  )