Skip to content

desktop: Soft Tactile redesign — every screen, integrated - #450

Open
plombeer31 wants to merge 29 commits into
atagdesktopfrom
facelift/integration
Open

plombeer31 wants to merge 29 commits into
atagdesktopfrom
facelift/integration

Conversation

@plombeer31

Copy link
Copy Markdown
Collaborator

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

PR Area Screens
foundation tokens, Figtree, brand logos, icons, shared kit, stylesheet split by region
shell toolbar, floating sidebar and inspector, console drawer, download strip, rail SH-01…09, SH-17, CH-23
overlays palette, sheets, delete alert, toasts, theme that persists SH-03, SH-10…16
composer glass composer, Backend · Provider · Model with logos, status line, voice strip, slash list CM-01…07, CM-09…11
popovers route / provider / model popovers anchored per chip, wizard, modes, context, voice languages MP-01…18, CM-08, CM-12…17
transcript bubbles, tool cards, indigo approval block, plan hand-off, notices CH-01…26
onboarding title card, indigo side rail, route cards, logo lists, every wizard step ON-01…23
settings frame Manage window and nav, Tasks, Skills, Privacy ST-01…11, ST-33
settings data Memory, MCP, Telegram, Import ST-12…19, ST-28…32
settings LLM route card, run mode, models, providers, external, fallback, logs ST-20…27

Fixes that live only here

  • Settings keeps no hidden text, and the smoke reads what is drawn. To keep the smoke's old terminal-text checks passing, the three Settings PRs kept invisible copies of TUI strings in the DOM (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 only aria-hidden behind a copy is readable again, and menu rows carry aria-keyshortcuts.
  • The Memory and MCP background refreshes no longer overwrite whichever Settings nav row is active.
  • cloud-setup.drive.mjs reads the provider row (id, key chip, Enter text) instead of bracketed text the redesign no longer draws.
  • The Import tab's Source row lines up with the fields below it.
  • Kit: an on toolbar toggle sits on --tint (follows the theme); .tk-spin is inline-block; the destructive fill's label reads in dark. Test infra: the smoke reads every renderer stylesheet; visual.drive.mjs checks Figtree for Latin and Inter for Cyrillic; drive-selector-lib.mjs launches 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-dir and CDP port — never the operator's state or profile.

Check a0811bc3 this branch
token check (styles.css + css/*.css) clean
electron . --smoke 497 pass · 4 fail 497 pass · the same 4 fail
model-picks.drive.mjs (68 / 8 / 4 GB) 23 / 29 29 / 29
drive-selector.mjs 3 custom-route fails the same 3
unverified.drive.mjs 7 / 7
model-step.drive.mjs (real AI/ML API catalogue) 5 / 5
failure-line.drive.mjs 5 / 5
visual.drive.mjs Figtree, Inter (Cyrillic) and DM Mono load; slash list painted, 34 rows, unclipped, both themes
onboarding-mouse.mjs, keyed state, one run 61 / 61 61 / 61 unloaded
onboarding-mouse.mjs, keyed state, four at once 2 / 2 fail at the import step (same batch) 2 / 2 fail at the same step (same batch)

The 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

  • Choosing Fusion in Settings › LLM reports success but the 0.5.5 agent's config schema has no llm.runMode, so the whole-file write drops it; Fusion needs an agent ≥ 0.6.0.
  • Cloud reads "Active" on local routes (unset run mode defaults to cloud); "key ok" means a key name is present, not checked.
  • Main-process error copy still reaches users as "fetch failed", "export HF_TOKEN", "re-run with --refresh".
  • A cancelled task still offers Run now and Cancel; the session sheet's filter and Open do nothing; Enter on the palette's "No results" does nothing (only a click on Ask the agent works).
  • drive-selector.mjs fails 3 custom-route checks on a0811bc3 too (no llama-server answering, so no model chip).

Needs a decision

  • The sidebar Settings entry now follows the reference (neutral button, gear, word, ⌘, keycap). That reverses the r5 item 8 wording ("a plain blue button — no keycap hint, no second icon"). Say which one stays.

Region PRs

…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.
…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.
…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.
…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.
…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.
@plombeer31

Copy link
Copy Markdown
Collaborator Author

The desktop's agent now has its own PR: #451 merges main (v0.6.1) into atagdesktop. That brings config format 65, which the live desktop state (64) needs, and the llm.runMode schema the Fusion settings here rely on. A test DMG built from this branch with that agent passed 7/7 when driven from the DMG copy against a copy of a live state: it connects on the bundled agent, the Tactile UI loads, mode switching goes through /api/coding-mode, and a Settings write lands with no version wall.

@plombeer31

Copy link
Copy Markdown
Collaborator Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant