summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/ui/ChatView.svelte
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-07 00:21:04 +0900
committerAdam Malczewski <[email protected]>2026-06-07 00:21:04 +0900
commit979fd1aac559805e05b36369e0fb756a8ec517dd (patch)
treed7d69d8a80a52a9cf14a54d7cb92e16cdb732a75 /src/features/chat/ui/ChatView.svelte
parent5d9ae1849337b64af1b0d47c23b8c4950a55f792 (diff)
downloaddispatch-web-979fd1aac559805e05b36369e0fb756a8ec517dd.tar.gz
dispatch-web-979fd1aac559805e05b36369e0fb756a8ec517dd.zip
Slice 2 wave 2: IndexedDB cache adapter + chat feature
- adapters/idb: createIdbChunkStore implements the ConversationChunkStore port over IndexedDB (compound [conversationId,seq] key, idempotent append, meta store for lastAccess); 8 tests with fake-indexeddb - features/chat: createChatStore (runes-thin over the core/chunks reducer, all effects injected via ChatTransport/HistorySync/ConversationCache ports) + ChatView/Composer svelte-thin UI; folds chat.delta, syncs on turn-sealed, hydrates from cache then catches up; 25 tests Verified green: svelte-check 0/0, vitest 202, biome clean, build ok.
Diffstat (limited to 'src/features/chat/ui/ChatView.svelte')
-rw-r--r--src/features/chat/ui/ChatView.svelte45
1 files changed, 45 insertions, 0 deletions
diff --git a/src/features/chat/ui/ChatView.svelte b/src/features/chat/ui/ChatView.svelte
new file mode 100644
index 0000000..a7c39cc
--- /dev/null
+++ b/src/features/chat/ui/ChatView.svelte
@@ -0,0 +1,45 @@
+<script lang="ts">
+ import type { RenderedChunk } from "../index";
+
+ let { chunks }: { chunks: readonly RenderedChunk[] } = $props();
+</script>
+
+<div class="chat-transcript" role="log" aria-live="polite">
+ {#each chunks as rendered (rendered)}
+ <article
+ class="message message--{rendered.role}"
+ class:message--provisional={rendered.provisional}
+ >
+ <header class="message__role">{rendered.role}</header>
+ <div class="message__content">
+ {#if rendered.chunk.type === "text"}
+ <p>{rendered.chunk.text}</p>
+ {:else if rendered.chunk.type === "thinking"}
+ <details>
+ <summary>Thinking</summary>
+ <p>{rendered.chunk.text}</p>
+ </details>
+ {:else if rendered.chunk.type === "tool-call"}
+ <div class="tool-call">
+ <strong>{rendered.chunk.toolName}</strong>
+ <pre>{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}>
+ <strong>{rendered.chunk.toolName}</strong>
+ <pre>{rendered.chunk.content}</pre>
+ </div>
+ {:else if rendered.chunk.type === "error"}
+ <div class="error" role="alert">
+ {rendered.chunk.message}
+ {#if rendered.chunk.code}
+ <span class="error__code">[{rendered.chunk.code}]</span>
+ {/if}
+ </div>
+ {:else if rendered.chunk.type === "system"}
+ <div class="system">{rendered.chunk.text}</div>
+ {/if}
+ </div>
+ </article>
+ {/each}
+</div>