refactor: replace any types with proper TypeScript types across gateway client, hooks, and components

- Add GatewayMessage and JsonPayload interfaces to gateway.ts
- Type WebSocket message parsing with GatewayMessage instead of any
- Use ReturnType<typeof setTimeout> for timer refs
- Type chat event payload destructuring explicitly
- Add ChatPayloadMessage interface for extractText()
- Replace any casts in loadSessions/loadHistory with typed assertions
- Add str() helper in ToolCall.tsx for safe unknown→string extraction
- Use Extract<MessageBlock, ...> type guards instead of `as any` casts
- Type CodeBlock children prop properly
This commit is contained in:
Nicolas Varrot
2026-02-11 21:17:44 +00:00
parent d724a8ca0b
commit 693229c14e
6 changed files with 91 additions and 57 deletions

View File

@@ -148,7 +148,7 @@ function renderTextBlocks(blocks: MessageBlock[]) {
return getTextBlocks(blocks).map((block, i) => (
<div key={`text-${i}`} className="markdown-body">
<ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} rehypePlugins={[rehypeHighlight]} components={markdownComponents}>
{autoFormatText((block as any).text)}
{autoFormatText((block as Extract<MessageBlock, { type: 'text' }>).text)}
</ReactMarkdown>
</div>
));
@@ -238,7 +238,7 @@ function CopyButton({ text }: { text: string }) {
/** Extract plain text from message blocks for clipboard copy */
function getPlainText(message: ChatMessageType): string {
if (message.blocks.length > 0) {
return getTextBlocks(message.blocks).map(b => (b as any).text).join('\n\n');
return getTextBlocks(message.blocks).map(b => (b as Extract<MessageBlock, { type: 'text' }>).text).join('\n\n');
}
return message.content;
}

View File

@@ -10,7 +10,7 @@ export function CodeBlock(props: HTMLAttributes<HTMLPreElement>) {
const handleCopy = useCallback(() => {
// Extract text from the nested <code> element
const code = (props.children as any)?.props?.children;
const code = (props.children as React.ReactElement<{ children?: string }> | undefined)?.props?.children;
if (typeof code === 'string') {
navigator.clipboard.writeText(code).then(() => {
setCopied(true);

View File

@@ -122,33 +122,40 @@ export function HighlightedPre({ text, className }: { text: string; className: s
return <pre className={className}>{text}</pre>;
}
function getContextHint(name: string, input: any): string | null {
function str(v: unknown): string | null {
return typeof v === 'string' ? v : null;
}
function getContextHint(name: string, input: Record<string, unknown> | undefined): string | null {
if (!input || typeof input !== 'object') return null;
switch (name) {
case 'exec':
return input.command ? truncate(input.command, 60) : null;
return str(input.command) ? truncate(str(input.command)!, 60) : null;
case 'Read': case 'read':
case 'Write': case 'write':
case 'Edit': case 'edit':
return input.file_path || input.path || null;
return str(input.file_path) || str(input.path) || null;
case 'web_search':
return input.query ? truncate(input.query, 50) : null;
return str(input.query) ? truncate(str(input.query)!, 50) : null;
case 'web_fetch':
return input.url ? truncate(input.url, 60) : null;
return str(input.url) ? truncate(str(input.url)!, 60) : null;
case 'browser':
return input.action || null;
case 'message':
return input.action ? `${input.action}${input.target ? ' → ' + input.target : ''}` : null;
return str(input.action) || null;
case 'message': {
const action = str(input.action);
const target = str(input.target);
return action ? `${action}${target ? ' → ' + target : ''}` : null;
}
case 'memory_search':
return input.query ? truncate(input.query, 50) : null;
return str(input.query) ? truncate(str(input.query)!, 50) : null;
case 'memory_get':
return input.path || null;
return str(input.path) || null;
case 'cron':
return input.action || null;
return str(input.action) || null;
case 'sessions_spawn':
return input.task ? truncate(input.task, 50) : null;
return str(input.task) ? truncate(str(input.task)!, 50) : null;
case 'image':
return input.prompt ? truncate(input.prompt, 50) : null;
return str(input.prompt) ? truncate(str(input.prompt)!, 50) : null;
default:
return null;
}
@@ -184,7 +191,7 @@ function extractImageFromResult(result: string): { src: string; remaining: strin
return null;
}
export function ToolCall({ name, input, result }: { name: string; input?: any; result?: string }) {
export function ToolCall({ name, input, result }: { name: string; input?: Record<string, unknown>; result?: string }) {
const t = useT();
const [open, setOpen] = useState(false);
const c = getColor(name);