fix: consistent theming across tool and message components
- MessageContent: Make prose-invert conditional (dark:prose-invert) and use !important on inline code colors to override Typography - ToolCallDisplay: Replace theme variables with explicit Tailwind dark/light classes for reliable styling - ToolResultDisplay: Same treatment - explicit slate colors for both light and dark modes All components now properly respect light/dark mode toggle. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -391,7 +391,7 @@
|
||||
{:else if part.type === 'tool-result'}
|
||||
<ToolResultDisplay content={part.content} />
|
||||
{:else}
|
||||
<div class="prose prose-sm prose-invert max-w-none">
|
||||
<div class="prose prose-sm max-w-none dark:prose-invert">
|
||||
{@html renderMarkdown(part.content)}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -489,7 +489,9 @@
|
||||
}
|
||||
|
||||
.message-content :global(.prose code:not(pre code)) {
|
||||
@apply rounded bg-slate-200 px-1.5 py-0.5 text-sm text-emerald-700;
|
||||
@apply rounded px-1.5 py-0.5 text-sm;
|
||||
background-color: rgb(226 232 240) !important; /* slate-200 */
|
||||
color: rgb(4 120 87) !important; /* emerald-700 */
|
||||
}
|
||||
|
||||
.message-content :global(.prose a) {
|
||||
@@ -544,7 +546,8 @@
|
||||
}
|
||||
|
||||
:global(.dark) .message-content :global(.prose code:not(pre code)) {
|
||||
@apply bg-slate-700 text-emerald-400;
|
||||
background-color: rgb(51 65 85) !important; /* slate-700 */
|
||||
color: rgb(52 211 153) !important; /* emerald-400 */
|
||||
}
|
||||
|
||||
:global(.dark) .message-content :global(.prose a) {
|
||||
|
||||
@@ -187,14 +187,14 @@
|
||||
{@const isExpanded = expandedCalls.has(call.id)}
|
||||
|
||||
<div
|
||||
class="overflow-hidden rounded-xl border border-theme/50 bg-gradient-to-r {meta.color} p-[1px] shadow-lg"
|
||||
class="overflow-hidden rounded-xl border border-slate-300 bg-gradient-to-r dark:border-slate-700 {meta.color} p-[1px] shadow-lg"
|
||||
>
|
||||
<div class="rounded-xl bg-theme-primary/95 backdrop-blur">
|
||||
<div class="rounded-xl bg-white/95 backdrop-blur dark:bg-slate-800/95">
|
||||
<!-- Header -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => toggleExpand(call.id)}
|
||||
class="flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-theme-secondary/50"
|
||||
class="flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-slate-100/50 dark:hover:bg-slate-700/50"
|
||||
>
|
||||
<!-- Icon -->
|
||||
<span class="text-xl" role="img" aria-label={meta.label}>{meta.icon}</span>
|
||||
@@ -202,14 +202,14 @@
|
||||
<!-- Tool name and summary -->
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium text-theme-primary">{meta.label}</span>
|
||||
<span class="font-mono text-xs text-theme-muted">{call.name}</span>
|
||||
<span class="font-medium text-slate-800 dark:text-slate-100">{meta.label}</span>
|
||||
<span class="font-mono text-xs text-slate-500 dark:text-slate-400">{call.name}</span>
|
||||
</div>
|
||||
|
||||
<!-- Quick preview of main argument -->
|
||||
{#if argEntries.length > 0}
|
||||
{@const [firstKey, firstValue] = argEntries[0]}
|
||||
<p class="mt-0.5 truncate text-sm text-theme-muted">
|
||||
<p class="mt-0.5 truncate text-sm text-slate-500 dark:text-slate-400">
|
||||
{#if call.name === 'web_search' && typeof firstValue === 'string'}
|
||||
Searching: "{firstValue}"
|
||||
{:else if call.name === 'fetch_url' && typeof firstValue === 'string'}
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<!-- Expand indicator -->
|
||||
<svg
|
||||
class="h-5 w-5 flex-shrink-0 text-theme-muted transition-transform duration-200"
|
||||
class="h-5 w-5 flex-shrink-0 text-slate-400 transition-transform duration-200 dark:text-slate-500"
|
||||
class:rotate-180={isExpanded}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -240,14 +240,14 @@
|
||||
|
||||
<!-- Expanded arguments -->
|
||||
{#if isExpanded && argEntries.length > 0}
|
||||
<div class="border-t border-theme px-4 py-3">
|
||||
<div class="border-t border-slate-200 px-4 py-3 dark:border-slate-700">
|
||||
<div class="space-y-2">
|
||||
{#each argEntries as [key, value]}
|
||||
<div class="flex items-start gap-3 text-sm">
|
||||
<span class="w-24 flex-shrink-0 font-medium text-theme-muted">
|
||||
<span class="w-24 flex-shrink-0 font-medium text-slate-500 dark:text-slate-400">
|
||||
{argLabel(key)}
|
||||
</span>
|
||||
<span class="break-all font-mono text-theme-secondary">
|
||||
<span class="break-all font-mono text-slate-700 dark:text-slate-200">
|
||||
{formatValue(value)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -262,25 +262,25 @@
|
||||
{@const parsed = parseResult(call.result)}
|
||||
{@const isResultExpanded = expandedResults.has(call.id)}
|
||||
|
||||
<div class="border-t border-theme">
|
||||
<div class="border-t border-slate-200 dark:border-slate-700">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => toggleResult(call.id)}
|
||||
class="flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-theme-secondary/50"
|
||||
class="flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-slate-100/50 dark:hover:bg-slate-700/50"
|
||||
>
|
||||
<!-- Status icon -->
|
||||
{#if call.error}
|
||||
<span class="text-red-400">✗</span>
|
||||
<span class="flex-1 text-red-300">Error: {call.error}</span>
|
||||
<span class="text-red-500 dark:text-red-400">✗</span>
|
||||
<span class="flex-1 text-red-600 dark:text-red-300">Error: {call.error}</span>
|
||||
{:else}
|
||||
<span class="text-emerald-400">✓</span>
|
||||
<span class="flex-1 text-theme-muted">{parsed.summary}</span>
|
||||
<span class="text-emerald-500 dark:text-emerald-400">✓</span>
|
||||
<span class="flex-1 text-slate-500 dark:text-slate-400">{parsed.summary}</span>
|
||||
{/if}
|
||||
|
||||
<!-- Expand arrow -->
|
||||
{#if hasResult && parsed.full}
|
||||
<svg
|
||||
class="h-4 w-4 flex-shrink-0 text-theme-muted transition-transform duration-200"
|
||||
class="h-4 w-4 flex-shrink-0 text-slate-400 transition-transform duration-200 dark:text-slate-500"
|
||||
class:rotate-180={isResultExpanded}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -294,8 +294,8 @@
|
||||
|
||||
<!-- Expanded result content -->
|
||||
{#if isResultExpanded && hasResult && parsed.full}
|
||||
<div class="max-h-96 overflow-auto border-t border-theme/50 bg-theme-primary/50 px-4 py-3">
|
||||
<pre class="whitespace-pre-wrap break-words text-xs text-theme-muted">{parsed.full.length > 10000 ? parsed.full.substring(0, 10000) + '\n\n... (truncated)' : parsed.full}</pre>
|
||||
<div class="max-h-96 overflow-auto border-t border-slate-200/50 bg-slate-50/50 px-4 py-3 dark:border-slate-700/50 dark:bg-slate-900/50">
|
||||
<pre class="whitespace-pre-wrap break-words text-xs text-slate-600 dark:text-slate-400">{parsed.full.length > 10000 ? parsed.full.substring(0, 10000) + '\n\n... (truncated)' : parsed.full}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<div class="flex items-center gap-3 px-4 py-3">
|
||||
<span class="text-2xl">📍</span>
|
||||
<div>
|
||||
<p class="font-medium text-theme-primary">
|
||||
<p class="font-medium text-slate-800 dark:text-slate-100">
|
||||
{#if loc.location?.city}
|
||||
{loc.location.city}{#if loc.location.country}, {loc.location.country}{/if}
|
||||
{:else if loc.message}
|
||||
@@ -116,9 +116,9 @@
|
||||
{/if}
|
||||
</p>
|
||||
{#if loc.source === 'ip'}
|
||||
<p class="text-xs text-theme-muted">Based on IP address (approximate)</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">Based on IP address (approximate)</p>
|
||||
{:else if loc.source === 'gps'}
|
||||
<p class="text-xs text-theme-muted">From device GPS</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">From device GPS</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -127,7 +127,7 @@
|
||||
{:else if parsed.type === 'search'}
|
||||
{@const search = parsed.data as SearchData}
|
||||
<div class="my-3 space-y-2">
|
||||
<div class="flex items-center gap-2 text-sm text-theme-muted">
|
||||
<div class="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400">
|
||||
<span>🔍</span>
|
||||
<span>Found {search.resultCount || search.results?.length || 0} results for "{search.query}"</span>
|
||||
</div>
|
||||
@@ -139,15 +139,15 @@
|
||||
href={result.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="block rounded-lg border border-theme/50 bg-theme-secondary/50 p-3 transition-colors hover:border-blue-500/50 hover:bg-theme-secondary"
|
||||
class="block rounded-lg border border-slate-200 bg-slate-100/50 p-3 transition-colors hover:border-blue-500/50 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-800/50 dark:hover:bg-slate-800"
|
||||
>
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="mt-0.5 text-blue-400">#{result.rank}</span>
|
||||
<span class="mt-0.5 text-blue-500 dark:text-blue-400">#{result.rank}</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="font-medium text-blue-400 hover:underline">{result.title}</p>
|
||||
<p class="mt-0.5 truncate text-xs text-theme-muted">{result.url}</p>
|
||||
<p class="font-medium text-blue-600 hover:underline dark:text-blue-400">{result.title}</p>
|
||||
<p class="mt-0.5 truncate text-xs text-slate-500 dark:text-slate-400">{result.url}</p>
|
||||
{#if result.snippet && result.snippet !== '(no snippet available)'}
|
||||
<p class="mt-1 text-sm text-theme-muted">{result.snippet}</p>
|
||||
<p class="mt-1 text-sm text-slate-600 dark:text-slate-400">{result.snippet}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -158,10 +158,10 @@
|
||||
</div>
|
||||
|
||||
{:else if parsed.type === 'error'}
|
||||
<div class="my-3 rounded-xl border border-red-500/30 bg-red-500/10 px-4 py-3">
|
||||
<div class="my-3 rounded-xl border border-red-300 bg-red-50 px-4 py-3 dark:border-red-500/30 dark:bg-red-500/10">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-red-400">⚠️</span>
|
||||
<span class="text-sm text-red-300">{parsed.data}</span>
|
||||
<span class="text-red-500 dark:text-red-400">⚠️</span>
|
||||
<span class="text-sm text-red-700 dark:text-red-300">{parsed.data}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -172,32 +172,32 @@
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span>🌐</span>
|
||||
{#if data.title}
|
||||
<span class="font-medium text-theme-secondary">{data.title}</span>
|
||||
<span class="font-medium text-slate-700 dark:text-slate-200">{data.title}</span>
|
||||
{:else if data.url}
|
||||
<a href={String(data.url)} target="_blank" rel="noopener noreferrer" class="text-violet-400 hover:underline">
|
||||
<a href={String(data.url)} target="_blank" rel="noopener noreferrer" class="text-violet-600 hover:underline dark:text-violet-400">
|
||||
{data.url}
|
||||
</a>
|
||||
{:else}
|
||||
<span class="text-theme-muted">Fetched content</span>
|
||||
<span class="text-slate-500 dark:text-slate-400">Fetched content</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if data.text && typeof data.text === 'string'}
|
||||
<p class="mt-2 line-clamp-4 text-sm text-theme-muted">{data.text.substring(0, 300)}{data.text.length > 300 ? '...' : ''}</p>
|
||||
<p class="mt-2 line-clamp-4 text-sm text-slate-600 dark:text-slate-400">{data.text.substring(0, 300)}{data.text.length > 300 ? '...' : ''}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{:else if parsed.type === 'json'}
|
||||
{@const data = parsed.data as Record<string, unknown>}
|
||||
<div class="my-3 rounded-xl border border-theme/50 bg-theme-secondary/50 p-3">
|
||||
<pre class="overflow-x-auto text-xs text-theme-muted">{JSON.stringify(data, null, 2)}</pre>
|
||||
<div class="my-3 rounded-xl border border-slate-200 bg-slate-100/50 p-3 dark:border-slate-700 dark:bg-slate-800/50">
|
||||
<pre class="overflow-x-auto text-xs text-slate-600 dark:text-slate-400">{JSON.stringify(data, null, 2)}</pre>
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
<!-- Fallback: just show the text (if not empty/whitespace) -->
|
||||
{#if typeof parsed.data === 'string' && parsed.data.trim().length > 0}
|
||||
<div class="my-3 rounded-xl border border-theme/50 bg-theme-secondary/50 p-3">
|
||||
<p class="text-sm text-theme-secondary">{parsed.data}</p>
|
||||
<div class="my-3 rounded-xl border border-slate-200 bg-slate-100/50 p-3 dark:border-slate-700 dark:bg-slate-800/50">
|
||||
<p class="text-sm text-slate-700 dark:text-slate-200">{parsed.data}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user