@peers-app/peers-ui 0.21.7 → 0.23.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.
Files changed (69) hide show
  1. package/dist/components/access-level-field.d.ts +10 -0
  2. package/dist/components/access-level-field.js +28 -0
  3. package/dist/components/access-level-field.test.d.ts +1 -0
  4. package/dist/components/access-level-field.test.js +28 -0
  5. package/dist/components/message-logs/message-logs.d.ts +2 -1
  6. package/dist/components/message-logs/message-logs.js +44 -2
  7. package/dist/components/message-logs/tool-access-approval-card.d.ts +13 -0
  8. package/dist/components/message-logs/tool-access-approval-card.js +81 -0
  9. package/dist/components/message-logs/tool-access-approval-card.test.d.ts +1 -0
  10. package/dist/components/message-logs/tool-access-approval-card.test.js +102 -0
  11. package/dist/index.d.ts +0 -2
  12. package/dist/index.js +0 -2
  13. package/dist/screens/assistants/assistant-info.d.ts +1 -0
  14. package/dist/screens/assistants/assistant-info.js +3 -1
  15. package/dist/screens/assistants/assistant-list.d.ts +1 -0
  16. package/dist/screens/assistants/assistant-list.js +8 -1
  17. package/dist/screens/packages/package-versions.js +1 -1
  18. package/dist/screens/settings/settings-page.js +0 -5
  19. package/dist/screens/tools/tool-access-ipc.d.ts +5 -0
  20. package/dist/screens/tools/tool-access-ipc.js +65 -0
  21. package/dist/screens/tools/tool-access-ipc.test.d.ts +1 -0
  22. package/dist/screens/tools/tool-access-ipc.test.js +40 -0
  23. package/dist/screens/tools/tool-info.d.ts +1 -0
  24. package/dist/screens/tools/tool-info.js +3 -1
  25. package/package.json +3 -3
  26. package/src/components/access-level-field.test.tsx +32 -0
  27. package/src/components/access-level-field.tsx +68 -0
  28. package/src/components/message-logs/message-logs.tsx +58 -1
  29. package/src/components/message-logs/tool-access-approval-card.test.tsx +127 -0
  30. package/src/components/message-logs/tool-access-approval-card.tsx +178 -0
  31. package/src/index.tsx +0 -2
  32. package/src/screens/assistants/assistant-info.tsx +4 -0
  33. package/src/screens/assistants/assistant-list.tsx +29 -2
  34. package/src/screens/packages/package-versions.tsx +1 -0
  35. package/src/screens/settings/settings-page.tsx +0 -5
  36. package/src/screens/tools/tool-access-ipc.test.ts +55 -0
  37. package/src/screens/tools/tool-access-ipc.ts +88 -0
  38. package/src/screens/tools/tool-info.tsx +4 -0
  39. package/tsconfig.json +1 -0
  40. package/dist/components/voice-indicator.d.ts +0 -15
  41. package/dist/components/voice-indicator.js +0 -276
  42. package/dist/components/voice-playback.d.ts +0 -15
  43. package/dist/components/voice-playback.js +0 -86
  44. package/dist/components/voice-subscribe-events.d.ts +0 -28
  45. package/dist/components/voice-subscribe-events.js +0 -2
  46. package/dist/screens/settings/voice-settings-agent.d.ts +0 -2
  47. package/dist/screens/settings/voice-settings-agent.js +0 -39
  48. package/dist/screens/settings/voice-settings-api-keys.d.ts +0 -13
  49. package/dist/screens/settings/voice-settings-api-keys.js +0 -8
  50. package/dist/screens/settings/voice-settings-output.d.ts +0 -16
  51. package/dist/screens/settings/voice-settings-output.js +0 -7
  52. package/dist/screens/settings/voice-settings-providers.d.ts +0 -9
  53. package/dist/screens/settings/voice-settings-providers.js +0 -6
  54. package/dist/screens/settings/voice-settings-types.d.ts +0 -33
  55. package/dist/screens/settings/voice-settings-types.js +0 -55
  56. package/dist/screens/settings/voice-settings-wake-word.d.ts +0 -9
  57. package/dist/screens/settings/voice-settings-wake-word.js +0 -8
  58. package/dist/screens/settings/voice-settings.d.ts +0 -10
  59. package/dist/screens/settings/voice-settings.js +0 -173
  60. package/src/components/voice-indicator.tsx +0 -353
  61. package/src/components/voice-playback.tsx +0 -97
  62. package/src/components/voice-subscribe-events.ts +0 -16
  63. package/src/screens/settings/voice-settings-agent.tsx +0 -73
  64. package/src/screens/settings/voice-settings-api-keys.tsx +0 -91
  65. package/src/screens/settings/voice-settings-output.tsx +0 -126
  66. package/src/screens/settings/voice-settings-providers.tsx +0 -49
  67. package/src/screens/settings/voice-settings-types.ts +0 -94
  68. package/src/screens/settings/voice-settings-wake-word.tsx +0 -94
  69. package/src/screens/settings/voice-settings.tsx +0 -326
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VoiceSettingsApiKeys = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const tooltip_1 = require("../../components/tooltip");
6
- const voice_settings_types_1 = require("./voice-settings-types");
7
- const VoiceSettingsApiKeys = ({ settings, picovoiceKey, openaiKey, keyError, onPicovoiceKeyChange, onOpenaiKeyChange, onDismissKeyError, }) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("label", { className: "form-label", children: ["Picovoice Access Key", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Required for wake word detection. Get a free key at [console.picovoice.ai](https://console.picovoice.ai)" }), settings.picovoiceAccessKeySet && (0, jsx_runtime_1.jsx)("span", { className: "badge bg-success ms-2", children: "Set" })] }), (0, jsx_runtime_1.jsx)("input", { type: "password", className: "form-control", value: picovoiceKey, onChange: (e) => onPicovoiceKeyChange(e.target.value), placeholder: settings.picovoiceAccessKeySet ? "••••••••••••••••" : "Enter your Picovoice access key" }), (0, jsx_runtime_1.jsxs)("small", { className: "text-muted", children: [(0, jsx_runtime_1.jsx)("a", { href: "https://console.picovoice.ai", target: "_blank", rel: "noopener noreferrer", children: "Get a free access key" }), settings.picovoiceAccessKeySet && " (leave blank to keep existing key)"] })] }), keyError && ((0, jsx_runtime_1.jsxs)("div", { className: "alert alert-warning alert-dismissible mb-4", role: "alert", children: [(0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-start gap-2", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-exclamation-triangle-fill flex-shrink-0 mt-1" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("strong", { children: voice_settings_types_1.PICOVOICE_KEY_ERROR_MESSAGES[keyError].heading }), (0, jsx_runtime_1.jsx)("div", { className: "mt-1", children: voice_settings_types_1.PICOVOICE_KEY_ERROR_MESSAGES[keyError].body }), (0, jsx_runtime_1.jsx)("a", { href: "https://console.picovoice.ai", target: "_blank", rel: "noopener noreferrer", className: "alert-link mt-1 d-inline-block", children: "Open console.picovoice.ai" })] })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn-close", onClick: onDismissKeyError })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mb-3", children: [(0, jsx_runtime_1.jsxs)("label", { className: "form-label", children: ["OpenAI API Key", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Required for cloud STT (Whisper) and cloud TTS. Leave empty to use local/browser alternatives." }), settings.openaiApiKeySet && (0, jsx_runtime_1.jsx)("span", { className: "badge bg-success ms-2", children: "Set" })] }), (0, jsx_runtime_1.jsx)("input", { type: "password", className: "form-control", value: openaiKey, onChange: (e) => onOpenaiKeyChange(e.target.value), placeholder: settings.openaiApiKeySet ? "••••••••••••••••" : "sk-..." }), settings.openaiApiKeySet && ((0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: "Leave blank to keep existing key" }))] })] }));
8
- exports.VoiceSettingsApiKeys = VoiceSettingsApiKeys;
@@ -1,16 +0,0 @@
1
- import type React from "react";
2
- import { type VoiceSettings } from "./voice-settings-types";
3
- interface OutputProps {
4
- settings: VoiceSettings;
5
- localSettings: VoiceSettings;
6
- saving: boolean;
7
- testText: string;
8
- hasUnsavedChanges: boolean;
9
- audioDevices: string[];
10
- onChange: (patch: Partial<VoiceSettings>) => void;
11
- onTestTextChange: (text: string) => void;
12
- onTestTTS: () => void;
13
- onTestRecording: () => void;
14
- }
15
- export declare const VoiceSettingsOutput: React.FC<OutputProps>;
16
- export {};
@@ -1,7 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VoiceSettingsOutput = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const voice_settings_types_1 = require("./voice-settings-types");
6
- const VoiceSettingsOutput = ({ settings, localSettings, saving, testText, hasUnsavedChanges, audioDevices, onChange, onTestTextChange, onTestTTS, onTestRecording, }) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [localSettings.ttsProvider === "cloud" && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-3", children: [(0, jsx_runtime_1.jsx)("label", { className: "form-label", children: "Voice" }), (0, jsx_runtime_1.jsx)("select", { className: "form-select", value: localSettings.ttsVoice || "nova", onChange: (e) => onChange({ ttsVoice: e.target.value }), disabled: saving, children: voice_settings_types_1.OPENAI_TTS_VOICES.map((voice) => ((0, jsx_runtime_1.jsxs)("option", { value: voice.id, children: [voice.name, " - ", voice.description] }, voice.id))) })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("label", { className: "form-label", children: ["Speaking Speed: ", localSettings.ttsSpeed.toFixed(1), "x"] }), (0, jsx_runtime_1.jsx)("input", { type: "range", className: "form-range", min: "0.5", max: "2", step: "0.1", value: localSettings.ttsSpeed, onChange: (e) => onChange({ ttsSpeed: parseFloat(e.target.value) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between", children: [(0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: "Slower" }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: "Faster" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: "form-label", children: "Test TTS" }), (0, jsx_runtime_1.jsxs)("div", { className: "input-group", children: [(0, jsx_runtime_1.jsx)("input", { type: "text", className: "form-control", value: testText, onChange: (e) => onTestTextChange(e.target.value), placeholder: "Enter text to speak" }), (0, jsx_runtime_1.jsxs)("button", { className: "btn btn-outline-primary", type: "button", onClick: onTestTTS, disabled: !settings.enabled && localSettings.ttsProvider === "cloud" && !settings.openaiApiKeySet, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-play-fill me-1" }), "Speak"] })] }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: hasUnsavedChanges && "Save changes first to test with new settings" })] }), (0, jsx_runtime_1.jsx)("hr", { className: "my-4" }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("h6", { className: "mb-3", children: "Test Voice Input" }), (0, jsx_runtime_1.jsx)("p", { className: "text-muted small", children: "Click the button below to test voice recording. Speak and it will stop when silence is detected." }), (0, jsx_runtime_1.jsxs)("button", { className: "btn btn-primary", onClick: onTestRecording, disabled: !settings.enabled || saving, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-mic me-2" }), "Test Recording"] }), !settings.enabled && ((0, jsx_runtime_1.jsx)("small", { className: "text-muted ms-2", children: "Enable and save voice settings first" }))] }), audioDevices.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("h6", { className: "mb-3", children: "Available Audio Devices" }), (0, jsx_runtime_1.jsx)("ul", { className: "list-group list-group-flush", children: audioDevices.map((device, index) => ((0, jsx_runtime_1.jsx)("li", { className: "list-group-item bg-transparent", children: (0, jsx_runtime_1.jsx)("small", { children: device }) }, index))) })] }))] }));
7
- exports.VoiceSettingsOutput = VoiceSettingsOutput;
@@ -1,9 +0,0 @@
1
- import type React from "react";
2
- import type { VoiceSettings } from "./voice-settings-types";
3
- interface ProvidersProps {
4
- localSettings: VoiceSettings;
5
- saving: boolean;
6
- onChange: (patch: Partial<VoiceSettings>) => void;
7
- }
8
- export declare const VoiceSettingsProviders: React.FC<ProvidersProps>;
9
- export {};
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VoiceSettingsProviders = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const VoiceSettingsProviders = ({ localSettings, saving, onChange, }) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h6", { className: "mb-3", children: "Speech to Text (STT)" }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: "form-label", children: "STT Provider" }), (0, jsx_runtime_1.jsxs)("select", { className: "form-select", value: localSettings.sttProvider, onChange: (e) => onChange({ sttProvider: e.target.value }), disabled: saving, children: [(0, jsx_runtime_1.jsx)("option", { value: "auto", children: "Auto (Cloud with local fallback)" }), (0, jsx_runtime_1.jsx)("option", { value: "cloud", children: "Cloud only (OpenAI Whisper)" }), (0, jsx_runtime_1.jsx)("option", { value: "local", children: "Local only (requires setup)" })] })] }), (0, jsx_runtime_1.jsx)("hr", { className: "my-4" }), (0, jsx_runtime_1.jsx)("h6", { className: "mb-3", children: "Text to Speech (TTS)" }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-3", children: [(0, jsx_runtime_1.jsx)("label", { className: "form-label", children: "TTS Provider" }), (0, jsx_runtime_1.jsxs)("select", { className: "form-select", value: localSettings.ttsProvider, onChange: (e) => onChange({ ttsProvider: e.target.value }), disabled: saving, children: [(0, jsx_runtime_1.jsx)("option", { value: "browser", children: "Browser (free, works offline)" }), (0, jsx_runtime_1.jsx)("option", { value: "cloud", children: "Cloud (OpenAI TTS, natural voice)" })] })] })] }));
6
- exports.VoiceSettingsProviders = VoiceSettingsProviders;
@@ -1,33 +0,0 @@
1
- export type STTProvider = "auto" | "cloud" | "local";
2
- export type TTSProvider = "browser" | "cloud";
3
- export type PicovoiceKeyError = "refused" | "invalid" | "limit" | "throttled";
4
- export type BuiltinWakeWord = "ALEXA" | "AMERICANO" | "BLUEBERRY" | "BUMBLEBEE" | "COMPUTER" | "GRAPEFRUIT" | "GRASSHOPPER" | "HEY_GOOGLE" | "HEY_SIRI" | "JARVIS" | "OK_GOOGLE" | "PICOVOICE" | "PORCUPINE" | "TERMINATOR";
5
- export interface VoiceSettingsData {
6
- enabled: boolean;
7
- voiceOutputEnabled?: boolean;
8
- wakeWord: BuiltinWakeWord;
9
- wakeWordSensitivity: number;
10
- speechSensitivity: number;
11
- sttProvider: STTProvider;
12
- ttsProvider: TTSProvider;
13
- ttsVoice?: string;
14
- ttsSpeed: number;
15
- }
16
- export interface VoiceSettings extends VoiceSettingsData {
17
- picovoiceAccessKeySet?: boolean;
18
- openaiApiKeySet?: boolean;
19
- }
20
- export declare const DEFAULT_SETTINGS: VoiceSettingsData;
21
- export declare const BUILTIN_WAKE_WORDS: {
22
- id: BuiltinWakeWord;
23
- name: string;
24
- }[];
25
- export declare const PICOVOICE_KEY_ERROR_MESSAGES: Record<PicovoiceKeyError, {
26
- heading: string;
27
- body: string;
28
- }>;
29
- export declare const OPENAI_TTS_VOICES: {
30
- id: string;
31
- name: string;
32
- description: string;
33
- }[];
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OPENAI_TTS_VOICES = exports.PICOVOICE_KEY_ERROR_MESSAGES = exports.BUILTIN_WAKE_WORDS = exports.DEFAULT_SETTINGS = void 0;
4
- exports.DEFAULT_SETTINGS = {
5
- enabled: false,
6
- voiceOutputEnabled: true,
7
- wakeWord: "COMPUTER",
8
- wakeWordSensitivity: 0.5,
9
- speechSensitivity: 0.7,
10
- sttProvider: "auto",
11
- ttsProvider: "browser",
12
- ttsSpeed: 1.0,
13
- };
14
- exports.BUILTIN_WAKE_WORDS = [
15
- { id: "ALEXA", name: "Alexa" },
16
- { id: "AMERICANO", name: "Americano" },
17
- { id: "BLUEBERRY", name: "Blueberry" },
18
- { id: "BUMBLEBEE", name: "Bumblebee" },
19
- { id: "COMPUTER", name: "Computer" },
20
- { id: "GRAPEFRUIT", name: "Grapefruit" },
21
- { id: "GRASSHOPPER", name: "Grasshopper" },
22
- { id: "HEY_GOOGLE", name: "Hey Google" },
23
- { id: "HEY_SIRI", name: "Hey Siri" },
24
- { id: "JARVIS", name: "Jarvis" },
25
- { id: "OK_GOOGLE", name: "Ok Google" },
26
- { id: "PICOVOICE", name: "Picovoice" },
27
- { id: "PORCUPINE", name: "Porcupine" },
28
- { id: "TERMINATOR", name: "Terminator" },
29
- ];
30
- exports.PICOVOICE_KEY_ERROR_MESSAGES = {
31
- refused: {
32
- heading: "Picovoice access key refused",
33
- body: "Your key was rejected — it may have been revoked or banned. Generate a new key at console.picovoice.ai and paste it below.",
34
- },
35
- invalid: {
36
- heading: "Picovoice access key invalid",
37
- body: "Your key is malformed or expired. Generate a new key at console.picovoice.ai and paste it below.",
38
- },
39
- limit: {
40
- heading: "Picovoice device limit reached",
41
- body: "Your key has been activated on too many devices. Go to console.picovoice.ai to deactivate old devices, generate a new key, or upgrade your plan.",
42
- },
43
- throttled: {
44
- heading: "Picovoice activation throttled",
45
- body: "Too many activation attempts. Please wait a moment, then save your settings again to retry.",
46
- },
47
- };
48
- exports.OPENAI_TTS_VOICES = [
49
- { id: "alloy", name: "Alloy", description: "Neutral and balanced" },
50
- { id: "echo", name: "Echo", description: "Warm and clear" },
51
- { id: "fable", name: "Fable", description: "Expressive and dynamic" },
52
- { id: "onyx", name: "Onyx", description: "Deep and authoritative" },
53
- { id: "nova", name: "Nova", description: "Friendly and upbeat" },
54
- { id: "shimmer", name: "Shimmer", description: "Soft and gentle" },
55
- ];
@@ -1,9 +0,0 @@
1
- import type React from "react";
2
- import { type VoiceSettings } from "./voice-settings-types";
3
- interface WakeWordProps {
4
- localSettings: VoiceSettings;
5
- saving: boolean;
6
- onChange: (patch: Partial<VoiceSettings>) => void;
7
- }
8
- export declare const VoiceSettingsWakeWord: React.FC<WakeWordProps>;
9
- export {};
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VoiceSettingsWakeWord = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const tooltip_1 = require("../../components/tooltip");
6
- const voice_settings_types_1 = require("./voice-settings-types");
7
- const VoiceSettingsWakeWord = ({ localSettings, saving, onChange, }) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("label", { className: "form-label", children: ["Wake Word", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "The phrase you say to activate voice recording. Choose a word that's easy for you to say and unlikely to occur in normal conversation." })] }), (0, jsx_runtime_1.jsx)("select", { className: "form-select", value: localSettings.wakeWord, onChange: (e) => onChange({ wakeWord: e.target.value }), disabled: saving, children: voice_settings_types_1.BUILTIN_WAKE_WORDS.map((word) => ((0, jsx_runtime_1.jsx)("option", { value: word.id, children: word.name }, word.id))) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("label", { className: "form-label", children: ["Wake Word Sensitivity: ", localSettings.wakeWordSensitivity.toFixed(2), (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Higher values make the wake word easier to trigger but may cause false positives." })] }), (0, jsx_runtime_1.jsx)("input", { type: "range", className: "form-range", min: "0", max: "1", step: "0.05", value: localSettings.wakeWordSensitivity, onChange: (e) => onChange({ wakeWordSensitivity: parseFloat(e.target.value) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between", children: [(0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: "Less sensitive" }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: "More sensitive" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("label", { className: "form-label", children: ["Speech Detection: ", (localSettings.speechSensitivity ?? 0.7).toFixed(2), (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Controls how sensitive the microphone is when detecting speech. Higher values pick up quieter speech (better for quiet rooms or a close mic). Lower values require louder speech, which helps filter out background noise." })] }), (0, jsx_runtime_1.jsx)("input", { type: "range", className: "form-range", min: "0", max: "1", step: "0.05", value: localSettings.speechSensitivity ?? 0.7, onChange: (e) => onChange({ speechSensitivity: parseFloat(e.target.value) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between mb-1", children: [(0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: "Loud / Noisy environment" }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted", children: "Quiet / Close mic" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between px-1", children: [(0, jsx_runtime_1.jsx)("small", { className: "text-muted", style: { fontSize: "0.7rem" }, children: "0.2 \u2014 Shout" }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted", style: { fontSize: "0.7rem" }, children: "0.5 \u2014 Normal" }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted", style: { fontSize: "0.7rem" }, children: "0.7 \u2014 Desk" }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted", style: { fontSize: "0.7rem" }, children: "0.9 \u2014 Whisper" })] })] })] }));
8
- exports.VoiceSettingsWakeWord = VoiceSettingsWakeWord;
@@ -1,10 +0,0 @@
1
- /**
2
- * Voice Settings Component
3
- *
4
- * Settings panel for voice input configuration.
5
- * Settings are stored directly via pvars (auto-synced).
6
- * Keys are stored as secure pvars (write-only).
7
- */
8
- import type React from "react";
9
- export declare const VoiceSettings: React.FC;
10
- export default VoiceSettings;
@@ -1,173 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VoiceSettings = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- /**
6
- * Voice Settings Component
7
- *
8
- * Settings panel for voice input configuration.
9
- * Settings are stored directly via pvars (auto-synced).
10
- * Keys are stored as secure pvars (write-only).
11
- */
12
- const peers_sdk_1 = require("@peers-app/peers-sdk");
13
- const react_1 = require("react");
14
- const voice_settings_agent_1 = require("./voice-settings-agent");
15
- const voice_settings_api_keys_1 = require("./voice-settings-api-keys");
16
- const voice_settings_output_1 = require("./voice-settings-output");
17
- const voice_settings_providers_1 = require("./voice-settings-providers");
18
- const voice_settings_types_1 = require("./voice-settings-types");
19
- const voice_settings_wake_word_1 = require("./voice-settings-wake-word");
20
- // Pvars - shared with voice service (writes here trigger service updates)
21
- const voiceSettingsPvar = (0, peers_sdk_1.userVar)("voiceSettings", {
22
- defaultValue: voice_settings_types_1.DEFAULT_SETTINGS,
23
- });
24
- const picovoiceKeyPvar = (0, peers_sdk_1.userVar)("PICOVOICE_ACCESS_KEY", {
25
- isSecret: true,
26
- defaultValue: "",
27
- });
28
- const openaiKeyPvar = (0, peers_sdk_1.userVar)("OPENAI_API_KEY", { isSecret: true, defaultValue: "" });
29
- const VoiceSettings = () => {
30
- const [settings, setSettings] = (0, react_1.useState)({ ...voice_settings_types_1.DEFAULT_SETTINGS });
31
- const [loading, setLoading] = (0, react_1.useState)(true);
32
- const [saving, setSaving] = (0, react_1.useState)(false);
33
- const [error, setError] = (0, react_1.useState)(null);
34
- const [success, setSuccess] = (0, react_1.useState)(null);
35
- const [audioDevices, setAudioDevices] = (0, react_1.useState)([]);
36
- const [keyError, setKeyError] = (0, react_1.useState)(null);
37
- // Local state for unsaved changes
38
- const [localSettings, setLocalSettings] = (0, react_1.useState)(settings);
39
- const [picovoiceKey, setPicovoiceKey] = (0, react_1.useState)("");
40
- const [openaiKey, setOpenaiKey] = (0, react_1.useState)("");
41
- const [hasUnsavedChanges, setHasUnsavedChanges] = (0, react_1.useState)(false);
42
- const [testText, setTestText] = (0, react_1.useState)("Hello, this is a test of the text to speech system.");
43
- // Load initial settings from pvars
44
- (0, react_1.useEffect)(() => {
45
- const loadSettings = async () => {
46
- try {
47
- await voiceSettingsPvar.loadingPromise;
48
- await picovoiceKeyPvar.loadingPromise;
49
- await openaiKeyPvar.loadingPromise;
50
- const savedSettings = voiceSettingsPvar() || voice_settings_types_1.DEFAULT_SETTINGS;
51
- const picovoiceKeyValue = picovoiceKeyPvar();
52
- const openaiKeyValue = openaiKeyPvar();
53
- const loadedSettings = {
54
- ...savedSettings,
55
- picovoiceAccessKeySet: !!picovoiceKeyValue && picovoiceKeyValue !== "",
56
- openaiApiKeySet: !!openaiKeyValue && openaiKeyValue !== "",
57
- };
58
- setSettings(loadedSettings);
59
- setLocalSettings(loadedSettings);
60
- try {
61
- const devices = await peers_sdk_1.rpcServerCalls.voiceGetAudioDevices();
62
- setAudioDevices(devices || []);
63
- }
64
- catch {
65
- // voiceGetAudioDevices not available (e.g. React Native) - leave empty
66
- }
67
- try {
68
- const voiceState = await peers_sdk_1.rpcServerCalls.voiceGetState();
69
- setKeyError(voiceState.keyError ?? null);
70
- }
71
- catch {
72
- // voiceGetState not available - leave null
73
- }
74
- }
75
- catch (err) {
76
- console.error("Failed to load voice settings:", err);
77
- setError("Failed to load voice settings.");
78
- }
79
- finally {
80
- setLoading(false);
81
- }
82
- };
83
- loadSettings();
84
- }, []);
85
- // Track unsaved changes
86
- (0, react_1.useEffect)(() => {
87
- const settingsChanged = JSON.stringify(localSettings) !== JSON.stringify(settings);
88
- const keysEntered = picovoiceKey.length > 0 || openaiKey.length > 0;
89
- setHasUnsavedChanges(settingsChanged || keysEntered);
90
- }, [localSettings, settings, picovoiceKey, openaiKey]);
91
- const handleSaveAll = (0, react_1.useCallback)(async () => {
92
- setSaving(true);
93
- setError(null);
94
- setSuccess(null);
95
- try {
96
- if (picovoiceKey) {
97
- await picovoiceKeyPvar.loadingPromise;
98
- picovoiceKeyPvar(picovoiceKey);
99
- setPicovoiceKey("");
100
- setKeyError(null);
101
- }
102
- if (openaiKey) {
103
- await openaiKeyPvar.loadingPromise;
104
- openaiKeyPvar(openaiKey);
105
- setOpenaiKey("");
106
- }
107
- await voiceSettingsPvar.loadingPromise;
108
- voiceSettingsPvar({
109
- enabled: localSettings.enabled,
110
- voiceOutputEnabled: localSettings.voiceOutputEnabled ?? true,
111
- wakeWord: localSettings.wakeWord,
112
- wakeWordSensitivity: localSettings.wakeWordSensitivity,
113
- speechSensitivity: localSettings.speechSensitivity ?? 0.3,
114
- sttProvider: localSettings.sttProvider,
115
- ttsProvider: localSettings.ttsProvider,
116
- ttsVoice: localSettings.ttsVoice,
117
- ttsSpeed: localSettings.ttsSpeed,
118
- });
119
- const updatedSettings = {
120
- ...localSettings,
121
- picovoiceAccessKeySet: !!picovoiceKeyPvar() || !!picovoiceKey,
122
- openaiApiKeySet: !!openaiKeyPvar() || !!openaiKey,
123
- };
124
- setSettings(updatedSettings);
125
- setLocalSettings(updatedSettings);
126
- setSuccess("Settings saved successfully!");
127
- setTimeout(() => setSuccess(null), 3000);
128
- }
129
- catch (err) {
130
- console.error("Failed to save voice settings:", err);
131
- setError(err.message);
132
- }
133
- finally {
134
- setSaving(false);
135
- }
136
- }, [localSettings, picovoiceKey, openaiKey]);
137
- const handleToggleEnabled = (0, react_1.useCallback)(async () => {
138
- if (!localSettings.picovoiceAccessKeySet && !picovoiceKey && !localSettings.enabled) {
139
- setError("Please enter and save a Picovoice Access Key first");
140
- return;
141
- }
142
- setLocalSettings((prev) => ({ ...prev, enabled: !prev.enabled }));
143
- }, [localSettings.enabled, localSettings.picovoiceAccessKeySet, picovoiceKey]);
144
- const handleTestTTS = (0, react_1.useCallback)(async () => {
145
- try {
146
- setError(null);
147
- await peers_sdk_1.rpcServerCalls.voiceTestTTS(testText);
148
- }
149
- catch (err) {
150
- console.error("TTS test failed:", err);
151
- setError(`TTS test failed: ${err.message}`);
152
- }
153
- }, [testText]);
154
- const handleTestRecording = (0, react_1.useCallback)(async () => {
155
- try {
156
- setError(null);
157
- await peers_sdk_1.rpcServerCalls.voiceStartRecording();
158
- }
159
- catch (err) {
160
- console.error("Recording test failed:", err);
161
- setError(err.message);
162
- }
163
- }, []);
164
- const handleSettingsChange = (0, react_1.useCallback)((patch) => {
165
- setLocalSettings((prev) => ({ ...prev, ...patch }));
166
- }, []);
167
- if (loading) {
168
- return ((0, jsx_runtime_1.jsx)("div", { className: "d-flex justify-content-center p-4", children: (0, jsx_runtime_1.jsx)("div", { className: "spinner-border text-primary", role: "status", children: (0, jsx_runtime_1.jsx)("span", { className: "visually-hidden", children: "Loading..." }) }) }));
169
- }
170
- return ((0, jsx_runtime_1.jsxs)("div", { className: "voice-settings", children: [(0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center mb-3", children: [(0, jsx_runtime_1.jsx)("h5", { className: "mb-0 me-3", children: "Voice Input" }), (0, jsx_runtime_1.jsxs)("div", { className: "form-check form-switch me-auto", children: [(0, jsx_runtime_1.jsx)("input", { className: "form-check-input", type: "checkbox", id: "voiceEnabled", checked: localSettings.enabled, onChange: handleToggleEnabled, disabled: saving }), (0, jsx_runtime_1.jsx)("label", { className: "form-check-label", htmlFor: "voiceEnabled", children: localSettings.enabled ? "Enabled" : "Disabled" })] }), (0, jsx_runtime_1.jsx)("button", { className: "btn btn-primary btn-sm", onClick: handleSaveAll, disabled: saving || !hasUnsavedChanges, children: saving ? "Saving..." : "Save" })] }), localSettings.enabled && ((0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center mb-3 ms-3", children: [(0, jsx_runtime_1.jsx)("h6", { className: "mb-0 me-3 text-muted", children: "Voice Output" }), (0, jsx_runtime_1.jsxs)("div", { className: "form-check form-switch", children: [(0, jsx_runtime_1.jsx)("input", { className: "form-check-input", type: "checkbox", id: "voiceOutputEnabled", checked: localSettings.voiceOutputEnabled !== false, onChange: (e) => handleSettingsChange({ voiceOutputEnabled: e.target.checked }), disabled: saving }), (0, jsx_runtime_1.jsx)("label", { className: "form-check-label", htmlFor: "voiceOutputEnabled", children: localSettings.voiceOutputEnabled !== false ? "On" : "Off" })] }), (0, jsx_runtime_1.jsx)("small", { className: "text-muted ms-2", children: "Speak assistant responses aloud" })] })), error && ((0, jsx_runtime_1.jsxs)("div", { className: "alert alert-danger alert-dismissible fade show", role: "alert", children: [error, (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn-close", onClick: () => setError(null) })] })), success && ((0, jsx_runtime_1.jsxs)("div", { className: "alert alert-success alert-dismissible fade show", role: "alert", children: [success, (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn-close", onClick: () => setSuccess(null) })] })), (0, jsx_runtime_1.jsx)(voice_settings_api_keys_1.VoiceSettingsApiKeys, { settings: settings, picovoiceKey: picovoiceKey, openaiKey: openaiKey, keyError: keyError, onPicovoiceKeyChange: setPicovoiceKey, onOpenaiKeyChange: setOpenaiKey, onDismissKeyError: () => setKeyError(null) }), (0, jsx_runtime_1.jsx)("hr", { className: "my-4" }), (0, jsx_runtime_1.jsx)(voice_settings_wake_word_1.VoiceSettingsWakeWord, { localSettings: localSettings, saving: saving, onChange: handleSettingsChange }), (0, jsx_runtime_1.jsx)("hr", { className: "my-4" }), (0, jsx_runtime_1.jsx)(voice_settings_providers_1.VoiceSettingsProviders, { localSettings: localSettings, saving: saving, onChange: handleSettingsChange }), (0, jsx_runtime_1.jsx)(voice_settings_output_1.VoiceSettingsOutput, { settings: settings, localSettings: localSettings, saving: saving, testText: testText, hasUnsavedChanges: hasUnsavedChanges, audioDevices: audioDevices, onChange: handleSettingsChange, onTestTextChange: setTestText, onTestTTS: handleTestTTS, onTestRecording: handleTestRecording }), (0, jsx_runtime_1.jsx)("hr", { className: "my-4" }), (0, jsx_runtime_1.jsx)(voice_settings_agent_1.VoiceSettingsAgent, {}), (0, jsx_runtime_1.jsxs)("div", { className: "alert alert-info", children: [(0, jsx_runtime_1.jsxs)("h6", { className: "alert-heading", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-info-circle me-2" }), "How to use"] }), (0, jsx_runtime_1.jsxs)("ol", { className: "mb-0 small", children: [(0, jsx_runtime_1.jsx)("li", { children: "Get a free Picovoice access key from the link above" }), (0, jsx_runtime_1.jsxs)("li", { children: ["Enter the key and click ", (0, jsx_runtime_1.jsx)("strong", { children: "Save" })] }), (0, jsx_runtime_1.jsxs)("li", { children: ["Enable voice input and click ", (0, jsx_runtime_1.jsx)("strong", { children: "Save" }), " again"] }), (0, jsx_runtime_1.jsxs)("li", { children: ["Say", " ", (0, jsx_runtime_1.jsxs)("strong", { children: ["\"", voice_settings_types_1.BUILTIN_WAKE_WORDS.find((w) => w.id === localSettings.wakeWord)?.name || "Computer", "\""] }), " ", "to activate voice recording"] }), (0, jsx_runtime_1.jsx)("li", { children: "Speak your message - recording stops automatically when you pause" }), (0, jsx_runtime_1.jsx)("li", { children: "Your message will be sent to the assistant and the response will be spoken" })] })] })] }));
171
- };
172
- exports.VoiceSettings = VoiceSettings;
173
- exports.default = exports.VoiceSettings;