DubbingDemo and DemoPresetGrid already degrade gracefully (hide) when their assets / is_demo profiles are missing, but DictationDemo always rendered its three hardcoded cards — which fail on click without the bundled sample WAVs (rendered by scripts/build_demos.sh; absent in a plain source checkout). Add a mount-time HEAD probe of the first sample; if it's not present, hide the whole demo (mirrors DubbingDemo's missing-manifest behavior). When assets are present, behavior is unchanged. Test: HEAD 404 → demo renders nothing. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
993e6cf6a5
commit
034d1c4811
@@ -61,8 +61,23 @@ export default function DictationDemo({ embedded = false }) {
|
||||
const [hotkeyState, setHotkeyState] = useState('unknown'); // unknown | registered | verified
|
||||
const [playingId, setPlayingId] = useState(null);
|
||||
const [transcripts, setTranscripts] = useState({}); // {scriptId: {state, text, error}}
|
||||
// null = probing, true/false once the demo assets are confirmed present.
|
||||
// The sample WAVs are rendered by scripts/build_demos.sh and may be absent
|
||||
// (e.g. source checkout without a render step). When absent we hide the demo
|
||||
// rather than show cards that fail on click (#119/#124 follow-up).
|
||||
const [assetsAvailable, setAssetsAvailable] = useState(null);
|
||||
const audioRef = useRef(null);
|
||||
|
||||
// Probe whether the bundled dictation samples actually exist; hide the whole
|
||||
// demo if not, mirroring DubbingDemo's missing-manifest behavior.
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetch(`${API}${SCRIPTS[0].wav}`, { method: 'HEAD' })
|
||||
.then((r) => { if (!cancelled) setAssetsAvailable(r.ok); })
|
||||
.catch(() => { if (!cancelled) setAssetsAvailable(false); });
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
// Read the registered hotkey on mount.
|
||||
useEffect(() => {
|
||||
if (!isTauri()) return;
|
||||
@@ -179,6 +194,9 @@ export default function DictationDemo({ embedded = false }) {
|
||||
}
|
||||
})();
|
||||
|
||||
// No bundled samples on disk → don't render a demo that can't work.
|
||||
if (assetsAvailable === false) return null;
|
||||
|
||||
return (
|
||||
<section className={`dictation-demo ${embedded ? 'dictation-demo--embedded' : ''}`}>
|
||||
<header className="dictation-demo__head">
|
||||
|
||||
@@ -34,6 +34,16 @@ describe('DictationDemo', () => {
|
||||
expect(screen.getByText(/No hotkey registered/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides the demo when the sample assets are missing (HEAD 404)', async () => {
|
||||
// The mount probe HEAD-checks the first sample; a 404 means no rendered
|
||||
// assets on disk → the whole demo should disappear rather than show cards
|
||||
// that fail on click.
|
||||
global.fetch = vi.fn(() => Promise.resolve({ ok: false, status: 404 }));
|
||||
const { container } = render(withI18n(<DictationDemo />));
|
||||
await waitFor(() => expect(container).toBeEmptyDOMElement());
|
||||
expect(screen.queryByText(/Schedule a meeting with Pat/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('POSTs the bundled WAV to /transcribe when Replay is clicked', async () => {
|
||||
const wavBlob = new Blob([new Uint8Array([0, 0, 0, 0])], { type: 'audio/wav' });
|
||||
// Make the recognized text deliberately different from the on-card
|
||||
|
||||
Reference in New Issue
Block a user