Repository navigation
Fixes from the drowse.ai port: phone layouts, dialog focus, raw colours, R naming, the link card, recorded message lists - #71
Merged
Merged
Conversation
A stateful generate() from a message list hung the reply off the active node and recorded none of the list, so the node's path was the bare root and every replay (token anatomy, unit activity, a fork) rendered a different prompt from the one the reply was generated from: 11 positions where generation saw 46. The list is now a whole conversation: its turns land from the root through append_turn, a leading system message becomes the call's system prompt (kept in the reply's recipe), and the reply renders from those turns. A parent_node_id, raw mode, a second differing system prompt or other roles are refused (400). Stateless lists render alone as before.
Escape, Close or the scrim left focus on the body: the dialog focused its first stop on open and restored nothing. Focus now goes back to what opened the dialog while it is still a tab stop, else to the header's Menu (a Menu item that opened it is gone with the menu).
6.0 moved the reading tools into the chat's dock, which raw mode does not draw, so a completion could not change what its tokens are coloured by (RawBuffer still tints them and explains an empty colour, and the select still carries a raw-mode label). The tools are one snippet now: under the composer in a chat, under the completion's head behind its own Colors button in raw mode, sharing one open state.
…creens drowse.ai's WebKit iPhone checks found two layouts with no room. On a phone on its side (667x375) the log got 39 px between the Thread row and the dock, its words scrolled under the Thread toggle; at 844x390 the Page | Inspector layout left it 14 px. The Loom page at 440x796 gave the map 96 px from y=740 and none at 844x390. A phone is now narrow or short (520 px tall or less): App's phone layout and the stylesheets it leans on take the same query. A short phone lays the conversation out in two columns, the log under the Thread row and the dock or the inspector beside it; the loom's page scrolls to a map most of a screen tall. Opening the loom on a phone folds the thread, and the loom's column grows with its tools rather than clipping the map under them. scripts/workbench-layout.test.mjs reads the layout from the CSS; the CSS rule walker moves to markup.mjs for it and modal-surfaces.
The card was still the 5.x shader orb. Ledger makes a card its product in miniature: the mark and name, a steering term as the recipe bar draws it, a reply tinted by its probe as the Page colours words, and one word's reading across the layers on the depth ramp, the term's layers on a wash. It is drawn by scripts/social-card.mjs from scripts/social-card.html with Playwright's Chromium at the old card's 1200 x 630; the alt text sits in the HTML. The orb's SVG source goes. Also documents the focus return, the short phone layouts and the raw-mode colours in drowse/web/AGENTS.md.
…reuse its whole matching path split_message_list now validates every turn before the tree changes (empty content, labels and thinking, tokenization, thinking the family cannot render, and alternation on a legacy template) and returns MessageTurns. The list is recorded from the root without append_turn: each message lands as its own turn, an identical child (user or assistant, outside the reservation) is reused, and no existing node is edited. Re-sending a multi-turn list, or each row of a fan or sweep of one, now shares the history and makes the replies siblings, and [user, user] renders as two turns, as a stateless render of it does.
…s own column Under 480 px the side column left the log a 44 px column (12 px of text at 320x480), so a short portrait screen keeps the one column; the loom page's scroll still applies to every short screen. The dock now spans the side column at its foot instead of sitting under the Thread row, which pushed it 32 px past the bottom at 568x320. The layout test computes the log's height and text width and the dock's room from the CSS at each short screen (measured in WebKit: 480x320 144x172, 568x320 192x260, 667x375 247x352, 1280x500 380x872), pins which screens are phones, and drops the row arithmetic that could not fail.
… S lens and units kinds
…rd its turns and reserve them under one hold of the tree's lock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Seven fixes the drowse.ai port of 6.0 found, each with a test.
Engine: a stateful message list records the conversation it answers.
generate([system, user])hung the reply off the active node and recorded none of the list, so the node's path was the bare root and every replay of it rendered a different prompt:token_anatomyreplayed 11 positions where generation saw 46 (tiny qwen3, reproduced). A message list is now a whole conversation.split_message_listchecks the whole list before the tree changes (a leading system message becomes the call's system prompt, kept in the reply's recipe;parent_node_id, raw mode, a second different system prompt, other roles, empty content, thinking the family cannot render and, on a legacy template, non-alternating turns are refused with a 400). The turns are then recorded from the root, each message its own turn, reusing an identical child at every step (add_user_turn's dedup, andadd_assistant_turn(dedup_existing=True), which skips nodes inside the reservation). No existing node is edited,[user, user]renders as two turns as it does statelessly, and re-sending a multi-turn list (a regen, or each row of a fan or sweep) shares the history, so the replies are siblings. The reply renders from those turns, so its node replays exactly and a follow-up continues the conversation. Stateless lists, so the OpenAI and Ollama routes, render alone as before.loom/ops.py::split_message_list,begin_generation_turn,loom/tree.py::add_assistant_turn,engine.generate_one; tests intests/loom/test_loom_ops.py,tests/session/test_engine.py(a fan of[system, user, assistant, user, user]matches the stateless prompt, 111 tokens; the old code gave 102),tests/session/test_batch.py(the stub now runs the real split and record).Phone layouts. Measured on a tiny
drowse servewith WebKit's iPhone 13 emulation:(max-width: 720px), (max-height: 520px), in App'sphoneand the phone blocks of App,drowse.css, Chat, RawBuffer and LoomSidebar). A short phone at least 480 px wide lays the conversation out in two columns: the log under the Thread row, and beside it the composer at the foot of its own column, or the Inspector while it shows (narrower, two columns would leave the log a 44 px column, so 320x480 keeps one). Log height x text width: 480x320 144x172, 568x320 192x260, 667x375 247x352, 1280x500 380x872; the dock fits its column at each. A tapped word opens the Inspector beside the log.overflow-y: clipcut the map), and 0 px at 844x390. Opening the loom on a phone folds the thread, the loom's column grows with its tools and scrolls instead of clipping, and a short phone's loom page scrolls to a map most of a screen tall. The map fits at 440x796 (316 px), 320x568, 844x390 and 667x375.scripts/workbench-layout.test.mjscomputes the log's height and text width and the dock's room from the CSS at each short screen and pins which screens are phones (fails on the old CSS); the CSS rule walker moves frommodal-surfaces.test.mjstomarkup.mjsto share it.Focus after a dialog. Escape, Close or the scrim left focus on the body. It now goes back to what opened the dialog while that is still a tab stop, else to the header's Menu (
lib/focusTrap.ts::returnFocus;scripts/focus-trap.test.mjs).Raw completion's colour selector. This was a regression, not a choice. 5.4 drew the reading tools above both formats, with a raw-mode tip. 6.0 moved them into the chat's dock, which raw mode doesn't draw, but left behind the select's raw-mode label,
automaticComparisonAvailable's!rawModeand RawBuffer's tinting and empty-colour notice. The reading tools are now one snippet in Chat. Raw mode shows them under the completion's head behind its own Colors button, and both formats share one open state (scripts/raw-colors.test.mjs, server-rendered; fails without the fix).R naming. The correlation drawer said "J-lens readout" for
jlens/probes; it says "R-lens readout" now (scripts/profile-analytics.test.mjs).Link card.
public/social/drowse.pngwas the 5.x shader orb. It is now drawn in Ledger as the dashboard in miniature: the mark and name, the term0.4 calm, a reply tinted by its probe, and one word's reading across the layers on the depth ramp. It is still 1200 x 630, drawn bynode scripts/social-card.mjsfromscripts/social-card.htmlwith Playwright's Chromium (@playwright/test1.63.0 joins the webui devDependencies, as on neuron-lens.com and standardlogits.com). The readings on the card are illustrative. The orb's SVG source is deleted. The alt text is recorded in the HTML: "The drowse mark and name, the steering term 0.4 calm, a reply whose words are tinted by how calm the model reads them, and one word's calm reading across the model's layers." drowse.ai'ssite-metadataneeds that text and must drop its SVG checks when its pin moves past this.Checks:
npm run check(svelte-check 0/0, theme, ledger ratchet at zero, 41 script tests) andnpm run buildwith the dashboard rebuilt;pytest -m "not gpu"(6043 passed);ruff check .;pyright(0 errors);generate_webui_types.py --check.