summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/app/App.svelte8
-rw-r--r--src/app/App.test.ts23
2 files changed, 28 insertions, 3 deletions
diff --git a/src/app/App.svelte b/src/app/App.svelte
index f0cd7ec..e50648a 100644
--- a/src/app/App.svelte
+++ b/src/app/App.svelte
@@ -125,6 +125,7 @@
{ id: "cache-warming", label: "Cache Warming" },
{ id: "tasks", label: "Tasks" },
{ id: "compaction", label: "Compaction" },
+ { id: "vision", label: "Vision" },
{ id: "heartbeat", label: "Heartbeat" },
{ id: "concurrency", label: "Concurrency" },
{ id: "system-prompt", label: "System Prompt" },
@@ -814,8 +815,7 @@
{/key}
{:else if kind === "compaction"}
<!-- Message compaction is per-conversation (keyed so the percent + feedback
- can't bleed across tabs). Vision (image-compaction) settings are GLOBAL,
- so they stay mounted across conversation switches (no {#key}). -->
+ can't bleed across tabs). -->
{#key store.currentConversationId}
<CompactionView
percent={store.compactPercent}
@@ -824,7 +824,9 @@
savePercent={saveCompactPercent}
/>
{/key}
- <div class="divider my-1 text-xs text-base-content/40">Image compaction</div>
+ {:else if kind === "vision"}
+ <!-- Global vision settings (image-compaction limit + model). Not
+ conversation-scoped (no {#key}), so they stay mounted across tab switches. -->
<VisionSettingsView
models={store.models}
modelInfo={store.modelInfo}
diff --git a/src/app/App.test.ts b/src/app/App.test.ts
index dcdcb9b..f43a6b8 100644
--- a/src/app/App.test.ts
+++ b/src/app/App.test.ts
@@ -73,6 +73,11 @@ function fakeFetchImpl(): typeof fetch {
status: 200,
});
}
+ if (url.endsWith("/settings/vision")) {
+ return new Response(JSON.stringify({ imageLimit: 10, compactionModel: null }), {
+ status: 200,
+ });
+ }
return new Response(JSON.stringify({ chunks: [], latestSeq: 0 }), { status: 200 });
};
}
@@ -583,4 +588,22 @@ describe("App component interaction tests", () => {
store.dispose();
});
+
+ it("the Vision view renders the vision settings UI (image limit + compaction model)", async () => {
+ const ws = fakeSocket();
+ const store = createAppStore({
+ socketFactory: () => ws,
+ fetchImpl: fakeFetchImpl(),
+ localStorage: createFakeStorageWithViews(["vision"]),
+ });
+ ws.resolveOpen();
+
+ render(App, { props: { store, onNavigate: vi.fn() } });
+
+ // The vision settings load (`GET /settings/vision`) seeds the inputs.
+ expect(await screen.findByLabelText(/Image limit/)).toBeInTheDocument();
+ expect(await screen.findByLabelText(/Compaction model/)).toBeInTheDocument();
+
+ store.dispose();
+ });
});