@emblemvault/hustle-react 1.4.10 → 1.5.0

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/README.md CHANGED
@@ -109,6 +109,7 @@ Complete chat UI component.
109
109
  showDebug // Show tool call debug info
110
110
  placeholder="Message" // Input placeholder
111
111
  initialSystemPrompt="" // Initial system prompt
112
+ enableSpeechToText // Enable voice input via microphone (default: false)
112
113
 
113
114
  // Callbacks
114
115
  onMessage={(msg) => {}} // When user sends message
@@ -283,6 +284,59 @@ function App() {
283
284
 
284
285
  Each instance has separate settings and plugins stored under its unique `instanceId`.
285
286
 
287
+ ## Speech-to-Text
288
+
289
+ Enable voice input in `HustleChat` via prop, settings modal, or use the hook directly for custom implementations.
290
+
291
+ ### Using HustleChat
292
+
293
+ **Via prop:**
294
+ ```tsx
295
+ <HustleChat enableSpeechToText />
296
+ ```
297
+
298
+ **Via settings modal:**
299
+ When `showSettings` is enabled, users can toggle "Voice Input" in the settings modal. This preference is persisted to localStorage.
300
+
301
+ ```tsx
302
+ <HustleChat showSettings />
303
+ ```
304
+
305
+ When enabled, a microphone button appears next to the send button. Click to start listening, click again to stop. The button pulses red while actively listening.
306
+
307
+ ### Using the Hook Directly
308
+
309
+ ```tsx
310
+ import { useSpeechRecognition } from '@emblemvault/hustle-react';
311
+
312
+ function CustomInput() {
313
+ const [message, setMessage] = useState('');
314
+
315
+ const {
316
+ isSupported, // Browser supports Web Speech API
317
+ isListening, // Currently listening
318
+ toggleListening,
319
+ } = useSpeechRecognition({
320
+ onTranscript: (text) => setMessage(prev => prev + ' ' + text),
321
+ onInterimTranscript: (text) => console.log('Interim:', text),
322
+ onError: (error) => console.error('Speech error:', error),
323
+ continuous: true, // Keep listening (default: true)
324
+ language: 'en-US', // Recognition language (default: 'en-US')
325
+ });
326
+
327
+ if (!isSupported) return <p>Speech not supported</p>;
328
+
329
+ return (
330
+ <div>
331
+ <input value={message} onChange={e => setMessage(e.target.value)} />
332
+ <button onClick={toggleListening}>
333
+ {isListening ? 'Stop' : 'Speak'}
334
+ </button>
335
+ </div>
336
+ );
337
+ }
338
+ ```
339
+
286
340
  ## Underlying SDK
287
341
 
288
342
  This package wraps [hustle-incognito](https://github.com/nicktaylor-/hustle-incognito) for React. The SDK handles:
@@ -31,9 +31,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  ));
32
32
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
33
33
 
34
- // ../../node_modules/highlight.js/lib/core.js
34
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/lib/core.js
35
35
  var require_core = __commonJS({
36
- "../../node_modules/highlight.js/lib/core.js"(exports$1, module) {
36
+ "node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/lib/core.js"(exports$1, module) {
37
37
  function deepFreeze(obj) {
38
38
  if (obj instanceof Map) {
39
39
  obj.clear = obj.delete = obj.set = function() {
@@ -2341,6 +2341,134 @@ function useHustle() {
2341
2341
  }
2342
2342
  return context;
2343
2343
  }
2344
+ function useSpeechRecognition({
2345
+ onTranscript,
2346
+ onInterimTranscript,
2347
+ onError,
2348
+ continuous = true,
2349
+ language = "en-US"
2350
+ } = {}) {
2351
+ const [isListening, setIsListening] = useState(false);
2352
+ const [isSupported, setIsSupported] = useState(false);
2353
+ const recognitionRef = useRef(null);
2354
+ const finalTranscriptRef = useRef("");
2355
+ const onTranscriptRef = useRef(onTranscript);
2356
+ const onInterimTranscriptRef = useRef(onInterimTranscript);
2357
+ const onErrorRef = useRef(onError);
2358
+ useEffect(() => {
2359
+ onTranscriptRef.current = onTranscript;
2360
+ onInterimTranscriptRef.current = onInterimTranscript;
2361
+ onErrorRef.current = onError;
2362
+ }, [onTranscript, onInterimTranscript, onError]);
2363
+ useEffect(() => {
2364
+ if (typeof window === "undefined") {
2365
+ return;
2366
+ }
2367
+ const SpeechRecognitionAPI = window.SpeechRecognition || window.webkitSpeechRecognition;
2368
+ if (!SpeechRecognitionAPI) {
2369
+ console.warn("Speech recognition not supported in this browser");
2370
+ return;
2371
+ }
2372
+ setIsSupported(true);
2373
+ const recognition = new SpeechRecognitionAPI();
2374
+ recognition.continuous = continuous;
2375
+ recognition.interimResults = true;
2376
+ recognition.lang = language;
2377
+ recognition.onstart = () => {
2378
+ console.log("Speech recognition started");
2379
+ setIsListening(true);
2380
+ finalTranscriptRef.current = "";
2381
+ };
2382
+ recognition.onresult = (event) => {
2383
+ let interimTranscript = "";
2384
+ let finalTranscript = "";
2385
+ const resultIndex = event.resultIndex || 0;
2386
+ for (let i = resultIndex; i < event.results.length; i++) {
2387
+ const transcript = event.results[i][0].transcript;
2388
+ if (event.results[i].isFinal) {
2389
+ finalTranscript += transcript;
2390
+ } else {
2391
+ interimTranscript += transcript;
2392
+ }
2393
+ }
2394
+ if (finalTranscript) {
2395
+ finalTranscriptRef.current += finalTranscript;
2396
+ onTranscriptRef.current?.(finalTranscript);
2397
+ }
2398
+ if (interimTranscript) {
2399
+ onInterimTranscriptRef.current?.(interimTranscript);
2400
+ }
2401
+ };
2402
+ recognition.onerror = (event) => {
2403
+ console.error("Speech recognition error:", event.error);
2404
+ if (event.error !== "aborted" && event.error !== "no-speech") {
2405
+ onErrorRef.current?.(event.error);
2406
+ }
2407
+ setIsListening(false);
2408
+ };
2409
+ recognition.onend = () => {
2410
+ console.log("Speech recognition ended");
2411
+ setIsListening(false);
2412
+ };
2413
+ recognitionRef.current = recognition;
2414
+ return () => {
2415
+ if (recognitionRef.current) {
2416
+ try {
2417
+ recognitionRef.current.abort();
2418
+ } catch (e) {
2419
+ }
2420
+ }
2421
+ };
2422
+ }, [continuous, language]);
2423
+ const startListening = useCallback(() => {
2424
+ const recognition = recognitionRef.current;
2425
+ if (!recognition) {
2426
+ console.error("Speech recognition not available");
2427
+ return;
2428
+ }
2429
+ if (isListening) {
2430
+ return;
2431
+ }
2432
+ console.log("Starting speech recognition...");
2433
+ try {
2434
+ finalTranscriptRef.current = "";
2435
+ recognition.start();
2436
+ } catch (error2) {
2437
+ console.error("Error starting:", error2);
2438
+ onErrorRef.current?.("Failed to start speech recognition");
2439
+ }
2440
+ }, [isListening]);
2441
+ const stopListening = useCallback(() => {
2442
+ const recognition = recognitionRef.current;
2443
+ if (!recognition) {
2444
+ return;
2445
+ }
2446
+ if (!isListening) {
2447
+ return;
2448
+ }
2449
+ console.log("Stopping speech recognition...");
2450
+ try {
2451
+ recognition.stop();
2452
+ } catch (error2) {
2453
+ console.warn("Error stopping:", error2);
2454
+ setIsListening(false);
2455
+ }
2456
+ }, [isListening]);
2457
+ const toggleListening = useCallback(() => {
2458
+ if (isListening) {
2459
+ stopListening();
2460
+ } else {
2461
+ startListening();
2462
+ }
2463
+ }, [isListening, startListening, stopListening]);
2464
+ return {
2465
+ isSupported,
2466
+ isListening,
2467
+ startListening,
2468
+ stopListening,
2469
+ toggleListening
2470
+ };
2471
+ }
2344
2472
 
2345
2473
  // src/plugins/predictionMarket.ts
2346
2474
  var DOME_API_BASE = "https://api.domeapi.io/v1";
@@ -4648,7 +4776,7 @@ var animations = `
4648
4776
  }
4649
4777
  `;
4650
4778
 
4651
- // ../../node_modules/marked/lib/marked.esm.js
4779
+ // node_modules/.pnpm/marked@17.0.1/node_modules/marked/lib/marked.esm.js
4652
4780
  function L() {
4653
4781
  return { async: false, breaks: false, extensions: null, gfm: true, hooks: null, pedantic: false, renderer: null, silent: false, tokenizer: null, walkTokens: null };
4654
4782
  }
@@ -5823,11 +5951,11 @@ d.parseInline;
5823
5951
  b.parse;
5824
5952
  x.lex;
5825
5953
 
5826
- // ../../node_modules/highlight.js/es/core.js
5954
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/core.js
5827
5955
  var import_core = __toESM(require_core());
5828
5956
  var core_default = import_core.default;
5829
5957
 
5830
- // ../../node_modules/highlight.js/es/languages/javascript.js
5958
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/javascript.js
5831
5959
  var IDENT_RE = "[A-Za-z$_][0-9A-Za-z$_]*";
5832
5960
  var KEYWORDS = [
5833
5961
  "as",
@@ -6528,7 +6656,7 @@ function javascript(hljs) {
6528
6656
  };
6529
6657
  }
6530
6658
 
6531
- // ../../node_modules/highlight.js/es/languages/typescript.js
6659
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/typescript.js
6532
6660
  var IDENT_RE2 = "[A-Za-z$_][0-9A-Za-z$_]*";
6533
6661
  var KEYWORDS2 = [
6534
6662
  "as",
@@ -7342,7 +7470,7 @@ function typescript(hljs) {
7342
7470
  return tsLanguage;
7343
7471
  }
7344
7472
 
7345
- // ../../node_modules/highlight.js/es/languages/python.js
7473
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/python.js
7346
7474
  function python(hljs) {
7347
7475
  const regex = hljs.regex;
7348
7476
  const IDENT_RE3 = /[\p{XID_Start}_]\p{XID_Continue}*/u;
@@ -7757,7 +7885,7 @@ function python(hljs) {
7757
7885
  };
7758
7886
  }
7759
7887
 
7760
- // ../../node_modules/highlight.js/es/languages/json.js
7888
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/json.js
7761
7889
  function json(hljs) {
7762
7890
  const ATTRIBUTE = {
7763
7891
  className: "attr",
@@ -7797,7 +7925,7 @@ function json(hljs) {
7797
7925
  };
7798
7926
  }
7799
7927
 
7800
- // ../../node_modules/highlight.js/es/languages/bash.js
7928
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/bash.js
7801
7929
  function bash(hljs) {
7802
7930
  const regex = hljs.regex;
7803
7931
  const VAR = {};
@@ -8191,7 +8319,7 @@ function bash(hljs) {
8191
8319
  };
8192
8320
  }
8193
8321
 
8194
- // ../../node_modules/highlight.js/es/languages/shell.js
8322
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/shell.js
8195
8323
  function shell(hljs) {
8196
8324
  return {
8197
8325
  name: "Shell Session",
@@ -8215,7 +8343,7 @@ function shell(hljs) {
8215
8343
  };
8216
8344
  }
8217
8345
 
8218
- // ../../node_modules/highlight.js/es/languages/css.js
8346
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/css.js
8219
8347
  var MODES = (hljs) => {
8220
8348
  return {
8221
8349
  IMPORTANT: {
@@ -9152,7 +9280,7 @@ function css(hljs) {
9152
9280
  };
9153
9281
  }
9154
9282
 
9155
- // ../../node_modules/highlight.js/es/languages/xml.js
9283
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/xml.js
9156
9284
  function xml(hljs) {
9157
9285
  const regex = hljs.regex;
9158
9286
  const TAG_NAME_RE = regex.concat(/[\p{L}_]/u, regex.optional(/[\p{L}0-9_.-]*:/u), /[\p{L}0-9_.-]*/u);
@@ -9378,7 +9506,7 @@ function xml(hljs) {
9378
9506
  };
9379
9507
  }
9380
9508
 
9381
- // ../../node_modules/highlight.js/es/languages/markdown.js
9509
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/markdown.js
9382
9510
  function markdown(hljs) {
9383
9511
  const regex = hljs.regex;
9384
9512
  const INLINE_HTML = {
@@ -9610,7 +9738,7 @@ function markdown(hljs) {
9610
9738
  };
9611
9739
  }
9612
9740
 
9613
- // ../../node_modules/highlight.js/es/languages/sql.js
9741
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/sql.js
9614
9742
  function sql(hljs) {
9615
9743
  const regex = hljs.regex;
9616
9744
  const COMMENT_MODE = hljs.COMMENT("--", "$");
@@ -10253,7 +10381,7 @@ function sql(hljs) {
10253
10381
  };
10254
10382
  }
10255
10383
 
10256
- // ../../node_modules/highlight.js/es/languages/yaml.js
10384
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/yaml.js
10257
10385
  function yaml(hljs) {
10258
10386
  const LITERALS3 = "true false yes no null";
10259
10387
  const URI_CHARACTERS = "[\\w#;/?:@&=+$,.~*'()[\\]]+";
@@ -10454,7 +10582,7 @@ function yaml(hljs) {
10454
10582
  };
10455
10583
  }
10456
10584
 
10457
- // ../../node_modules/highlight.js/es/languages/rust.js
10585
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/rust.js
10458
10586
  function rust(hljs) {
10459
10587
  const regex = hljs.regex;
10460
10588
  const RAW_IDENTIFIER = /(r#)?/;
@@ -10767,7 +10895,7 @@ function rust(hljs) {
10767
10895
  };
10768
10896
  }
10769
10897
 
10770
- // ../../node_modules/highlight.js/es/languages/go.js
10898
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/go.js
10771
10899
  function go(hljs) {
10772
10900
  const LITERALS3 = [
10773
10901
  "true",
@@ -10921,7 +11049,7 @@ function go(hljs) {
10921
11049
  };
10922
11050
  }
10923
11051
 
10924
- // ../../node_modules/highlight.js/es/languages/java.js
11052
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/java.js
10925
11053
  var decimalDigits = "[0-9](_*[0-9])*";
10926
11054
  var frac = `\\.(${decimalDigits})`;
10927
11055
  var hexDigits = "[0-9a-fA-F](_*[0-9a-fA-F])*";
@@ -11175,7 +11303,7 @@ function java(hljs) {
11175
11303
  };
11176
11304
  }
11177
11305
 
11178
- // ../../node_modules/highlight.js/es/languages/cpp.js
11306
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/cpp.js
11179
11307
  function cpp(hljs) {
11180
11308
  const regex = hljs.regex;
11181
11309
  const C_LINE_COMMENT_MODE = hljs.COMMENT("//", "$", { contains: [{ begin: /\\\n/ }] });
@@ -11718,7 +11846,7 @@ function cpp(hljs) {
11718
11846
  };
11719
11847
  }
11720
11848
 
11721
- // ../../node_modules/highlight.js/es/languages/csharp.js
11849
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/csharp.js
11722
11850
  function csharp(hljs) {
11723
11851
  const BUILT_IN_KEYWORDS = [
11724
11852
  "bool",
@@ -12118,7 +12246,7 @@ function csharp(hljs) {
12118
12246
  };
12119
12247
  }
12120
12248
 
12121
- // ../../node_modules/highlight.js/es/languages/php.js
12249
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/php.js
12122
12250
  function php(hljs) {
12123
12251
  const regex = hljs.regex;
12124
12252
  const NOT_PERL_ETC = /(?![A-Za-z0-9])(?![$])/;
@@ -12719,7 +12847,7 @@ function php(hljs) {
12719
12847
  };
12720
12848
  }
12721
12849
 
12722
- // ../../node_modules/highlight.js/es/languages/ruby.js
12850
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/ruby.js
12723
12851
  function ruby(hljs) {
12724
12852
  const regex = hljs.regex;
12725
12853
  const RUBY_METHOD_RE = "([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)";
@@ -13132,7 +13260,7 @@ function ruby(hljs) {
13132
13260
  };
13133
13261
  }
13134
13262
 
13135
- // ../../node_modules/highlight.js/es/languages/swift.js
13263
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/swift.js
13136
13264
  function source(re2) {
13137
13265
  if (!re2) return null;
13138
13266
  if (typeof re2 === "string") return re2;
@@ -14007,7 +14135,7 @@ function swift(hljs) {
14007
14135
  };
14008
14136
  }
14009
14137
 
14010
- // ../../node_modules/highlight.js/es/languages/kotlin.js
14138
+ // node_modules/.pnpm/highlight.js@11.11.1/node_modules/highlight.js/es/languages/kotlin.js
14011
14139
  var decimalDigits2 = "[0-9](_*[0-9])*";
14012
14140
  var frac2 = `\\.(${decimalDigits2})`;
14013
14141
  var hexDigits2 = "[0-9a-fA-F](_*[0-9a-fA-F])*";
@@ -14945,6 +15073,28 @@ var styles = {
14945
15073
  borderRadius: tokens.radius.full,
14946
15074
  animation: "hustle-spin 0.8s linear infinite"
14947
15075
  },
15076
+ // Mic button
15077
+ micBtn: {
15078
+ width: "40px",
15079
+ height: "40px",
15080
+ padding: 0,
15081
+ background: tokens.colors.bgTertiary,
15082
+ border: `1px solid ${tokens.colors.borderSecondary}`,
15083
+ borderRadius: tokens.radius.lg,
15084
+ color: tokens.colors.textSecondary,
15085
+ flexShrink: 0,
15086
+ display: "flex",
15087
+ alignItems: "center",
15088
+ justifyContent: "center",
15089
+ cursor: "pointer",
15090
+ transition: `all ${tokens.transitions.fast}`
15091
+ },
15092
+ micBtnListening: {
15093
+ background: "rgba(239, 68, 68, 0.1)",
15094
+ borderColor: "rgba(239, 68, 68, 0.3)",
15095
+ color: "#ef4444",
15096
+ animation: "hustle-pulse 1s ease-in-out infinite"
15097
+ },
14948
15098
  // Error display
14949
15099
  errorBox: {
14950
15100
  marginTop: tokens.spacing.sm,
@@ -15118,6 +15268,7 @@ function HustleChat({
15118
15268
  showDebug = false,
15119
15269
  hideHeader = false,
15120
15270
  initialSystemPrompt = "",
15271
+ enableSpeechToText = false,
15121
15272
  onMessage,
15122
15273
  onToolCall,
15123
15274
  onResponse
@@ -15154,6 +15305,30 @@ function HustleChat({
15154
15305
  const [attachments, setAttachments] = useState([]);
15155
15306
  const [currentToolCalls, setCurrentToolCalls] = useState([]);
15156
15307
  const [showSettingsPanel, setShowSettingsPanel] = useState(false);
15308
+ const [speechToTextEnabled, setSpeechToTextEnabled] = useState(() => {
15309
+ if (typeof window !== "undefined") {
15310
+ const stored = localStorage.getItem(`hustle-stt-enabled-${instanceId}`);
15311
+ return stored === "true";
15312
+ }
15313
+ return false;
15314
+ });
15315
+ useEffect(() => {
15316
+ if (typeof window !== "undefined") {
15317
+ localStorage.setItem(`hustle-stt-enabled-${instanceId}`, String(speechToTextEnabled));
15318
+ }
15319
+ }, [speechToTextEnabled, instanceId]);
15320
+ const {
15321
+ isSupported: isSpeechSupported,
15322
+ isListening,
15323
+ toggleListening
15324
+ } = useSpeechRecognition({
15325
+ onTranscript: useCallback((transcript) => {
15326
+ setInputValue((prev) => prev + (prev ? " " : "") + transcript);
15327
+ }, []),
15328
+ onError: useCallback((error3) => {
15329
+ console.error("Speech recognition error:", error3);
15330
+ }, [])
15331
+ });
15157
15332
  const messagesEndRef = useRef(null);
15158
15333
  const fileInputRef = useRef(null);
15159
15334
  const messagesRef = useRef(messages);
@@ -15622,6 +15797,27 @@ function HustleChat({
15622
15797
  }
15623
15798
  )
15624
15799
  ] }),
15800
+ isSpeechSupported && /* @__PURE__ */ jsxs("div", { style: styles.settingGroup, children: [
15801
+ /* @__PURE__ */ jsx("label", { style: styles.settingLabel, children: "Voice Input" }),
15802
+ /* @__PURE__ */ jsxs(
15803
+ "div",
15804
+ {
15805
+ style: styles.toggleRow,
15806
+ onClick: () => setSpeechToTextEnabled(!speechToTextEnabled),
15807
+ children: [
15808
+ /* @__PURE__ */ jsx("span", { style: styles.toggleLabel, children: "Enable speech-to-text" }),
15809
+ /* @__PURE__ */ jsx("div", { style: {
15810
+ ...styles.toggleSwitch,
15811
+ ...speechToTextEnabled ? styles.toggleSwitchActive : {}
15812
+ }, children: /* @__PURE__ */ jsx("div", { style: {
15813
+ ...styles.toggleKnob,
15814
+ ...speechToTextEnabled ? styles.toggleKnobActive : {}
15815
+ } }) })
15816
+ ]
15817
+ }
15818
+ ),
15819
+ /* @__PURE__ */ jsx("p", { style: styles.settingDescription, children: "Use your microphone to dictate messages" })
15820
+ ] }),
15625
15821
  /* @__PURE__ */ jsx("div", { style: styles.settingDivider }),
15626
15822
  /* @__PURE__ */ jsxs("div", { style: { ...styles.settingGroup, marginBottom: 0 }, children: [
15627
15823
  /* @__PURE__ */ jsx("label", { style: styles.settingLabel, children: "Plugins" }),
@@ -15758,6 +15954,21 @@ function HustleChat({
15758
15954
  }
15759
15955
  ) })
15760
15956
  ] }),
15957
+ (enableSpeechToText || speechToTextEnabled) && isSpeechSupported && /* @__PURE__ */ jsx(
15958
+ "button",
15959
+ {
15960
+ type: "button",
15961
+ onClick: toggleListening,
15962
+ disabled: !canChat || isStreaming || isLoading,
15963
+ style: {
15964
+ ...styles.micBtn,
15965
+ ...isListening ? styles.micBtnListening : {},
15966
+ ...!canChat || isStreaming || isLoading ? { opacity: 0.5, cursor: "not-allowed" } : {}
15967
+ },
15968
+ title: isListening ? "Stop listening" : "Start voice input",
15969
+ children: isListening ? /* @__PURE__ */ jsx(MicOffIcon, {}) : /* @__PURE__ */ jsx(MicIcon, {})
15970
+ }
15971
+ ),
15761
15972
  /* @__PURE__ */ jsx(
15762
15973
  "button",
15763
15974
  {
@@ -15817,6 +16028,23 @@ function SettingsIcon() {
15817
16028
  function AttachIcon() {
15818
16029
  return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
15819
16030
  }
16031
+ function MicIcon() {
16032
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
16033
+ /* @__PURE__ */ jsx("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
16034
+ /* @__PURE__ */ jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
16035
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
16036
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
16037
+ ] });
16038
+ }
16039
+ function MicOffIcon() {
16040
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
16041
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
16042
+ /* @__PURE__ */ jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" }),
16043
+ /* @__PURE__ */ jsx("path", { d: "M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23" }),
16044
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
16045
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
16046
+ ] });
16047
+ }
15820
16048
 
15821
16049
  // src/constants/index.ts
15822
16050
  var AGENT_HUSTLE_ICON = "data:image/jpeg;base64,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";
@@ -16256,6 +16484,6 @@ var DEFAULTS = {
16256
16484
  EMBLEM_MODAL_URL: "https://emblemvault.ai/connect"
16257
16485
  };
16258
16486
 
16259
- export { AGENT_HUSTLE_ICON, DEFAULTS, HustleChat, HustleChatWidget, HustleProvider, MarkdownContent, STORAGE_KEYS, availablePlugins, debounce, formatFileSize, getAvailablePlugin, hydratePlugin, migrateFunPlugin, pluginRegistry, predictionMarketPlugin, tokens, useHustle, usePlugins };
16487
+ export { AGENT_HUSTLE_ICON, DEFAULTS, HustleChat, HustleChatWidget, HustleProvider, MarkdownContent, STORAGE_KEYS, availablePlugins, debounce, formatFileSize, getAvailablePlugin, hydratePlugin, migrateFunPlugin, pluginRegistry, predictionMarketPlugin, tokens, useHustle, usePlugins, useSpeechRecognition };
16260
16488
  //# sourceMappingURL=hustle-react.js.map
16261
16489
  //# sourceMappingURL=hustle-react.js.map