From 529c6a2bb56447fe93796111df3d4cc5a05fdd93 Mon Sep 17 00:00:00 2001
From: Adam Malczewski <github@tradam.dev>
Date: Sun, 7 Jun 2026 02:06:55 +0900
Subject: Slice 3 wave A: tabs model, model selector, cache delete,
 localStorage

- features/tabs: pure tab-workspace reducer (create/select/close/setModel/
  setTitle/deriveTitle, draft=null active) + injected-persistence runes store
- features/chat: mutable per-tab model (setModel) + delta routing guard
  (ignore foreign conversationId) + ModelSelector.svelte + DaisyUI chat bubbles
  / composer (keeps streaming <details> keying fix)
- features/conversation-cache: surface delete(conversationId) on the wrapper
  for tab-close local-forget
- adapters/local-storage: generic injected JSON localStore<T> (quota/corrupt-safe)

Verified: svelte-check 0/0, vitest 273, biome clean, build ok.
---
 src/features/chat/ui/ChatView.svelte | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

(limited to 'src/features/chat/ui/ChatView.svelte')

diff --git a/src/features/chat/ui/ChatView.svelte b/src/features/chat/ui/ChatView.svelte
index ce66798..cb6069b 100644
--- a/src/features/chat/ui/ChatView.svelte
+++ b/src/features/chat/ui/ChatView.svelte
@@ -4,14 +4,16 @@
 	let { chunks }: { chunks: readonly RenderedChunk[] } = $props();
 </script>
 
-<div class="chat-transcript" role="log" aria-live="polite">
+<div class="flex flex-col gap-2 p-4" role="log" aria-live="polite">
 	{#each chunks as rendered, i (rendered.seq != null ? `c${rendered.seq}` : `p${i}`)}
-		<article
-			class="message message--{rendered.role}"
-			class:message--provisional={rendered.provisional}
-		>
-			<header class="message__role">{rendered.role}</header>
-			<div class="message__content">
+		<div class="chat {rendered.role === 'user' ? 'chat-start' : 'chat-end'}">
+			<div class="chat-header text-xs opacity-70">{rendered.role}</div>
+			<div
+				class="chat-bubble"
+				class:chat-bubble-primary={rendered.role === "user"}
+				class:chat-bubble-secondary={rendered.role === "assistant"}
+				class:opacity-50={rendered.provisional}
+			>
 				{#if rendered.chunk.type === "text"}
 					<p>{rendered.chunk.text}</p>
 				{:else if rendered.chunk.type === "thinking"}
@@ -20,26 +22,26 @@
 						<p>{rendered.chunk.text}</p>
 					</details>
 				{:else if rendered.chunk.type === "tool-call"}
-					<div class="tool-call">
+					<div class="text-sm">
 						<strong>{rendered.chunk.toolName}</strong>
-						<pre>{JSON.stringify(rendered.chunk.input, null, 2)}</pre>
+						<pre class="text-xs mt-1">{JSON.stringify(rendered.chunk.input, null, 2)}</pre>
 					</div>
 				{:else if rendered.chunk.type === "tool-result"}
-					<div class="tool-result" class:tool-result--error={rendered.chunk.isError}>
+					<div class="text-sm" class:text-error={rendered.chunk.isError}>
 						<strong>{rendered.chunk.toolName}</strong>
-						<pre>{rendered.chunk.content}</pre>
+						<pre class="text-xs mt-1">{rendered.chunk.content}</pre>
 					</div>
 				{:else if rendered.chunk.type === "error"}
-					<div class="error" role="alert">
+					<div class="text-error" role="alert">
 						{rendered.chunk.message}
 						{#if rendered.chunk.code}
-							<span class="error__code">[{rendered.chunk.code}]</span>
+							<span class="text-xs opacity-70">[{rendered.chunk.code}]</span>
 						{/if}
 					</div>
 				{:else if rendered.chunk.type === "system"}
-					<div class="system">{rendered.chunk.text}</div>
+					<div class="text-sm opacity-70">{rendered.chunk.text}</div>
 				{/if}
 			</div>
-		</article>
+		</div>
 	{/each}
 </div>
-- 
cgit v1.2.3

