mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
persist tts settings
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||||
<title>Multi Roomba Rover</title>
|
<title>Multi Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-DoVsLZ1w.js"></script>
|
<script type="module" crossorigin src="/assets/index-Bfcf31QB.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-B88Ix9u6.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-B88Ix9u6.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useChat } from '../context/ChatContext.jsx';
|
import { useChat } from '../context/ChatContext.jsx';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../context/SessionContext.jsx';
|
||||||
|
import { useSettingsNamespace } from '../settings/index.js';
|
||||||
|
|
||||||
function roleColors(role, fromDiscord) {
|
function roleColors(role, fromDiscord) {
|
||||||
if (fromDiscord) return 'text-indigo-200';
|
if (fromDiscord) return 'text-indigo-200';
|
||||||
@@ -31,12 +32,16 @@ const ESPEAK_PITCHES = Array.from({ length: 10 }, (_, idx) => idx * 10);
|
|||||||
export default function ChatPanel({ hideInput = false, hideSpectatorNotice = false, fillHeight = false }) {
|
export default function ChatPanel({ hideInput = false, hideSpectatorNotice = false, fillHeight = false }) {
|
||||||
const { session } = useSession();
|
const { session } = useSession();
|
||||||
const { messages, sendMessage, registerInputRef, onInputFocus, onInputBlur, blurChat } = useChat();
|
const { messages, sendMessage, registerInputRef, onInputFocus, onInputBlur, blurChat } = useChat();
|
||||||
|
const {
|
||||||
|
value: ttsSettings,
|
||||||
|
save: saveTtsSettings,
|
||||||
|
} = useSettingsNamespace('tts', { engine: 'flite', voice: 'rms', pitch: 50 });
|
||||||
const [draft, setDraft] = useState('');
|
const [draft, setDraft] = useState('');
|
||||||
const [sending, setSending] = useState(false);
|
const [sending, setSending] = useState(false);
|
||||||
const [speak, setSpeak] = useState(false);
|
const [speak, setSpeak] = useState(false);
|
||||||
const [engine, setEngine] = useState('flite');
|
const [engine, setEngine] = useState(() => ttsSettings?.engine || 'flite');
|
||||||
const [voice, setVoice] = useState('rms');
|
const [voice, setVoice] = useState(() => ttsSettings?.voice || 'rms');
|
||||||
const [pitch, setPitch] = useState(50);
|
const [pitch, setPitch] = useState(() => (Number.isFinite(ttsSettings?.pitch) ? ttsSettings.pitch : 50));
|
||||||
const canChat = session?.role !== 'spectator';
|
const canChat = session?.role !== 'spectator';
|
||||||
const listRef = useRef(null);
|
const listRef = useRef(null);
|
||||||
const currentRoverId = session?.assignment?.roverId || null;
|
const currentRoverId = session?.assignment?.roverId || null;
|
||||||
@@ -54,6 +59,15 @@ export default function ChatPanel({ hideInput = false, hideSpectatorNotice = fal
|
|||||||
listRef.current.scrollTop = listRef.current.scrollHeight;
|
listRef.current.scrollTop = listRef.current.scrollHeight;
|
||||||
}, [sorted]);
|
}, [sorted]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const nextEngine = ttsSettings?.engine || 'flite';
|
||||||
|
const nextVoice = ttsSettings?.voice || 'rms';
|
||||||
|
const nextPitch = Number.isFinite(ttsSettings?.pitch) ? ttsSettings.pitch : 50;
|
||||||
|
if (engine !== nextEngine) setEngine(nextEngine);
|
||||||
|
if (voice !== nextVoice) setVoice(nextVoice);
|
||||||
|
if (pitch !== nextPitch) setPitch(nextPitch);
|
||||||
|
}, [engine, pitch, ttsSettings?.engine, ttsSettings?.pitch, ttsSettings?.voice, voice]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ttsSupported) {
|
if (ttsSupported) {
|
||||||
setSpeak(true);
|
setSpeak(true);
|
||||||
@@ -154,7 +168,11 @@ export default function ChatPanel({ hideInput = false, hideSpectatorNotice = fal
|
|||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={engine}
|
value={engine}
|
||||||
onChange={(e) => setEngine(e.target.value)}
|
onChange={(e) => {
|
||||||
|
const next = e.target.value;
|
||||||
|
setEngine(next);
|
||||||
|
saveTtsSettings((current) => ({ ...(current || {}), engine: next }));
|
||||||
|
}}
|
||||||
className="field-input text-xs"
|
className="field-input text-xs"
|
||||||
>
|
>
|
||||||
<option value="flite">flite</option>
|
<option value="flite">flite</option>
|
||||||
@@ -163,7 +181,11 @@ export default function ChatPanel({ hideInput = false, hideSpectatorNotice = fal
|
|||||||
{engine === 'flite' ? (
|
{engine === 'flite' ? (
|
||||||
<select
|
<select
|
||||||
value={voice}
|
value={voice}
|
||||||
onChange={(e) => setVoice(e.target.value)}
|
onChange={(e) => {
|
||||||
|
const next = e.target.value;
|
||||||
|
setVoice(next);
|
||||||
|
saveTtsSettings((current) => ({ ...(current || {}), voice: next }));
|
||||||
|
}}
|
||||||
className="field-input text-xs"
|
className="field-input text-xs"
|
||||||
>
|
>
|
||||||
{FLITE_VOICES.map((v) => (
|
{FLITE_VOICES.map((v) => (
|
||||||
@@ -175,7 +197,11 @@ export default function ChatPanel({ hideInput = false, hideSpectatorNotice = fal
|
|||||||
) : (
|
) : (
|
||||||
<select
|
<select
|
||||||
value={pitch}
|
value={pitch}
|
||||||
onChange={(e) => setPitch(Number(e.target.value))}
|
onChange={(e) => {
|
||||||
|
const next = Number(e.target.value);
|
||||||
|
setPitch(next);
|
||||||
|
saveTtsSettings((current) => ({ ...(current || {}), pitch: next }));
|
||||||
|
}}
|
||||||
className="field-input text-xs"
|
className="field-input text-xs"
|
||||||
>
|
>
|
||||||
{ESPEAK_PITCHES.map((p) => (
|
{ESPEAK_PITCHES.map((p) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user