@emblemvault/hustle-react 1.4.11 → 1.5.1

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.
@@ -379,6 +379,134 @@ function useHustle() {
379
379
  }
380
380
  return context;
381
381
  }
382
+ function useSpeechRecognition({
383
+ onTranscript,
384
+ onInterimTranscript,
385
+ onError,
386
+ continuous = true,
387
+ language = "en-US"
388
+ } = {}) {
389
+ const [isListening, setIsListening] = react.useState(false);
390
+ const [isSupported, setIsSupported] = react.useState(false);
391
+ const recognitionRef = react.useRef(null);
392
+ const finalTranscriptRef = react.useRef("");
393
+ const onTranscriptRef = react.useRef(onTranscript);
394
+ const onInterimTranscriptRef = react.useRef(onInterimTranscript);
395
+ const onErrorRef = react.useRef(onError);
396
+ react.useEffect(() => {
397
+ onTranscriptRef.current = onTranscript;
398
+ onInterimTranscriptRef.current = onInterimTranscript;
399
+ onErrorRef.current = onError;
400
+ }, [onTranscript, onInterimTranscript, onError]);
401
+ react.useEffect(() => {
402
+ if (typeof window === "undefined") {
403
+ return;
404
+ }
405
+ const SpeechRecognitionAPI = window.SpeechRecognition || window.webkitSpeechRecognition;
406
+ if (!SpeechRecognitionAPI) {
407
+ console.warn("Speech recognition not supported in this browser");
408
+ return;
409
+ }
410
+ setIsSupported(true);
411
+ const recognition = new SpeechRecognitionAPI();
412
+ recognition.continuous = continuous;
413
+ recognition.interimResults = true;
414
+ recognition.lang = language;
415
+ recognition.onstart = () => {
416
+ console.log("Speech recognition started");
417
+ setIsListening(true);
418
+ finalTranscriptRef.current = "";
419
+ };
420
+ recognition.onresult = (event) => {
421
+ let interimTranscript = "";
422
+ let finalTranscript = "";
423
+ const resultIndex = event.resultIndex || 0;
424
+ for (let i = resultIndex; i < event.results.length; i++) {
425
+ const transcript = event.results[i][0].transcript;
426
+ if (event.results[i].isFinal) {
427
+ finalTranscript += transcript;
428
+ } else {
429
+ interimTranscript += transcript;
430
+ }
431
+ }
432
+ if (finalTranscript) {
433
+ finalTranscriptRef.current += finalTranscript;
434
+ onTranscriptRef.current?.(finalTranscript);
435
+ }
436
+ if (interimTranscript) {
437
+ onInterimTranscriptRef.current?.(interimTranscript);
438
+ }
439
+ };
440
+ recognition.onerror = (event) => {
441
+ console.error("Speech recognition error:", event.error);
442
+ if (event.error !== "aborted" && event.error !== "no-speech") {
443
+ onErrorRef.current?.(event.error);
444
+ }
445
+ setIsListening(false);
446
+ };
447
+ recognition.onend = () => {
448
+ console.log("Speech recognition ended");
449
+ setIsListening(false);
450
+ };
451
+ recognitionRef.current = recognition;
452
+ return () => {
453
+ if (recognitionRef.current) {
454
+ try {
455
+ recognitionRef.current.abort();
456
+ } catch (e) {
457
+ }
458
+ }
459
+ };
460
+ }, [continuous, language]);
461
+ const startListening = react.useCallback(() => {
462
+ const recognition = recognitionRef.current;
463
+ if (!recognition) {
464
+ console.error("Speech recognition not available");
465
+ return;
466
+ }
467
+ if (isListening) {
468
+ return;
469
+ }
470
+ console.log("Starting speech recognition...");
471
+ try {
472
+ finalTranscriptRef.current = "";
473
+ recognition.start();
474
+ } catch (error2) {
475
+ console.error("Error starting:", error2);
476
+ onErrorRef.current?.("Failed to start speech recognition");
477
+ }
478
+ }, [isListening]);
479
+ const stopListening = react.useCallback(() => {
480
+ const recognition = recognitionRef.current;
481
+ if (!recognition) {
482
+ return;
483
+ }
484
+ if (!isListening) {
485
+ return;
486
+ }
487
+ console.log("Stopping speech recognition...");
488
+ try {
489
+ recognition.stop();
490
+ } catch (error2) {
491
+ console.warn("Error stopping:", error2);
492
+ setIsListening(false);
493
+ }
494
+ }, [isListening]);
495
+ const toggleListening = react.useCallback(() => {
496
+ if (isListening) {
497
+ stopListening();
498
+ } else {
499
+ startListening();
500
+ }
501
+ }, [isListening, startListening, stopListening]);
502
+ return {
503
+ isSupported,
504
+ isListening,
505
+ startListening,
506
+ stopListening,
507
+ toggleListening
508
+ };
509
+ }
382
510
 
383
511
  // src/plugins/predictionMarket.ts
384
512
  var DOME_API_BASE = "https://api.domeapi.io/v1";
@@ -3366,6 +3494,28 @@ var styles = {
3366
3494
  borderRadius: tokens.radius.full,
3367
3495
  animation: "hustle-spin 0.8s linear infinite"
3368
3496
  },
3497
+ // Mic button
3498
+ micBtn: {
3499
+ width: "40px",
3500
+ height: "40px",
3501
+ padding: 0,
3502
+ background: tokens.colors.bgTertiary,
3503
+ border: `1px solid ${tokens.colors.borderSecondary}`,
3504
+ borderRadius: tokens.radius.lg,
3505
+ color: tokens.colors.textSecondary,
3506
+ flexShrink: 0,
3507
+ display: "flex",
3508
+ alignItems: "center",
3509
+ justifyContent: "center",
3510
+ cursor: "pointer",
3511
+ transition: `all ${tokens.transitions.fast}`
3512
+ },
3513
+ micBtnListening: {
3514
+ background: "rgba(239, 68, 68, 0.1)",
3515
+ borderColor: "rgba(239, 68, 68, 0.3)",
3516
+ color: "#ef4444",
3517
+ animation: "hustle-pulse 1s ease-in-out infinite"
3518
+ },
3369
3519
  // Error display
3370
3520
  errorBox: {
3371
3521
  marginTop: tokens.spacing.sm,
@@ -3536,9 +3686,12 @@ function HustleChat({
3536
3686
  className = "",
3537
3687
  placeholder = "Type a message...",
3538
3688
  showSettings = false,
3689
+ settingsPanelOpen: controlledSettingsPanelOpen,
3690
+ onSettingsPanelOpenChange,
3539
3691
  showDebug = false,
3540
3692
  hideHeader = false,
3541
3693
  initialSystemPrompt = "",
3694
+ enableSpeechToText = false,
3542
3695
  onMessage,
3543
3696
  onToolCall,
3544
3697
  onResponse
@@ -3574,7 +3727,41 @@ function HustleChat({
3574
3727
  const [isStreaming, setIsStreaming] = react.useState(false);
3575
3728
  const [attachments, setAttachments] = react.useState([]);
3576
3729
  const [currentToolCalls, setCurrentToolCalls] = react.useState([]);
3577
- const [showSettingsPanel, setShowSettingsPanel] = react.useState(false);
3730
+ const [internalShowSettingsPanel, setInternalShowSettingsPanel] = react.useState(false);
3731
+ const isSettingsControlled = controlledSettingsPanelOpen !== void 0;
3732
+ const showSettingsPanel = isSettingsControlled ? controlledSettingsPanelOpen : internalShowSettingsPanel;
3733
+ const setShowSettingsPanel = (open) => {
3734
+ const newValue = typeof open === "function" ? open(showSettingsPanel) : open;
3735
+ if (isSettingsControlled) {
3736
+ onSettingsPanelOpenChange?.(newValue);
3737
+ } else {
3738
+ setInternalShowSettingsPanel(newValue);
3739
+ }
3740
+ };
3741
+ const [speechToTextEnabled, setSpeechToTextEnabled] = react.useState(() => {
3742
+ if (typeof window !== "undefined") {
3743
+ const stored = localStorage.getItem(`hustle-stt-enabled-${instanceId}`);
3744
+ return stored === "true";
3745
+ }
3746
+ return false;
3747
+ });
3748
+ react.useEffect(() => {
3749
+ if (typeof window !== "undefined") {
3750
+ localStorage.setItem(`hustle-stt-enabled-${instanceId}`, String(speechToTextEnabled));
3751
+ }
3752
+ }, [speechToTextEnabled, instanceId]);
3753
+ const {
3754
+ isSupported: isSpeechSupported,
3755
+ isListening,
3756
+ toggleListening
3757
+ } = useSpeechRecognition({
3758
+ onTranscript: react.useCallback((transcript) => {
3759
+ setInputValue((prev) => prev + (prev ? " " : "") + transcript);
3760
+ }, []),
3761
+ onError: react.useCallback((error3) => {
3762
+ console.error("Speech recognition error:", error3);
3763
+ }, [])
3764
+ });
3578
3765
  const messagesEndRef = react.useRef(null);
3579
3766
  const fileInputRef = react.useRef(null);
3580
3767
  const messagesRef = react.useRef(messages);
@@ -4043,6 +4230,27 @@ function HustleChat({
4043
4230
  }
4044
4231
  )
4045
4232
  ] }),
4233
+ isSpeechSupported && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.settingGroup, children: [
4234
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles.settingLabel, children: "Voice Input" }),
4235
+ /* @__PURE__ */ jsxRuntime.jsxs(
4236
+ "div",
4237
+ {
4238
+ style: styles.toggleRow,
4239
+ onClick: () => setSpeechToTextEnabled(!speechToTextEnabled),
4240
+ children: [
4241
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.toggleLabel, children: "Enable speech-to-text" }),
4242
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4243
+ ...styles.toggleSwitch,
4244
+ ...speechToTextEnabled ? styles.toggleSwitchActive : {}
4245
+ }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4246
+ ...styles.toggleKnob,
4247
+ ...speechToTextEnabled ? styles.toggleKnobActive : {}
4248
+ } }) })
4249
+ ]
4250
+ }
4251
+ ),
4252
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles.settingDescription, children: "Use your microphone to dictate messages" })
4253
+ ] }),
4046
4254
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.settingDivider }),
4047
4255
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...styles.settingGroup, marginBottom: 0 }, children: [
4048
4256
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles.settingLabel, children: "Plugins" }),
@@ -4179,6 +4387,21 @@ function HustleChat({
4179
4387
  }
4180
4388
  ) })
4181
4389
  ] }),
4390
+ (enableSpeechToText || speechToTextEnabled) && isSpeechSupported && /* @__PURE__ */ jsxRuntime.jsx(
4391
+ "button",
4392
+ {
4393
+ type: "button",
4394
+ onClick: toggleListening,
4395
+ disabled: !canChat || isStreaming || isLoading,
4396
+ style: {
4397
+ ...styles.micBtn,
4398
+ ...isListening ? styles.micBtnListening : {},
4399
+ ...!canChat || isStreaming || isLoading ? { opacity: 0.5, cursor: "not-allowed" } : {}
4400
+ },
4401
+ title: isListening ? "Stop listening" : "Start voice input",
4402
+ children: isListening ? /* @__PURE__ */ jsxRuntime.jsx(MicOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(MicIcon, {})
4403
+ }
4404
+ ),
4182
4405
  /* @__PURE__ */ jsxRuntime.jsx(
4183
4406
  "button",
4184
4407
  {
@@ -4238,6 +4461,23 @@ function SettingsIcon() {
4238
4461
  function AttachIcon() {
4239
4462
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsxRuntime.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" }) });
4240
4463
  }
4464
+ function MicIcon() {
4465
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
4466
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
4467
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
4468
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
4469
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
4470
+ ] });
4471
+ }
4472
+ function MicOffIcon() {
4473
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
4474
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
4475
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" }),
4476
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23" }),
4477
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
4478
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
4479
+ ] });
4480
+ }
4241
4481
 
4242
4482
  // src/constants/index.ts
4243
4483
  var AGENT_HUSTLE_ICON = "data:image/jpeg;base64,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";
@@ -4370,6 +4610,12 @@ function CloseIcon() {
4370
4610
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
4371
4611
  ] });
4372
4612
  }
4613
+ function SettingsIcon2() {
4614
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
4615
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" }),
4616
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" })
4617
+ ] });
4618
+ }
4373
4619
  function ExpandIcon() {
4374
4620
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4375
4621
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
@@ -4418,6 +4664,8 @@ function HustleChatWidget({
4418
4664
  const [isHovered, setIsHovered] = react.useState(false);
4419
4665
  const [closeHovered, setCloseHovered] = react.useState(false);
4420
4666
  const [mounted, setMounted] = react.useState(false);
4667
+ const [settingsOpen, setSettingsOpen] = react.useState(false);
4668
+ const { showSettings, ...restChatProps } = chatProps;
4421
4669
  react.useEffect(() => {
4422
4670
  setMounted(true);
4423
4671
  if (storageKey && typeof window !== "undefined") {
@@ -4527,6 +4775,18 @@ function HustleChatWidget({
4527
4775
  ] })
4528
4776
  ] }),
4529
4777
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: tokens.spacing.xs }, children: [
4778
+ showSettings && /* @__PURE__ */ jsxRuntime.jsx(
4779
+ "button",
4780
+ {
4781
+ onClick: () => setSettingsOpen(!settingsOpen),
4782
+ style: {
4783
+ ...widgetStyles.closeBtn,
4784
+ ...settingsOpen ? { background: tokens.colors.bgSecondary } : {}
4785
+ },
4786
+ title: "Settings",
4787
+ children: /* @__PURE__ */ jsxRuntime.jsx(SettingsIcon2, {})
4788
+ }
4789
+ ),
4530
4790
  size === "sideDock" && /* @__PURE__ */ jsxRuntime.jsx(
4531
4791
  "button",
4532
4792
  {
@@ -4561,7 +4821,15 @@ function HustleChatWidget({
4561
4821
  ]
4562
4822
  }
4563
4823
  ),
4564
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(HustleChatInner, { ...chatProps }) })
4824
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(
4825
+ HustleChatInner,
4826
+ {
4827
+ ...restChatProps,
4828
+ showSettings,
4829
+ settingsPanelOpen: settingsOpen,
4830
+ onSettingsPanelOpenChange: setSettingsOpen
4831
+ }
4832
+ ) })
4565
4833
  ]
4566
4834
  }
4567
4835
  ),