@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
|
@@ -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,
|
|
@@ -3539,6 +3689,7 @@ function HustleChat({
|
|
|
3539
3689
|
showDebug = false,
|
|
3540
3690
|
hideHeader = false,
|
|
3541
3691
|
initialSystemPrompt = "",
|
|
3692
|
+
enableSpeechToText = false,
|
|
3542
3693
|
onMessage,
|
|
3543
3694
|
onToolCall,
|
|
3544
3695
|
onResponse
|
|
@@ -3575,6 +3726,30 @@ function HustleChat({
|
|
|
3575
3726
|
const [attachments, setAttachments] = react.useState([]);
|
|
3576
3727
|
const [currentToolCalls, setCurrentToolCalls] = react.useState([]);
|
|
3577
3728
|
const [showSettingsPanel, setShowSettingsPanel] = react.useState(false);
|
|
3729
|
+
const [speechToTextEnabled, setSpeechToTextEnabled] = react.useState(() => {
|
|
3730
|
+
if (typeof window !== "undefined") {
|
|
3731
|
+
const stored = localStorage.getItem(`hustle-stt-enabled-${instanceId}`);
|
|
3732
|
+
return stored === "true";
|
|
3733
|
+
}
|
|
3734
|
+
return false;
|
|
3735
|
+
});
|
|
3736
|
+
react.useEffect(() => {
|
|
3737
|
+
if (typeof window !== "undefined") {
|
|
3738
|
+
localStorage.setItem(`hustle-stt-enabled-${instanceId}`, String(speechToTextEnabled));
|
|
3739
|
+
}
|
|
3740
|
+
}, [speechToTextEnabled, instanceId]);
|
|
3741
|
+
const {
|
|
3742
|
+
isSupported: isSpeechSupported,
|
|
3743
|
+
isListening,
|
|
3744
|
+
toggleListening
|
|
3745
|
+
} = useSpeechRecognition({
|
|
3746
|
+
onTranscript: react.useCallback((transcript) => {
|
|
3747
|
+
setInputValue((prev) => prev + (prev ? " " : "") + transcript);
|
|
3748
|
+
}, []),
|
|
3749
|
+
onError: react.useCallback((error3) => {
|
|
3750
|
+
console.error("Speech recognition error:", error3);
|
|
3751
|
+
}, [])
|
|
3752
|
+
});
|
|
3578
3753
|
const messagesEndRef = react.useRef(null);
|
|
3579
3754
|
const fileInputRef = react.useRef(null);
|
|
3580
3755
|
const messagesRef = react.useRef(messages);
|
|
@@ -4043,6 +4218,27 @@ function HustleChat({
|
|
|
4043
4218
|
}
|
|
4044
4219
|
)
|
|
4045
4220
|
] }),
|
|
4221
|
+
isSpeechSupported && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.settingGroup, children: [
|
|
4222
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: styles.settingLabel, children: "Voice Input" }),
|
|
4223
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4224
|
+
"div",
|
|
4225
|
+
{
|
|
4226
|
+
style: styles.toggleRow,
|
|
4227
|
+
onClick: () => setSpeechToTextEnabled(!speechToTextEnabled),
|
|
4228
|
+
children: [
|
|
4229
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.toggleLabel, children: "Enable speech-to-text" }),
|
|
4230
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: {
|
|
4231
|
+
...styles.toggleSwitch,
|
|
4232
|
+
...speechToTextEnabled ? styles.toggleSwitchActive : {}
|
|
4233
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
|
|
4234
|
+
...styles.toggleKnob,
|
|
4235
|
+
...speechToTextEnabled ? styles.toggleKnobActive : {}
|
|
4236
|
+
} }) })
|
|
4237
|
+
]
|
|
4238
|
+
}
|
|
4239
|
+
),
|
|
4240
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: styles.settingDescription, children: "Use your microphone to dictate messages" })
|
|
4241
|
+
] }),
|
|
4046
4242
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.settingDivider }),
|
|
4047
4243
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...styles.settingGroup, marginBottom: 0 }, children: [
|
|
4048
4244
|
/* @__PURE__ */ jsxRuntime.jsx("label", { style: styles.settingLabel, children: "Plugins" }),
|
|
@@ -4179,6 +4375,21 @@ function HustleChat({
|
|
|
4179
4375
|
}
|
|
4180
4376
|
) })
|
|
4181
4377
|
] }),
|
|
4378
|
+
(enableSpeechToText || speechToTextEnabled) && isSpeechSupported && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4379
|
+
"button",
|
|
4380
|
+
{
|
|
4381
|
+
type: "button",
|
|
4382
|
+
onClick: toggleListening,
|
|
4383
|
+
disabled: !canChat || isStreaming || isLoading,
|
|
4384
|
+
style: {
|
|
4385
|
+
...styles.micBtn,
|
|
4386
|
+
...isListening ? styles.micBtnListening : {},
|
|
4387
|
+
...!canChat || isStreaming || isLoading ? { opacity: 0.5, cursor: "not-allowed" } : {}
|
|
4388
|
+
},
|
|
4389
|
+
title: isListening ? "Stop listening" : "Start voice input",
|
|
4390
|
+
children: isListening ? /* @__PURE__ */ jsxRuntime.jsx(MicOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(MicIcon, {})
|
|
4391
|
+
}
|
|
4392
|
+
),
|
|
4182
4393
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4183
4394
|
"button",
|
|
4184
4395
|
{
|
|
@@ -4238,6 +4449,23 @@ function SettingsIcon() {
|
|
|
4238
4449
|
function AttachIcon() {
|
|
4239
4450
|
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
4451
|
}
|
|
4452
|
+
function MicIcon() {
|
|
4453
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
4454
|
+
/* @__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" }),
|
|
4455
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
4456
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
|
|
4457
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
|
|
4458
|
+
] });
|
|
4459
|
+
}
|
|
4460
|
+
function MicOffIcon() {
|
|
4461
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
4462
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
|
|
4463
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" }),
|
|
4464
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23" }),
|
|
4465
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
|
|
4466
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
|
|
4467
|
+
] });
|
|
4468
|
+
}
|
|
4241
4469
|
|
|
4242
4470
|
// src/constants/index.ts
|
|
4243
4471
|
var AGENT_HUSTLE_ICON = "data:image/jpeg;base64,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";
|