@burtson-labs/bandit-engine 2.0.136 → 2.0.138

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  chat_default
3
- } from "./chunk-72TR4XAJ.mjs";
3
+ } from "./chunk-LDGJKNI4.mjs";
4
4
  import {
5
5
  chat_provider_default
6
6
  } from "./chunk-PU74BI6Y.mjs";
@@ -10,7 +10,7 @@ import {
10
10
  useGatewayHealth,
11
11
  useGatewayMemory,
12
12
  useGatewayModels
13
- } from "./chunk-LG4S6XLH.mjs";
13
+ } from "./chunk-WBWVXJF3.mjs";
14
14
  import "./chunk-BVYSGIWG.mjs";
15
15
  import "./chunk-F7JXIW6T.mjs";
16
16
  import "./chunk-K2JGFKEB.mjs";
@@ -21438,6 +21438,8 @@ ${sanitize(
21438
21438
  const hasFeedbackAction = isFeedbackEnabled && isMobile;
21439
21439
  const hasSttAction = isSTTAvailable && !isVoiceModeEnabled;
21440
21440
  const hasVoicePickerAction = isMobile && isTTSAvailable && availableVoices.length > 0;
21441
+ const hasComposeContent = !!inputValue.trim() || pastedImages.length > 0 || fileInputs.length > 0;
21442
+ const showSendButton = streamingActive || hasComposeContent || !isVoiceModeEligible;
21441
21443
  const hasSecondaryActions = isMobile && (hasAttachmentAction || hasMemoryAction || hasFeedbackAction || hasSttAction || hasVoicePickerAction);
21442
21444
  (0, import_react46.useEffect)(() => {
21443
21445
  if (!isMobile || !hasSecondaryActions) {
@@ -21502,6 +21504,33 @@ ${sanitize(
21502
21504
  if (!hasSttAction) return null;
21503
21505
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material39.Box, { sx: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(transcriber_default, { onTranscriptionCompleted: handleTranscriptionCompleted }) }, key ?? "stt");
21504
21506
  };
21507
+ const renderVoiceModeButton = (key) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21508
+ import_material39.Tooltip,
21509
+ {
21510
+ title: !isVoiceModeEnabled ? "Enable voice mode" : voiceStatus === "error" ? voiceError || "Voice mode error" : voiceStatus === "processing" ? "Transcribing your speech" : voiceStatus === "recording" ? "Recording - click to stop" : voiceStatus === "initializing" ? "Preparing microphone" : "Listening - click to turn off",
21511
+ children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21512
+ import_material39.IconButton,
21513
+ {
21514
+ onClick: toggleVoiceMode,
21515
+ sx: {
21516
+ width: sendIconSize,
21517
+ height: sendIconSize,
21518
+ borderRadius: "50%",
21519
+ bgcolor: isVoiceModeEnabled ? (0, import_styles24.alpha)(theme.palette.error.main, theme.palette.mode === "dark" ? 0.45 : 0.3) : fileText,
21520
+ color: isVoiceModeEnabled ? theme.palette.common.white : fileIconBg,
21521
+ boxShadow: isVoiceModeEnabled && voiceStatus === "recording" ? `0 0 0 2px ${(0, import_styles24.alpha)(theme.palette.error.main, 0.25)}` : "none",
21522
+ transform: isVoiceModeEnabled && voiceStatus === "recording" ? "scale(1.05)" : "none",
21523
+ transition: "transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease",
21524
+ "&:hover": {
21525
+ bgcolor: isVoiceModeEnabled ? (0, import_styles24.alpha)(theme.palette.error.main, theme.palette.mode === "dark" ? 0.55 : 0.38) : hoverBadgeBackground
21526
+ }
21527
+ },
21528
+ children: !isVoiceModeEnabled ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GraphicEqIcon, { fontSize: "small" }) : voiceStatus === "processing" || voiceStatus === "initializing" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material39.CircularProgress, { size: 18, sx: { color: theme.palette.common.white } }) : voiceStatus === "error" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(HearingDisabledIcon, { fontSize: "small", sx: { color: theme.palette.error.light } }) : voiceStatus === "recording" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GraphicEqIcon, { fontSize: "small", sx: { color: theme.palette.error.light } }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GraphicEqIcon, { fontSize: "small", sx: { color: theme.palette.common.white } })
21529
+ }
21530
+ )
21531
+ },
21532
+ key ?? "voice-mode"
21533
+ );
21505
21534
  const renderVoicePickerButton = (key) => {
21506
21535
  if (!hasVoicePickerAction) return null;
21507
21536
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material39.Tooltip, { title: `Voice: ${selectedVoice ? toTitleCase(selectedVoice.split("-")[1] ?? selectedVoice) : "Default"}`, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
@@ -21749,51 +21778,22 @@ ${sanitize(
21749
21778
  minHeight: primaryIconSize
21750
21779
  },
21751
21780
  children: [
21752
- isVoiceModeEligible && /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
21753
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21754
- import_material39.Tooltip,
21755
- {
21756
- title: !isVoiceModeEnabled ? "Enable voice mode" : voiceStatus === "error" ? voiceError || "Voice mode error" : voiceStatus === "processing" ? "Transcribing your speech" : voiceStatus === "recording" ? "Recording - click to stop" : voiceStatus === "initializing" ? "Preparing microphone" : "Listening - click to turn off",
21757
- children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21758
- import_material39.IconButton,
21759
- {
21760
- onClick: toggleVoiceMode,
21761
- sx: {
21762
- width: primaryIconSize,
21763
- height: primaryIconSize,
21764
- borderRadius: "50%",
21765
- bgcolor: isVoiceModeEnabled ? (0, import_styles24.alpha)(theme.palette.error.main, theme.palette.mode === "dark" ? 0.45 : 0.3) : badgeBackground,
21766
- color: isVoiceModeEnabled ? theme.palette.common.white : fileText,
21767
- boxShadow: isVoiceModeEnabled && voiceStatus === "recording" ? `0 0 0 2px ${(0, import_styles24.alpha)(theme.palette.error.main, 0.25)}` : "none",
21768
- transform: isVoiceModeEnabled && voiceStatus === "recording" ? "scale(1.05)" : "none",
21769
- transition: "transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease",
21770
- "&:hover": {
21771
- bgcolor: isVoiceModeEnabled ? (0, import_styles24.alpha)(theme.palette.error.main, theme.palette.mode === "dark" ? 0.55 : 0.38) : hoverBadgeBackground
21772
- }
21773
- },
21774
- children: !isVoiceModeEnabled ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GraphicEqIcon, { fontSize: "small", sx: { color: theme.palette.mode === "dark" ? fileText : theme.palette.text.secondary } }) : voiceStatus === "processing" || voiceStatus === "initializing" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material39.CircularProgress, { size: 18, sx: { color: fileText } }) : voiceStatus === "error" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(HearingDisabledIcon, { fontSize: "small", sx: { color: theme.palette.error.light } }) : voiceStatus === "recording" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GraphicEqIcon, { fontSize: "small", sx: { color: theme.palette.error.light } }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GraphicEqIcon, { fontSize: "small", sx: { color: theme.palette.common.white } })
21775
- }
21776
- )
21777
- }
21778
- ),
21779
- !isMobile && isVoiceModeEnabled && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21780
- import_material39.Typography,
21781
- {
21782
- variant: "caption",
21783
- sx: {
21784
- color: voiceStatus === "error" ? theme.palette.error.main : voiceStatus === "processing" ? theme.palette.warning.main : fileText,
21785
- maxWidth: 240,
21786
- overflow: "hidden",
21787
- textOverflow: "ellipsis",
21788
- whiteSpace: "nowrap"
21789
- },
21790
- children: voiceStatus === "error" ? voiceError || "Voice mode unavailable" : voiceStatus === "initializing" ? "Starting voice mode..." : voiceStatus === "recording" ? "Recording..." : voiceStatus === "processing" ? "Transcribing..." : voiceLastTranscript ? `Heard: "${voiceLastTranscript.length > 60 ? `${voiceLastTranscript.slice(0, 57)}...` : voiceLastTranscript}"` : "Listening..."
21791
- }
21792
- )
21793
- ] }),
21794
21781
  !isMobile && renderAttachmentButton("attach-inline"),
21795
21782
  !isMobile && renderMemoryButton("memory-inline"),
21796
- !isMobile && renderSttButton("stt-inline"),
21783
+ !isMobile && isVoiceModeEligible && isVoiceModeEnabled && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21784
+ import_material39.Typography,
21785
+ {
21786
+ variant: "caption",
21787
+ sx: {
21788
+ color: voiceStatus === "error" ? theme.palette.error.main : voiceStatus === "processing" ? theme.palette.warning.main : fileText,
21789
+ maxWidth: 240,
21790
+ overflow: "hidden",
21791
+ textOverflow: "ellipsis",
21792
+ whiteSpace: "nowrap"
21793
+ },
21794
+ children: voiceStatus === "error" ? voiceError || "Voice mode unavailable" : voiceStatus === "initializing" ? "Starting voice mode..." : voiceStatus === "recording" ? "Recording..." : voiceStatus === "processing" ? "Transcribing..." : voiceLastTranscript ? `Heard: "${voiceLastTranscript.length > 60 ? `${voiceLastTranscript.slice(0, 57)}...` : voiceLastTranscript}"` : "Listening..."
21795
+ }
21796
+ ),
21797
21797
  isMobile && hasSecondaryActions && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21798
21798
  import_material39.IconButton,
21799
21799
  {
@@ -21849,7 +21849,9 @@ ${sanitize(
21849
21849
  ),
21850
21850
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_material39.Box, { sx: { display: "flex", alignItems: "center", gap: isMobile ? 0.6 : 1 }, children: [
21851
21851
  !isMobile && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(engine_picker_default, {}),
21852
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material39.Tooltip, { title: isStreaming ? "Stop response" : "Send message", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21852
+ !isMobile && renderSttButton("stt-right"),
21853
+ isVoiceModeEligible && !showSendButton && renderVoiceModeButton("voice-right"),
21854
+ showSendButton && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material39.Tooltip, { title: isStreaming ? "Stop response" : "Send message", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
21853
21855
  import_material39.IconButton,
21854
21856
  {
21855
21857
  onClick: isStreaming ? onStop || (() => {
@@ -28131,8 +28133,10 @@ var init_enhanced_mobile_conversations_modal = __esm({
28131
28133
  // Keep in sync with MOBILE_CONVERSATIONS_DRAWER_WIDTH in chat.tsx
28132
28134
  // (the chat shell translates by the same amount).
28133
28135
  width: "min(calc(100vw - 56px), 360px)",
28134
- // top/bottom stretch instead of 100dvh — iOS standalone
28135
- // under-reports dvh, leaving a dead band above the home bar.
28136
+ // Explicit 100lvh overflows iOS standalone's under-reported
28137
+ // viewport downward — bottom-anchoring alone stops short.
28138
+ height: "100dvh",
28139
+ "@supports (height: 100lvh)": { height: "100lvh" },
28136
28140
  bgcolor: theme.palette.background.paper,
28137
28141
  display: "flex",
28138
28142
  flexDirection: "column",
@@ -31228,18 +31232,20 @@ var init_chat2 = __esm({
31228
31232
  sx: (theme) => ({
31229
31233
  display: "flex",
31230
31234
  flexDirection: "column",
31231
- // Stretch via top/bottom instead of 100dvh — the iOS standalone
31232
- // webview under-reports dvh and left a dead band above the home
31233
- // indicator; inset-stretched fixed elements get the real viewport.
31235
+ // iOS standalone under-reports both dvh AND the fixed-position
31236
+ // containing block, stranding a dead band above the home bar.
31237
+ // An explicit 100lvh height can overflow that short viewport
31238
+ // downward, which bottom-anchoring cannot.
31234
31239
  overflow: "hidden",
31235
31240
  bgcolor: theme.palette.chat.shell,
31236
31241
  color: theme.palette.text.primary,
31237
31242
  position: "fixed",
31238
31243
  top: 0,
31239
31244
  bottom: 0,
31245
+ height: "100dvh",
31246
+ "@supports (height: 100lvh)": { height: "100lvh" },
31240
31247
  left: drawerOpen && !isMobile ? "340px" : 0,
31241
31248
  width: !isMobile && canvasOpen ? `calc(100vw - ${drawerOpen ? "340px" : "0px"} - ${CANVAS_WIDTH})` : drawerOpen && !isMobile ? "calc(100vw - 340px)" : "100vw",
31242
- zIndex: 0,
31243
31249
  // Claude-style push (mobile only; desktop stays flush): the chat
31244
31250
  // shell slides right as a full-height rounded card — exactly like
31245
31251
  // Claude iOS, where the card runs edge-to-edge vertically and the
@@ -31250,15 +31256,20 @@ var init_chat2 = __esm({
31250
31256
  // status area, the bottom stays flush with the screen edge, and the
31251
31257
  // motion reads as the native sheet-behind effect instead of the
31252
31258
  // content lurching downward.
31253
- transform: isMobile && mobileConversationsOpen ? "translateX(min(calc(100vw - 56px), 360px)) scale(0.93)" : "none",
31259
+ // Slides 12px OVER the drawer's edge (not edge-to-edge) and rides
31260
+ // above it in z-order, so the card's shadow falls on the drawer —
31261
+ // the overlapped-sheet look.
31262
+ transform: isMobile && mobileConversationsOpen ? "translateX(calc(min(calc(100vw - 56px), 360px) - 12px)) scale(0.93)" : "none",
31254
31263
  transformOrigin: "left bottom",
31255
31264
  borderRadius: isMobile && mobileConversationsOpen ? "36px" : 0,
31265
+ zIndex: isMobile && mobileConversationsOpen ? theme.zIndex.modal + 1 : 0,
31256
31266
  // Hairline border so the rounded card reads even when the backdrop
31257
31267
  // is the same near-black as the shell.
31258
31268
  border: isMobile && mobileConversationsOpen ? `1px solid ${theme.palette.mode === "dark" ? "rgba(255,255,255,0.14)" : "rgba(0,0,0,0.12)"}` : "none",
31259
31269
  boxShadow: isMobile && mobileConversationsOpen ? `0 12px 40px ${theme.palette.mode === "dark" ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.25)"}` : "none",
31260
31270
  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"
31261
31271
  }),
31272
+ onClick: isMobile && mobileConversationsOpen ? () => setMobileConversationsOpen(false) : void 0,
31262
31273
  children: [
31263
31274
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
31264
31275
  chat_app_bar_default,
@@ -31363,7 +31374,10 @@ var init_chat2 = __esm({
31363
31374
  px: isMobile ? 1 : 2,
31364
31375
  // Reduce padding on mobile for better width utilization
31365
31376
  pb: logoOnly ? 0 : `${inputHeight + 24}px`,
31366
- maxHeight: isMobile ? "calc(var(--vh, 1vh) * 100)" : "100%",
31377
+ // The flex column already bounds this; the old --vh cap (from
31378
+ // window.innerHeight) under-measured in iOS standalone and let
31379
+ // the composer's auto margins float it above the bottom.
31380
+ maxHeight: "100%",
31367
31381
  WebkitOverflowScrolling: "touch",
31368
31382
  overscrollBehavior: "contain",
31369
31383
  position: "relative",
@@ -31489,7 +31503,7 @@ var init_chat2 = __esm({
31489
31503
  display: "flex",
31490
31504
  flexDirection: "column",
31491
31505
  alignItems: "center",
31492
- margin: "auto",
31506
+ margin: "0 auto",
31493
31507
  width: "100vw",
31494
31508
  maxWidth: "768px"
31495
31509
  },