Commit Graph
5 Commits
Author SHA1 Message Date
adrianbonpin 400f81fa6c fix: stop reporting a timezone's name as the user's city
get_location printed "City: Manila", derived from the last segment of the
IANA zone name. That is the timezone database's representative place, not
the user's: Asia/Manila is one zone covering the entire Philippines,
Asia/Kolkata covers all of India, Europe/Oslo all of Norway. It was wrong
for anyone outside the zone's namesake, and confidently so, because a model
asked about the weather uses it without hesitating. The README claimed the
value was region-level, so the output also contradicted its own docs.

- The zone's label is still reported, as `Timezone area`, and named for what
  it is. Both the field and the tool note say outright that it is not the
  user's city, and the tool description repeats it.
- The locale region subtag is now used, which is a declaration rather than an
  inference: a locale of en-PH is the Philippines because the user set it.
- A Location preference was added. It is the only exact place this extension
  has, it is reported verbatim and first, and when it is set the note tells
  the model to prefer it over anything inferred.
- The continent grouping from the zone name was dropped: "Asia" next to a
  region of "Philippines" reads as two answers to the same question.
- ToolContext takes the place as an injected value, keeping tools.ts free of
  Vicinae imports as the module wiring test requires.

Verified: 316 tests, tsc, eslint, prettier, and vici build.
2026-10-04 15:50:45 +08:00
adrianbonpin 5f359778b1 fix: take the launcher window out of the shot when snipping
The snip was selecting a region around the launcher panel sitting in the
middle of the screen. `activateEntrypoint` calls `showWindow()` when it
activates a command from a closed launcher, so a view command's window is
already up before any extension code runs. Capturing from inside the chat
view could never have worked.

- `snip` is now a `no-view` command. It calls `closeMainWindow()` first,
  waits out the 50ms hide that `ui-service.hpp` schedules, then captures
  with nothing on screen, then brings the window back by launching `chat`
  with the file. `settleWindow()` exists because a capture started the
  instant the close resolves could still find the window there.
- The chat's action no longer captures inline. It launches `snip` and
  passes the session id, so there is one capture path, one capability
  check, and one place that deals with the window.
- The hand-off relays through `snip` rather than going straight back to
  `chat` on purpose: `ActivateEntrypointOptions::toggleIfAlreadyActive`
  defaults to true, so launching the *already active* view command closes
  the window instead of reopening it. A launch cannot override that flag,
  but relaying through a different no-view command makes the toggle branch
  unreachable.
- The action is hidden while streaming, since the hand-off unloads the
  view and would abandon an answer mid-sentence.
- Vision is settled before the selector appears, so a model that cannot
  read images never costs a selection gesture. `no` and `unknown` get
  different HUD wording.
- `lib/snip.ts` holds the hand-off contract; `lib/hooks/useSnip.ts` is
  gone, as nothing captures from a view any more.

Verified: 252 tests, tsc, eslint, prettier, and vici build. The deployed
manifest now reports `snip mode=no-view`.
2026-10-04 15:26:55 +08:00
adrianbonpin 5e083d8142 feat: snip a region into chat, and make links clickable
Two changes: screenshot input gated on the model really reporting vision,
and a fix for URLs that arrived as code.

Links
- A fence whose body is all URLs, a lone `https://...` inline span, and a
  line that is only a URL are rewritten into [url](url) markdown links.
  Vicinae attaches only the table and strikethrough GFM extensions, so
  autolink is off and a bare URL is dead text; clickability comes from
  MarkdownModel turning CMARK_NODE_LINK into an <a href>. A fenced URL was
  a dark panel with a Copy button and no way to open it.
- promoteStandaloneCodeSpans was itself promoting a lone URL span into a
  fence, so it made this worse. Link restoration now runs first.
- The default prompt says never to fence a URL.
- Anything that is not one of those three shapes is copied through, so a
  curl invocation containing an address is never rewritten.

Screenshots
- capture.ts shells out to an auto-detected tool: grimblast, grim + slurp,
  spectacle, maim, scrot, flameshot, or screencapture on macOS. There is
  no screenshot call in @vicinae/api and Clipboard moves no binary, so the
  extension owns the capture and reads the file it wrote rather than
  guessing at the clipboard. Cancel is detected by whether an image landed
  on disk, because the tools disagree on exit codes.
- In chat: "Snip a Region" in the action panel (cmd+shift+S) attaches to
  the current conversation. Standalone: "Snip to Lam" starts a new one.
- vision.ts caches the /api/show capability answer per host and model. A
  failed check is "unknown", never "no", so a dropped connection is not
  reported as a fact about the user's model. Actions still fail closed.
- Only the path is stored, base64 is read at send time, and the image goes
  on its own turn only. Capture is never a model-callable tool.

Verified: 244 tests, tsc, eslint, and vici build (both entrypoints).
2026-10-04 15:13:15 +08:00
adrianbonpin ee96f1ee61 feat: display toggles, smart session resume, fix stale transcript pane (#8) 2026-10-03 21:22:04 +00:00
adrianbonpin 8b507e7953 feat: initial Lam Agent extension (#1) 2026-10-03 20:32:40 +00:00