- Agent: pass the open email reader (uid/folder/account/from/subject/body
preview) on every chat submit so 'reply to this' / 'write email saying
hi' route to ui_control open_email_reply with the right UID instead of
inventing a new .md draft. Code-level enforcement (chat_routes strips
create_document + send_email when active_email is set); cross-session
active_doc_id is now trusted instead of being silently dropped.
set_active_email/clear_active_email tool-layer helpers in
tool_implementations.
- ui_control open_email_reply: optional body argument so the agent can
open-and-write in one call; envelope now forwards uid/folder/account/
body/panel through tool_output. Tool description sharpened and the
parser rejects empty bodies on reply/reply-all (forces the agent to
write rather than open an empty draft).
- Email library: search now runs against [Gmail]/All Mail when the
current folder is INBOX (archived emails surface). Whirlpool spinner
+ 'Searching…' placeholder while in flight. Each search result is
stamped with its source folder so clicks open the right email instead
of whatever shares its UID in INBOX. Search no longer re-applies the
same text pill locally (which only checks subject/from/snippet, never
body) so body-only matches don't get dropped after IMAP returns them.
Initial inbox load bumped 100→500.
- Email favorites: 'Favorite (pin to top)' / 'Unfavorite' in both the
card menu and the open-reader more menu, backed by a new
/api/email/flag/{uid}?on=true|false endpoint. Flagged emails always
bubble to the top of the grid regardless of active sort.
- AI reply in doc editor: never overwrites existing draft text or the
quoted history. AI suggestion is prepended; AI-generated 'On …
wrote:' re-quotes are stripped so the original quote isn't visually
edited.
- Cookbook serve: pre-launch GPU driver / has_gpu / install / version-
floor checks (vllm minimax_m2 needs 0.10.0+, deepseek_r1 needs 0.7.0
etc.) before the launch chain starts. Detect 'another model already
running on this host' and offer Stop & launch (with graceful then
force tmux kill helpers, port release wait). Per-vendor deep-link
buttons (vLLM recipe / SGLang cookbook) with hardware hash. Backend
picker is now a custom dropdown with accent-coloured logos for vLLM,
SGLang, llama.cpp, Ollama, Diffusers; same glyphs added next to
package names in Dependencies. Runtime-readiness note moved inside
the panel (green when ready, red when missing) with an × dismiss.
Esc collapses the expanded card; expanded card scrolls when it
overflows; Trust Remote / Auto Tool / Reasoning Parser / Enforce
Eager / Prefix Caching / Expert Parallel / Speculative / MoE Env on
one row (Reasoning Parser auto-detected per model family).
Dtype→Row 1, GPUs→Row 2 (rightmost). Removed redundant GPU 'auto'
input — command builders read from the GPU button strip. Default
cookbook open is Download tab.
- Cookbook hwfit: 'Model (latest)' / 'Model (oldest)' header sorts by
release_date; release dates can be backfilled with the new
scripts/backfill_model_release_dates.py and recipe metadata pulled
with scripts/import_from_vllm_recipes.py against the upstream
vllm-project/recipes catalog (vllm_recipe + min_vllm_version stamped
on entries).
- Calendar: Quick add hint cycles a random Odysseus-themed example per
open (wooden horse Friday, crew muster 10am daily, council on
Ithaca, …). Typing a time like '11pm' in the event title updates
the hero clock live.
- Doc editor: email-mode Reply button (sparkle icon, accent) opens the
same Fast/Full + context popover the email reader uses; Ctrl+Alt+M
toggles markdown preview.
- Memories panel: custom sort picker with per-option icons, default
'Latest', visible Enabled/Disabled toggle text matching the section
description style.
- Each input now has a sibling .email-field-prefix span (To / Cc /
Bcc / Subject) absolute-positioned at the left edge in the
accent color. Inputs get padding-left:44px (64px for Subject)
so typed text doesn't slide under the prefix.
- Placeholders shrink back to just the example so only the
prefix gets the accent color, not the example text.
- Cc toggle moved another 2px up (calc(50% + 4px) → calc(50% + 2px)).
- Removed the <label>To/Cc/Bcc/Subject</label> elements — they
doubled what the placeholder said.
- Placeholders now carry both the field name AND an example so an
empty input still tells the user what to type:
To recipient@example.com
Cc cc@example.com, example2
Bcc bcc@example.com
Subject
Adds a per-field X (24x24 SVG, opacity 0.4 → 1 + accent on hover)
absolute-positioned at the right edge of each Cc/Bcc field. Click
hides both rows, clears their inputs, and restores the Cc opener
on the To row. Inputs get padding-right:32px so the close button
doesn't overlap typed text.
The document editor stores the AI-edit diff state (_diffModeActive,
_diffOldContent, _diffNewContent, _diffChunks) as a module-global
singleton bound to whatever document was active when the diff opened,
and every document shares one #doc-editor-textarea. When the active
document is switched while an unapproved diff is open, the stale diff
must be discarded first or a later exitDiffMode (tab switch /
Accept-Reject-All) flushes the old document's content into the new
active document and overwrites it (issue #2467).
Guard both paths that switch the active document for an AI update,
while activeDocId still points at the previously-active doc:
- handleDocUpdate(): a doc_update targeting a different document.
- streamDocOpen(): the AI streaming a NEW document — this runs first on
that path, so a guard only in handleDocUpdate would fire too late and
still overwrite the streamed document.
Both reuse the exact `if (_diffModeActive) exitDiffMode(true);` guard
switchToDoc() and enterDiffMode() already use.
Fixes#2467
* fix: prevent document link click from resetting active session
Clicking a #document-<uuid> link in chat caused the session to reset
because of two issues:
1. chatRenderer.js: clicking on the text inside an <a> yields a Text
node target whose .closest() is undefined, so preventDefault never
fires and the browser performs a default hash-navigation
2. sessions.js: the hashchange handler treated the entity hash
(document-<uuid>) as a session lookup, found no match, and the
subsequent loadSessions created a new default-model chat
Fix: walk past Text nodes before calling .closest(), and skip
entity-prefixed hashes in the hashchange handler.
Fixes#2035
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(documents): move isOpen=true after container check in openPanel
isOpen was set to true before the #chat-container existence check.
If the container was missing during a race, the function returned
early but isOpen stayed true, preventing the panel from ever
reopening on subsequent calls.
Move isOpen=true to after the container guard so a failed open
doesn't leave the flag stuck.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chat models often emit GitHub/Slack-style :shortcode: text (e.g. 😊,
🎤) instead of the actual emoji. The renderer only converted real
Unicode emoji to the monochrome line icons, so shortcodes rendered as literal
text.
Add a pure, browser-free shortcode->Unicode map (emojiShortcodes.js) and run it
inside svgifyEmoji ahead of the existing Unicode->SVG pass, skipping <code>/<pre>
so code stays literal. Covers ~430 common shortcodes plus common aliases
(+1/thumbsup, etc.).
Keep the conversion from touching anything it shouldn't:
* Scope it to chat. mdToHtml/svgifyEmoji take a { shortcodes } option (default
on); document and email body rendering (compose, export, preview) pass it as
false so author-typed :shortcode: text stays literal. The Unicode->SVG pass
still runs there exactly as before.
* Only convert a :shortcode: that stands on its own. A word-boundary guard
leaves embedded colon runs alone, so "1:100:2", "10:30:45", "16:9" and
host:fire:port are never rewritten.
Tests: extend the node-driven unit test with the boundary/false-positive cases,
and fix the markdown-rendering test loader to resolve the new emojiShortcodes
import.
Add a hashchange handler for #document-<id> so refresh / URL-bar nav opens the document, and replace the silent console.error in loadDocument with a user-facing toast.
Closes#560