diff options
| author | Adam Malczewski <[email protected]> | 2026-06-27 19:15:04 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-27 19:15:04 +0900 |
| commit | fa0bd9c0e433b1abddc814b48a358c94954c7d36 (patch) | |
| tree | dedf7adf827de6908b5c7cbb4b4153aba38c1882 /src/app/App.svelte | |
| parent | 3566a20ebbded754070fce66af48690d1a904879 (diff) | |
| download | dispatch-web-fa0bd9c0e433b1abddc814b48a358c94954c7d36.tar.gz dispatch-web-fa0bd9c0e433b1abddc814b48a358c94954c7d36.zip | |
feat(vision): consult_vision tool + vision settings API
Backend vision update (additive to [email protected] / [email protected],
no version bump).
Contracts mirrored (.dispatch/transport-contract.reference.md):
- VisionSettingsResponse + SetVisionSettingsRequest (GET/PUT /settings/vision).
- Delta note: read_image -> consult_vision; numbered placeholders; compaction.
Tool rendering (ChatView):
- read_image test -> consult_vision (rendering is generic by toolName).
- +2 tests: numbered-placeholder text chunk + [Compacted image] text chunk
(both regular text chunks, render as-is — no special handling).
New vision feature library (src/features/vision/):
- logic/view-model.ts (32 tests): VisionSettings/VisionSettingsPatch types
(consumer-defines-port), LoadVisionSettings/SaveVisionSettings ports +
results, normalizeVisionSettings (network-seam coercion), parseImageLimit/
imageLimitChanged, compactionModelOptions (vision-capable models via chat's
public isVisionModel + Auto sentinel), round-trip helpers, imageLimitLabel.
- ui/VisionSettingsView.svelte (9 tests): imageLimit input + Save,
compactionModel dropdown (Auto + vision-capable models), load-on-mount,
save-on-change, error/saved feedback.
- index.ts.
Cross-unit seam: isVisionModel added to features/chat public index.ts
(additive); imported through the public surface, not internals.
Store wiring (src/app/store.svelte.ts):
- visionSettings state + refreshVisionSettings (GET /settings/vision,
normalized at the seam) + setVisionSettings (PUT, partial, returns merged) +
VisionSettingsResult; seeded on boot; exposed on AppStore. +4 store tests.
Mounted in App.svelte: new "Vision" sidebar view kind + VisionSettingsView
in viewContent (not conversation-scoped); load/save adapters; visionManifest.
Verification: svelte-check 0/0; vitest 948/948 (run twice, +47 since the
prior vision commit); biome clean; vite build OK. See backend-handoff.md §2j.
Not merged or pushed.
Diffstat (limited to 'src/app/App.svelte')
| -rw-r--r-- | src/app/App.svelte | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/src/app/App.svelte b/src/app/App.svelte index f240a06..f41d7ba 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -77,6 +77,13 @@ type SaveSystemPrompt as SaveSystemPromptAlias, manifest as systemPromptManifest, } from "../features/system-prompt"; + import { + VisionSettingsView, + manifest as visionManifest, + type LoadVisionSettingsResult, + type SaveVisionSettingsResult, + type VisionSettingsPatch, + } from "../features/vision"; import type { AppStore } from "./store.svelte"; import ErrorModal from "./ErrorModal.svelte"; import { createLocalStore } from "../adapters/local-storage"; @@ -106,6 +113,7 @@ { id: "cache-warming", label: "Cache Warming" }, { id: "tasks", label: "Tasks" }, { id: "compaction", label: "Compaction" }, + { id: "vision", label: "Vision" }, { id: "heartbeat", label: "Heartbeat" }, { id: "system-prompt", label: "System Prompt" }, { id: "settings", label: "Settings" }, @@ -143,6 +151,7 @@ settingsManifest, systemPromptManifest, heartbeatManifest, + visionManifest, ].map((m) => [m.name, m.description] as const); // Smart-scroll: keep the transcript pinned to the bottom while it streams, @@ -304,6 +313,27 @@ : { ok: false, error: result.error }; } + // Adapt the store's global vision-settings API to the vision feature's ports. + async function loadVisionSettings(): Promise<LoadVisionSettingsResult> { + // The store seeds `visionSettings` on boot; a refresh keeps it current. + await store.refreshVisionSettings(); + const settings = store.visionSettings; + if (settings === null) { + return { ok: false, error: "Vision settings not available." }; + } + return { ok: true, settings }; + } + + async function saveVisionSettings( + patch: VisionSettingsPatch, + ): Promise<SaveVisionSettingsResult> { + const result = await store.setVisionSettings(patch); + if (result === null) return { ok: false, error: "Vision settings not available." }; + return result.ok + ? { ok: true, settings: result.settings } + : { ok: false, error: result.error }; + } + // Adapt the store's chat-limit result to the settings feature's port. On a // raise the active chat refills (prepends older history); preserve the // reader's viewport over the prepend (the manual analogue of CSS scroll @@ -660,6 +690,14 @@ savePercent={saveCompactPercent} /> {/key} + {:else if kind === "vision"} + <!-- Global vision settings (image compaction). Not conversation-scoped (no {#key}). --> + <VisionSettingsView + models={store.models} + modelInfo={store.modelInfo} + load={loadVisionSettings} + save={saveVisionSettings} + /> {:else if kind === "system-prompt"} <!-- Global system prompt template. Opens a full-page modal editor (half template / half variable palette). Not conversation-scoped (no {#key}). --> |
