@burtson-labs/bandit-engine 2.0.127 → 2.0.129

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 (31) hide show
  1. package/dist/{chat-2XYHFUT2.mjs → chat-TO26ORKD.mjs} +5 -5
  2. package/dist/chat-provider.js +1 -1
  3. package/dist/chat-provider.js.map +1 -1
  4. package/dist/chat-provider.mjs +2 -2
  5. package/dist/{chunk-RRSACJI2.mjs → chunk-3LOS2EAN.mjs} +135 -448
  6. package/dist/chunk-3LOS2EAN.mjs.map +1 -0
  7. package/dist/{chunk-OLKDPUWL.mjs → chunk-BQW7DSJ6.mjs} +2 -2
  8. package/dist/{chunk-574ILQTT.mjs → chunk-FWFDOSRM.mjs} +2 -2
  9. package/dist/{chunk-TQP3GUW5.mjs → chunk-N3TVVQVS.mjs} +3 -3
  10. package/dist/{chunk-6ONL4SRK.mjs → chunk-NLBZZP3B.mjs} +2 -2
  11. package/dist/{chunk-6ONL4SRK.mjs.map → chunk-NLBZZP3B.mjs.map} +1 -1
  12. package/dist/{chunk-SAQUEXYS.mjs → chunk-PU74BI6Y.mjs} +2 -2
  13. package/dist/{chunk-7NBLIL76.mjs → chunk-VXGJ4XH6.mjs} +23 -6
  14. package/dist/chunk-VXGJ4XH6.mjs.map +1 -0
  15. package/dist/index.js +148 -442
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +7 -7
  18. package/dist/management/management.js +148 -442
  19. package/dist/management/management.js.map +1 -1
  20. package/dist/management/management.mjs +5 -5
  21. package/dist/modals/chat-modal/chat-modal.js +1 -1
  22. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  23. package/dist/modals/chat-modal/chat-modal.mjs +3 -3
  24. package/package.json +1 -1
  25. package/dist/chunk-7NBLIL76.mjs.map +0 -1
  26. package/dist/chunk-RRSACJI2.mjs.map +0 -1
  27. /package/dist/{chat-2XYHFUT2.mjs.map → chat-TO26ORKD.mjs.map} +0 -0
  28. /package/dist/{chunk-OLKDPUWL.mjs.map → chunk-BQW7DSJ6.mjs.map} +0 -0
  29. /package/dist/{chunk-574ILQTT.mjs.map → chunk-FWFDOSRM.mjs.map} +0 -0
  30. /package/dist/{chunk-TQP3GUW5.mjs.map → chunk-N3TVVQVS.mjs.map} +0 -0
  31. /package/dist/{chunk-SAQUEXYS.mjs.map → chunk-PU74BI6Y.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -1583,7 +1583,7 @@ var init_conversationStore = __esm({
1583
1583
  const now = /* @__PURE__ */ new Date();
1584
1584
  const newConv = normalizeConversation({
1585
1585
  id: (0, import_uuid.v4)(),
1586
- name: "Incognito chat",
1586
+ name: "Temporary chat",
1587
1587
  history: [],
1588
1588
  model: selectedModel,
1589
1589
  createdAt: now,
@@ -15880,7 +15880,7 @@ var init_chat_messages = __esm({
15880
15880
  StreamingMarkdown_default,
15881
15881
  {
15882
15882
  content: linkifyCitations(stripSourcesForDisplay(content), parseWebSources(content)),
15883
- isStreaming: isStreaming && isLast,
15883
+ isStreaming: isStreaming && isLast && !workingMatch,
15884
15884
  sources: sourceSummaries
15885
15885
  }
15886
15886
  ),
@@ -19578,7 +19578,7 @@ ${sanitize(
19578
19578
  fullWidth: true,
19579
19579
  multiline: true,
19580
19580
  maxRows: compactMobile ? 6 : 12,
19581
- placeholder: isIncognito ? "Incognito chat \u2014 not saved" : "What's on your mind?",
19581
+ placeholder: isIncognito ? "Temporary chat \u2014 not saved" : "What's on your mind?",
19582
19582
  value: inputValue,
19583
19583
  onChange: (e) => setInputValue(e.target.value),
19584
19584
  onKeyDown: handleKeyPress,
@@ -19702,7 +19702,7 @@ ${sanitize(
19702
19702
  fullWidth: true,
19703
19703
  multiline: true,
19704
19704
  maxRows: 6,
19705
- placeholder: isIncognito ? "Incognito chat \u2014 not saved" : "What's on your mind?",
19705
+ placeholder: isIncognito ? "Temporary chat \u2014 not saved" : "What's on your mind?",
19706
19706
  value: inputValue,
19707
19707
  onChange: (e) => setInputValue(e.target.value),
19708
19708
  onKeyDown: handleKeyPress,
@@ -19720,26 +19720,29 @@ ${sanitize(
19720
19720
  sx: { flex: 1, alignSelf: "center", mx: 0.5 }
19721
19721
  }
19722
19722
  ),
19723
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_material18.Box, { sx: { display: "flex", alignItems: "center", gap: isMobile ? 0.6 : 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_material18.Tooltip, { title: isStreaming ? "Stop response" : "Send message", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19724
- import_material18.IconButton,
19725
- {
19726
- onClick: isStreaming ? onStop || (() => {
19727
- }) : handleSubmit,
19728
- disabled: (isStreaming ? false : isSubmitting) || !isStreaming && !inputValue.trim() && pastedImages.length === 0 && fileInputs.length === 0,
19729
- sx: {
19730
- bgcolor: sendButtonBackground,
19731
- color: sendButtonColor,
19732
- borderRadius: "50%",
19733
- width: sendIconSize,
19734
- height: sendIconSize,
19735
- boxShadow: sendButtonShadow,
19736
- transition: "background-color 0.2s ease, box-shadow 0.2s ease",
19737
- "&:hover": { bgcolor: sendButtonHover },
19738
- "&.Mui-disabled": { opacity: 0.5 }
19739
- },
19740
- children: isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CloseIcon, { fontSize: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ArrowUpwardIcon, { fontSize: "small" })
19741
- }
19742
- ) }) }) })
19723
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_material18.Box, { sx: { display: "flex", alignItems: "center", gap: isMobile ? 0.6 : 1 }, children: [
19724
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(engine_picker_default, {}),
19725
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_material18.Tooltip, { title: isStreaming ? "Stop response" : "Send message", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19726
+ import_material18.IconButton,
19727
+ {
19728
+ onClick: isStreaming ? onStop || (() => {
19729
+ }) : handleSubmit,
19730
+ disabled: (isStreaming ? false : isSubmitting) || !isStreaming && !inputValue.trim() && pastedImages.length === 0 && fileInputs.length === 0,
19731
+ sx: {
19732
+ bgcolor: sendButtonBackground,
19733
+ color: sendButtonColor,
19734
+ borderRadius: "50%",
19735
+ width: sendIconSize,
19736
+ height: sendIconSize,
19737
+ boxShadow: sendButtonShadow,
19738
+ transition: "background-color 0.2s ease, box-shadow 0.2s ease",
19739
+ "&:hover": { bgcolor: sendButtonHover },
19740
+ "&.Mui-disabled": { opacity: 0.5 }
19741
+ },
19742
+ children: isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CloseIcon, { fontSize: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ArrowUpwardIcon, { fontSize: "small" })
19743
+ }
19744
+ ) }) })
19745
+ ] })
19743
19746
  ]
19744
19747
  }
19745
19748
  ),
@@ -19786,7 +19789,7 @@ ${sanitize(
19786
19789
  ]
19787
19790
  }
19788
19791
  ),
19789
- !isMobile && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19792
+ (!isMobile || isIncognito) && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19790
19793
  import_material18.Typography,
19791
19794
  {
19792
19795
  variant: "caption",
@@ -19798,10 +19801,7 @@ ${sanitize(
19798
19801
  textAlign: "center",
19799
19802
  pointerEvents: "none"
19800
19803
  },
19801
- children: [
19802
- brandingText || "BanditAI",
19803
- " may be wrong - double-check important info."
19804
- ]
19804
+ children: isIncognito ? "Temporary chats aren't saved to history, synced, or added to memory." : `${brandingText || "BanditAI"} may be wrong - double-check important info.`
19805
19805
  }
19806
19806
  )
19807
19807
  ]
@@ -26719,7 +26719,6 @@ var init_chat_app_bar = __esm({
26719
26719
  import_material28 = require("@mui/material");
26720
26720
  import_react_router_dom2 = require("react-router-dom");
26721
26721
  init_lucide_icons();
26722
- init_util();
26723
26722
  init_conversation_drawer();
26724
26723
  init_enhanced_mobile_conversations_modal();
26725
26724
  init_conversationStore();
@@ -26941,13 +26940,13 @@ var init_chat_app_bar = __esm({
26941
26940
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
26942
26941
  import_material28.Tooltip,
26943
26942
  {
26944
- title: isIncognitoActive ? "End incognito chat" : "Start incognito chat \u2014 not saved",
26943
+ title: isIncognitoActive ? "End temporary chat" : "Temporary chat \u2014 not saved anywhere",
26945
26944
  arrow: true,
26946
26945
  children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
26947
26946
  import_material28.IconButton,
26948
26947
  {
26949
26948
  onClick: toggleIncognito,
26950
- "aria-label": isIncognitoActive ? "End incognito chat" : "Start incognito chat",
26949
+ "aria-label": isIncognitoActive ? "End temporary chat" : "Start temporary chat",
26951
26950
  "aria-pressed": isIncognitoActive,
26952
26951
  sx: {
26953
26952
  width: 44,
@@ -27046,35 +27045,11 @@ var init_chat_app_bar = __esm({
27046
27045
  "aria-label": "Create new conversation",
27047
27046
  children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(AddIcon, {})
27048
27047
  }
27049
- ) }),
27050
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27051
- import_material28.Tooltip,
27052
- {
27053
- title: isIncognitoActive ? "End incognito chat" : "Start incognito chat \u2014 not saved",
27054
- arrow: true,
27055
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27056
- import_material28.IconButton,
27057
- {
27058
- onClick: toggleIncognito,
27059
- "aria-label": isIncognitoActive ? "End incognito chat" : "Start incognito chat",
27060
- "aria-pressed": isIncognitoActive,
27061
- sx: {
27062
- ...pillButtonStyles,
27063
- ...isIncognitoActive && {
27064
- bgcolor: theme.palette.text.primary,
27065
- color: theme.palette.getContrastText(theme.palette.text.primary),
27066
- "&:hover": { bgcolor: theme.palette.text.primary }
27067
- }
27068
- },
27069
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GhostIcon, {})
27070
- }
27071
- )
27072
- }
27073
- )
27048
+ ) })
27074
27049
  ]
27075
27050
  }
27076
27051
  ),
27077
- !isMobile && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
27052
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27078
27053
  import_material28.Box,
27079
27054
  {
27080
27055
  sx: {
@@ -27094,368 +27069,30 @@ var init_chat_app_bar = __esm({
27094
27069
  boxShadow: theme.palette.mode === "dark" ? "0 6px 20px rgba(0,0,0,0.4)" : "0 6px 20px rgba(0,0,0,0.15)"
27095
27070
  }
27096
27071
  },
27097
- children: [
27098
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Tooltip, { title: `Current AI: ${selectedModel.replace("Bandit-", "")}`, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27099
- import_material28.IconButton,
27100
- {
27101
- onClick: (e) => setModelAnchorEl(e.currentTarget),
27102
- sx: {
27103
- ...pillButtonStyles,
27104
- "&:hover": {
27105
- ...pillButtonStyles["&:hover"],
27106
- "& .MuiAvatar-root": {
27107
- transform: "scale(1.1)",
27108
- filter: "brightness(2) saturate(1.2)"
27109
- }
27110
- }
27111
- },
27112
- "aria-label": `Change AI personality. Currently using ${selectedModel}`,
27113
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27114
- import_material27.Avatar,
27115
- {
27116
- src: currentAvatar,
27117
- alt: selectedModel,
27118
- sx: {
27119
- width: "36px",
27120
- height: "36px",
27121
- filter: "brightness(1.7)",
27122
- transition: "all 0.2s ease-in-out",
27123
- border: `2px solid ${theme.palette.primary.main}20`
27124
- },
27125
- className: "MuiAvatar-root"
27126
- }
27127
- )
27128
- }
27129
- ) }),
27130
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Tooltip, { title: `Engine \xB7 ${engineDisplay}`, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27131
- import_material28.IconButton,
27132
- {
27133
- onClick: (e) => setEngineAnchorEl(e.currentTarget),
27134
- sx: pillButtonStyles,
27135
- "aria-label": `Change base model (engine). Currently ${engineDisplay}`,
27136
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(AutoAwesomeIcon, { fontSize: "small" })
27137
- }
27138
- ) }),
27139
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
27140
- import_material28.Menu,
27141
- {
27142
- anchorEl: engineAnchorEl,
27143
- open: Boolean(engineAnchorEl),
27144
- onClose: () => setEngineAnchorEl(null),
27145
- transformOrigin: { horizontal: "right", vertical: "top" },
27146
- anchorOrigin: { horizontal: "right", vertical: "bottom" },
27147
- children: [
27148
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Typography, { variant: "overline", sx: { px: 2, color: theme.palette.text.secondary }, children: "Engine \xB7 base model" }),
27149
- engines.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.MenuItem, { disabled: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Typography, { variant: "body2", children: "No engines available" }) }),
27150
- engines.map((engine) => {
27151
- const badges = [
27152
- engine.vision && "vision",
27153
- engine.tools && "tools",
27154
- engine.thinking && "thinking",
27155
- engine.cloud && "cloud"
27156
- ].filter(Boolean);
27157
- const isGated = engine.cloud && !frontierEligible;
27158
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
27159
- import_material28.MenuItem,
27160
- {
27161
- selected: engine.id === resolvedEngineId,
27162
- disabled: !engine.available || isGated,
27163
- onClick: () => {
27164
- if (isGated) return;
27165
- useEngineStore.getState().setSelectedEngine(engine.id);
27166
- setEngineAnchorEl(null);
27167
- },
27168
- sx: {
27169
- display: "flex",
27170
- flexDirection: "column",
27171
- alignItems: "flex-start",
27172
- gap: 0.5,
27173
- py: 1,
27174
- px: 2,
27175
- maxWidth: 360,
27176
- whiteSpace: "normal",
27177
- // Keep gated rows readable (MUI disabled dims to ~38%) so the
27178
- // upsell stays legible.
27179
- ...isGated ? { opacity: 1, "&.Mui-disabled": { opacity: 1 } } : {}
27180
- },
27181
- children: [
27182
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_material28.Box, { sx: { display: "flex", alignItems: "center", gap: 1, width: "100%" }, children: [
27183
- isGated && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27184
- LockIcon,
27185
- {
27186
- fontSize: "small",
27187
- sx: { fontSize: "0.95rem", color: theme.palette.text.secondary }
27188
- }
27189
- ),
27190
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27191
- import_material28.Typography,
27192
- {
27193
- variant: "body2",
27194
- sx: {
27195
- fontWeight: 600,
27196
- flex: 1,
27197
- color: isGated ? theme.palette.text.secondary : void 0
27198
- },
27199
- children: cleanEngineName(engine.displayName)
27200
- }
27201
- ),
27202
- engine.id === resolvedEngineId && !isGated && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Box, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: theme.palette.primary.main } })
27203
- ] }),
27204
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.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" }),
27205
- isGated && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27206
- import_material28.Button,
27207
- {
27208
- size: "small",
27209
- variant: "text",
27210
- startIcon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(LockIcon, { sx: { fontSize: "0.85rem" } }),
27211
- onClick: (e) => {
27212
- e.stopPropagation();
27213
- setEngineAnchorEl(null);
27214
- safeNavigate(plansPath);
27215
- },
27216
- sx: {
27217
- mt: 0.25,
27218
- px: 0.75,
27219
- py: 0.1,
27220
- minWidth: 0,
27221
- textTransform: "none",
27222
- fontSize: "0.7rem",
27223
- fontWeight: 600,
27224
- color: theme.palette.primary.main,
27225
- // Re-enable pointer events the disabled MenuItem strips.
27226
- pointerEvents: "auto"
27227
- },
27228
- children: "Upgrade to unlock"
27229
- }
27230
- ),
27231
- badges.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.25 }, children: badges.map((b) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27232
- import_material28.Box,
27233
- {
27234
- sx: {
27235
- fontSize: "0.65rem",
27236
- px: 0.75,
27237
- py: 0.1,
27238
- borderRadius: 1,
27239
- bgcolor: theme.palette.primary.main + "22",
27240
- color: theme.palette.primary.main
27241
- },
27242
- children: b
27243
- },
27244
- b
27245
- )) })
27246
- ]
27247
- },
27248
- engine.id
27249
- );
27250
- })
27251
- ]
27252
- }
27253
- ),
27254
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27255
- import_material28.Menu,
27256
- {
27257
- anchorEl: modelAnchorEl,
27258
- open: Boolean(modelAnchorEl),
27259
- onClose: () => setModelAnchorEl(null),
27260
- transformOrigin: { horizontal: "right", vertical: "top" },
27261
- anchorOrigin: { horizontal: "right", vertical: "bottom" },
27262
- PaperProps: {
27263
- sx: {
27264
- bgcolor: menuBackground,
27265
- color: menuText,
27266
- fontSize: "0.875rem",
27267
- zIndex: 2e4,
27268
- borderRadius: 2,
27269
- border: `1px solid ${border}`,
27270
- boxShadow: theme.palette.mode === "dark" ? "0 8px 32px rgba(0,0,0,0.5)" : "0 8px 32px rgba(0,0,0,0.2)",
27271
- minWidth: 200,
27272
- "& .MuiMenuItem-root": {
27273
- transition: "all 0.15s ease-in-out",
27274
- borderRadius: "6px",
27275
- margin: "2px 4px",
27276
- "&:hover": {
27277
- bgcolor: theme.palette.mode === "dark" ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.04)",
27278
- transform: "translateX(2px)"
27279
- },
27280
- "&.Mui-selected": {
27281
- bgcolor: theme.palette.primary.main + "20",
27282
- color: theme.palette.primary.main,
27283
- "&:hover": {
27284
- bgcolor: theme.palette.primary.main + "30"
27285
- }
27286
- }
27287
- }
27288
- }
27289
- },
27290
- children: availableModels.map((model) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
27291
- import_material28.MenuItem,
27292
- {
27293
- selected: model.name === selectedModel,
27294
- onClick: () => {
27295
- if (!currentConversation?.history.length || currentConversation?.history.length === 0) {
27296
- handleModelChange(model.name);
27297
- useModelStore.getState().setSelectedModel(model.name);
27298
- (async () => {
27299
- try {
27300
- const storeConfigs6 = [{ name: "config", keyPath: "id" }];
27301
- const current = await indexedDBService_default.get(
27302
- "banditConfig",
27303
- 1,
27304
- "config",
27305
- "main",
27306
- storeConfigs6
27307
- );
27308
- const updated = {
27309
- ...current ?? {},
27310
- id: "main",
27311
- model: {
27312
- ...current?.model || {},
27313
- name: model.name,
27314
- selectedModel: model.name
27315
- }
27316
- };
27317
- await indexedDBService_default.put("banditConfig", 1, "config", updated, storeConfigs6);
27318
- } catch (err) {
27319
- debugLogger.error("Failed to persist selectedModel to IndexedDB", { error: err });
27320
- }
27321
- })();
27322
- setModelAnchorEl(null);
27323
- return;
27324
- }
27325
- setPendingModel(model.name);
27326
- setConfirmModelChangeOpen(true);
27327
- setModelAnchorEl(null);
27328
- },
27329
- sx: {
27330
- display: "flex",
27331
- alignItems: "center",
27332
- gap: 1.5,
27333
- minHeight: 48,
27334
- px: 2
27335
- },
27336
- children: [
27337
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27338
- import_material27.Avatar,
27339
- {
27340
- src: model.avatarBase64 || modelAvatars2[model.name] || banditHead2,
27341
- alt: model.name,
27342
- sx: {
27343
- width: 28,
27344
- height: 28,
27345
- filter: "brightness(1.7)",
27346
- transition: "all 0.2s ease-in-out"
27347
- }
27348
- }
27349
- ),
27350
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_material28.Box, { sx: { flex: 1 }, children: [
27351
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Typography, { variant: "body2", sx: { fontWeight: 500 }, children: model.name.replace("Bandit-", "") }),
27352
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Typography, { variant: "caption", sx: { color: theme.palette.text.secondary, display: "block" }, children: model.name === selectedModel ? "Currently active" : "Switch to this AI" })
27353
- ] }),
27354
- model.name === selectedModel && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27355
- import_material28.Box,
27356
- {
27357
- sx: {
27358
- width: 8,
27359
- height: 8,
27360
- borderRadius: "50%",
27361
- bgcolor: theme.palette.primary.main
27362
- }
27363
- }
27364
- )
27365
- ]
27366
- },
27367
- model.name
27368
- ))
27369
- }
27370
- ),
27371
- isTTSAvailable && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
27372
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Tooltip, { title: `Voice: ${selectedVoice ? toTitleCase(selectedVoice.split("-")[1]) : "Default"}`, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27072
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27073
+ import_material28.Tooltip,
27074
+ {
27075
+ title: isIncognitoActive ? "End temporary chat" : "Temporary chat \u2014 not saved anywhere",
27076
+ arrow: true,
27077
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27373
27078
  import_material28.IconButton,
27374
27079
  {
27375
- onClick: (e) => setVoiceAnchorEl(e.currentTarget),
27080
+ onClick: toggleIncognito,
27081
+ "aria-label": isIncognitoActive ? "End temporary chat" : "Start temporary chat",
27082
+ "aria-pressed": isIncognitoActive,
27376
27083
  sx: {
27377
27084
  ...pillButtonStyles,
27378
- bgcolor: theme.palette.info.main + "20",
27379
- color: theme.palette.info.main,
27380
- "&:hover": {
27381
- ...pillButtonStyles["&:hover"],
27382
- bgcolor: theme.palette.info.main + "30"
27383
- }
27384
- },
27385
- "aria-label": `Change voice. Currently using ${selectedVoice ? toTitleCase(selectedVoice.split("-")[1]) : "default"}`,
27386
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RecordVoiceOverIcon, { fontSize: "small" })
27387
- }
27388
- ) }),
27389
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27390
- import_material28.Menu,
27391
- {
27392
- anchorEl: voiceAnchorEl,
27393
- open: Boolean(voiceAnchorEl),
27394
- onClose: () => setVoiceAnchorEl(null),
27395
- transformOrigin: { horizontal: "right", vertical: "top" },
27396
- anchorOrigin: { horizontal: "right", vertical: "bottom" },
27397
- PaperProps: {
27398
- sx: {
27399
- bgcolor: menuBackground,
27400
- color: menuText,
27401
- fontSize: "0.875rem",
27402
- zIndex: 2e4,
27403
- borderRadius: 2,
27404
- border: `1px solid ${border}`,
27405
- boxShadow: theme.palette.mode === "dark" ? "0 8px 32px rgba(0,0,0,0.5)" : "0 8px 32px rgba(0,0,0,0.2)",
27406
- minWidth: 180,
27407
- "& .MuiMenuItem-root": {
27408
- transition: "all 0.15s ease-in-out",
27409
- borderRadius: "6px",
27410
- margin: "2px 4px",
27411
- minHeight: 40,
27412
- "&:hover": {
27413
- bgcolor: theme.palette.mode === "dark" ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.04)",
27414
- transform: "translateX(2px)"
27415
- },
27416
- "&.Mui-selected": {
27417
- bgcolor: theme.palette.info.main + "20",
27418
- color: theme.palette.info.main,
27419
- "&:hover": {
27420
- bgcolor: theme.palette.info.main + "30"
27421
- }
27422
- }
27423
- }
27085
+ ...isIncognitoActive && {
27086
+ bgcolor: theme.palette.text.primary,
27087
+ color: theme.palette.getContrastText(theme.palette.text.primary),
27088
+ "&:hover": { bgcolor: theme.palette.text.primary }
27424
27089
  }
27425
27090
  },
27426
- children: availableVoices.length > 0 ? availableVoices.map((voice) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27427
- import_material28.MenuItem,
27428
- {
27429
- selected: voice === selectedVoice,
27430
- onClick: () => {
27431
- handleVoiceChange(voice);
27432
- setVoiceAnchorEl(null);
27433
- },
27434
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_material28.Box, { sx: { display: "flex", alignItems: "center", gap: 1, width: "100%" }, children: [
27435
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RecordVoiceOverIcon, { fontSize: "small", sx: { color: theme.palette.text.secondary } }),
27436
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_material28.Box, { sx: { flex: 1 }, children: [
27437
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Typography, { variant: "body2", children: toTitleCase(voice.split("-")[1]) }),
27438
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Typography, { variant: "caption", sx: { color: theme.palette.text.secondary }, children: voice === selectedVoice ? "Currently active" : "Switch to this voice" })
27439
- ] }),
27440
- voice === selectedVoice && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
27441
- import_material28.Box,
27442
- {
27443
- sx: {
27444
- width: 8,
27445
- height: 8,
27446
- borderRadius: "50%",
27447
- bgcolor: theme.palette.info.main
27448
- }
27449
- }
27450
- )
27451
- ] })
27452
- },
27453
- voice
27454
- )) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.MenuItem, { disabled: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material28.Typography, { variant: "body2", color: "text.secondary", children: "No voices available" }) })
27091
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GhostIcon, {})
27455
27092
  }
27456
27093
  )
27457
- ] })
27458
- ]
27094
+ }
27095
+ )
27459
27096
  }
27460
27097
  )
27461
27098
  ]
@@ -31053,7 +30690,8 @@ var init_chat2 = __esm({
31053
30690
  const [inputHeight, setInputHeight] = (0, import_react41.useState)(80);
31054
30691
  const [isSubmitting, setIsSubmitting] = (0, import_react41.useState)(false);
31055
30692
  const [pendingMessage, setPendingMessage] = (0, import_react41.useState)(null);
31056
- const { conversations, currentId, _hasHydrated, hydrate } = useConversationStore();
30693
+ const { conversations, currentId, _hasHydrated, hydrate, toggleIncognito } = useConversationStore();
30694
+ const isTemporaryChat = Boolean(conversations.find((c) => c.id === currentId)?.incognito);
31057
30695
  const [isMobile, setIsMobile] = (0, import_react41.useState)(false);
31058
30696
  const [drawerOpen, setDrawerOpen] = (0, import_react41.useState)(false);
31059
30697
  const [mobileConversationsOpen, setMobileConversationsOpen] = (0, import_react41.useState)(false);
@@ -31906,12 +31544,11 @@ var init_chat2 = __esm({
31906
31544
  width: !isMobile && canvasOpen ? `calc(100vw - ${drawerOpen ? "340px" : "0px"} - ${CANVAS_WIDTH})` : drawerOpen && !isMobile ? "calc(100vw - 340px)" : "100vw",
31907
31545
  zIndex: 0,
31908
31546
  // Claude-style push (mobile only; desktop stays flush): the chat
31909
- // shell slides right and shrinks slightly into a rounded card. The
31910
- // scale insets the card from every screen edge so all four rounded
31911
- // corners stay visible — a full-height 100dvh card had its bottom
31912
- // corners clipped at the device edge in the PWA. Width kept in sync
31547
+ // shell slides right as a full-height rounded card — exactly like
31548
+ // Claude iOS, where the card runs edge-to-edge vertically and the
31549
+ // hairline border keeps the corners legible. Width kept in sync
31913
31550
  // with the drawer in enhanced-mobile-conversations-modal.tsx.
31914
- transform: isMobile && mobileConversationsOpen ? "translateX(min(calc(100vw - 56px), 360px)) scale(0.96)" : "none",
31551
+ transform: isMobile && mobileConversationsOpen ? "translateX(min(calc(100vw - 56px), 360px))" : "none",
31915
31552
  borderRadius: isMobile && mobileConversationsOpen ? "24px" : 0,
31916
31553
  // Hairline border so the rounded card reads even when the backdrop
31917
31554
  // is the same near-black as the shell.
@@ -31934,30 +31571,82 @@ var init_chat2 = __esm({
31934
31571
  onMobileConversationsOpenChange: setMobileConversationsOpen
31935
31572
  }
31936
31573
  ),
31937
- conversations.find((c) => c.id === currentId)?.incognito && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
31938
- import_material33.Box,
31939
- {
31940
- sx: (theme) => ({
31941
- position: "absolute",
31942
- top: `calc(${theme.spacing(8)} + env(safe-area-inset-top, 0px))`,
31943
- left: "50%",
31944
- transform: "translateX(-50%)",
31945
- zIndex: 1200,
31946
- px: 1.5,
31947
- py: 0.5,
31948
- borderRadius: 999,
31949
- fontSize: "0.75rem",
31950
- fontWeight: 600,
31951
- color: theme.palette.text.secondary,
31952
- bgcolor: theme.palette.mode === "dark" ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.06)",
31953
- border: `1px solid ${theme.palette.divider}`,
31954
- backdropFilter: "blur(8px)",
31955
- pointerEvents: "none",
31956
- whiteSpace: "nowrap"
31957
- }),
31958
- children: "Incognito \u2014 this chat won't be saved"
31959
- }
31960
- ),
31574
+ isTemporaryChat && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
31575
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
31576
+ import_material33.Box,
31577
+ {
31578
+ sx: (theme) => ({
31579
+ position: "absolute",
31580
+ inset: 0,
31581
+ border: `2px solid ${theme.palette.mode === "dark" ? "rgba(255,255,255,0.45)" : "rgba(0,0,0,0.4)"}`,
31582
+ borderRadius: "inherit",
31583
+ zIndex: 1400,
31584
+ pointerEvents: "none"
31585
+ })
31586
+ }
31587
+ ),
31588
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
31589
+ import_material33.Box,
31590
+ {
31591
+ sx: (theme) => ({
31592
+ position: "absolute",
31593
+ top: `calc(${theme.spacing(8)} + env(safe-area-inset-top, 0px))`,
31594
+ left: "50%",
31595
+ transform: "translateX(-50%)",
31596
+ zIndex: 1401,
31597
+ display: "flex",
31598
+ alignItems: "center",
31599
+ gap: 0.75,
31600
+ pl: 1.5,
31601
+ pr: 0.75,
31602
+ py: 0.4,
31603
+ borderRadius: 999,
31604
+ fontSize: "0.78rem",
31605
+ fontWeight: 600,
31606
+ color: theme.palette.text.primary,
31607
+ bgcolor: theme.palette.mode === "dark" ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.08)",
31608
+ border: `1px solid ${theme.palette.divider}`,
31609
+ backdropFilter: "blur(8px)",
31610
+ whiteSpace: "nowrap"
31611
+ }),
31612
+ children: [
31613
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_material33.Box, { component: "span", "aria-hidden": true, sx: { fontSize: "0.9rem", lineHeight: 1 }, children: "\u{1F47B}" }),
31614
+ "Temporary chat",
31615
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
31616
+ import_material33.Box,
31617
+ {
31618
+ component: "button",
31619
+ type: "button",
31620
+ onClick: toggleIncognito,
31621
+ "aria-label": "End temporary chat",
31622
+ sx: (theme) => ({
31623
+ display: "inline-flex",
31624
+ alignItems: "center",
31625
+ justifyContent: "center",
31626
+ width: 20,
31627
+ height: 20,
31628
+ ml: 0.25,
31629
+ p: 0,
31630
+ border: "none",
31631
+ borderRadius: "50%",
31632
+ cursor: "pointer",
31633
+ color: theme.palette.text.secondary,
31634
+ bgcolor: "transparent",
31635
+ font: "inherit",
31636
+ fontSize: "0.85rem",
31637
+ lineHeight: 1,
31638
+ "&:hover": {
31639
+ bgcolor: theme.palette.mode === "dark" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.1)",
31640
+ color: theme.palette.text.primary
31641
+ }
31642
+ }),
31643
+ children: "\u2715"
31644
+ }
31645
+ )
31646
+ ]
31647
+ }
31648
+ )
31649
+ ] }),
31961
31650
  /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
31962
31651
  import_material33.Box,
31963
31652
  {
@@ -46002,6 +45691,23 @@ var Management = () => {
46002
45691
  boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
46003
45692
  },
46004
45693
  children: [
45694
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
45695
+ import_material54.Button,
45696
+ {
45697
+ onClick: () => navigate("/chat"),
45698
+ onMouseEnter: preloadChatPage,
45699
+ "aria-label": "Back to chat",
45700
+ sx: {
45701
+ minWidth: 48,
45702
+ minHeight: 48,
45703
+ p: 1.5,
45704
+ borderRadius: 2,
45705
+ color: "primary.main",
45706
+ bgcolor: (theme2) => theme2.palette.mode === "dark" ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.04)"
45707
+ },
45708
+ children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ChevronLeftIcon, { sx: { fontSize: 22 } })
45709
+ }
45710
+ ),
46005
45711
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
46006
45712
  import_material54.Button,
46007
45713
  {