feat: render the attached screenshot instead of just naming it
The transcript said "_Screenshot attached_" and left it at that, on a note in the test file claiming Vicinae could not resolve a local image. It can. That claim was true of a URL the model invented, not of a file this extension captured itself. Checked against the renderer rather than assumed: - `imageProviderUrl` in `markdown-model.cpp` routes `file://`, and a scheme-less string, through `ImageURL::local`. Anything it does not recognise comes back empty, which is why a bare path is not good enough. - `MdImage.qml` draws it block-level and centred, using the alt text as an italic caption, and caps the rendered height at 200px regardless of the `raycast-width`/`raycast-height` hint it also accepts. So `attachments.ts` gains `imageSource`, which builds a `file://` URL with `pathToFileURL` so a path with a space or non-ascii still resolves, and `markdown.ts` gains `renderAttachments`, used for a turn's attachments and for an unsent pending snip. That second use is the one that matters most: the capture is now visible before the question about it exists. The alt text carries the label, so a deleted file degrades to what the transcript used to say. The 200px cap is a Vicinae limit, so the action panel keeps Open Screenshot for the full image. Tests that asserted the old behaviour are inverted, and imageSource is covered for spaces and non-ascii. Verified: 280 tests, tsc, eslint, prettier, and vici build.
This commit is contained in:
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { readAttachment } from "../attachments";
|
||||
import { useChat } from "../hooks/useChat";
|
||||
import { useVisionSupport } from "../hooks/useVision";
|
||||
import { buildThreadMarkdown, type TranscriptOptions } from "../markdown";
|
||||
import { buildThreadMarkdown, renderAttachments, type TranscriptOptions } from "../markdown";
|
||||
import { getLamPreferences } from "../preferences";
|
||||
import { deleteSession, listSessions, loadSession, summarizeSession } from "../sessions";
|
||||
import { resolveStartup } from "../startup";
|
||||
@@ -59,7 +59,7 @@ const EMPTY_TRANSCRIPT = "**Ask Lam anything**\n\nType a question in the search
|
||||
const DEFAULT_IMAGE_PROMPT = "What is in this image?";
|
||||
|
||||
/** The reminder shown while a snip is attached but not yet sent. */
|
||||
const PENDING_NOTE = "_Screenshot attached. Ask a question about it, or press Return to send it as it is._";
|
||||
const PENDING_NOTE = "_Ask a question about it, or press Return to send it as it is._";
|
||||
|
||||
export interface ChatViewProps {
|
||||
/** Opens a specific conversation instead of resuming the latest one. */
|
||||
@@ -182,9 +182,13 @@ export function ChatView({
|
||||
* already looking.
|
||||
*/
|
||||
const threadMarkdown = useMemo(() => {
|
||||
const notes = [launchNote, snipNote, pending ? PENDING_NOTE : undefined].filter((note): note is string =>
|
||||
Boolean(note),
|
||||
);
|
||||
const notes = [
|
||||
launchNote,
|
||||
snipNote,
|
||||
// The preview comes first so the captured image is visible before the user
|
||||
// decides what to ask about it, rather than only after sending.
|
||||
pending ? `${renderAttachments([pending])}\n\n${PENDING_NOTE}` : undefined,
|
||||
].filter((note): note is string => Boolean(note));
|
||||
|
||||
const transcript =
|
||||
thread.length === 0 ? EMPTY_TRANSCRIPT : buildThreadMarkdown(thread, transcriptOptions);
|
||||
|
||||
Reference in New Issue
Block a user