desktop: Soft Tactile redesign — every screen, integrated - #450
plombeer31 wants to merge 29 commits into
Conversation
…s, shared kit
Tokens: the Tactile palette in the app's --p-*/--i-* architecture (well,
lift, tint, success, amber-text, reasoning, soft shadows, washes, glass and
indigo inks), radius scale, one easing curve. The legacy bridge now points
at the Tactile names, so every 0.5.5 rule inherits the new ground, lift and
tints at once.
Type: Figtree variable (latin + latin-ext) with Inter kept second for
Cyrillic; OFL texts for Figtree, Inter and DM Mono shipped beside the fonts
and listed in THIRD-PARTY-NOTICES.
Logos: 29 model/provider/import marks in renderer/logos with
modelLogoKey/providerLogoKey/logoHTML/modelMark/providerMark — always on a
white round badge, server or CPU icon where there is no mark.
Icons: the Tactile set at 1.75 stroke with round caps; every 0.5.5 icon
name still resolves.
Stylesheet: styles.css is tokens + base + the shared kit (buttons, keycaps,
segmented, chips, dots, inputs, switches, lists, tables, plates, popover
items, notices, modals, hint strips). Region rules moved verbatim into
css/{shell,overlays,popovers,chat,composer,onboarding,settings}.css so each
region can be redrawn on its own; the kit replaced the uppercase micro
labels, 2px radii and 3px rules it covered.
The stylesheet is split by region now, so a rule the smoke looks for on disk (.poprow.dim, the message-action hover reveal) can live in any file index.html links. rendererCssText() reads them in load order.
…heme persistence Command palette (SH-10…13): 620px lifted panel, radius 24, blurred scrim, 56px input row with the Theme scope as a blue chip, sentence-case groups, rows of icon · bold title · context · keycaps or mono slash alias, brand-tint selection, Chats/Tasks/Skills headers and status dots under a query, the Ask-the-agent row when nothing matches, sun/moon/laptop rows with a check on the current theme. Sheets (SH-14/15): centred cards with a title, close button and footer. Switch session uses 46px list rows with the Tactile dot grammar and the state word; keyboard shortcuts is a two-column keycap grid without the prototype's Ctrl captions, and the approval row no longer advertises a grant key that does nothing. Delete alert (SH-03): 44px app mark, Cancel focused on arrival, Delete as the destructive fill. Toasts (SH-16): lifted cards with a round green check or red alert. The theme chosen in the palette or View > Appearance is saved to localStorage and re-applied before the first render. The palette field no longer gets its old text copied back after entering a scope.
… drawer, download strip Window ground is --well. Toolbar: 32px round toggles, "Chat" with the session line on one baseline, a lifted Search pill with one ⌘K keycap. Sidebar is a floating --lift panel (radius 16, 8px from the window edge) painted by #sidebar::before, so #sidebar keeps its 260px / 52px box. Workspace chip on the well with the app mark laid over it. Sentence-case group headers, 34px pill rows, 6px status dots with amber (waiting for approval, blocked) and red (failed) tones from a third, presentation-only element of chatDot/taskDot. Task rows show their schedule or status word in mono through data-m. Settings is a full-width neutral button with the gear, the word and a ⌘, keycap; the rail keeps the gear alone. Rail and the ≤1000px layout: mark, pluses, dots, gear, a hairline between lists. Inspector is a floating 292px panel with a full-width segmented control; steps are 38px mono rows with a spinner for the running step; reasoning is a labelled mono well per step; World is a label-over-value data plate. Console is a floating drawer with a neutral Write Debug Bundle button, a labelled close, amber warn and red error levels, and real empty states (the LLM log says this build does not record model requests there). Download strip is a lifted pill under the toolbar: icon, mono id, a .tk-prog fill whose width is the only thing repainted per sample, percent, ETA, queue, ghost Cancel. dlBarHTML is unchanged for the wizard.
Two presentation checks encoded the 0.5.5 sidebar: - sidebar rows are 34px pills now (was 30px); - the Settings entry is a neutral full-width button with the gear, the word and one ⌘, keycap, 36px tall and 236px wide inside the floating panel (was no keycap, no icon, 32x235). Everything else the shell checks read (260/52px sidebar box, centred rail pluses, 6px dot ring and its seat, the unread reveal rule, the drag region, the strip text) is unchanged.
…heme The brand wash is one colour-mix in both themes, so the sidebar toggle's ground read identical in light and dark and the smoke's item 2 contract (the on state follows the theme) broke. --tint is Tactile's soft blue wash with a value per theme.
…, logos, ring gauge, nine-bar voice meter
The composer dock is redrawn in Soft Tactile on the app's own markup and
logic: CM-01…07, CM-09…11 and the dock in every CH / MP screen.
- Composer card: 760px dock, 26px radius, translucent lift with an 18px
blur and the composer shadow; a brand ring while typing; a soft glint
along the top edge replaces the hard 2px running bar.
- Control row: Backend · Provider · Model, spacer, Context · Mode. The
uppercase legend labels are gone; "Backend" and "Provider" are captions
inside the pills, drawn with ::before from data-cap, so every chip's
textContent is still exactly the id the drivers compare. Backend
laptop/cloud/server icons, provider and model brand marks on white
badges, model id in DM Mono. `choose a model` and `download model` are
amber. Chips mark `.is-open` while their popover is up.
- Context gauge: a 20px ring over the percentage the app already computes,
amber above 70%, red above 85%; the figure is spaced ("36.6k / 1.0M").
- Mode chip tones as classes: default neutral, plan blue, auto amber,
bypass red; `mode —` muted.
- Status line: a chip then machine text for approval-gated (+ Jump to
request), waiting (readout, reason, Stop), busy (dots, phase, elapsed,
Stop), switch failed (red; the 45s watchdog line stays Caution), and
Ready / Caution.
- Send: 36px circle; empty muted, draft ink, busy stop, busy with a draft
brand (new `.steer` class), switch lock muted with the small spinner.
- Queue tray: outline pills "Queued · text · ×".
- Voice strip: lifted card; nine level bars driven by one --lv property
(refreshVoice patches it in place, so elements and animations survive);
a spinner while finishing; green chip when the second language wins;
error sentence in red with Open Settings / Reset and ask again and a
round dismiss. Mic pulses red while recording.
- Slash list: lifted popover above the whole dock, 34px rows, typed part
highlighted, amber no-match row, and a footer with the count and keys.
Every hook the drivers and the smoke read is kept: #composer, #entry,
.composerwrap, .cfoot [data-sel-open], .cfoot .cchip text, .sendbtn
(.locked/.stop/[disabled]), .statusstrip (+ .waiting .readout .ob-help
.ann, the busy strip's first .tnum), .slash/.slashrow in .composerwrap,
.composer .field with micbtn next to sendbtn, .qchip/data-unqueue,
.voicestrip hidden placeholder, .vstext/.vspart/.vsnote/.vschip, .ctxbtn
.gaugelb, .modechip/.cmodechip/.providerchip/.modelchip/.dlchip/.needsmodel.
…tus line, voice strip, slash list
…ck, notices, prose Rewrites css/chat.css and the transcript markup for CH-01…26 / SY-*: - empty chat: lifted plate card with the app mark, provider and model brand marks, pill ghost chips (data-fill kept) - column 720px with 28px padding and 14px rhythm, a top fade mask on #scroller; rows keep the empty-gutter grid (track now 0px) - brand user bubble; labelled Send again / Copy on the last message, hover icons elsewhere (opacity/pointer-events contract unchanged) - prose 14.5/1.62, markdown blocks laid out as inline-blocks so the pre-wrap newlines stop adding blank lines; file chips as pills - tool cards and folded runs: lift cards, status glyph discs, mono head, ellipsis args, red ring and "N failed" chip on failures - approval: indigo decision block (critical block for trust_config), Approve white / Deny glass (focused) / Abort ghost; receipt pill - plan hand-off as a card with chord keycaps drawn in CSS (labels stay verbatim); system rows with inline Switch provider link; the session model stamp as a blue notice inside its .sysrow - end mark seated on the action row so turn end adds no height Every id, class, data-* hook and compared text the drivers and the smoke read is kept.
With Figtree first in the UI stack, Inter is only fetched for Cyrillic, so asking the loader about Inter with no sample reported a missing face that was never needed. Load a Latin and a Cyrillic sample and check both, plus DM Mono.
Without --user-data-dir, electron . shares the installed app's profile under ~/Library/Application Support/Atomic Agent, so a driven run could write the operator's prefs.json and fight their open window for it.
…val block, plan hand-off, notices
…d-mark rows, query highlight Where it runs, Provider, Model, the add-provider wizard inside the popover, context breakdown, modes and the dictation language menu, restyled as floating lift cards (radius 18, pop shadow) with the app's logic and copy unchanged. - anchorStyle takes a selector or an element, opens 8px above the composer card of the chip that opened the popover, aligns with that chip (right edge for chips on the right half), clamps inside the content column and caps the height to what the column leaves. Widths per pane: backend 440, provider 420, model 520, wizard 460, presets 440, context 380, modes 360. - Route rows: real provider/model marks (server/CPU badge otherwise), route icons on backend rows with a radio, ids in DM Mono, Unverified amber chip, blue download chip, a divider before the trailing action row, the typed query marked inside model ids without changing .nm text. - Wizard: provider mark in the header, key/URL fields with a key icon that take the error (red) or could-not-check (amber) tone, Tactile footer buttons. - Context: breakdown bar (amber above 70%, red above 85%, striped when projected) with the reply reserve, amber trimming line, icon steppers. - Modes: title, rows whose word takes the mode colour, level-5 note in amber, Update agent in the footer when the agent lacks the route. - Voice menu floats above the strip with radio, badge, progress bar and a short-window height budget. Hooks kept: .popover, .selpop, .selhead text and busy captions, .nm, .modelrow, first .cap as row detail, the inline danger colour on switch errors, .ob-err, #wiz-key, #sel-filter, #sel-key, [data-wizmodel], .popfoot [data-act=close], .ctxdial/.ctxbound/.ctxbasis/.hd, .poprow(.on/.dim), .vsmrow/.vsmfoot, every data-act and data-* attribute.
…ed per chip, wizard, modes, context, voice languages
…Import redrawn as lists, detail pages and forms Memory (ST-12…15): a segmented channel switch over all six channels (the ones memory.*.enabled keeps off are disabled and say which setting), the status readout, profile facts as a table (key · value · pinned/contextual chip · votes), notes/lessons/procedures as rows with tag chips and an active/archived/all switch, a note as a page (plate, prose, Links card with weights; a link row opens that neighbour), other details as plate + mono well, and the turned-off channel as an amber notice plus empty state. MCP (ST-16…19): server rows with the GitHub mark or a plug badge, the honest "state —" chip, transport, trust class and tool count; a detail page with the launch command, tools / resources / prompts switch and tool rows; the add-server and remove confirms as modals over the dimmed, inert view, with the specific parse error in red; the restart line after a change. Telegram (ST-28…30): the connect card with its one action, the token card with a password field, the pairing well that points to the terminal app, and the advanced block as setting rows (enable switch, token, owner, channel) with their actions beside them. Import (ST-31, ST-32): source switch with the Hermes/OpenClaw marks, source folder, four toggles with the agent's own option descriptions, limit and Run preview; the report as a table (outcome chip · kind · item) under the summary line, with Edit/Apply or Back to form. Logic is unchanged: same state, acts, key handlers, ids and data-* attributes. memoryAct gains two backward-compatible arguments so the new controls can pick directly: filter:<name> and neighbor:<id> (bare filter and neighbor behave as before). The settings smoke in main.ts still reads the TUI strings from innerText; each view keeps them in one visually hidden, aria-hidden twin until the smoke reads the new markup.
…real list, detail and form screens
…lists, pill fields Title card: full indigo with a brand glow, 96px mark, 64px wordmark, build line. Every later step: an indigo side rail (lockup, phase statement, 01 Setup / 02 Data steps, build) beside the step column (34px title, lede, body, action bar, keycap hint strip). Route choice as three lifted cards with icons and 1-3 keycaps, plus Continue. Model, provider, file and import lists as lifted cards of 46px rows with real brand logos (models, providers, Hugging Face, import sources, the app mark for the terminal agent). Best fit / Recommended / New / Use at your own risk chips; fit lines green, amber text, or muted; out-of-reach models in a quiet well. Fields are 52px pills with icons; errors are notices under the field; the key screen names the provider with its logo; could-not-reach reads amber and a rejection red. The download shows real progress bars in a card and the two offers as cards with their keycap. Rows no longer shrink inside scrolling lists. The rail narrows below 1240px and 1060px and drops its statement on windows shorter than 720px. Logic untouched: same reducer, key router, act names, data attributes, ids and compared text. The shared wizard parts the composer popover also draws keep an unscoped base style.
…s, logo lists, download, cloud, custom and import steps
… cards, list rows, floating confirms Settings › LLM is redrawn from terminal text into real screens (ST-20…27), with the state machine, keys, acts and data attributes unchanged. - Toolbar: Local / Cloud / External / Fallback as a segmented control (.llmmode, .on, data-act kept), status readout, refresh, LLM logs. - Route card: provider and model marks, ids in mono, route and daemon chips that restate llmFormatDaemon's own word. - Run mode as three selectable cards; workers as a segment under Fusion. - Local, cloud, external and fallback rows as .tk-li list rows with marks, chips, fit lines and the Enter action; .tuirow, .llm-sub, data-llm-row kept. - Cloud filter in a search field (#llm-filter kept), disabled price facet with "pricing is not exposed". - Fallback: ordered rows, move up/down on the selected link, local last resort locked, append-local as a switch. - Confirms, the URL prompt and the Ollama offer float over an inert pane; Y / N / Esc keycaps in the buttons. - Hugging Face branch as a form; pick list with sizes; RAM warning notice. - Download and model-starting banners as blue notices; log view as a mono well with coloured levels. The smoke and model-picks read this pane's innerText for the TUI sentences, so those stay whole: hidden punctuation sits in zero-size spans and anything that must read as one line is inline or inline-block. model-picks.drive.mjs: the Settings leg waits for the tab's first refresh before reading the mode strip. Read at 2 s it showed the default Local and flipped to Cloud once the route sync landed, so no Local rows appeared.
…e, model lists, providers, external, fallback, logs
The Manage window is redrawn from terminal text into a real window
(ST-01…11, ST-33), with the same state, acts, keys and data calls.
Frame: a 1060×700 well over the blurred scrim. The nav lists the eight
Manage tabs: each menu row carries a .settab switcher with its count and
chord keycap, and the other groups of the menu tree scroll under Commands.
The body is a lifted card with a "Manage › Tab" crumb, the close button,
the pane, and the Build / Agent / State data plate. The per-pane scroll
restore is kept, and the nav's own scroll now survives a repaint too.
Tasks: the filters as a segmented control, a search box, an auto-refresh
readout (the only part the poll repaints), Refresh and New task, then a
real table with status chips. The detail shows a data plate, the last
error and Open session / Run now / Cancel task. The New task form has a
kind segment, labelled fields, the next five firings beside them, and
Create task disabled until the schedule parses. Cancelling a recurring
task asks in an amber confirm over the card. Every hint is a keycap
button.
Skills: rows with an on/off switch, source chip, version and
description, and the restart line after a toggle. The detail shows
SKILL.md in a mono well and offers Remove. The Hub has its own search,
result rows and an install card. The remove and security-scan confirms
are danger modals over the view they came from.
Privacy: the analytics switch row, session grants ("none active"), what
is and is not sent, and the restart line.
tuiBtn/tuiHints draw the leading key as a keycap; brackets and colons
stay in the text at zero size, so labels read exactly as before. The TUI
column headers and a few lowercase labels the smoke compares are kept as
visually hidden text. tasksAct accepts filter:<name> for the segments;
the bare f key still cycles. __settingsStripRows counts the nav's single
column. The 0.5.5 rules for Memory / MCP / Telegram / Import / LLM are
removed from settings.css; the generic .tui* fallbacks are restyled.
…lls and Privacy as real screens
…in dark White on the dark theme's #FF6A5C is about 2.9:1; --on-fill is white on the light critical (5:1) and near-black on the dark one.
The redrawn Settings tabs carried the old terminal strings in the DOM at zero size or clipped away (sdTwin, .set-sr, .set-z, llmSr), only so the smoke's innerText checks kept matching. Those checks asserted on text nobody could see. All of it is gone: - sdTwin (11 calls) and .sd-twin; .set-sr (13) and .set-z (6); llmSr (12) and .llm-sr; the twin-only helpers impLabel and mcpPad. - Visible text that was aria-hidden behind a hidden duplicate (empty states, the Skills column header, the Hub search, card badges, the Privacy row title) is exposed to assistive tech again. The chord keycap row carries aria-keyshortcuts; the Tasks table, Memory channel, MCP detail and Import source segments are labelled groups. - The Memory and MCP refreshes no longer overwrite the active nav row's text with "Memory (N)"; they repaint their own row's label and badge. The smoke keeps every state, data and config assertion and now reads the markup a person sees: nav labels and count badges, keycaps, column headers, empty-state titles, pressed segment buttons, row chips, the route card's label and value, the Import form's labels, inputs and switch positions, and the report table. cloud-setup.drive.mjs reads the provider row's id, key chip and Enter effect instead of a bracketed string that the page no longer renders. Import: the Source field row no longer runs 10px past the content column on each side; field rows are as wide as the Sessions card and their labels sit on its 16px text edge.
… blur filter The two orbs already fade to transparent inside their radial gradients, so filter:blur(2px) drew nothing visible and only added a paint surface on a 440px layer inside the rail. Removed; the rail looks the same.
|
The desktop's agent now has its own PR: #451 merges |
|
Round 2 from testing the DMG is #454, based on this branch. It covers the first-run trims, spinner, model tick and double click, comet, one Stop, no app-status strip, closable toasts, the house icon, and Fusion as a backend mode. The agent's final reply is now the last row of a turn. The agent side of those fixes (fusion_worker SSE, stored approvals, the vision catalogue check, shell results, replies that never stream) is on #451. |
The whole Soft Tactile redesign of the desktop in one branch: the foundation and the nine region PRs merged, plus the fixes that only show once they run together. This is the build to try and the one to merge; the region PRs are there to review one area at a time.
Contents
Fixes that live only here
sdTwin,.set-sr,.set-z,llmSr). A test that passes on text nobody can see proves nothing, so here they are gone. About thirty smoke checks now assert on the visible screen: nav labels and count badges, table headers, empty-state titles, the channel and mode switches, provider rows with their key chips, Import labels and values. Every state, data and config assertion is unchanged. Proof: renaming three visible labels on purpose failed exactly those three checks, each naming what broke; reverting made them pass again. Text that was onlyaria-hiddenbehind a copy is readable again, and menu rows carryaria-keyshortcuts.cloud-setup.drive.mjsreads the provider row (id, key chip, Enter text) instead of bracketed text the redesign no longer draws.--tint(follows the theme);.tk-spinis inline-block; the destructive fill's label reads in dark. Test infra: the smoke reads every renderer stylesheet;visual.drive.mjschecks Figtree for Latin and Inter for Cyrillic;drive-selector-lib.mjslaunches with a throwaway profile. (These are also on the foundation PR.)Verified on this branch
All runs on scratch copies of a configured state (settings version set to the build's, chats moved to a scratch workspace) or fresh directories, each with its own
--user-data-dirand CDP port — never the operator's state or profile.a0811bc3styles.css+css/*.css)electron . --smokemodel-picks.drive.mjs(68 / 8 / 4 GB)drive-selector.mjsunverified.drive.mjsmodel-step.drive.mjs(real AI/ML API catalogue)failure-line.drive.mjsvisual.drive.mjsonboarding-mouse.mjs, keyed state, one runonboarding-mouse.mjs, keyed state, four at onceThe four-at-once miss predates this work: the wizard's import step moves on before the driver's click under heavy load, on 0.5.5 as well.
The 4 known smoke failures on both:
llm tab … [downloaded](no model on disk in the fixture),state dir: the import copies providers without keys(fixture keys inline),backend: config round-trip to local(daemon untouched),switch timings … selectLocalModel=NOT EXERCISED.Screens were compared against the AA-DS-002 references at 1470×923 and 940×620 in light and dark.
Found, not changed
llm.runMode, so the whole-file write drops it; Fusion needs an agent ≥ 0.6.0.drive-selector.mjsfails 3 custom-route checks ona0811bc3too (no llama-server answering, so no model chip).Needs a decision
Region PRs