@djangocfg/ui-tools 2.1.381 → 2.1.382

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 (183) hide show
  1. package/README.md +132 -899
  2. package/dist/ChatRoot-6IZFM5HM.mjs +5 -0
  3. package/dist/{ChatRoot-EJC5Y2YM.cjs.map → ChatRoot-6IZFM5HM.mjs.map} +1 -1
  4. package/dist/ChatRoot-LW4XNIKP.cjs +14 -0
  5. package/dist/{ChatRoot-QOSKJPM6.mjs.map → ChatRoot-LW4XNIKP.cjs.map} +1 -1
  6. package/dist/DictationField-2ZLQWLYV.mjs +4 -0
  7. package/dist/DictationField-2ZLQWLYV.mjs.map +1 -0
  8. package/dist/DictationField-IPPJ54CU.cjs +13 -0
  9. package/dist/DictationField-IPPJ54CU.cjs.map +1 -0
  10. package/dist/{DocsLayout-2YKPXZYO.mjs → DocsLayout-2P3ONDWJ.mjs} +3 -3
  11. package/dist/{DocsLayout-2YKPXZYO.mjs.map → DocsLayout-2P3ONDWJ.mjs.map} +1 -1
  12. package/dist/{DocsLayout-Q4KS3QWW.cjs → DocsLayout-2YZNS5VK.cjs} +8 -8
  13. package/dist/{DocsLayout-Q4KS3QWW.cjs.map → DocsLayout-2YZNS5VK.cjs.map} +1 -1
  14. package/dist/chunk-4LXG3NBV.mjs +833 -0
  15. package/dist/chunk-4LXG3NBV.mjs.map +1 -0
  16. package/dist/{chunk-XACCHZH2.cjs → chunk-FIRK5CEH.cjs} +42 -4
  17. package/dist/chunk-FIRK5CEH.cjs.map +1 -0
  18. package/dist/{chunk-NWUT327A.mjs → chunk-HIK6BPL7.mjs} +38 -5
  19. package/dist/chunk-HIK6BPL7.mjs.map +1 -0
  20. package/dist/chunk-KMSBGNVC.cjs +835 -0
  21. package/dist/chunk-KMSBGNVC.cjs.map +1 -0
  22. package/dist/chunk-OZAU3QWD.cjs +2493 -0
  23. package/dist/chunk-OZAU3QWD.cjs.map +1 -0
  24. package/dist/chunk-UWVP6LCW.mjs +2447 -0
  25. package/dist/chunk-UWVP6LCW.mjs.map +1 -0
  26. package/dist/index.cjs +1532 -100
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +1148 -107
  29. package/dist/index.d.ts +1148 -107
  30. package/dist/index.mjs +1421 -51
  31. package/dist/index.mjs.map +1 -1
  32. package/package.json +16 -8
  33. package/src/audio-assets.d.ts +8 -0
  34. package/src/components/markdown/MarkdownMessage/CollapseToggle.tsx +3 -1
  35. package/src/components/markdown/MarkdownMessage/components.tsx +2 -5
  36. package/src/stories/index.ts +32 -2
  37. package/src/tools/Chat/README.md +347 -530
  38. package/src/tools/Chat/components/Attachments.tsx +6 -1
  39. package/src/tools/Chat/components/ChatRoot.tsx +30 -2
  40. package/src/tools/Chat/components/Composer.tsx +20 -3
  41. package/src/tools/Chat/components/ErrorBanner.tsx +7 -3
  42. package/src/tools/Chat/components/MessageActions.tsx +3 -1
  43. package/src/tools/Chat/components/MessageBubble.tsx +6 -5
  44. package/src/tools/Chat/components/MessageList.tsx +87 -1
  45. package/src/tools/Chat/components/ToolCalls.tsx +21 -3
  46. package/src/tools/Chat/context/ChatProvider.tsx +21 -3
  47. package/src/tools/Chat/core/audio/audioBus.ts +10 -163
  48. package/src/tools/Chat/core/audio/defaults.ts +43 -0
  49. package/src/tools/Chat/core/audio/index.ts +1 -0
  50. package/src/tools/Chat/core/audio/preferences.ts +5 -59
  51. package/src/tools/Chat/core/audio/sounds/error.mp3 +0 -0
  52. package/src/tools/Chat/core/audio/sounds/mention.mp3 +0 -0
  53. package/src/tools/Chat/core/audio/sounds/notification.mp3 +0 -0
  54. package/src/tools/Chat/core/audio/sounds/received.mp3 +0 -0
  55. package/src/tools/Chat/core/audio/sounds/sent.mp3 +0 -0
  56. package/src/tools/Chat/core/audio/sounds/start.mp3 +0 -0
  57. package/src/tools/Chat/core/audio/types.ts +28 -0
  58. package/src/tools/Chat/core/reducer.ts +33 -0
  59. package/src/tools/Chat/core/transport/index.ts +13 -0
  60. package/src/tools/Chat/core/transport/mappers/index.ts +6 -0
  61. package/src/tools/Chat/core/transport/mappers/pydantic-ai.ts +142 -0
  62. package/src/tools/Chat/core/transport/pydantic-ai-transport.ts +208 -0
  63. package/src/tools/Chat/core/transport/sse.ts +18 -5
  64. package/src/tools/Chat/hooks/index.ts +25 -0
  65. package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +5 -3
  66. package/src/tools/Chat/hooks/useChat.ts +28 -0
  67. package/src/tools/Chat/hooks/useChatAudio.ts +59 -180
  68. package/src/tools/Chat/hooks/useChatDockPrefs.ts +74 -0
  69. package/src/tools/Chat/hooks/useChatReset.ts +70 -0
  70. package/src/tools/Chat/hooks/useChatUnread.ts +87 -0
  71. package/src/tools/Chat/hooks/useFocusOnEmptyClick.ts +111 -0
  72. package/src/tools/Chat/hooks/useVisitorFingerprint.ts +48 -0
  73. package/src/tools/Chat/index.ts +69 -1
  74. package/src/tools/Chat/launcher/ChatDock.tsx +263 -0
  75. package/src/tools/Chat/launcher/ChatFAB.tsx +349 -0
  76. package/src/tools/Chat/launcher/ChatGreeting.tsx +200 -0
  77. package/src/tools/Chat/launcher/ChatHeader.tsx +76 -0
  78. package/src/tools/Chat/launcher/ChatHeaderActionButton.tsx +87 -0
  79. package/src/tools/Chat/launcher/ChatHeaderAudioToggle.tsx +47 -0
  80. package/src/tools/Chat/launcher/ChatHeaderLanguageButton.tsx +179 -0
  81. package/src/tools/Chat/launcher/ChatHeaderModeToggle.tsx +57 -0
  82. package/src/tools/Chat/launcher/ChatHeaderResetButton.tsx +93 -0
  83. package/src/tools/Chat/launcher/ChatLauncher.tsx +321 -0
  84. package/src/tools/Chat/launcher/ChatUnreadPreview.tsx +197 -0
  85. package/src/tools/Chat/launcher/index.ts +46 -0
  86. package/src/tools/Chat/launcher/useChatPresence.ts +44 -0
  87. package/src/tools/Chat/stories/01-basic.story.tsx +64 -0
  88. package/src/tools/Chat/stories/02-bubbles.story.tsx +21 -0
  89. package/src/tools/Chat/stories/03-tool-calls.story.tsx +59 -0
  90. package/src/tools/Chat/stories/04-personas.story.tsx +78 -0
  91. package/src/tools/Chat/stories/05-launcher.story.tsx +321 -0
  92. package/src/tools/Chat/stories/06-header.story.tsx +147 -0
  93. package/src/tools/Chat/stories/07-audio-actions.story.tsx +112 -0
  94. package/src/tools/Chat/stories/shared/Frame.tsx +21 -0
  95. package/src/tools/Chat/stories/shared/index.ts +5 -0
  96. package/src/tools/Chat/stories/shared/messages.ts +39 -0
  97. package/src/tools/Chat/stories/shared/personas.ts +13 -0
  98. package/src/tools/Chat/stories/shared/seeds.ts +92 -0
  99. package/src/tools/Chat/stories/shared/transports.ts +36 -0
  100. package/src/tools/Chat/styles/bubbleTokens.ts +71 -0
  101. package/src/tools/Chat/styles/index.ts +16 -0
  102. package/src/tools/Chat/styles/useChatStyles.ts +101 -0
  103. package/src/tools/Chat/types/attachment.ts +25 -0
  104. package/src/tools/Chat/types/config.ts +48 -0
  105. package/src/tools/Chat/types/events.ts +35 -0
  106. package/src/tools/Chat/types/index.ts +34 -0
  107. package/src/tools/Chat/types/labels.ts +38 -0
  108. package/src/tools/Chat/types/message.ts +32 -0
  109. package/src/tools/Chat/types/persona.ts +31 -0
  110. package/src/tools/Chat/types/session.ts +43 -0
  111. package/src/tools/Chat/types/tool-call.ts +17 -0
  112. package/src/tools/Chat/types/transport.ts +28 -0
  113. package/src/tools/Chat/types.ts +5 -240
  114. package/src/tools/MarkdownEditor/MarkdownEditor.tsx +50 -14
  115. package/src/tools/MarkdownEditor/index.ts +1 -1
  116. package/src/tools/SpeechRecognition/README.md +336 -0
  117. package/src/tools/SpeechRecognition/__tests__/ids.test.ts +15 -0
  118. package/src/tools/SpeechRecognition/__tests__/language.test.ts +59 -0
  119. package/src/tools/SpeechRecognition/__tests__/reducer.test.ts +71 -0
  120. package/src/tools/SpeechRecognition/__tests__/transcript.test.ts +52 -0
  121. package/src/tools/SpeechRecognition/components/DevicePicker.tsx +49 -0
  122. package/src/tools/SpeechRecognition/components/DictationButton.tsx +93 -0
  123. package/src/tools/SpeechRecognition/components/EngineBadge.tsx +30 -0
  124. package/src/tools/SpeechRecognition/components/ErrorBanner.tsx +52 -0
  125. package/src/tools/SpeechRecognition/components/LanguagePicker.tsx +63 -0
  126. package/src/tools/SpeechRecognition/components/MicMeter.tsx +63 -0
  127. package/src/tools/SpeechRecognition/components/PushToTalkHint.tsx +51 -0
  128. package/src/tools/SpeechRecognition/components/TranscriptView.tsx +55 -0
  129. package/src/tools/SpeechRecognition/components/index.ts +16 -0
  130. package/src/tools/SpeechRecognition/context/SpeechRecognitionProvider.tsx +47 -0
  131. package/src/tools/SpeechRecognition/context/index.ts +6 -0
  132. package/src/tools/SpeechRecognition/core/audio/defaults.ts +24 -0
  133. package/src/tools/SpeechRecognition/core/engine/external.ts +222 -0
  134. package/src/tools/SpeechRecognition/core/engine/http.ts +147 -0
  135. package/src/tools/SpeechRecognition/core/engine/index.ts +52 -0
  136. package/src/tools/SpeechRecognition/core/engine/mediarecorder.ts +105 -0
  137. package/src/tools/SpeechRecognition/core/engine/websocket.ts +211 -0
  138. package/src/tools/SpeechRecognition/core/engine/webspeech.ts +188 -0
  139. package/src/tools/SpeechRecognition/core/ids.ts +11 -0
  140. package/src/tools/SpeechRecognition/core/index.ts +14 -0
  141. package/src/tools/SpeechRecognition/core/language.ts +78 -0
  142. package/src/tools/SpeechRecognition/core/languages-catalog.ts +229 -0
  143. package/src/tools/SpeechRecognition/core/logger.ts +3 -0
  144. package/src/tools/SpeechRecognition/core/reducer.ts +105 -0
  145. package/src/tools/SpeechRecognition/core/transcript.ts +36 -0
  146. package/src/tools/SpeechRecognition/hooks/index.ts +14 -0
  147. package/src/tools/SpeechRecognition/hooks/useDictation.ts +59 -0
  148. package/src/tools/SpeechRecognition/hooks/useEnginePrefs.ts +15 -0
  149. package/src/tools/SpeechRecognition/hooks/useMicDevices.ts +57 -0
  150. package/src/tools/SpeechRecognition/hooks/useMicLevel.ts +52 -0
  151. package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +85 -0
  152. package/src/tools/SpeechRecognition/hooks/useResolvedLanguage.ts +28 -0
  153. package/src/tools/SpeechRecognition/hooks/useSpeechLanguageInfo.ts +108 -0
  154. package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +188 -0
  155. package/src/tools/SpeechRecognition/hooks/useVoiceSupport.ts +78 -0
  156. package/src/tools/SpeechRecognition/index.ts +82 -0
  157. package/src/tools/SpeechRecognition/lazy.tsx +19 -0
  158. package/src/tools/SpeechRecognition/store/index.ts +2 -0
  159. package/src/tools/SpeechRecognition/store/prefsStore.ts +54 -0
  160. package/src/tools/SpeechRecognition/stories/01-basic.story.tsx +32 -0
  161. package/src/tools/SpeechRecognition/stories/02-dictation-field.story.tsx +32 -0
  162. package/src/tools/SpeechRecognition/stories/03-push-to-talk.story.tsx +27 -0
  163. package/src/tools/SpeechRecognition/stories/04-mic-meter.story.tsx +35 -0
  164. package/src/tools/SpeechRecognition/stories/05-custom-engine-http.story.tsx +40 -0
  165. package/src/tools/SpeechRecognition/stories/06-custom-engine-ws.story.tsx +48 -0
  166. package/src/tools/SpeechRecognition/stories/07-language-device.story.tsx +57 -0
  167. package/src/tools/SpeechRecognition/stories/08-errors-permissions.story.tsx +25 -0
  168. package/src/tools/SpeechRecognition/stories/09-chat-voice.story.tsx +90 -0
  169. package/src/tools/SpeechRecognition/stories/shared.tsx +123 -0
  170. package/src/tools/SpeechRecognition/types.ts +133 -0
  171. package/src/tools/SpeechRecognition/widgets/DictationField.tsx +105 -0
  172. package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +305 -0
  173. package/src/tools/SpeechRecognition/widgets/VoiceMessageRecorder.tsx +88 -0
  174. package/src/tools/SpeechRecognition/widgets/index.ts +6 -0
  175. package/dist/ChatRoot-EJC5Y2YM.cjs +0 -14
  176. package/dist/ChatRoot-QOSKJPM6.mjs +0 -5
  177. package/dist/chunk-NWUT327A.mjs.map +0 -1
  178. package/dist/chunk-QLMKCSR6.mjs +0 -2420
  179. package/dist/chunk-QLMKCSR6.mjs.map +0 -1
  180. package/dist/chunk-SI5RD2GD.cjs +0 -2460
  181. package/dist/chunk-SI5RD2GD.cjs.map +0 -1
  182. package/dist/chunk-XACCHZH2.cjs.map +0 -1
  183. package/src/tools/Chat/Chat.story.tsx +0 -1457
@@ -0,0 +1,835 @@
1
+ 'use strict';
2
+
3
+ var chunkOLISEQHS_cjs = require('./chunk-OLISEQHS.cjs');
4
+ var lib = require('@djangocfg/ui-core/lib');
5
+ var lucideReact = require('lucide-react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var react = require('react');
8
+ var consola = require('consola');
9
+ var zustand = require('zustand');
10
+ var middleware = require('zustand/middleware');
11
+ var i18n = require('@djangocfg/i18n');
12
+
13
+ var SIZE_CLS = {
14
+ sm: "h-8 w-8 [&_svg]:h-4 [&_svg]:w-4",
15
+ md: "h-10 w-10 [&_svg]:h-5 [&_svg]:w-5",
16
+ lg: "h-12 w-12 [&_svg]:h-6 [&_svg]:w-6"
17
+ };
18
+ function DictationButton({
19
+ status,
20
+ onClick,
21
+ isSupported = true,
22
+ size = "md",
23
+ className,
24
+ style,
25
+ ariaLabel,
26
+ idleIcon,
27
+ listeningIcon,
28
+ disabled
29
+ }) {
30
+ const listening = status === "listening" || status === "starting";
31
+ const stopping = status === "stopping";
32
+ const off = !isSupported;
33
+ return /* @__PURE__ */ jsxRuntime.jsxs(
34
+ "button",
35
+ {
36
+ type: "button",
37
+ onClick,
38
+ disabled: disabled || off,
39
+ "aria-pressed": listening,
40
+ "aria-label": ariaLabel ?? (listening ? "Stop dictation" : off ? "Dictation not supported" : "Start dictation"),
41
+ className: lib.cn(
42
+ "relative inline-flex items-center justify-center rounded-full transition-colors",
43
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
44
+ "disabled:cursor-not-allowed disabled:opacity-50",
45
+ SIZE_CLS[size],
46
+ listening ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : "bg-primary text-primary-foreground hover:bg-primary/90",
47
+ className
48
+ ),
49
+ style,
50
+ children: [
51
+ listening && /* @__PURE__ */ jsxRuntime.jsx(
52
+ "span",
53
+ {
54
+ "aria-hidden": true,
55
+ className: "absolute inset-0 rounded-full bg-destructive/40 animate-ping"
56
+ }
57
+ ),
58
+ stopping ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin" }) : off ? listeningIcon ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicOff, {}) : listening ? listeningIcon ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, {}) : idleIcon ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, {})
59
+ ]
60
+ }
61
+ );
62
+ }
63
+ chunkOLISEQHS_cjs.__name(DictationButton, "DictationButton");
64
+ var FRIENDLY = {
65
+ unsupported: "Speech recognition isn't available in this browser.",
66
+ "permission-denied": "Microphone access was denied. Allow it in your browser settings to dictate.",
67
+ "no-microphone": "No microphone found.",
68
+ network: "Network error talking to the speech service.",
69
+ aborted: "Recognition was interrupted.",
70
+ "no-speech": "No speech was detected. Try again.",
71
+ language: "The requested language isn't supported by the engine.",
72
+ engine: "The speech engine reported an error.",
73
+ unknown: "Something went wrong with the microphone."
74
+ };
75
+ function ErrorBanner({ error, className, onDismiss }) {
76
+ if (!error) return null;
77
+ return /* @__PURE__ */ jsxRuntime.jsxs(
78
+ "div",
79
+ {
80
+ role: "alert",
81
+ className: lib.cn(
82
+ "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
83
+ className
84
+ ),
85
+ children: [
86
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
87
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: FRIENDLY[error.code] ?? error.message }),
88
+ onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
89
+ "button",
90
+ {
91
+ type: "button",
92
+ onClick: onDismiss,
93
+ className: "text-destructive hover:underline",
94
+ children: "Dismiss"
95
+ }
96
+ )
97
+ ]
98
+ }
99
+ );
100
+ }
101
+ chunkOLISEQHS_cjs.__name(ErrorBanner, "ErrorBanner");
102
+ function MicMeter({
103
+ level,
104
+ bars = 12,
105
+ gap = 2,
106
+ barWidth = 3,
107
+ height = 24,
108
+ className
109
+ }) {
110
+ const clamped = Math.min(1, Math.max(0, level));
111
+ return /* @__PURE__ */ jsxRuntime.jsx(
112
+ "div",
113
+ {
114
+ role: "meter",
115
+ "aria-valuemin": 0,
116
+ "aria-valuemax": 1,
117
+ "aria-valuenow": clamped,
118
+ className: lib.cn("inline-flex items-center", className),
119
+ style: { gap, height },
120
+ children: Array.from({ length: bars }).map((_, i) => {
121
+ const ratio = (i + 1) / bars;
122
+ const active = clamped >= ratio - 0.5 / bars;
123
+ const heightRatio = 1 - Math.abs(i - (bars - 1) / 2) / ((bars - 1) / 2 || 1);
124
+ const barH = Math.max(2, height * (0.35 + heightRatio * 0.65));
125
+ return /* @__PURE__ */ jsxRuntime.jsx(
126
+ "span",
127
+ {
128
+ className: lib.cn(
129
+ "rounded-sm transition-colors",
130
+ active ? "bg-primary" : "bg-border"
131
+ ),
132
+ style: { width: barWidth, height: barH }
133
+ },
134
+ i
135
+ );
136
+ })
137
+ }
138
+ );
139
+ }
140
+ chunkOLISEQHS_cjs.__name(MicMeter, "MicMeter");
141
+ function PushToTalkHint({ chord, className }) {
142
+ const formatted = chord.split("+").map((p) => p.trim().toLowerCase()).map((p) => {
143
+ switch (p) {
144
+ case "mod":
145
+ case "meta":
146
+ return "\u2318";
147
+ case "alt":
148
+ return "\u2325";
149
+ case "shift":
150
+ return "\u21E7";
151
+ case "ctrl":
152
+ return "\u2303";
153
+ default:
154
+ return p.length === 1 ? p.toUpperCase() : p;
155
+ }
156
+ }).join("");
157
+ return /* @__PURE__ */ jsxRuntime.jsxs(
158
+ "span",
159
+ {
160
+ className: lib.cn(
161
+ "inline-flex items-center gap-1 text-[11px] text-muted-foreground",
162
+ className
163
+ ),
164
+ children: [
165
+ "Hold",
166
+ /* @__PURE__ */ jsxRuntime.jsx("kbd", { className: "rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px] text-foreground", children: formatted }),
167
+ "to talk"
168
+ ]
169
+ }
170
+ );
171
+ }
172
+ chunkOLISEQHS_cjs.__name(PushToTalkHint, "PushToTalkHint");
173
+
174
+ // src/tools/SpeechRecognition/core/transcript.ts
175
+ var EMPTY_TRANSCRIPT = {
176
+ interim: "",
177
+ final: "",
178
+ segments: []
179
+ };
180
+ function joinFinal(segments) {
181
+ let out = "";
182
+ for (const seg of segments) {
183
+ if (!seg.isFinal) continue;
184
+ const text = seg.text.trim();
185
+ if (!text) continue;
186
+ out = out ? `${out} ${text}` : text;
187
+ }
188
+ return out;
189
+ }
190
+ chunkOLISEQHS_cjs.__name(joinFinal, "joinFinal");
191
+ function buildTranscript(segments) {
192
+ const last = segments[segments.length - 1];
193
+ const interim = last && !last.isFinal ? last.text : "";
194
+ return {
195
+ interim,
196
+ final: joinFinal(segments),
197
+ segments
198
+ };
199
+ }
200
+ chunkOLISEQHS_cjs.__name(buildTranscript, "buildTranscript");
201
+ function normaliseFinal(text) {
202
+ return text.replace(/\s+/g, " ").replace(/\s+([,.!?])/g, "$1").trim();
203
+ }
204
+ chunkOLISEQHS_cjs.__name(normaliseFinal, "normaliseFinal");
205
+
206
+ // src/tools/SpeechRecognition/core/ids.ts
207
+ var counter = 0;
208
+ function newSegmentId() {
209
+ counter = (counter + 1) % Number.MAX_SAFE_INTEGER;
210
+ return `seg_${Date.now().toString(36)}_${counter.toString(36)}`;
211
+ }
212
+ chunkOLISEQHS_cjs.__name(newSegmentId, "newSegmentId");
213
+ var sttLogger = consola.consola.withTag("ui-tools:speech");
214
+
215
+ // src/tools/SpeechRecognition/core/reducer.ts
216
+ var INITIAL_STATE = {
217
+ status: "idle",
218
+ segments: [],
219
+ error: null,
220
+ startedAt: null
221
+ };
222
+ function nowSinceStart(state) {
223
+ return state.startedAt ? Date.now() - state.startedAt : 0;
224
+ }
225
+ chunkOLISEQHS_cjs.__name(nowSinceStart, "nowSinceStart");
226
+ function upsertSegment(segments, patch) {
227
+ const idx = segments.findIndex((s) => s.id === patch.id);
228
+ if (idx === -1) return [...segments, patch];
229
+ const next = segments.slice();
230
+ next[idx] = { ...next[idx], ...patch };
231
+ return next;
232
+ }
233
+ chunkOLISEQHS_cjs.__name(upsertSegment, "upsertSegment");
234
+ function reducer(state, action) {
235
+ switch (action.type) {
236
+ case "START":
237
+ return {
238
+ ...state,
239
+ status: "starting",
240
+ error: null,
241
+ startedAt: Date.now()
242
+ };
243
+ case "STARTED":
244
+ return { ...state, status: "listening" };
245
+ case "STOP":
246
+ return { ...state, status: "stopping" };
247
+ case "STOPPED":
248
+ case "ABORT":
249
+ return { ...state, status: "idle" };
250
+ case "PARTIAL": {
251
+ const seg = {
252
+ id: action.segmentId,
253
+ text: action.text,
254
+ isFinal: false,
255
+ confidence: action.confidence,
256
+ startedAt: nowSinceStart(state)
257
+ };
258
+ return { ...state, segments: upsertSegment(state.segments, seg) };
259
+ }
260
+ case "FINAL": {
261
+ const seg = {
262
+ id: action.segmentId || newSegmentId(),
263
+ text: action.text,
264
+ isFinal: true,
265
+ confidence: action.confidence,
266
+ startedAt: nowSinceStart(state),
267
+ endedAt: nowSinceStart(state)
268
+ };
269
+ return { ...state, segments: upsertSegment(state.segments, seg) };
270
+ }
271
+ case "ERROR":
272
+ return { ...state, status: "error", error: action.error };
273
+ case "RESET":
274
+ return { ...INITIAL_STATE };
275
+ default:
276
+ return state;
277
+ }
278
+ }
279
+ chunkOLISEQHS_cjs.__name(reducer, "reducer");
280
+
281
+ // src/tools/SpeechRecognition/core/engine/index.ts
282
+ function createEngineBus() {
283
+ const listeners = {
284
+ partial: /* @__PURE__ */ new Set(),
285
+ final: /* @__PURE__ */ new Set(),
286
+ error: /* @__PURE__ */ new Set(),
287
+ state: /* @__PURE__ */ new Set()
288
+ };
289
+ return {
290
+ on(event, cb) {
291
+ const set = listeners[event];
292
+ set.add(cb);
293
+ return () => {
294
+ set.delete(cb);
295
+ };
296
+ },
297
+ emit(event, ...args) {
298
+ const set = listeners[event];
299
+ for (const cb of set) {
300
+ try {
301
+ cb(...args);
302
+ } catch {
303
+ }
304
+ }
305
+ },
306
+ clear() {
307
+ for (const key of Object.keys(listeners)) {
308
+ listeners[key].clear();
309
+ }
310
+ }
311
+ };
312
+ }
313
+ chunkOLISEQHS_cjs.__name(createEngineBus, "createEngineBus");
314
+
315
+ // src/tools/SpeechRecognition/core/engine/webspeech.ts
316
+ function resolveCtor() {
317
+ if (typeof window === "undefined") return null;
318
+ const w = window;
319
+ return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
320
+ }
321
+ chunkOLISEQHS_cjs.__name(resolveCtor, "resolveCtor");
322
+ var ERROR_MAP = {
323
+ "no-speech": "no-speech",
324
+ aborted: "aborted",
325
+ "audio-capture": "no-microphone",
326
+ network: "network",
327
+ "not-allowed": "permission-denied",
328
+ "service-not-allowed": "permission-denied",
329
+ "bad-grammar": "engine",
330
+ "language-not-supported": "language"
331
+ };
332
+ function createWebSpeechEngine(opts = {}) {
333
+ const Ctor = resolveCtor();
334
+ const bus = createEngineBus();
335
+ let instance = null;
336
+ let currentSegmentId = null;
337
+ function teardown() {
338
+ if (!instance) return;
339
+ instance.onresult = null;
340
+ instance.onerror = null;
341
+ instance.onstart = null;
342
+ instance.onend = null;
343
+ instance = null;
344
+ currentSegmentId = null;
345
+ }
346
+ chunkOLISEQHS_cjs.__name(teardown, "teardown");
347
+ return {
348
+ id: "webspeech",
349
+ isSupported: Ctor !== null,
350
+ on(event, cb) {
351
+ return bus.on(event, cb);
352
+ },
353
+ async start(start) {
354
+ if (!Ctor) {
355
+ const err = {
356
+ code: "unsupported",
357
+ message: "Web Speech API is not available in this browser."
358
+ };
359
+ bus.emit("error", err);
360
+ throw err;
361
+ }
362
+ if (instance) {
363
+ sttLogger.debug("[webspeech] start() called while running \u2014 ignoring");
364
+ return;
365
+ }
366
+ bus.emit("state", "connecting");
367
+ const rec = new Ctor();
368
+ rec.lang = start.language;
369
+ rec.interimResults = start.interim;
370
+ rec.continuous = opts.continuous ?? true;
371
+ rec.maxAlternatives = opts.maxAlternatives ?? 1;
372
+ rec.onstart = () => {
373
+ bus.emit("state", "listening");
374
+ };
375
+ rec.onend = () => {
376
+ bus.emit("state", "closed");
377
+ teardown();
378
+ };
379
+ rec.onerror = (e) => {
380
+ const code = ERROR_MAP[e.error] ?? "engine";
381
+ const err = {
382
+ code,
383
+ message: e.message || `Web Speech error: ${e.error}`
384
+ };
385
+ bus.emit("error", err);
386
+ };
387
+ rec.onresult = (e) => {
388
+ for (let i = e.resultIndex; i < e.results.length; i += 1) {
389
+ const res = e.results[i];
390
+ const alt = res[0];
391
+ const text = alt.transcript;
392
+ if (!currentSegmentId) currentSegmentId = newSegmentId();
393
+ if (res.isFinal) {
394
+ bus.emit("final", text, currentSegmentId, alt.confidence);
395
+ currentSegmentId = null;
396
+ } else {
397
+ bus.emit("partial", text, currentSegmentId);
398
+ }
399
+ }
400
+ };
401
+ if (start.signal) {
402
+ start.signal.addEventListener("abort", () => {
403
+ rec.abort();
404
+ });
405
+ }
406
+ instance = rec;
407
+ try {
408
+ rec.start();
409
+ } catch (cause) {
410
+ const err = {
411
+ code: "engine",
412
+ message: "Failed to start Web Speech recognition.",
413
+ cause
414
+ };
415
+ bus.emit("error", err);
416
+ teardown();
417
+ throw err;
418
+ }
419
+ },
420
+ async stop() {
421
+ if (!instance) return;
422
+ bus.emit("state", "closing");
423
+ instance.stop();
424
+ },
425
+ abort() {
426
+ if (!instance) return;
427
+ instance.abort();
428
+ }
429
+ };
430
+ }
431
+ chunkOLISEQHS_cjs.__name(createWebSpeechEngine, "createWebSpeechEngine");
432
+ var DEFAULTS = {
433
+ language: null,
434
+ deviceId: null,
435
+ engineId: null,
436
+ earcons: false
437
+ };
438
+ var useSpeechPrefs = zustand.create()(
439
+ middleware.persist(
440
+ (set) => ({
441
+ ...DEFAULTS,
442
+ setLanguage: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((language) => set({ language }), "setLanguage"),
443
+ setDeviceId: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((deviceId) => set({ deviceId }), "setDeviceId"),
444
+ setEngineId: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((engineId) => set({ engineId }), "setEngineId"),
445
+ setEarcons: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((earcons) => set({ earcons }), "setEarcons"),
446
+ reset: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => set({ ...DEFAULTS }), "reset")
447
+ }),
448
+ {
449
+ name: "djangocfg-stt:prefs",
450
+ storage: middleware.createJSONStorage(
451
+ () => typeof window === "undefined" ? void 0 : window.localStorage
452
+ )
453
+ }
454
+ )
455
+ );
456
+ function useMicLevel(stream) {
457
+ const [level, setLevel] = react.useState(0);
458
+ const raf = react.useRef(null);
459
+ react.useEffect(() => {
460
+ if (!stream) {
461
+ setLevel(0);
462
+ return void 0;
463
+ }
464
+ const AC = window.AudioContext ?? window.webkitAudioContext;
465
+ if (!AC) return void 0;
466
+ const ctx = new AC();
467
+ const source = ctx.createMediaStreamSource(stream);
468
+ const analyser = ctx.createAnalyser();
469
+ analyser.fftSize = 1024;
470
+ analyser.smoothingTimeConstant = 0.7;
471
+ source.connect(analyser);
472
+ const buf = new Float32Array(analyser.fftSize);
473
+ const tick = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
474
+ analyser.getFloatTimeDomainData(buf);
475
+ let sum = 0;
476
+ for (let i = 0; i < buf.length; i += 1) sum += buf[i] * buf[i];
477
+ const rms = Math.sqrt(sum / buf.length);
478
+ setLevel(Math.min(1, rms * 2.5));
479
+ raf.current = requestAnimationFrame(tick);
480
+ }, "tick");
481
+ tick();
482
+ return () => {
483
+ if (raf.current != null) cancelAnimationFrame(raf.current);
484
+ raf.current = null;
485
+ source.disconnect();
486
+ analyser.disconnect();
487
+ void ctx.close();
488
+ };
489
+ }, [stream]);
490
+ return level;
491
+ }
492
+ chunkOLISEQHS_cjs.__name(useMicLevel, "useMicLevel");
493
+
494
+ // src/tools/SpeechRecognition/core/language.ts
495
+ var ISO_TO_BCP47 = {
496
+ en: "en-US",
497
+ ru: "ru-RU",
498
+ ko: "ko-KR",
499
+ ja: "ja-JP",
500
+ zh: "zh-CN",
501
+ de: "de-DE",
502
+ fr: "fr-FR",
503
+ it: "it-IT",
504
+ es: "es-ES",
505
+ nl: "nl-NL",
506
+ ar: "ar-SA",
507
+ tr: "tr-TR",
508
+ pl: "pl-PL",
509
+ sv: "sv-SE",
510
+ no: "nb-NO",
511
+ da: "da-DK",
512
+ pt: "pt-BR"
513
+ };
514
+ function toBCP47(code, table = ISO_TO_BCP47) {
515
+ if (!code) return void 0;
516
+ const trimmed = code.trim();
517
+ if (!trimmed) return void 0;
518
+ if (trimmed.includes("-")) return trimmed;
519
+ const lower = trimmed.toLowerCase();
520
+ return table[lower] ?? `${lower}-${lower.toUpperCase()}`;
521
+ }
522
+ chunkOLISEQHS_cjs.__name(toBCP47, "toBCP47");
523
+ function resolveSpeechLanguage(opts) {
524
+ return toBCP47(opts.explicit) ?? toBCP47(opts.prefs) ?? toBCP47(opts.i18n) ?? toBCP47(typeof navigator !== "undefined" ? navigator.language : null) ?? "en-US";
525
+ }
526
+ chunkOLISEQHS_cjs.__name(resolveSpeechLanguage, "resolveSpeechLanguage");
527
+
528
+ // src/tools/SpeechRecognition/hooks/useResolvedLanguage.ts
529
+ function useResolvedLanguage(explicit) {
530
+ const prefs = useSpeechPrefs();
531
+ const locale = i18n.useLocaleOptional();
532
+ return resolveSpeechLanguage({
533
+ explicit,
534
+ prefs: prefs.language,
535
+ i18n: locale
536
+ });
537
+ }
538
+ chunkOLISEQHS_cjs.__name(useResolvedLanguage, "useResolvedLanguage");
539
+
540
+ // src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts
541
+ function useSpeechRecognition(config = {}) {
542
+ const prefs = useSpeechPrefs();
543
+ const language = useResolvedLanguage(config.language);
544
+ const engine = react.useMemo(
545
+ () => config.engine ?? createWebSpeechEngine(),
546
+ [config.engine]
547
+ );
548
+ const [state, dispatch] = react.useReducer(reducer, INITIAL_STATE);
549
+ const [stream, setStream] = react.useState(null);
550
+ const level = useMicLevel(stream);
551
+ const cbRef = react.useRef(config);
552
+ cbRef.current = config;
553
+ react.useEffect(() => {
554
+ const offs = [
555
+ engine.on("partial", (text, segmentId) => {
556
+ dispatch({ type: "PARTIAL", text, segmentId });
557
+ const seg = {
558
+ id: segmentId,
559
+ text,
560
+ isFinal: false,
561
+ startedAt: Date.now()
562
+ };
563
+ cbRef.current.onPartial?.(text, seg);
564
+ }),
565
+ engine.on("final", (text, segmentId, confidence) => {
566
+ dispatch({ type: "FINAL", text, segmentId, confidence });
567
+ const seg = {
568
+ id: segmentId,
569
+ text,
570
+ isFinal: true,
571
+ confidence,
572
+ startedAt: Date.now(),
573
+ endedAt: Date.now()
574
+ };
575
+ cbRef.current.onFinal?.(text, seg);
576
+ }),
577
+ engine.on("error", (err) => {
578
+ dispatch({ type: "ERROR", error: err });
579
+ cbRef.current.onError?.(err);
580
+ }),
581
+ engine.on("state", (s) => {
582
+ if (s === "listening") {
583
+ dispatch({ type: "STARTED" });
584
+ cbRef.current.onStart?.();
585
+ setStream(engine.getStream?.() ?? null);
586
+ } else if (s === "closed") {
587
+ dispatch({ type: "STOPPED" });
588
+ cbRef.current.onStop?.();
589
+ setStream(null);
590
+ }
591
+ })
592
+ ];
593
+ return () => {
594
+ offs.forEach((off) => off());
595
+ };
596
+ }, [engine]);
597
+ const silenceTimer = react.useRef(null);
598
+ const maxTimer = react.useRef(null);
599
+ react.useEffect(() => {
600
+ if (state.status !== "listening") return void 0;
601
+ const { silenceMs, maxMs, silenceThreshold = 0.02 } = config.autoStop ?? {};
602
+ if (maxMs) {
603
+ maxTimer.current = window.setTimeout(() => {
604
+ sttLogger.debug("[autoStop] max duration hit");
605
+ void engine.stop();
606
+ }, maxMs);
607
+ }
608
+ if (silenceMs) {
609
+ const checkInterval = window.setInterval(() => {
610
+ if (level < silenceThreshold) {
611
+ if (silenceTimer.current == null) {
612
+ silenceTimer.current = window.setTimeout(() => {
613
+ sttLogger.debug("[autoStop] silence detected");
614
+ void engine.stop();
615
+ }, silenceMs);
616
+ }
617
+ } else if (silenceTimer.current != null) {
618
+ clearTimeout(silenceTimer.current);
619
+ silenceTimer.current = null;
620
+ }
621
+ }, 200);
622
+ return () => {
623
+ clearInterval(checkInterval);
624
+ if (silenceTimer.current != null) clearTimeout(silenceTimer.current);
625
+ silenceTimer.current = null;
626
+ if (maxTimer.current != null) clearTimeout(maxTimer.current);
627
+ maxTimer.current = null;
628
+ };
629
+ }
630
+ return () => {
631
+ if (maxTimer.current != null) clearTimeout(maxTimer.current);
632
+ maxTimer.current = null;
633
+ };
634
+ }, [state.status, config.autoStop, level, engine]);
635
+ const start = react.useCallback(async () => {
636
+ if (state.status === "listening" || state.status === "starting") return;
637
+ dispatch({ type: "START" });
638
+ try {
639
+ await engine.start({
640
+ language,
641
+ interim: config.interim ?? true,
642
+ deviceId: config.deviceId ?? prefs.deviceId ?? void 0
643
+ });
644
+ } catch (cause) {
645
+ sttLogger.debug("[start] engine threw", cause);
646
+ }
647
+ }, [engine, language, config.interim, config.deviceId, prefs.deviceId, state.status]);
648
+ const stop = react.useCallback(async () => {
649
+ if (state.status === "idle" || state.status === "stopping") return;
650
+ dispatch({ type: "STOP" });
651
+ await engine.stop();
652
+ }, [engine, state.status]);
653
+ const abort = react.useCallback(() => {
654
+ engine.abort();
655
+ dispatch({ type: "ABORT" });
656
+ }, [engine]);
657
+ const toggle = react.useCallback(async () => {
658
+ if (state.status === "listening" || state.status === "starting") {
659
+ await stop();
660
+ } else {
661
+ await start();
662
+ }
663
+ }, [state.status, start, stop]);
664
+ const reset = react.useCallback(() => {
665
+ dispatch({ type: "RESET" });
666
+ }, []);
667
+ const transcript = react.useMemo(
668
+ () => state.segments.length === 0 ? EMPTY_TRANSCRIPT : buildTranscript(state.segments),
669
+ [state.segments]
670
+ );
671
+ return {
672
+ status: state.status,
673
+ isSupported: engine.isSupported,
674
+ transcript,
675
+ error: state.error,
676
+ level,
677
+ start,
678
+ stop,
679
+ abort,
680
+ toggle,
681
+ reset
682
+ };
683
+ }
684
+ chunkOLISEQHS_cjs.__name(useSpeechRecognition, "useSpeechRecognition");
685
+
686
+ // src/tools/SpeechRecognition/hooks/useDictation.ts
687
+ function useDictation(config) {
688
+ const { value, onChange, separator = " ", ...rest } = config;
689
+ const valueRef = react.useRef(value);
690
+ valueRef.current = value;
691
+ const onChangeRef = react.useRef(onChange);
692
+ onChangeRef.current = onChange;
693
+ const rec = useSpeechRecognition({
694
+ ...rest,
695
+ onFinal: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((text) => {
696
+ const clean = normaliseFinal(text);
697
+ if (!clean) return;
698
+ const prev = valueRef.current;
699
+ const next = prev ? `${prev}${separator}${clean}` : clean;
700
+ onChangeRef.current(next);
701
+ }, "onFinal")
702
+ });
703
+ react.useEffect(() => {
704
+ valueRef.current = value;
705
+ }, [value]);
706
+ return {
707
+ ...rec,
708
+ toggleDictation: rec.toggle
709
+ };
710
+ }
711
+ chunkOLISEQHS_cjs.__name(useDictation, "useDictation");
712
+ var MOD_KEYS = /* @__PURE__ */ new Set(["shift", "ctrl", "alt", "meta", "mod"]);
713
+ function parseChord(chord) {
714
+ const parts = chord.toLowerCase().split("+").map((s) => s.trim());
715
+ const mods = /* @__PURE__ */ new Set();
716
+ let main = null;
717
+ for (const part of parts) {
718
+ if (MOD_KEYS.has(part)) {
719
+ mods.add(part === "mod" ? "meta" : part);
720
+ } else {
721
+ main = part;
722
+ }
723
+ }
724
+ return { mods, main };
725
+ }
726
+ chunkOLISEQHS_cjs.__name(parseChord, "parseChord");
727
+ function matches(e, mods, main) {
728
+ if (mods.has("shift") !== e.shiftKey) return false;
729
+ if (mods.has("ctrl") !== e.ctrlKey) return false;
730
+ if (mods.has("alt") !== e.altKey) return false;
731
+ if (mods.has("meta") !== (e.metaKey || !e.metaKey && false)) return false;
732
+ if (main && e.key.toLowerCase() !== main) return false;
733
+ return true;
734
+ }
735
+ chunkOLISEQHS_cjs.__name(matches, "matches");
736
+ function usePushToTalk(recognition, opts) {
737
+ const { key, enabled = true } = opts;
738
+ react.useEffect(() => {
739
+ if (!enabled || typeof window === "undefined") return void 0;
740
+ const { mods, main } = parseChord(key);
741
+ const onDown = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((e) => {
742
+ if (e.repeat) return;
743
+ if (!main && mods.size === 0) return;
744
+ const target = e.target;
745
+ const inField = target?.tagName === "INPUT" || target?.tagName === "TEXTAREA" || target?.isContentEditable;
746
+ if (inField && mods.size === 0) return;
747
+ if (!matches(e, mods, main)) return;
748
+ if (recognition.status === "listening" || recognition.status === "starting") return;
749
+ e.preventDefault();
750
+ void recognition.start();
751
+ }, "onDown");
752
+ const onUp = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((e) => {
753
+ if (!matches(e, mods, main) && main && e.key.toLowerCase() !== main) return;
754
+ if (recognition.status !== "listening" && recognition.status !== "starting") return;
755
+ void recognition.stop();
756
+ }, "onUp");
757
+ window.addEventListener("keydown", onDown);
758
+ window.addEventListener("keyup", onUp);
759
+ return () => {
760
+ window.removeEventListener("keydown", onDown);
761
+ window.removeEventListener("keyup", onUp);
762
+ };
763
+ }, [enabled, key, recognition]);
764
+ }
765
+ chunkOLISEQHS_cjs.__name(usePushToTalk, "usePushToTalk");
766
+ function DictationField({
767
+ value,
768
+ onChange,
769
+ engine,
770
+ language,
771
+ pushToTalk,
772
+ placeholder = "Type or press the mic to dictate\u2026",
773
+ rows = 3,
774
+ disabled,
775
+ showInterim = true,
776
+ showMeter = true,
777
+ className,
778
+ textareaClassName
779
+ }) {
780
+ const rec = useDictation({
781
+ value,
782
+ onChange,
783
+ engine,
784
+ language
785
+ });
786
+ usePushToTalk(rec, {
787
+ key: pushToTalk?.key ?? "alt",
788
+ enabled: !!pushToTalk && pushToTalk.enabled !== false
789
+ });
790
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col gap-2", className), children: [
791
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
792
+ /* @__PURE__ */ jsxRuntime.jsx(
793
+ "textarea",
794
+ {
795
+ value,
796
+ onChange: (e) => onChange(e.target.value),
797
+ placeholder,
798
+ rows,
799
+ disabled,
800
+ className: lib.cn(
801
+ "w-full resize-y rounded-md border border-input bg-background px-3 py-2 text-sm",
802
+ "placeholder:text-muted-foreground",
803
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
804
+ "disabled:cursor-not-allowed disabled:opacity-50",
805
+ textareaClassName
806
+ )
807
+ }
808
+ ),
809
+ showInterim && rec.transcript.interim && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-none mt-1 text-xs italic text-muted-foreground", children: [
810
+ "\u2026 ",
811
+ rec.transcript.interim
812
+ ] })
813
+ ] }),
814
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
815
+ /* @__PURE__ */ jsxRuntime.jsx(
816
+ DictationButton,
817
+ {
818
+ status: rec.status,
819
+ isSupported: rec.isSupported,
820
+ onClick: () => void rec.toggleDictation(),
821
+ size: "sm",
822
+ disabled
823
+ }
824
+ ),
825
+ showMeter && /* @__PURE__ */ jsxRuntime.jsx(MicMeter, { level: rec.level, bars: 10, height: 20 }),
826
+ pushToTalk && /* @__PURE__ */ jsxRuntime.jsx(PushToTalkHint, { chord: pushToTalk.key, className: "ml-auto" })
827
+ ] }),
828
+ /* @__PURE__ */ jsxRuntime.jsx(ErrorBanner, { error: rec.error })
829
+ ] });
830
+ }
831
+ chunkOLISEQHS_cjs.__name(DictationField, "DictationField");
832
+
833
+ exports.DictationField = DictationField;
834
+ //# sourceMappingURL=chunk-KMSBGNVC.cjs.map
835
+ //# sourceMappingURL=chunk-KMSBGNVC.cjs.map