Commit graph

98 commits

Author SHA1 Message Date
CRT
124ebb3a73 Theme runtime: a static sheet for what does not depend on the site's stylesheet (components, variants, decoration, icons); refresh()
The site adapter's staticCss(choice, mode) is written to its own <style> (wb-theme-static), rebuilt
only when the theme or the site's mode changes or on refresh(); the palette pass never reads it back
as the site's rules, and the first-frame snapshot carries it.
2026-09-30 23:30:34 +07:00
CRT
3d9189077a waybetter manifests: winget, Chocolatey, Homebrew cask and AUR files from one JSON description
Generic (publisher, id, name, version, installer URLs and sha256 or local files, silent switches, licence, homepage). winget: version, installer and locale manifests (validated with the installed winget), Chocolatey: nuspec + install script with checksum (choco pack), Homebrew: a cask for our own tap, AUR: PKGBUILD and .SRCINFO from the .deb or AppImage. waybetter release windows|macos|linux writes them to <outDir>/distribution/<version> once the public download URL is given (--download-base); nothing is submitted.
2026-09-30 23:28:44 +07:00
CRT
c92e99b4bd Remote builds: unpack with the current time (cargo must not reuse stale objects from a cached target), export blobs as committed, build at low priority
git archive stamps files with the commit time, older than the cached build in the shared target dir: a changed source looked unchanged and the build reused stale objects (seen as a missing module after a pointer bump).
2026-09-30 23:26:19 +07:00
CRT
d6300aee3a Report a problem: a diagnostic summary with no personal data, shown as it would be shared, with Copy and the issue page
The app menu's "Report a problem…" opens a small dialog with the summary (reportModel.ts, English on purpose: the people who read reports do): the app and framework versions, the system and webview (coarse, from the user agent), the language and how it was chosen, the mode, the theme and icon pack ids, the file names of the plugins that are on, what paused itself (the panel's `reportDetails`), and safe mode. The model has no field for an account, a handle, an address, a tab or a path, and a value that is not made of id characters is left out rather than trimmed into shape, so nothing of that kind can ride along (tested with hostile input). The dialog sends nothing: Copy puts the text on the clipboard, and "Open the issue page" opens `ShellSite.menu.report.issueUrl` bare in the system browser (the summary is never put in a URL). Without an issue page the button is off and the dialog says so.
2026-09-30 23:25:13 +07:00
CRT
22a4665f18 waybetter release: builds of the committed state on a Mac over SSH and in WSL
release macos --remote and release linux --wsl export HEAD plus each submodule at its recorded commit (git archive, never the working tree, nothing fetched from origin), build it path-free through build-env with unsigned bundles, bring the artifacts back and scan them here. release linux --appimage/--deb signs the AppImage (the Linux update payload) on the release machine and adds linux-x86_64 to latest.json. The remote script is low priority, never installs, quits or replaces an app on the Mac. Config: prepareCommand, exportExclude.
2026-09-30 23:24:04 +07:00
CRT
5f910e95c1 Windows installer: an install made under the app's old name is older than any setup of the renamed app, whatever its version number (upgrade in place, data kept)
The renamed app may start its numbering again. Told apart by DisplayName in the uninstall key
(the old app wrote its old name there; this template writes the new one). Also exempt from the
silent-downgrade abort. Compiled with and without a legacy name and run on the test variant.
2026-09-30 23:23:43 +07:00
CRT
582c3d9460 Theme engine v2, the document side: component tokens, layout variants, decoration, icon packs
A theme may now say how each piece of a site is built, not only one radius and one density:
- components (themeComponents.ts): 36 generic parts with clamped tokens (radius, border incl.
  98.css-style bevels, fill by color role / glass / gradient, padding, sizes, type, shadow, shape,
  hover/active/focus), presets and parents; compiled over a site's part map as soft values
  (--wb-part-*) and rigid rules; text on a chosen fill keeps its contrast.
- variants (themeVariants.ts): fixed words on six axes; the owner holds an axis or switches layout
  changes off; the look of a variant is the site's skin.
- decoration (themeDecor.ts): dividers, chrome (title bar, masthead, prompt), a focus ring that is
  always visible, selection, scrollbars, links, motion under reduced-motion.
- icon packs (themeIcons.ts): a standalone document of glyphs by semantic name (path data, colored
  layers or a small PNG, never an SVG document), with license, source and credits; a theme only
  names a pack; the owner's icon choice is separate; drawn as CSS masks of the current color over
  the site's own svg boxes, behind forced-colors and mask-support guards, per group.
- a theme document gains the optional groups components, variants, decor, icons and
  colors.actions; a theme without them is sanitized to exactly what it was.
- theme.schema.json and the new icons.schema.json are generated from the same tables
  (scripts/theme-schema.mjs; a test fails when they are stale).
2026-09-30 23:22:36 +07:00
CRT
507cd95d8e READMEs for strangers: overview, three layers, a real minimal adapter, security model; the long reference moves to docs/ARCHITECTURE.md 2026-09-30 23:20:58 +07:00
CRT
8dd570147e Safe mode: one run with only the shell working (the app menu, --safe-mode, Shift at launch on Windows)
Host: `safe.rs` is a flag in this process's memory, never written to disk, so the next normal start is back to the owner's setup. While it is on a site tab gets the tab bridge and nothing of the app's page layers: its document-start scripts are `SiteHooks::safe_init_scripts` (default: the bridge alone) instead of `init_scripts`; `site_page_loaded`, `tab_placed` and `tab_background` are not called; `page_scripts` is empty; `eval_in_site_tabs` does nothing. So no page runtime, plugins, theme, custom CSS, feature flags, fixes, hotfix data or words reach a page: the site runs as in a browser, and only the shell is the app's. It starts from the launch argument `--safe-mode` (for when the interface itself is broken), from Shift held while the app starts (Windows), or from the shell: `safe_mode_get` / `safe_mode_set {on}` (shell-only), where a switch rebuilds every tab webview (profiles marked stale, `tabs-rebuild` asks the shell for its layout) so document-start scripts change too. Tests: the argument, the flag, and that app.rs and host.rs gate every layer.

Frame: the app menu's "Restart in safe mode…" says what it does before anything reloads; while on, the titlebar shows an amber "Safe mode | Leave" chip (one click leaves) and the row reads "Leave safe mode"; the chrome drops its theme (and pane themes) and takes it back on leaving, without touching the first-frame cache. `features.safeMode` (on by default).
2026-09-30 23:13:03 +07:00
CRT
b55c802097 Words: the deck is "Deck" (it was "Power User Mode", which collided with the Power User mode); the three feature-health words in all 48 translated languages (machine)
Keys and saved data unchanged. Each language reuses its own word for a deck; only "Deck settings"
is newly translated.
2026-09-30 23:05:23 +07:00
CRT
33c693ed93 Theme color model: a foreground the site paints dimmer than its secondary text becomes the theme's secondary text
A site may color an icon or text with one of its border grays (X's composer toolbar: 1.7:1 on its
own black). Placed by axis position it landed between a theme's background and its secondary
text, under every contrast a theme guarantees. In text usage that stretch of the axis (above the
most raised surface, below secondary text) now maps to the theme's secondary text; surface-dark
foregrounds (labels on a filled surface), borders and backgrounds are unchanged.
2026-09-30 22:58:48 +07:00
CRT
615981c7c9 App menu: "Check for updates" is a real check, once, now, and says what came of it calmly
Host: `updater::check_now` runs the same check the timer runs (the same updater endpoint, nothing else) for the app's shell-only `update_check`. It never queues or retries: while a check is in flight (the timer's or an earlier request's), or within 10 seconds of the last request, it only answers with the current status (`update::may_check_now`, tested). The timer's loop uses the same single-flight guard.

Frame: the menu's Updates row is that check (`updateRow`, tested): "Checking…", "Up to date · <version>", "Downloading <version>…", "Restart to update" when an update is ready (the row then installs it), and "Could not check" with a plain sentence as its tooltip when the server does not answer. The updater's error text is never shown there, and a failure is only mentioned when the owner asked for the check in this menu; the timer's own failures stay quiet.
2026-09-30 22:54:51 +07:00
CRT
9209280959 String packs, phase 1: data-only packs that replace the wrapped site's own UI strings. Pack and inventory formats, validator (only inventory keys, parameter names, plural and select kinds, no markup), ordered idempotent engine that keeps what a restore needs, coverage and drift report; the per-site catalog hook adapter with a self-check (ok, hook not matched, no catalog seen), a fail-open webpack chunk array hook, a small DOM text net; waybetter strings check 2026-09-30 22:38:07 +07:00
CRT
8324bc94af Find in page: the shell's find bar per pane (Ctrl+F, the app menu, the palette) over the engine's own find
Host: `tab_find {id, action, query}` (shell-only; start, next, previous, stop). On Windows it drives WebView2's own find (`ICoreWebView2Find` from `ICoreWebView2_28`, find options from `ICoreWebView2Environment15`; its built-in find bar suppressed, all matches highlighted) and reports the engine's match count and active match to the shell as `tab-find` events. On macOS, Linux and an older WebView2 Runtime native code evaluates the engine's `window.find` in the tab instead (next and previous work, no count); WKWebView's and WebKitGTK's native find are not wired here because this tree cannot be compiled or run for them on this machine. The query is one typed line (1..=256 characters, no control characters), goes to the engine and is kept nowhere. Tabs gain no command: a tab forwards the existing key name `find` (Ctrl+F) through tab_shortcut.

Frame: FindBar.tsx takes the address field's place in its pane's toolbar while open, so the page does not move and in split view the bar is the searched pane's. "3/17" where the engine counts, "No matches" for none; Enter / F3 next, Shift+Enter / Shift+F3 previous, Esc closes, clears the highlights and gives the page the keyboard. `features.find` (on by default) gates the menu row, the palette entry and the keys. Only what the page has rendered can be found, as in a browser.

Also: this makes `pub mod find` in lib.rs (already in HEAD) resolve again.
2026-09-30 22:34:48 +07:00
CRT
24c8620d4e README: Dev loop section 2026-09-30 22:32:38 +07:00
CRT
9af40411dc devloop guard test: skip its own module 2026-09-30 22:21:41 +07:00
CRT
04a405c1ba Dev loop: debug-only page scripts from disk + tab rebuild on change (waybetter-host devloop, page_script!), generic devmode watcher, waybetter dev, buildRuntime watch, template scripts 2026-09-30 22:20:22 +07:00
CRT
2fd866d8fb App menu: the titlebar logo opens it too, hung under the logo on its side of the window
The logo at the start of the titlebar is now a button (aria-haspopup, keyboard reachable, tooltip) and a second trigger of the same menu: same component and model, only the anchor differs (`openAppMenu({ anchor: 'logo' })`). A submenu opens toward the middle of the window, else on the other side, else over the menu (`submenuLeft`, tested). A click on the logo never starts a window drag; a press that moves more than 5px does (the window's `startDragging`), so the logo still moves the window like the titlebar around it. A right-click shows the window's system menu. Clear of the macOS traffic lights: the logo sits after the titlebar's 78px inset and the menu hangs below it. The button at the end and the account menu's row stay.
2026-09-30 22:15:09 +07:00
CRT
8dd5eee99b Feature health on the area cards: a feature that paused itself is named by its area's "health" palette entry, and the panel reads it for the row and the card (no new report to the host)
The entry opens the notes in the page; its keywords carry the paused flags of that area. English
for the three new words; the other catalogs follow with the words step.
2026-09-30 22:00:04 +07:00
CRT
715f6f2fec App menu: the titlebar's "Settings and more" (Alt+F, F10, the palette, the account menu's last row) with rows from the site config; page zoom per tab in the host
Frame: AppMenu.tsx is a menu at the end of the titlebar cluster (before the caption buttons; macOS has none there). Its rows are `ShellSite.menu`: ordered groups of built-in rows by name and the app's own ({id, label, icon, shortcut, hint, when, checked, run | submenu}); `DEFAULT_MENU`, `menuWith` and `menuWithout` order, add and remove. A built-in row shows only when the app serves it (decks, `menu.languages`, themes, `menu.modes`, and `features`: zoom, store, workspaces, developer, updates), so no app gets a dead row. Language, Theme, Mode, Workspaces and Developer are submenus; Language and Theme apply live and keep the menu open, Mode asks the same keep-my-changes question as Settings (panel `menuModes()`). Keyboard: arrows, Home/End, type-ahead, the arrow along the reading direction opens a submenu, Esc closes one level; ARIA menu roles; submenus stay inside the window; one highlight for pointer and keys. It is an overlay like the other shell menus (the shell is raised, panes stay visible, nothing is dimmed).

Words: menuMessages.en.json (`wb.menu.*`), read through the framework i18n with that English as the fallback until the catalogs have them; words the panel already has are reused.

Host: `tab_zoom {id, factor}` (shell-only) sets a tab's page zoom with the engine's own zoom, clamped to 25%..500% and remembered per tab id in app data `zoom.json` (a number per tab id, nothing else; a closed tab's entry goes); without a factor it reads. A tab may forward four more shortcut names: `app-menu`, `zoom-in`, `zoom-out`, `zoom-reset`. The titlebar shows a zoom chip only while the pane in front is not at 100%.

create-waybetter: `tab_zoom` in the shell capability list; the template says which panel sections it lacks (store, updates) and how to add menu rows.
2026-09-30 21:52:51 +07:00
CRT
061f9c4725 Route listeners: one removed by an earlier listener in the same round is not called (a feature stopped by the route re-added its stylesheet); the route poll never keeps a test process alive 2026-09-30 21:19:02 +07:00
CRT
041ee3b4f6 Panel: a long section name wraps left-aligned in the side rail instead of centring 2026-09-30 21:19:00 +07:00
CRT
e66875515d Theme engine: a rule is read as it takes effect (a Tailwind opacity utility's nested @supports value, not its solid fallback); unsupported @supports groups are left out; reach() is exported for site adapters 2026-09-30 21:04:12 +07:00
CRT
b7575681b2 Feature modules: a feature ported from a plugin keeps that plugin's words (words), so nothing is retranslated; the keep-my-changes question takes its own line 2026-09-30 19:53:11 +07:00
CRT
d633de09d2 Locales: the area cards, the Fixes list, modes and the feature settings words in all 48 translated languages (machine) 2026-09-30 19:37:56 +07:00
CRT
513200b707 Feature modules in a new app, i18n rename, and what the first area port needed
- create-waybetter: the generated app has a feature table (one area, one feature, three modes), a feature module, the registry in its runtime, the cards and the first-run screen in its panel
- waybetter i18n rename --map <file> [--copy]: keys move or are copied in every catalog with each language's own value
- feature context: win (no globals in a module), text() for a feature's words (t('...') is read as a whole catalog key by the i18n tools)
- probes: `when` (only counts while the part of the page it lives in is there)
- cards: a caret, an empty area says its features come later; the first-run screen takes the whole panel
2026-09-30 19:25:15 +07:00
CRT
c7c25fa8bc Panel: area plugin cards with per-feature toggles, the Fixes list, modes (first-run screen and Settings)
- PanelSite.features: the app's feature table; AreaPlugins draws one card per area (switch, count, grouped features, Settings button that runs the area's command in the site tab) and the core flags as Fixes
- a feature still backed by an old plugin file is switched through plugin_set_enabled from its card and left out of the JavaScript plugins list
- Modes: Onboarding (three cards, recommended preselected; an existing setup may be kept) and ModeSettings with the keep-my-changes question; openOnboardingWhenDue for ShellSite.start
- flags are saved sparsely (only what differs from the table's defaults)
- PanelSite.appearance.extra; palette commands of an area are listed under its name
- the kit's words for feature settings and health notes; English in the framework catalog
2026-09-30 18:34:14 +07:00
CRT
e38314f65c Host: 256 feature flags, the onboarding mode (mode_get/mode_set, shell-only), and the one-time move of replaced plugin files to feature flags
- settings: MAX_FEATURES 64 -> 256 (an older app's whole-map backup still validates)
- modes: ModeState {mode, onboarded, existing} in modes.json; existing is decided natively on the first start
- plugins::migrate: PluginHost.superseded (file -> flag); enabled and as shipped -> flag on and the file goes; edited -> kept, flag left off; marker written last, every step repeatable; map_backup for imported enabled lists
- a superseded file is never written again by install_bundled
- create-waybetter: the host command list and PluginHost.superseded
2026-09-30 18:34:13 +07:00
CRT
43fa215f3b Feature modules: a feature table (areas, flags, modes), a registry that runs a module only on its surfaces, one shared observer for every feature and plugin, settings from a schema, selector health with self-pause
- featureTable.ts: areas, per-feature flags, core flags, modes as presets; sparse storage, ownership (an area that is off turns off what it owns), mode switching that keeps or drops manual changes, table validation
- featureModules.ts: createFeatureRegistry (flag + surface gating, scoped context, palette commands under the area with a budget of 5, settings dialog, probes, hotfix selector overrides and kill flags)
- settingsForm.ts: a settings schema normalizes stored values and draws the controls from the UI kit
- domHub observeSelector; api.observe goes through the shared hub (a root of the caller's own keeps its observer)
- createFeatureFlags: a delivery is the whole state (a flag it does not name is at its default), resolve() for ownership, current()
2026-09-30 18:11:42 +07:00
CRT
7cc82a9f2a build-env: MSVC C and C++ lose the build paths too (/d1trimfile in CL, /PDBALTPATH in LINK)
A library a build script compiles with MSVC (CMake, the cc crate) kept its
absolute source paths through __FILE__: -ffile-prefix-map exists only on gcc
and clang. cl.exe and link.exe read CL and LINK themselves, so the roots are
trimmed whatever runs the compiler. Longest root first: the first match wins.
2026-09-30 17:48:27 +07:00
CRT
308d78920a Comments: X's own panel sections are Tweaks and Drafts (My Space removed for now)
wayBetter for X took My Space (profile customization) out; the framework only named it in
comments as an example of an app's own section and runtime member.
2026-09-30 16:52:50 +07:00
CRT
6acc84a8e3 waybetter 0.6.0: path-free native builds (build-env) and the privacy-scan release gate
A Rust binary embeds the absolute paths of its sources, and on a developer's machine those hold
the account name. waybetter build-env runs a build with the home folder, the workspace, CARGO_HOME,
RUSTUP_HOME and CARGO_TARGET_DIR remapped to /home, /src, /cargo, /rustup and /target (flags already
set are kept), a path-free PDB reference on MSVC and the same mapping for C sources on gcc and clang.

waybetter privacy-scan searches files, binaries (UTF-8, UTF-16), archives (zip, tar, gzip, zstd,
.deb, DMG; NSIS, AppImage and xz through 7-Zip or the matching tool), git-tracked files and a whole
history for the terms of a file kept outside every repository plus the machine's account name. Hits
are printed masked; a container that cannot be opened is a failure.

waybetter release windows builds with build-env; both release commands refuse an artifact with a hit.
The scaffolded app gets desktop:build and privacy:scan scripts.
2026-09-30 15:58:25 +07:00
CRT
a8dd727612 i18n: supported and community language tiers
Automatic language choices resolve only to supported languages; community
translations are listed apart in Settings and offered as a one-click switch.
waybetter i18n check reads `supported` and generates `tier` into languages.json.
2026-09-30 15:48:11 +07:00
CRT
eb496a795b locales: light review of the 11 most-used languages (terminology, wording; 272 strings) 2026-09-30 14:59:59 +07:00
CRT
f99b8e6755 waybetter 0.5.0: docs for the host's page commands, the runtime composition, the i18n command and dev-tools 2026-09-30 14:48:06 +07:00
CRT
e1aa2631d9 dev-tools: the generic development harnesses (perf probes, close check, tab and webview stress)
Moved from wayBetter for X; an app passes its own origin, process name, scripts and fixtures.
2026-09-30 14:48:06 +07:00
CRT
d8a94a2285 waybetter i18n check|extract: an app's catalogs against the framework's rules
Configured by waybetter.i18n.json in the app's root; --write regenerates languages.json and the native localization files (macOS, Linux).
2026-09-30 14:48:05 +07:00
CRT
c570f308e9 locales: the Windows installer's words (wb.installer.*) and its language files, generated from the catalogs
Moved from wayBetter for X: 27 messages in 51 languages, and windows/languages/<Language>.nsh for the 17 NSIS languages Tauri does not ship (scripts/installer-languages.mjs writes and checks them).
2026-09-30 14:48:04 +07:00
CRT
4c40d28eac create-waybetter: a new app gets the real panel, the composed runtime and the host's commands
- src/shell/Panel.tsx is createPanel (waybetter/shell/panel) with the app's home page: Home,
  Plugins with custom CSS, Accounts and Settings, growing with what the app serves.
- src/runtime/main.ts is composed with waybetter/runtime/siteRuntime (feature flags, page strings,
  plugin API views per @permissions, the floating panel button).
- Native: the host serves the plugin, custom CSS, feature flag and settings commands
  (AppConfig::new(&SITE, title, &plugins::HOST), app::page_scripts on a loaded page); the app keeps
  tab_report and site_ready. site-tab grants the host's open_panel on the site only, checked with
  assert_site_only.
- HOST_COMMANDS follows commands.rs again (shell_ready, tab_still_ready and the new ones; SITE).
2026-09-30 14:28:55 +07:00
CRT
dba43059b3 runtime: the site runtime's composition (siteRuntime, pageStrings, panelButton, commandReport)
What every app's page runtime entry did by hand (moved from wayBetter for X's inject/entry.tsx):

- siteRuntime.ts: pageInvoke (the IPC bridge read at call time), createFeatureFlags (parts that
  install on enable and dispose on disable, the flags native code delivers, a deck column's
  overrides on top of them, recorded at once and applied when <html> exists) and
  installSiteRuntime (the runtime object with the members native code calls, the plugin API views,
  developer-mode errors, its window names, the queued native deliveries, the command report).
- pageStrings.ts: the page's language (English built in, deliveries checked as data, the
  bundled-plugin namespace rule).
- panelButton.ts: the way into the panel from the page (the site's own navigation entry when the
  adapter mounts one, else a floating button that hides under the site's overlays and sits above
  its bottom bar).
- commandReport.ts: the palette command report and developer-mode start errors.
- tests/siteRuntime.test.ts.
2026-09-30 14:28:54 +07:00
CRT
d71a9391fd host: plugins, custom CSS, feature flags, settings and open_panel are host commands
run_app now keeps and serves what every app's panel and page need, with the same caller checks
as the rest (commands::SHELL: shell_only first; testkit::assert_host_guards covers them):

- app.rs: PageState (the plugin store in app data `plugins`, the feature flags, ShellSettings)
  managed by the host; commands plugins_list, plugin_set_enabled, plugins_open_folder,
  custom_css_get/set, features_get/set, settings_get/set (moved from wayBetter for X). The app
  reads the state through app::page(), gets a loaded document's scripts from app::page_scripts()
  (flags, links, custom CSS, plugins) and writes through set_features/set_settings/set_custom_css
  (a backup import). AppConfig takes the app's PluginHost (AppConfig::new(site, title, plugins))
  and an optional home_url. SiteHooks::navigation_started and new_window_mode default to the
  owner's link settings.
- settings.rs (pure, tested): ShellSettings { homeUrl, externalLinks, siteLinks, restoreTabs }
  with its input form (missing fields take the defaults; `xLinks`, the name wayBetter for X used,
  is read as siteLinks), links_script ({ site: mode }), feature validation and features_script.
- commands::SITE + open_panel: a tab on the site's origin asks the shell to open the app's panel
  (site_tab_only first). testkit: assert_site_only(command, site) checks that every capability
  granting a site command is a tab capability limited to the site's URLs; assert_host_commands and
  assert_host_guards include the site commands.
- shell frame backupModel: a settings section that still says xLinks compares as siteLinks.
2026-09-30 14:28:52 +07:00
CRT
ed06e24209 docs: the panel in the framework (what an app supplies, which sections it gets, words, native commands) 2026-09-30 14:03:41 +07:00
CRT
b2767a8e56 shell: the panel, createPanel(site) (moved from wayBetter for X)
packages/shell/src/panel (waybetter/shell/panel): an app's own tab. An app describes itself
(PanelSite: brand and the home page's words, its own sections and their order, its theme store,
catalog and the preview component a theme document is drawn in, Make's examples and docs, the
install controller, its languages, what its native side serves) and createPanel returns the
component for ShellSite.panel. It is in the shell package because it is drawn in the frame and
shares its stores, native bridge and language.

- Panel.tsx (header, settings search over every settings section, navigation, toasts; Accounts
  and Updates), nav.ts (which built-in sections an app gets: each shows only when the app serves
  what it needs; the app's sections in its order), Home.tsx (hero, highlights, About from the
  app's data), controls.tsx + panel.css.
- Appearance.tsx, ThemeBuilder.tsx + builderModel.ts + builder.css, fonts/ (FontBrowser, FontRoles,
  catalog, the offline Google Fonts list; scripts/google-fonts-catalog.mjs regenerates it):
  the theme documents are the runtime's, the store, catalog, accents, draft palette, file suffix
  and preview are the app's (PanelSite.themes).
- Plugins.tsx (the plugins list and custom CSS over the host's plugins_*/custom_css_*),
  StoreSection.tsx, MakePanel.tsx + makeModel.ts + MarkdownDoc.tsx + markdown.ts + make.css (the
  examples, the theme file and the docs are the app's), Settings.tsx (links and session over
  settings_get/set with `siteLinks`, keyword alerts, the media panel), FeatureToggles.tsx
  (useFeatures + FeatureGroups over features_get/set: the app names its flags and groups),
  PowerSettings.tsx (deck defaults from site.deck: its flags and presets; DeckPreset gets `name`
  and `hint`), BackupPanel.tsx + backup.css, DeveloperPanel.tsx, LanguageSettings.tsx, compare.ts.
- frame: language.ts (createLanguage: follow the site / the system / one language, i18n_set;
  moved from the app), appI18n.ts (createAppI18n: the app's typed i18n instance, useT, dates and
  numbers; the app passes React's useSyncExternalStore), formatDate in i18n.ts (ShellI18n.date).
- tests/panel.test.ts: every panel key has English and is used, sections per app, the builder
  model, the font catalog, plugin header rules, the UI kit demos, Markdown, compare.
- npm package: exports ./shell/panel, ./shell/panel/*, ./shell/panel/*.css; the build is
  scripts/build.mjs (the shell is compiled with the runtime sources it imports under one root and
  its imports are pointed at dist/runtime; stylesheets and the font list are copied).
2026-09-30 14:03:40 +07:00
CRT
81ab5bd9fc locales: the panel's words (wb.*), 51 languages
- 668 wb.* messages for the panel (navigation, settings search, accounts, updates, Appearance, the
  Theme Builder, fonts, plugins, custom CSS, Make, Power User, settings, backup and sync, developer
  mode, the controls), moved with every translation from wayBetter for X's catalogs; `{site}`
  stands where the English said "X" (49 keys; the app passes "X", so X reads the same). Keys that
  named X got site-neutral names (settings.links.site*, settings.language.followSite*,
  builder.leadingSite, builder.liveOnSite, fonts.asSite, make.siteDefaultLeading, features.default).
  English source: shell/src/panel/panelMessages.ts.
- One translation could not take `{site}` (hu wb.settings.alerts.hint never named the site) and
  falls back to English.
- locales/en.meta.json: the translator notes of the moved keys (40), among them the one fragment a
  language may leave empty (wb.make.step2Before); the catalog test reads it.
- runtime/tests/i18n.test.ts: en.json is the kit, shell-model, store, shell-frame and panel source.
2026-09-30 14:03:09 +07:00
CRT
76a221a3ef The Windows installer is the framework's: a setup run again opens the installed app
packages/waybetter/windows/installer.nsi (shipped in the npm package) is the NSIS
template wayBetter for X used, made generic: an app that was renamed keeps its
registry keys by defining WAYBETTER_LEGACY_NAME in its installer hooks; without it
the keys are Tauri's defaults.

Run by hand (no /UPDATE) it installs only when it is newer than what is installed.
With the same or a newer version installed it installs nothing, opens the installed
exe (the single-instance guard shows the running window) and exits 0, so an old
setup can no longer replace or downgrade an install the updater has moved on.
/FORCE reinstalls the same version, /NORUN is kept, the updater path is unchanged.

create-waybetter apps use the template (bundle.windows.nsis, per-user).
wayBetter for X's page is waybetter.fyi/x in the READMEs.
2026-09-30 13:55:00 +07:00
CRT
526a4a656d fix(shell): coordinate startup cover and tab stills 2026-09-30 12:25:46 +07:00
CRT
5c93b78704 runtime + host: the theme engine (theme documents, choice catalog, page runtime, fonts; native theme state)
The site-independent theme engine moves from wayBetter for X into the framework:
- runtime/themeSpec.ts: theme document types, the schema's defaults (BASE_*), sanitizing,
  import, overrides, ensureContrast, font roles/stacks, font choices, backgrounds.
- runtime/themeChoice.ts: ThemeChoice and createThemeCatalog(builtins) (normalize, base,
  resolve, palette, all), choiceForId, serializeChoice.
- runtime/themeRuntime.ts: installThemeRuntime(win, site) (live CSS over the site's rules,
  --wb-* vars, site color mode, first-frame snapshot), collectRules, resolvedFonts.
- runtime/fonts.ts: bundled fonts and owner-downloaded Google Fonts from bytes.
- themeApi exports hexToRgb, mix and MONO_FAMILIES (one copy: themeTokens and themeSpec use them).
- host theme.rs: ThemeChoice/ThemeScopes over a site's ThemeSite (envelope checks against
  theme.schema.json, which theme a tab shows, page color, delivery script).
2026-09-29 13:07:59 +07:00
CRT
3a4c7c9831 lockfile: waybetter 0.4.0 2026-09-29 12:50:33 +07:00
CRT
0f281f13b6 docs: the shell frame in the framework (what a site supplies, styles, words, native commands)
README: "The shell frame" (createShell, the ShellSite slots and features, frame words and styles,
bootScript, preview mocks, the native commands and events it uses), the layout, the create-waybetter
app, how wayBetter for X consumes it. npm README: waybetter/shell/frame.
2026-09-29 12:47:19 +07:00
CRT
9ac8ab8767 create-waybetter: a new app's shell is the framework's shell frame
The app skeleton's src/shell/main.tsx calls createShell with the site (pages, account rules, search,
address chips, a Home link, a Plugins section) and src/shell/Panel.tsx (the app's own tab: the
plugins, switched on or off live); app.css themes it with the --wb-c-* tokens. build.mjs bundles
the shell with waybetter/build (JSX, CSS into dist/shell.css) and writes bootScript() as boot.js.
desktop.rs keeps the frame's titlebar (custom_titlebar: true) and forwards shortcuts and link drags
(BridgeOptions::ALL); the shell capability grants the frame's window calls. Native features the
skeleton does not wire up (workspaces, sync, developer mode, plugin commands) are off in the site.
Dependencies: react, react-dom, dockview, waybetter ^0.4.0.

Tests: sources import only published subpaths (patterns included), main.tsx uses createShell, the
shell capability has the resize permission, and the skeleton typechecks with the React types.
2026-09-29 12:47:18 +07:00