feat: snip a region into chat, and make links clickable (#14)

This commit was merged in pull request #14.
This commit is contained in:
2026-10-05 08:38:50 +00:00
parent f5d1ced4d6
commit 576c5946f7
44 changed files with 3890 additions and 120 deletions
+49
View File
@@ -37,8 +37,53 @@ export interface ApiMessage {
tool_calls?: ToolCall[];
/** Only set on `role: "tool"` messages, naming the tool that produced this content. */
tool_name?: string;
/**
* Base64 image data, for vision models.
*
* Ollama only reads these on a `user` message (`gpt-oss` style tool roles have
* no images). They are the one genuinely large thing in a request, so they are
* attached to the question they belong to and never replayed later, the same
* way tool output is not replayed.
*/
images?: string[];
}
/**
* A file the user attached to a question.
*
* Only the path is stored. A screenshot runs to megabytes, and
* `MAX_STORED_CHARS_PER_TURN` exists precisely so one turn cannot dominate
* LocalStorage, so the bytes stay on disk and are read when the turn is sent.
*/
export interface Attachment {
/** Absolute path to the image on disk. */
path: string;
/** Sent to Ollama next to the base64 payload. */
mime: string;
/**
* Pixel dimensions, read from the file's header.
*
* Kept because the transcript has to lay the preview out long after the file
* was read, and Vicinae sizes an image's container to the request rather than
* to the image, which leaves dead space unless the request has the right
* aspect ratio. Absent for a format whose header we do not parse.
*/
width?: number;
height?: number;
}
/**
* An attachment that has been read and is ready to send, held in the view while
* the user types their question.
*
* The base64 payload lives here and nowhere else. It is needed for exactly one
* request, so it is deliberately not part of `Attachment` and never reaches
* storage.
*/
export interface PendingAttachment extends Attachment {
/** Base64 of the image, with no `data:` URL prefix. Ollama wants it bare. */
base64: string;
}
/** What a tool actually returned during a turn, kept for display and debugging. */
export interface ToolCallRecord {
name: string;
@@ -58,6 +103,8 @@ export interface ChatTurn {
/** Set when the turn failed, so a stored session can show what went wrong. */
error?: string;
toolCalls: ToolCallRecord[];
/** Images the user attached, kept for the transcript. The bytes are not stored. */
attachments: Attachment[];
}
export interface Session {
@@ -104,4 +151,6 @@ export interface ThreadMessage {
error?: string;
/** Activity line shown while this message is streaming, e.g. "Searching the web". */
status?: string;
/** Images attached to this question. Only ever non-empty on a user message. */
attachments: Attachment[];
}