@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.
- package/README.md +54 -0
- package/dist/browser/hustle-react.js +253 -25
- package/dist/browser/hustle-react.js.map +1 -1
- package/dist/components/index.cjs +228 -0
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +3 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.js +228 -0
- package/dist/components/index.js.map +1 -1
- package/dist/hooks/index.cjs +129 -0
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +82 -1
- package/dist/hooks/index.d.ts +82 -1
- package/dist/hooks/index.js +129 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +229 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +229 -1
- package/dist/index.js.map +1 -1
- package/package.json +11 -12
|
@@ -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
|
package/dist/components/index.js
CHANGED
|
@@ -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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";
|