@emblemvault/hustle-react 1.4.11 → 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.
@@ -19,6 +19,8 @@ interface HustleChatProps {
19
19
  hideHeader?: boolean;
20
20
  /** Initial system prompt */
21
21
  initialSystemPrompt?: string;
22
+ /** Enable speech-to-text input via microphone (default: false) */
23
+ enableSpeechToText?: boolean;
22
24
  /** Callback when message is sent */
23
25
  onMessage?: (message: ChatMessage) => void;
24
26
  /** Callback when tool is called */
@@ -43,7 +45,7 @@ interface HustleChatProps {
43
45
  * />
44
46
  * ```
45
47
  */
46
- declare function HustleChat({ className, placeholder, showSettings, showDebug, hideHeader, initialSystemPrompt, onMessage, onToolCall, onResponse, }: HustleChatProps): react_jsx_runtime.JSX.Element;
48
+ declare function HustleChat({ className, placeholder, showSettings, showDebug, hideHeader, initialSystemPrompt, enableSpeechToText, onMessage, onToolCall, onResponse, }: HustleChatProps): react_jsx_runtime.JSX.Element;
47
49
 
48
50
  /**
49
51
  * Position options for the widget
@@ -19,6 +19,8 @@ interface HustleChatProps {
19
19
  hideHeader?: boolean;
20
20
  /** Initial system prompt */
21
21
  initialSystemPrompt?: string;
22
+ /** Enable speech-to-text input via microphone (default: false) */
23
+ enableSpeechToText?: boolean;
22
24
  /** Callback when message is sent */
23
25
  onMessage?: (message: ChatMessage) => void;
24
26
  /** Callback when tool is called */
@@ -43,7 +45,7 @@ interface HustleChatProps {
43
45
  * />
44
46
  * ```
45
47
  */
46
- declare function HustleChat({ className, placeholder, showSettings, showDebug, hideHeader, initialSystemPrompt, onMessage, onToolCall, onResponse, }: HustleChatProps): react_jsx_runtime.JSX.Element;
48
+ declare function HustleChat({ className, placeholder, showSettings, showDebug, hideHeader, initialSystemPrompt, enableSpeechToText, onMessage, onToolCall, onResponse, }: HustleChatProps): react_jsx_runtime.JSX.Element;
47
49
 
48
50
  /**
49
51
  * Position options for the widget
@@ -353,6 +353,134 @@ function useHustle() {
353
353
  }
354
354
  return context;
355
355
  }
356
+ function useSpeechRecognition({
357
+ onTranscript,
358
+ onInterimTranscript,
359
+ onError,
360
+ continuous = true,
361
+ language = "en-US"
362
+ } = {}) {
363
+ const [isListening, setIsListening] = useState(false);
364
+ const [isSupported, setIsSupported] = useState(false);
365
+ const recognitionRef = useRef(null);
366
+ const finalTranscriptRef = useRef("");
367
+ const onTranscriptRef = useRef(onTranscript);
368
+ const onInterimTranscriptRef = useRef(onInterimTranscript);
369
+ const onErrorRef = useRef(onError);
370
+ useEffect(() => {
371
+ onTranscriptRef.current = onTranscript;
372
+ onInterimTranscriptRef.current = onInterimTranscript;
373
+ onErrorRef.current = onError;
374
+ }, [onTranscript, onInterimTranscript, onError]);
375
+ useEffect(() => {
376
+ if (typeof window === "undefined") {
377
+ return;
378
+ }
379
+ const SpeechRecognitionAPI = window.SpeechRecognition || window.webkitSpeechRecognition;
380
+ if (!SpeechRecognitionAPI) {
381
+ console.warn("Speech recognition not supported in this browser");
382
+ return;
383
+ }
384
+ setIsSupported(true);
385
+ const recognition = new SpeechRecognitionAPI();
386
+ recognition.continuous = continuous;
387
+ recognition.interimResults = true;
388
+ recognition.lang = language;
389
+ recognition.onstart = () => {
390
+ console.log("Speech recognition started");
391
+ setIsListening(true);
392
+ finalTranscriptRef.current = "";
393
+ };
394
+ recognition.onresult = (event) => {
395
+ let interimTranscript = "";
396
+ let finalTranscript = "";
397
+ const resultIndex = event.resultIndex || 0;
398
+ for (let i = resultIndex; i < event.results.length; i++) {
399
+ const transcript = event.results[i][0].transcript;
400
+ if (event.results[i].isFinal) {
401
+ finalTranscript += transcript;
402
+ } else {
403
+ interimTranscript += transcript;
404
+ }
405
+ }
406
+ if (finalTranscript) {
407
+ finalTranscriptRef.current += finalTranscript;
408
+ onTranscriptRef.current?.(finalTranscript);
409
+ }
410
+ if (interimTranscript) {
411
+ onInterimTranscriptRef.current?.(interimTranscript);
412
+ }
413
+ };
414
+ recognition.onerror = (event) => {
415
+ console.error("Speech recognition error:", event.error);
416
+ if (event.error !== "aborted" && event.error !== "no-speech") {
417
+ onErrorRef.current?.(event.error);
418
+ }
419
+ setIsListening(false);
420
+ };
421
+ recognition.onend = () => {
422
+ console.log("Speech recognition ended");
423
+ setIsListening(false);
424
+ };
425
+ recognitionRef.current = recognition;
426
+ return () => {
427
+ if (recognitionRef.current) {
428
+ try {
429
+ recognitionRef.current.abort();
430
+ } catch (e) {
431
+ }
432
+ }
433
+ };
434
+ }, [continuous, language]);
435
+ const startListening = useCallback(() => {
436
+ const recognition = recognitionRef.current;
437
+ if (!recognition) {
438
+ console.error("Speech recognition not available");
439
+ return;
440
+ }
441
+ if (isListening) {
442
+ return;
443
+ }
444
+ console.log("Starting speech recognition...");
445
+ try {
446
+ finalTranscriptRef.current = "";
447
+ recognition.start();
448
+ } catch (error2) {
449
+ console.error("Error starting:", error2);
450
+ onErrorRef.current?.("Failed to start speech recognition");
451
+ }
452
+ }, [isListening]);
453
+ const stopListening = useCallback(() => {
454
+ const recognition = recognitionRef.current;
455
+ if (!recognition) {
456
+ return;
457
+ }
458
+ if (!isListening) {
459
+ return;
460
+ }
461
+ console.log("Stopping speech recognition...");
462
+ try {
463
+ recognition.stop();
464
+ } catch (error2) {
465
+ console.warn("Error stopping:", error2);
466
+ setIsListening(false);
467
+ }
468
+ }, [isListening]);
469
+ const toggleListening = useCallback(() => {
470
+ if (isListening) {
471
+ stopListening();
472
+ } else {
473
+ startListening();
474
+ }
475
+ }, [isListening, startListening, stopListening]);
476
+ return {
477
+ isSupported,
478
+ isListening,
479
+ startListening,
480
+ stopListening,
481
+ toggleListening
482
+ };
483
+ }
356
484
 
357
485
  // src/plugins/predictionMarket.ts
358
486
  var DOME_API_BASE = "https://api.domeapi.io/v1";
@@ -3340,6 +3468,28 @@ var styles = {
3340
3468
  borderRadius: tokens.radius.full,
3341
3469
  animation: "hustle-spin 0.8s linear infinite"
3342
3470
  },
3471
+ // Mic button
3472
+ micBtn: {
3473
+ width: "40px",
3474
+ height: "40px",
3475
+ padding: 0,
3476
+ background: tokens.colors.bgTertiary,
3477
+ border: `1px solid ${tokens.colors.borderSecondary}`,
3478
+ borderRadius: tokens.radius.lg,
3479
+ color: tokens.colors.textSecondary,
3480
+ flexShrink: 0,
3481
+ display: "flex",
3482
+ alignItems: "center",
3483
+ justifyContent: "center",
3484
+ cursor: "pointer",
3485
+ transition: `all ${tokens.transitions.fast}`
3486
+ },
3487
+ micBtnListening: {
3488
+ background: "rgba(239, 68, 68, 0.1)",
3489
+ borderColor: "rgba(239, 68, 68, 0.3)",
3490
+ color: "#ef4444",
3491
+ animation: "hustle-pulse 1s ease-in-out infinite"
3492
+ },
3343
3493
  // Error display
3344
3494
  errorBox: {
3345
3495
  marginTop: tokens.spacing.sm,
@@ -3513,6 +3663,7 @@ function HustleChat({
3513
3663
  showDebug = false,
3514
3664
  hideHeader = false,
3515
3665
  initialSystemPrompt = "",
3666
+ enableSpeechToText = false,
3516
3667
  onMessage,
3517
3668
  onToolCall,
3518
3669
  onResponse
@@ -3549,6 +3700,30 @@ function HustleChat({
3549
3700
  const [attachments, setAttachments] = useState([]);
3550
3701
  const [currentToolCalls, setCurrentToolCalls] = useState([]);
3551
3702
  const [showSettingsPanel, setShowSettingsPanel] = useState(false);
3703
+ const [speechToTextEnabled, setSpeechToTextEnabled] = useState(() => {
3704
+ if (typeof window !== "undefined") {
3705
+ const stored = localStorage.getItem(`hustle-stt-enabled-${instanceId}`);
3706
+ return stored === "true";
3707
+ }
3708
+ return false;
3709
+ });
3710
+ useEffect(() => {
3711
+ if (typeof window !== "undefined") {
3712
+ localStorage.setItem(`hustle-stt-enabled-${instanceId}`, String(speechToTextEnabled));
3713
+ }
3714
+ }, [speechToTextEnabled, instanceId]);
3715
+ const {
3716
+ isSupported: isSpeechSupported,
3717
+ isListening,
3718
+ toggleListening
3719
+ } = useSpeechRecognition({
3720
+ onTranscript: useCallback((transcript) => {
3721
+ setInputValue((prev) => prev + (prev ? " " : "") + transcript);
3722
+ }, []),
3723
+ onError: useCallback((error3) => {
3724
+ console.error("Speech recognition error:", error3);
3725
+ }, [])
3726
+ });
3552
3727
  const messagesEndRef = useRef(null);
3553
3728
  const fileInputRef = useRef(null);
3554
3729
  const messagesRef = useRef(messages);
@@ -4017,6 +4192,27 @@ function HustleChat({
4017
4192
  }
4018
4193
  )
4019
4194
  ] }),
4195
+ isSpeechSupported && /* @__PURE__ */ jsxs("div", { style: styles.settingGroup, children: [
4196
+ /* @__PURE__ */ jsx("label", { style: styles.settingLabel, children: "Voice Input" }),
4197
+ /* @__PURE__ */ jsxs(
4198
+ "div",
4199
+ {
4200
+ style: styles.toggleRow,
4201
+ onClick: () => setSpeechToTextEnabled(!speechToTextEnabled),
4202
+ children: [
4203
+ /* @__PURE__ */ jsx("span", { style: styles.toggleLabel, children: "Enable speech-to-text" }),
4204
+ /* @__PURE__ */ jsx("div", { style: {
4205
+ ...styles.toggleSwitch,
4206
+ ...speechToTextEnabled ? styles.toggleSwitchActive : {}
4207
+ }, children: /* @__PURE__ */ jsx("div", { style: {
4208
+ ...styles.toggleKnob,
4209
+ ...speechToTextEnabled ? styles.toggleKnobActive : {}
4210
+ } }) })
4211
+ ]
4212
+ }
4213
+ ),
4214
+ /* @__PURE__ */ jsx("p", { style: styles.settingDescription, children: "Use your microphone to dictate messages" })
4215
+ ] }),
4020
4216
  /* @__PURE__ */ jsx("div", { style: styles.settingDivider }),
4021
4217
  /* @__PURE__ */ jsxs("div", { style: { ...styles.settingGroup, marginBottom: 0 }, children: [
4022
4218
  /* @__PURE__ */ jsx("label", { style: styles.settingLabel, children: "Plugins" }),
@@ -4153,6 +4349,21 @@ function HustleChat({
4153
4349
  }
4154
4350
  ) })
4155
4351
  ] }),
4352
+ (enableSpeechToText || speechToTextEnabled) && isSpeechSupported && /* @__PURE__ */ jsx(
4353
+ "button",
4354
+ {
4355
+ type: "button",
4356
+ onClick: toggleListening,
4357
+ disabled: !canChat || isStreaming || isLoading,
4358
+ style: {
4359
+ ...styles.micBtn,
4360
+ ...isListening ? styles.micBtnListening : {},
4361
+ ...!canChat || isStreaming || isLoading ? { opacity: 0.5, cursor: "not-allowed" } : {}
4362
+ },
4363
+ title: isListening ? "Stop listening" : "Start voice input",
4364
+ children: isListening ? /* @__PURE__ */ jsx(MicOffIcon, {}) : /* @__PURE__ */ jsx(MicIcon, {})
4365
+ }
4366
+ ),
4156
4367
  /* @__PURE__ */ jsx(
4157
4368
  "button",
4158
4369
  {
@@ -4212,6 +4423,23 @@ function SettingsIcon() {
4212
4423
  function AttachIcon() {
4213
4424
  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" }) });
4214
4425
  }
4426
+ function MicIcon() {
4427
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
4428
+ /* @__PURE__ */ jsx("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
4429
+ /* @__PURE__ */ jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
4430
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
4431
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
4432
+ ] });
4433
+ }
4434
+ function MicOffIcon() {
4435
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
4436
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
4437
+ /* @__PURE__ */ jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" }),
4438
+ /* @__PURE__ */ jsx("path", { d: "M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23" }),
4439
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
4440
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
4441
+ ] });
4442
+ }
4215
4443
 
4216
4444
  // src/constants/index.ts
4217
4445
  var AGENT_HUSTLE_ICON = "data:image/jpeg;base64,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";