say something when the chat fails silently
read-aloud swallowed every error: the spinner stopped, the speaker icon came back, and a
TTS service that was simply down looked identical to a button that did nothing. Both the
synthesis failure and the playback failure now say so.
errorText moves to helpers — useClient rejects with a plain { status, message } object
rather than an Error, so the reflex instanceof check reports every API failure in the app
as "unknown error". Two callers now, and it is the wrong thing to reimplement per file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* A readable string for anything that was thrown.
|
||||
*
|
||||
* `useClient` rejects with a plain `{ status, message }` object, not an `Error`, so the reflex
|
||||
* `err instanceof Error ? err.message : …` misses the most common failure in this app entirely and
|
||||
* reports every API error as "unknown error". Check the shape first, the class second.
|
||||
*
|
||||
* Truncated because these end up in toasts, and a server that answers a failure with a wall of HTML
|
||||
* should not push the rest of the screen off it.
|
||||
*/
|
||||
export function errorText(err: unknown, fallback = 'unknown error'): string {
|
||||
if (typeof err === 'object' && err !== null && 'message' in err) {
|
||||
const message = (err as { message: unknown }).message;
|
||||
if (typeof message === 'string' && message.trim()) return message.slice(0, 200);
|
||||
}
|
||||
if (typeof err === 'string' && err.trim()) return err.slice(0, 200);
|
||||
return fallback;
|
||||
}
|
||||
@@ -4,8 +4,10 @@ import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
|
||||
import { Volume2, Loader2, Square, Clock, Check, X, CircleSlash } from 'lucide-react';
|
||||
import { toast } from '@/components/ui/sonner';
|
||||
import type { Tone } from '@/components/Data';
|
||||
import { toneText } from '@/components/Data';
|
||||
import { errorText } from 'helpers/error-text';
|
||||
import type { ChatMessage } from '../types';
|
||||
import { ToolActivity } from './ToolActivity';
|
||||
import { QuestionActivity } from './QuestionActivity';
|
||||
@@ -91,11 +93,16 @@ const ReadAloudButton = ({ id, text }: { id: string; text: string }) => {
|
||||
audio.onerror = () => {
|
||||
audioRef.current = null;
|
||||
setState('idle');
|
||||
toast.error('The narration was generated but would not play.');
|
||||
};
|
||||
await audio.play();
|
||||
setState('playing');
|
||||
} catch {
|
||||
} catch (err) {
|
||||
// Synthesis is slow enough that the spinner is the only feedback there is; when it just stops and
|
||||
// the icon comes back, the button looks broken rather than failed. Most often this is the TTS
|
||||
// service being down, which is worth saying out loud.
|
||||
setState('idle');
|
||||
toast.error(`Could not read this message aloud: ${errorText(err)}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Plus, MessageSquare, RefreshCw, Trash2, Pencil, Check, X } from 'lucide
|
||||
import { toast } from '@/components/ui/sonner';
|
||||
import { DataList, DataRow, EmptyBlock, ErrorBlock, LoadingBlock, RelativeTime } from '@/components/Data';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { errorText } from 'helpers/error-text';
|
||||
import { useClaudeSessions } from 'state/useClaudeSessions';
|
||||
import type { SelectedSession } from './ChatDetailPanel';
|
||||
import { PwdSelector } from './PwdSelector';
|
||||
@@ -257,12 +258,3 @@ export const SessionList = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/** `useClient` throws `{ status, message }`, not an Error, so `err.message` alone misses the common case. */
|
||||
function errorText(err: unknown): string {
|
||||
if (typeof err === 'object' && err !== null && 'message' in err) {
|
||||
const message = (err as { message: unknown }).message;
|
||||
if (typeof message === 'string' && message.trim()) return message.slice(0, 200);
|
||||
}
|
||||
return err instanceof Error ? err.message : 'unknown error';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user