feat(play): integrate vision click with unified settings modal
Merge vision-click toggle into the shared SettingsModal alongside player name and TTS key configuration. Remove standalone TtsKeyModal. Add settings gear button to PlayCanvas dialogue card and header. Fix fullscreen settings modal not rendering in immersive mode. Voice toggle uses standard CategorySelect dropdown matching other tab bar options. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+29
-20
@@ -17,8 +17,7 @@ import {
|
||||
} from "@/components/PlayCanvas";
|
||||
import type { DialogueHistoryItem } from "@/components/DialogueHistoryModal";
|
||||
import type { GalleryDoc, GalleryScene } from "@/app/gallery/page";
|
||||
import { TtsKeyModal } from "@/components/TtsKeyModal";
|
||||
import { readStoredPlayerName } from "@/components/SettingsModal";
|
||||
import { SettingsModal, readStoredPlayerName, readStoredVisionClick } from "@/components/SettingsModal";
|
||||
import { annotateClick } from "@/lib/annotateClient";
|
||||
import { loadClientTtsConfig } from "@/lib/clientTtsConfig";
|
||||
import { PRESETS } from "@/lib/presets";
|
||||
@@ -580,9 +579,11 @@ function PlayInner() {
|
||||
const [silenceStrikes, setSilenceStrikes] = useState(0);
|
||||
// Once the player dismisses the silence nudge, keep it gone for this session.
|
||||
const [nudgeDismissed, setNudgeDismissed] = useState(false);
|
||||
// The in-place BYO-key modal, opened from the silence nudge so the player can
|
||||
// add a key without leaving the play page.
|
||||
const [ttsModalOpen, setTtsModalOpen] = useState(false);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [visionClickEnabled, setVisionClickEnabled] = useState<boolean>(() => {
|
||||
if (typeof window === "undefined") return true;
|
||||
return readStoredVisionClick();
|
||||
});
|
||||
|
||||
const startedRef = useRef(false);
|
||||
const poolRef = useRef<Map<string, PrefetchEntry>>(new Map());
|
||||
@@ -852,15 +853,10 @@ function PlayInner() {
|
||||
prefetchSceneAudio();
|
||||
}, [muted, prefetchSceneAudio]);
|
||||
|
||||
// ── BYO key enabled/disabled from the play page (silence nudge → modal) ─
|
||||
// On enable: point the synth path at the user's key and immediately
|
||||
// re-synthesize the current scene in-browser, so the voices the player just
|
||||
// missed come back without a reload (their characters already carry
|
||||
// server-provisioned `voice`, which resolveByoVoice reuses with the new key).
|
||||
// On disable: just stop using it; later scenes fall back to the server.
|
||||
const handleByoSaved = useCallback(
|
||||
(configured: boolean) => {
|
||||
const cfg = configured ? loadClientTtsConfig() : null;
|
||||
const handleSettingsSaved = useCallback(
|
||||
(settings: { ttsConfigured: boolean; playerName: string; visionClickEnabled: boolean }) => {
|
||||
setVisionClickEnabled(settings.visionClickEnabled);
|
||||
const cfg = settings.ttsConfigured ? loadClientTtsConfig() : null;
|
||||
byoTtsRef.current = cfg;
|
||||
setByoTtsConfig(cfg);
|
||||
if (cfg) {
|
||||
@@ -1761,6 +1757,8 @@ function PlayInner() {
|
||||
onFreeformInput={onFreeformInput}
|
||||
orientation={orientation}
|
||||
playerName={session?.playerName}
|
||||
visionClickEnabled={visionClickEnabled}
|
||||
onOpenSettings={() => setSettingsOpen(true)}
|
||||
fullViewport
|
||||
dialogueHistory={dialogueHistory}
|
||||
/>
|
||||
@@ -1788,6 +1786,14 @@ function PlayInner() {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{settingsOpen && (
|
||||
<SettingsModal
|
||||
initialVisionClickEnabled={visionClickEnabled}
|
||||
onClose={() => setSettingsOpen(false)}
|
||||
onSaved={handleSettingsSaved}
|
||||
footerNote="保存后配音 Key 会立即生效,用你自己的额度合成当前这一幕的配音。"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1838,6 +1844,8 @@ function PlayInner() {
|
||||
onFreeformInput={onFreeformInput}
|
||||
orientation={orientation}
|
||||
playerName={session?.playerName}
|
||||
visionClickEnabled={visionClickEnabled}
|
||||
onOpenSettings={() => setSettingsOpen(true)}
|
||||
dialogueHistory={dialogueHistory}
|
||||
aboveCanvas={
|
||||
<button
|
||||
@@ -1887,7 +1895,7 @@ function PlayInner() {
|
||||
<span className="flex items-center gap-1 animate-fade-in">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTtsModalOpen(true)}
|
||||
onClick={() => setSettingsOpen(true)}
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-ember-500/40 bg-ember-500/10 px-2.5 py-1 text-[10px] text-ember-500 hover:bg-ember-500/20 transition-colors"
|
||||
title="经常没声音?填入你自己的小米 MiMo Key(免费),配音更稳定"
|
||||
>
|
||||
@@ -1925,11 +1933,12 @@ function PlayInner() {
|
||||
|
||||
</main>
|
||||
|
||||
{ttsModalOpen && (
|
||||
<TtsKeyModal
|
||||
onClose={() => setTtsModalOpen(false)}
|
||||
onSaved={handleByoSaved}
|
||||
footerNote="保存后会立即用这把 Key 在你的浏览器里合成当前这一幕的配音;本设备后续游玩也会自动使用此 Key。"
|
||||
{settingsOpen && (
|
||||
<SettingsModal
|
||||
initialVisionClickEnabled={visionClickEnabled}
|
||||
onClose={() => setSettingsOpen(false)}
|
||||
onSaved={handleSettingsSaved}
|
||||
footerNote="保存后配音 Key 会立即生效,用你自己的额度合成当前这一幕的配音。"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user