Auto handles 90%+ of cases — the row of category buttons was
visual noise on the main panel. Now:
- Removed the .research-category-row from above the textarea.
- Added a Format <select> inside Settings (next to Rounds) with
Auto / Product / Compare / How-to / Fact-check options. Default
is Auto, same as before.
- Updated all the JS that read .research-cat.active / data-cat to
read #research-category.value instead (_saveSettings, _readSettings,
_resetCategoryToAuto, _editJob, _restoreSavedSettings).
Same wire to the backend — settings.category still carries through.
Was rendering on its own row under "Multi-step web research with an
LLM-in-the-loop agent". Now appended to that same flex-wrap line as
"— past runs in Library, Research" so the header section stays one
visual block instead of two.
Was rendering on a second row below the "Past research" header,
inflating it to two rows. Now appended to the title span as a small
inline chip — "Past research — all in Library, Research" — keeping
the header at one row. Same click → close panel + open Library tab.
The capture-phase scroll listener was firing for scrolls anywhere
in the modal — including the Trending models list, which lives
inside the Direct Download fold body. Scrolling that list was
auto-folding the section that contains it.
Bail early if the scroll target is the fold body or a descendant —
the section only folds on scrolls in sibling scrollers (.cookbook-body,
.hwfit-list, .modal-content).
Each time the panel opens we pick a random entry from a list of 10
diverse research prompts (history, tech, food, science, fact-check,
how-to) so the textarea hint feels fresh and shows the breadth of
queries the tool handles instead of always nudging toward the same
Odysseus example.
- Removed standalone "Edit cmd & relaunch" — "Edit in serve panel"
renamed to "Edit & relaunch" and is now the single edit entry.
Tooltip notes that the raw cmd is still editable inside the panel.
- Tagged each item with a group (run / edit / endpoint / copy /
danger) and renderer inserts a thin divider whenever the group
changes, so the menu reads as visual blocks instead of one long
list.
- Header h2 inside the Active group now says "Active" (matches
the renamed tab) instead of "Running".
- Both context-menu Reconnect entries (the normal one and the
recover-from-vanished-process fix) say "Reconnect tmux" so the
user knows what the action actually does.
- Sibling cookbook-server-section-* blocks inside the Active group
get a top divider + 14px gap so transitions between server
groups (local / remote-host / etc) read clearly.
Previously the global GPU-toggle total was set once and never
overridden, so a first scan on the local 1-GPU container left
the Run-panel GPU button row stuck on GPU 0 even after switching
to a 4-GPU remote host. Now any scan returning a positive total
updates the binding; zero/missing values still don't clobber a
known-good count (no flicker during in-flight re-probes).
Mirrored the panel's runtime readiness note into a small chip
appended to the .memory-item-title at the top of the expanded
serve card. The in-panel note becomes a hidden source-of-truth.
This way the "vLLM ready on … : vLLM CLI: …; python package:
vllm 0.22.0" status sits inline with the model name where the
user is already looking, instead of buried below the toolbar row.
On initial render, compare total_ram_gb vs gpu_vram_gb — if RAM is
the larger pool, pre-select the RAM (count=0) button instead of the
max-GPU button. Boxes with more system RAM than VRAM (low-VRAM
GPU + lots of system memory, or CPU-only servers with a small
adapter) now open on the dominant pool.
New order: [Standard ▾] [Search ............] [Engine] [Quant] [Context]
so the two primary picks (type + free text) sit together at the
left, with the more advanced filters lined up to the right.
display:none toggle was instant and felt jarring during auto-fold/
auto-expand. Swapped to a CSS class `.is-folded` that transitions
max-height (0 ↔ 1200px) and opacity (0 ↔ 1) over ~280ms with ease,
so both manual chevron clicks and the scroll-driven toggles slide
in/out smoothly.
scroll handler now tracks per-target scrollTop via WeakMap. Downward
scroll on any scroller in the cookbook modal folds Direct Download;
scrolling back to top (scrollTop <= 0) unfolds it. Manual chevron
clicks still win — they persist to localStorage; auto-toggles
don't, so the user's last explicit pick survives reload.
IntersectionObserver missed the case because scrolling inside the
nested .hwfit-list (max-height:52vh own scroller) doesn't move the
header out of view at all. The user wants any downward scroll in
the scan/download area to fold Direct Download.
Switched to a capture-phase scroll listener on #cookbook-modal that
catches every scroll event from any nested scroller (.hwfit-list,
.cookbook-body, .modal-content). Folds only on downward scrolls so
scrolling back up doesn't keep re-folding.
The scroll listener on .cookbook-body never fired — the user is
likely scrolling inside the nested .hwfit-list (max-height:52vh)
which doesn't bubble to its parent. IntersectionObserver fires
whenever the Direct Download header crosses the viewport edge
regardless of which container moved.
Folds only when boundingClientRect.top < 0 (header pushed up past
the top) so modal close / detach doesn't trigger it.
Previous .modal-body / .cookbook-content lookup matched neither the
desktop scroller (.cookbook-body) nor the mobile one (#cookbook-modal
.modal-content), so the scroll listener was attached to document.body
and never fired. Walk up to whichever scroller actually exists.
Added a scroll listener on the parent .modal-body / cookbook-content
that folds the Direct Download body once its h2 header has scrolled
above the container's top edge. Frees the viewport for the Scan
section below while leaving the chevron clickable to expand again.
Auto-fold doesn't write to localStorage (only manual clicks do)
so the user's last explicit preference still wins on reload.
- Added a trending-up (market-up) SVG before the label, tinted
accent so the section reads as "what's hot".
- Chevron ▸ moved from the left to the right side of the toggle
row (still rotates via the existing CSS).
- Bumped the toggle row taller (26→34px) with 13px font + 18px
icon so the section header has more presence.
- Brain admin-card header rows get min-height:32px so cards with
toggles and cards without (Inject Skills) align.
- Cookbook Trending models tab nudged up 8px (top:-3 → -11).
- Removed the ↻ RESCAN button in hwfit toolbar; manual EDIT still
available and auto-probe runs on container restart.
- Reordered the toolbar so Audit sits left of Select (matches the
brain memories layout where bulk actions live before Select)
- Renamed "Audit all" → "Audit"
- Star icon in Audit now tinted with var(--accent, var(--red))
- Select button gets the same dot/X SVG swap used in brain
memories (dot in idle state, X when bulk-select mode is active)
Per user report — the tag's mode metadata coincided with a
500 error on agent mode (especially on mobile). Removing the
UI tag, the chat.js writes of metadata.mode, and the CSS pill
so agent mode posts work cleanly again.
Touches:
- chat.js: drop _sendMode capture + meta.mode writes (user + assistant)
- chatRenderer.js: roleTimestamp() back to a single (when) arg, drop
the .role-mode-tag append; updated three call sites
- style.css: dropped .role-mode-tag and .role-mode-agent rules
- 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.
Was `top: calc(50% + 4px)` which left the button 4px below the
true vertical center of the input — visibly misaligned. Dropped
the +4 offset so the toggle anchors at top:50% / translateY(-50%)
and tracks the input's center.
Also removed the redundant base rule's position:relative + top:2px
nudge — it was being overridden by the more-specific
.email-field .email-cc-toggle absolute positioning anyway.
- Renamed "Note (no timer)" → "Note".
- Clicking it now opens a small modal with a textarea + Save/Cancel.
- The typed text becomes the todo item; due_date is omitted so no
timer fires. Esc cancels; Cmd/Ctrl+Enter saves.
Re-adds the timer-less note path next to the time-based presets.
Picking it POSTs the same payload but omits due_date so the entry
lives in notes as a plain reply todo with no reminder firing.
Toast: "Reply note saved" instead of "Todo reminder set for …".
Was sticking on toggled-on state if the user closed the library
while in select-mode — reopening showed the Cancel/X toggle even
though no emails were selected. Force-reset state._selectMode and
state._selectedUids in openEmailLibrary so each open starts fresh.
Correct behavior:
1. Cached draft + first click → opens the cached reply
2. Cached draft + second click → clears the cache and opens the
Fast/Full + context menu so the user can request a fresh draft
3. No cache → opens the menu directly
Per-button shownOnce dataset tracks the first-click state so the
second click triggers the menu instead of replaying the cached
reply again.
- AI reply: removed the cached_ai_reply shortcut so clicking the
button always reopens the Fast/Full + context menu. Lets the user
ask for a fresh draft (with new steering) instead of being locked
into the cached one.
- .email-cc-toggle gets position:relative + top:2px so it
baseline-aligns with the To: field chips next to it in the
document email compose.
- Initial button: dot-in-circle SVG + "Select" label
- After click (select-mode on): X SVG + "Cancel" label + .active class
- Same SVG glyphs as memory.js so the two pages feel consistent.
Hooked into the toolbar Select toggle AND the bulk-bar Cancel button
so both reset the icon state.
When the chevron opens the details, the To/Cc rows pop up as an
absolutely-positioned panel anchored to the bottom of the meta
block — with bg, border, rounded corners and a shadow. Nothing
in the rest of the header reflows: From row stays put, the action
cluster stays put, the email body content stays put. This kills
all the height-/spacing-jump quirks the inline-expanded design
was fighting.
- .email-reader-meta .recipient-chip drops max-width and overflow
truncation so the full name renders in each chip. The parent
.recipient-chips span already has overflow-x:auto, so users can
swipe horizontally to reveal any chip whose tail is clipped off
the right edge of the row.
- Strong (From: / To: / Cc:) labels get explicit white-space:nowrap
+ flex-shrink:0 so they never truncate even when the row is
squeezed to its minimum width.
- Horizontal: max-width and left already clamped to viewport-16.
- Vertical: prefer below the button, but flip ABOVE if there's
more space there (e.g. button near the bottom of the viewport).
- max-height clamped to viewport-16 with overflow:auto as a final
guard so the menu can never extend past the screen edge.
Dropped the two-step (pick mode → context → OK) flow. Now the
context textarea is at the top of the popover and Fast (left) /
Full (right) sit below as the confirm buttons themselves — they
fire the draft with whatever's currently in the textarea (empty
= no steering).
The document-level capture listener was closing the popover on
ANY click — including clicks inside the context textarea, which
made it impossible to focus the input. Replaced with an inline
handler that bails when the click target is inside the menu.
Restructured flow:
1. Click Fast or Full → reveals an optional context textarea
("Add context (optional)") below
2. Type optional steering note or leave blank
3. Click OK → triggers the draft with the chosen mode + note
Dropped the standalone … note-toggle button — the textarea is now
gated on picking a mode, which makes it easier to discover.
- Removed the conditional Draft fast / Draft full buttons. Note
textarea is always-on via the … toggle, and whatever's in it
is picked up by the existing Fast / Full buttons as noteHint.
- Clamped the popover max-width and left position to
Math.min(220, viewport-16) + 8px margin so the (now wider) menu
doesn't spill off the right edge on narrow mobile screens.
Top row keeps Fast / Full + a new horizontal-dots button. Clicking
the dots reveals a textarea ("e.g. reply nicely but say no"); as
soon as text is in it the panel shows Draft fast / Draft full
buttons that pass the note through as noteHint to the AI reply
endpoint. Empty textarea hides the draft buttons so the user only
gets the steered draft when they've actually typed direction.
Firefox mobile rendered the backdrop-filter:blur + var(--panel)
combination on the slide-out sidebar as semi-transparent, so the
chat input bar's selected-model label (e.g. "minimax") was
visible behind the drawer. Force background:var(--panel) and
backdrop-filter:none inside the mobile @media block.
The left-edge gradient fade was likely the source of the perceived
shadow under the icons on mobile. Forced background:none and the
matching padding-left:0 on mobile so the cluster reads as bare
icons without any soft edge.
Was only the date moving — the expanded card had a more-specific
`padding: 4px 0 6px` shorthand on the title row that zeroed out
the padding-left from my earlier nudge. Added the expanded-card
selector to the padding-left:4px rule so the title now lines up
with the meta line in both list and expanded states.
Was using flex-wrap:wrap on the From row, which let the chip span
flip onto a new row below From: when the available width briefly
dropped — then snap back as the chip span's overflow-scroll kicked
in. Switching to flex-wrap:nowrap keeps the label glued to the
chip; the chip span shrinks/scrolls horizontally instead.
In docked mode the header already reserves vertical space for the
absolute action cluster, so the To/Cc details fit without any
height tradeoff — force [hidden] open and hide the chevron toggle
so the recipients are always visible there.
Was From→To = 0 (meta gap 2 + details margin-top 0) while To→Cc
was 6 (details gap). Set details gap to 2 in docked too so all
three meta rows have the same vertical distance. Dropped the
per-row margin-top:4 docked override since spacing now comes
entirely from gaps.
Docked header is flex-direction:column, and the base
align-items:flex-start was sizing the meta to its chip width and
parking it at the left — the absolute cluster's right:0 then
landed at the meta's right edge in the middle of the pane.
align-items:stretch makes meta fill the header width so right:0
hits the actual right edge.
Dropped the docked-specific overrides (cluster flowing below meta,
padding-right:0, header min-height:0). The same container-query
rules drive both: cluster floats top-right and wraps to 2 rows
when the reader width crosses 600px, snaps to overlay below 380px.
Docked pane width is just another container width.
1. Moved the min-height from .email-reader-header to .email-reader-meta
(92px) inside the <600 container query. Targeting the container
itself in its own @container rule was flaky; using a descendant
that affects the parent's intrinsic height works reliably.
2. Dropped the margin-top:0 reset on the cluster in the <380 overlay
rule — that was clearing the base -7px lift and sliding the
cluster ~7px downward at the breakpoint. Now both states use the
same -7px lift so the visual position is stable across the
transition.
Dropped the @media(769px) from-row min-height + align-items:center
and the strong > top:-2px nudge — leftovers from the grid layout
that were forcing extra height and label offsets the block-flow
meta doesn't need.
Consolidated docked overrides into a single flat block (no @media
wrapper) and merged the two .email-reader-meta declarations into
one. Same visual result, much less competing CSS to debug.
When the cluster wraps to 2 rows (44 + 4 gap + 44 = 92px tall), it
was peeking out below the header bottom because min-height stayed
at 60px (only ~44px of cluster room). Bumped min-height to 108px
inside the same <600 container query so the wrapped cluster sits
fully inside the header with 8px breathing room top + bottom.
Replaced the grid layout (which made From row height depend on
cluster height, causing To/Cc to shoot up or down at the wrap
breakpoint) with a plain block stack:
- meta = position:relative block
- From row + details = natural block flow with padding-right
reserving space for the absolute cluster on the right
- cluster = position:absolute top-right, width changes per
container query (308px wide / 158px narrow / 180px overlay)
- padding-right tightens from 320px → 170px → 0 as the cluster
shrinks and finally goes overlay
- details margin-top dropped from -10px to 0 since there's no
grid row gap to compensate for
To/Cc now hugs From with no jumps when the cluster wraps or
overlays.
Removed the medium-mode -12px details margin compensation — it
under/over-shot depending on grid row sizing. Replaced with a
:has() rule: when the user expands To/Cc, the From row gets
min-height 92px (matching the cluster's 2-row max height). Row 1
becomes the same size whether the cluster is 1 row (wide) or 2
rows (narrow), so resizing across the 600px wrap breakpoint no
longer makes To/Cc shoot up 4px.
When the cluster snaps to absolute overlay at <380px, it stops
contributing to grid row sizing — row 1 was collapsing to the From
row's natural height, which made the To/Cc details slide upward and
left the floating cluster visually misaligned against them. Setting
min-height:88px on the From row inside the same container query
holds row 1 at the cluster's two-row height so nothing jumps.
Was fanning out to 3 rows because the 152px max-width (3 icons +
2 gaps exact) had no slack — subpixel rounding could push the
third icon over and trigger another wrap. Bumped to 158px in the
in-grid mode (600px breakpoint) and 180px in the absolute-overlay
mode (380px breakpoint, where the 22px padding-left from the
gradient fade was also eating into the 3-icon row width).
Was wrapping into 4+ rows at narrow widths because the cluster's
grid column could shrink below the 3-icon cap. Set both min-width
and max-width to the 3-icon row width and justify-self:end on the
cluster so the icons stay glued to the right edge instead of
sliding toward the middle when the cluster is wider than its
content.
The 600px / 380px breakpoints were @container docpane queries but
the email reader isn't inside a docpane container — they never
fired and the cluster wrapped to 3+ rows at narrow widths. Added
container-type:inline-size + container-name:emailreader on
.email-reader-header and switched the queries to that container,
so the 2-row cap now actually applies.
Three-step shrink:
1. > 600px pane: cluster sits in col 2 as 1 row of 6
2. 380-600px pane: cluster capped at 3-icon width so wrapping
stops at 3 + 3 (max 2 rows) — chips share width with the 2-row
cluster instead of multiplying into 3+ rows
3. < 380px pane: cluster snaps to absolute overlay with left-edge
box-shadow, still capped at 3-icon width so it's the same 2-row
shape but floating above the truncated chips
Grid tracks now:
- col 1: minmax(60px, 250px) — chip natural width capped at 250px,
with the 60px (4 char) floor enforced on From / To / Cc alike
- col 2: minmax(48px, 1fr) — takes the rest, shrinks first when
the pane narrows
Removed the hard max-width on the action cluster so on wide panes
it stays as one row of 6. Once col 2 shrinks below the 1-row width,
flex-wrap kicks in and the icons re-stack to 3+3. Chips only start
to shrink past that point.
- Action cluster's max-width is calc(48*3 + 4*2) so the 6 icons
always lay out as 3 top / 3 bottom by default.
- When the pane narrows the chips in col 1 shrink first (with 60px
min so 4 chars + ellipsis stay visible).
- At <380px the cluster snaps to absolute overlay with a left-edge
box-shadow so it reads as floating above the truncated chip.
Two-step shrink behavior:
1. As the pane narrows, the action cluster (max-width:50% of meta)
wraps to a 2-row icon stack first
2. Then the recipient chip span starts overflow-scrolling, but
keeps a 60px min-width (~4 chars) so the first chars of the
sender/recipient name stay visible
Previously only the From row affected the action cluster's column
width — To/Cc detail rows spanned both columns and ignored the
cluster. Now:
- meta-details lives in col 1 only so the To/Cc chips shrink
together with the From chip when the pane narrows
- action cluster spans rows 1 and 2 so its width is set by the
widest col-1 content; a long To/Cc list triggers the wrap to a
2-row icon stack just like a long From sender does
Meta switched to CSS grid in undocked mode:
- row 1, col 1: From row (label + chip + chevron)
- row 1, col 2: action cluster
- row 2, span: To/Cc details
The cluster shrinks alongside the chip and flex-wraps into a 2-row
icon stack before crowding the chip. At very narrow pane widths
(< 380px via @container docpane) it snaps back to absolute overlay
so From: still fits.
Docked mode overrides meta back to flex column so the cluster
flows naturally last — under From, and under To/Cc when expanded.
Was rendering as a transparent ghost — From chip / sender text bled
through the gaps between icons. Added a left-fading gradient
backed by var(--bg) so the cluster reads as an opaque overlay
while chips poking out from underneath blend smoothly into its
left edge.
- Window-level recipient-chip click handler now bails if the chip
is inside .email-reader-meta — the per-reader handler still
toggles the expanded-address view on click.
- The from-sender (magnifying glass) search button SVG is now
tinted with var(--accent-primary) so it stands out as a deliberate
search action against the neutral Reply / Forward / etc icons.
Moved the action cluster out of the From row to a sibling of meta
inside .email-reader-meta. Undocked: cluster is absolute-positioned
top-right of the header so it overlays the From line as before.
Docked: cluster is in-flow at the bottom of the meta column, so it
sits below the From row when collapsed and below the To/Cc rows
when the user expands the recipient details via the chevron.
The previous commit read toggleState.mode before it was declared
(send-time site near line 632) and outside its closure (assistant
finalize site near line 3426). Both threw ReferenceError / TDZ on
first send, which crashed the chat send + render pipeline.
Read fresh via Storage.loadToggleState() at each site, defaulting to
'chat' on any error. Mode-tag rendering otherwise unchanged.
Cluster is now in-flow with margin-left:auto and flex-wrap:wrap so
when the chip text grows long enough to crowd it, the buttons split
to a second row of icons before they have to cover the chip. The
absolute-overlay behavior kicks back in at very narrow pane widths
(<380px via @container docpane) so From: still fits on one row when
the pane is truly cramped.