- Groups (like projects in Claude): below New chat and Files in the sidebar is a Groups entry leading to the groups page. It lists each group (item count, last activity, its preset and folder); open one to rename it, pick a preset, set a default folder, see its chats, or ungroup it. New chats in a group use the group's preset and bind the group's folder. In the history, groups are listed below the pinned chats, each headed by a 集 seal with a vermilion line on the left; the + on the group header starts a new chat in that group. A chat's ⋯ menu moves it into a group, to another group, or out. Next to the input box's + is the group the chat belongs to (removable on the welcome page; on a chat page it opens the groups page). Pinning a chat inside a group only lifts it within the group, marked with a small vermilion diamond. Ungrouping only dissolves the group; its chats return to the loose list. Groups are stored in the settings and shared by all browsers
- Multiple chats, create and delete; the sidebar searches titles and text, and supports pinning and renaming (double-click in the sidebar or click the chat title). Whether the sidebar is collapsed is remembered locally across reloads, and so is the page you were on (the open chat, Files): reloading brings you back
- As soon as the first question is sent, the model is asked for a title in parallel with the answer, so the sidebar shows a proper name right away (can be turned off under Settings → Roles, using this chat's own model; titles you've edited by hand are never overwritten). If the title request happens to fail, the chat isn't marked as titled; it tries again when that answer finishes (this time with the answer). The title is based only on this chat's questions and answers, keeps project, task or topic keywords, and is short and plain. If the first question is edited while titling, it re-titles from the new content
- New chats and every existing chat keep their own unsent drafts; text and pending attachments come back after a reload, switching chats or opening Files
- The Context ≈ n at the lower right estimates, live, how many tokens the next question will send (system prompt, tool definitions, history and step summaries since the last compaction, the draft you're typing, pending attachments and quotes); it moves as you type and as replies stream, and turns the accent color when heavy. Click it to compact earlier messages: the model condenses the exchanges so far into a summary (goals, facts, decisions, files changed, to-dos), recorded on a divider you can open and read. Compacted messages fold away on the page (expandable from the divider), and from then on each question carries only the summary and the messages after it. Compaction also happens automatically, based on the model's Context window: if the window is set, once an answer finishes and the next question is estimated at 75%, it compacts while you read and write; if not, when the API replies that the request doesn't fit, it compacts everything before this question into a summary on the spot and sends again — windows differ between models, and it copes even if you don't fill it in. To compact earlier and spend fewer tokens, set a smaller window. The summary is generated off to the side and becomes a divider all at once, only when done: sending messages during compaction loses nothing, and closing the page midway never leaves a half-made divider cutting off the history. If Context window is set in the model's advanced settings, the counter shows
n / windowwith a thin line underneath in proportion, turning the accent color at 75%. Tokens used n at the end of each answer is the total usage of all that answer's requests (subagents included) - In the history sent with the next question, each answer carries a one-line Steps summary: files read and changed, replies to requests, subagent reports, the top three titles and links from searches, and web pages read — so the model needn't explore again from scratch. Raw tool results and thinking are not replayed
- Once a chat has more than one question, a navigation strip appears on the right: one short dash per question, the current one longer and in the accent color; hover to see each question's first line, click to jump back to it. While compacted messages are folded, the strip lists only the questions after the divider; Show earlier messages lists them all
- Quote and ask: select text in a reply or in your own message and a Quote button floats up; click it to bring the text into the input box as a quote (remove it with ✕ or Esc; it's saved with the draft). Once sent, it shows above your question; clicking it jumps back to the source. It's sent to the model as a
>block before the question - Side threads: select a passage in a reply and choose Side thread; a small conversation attached to that spot opens on the right. It can read the main thread (up to the annotated message, respecting compaction); the main thread never reads it. It can read whatever there is to read but changes nothing: search, web pages, API requests (GET and HEAD only), documents, memory and past chats, reading and searching files in the folder, read-only commands (git log / diff, Get-Content and the like — no leaving the sandbox, nothing sent to the background), plus the MCP tools a server marks read-only — GitHub's viewing tools, the stage's snapshots and screenshots and the like: it doesn't touch files, ask for approval or use subagents, and its steps are drawn in the side reply's own Steps. You can open a side thread on an earlier reply and copy while the main thread is still generating; scrolling up while a side thread generates stops it pulling you back to the bottom. The annotated message's info line is marked 注 (注 n for several), and the chat title shows the side-thread count. Side thread in the reply actions, and the count under the title, open the list page: one line per side thread in this chat (the annotated passage, which answer, how many questions, last activity); click one to open it. The + at the top starts another: if a passage is selected in the main thread, it annotates that passage; if not, it's about the whole reply — one reply can have several. When viewing one, the top of the panel shows the annotated passage (not for whole-reply threads), ‹ › switches between threads, ‹ List at the lower left returns to the list, Widen fills the page and Delete removes it. Questions in a side thread can be edited and re-asked, answers regenerated — side threads keep no versions; a change is final. A side thread follows the question and answer it annotates: switch to another branch, and threads on the messages swapped out are out of sight (still kept; switch back and they return). The anchor is stored as the message id, the selected text and which occurrence it is in the text — if the same word appears twice in a reply and you annotated the second, it lands on the second after redrawing. It's found afresh in the clean text each time: overlapping passages each land correctly, and a selection crossing an existing side-thread footnote mark is fine
- Before any model is connected, the welcome page shows a guide. Settings have twelve tabs: General, Appearance, Model, Presets, Tools, Roles, Environment, MCP, Skills, Browser, Memory, Docs. Roles holds the models that do one job each besides the main reply: titles (this chat's own model, just on or off), review and images (each with its own API, key and model rather than borrowing one from the Model tab, so they don't evict the main reply's cache or add load to a local model; review can also run on the signed-in ChatGPT subscription, so it needs no key of its own; how review judges is in Sandbox). Collapsed, each row shows only the role and model name; a red dot on the right means on or set up, a hollow circle means off or not set up. The image API can be SiliconFlow or OpenAI-compatible (
/images/generations); only once it's set up does the main model get generate_image: the picture is saved to Files (the working folder in Agent mode) and attached to that step, naming its file in the reply draws it in place, and models that can see images get it back with the result to check it themselves. Each tab's heading has a small brush drawing on the left and an epigraph beneath it (for mood only, not instructions; registered inSETTINGS_TABS). Docs explains how to use Yan, one topic per page, with the version after the table of contents; data boundaries, keys and gestures, and open-source credits are there too - Seasons: the interface shifts a little with the 24 solar terms — the welcome title notes the current term, the heading text is still picked by hour with two lines for the season mixed into the pool; the paper tint leans slightly with the season (spring a little green, summer whitest, autumn a little warm, winter a little cool); a light ink drawing for the season sits at the lower right of the text (spring willow, summer lotus, autumn reeds, winter plum), alongside the moon at the top right in dark mode. The English interface only changes the paper tint and drawing with the season
- The send button is a seal: empty when there's nothing to send, inked when there's text, turning slightly on hover as if about to stamp; it reads 寄 (send). While generating it reads 止 (stop), with an ink ring circling; click it to stop
- Streaming replies are written at an even "ink" pace (the more backlog, the faster, lagging at most a fraction of a second); new characters fade in briefly and a pen-tip cursor follows the end. The small seal in the reply's info line breathes while writing and stamps down when done. Settings → Appearance sets interface motion to on, follow system, or off
- You can say something mid-answer: once you type in the input box, the seal changes from 止 to 寄, and what you send is a follow-up — guidance, not a queue, and it doesn't cut the model off hard. It first lands in Steps at the moment it arrived (a step "Follow-up · Pending"), then waits for a natural stopping point: if the model is thinking, until the thinking is done (however long); if it's drafting a tool call, until the call is complete, delivered when the result comes back (a running foreground command is moved to the background on the spot so its result returns immediately); if it's writing text, until the end of a sentence or paragraph, or until a code fence closes. Only then does this round's stream stop; what's written stays, and the model is asked to speak again with your words, which it reads and continues from, possibly changing course (the history holds "what was written + the follow-up"; the step is marked "Steering"; the stopped round's usage is estimated; the stop point steps back over the last few characters past a sentence end). If the stream ends before a stopping point and there's no next round, the follow-up is sent as a new question once the answer is written; if you press stop midway, it goes back into the input box. The seal only means stop when the input is empty
- Stop, regenerate, retry on failure, copy. Switching or creating chats never interrupts running requests; several chats can generate in the background at once, and the sidebar shows running, awaiting approval and unread states. On a shaky network it holds on: if the API didn't take the request (unreachable, rate-limited, 5xx, overloaded), it retries with backoff (intervals growing from 1 second to 1 minute, about five minutes in total for remote APIs, only four tries for local models), honors Retry-After, and waits for the network to come back; the status bar says "Network unstable · retry n". If a reply breaks off midway, what's written stays and the model is asked shortly to continue from the break, at most twice per round. If that still fails, or the page is reloaded, the generated text is kept and Continue is offered
- Following while generating: scrolling down to near the bottom counts as reaching it and starts following (content keeps growing, and chasing the last pixel often fails); you must scroll more than 80px up from the bottom to stop following. While following, the view stays pinned to the bottom however the content grows (tool output, charts rendering, images loading, blocks opening and closing), not just on each streaming frame
- Branches: editing a question or regenerating no longer truncates; the replaced part is kept whole as a version, switched with "‹ 2/2 ›" beside the message. Versions can nest, and the current message stays in place when switching. The backup format is v4 as a result (old backups import directly)
- Scrolling up while generating shows a Jump to latest button. Long chats save the scroll position by a visible message anchor, returning you to where you were reading after closing settings, switching chats, or when an async chart finishes
- Thinking models'
reasoning_content, and<think>…</think>that some APIs write at the start of the text, are shown in the Thinking fold. It's expanded while reasoning streams and collapses once the text starts; if you open or close it by hand, your choice is kept - The rules for automatic opening and closing (shared by Thinking, Steps and subagent cards): an answer opens once and closes once — expanded when it starts, collapsed only when the whole answer is done, not flipping back and forth in the gaps when the model speaks; it pauses a beat before closing so the check mark lands; if you're scrolled up reading that block, it's left for you; anything you've opened or closed by hand is never touched
- The message list syncs incrementally by id: only messages that changed are inserted, replaced or removed, and every other node (including mounted interactive content) stays as is. When the theme, accent color or font changes, interactive content recolors in place
- Markdown is parsed by marked and sanitized by DOMPurify: tables, nested lists, task lists, highlight.js code highlighting, language labels and copy buttons on code blocks, KaTeX math (
$…$,$$…$$,\(…\),\[…\]) - Linked images in replies (
) aren't fetched by the page: it only allows local images (CSP), otherwise text on a web page could talk the model into writing an image address with data in its parameters, and simply drawing it would send that data out. A linked image first shows as a dashed paper slip with its name and the host it comes from; click it and the bridge fetches it (POST /api/remote-image, the same address gate as reading pages, images only, up to 15 MB) and it is drawn like any other image, opening in the image viewer where it can be saved. Images fetched stay shown while the page is open; after a refresh, click again. If it can't be fetched the slip says why (404, not an image, a private address…), and clicking again opens it in the system browser - In-page visuals take one path only: the model writes a self-contained
html block, rendered in place as an interactive block inside an isolated sandbox with no network and no access to the parent page (`preview.html` + `preview-runtime.js`), on the same sheet as the text. Yan's palette, fonts and theme are passed in as variables like `var(--ink)` and `var(--accent)`; the background is transparent, height follows the content, common elements are already styled, and there are a few small pieces: `.card` `.row` `.grid` `.tag`. There's no frame: pointing at the block brings up a single fullscreen icon in the margin to the right of the text column (at the top right of the block in side notes); rerun and download live in the header shown in fullscreen and on error, and the frame returns only on error or in fullscreen. For data charts write `<script src="yan:echarts"></script>` and use `echarts.init` as usual (a theme matching Yan's palette is preconfigured, and it re-lays out when the width changes; a few common slips in the model's options — a series pointing at a missing axis, an axis pointing at a missing grid, a missing type — are straightened out before drawing). For flowcharts, sequence and structure diagrams write `<pre class="mermaid">…</pre>` and it renders automatically. Both libraries ship with the project and load on demand only inside that block; the page itself no longer carries them. Changing theme, vermilion tone or font recolors blocks already on the page in place, without rerunning and without losing their state. Oldmermaid / ```echarts fences in past chats become an equivalent HTML page and still render. While the code is still being written, a placeholder frame goes up first: inside is a sketch of a page layout, traced stroke by stroke in light ink, pausing when done, fading, and tracing again. Each incoming line dips the next stroke of the sketch in vermilion, which fades back; while streaming the vermilion strokes rise and fall, and when the stream stops the sketch only traces itself, so you can tell at a glance whether it's still writing or stuck. The frame's edge has a breathing ink glow; the placeholder isn't rebuilt on each frame, so the rhythm doesn't restart. It mounts when complete - KaTeX and pdf.js load on first use (mermaid and ECharts only inside an interactive block); the first screen carries only marked + DOMPurify + highlight.js (about 200 KB). Theme, accent color and font are written by
theme-boot.jsbefore styles are parsed, so dark mode doesn't flash off-white on load - Light and dark themes; fonts (Mixed, Sans, Serif, Kai, Fangsong — for replies and headings; Kai and Fangsong come from the system and fall back to Serif where missing) and accent color (reading width has no option; it's fixed at 860px). The paper has a faint texture: a ring of tea stain in the corner in light mode, a moon halo at the top right in dark mode. Switching themes: light to dark is "ink drop" — three drops fall one after another, soak into the paper, spread from where they land with ragged edges, and the moment the ink covers the sheet the paper turns dark, the ink recedes and the text emerges; dark to light is "daylight" — light has no shape, it spreads softly from where you pressed, whitens the whole sheet, then recedes as the text emerges. The ink blots and halos are a few SVGs with turbulence filters, each rendered once into a colored bitmap when idle after startup; the animation only scales the bitmap and fades it, entirely on the compositor (no masks, no filter animation, no View Transitions, so even a 240 Hz screen doesn't drop frames; the theme reflow is hidden under the ink). With motion off it switches directly. The three items at the right of the top bar share the brush: connection status is a dot of seal paste (hollow when idle, breathing vermilion when busy, red when disconnected); in the middle an inkstone (in light mode an empty stone with a hanging drop, in dark mode filled with ink and one glint of paper); on the right, a stroke for tokens left
- In narrow windows (such as a VS Code side-panel preview) the sidebar opens as a drawer and closes when you click outside; the tab title follows the current chat