Commit graph

166 commits

Author SHA1 Message Date
CRT
fc08bc89cb host: the drafts vault (CAS revisions, append-only history, archive, export, capture budget) with the app's VaultRules; feature vault
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:08:40 +07:00
CRT
4e44d8aaa0 create-waybetter: the template's host command list gains tab_run_command (matches commands.rs again)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:07:58 +07:00
CRT
52ab1fc00b host: AlertsConfig and ThemeConfig live in ungated modules, so the host compiles and tests without the tauri feature
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:05:27 +07:00
CRT
94cadc1bdb Theme icons: glyphs drawn whole in the icon's content box
Live 2026-10-01: X's search-field icon is a 28x16 box with 12px of padding; sized at 100% of that width a
pixel glyph lost its top and bottom and sat left of X's own icon. Glyphs are now sized with contain (a
scaled pack by the box's height) and placed in the content box, for masks and images alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 15:52:56 +07:00
CRT
a87e9c6516 Locales: icon packs, install, Store tabs, area switch and the theme doctor in the remaining catalogs (47 languages, machine)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:57:53 +07:00
CRT
3db5435208 Store: two catalog files from one source, so apps from before a kind keep a working Store
An app that does not know a catalog item's kind refuses the whole signed file, so
the icons and builtin kinds can never go into catalog.json. `waybetter store build`
now writes catalog.json (format 2: theme, plugin, strings only; what every app
reads today) and catalog.v3.json (format 3: every kind) from the same items;
`sign` and `verify` handle the pair; the rules refuse a format 2 file that lists a
newer kind (CLI, JSON Schema, host). The host reads catalog.v3.json first and falls
back to catalog.json only when the newer file is not there (a newer file that does
not verify is an error). An item of a kind a version does not know is skipped
instead of refusing the file, so the next new kind costs current apps nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:41:29 +07:00
CRT
ec548a53d4 shell: a pane's edges follow its own theme
themeStore marks every dock group whose theme (the pane's own, else the global one) is bevelled with
.wb-bevel; edges.css draws tabs, toolbar, address field, wB button and the settings panel per group.
The window-level parts (title bar, caption buttons, menus, palette, scrollbars) keep following the
global theme. A Windows 98 pane is bevelled inside a flat window, and a flat pane stays flat inside a
Windows 98 window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:39:11 +07:00
CRT
1edc6440ef host: the popover host (webview, session, z-order, close rules) is the host's
popover_host owns the one popover webview of an app: creation (recreated above tabs created after
it), placement through popover::place, the session (seq, source tab, app meta flattened into the
JSON the page reads), the toggle-closed window, close on layout change, navigation (the app says
whether the context moved on), shell raise, and the popover_only / current caller checks. The app
passes a PopoverConfig (page url, open event, macOS ground). Tests: layout staleness and the
session JSON shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:33:10 +07:00
CRT
7ab3ecd02e Theme decor: selection colors with a bare ::selection
With an ancestor in front (:root:root ::selection) every node the site inserts cost extra style
recalculation: Terminal measured 4 elements per insertion on the theme matrix (6 after a bar theme,
over the budget of 5); a bare ::selection measures 2 (3 after a bar theme). The rule did not predate
the design pass's changes; it was in Terminal's decor from the start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:26:29 +07:00
CRT
8a5403947d host: popover geometry (card placement, label, constants) is the host's
Pure placement of a trusted local card under a button of a site tab, kept inside the tab, flipped
above when short, with the caret on the button; moved from the X app with its seven tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:26:25 +07:00
CRT
5d19396d89 Store: Open With (files the OS hands over) and the icons and builtin catalog kinds
Open With: store_commands keeps each existing file the OS hands over (launch
argument, a second launch forwarded by single-instance with its working
directory, a macOS file:// open-URL) under a random id (Store::receive_file:
name says what it is, regular file, size cap, 8 kept, 15 minutes) and queues
LinkEvent.opened {id, name, kind}. The shell-only install_stage_opened {id}
reads that one file once and stages it exactly like a dropped file; it takes
an id only (testkit asserts that), so the shell cannot name a path. The shell
shows the same install screen; nothing installs without it. The NSIS template
lists the app under "Open with" for the declared extensions (OpenWithProgids)
and never makes it the default program, unlike Tauri's own macro.

Catalog kinds: icons (an icon pack, *.icons.json, at most 120 KB; validate_icons,
staged and committed as text for the shell, the install screen draws every icon,
.icons.json is no longer read as a theme) and builtin (a feature the app
already has, such as an area plugin: no url, sha256, size or file; never staged;
the Store view lists it as Built in with the app's own switch via the areas
prop). Rust: Kind, CatalogItem.check, verify_item, install links refuse builtin.
JS: storeModel, installController (requestOpened, icons adapter), InstallDialog,
StoreView, StoreSection (remove an icon pack, area switches), words. CLI and
JSON Schema: kinds, optional url/sha256/size on builtin, icons size cap; the
test schema validator learns not and if/then/else. docs/STORE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:23:23 +07:00
CRT
d40bf273bd host: the palette commands of site tabs are the host's (tab_commands, tab_run_command)
tab_commands keeps the list each site tab reported (validated by plugins::commands, accepted only
from a tab on the site's origin), clears it when a new document starts, tells the shell, and checks
the shell-only tab_run_command: only a command that tab reported, only while it is on the site.
tab_run_command joins commands::SHELL (guard checked by assert_host_guards); the app's tab_report
hands its `commands` to tab_commands::reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:22:00 +07:00
CRT
6b37744226 String packs: rich-text entries, language conditions and word rewriting; theme doctor report; translate-mode words
- Rich sentences (a link or button inside text) can be replaced by data: the inventory gives a rich entry `slots`, a pack value places the site's own elements with {e0}, {e1}, ... (each once, the last may be left out; no markup, plural or other parameters). The engine returns a pieces + element order template; the page inventory carries `rich` slot counts.
- Packs may carry `when: { languages }` (applies only while the site shows one of them; `en` matches `en-GB`) and `rewrite: { words }` (whole-word rewording of the site's own text, messages keep their parameters' values; no sentences of the site in the pack). The hook tells the engine the site's language; a rewording pack makes the delivery carry the whole inventory.
- Apps can ship built-in packs (BuiltInPacks) delivered before installed ones; syncStringPacks hands the tabs a delivery at start only when it changed.
- Translate mode words are a function the app supplies (asked at each draw) and gain a `plural` word; host validates `when` and `rewrite` envelopes.
- Theme doctor: every finding says where (token path or CSS rule), what and how to fix, as catalog keys wb.doctor.*; the Theme Builder shows the full report; the import hint is translated. Doctor words are in the 17 supported catalogs.
- waybetter strings check describes conditions and rewrites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:19:36 +07:00
CRT
1539cb68e1 host: keyword alert settings are the host's (AppConfig::with_alerts, commands::ALERTS)
alerts_commands keeps the owner's AlertSettings (saved in app data, validated by alerts), serves
the shell-only alerts_get / alerts_set (testkit::assert_alerts_guards) and builds the script that
hands the page runtime the keywords to watch. The app names the file and the runtime member
(AlertsConfig); store()/script() are the app's own writers (backup import).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:14:34 +07:00
CRT
d47a77d95d shell: the active tab's mark and the palette's edge take the theme's accent instead of wayBetter's brush
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:14:27 +07:00
CRT
a7988f70f8 macOS find: page matches drawn with the CSS Custom Highlight API (window.find showed nothing while the page was not the key view); --diag logs exit requests, window close/destroy and each native pane placement on macOS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:13:53 +07:00
CRT
82382293a2 host: site themes are the host's (AppConfig::with_themes, commands::THEME)
theme_commands serves theme_get / theme_set / theme_preview / theme_scopes_get / theme_scopes_set
(shell-only, each checks its caller first; testkit::assert_theme_guards) over the theme state the
host now keeps: the owner's theme, account and pane themes, the Theme Builder preview. The app
only names its built-in themes and the two files (ThemeConfig). apply() themes every site tab
(script, cached Google font bytes, native background); page_scripts()/tab_background() serve a new
document; set_theme/set_scopes/set_preview are the app's own writers (backup import, dev mode).
A font install or removal re-themes the tabs in the host itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:10:43 +07:00
CRT
260646bbc0 shell: the window follows a theme's corners and, for a bevelled theme, its edges
Every radius in the chrome and the panel scales with the theme's own radius (--wb-c-rs = radius / 12,
0 to 1.5): a square theme gets a square window, a round one a rounder window; themes that keep the
site's colors keep wayBetter's own corners. A theme built from raised and sunken edges (ChromeTheme
edges: 'bevel') gets frame/edges.css on html[data-wb-edges=bevel], also from the first frame: a face
color under tabs, toolbar, menus and the panel, 98.css's four edge lines derived from that face,
the active title bar's gradient across the tab row, tabs as a tab control, small raised caption
buttons, a sunken address field, selected menu rows in the title bar's color, raised and default
buttons, etched group boxes and classic scrollbars in the panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:08:01 +07:00
CRT
711fc53c55 host: a restored window fits the monitor it opens on
A state saved on a larger or rearranged screen (2720x1800 at x=3347 on a smaller second monitor)
left the titlebar off-screen and the window could not be moved. After the window-state restore,
the window is moved inside, and shrunk to, the work area of the monitor it is mostly on (the
primary one when that monitor is gone). Maximized and full-screen windows are left to the OS.
window_fit::fit is pure and unit tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:29:35 +07:00
CRT
2a8e926967 shell: the app menu hangs from each pane's wB button; the titlebar logo is plain again
ShellMenu.trigger chooses the trigger: 'pane' (default; each pane's wB button opens the menu under
itself, lined up with its end edge and kept inside the window), 'button' (titlebar) or 'logo'.
Alt+F, F10, the palette and the account menu's last row open it under the focused pane's wB (the
first one on screen otherwise). The menu's Settings row and the other panel rows still open the
panel, and Ctrl+Shift+W still does. The logo is part of the drag region again, as before e5d43af.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:22:41 +07:00
CRT
9fbfa791b4 Theme engine: page flags instead of page-wide :has()
A theme rule gated by html:has(...) / body:not(:has(...)) made the browser
restyle the whole page for every node the site inserted. Measured live on
x.com under any theme: about 440 elements restyled per insertion, against 1.5
without a theme; with the gates gone, 2.5.

Sites name page states as selectors (ThemeRuntimeSite.flags). The engine
keeps html[data-wb-<name>] in step, checked once per DOM hub frame and also
inside the site's open shadow roots. It passes the flags that hold to css(),
so CSS for one state can be left out of the other. Changing flags rebuild the
theme (they are part of its render key). Tests: pageFlags, flagAttribute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:42:45 +07:00
CRT
966a28cae6 privacy-scan test: a neutral example context for the allowlist
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:03:36 +07:00
CRT
cf98c381ae Theme in shadow roots: read the site's primitives inside the root
Live on X's chat embed: --x-white is declared only inside the embed's root, so
the document's values left .text-white unresolved and the sent bubble's label
stayed white on a light accent (2:1 under Nord). Each root's theme now reads
the site's primitive variables from the root's own content (our sheet off
while reading), falling back to the document's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 05:34:23 +07:00
CRT
d2c8b92464 shell: Ctrl+F, F3 and the Find row reach the last web tab when the app's own tab is in front
On macOS the shell can hold the keyboard while its own settings tab is active; Ctrl+F then did
nothing, so typed keys and Enter went to X's search field. Find now targets the last web tab that
was in front (or the first one on screen).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 05:31:08 +07:00
CRT
5c76df7cba Find in page: if the page kept the keyboard when the bar opened, ask for it once more
The keys that open the find bar are often pressed in the page. The bar asks for the keyboard (`shell_focus`) and focuses its field; if the shell still does not have the keyboard 250 ms later, it asks once more and focuses the field again, so typing never lands in the page (seen on macOS: text typed right after Cmd+F was lost). Diagnostics lines record both steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 05:23:16 +07:00
CRT
7f915eb27e Theme engine: the site's open shadow roots get the theme too
X renders its chat inside the open shadow root of its embed host since
2026-10-01; the theme's <style> in <head> never reached it. For each root the
DOM hub names (domRoots), the engine now builds the theme from that root's own
rules (collectRootRules: adopted sheets and <style> elements, its own sheet
excluded), adds the static sheet, adopts it last among the root's sheets,
rebuilds it when the root's rules change and takes it off when the theme is
off or the root is gone. Test: collectRootRules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 05:20:57 +07:00
CRT
f6deac4145 DOM hub: open shadow roots of hosts a site names are part of the page
X renders its chat inside an open shadow root since 2026-10-01 (the XChatEmbed
host [data-testid=xchatEmbedRoute]); the hub's document observer saw nothing
of it, selector observers found nothing, feature sheets in <head> did not
reach it and probes could not see it.

A site adapter may name shadowHosts. The hub's one MutationObserver then also
observes each such root (picked up on the next page mutation, or a few timed
retries while a host has no root yet; dropped with the observer), selector
observers search the roots too, domRoots(win) gives features the roots, a
feature's ctx.css sheet is also adopted by each root (CSP-proof, updated in
place, taken off on stop), and probes count matches inside the roots.
Tests: hub observes and searches a root that appears later with one
observer; registry adopts and removes the sheet and a probe inside a root is
never a miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 05:02:01 +07:00
CRT
046994f804 Last words of the night in every catalog: Share my setup (wb.setup.*), icon pack import (wb.appearance.icons.*), translate mode (wb.strings.translate*) and the menu's Languages row
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 05:00:31 +07:00
CRT
7e7105b54c Theme doctor: advice on imported themes, never a block
themeDoctor(theme) names the lighter mechanism for what a theme's CSS does
(radius, bevel, hiding, literal colors, fonts), its performance costs (:has,
universal selectors, blur, size) and accessibility costs (outline removed,
unguarded motion, small post text, moved navigation), plus contrast fixes.
Theme import adds the count and the first hint to its message. Tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:56:33 +07:00
CRT
cd9917aad3 Icon pack import: the install screen gets the row's full width
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:54:37 +07:00
CRT
a890dd506f create-waybetter: a TypeScript plugin project depends on waybetter ^0.6.0 like the app scaffold (^0.2.0 could not resolve to the current package)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:52:37 +07:00
CRT
10d86ef55c App menu: a Languages… row (after Store) opens the panel's Languages section; it shows only when the app says its panel has one (features.strings); English word wb.menu.strings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:52:27 +07:00
CRT
fe7f60cd8d Share my setup, links: a waybetter://setup link opened from outside reaches the shell as text on the install-link event (shape and size checked natively, never decoded or applied there); the install controller hands it to the app's onSetupLink and stages nothing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:49:57 +07:00
CRT
a8710dfe02 Theme v2: import icon packs (install screen, packs in the theme choice)
Appearance > Icons gets Import icon pack: a *.icons.json file is parsed by the
runtime sanitizer and shown on an install screen (name, author, licence,
source, credits, every glyph, size, data only) before it joins the persisted
theme choice (packs, at most 8, validated natively for shape and size) and
the Icons picker. Imported packs can be removed again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:48:26 +07:00
CRT
d902d3f65a App menu: with no menu button in the titlebar, the tab strip reserves no room for it
With the logo as the menu's only titlebar trigger (`<AppMenu trigger={false}/>`), `--bt-tb-menu` still kept 38px free at the end of the titlebar-hosted tab strip. It is 0 when there is no `.bt-app-button`, so the right side is back to its layout before the menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:45:26 +07:00
CRT
1c7a842d09 Languages section: a pack's name and its badges on one line
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:44:14 +07:00
CRT
44ea2d2060 Share my setup, panel: Backup & sync gets "Share my setup" (Copy link, Save file) and "Use a setup" (paste a waybetter://setup link or open a file, then What will change with Apply and Cancel); shown only for an app that sets PanelSite.setup; English words
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:43:24 +07:00
CRT
8005b2f047 README: the optional command groups the host serves and what the app supplies for each
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:42:07 +07:00
CRT
409949edb9 Host: the settings backup commands (collect, export, open, apply) are served for every app; SiteHooks::backup_drafts and backup_apply carry what only the app knows; commands::BACKUP, testkit::assert_backup_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:41:12 +07:00
CRT
198b1160f2 String packs: the site tab's delivery layer lives in app.rs, so a host without the net feature builds again (a generated app's cargo check failed on the net-gated store_commands module)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:40:25 +07:00
CRT
38b877f03a Share my setup, model: one file or one waybetter://setup link with the look (theme id, accent, icon pack), the mode and the non-default flags, nothing personal; reading drops what the app does not know and refuses another app's setup; the plan lists exactly what would change and applying it changes nothing else (not in the UI yet)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:38:30 +07:00
CRT
3402c1d85d Host: developer mode (dev_* commands and the watcher) is served for every app that sets AppConfig::with_dev; SiteHooks::dev_theme_apply and dev_theme_end carry the app's theme file; commands::DEV, testkit::assert_dev_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:32:54 +07:00
CRT
c83f541006 String packs, icon packs and install words in every catalog: wb.strings.*, wb.install.strings.*, license/credits, the strings install and error words, wb.appearance.icons*; dropHint and the two kind errors translated again for their new English
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:31:43 +07:00
CRT
311f5d9e20 App menu: one home in the titlebar, the logo
The top-right ⋮ and the logo opened the same menu: one of them was redundant.
The logo stays as the only titlebar trigger (the app's own mark, where an app
menu lives); the right side is back to workspaces, accounts and the window
buttons. Alt+F, F10, the palette and the account menu's last row open the
menu under the logo. Apps that want a separate button keep <AppMenu/>'s
default trigger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:26:40 +07:00
CRT
48ff509b15 Host: workspaces (always) and encrypted folder sync (AppConfig::with_sync) commands; SiteHooks::backup_sections and backup_section_names say what a backup and sync carry; commands::WORKSPACES, commands::SYNC, testkit::assert_sync_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:26:03 +07:00
CRT
385ca189da Docs: string packs as a Store kind (licence and credits on every item), the delivery to site tabs, the Languages section and translate mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:26:02 +07:00
CRT
b4157d7eac String packs: translate mode. Strings carry an invisible key marker, a click on a word opens an editor that checks the edit with the pack rules, edits collect in the tab as a draft pack that applies after a reload, and "Copy pack file" gives the pack; the Languages section switches it on (the delivery then carries the whole inventory, packs or not); native code accepts such a delivery without packs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:25:04 +07:00
CRT
8a0ab6d3a1 Icon packs are the owner's pick: the theme choice carries it, Appearance offers it
- ThemeChoice gains an optional icons field (absent: follow the theme's pack; site: the site's
  own icons; else a pack id), validated natively as a slug (waybetter-host theme.rs, with a
  test) and kept by per-theme choices; variants is an override group natively too.
- Appearance: an Icons row (Follow the theme / the site's own icons / the site's packs) when the
  site lists packs; English words in the catalog.
- The chrome's glyphs follow the owner's pick as well as the theme's suggestion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:19:55 +07:00
CRT
e88339bda6 Host: native update commands (update_status, update_check, apply_update, app_ready) and the update loop are served for every app that sets AppConfig::with_updates; commands::UPDATE, testkit::assert_update_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:18:34 +07:00
CRT
1904031e6f String packs in the shell: the install screen shows a pack's language, coverage against the site's inventory (and what is ignored or cannot be replaced yet), a sample, that it is data only, and any item's licence and credits; the Store lists string packs under Languages and removes them; the Languages section turns packs on and off, orders and removes them and hands the site tabs the new delivery; English words (wb.strings.*, wb.install.strings.*, wb.install.license/credits and the strings variants)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:14:41 +07:00