Commit graph

39 commits

Author SHA1 Message Date
CRT
5211995694 Find in page: WebView2 counts its active match from 1; the shell's index is 0-based
Seen live on the shared app: "domain" on example.com showed "2/2" for the first of two matches, and "2 matches" after Next. `ICoreWebView2Find::ActiveMatchIndex` starts at 1 (-1: none); `FindState::from_webview2` converts it (tested), so the bar reads 1/2, then 2/2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:47:23 +07:00
CRT
3450eac09a Host: the store's commands (install links, dropped files, the signed Store, the install screen) are served for every app that sets AppConfig::with_store; commands::STORE, testkit::assert_shell_only_group and assert_store_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:40:37 +07:00
CRT
26658e14ae Modes: a mode that no longer matches the features reads "Custom (based on <mode>)" and offers "Re-apply <mode>"
A mode is a starting point, never a lock and never applied again on its own. modeStatus() (runtime)
compares the flags with the mode's preset: custom, what re-applying turns on and off, and which of
those the preset gained in an update since it was applied. mode_set keeps the preset of that moment
(modes.json "preset", flag names only, bounded; forgotten when the mode changes). Settings, the
first-run screen and the app menu's Mode row show Custom; Re-apply lists what it turns on and off
before it writes anything; a preset that grew is mentioned once on the mode row (Review), never applied.
Tests: exact match, changed by the owner, preset grew, choice made before the preset was kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:33:41 +07:00
CRT
ca46210e53 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).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:13:03 +07:00
CRT
2f126659f9 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.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:54:51 +07:00
CRT
b31fa6b1d2 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.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:34:48 +07:00
CRT
098b985bce devloop guard test: skip its own module
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:21:41 +07:00
CRT
e84470a95d 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
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:20:22 +07:00
CRT
e5d43afffc 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.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:52:51 +07:00
CRT
e7e744f66d 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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:34:13 +07:00
CRT
f6a50602d7 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.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:28:52 +07:00
CRT
5a2e959763 fix(shell): coordinate startup cover and tab stills 2026-09-30 12:25:46 +07:00
CRT
01f2ec0417 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).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:07:59 +07:00
CRT
52a35048bc host: run_app — the window, shell, tab webviews and generic commands for every app
The `app` feature (App / run_app, AppConfig, SiteHooks): registers the Tauri plugins every app
uses (single-instance, deep-link and updater opt-in, window-state, opener, process, notification,
dialog), makes app data, loads profiles and the language, builds the window (restore, lost-geometry
reset, helper-close routing, native resize follow) and the transparent shell, and serves tabs_sync,
tab_navigate, tab_action, open_external_url, shell_focus, shell_raise, window_system_menu,
profiles_get/set, i18n_get/set (shell-only) and tab_shortcut, tab_link_drag (tab-only). The tab
builder is generic; the app fills in SiteHooks. `commands` lists them (manifest() for build.rs),
`testkit` checks capability files and caller guards, i18n::page_script is origin-guarded.
check:tauri now checks the app feature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:51:24 +07:00
CRT
ae61ae5526 host: the tab bridge ships from waybetter-host; create-waybetter uses it
tab-bridge.js (moved from wayBetter for X) is served by bridge::SOURCE,
or by bridge::script(BridgeOptions) with shortcut and link-drag
forwarding switched off for a shell that does not handle them (a
forwarded shortcut is taken from the page). Tests: one options line,
only tab_report/tab_shortcut/tab_link_drag, every forwarded shortcut is
one tabs::valid_shortcut accepts. Generated apps now inject
bridge::script(BridgeOptions::REPORT_ONLY) instead of writing their own
duplicated src-tauri/runtime/tab-bridge.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:22:54 +07:00
CRT
5b8ef1f7d3 host: updater (signed native updates and the hotfix channel)
updater (tauri + net features): the launch handshake, the cached hotfix
(re-verified on every load), the 30 min +/- 5 check loop, download and
verification through tauri-plugin-updater, the size-capped hotfix fetch
(no identifiers), hotfix delivery to the site's tabs, and status/apply/
ready for the app's shell-only commands. An UpdaterConfig carries what
differs per app: SiteSpec, hotfix URL and public key, page global/event,
manual download page, user agent and the "update ready" hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:17:54 +07:00
CRT
006ec960a3 host: self-hosted Google Fonts (gfonts) and the font cache (fonts)
gfonts (pure): CSS2 API URLs, the fonts.googleapis.com/fonts.gstatic.com
allowlist, CSS parsing and face selection, woff2 checks, the sha256
manifest cache format and the page script, whose origin guard and
runtime global now come from the SiteSpec. fonts (net feature): the
download client (allowlist re-checked per request, no redirects, size
caps), the family/preview caches and install/remove/faces/preview for the
app's shell-only commands. Tests and CSS fixtures moved with them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:15:10 +07:00
CRT
40ae81afbe host: gallery folders and the opt-in clipboard history
media: gallery settings (absolute folders, deduplicated), listing and
resolving images only directly inside owner-chosen folders (links must
stay inside), data URLs, the bounded clipboard history with its index
and image files, PNG encoding. media::clipboard (tauri feature): reading
the OS clipboard, the Windows change counter, skipping copies marked
private by password managers, and the opt-in polling loop (records from
the next copy after opting in; releases the clipboard while off).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:13:01 +07:00
CRT
c40ea12bfa host: Cargo.lock with both the store module and the alerts plugin
The alerts commit was locked against the tree before the store module
landed and dropped the store's packages (ed25519-dalek, reqwest, ...);
this is the store commit's lockfile plus tauri-plugin-notification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:06:56 +07:00
CRT
0f97439dd2 host: keyword alerts and native notifications
alerts: keyword settings (normalized, deduplicated, bounded), the
configured-keyword check, plain bounded snippets, the per-keyword and
global Throttle and an AlertGate over any KeywordMatch (one notification
per item). With the tauri feature, alerts::notify shows a native OS
notification (tauri-plugin-notification, registered by the app).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:05:49 +07:00
CRT
6a72e81256 host: store module: install links, allow-list, signed multi-app catalog, staging and installs
Moved from wayBetter for X (distribution.rs + desktop_store.rs) so every wayBetter app gets the
Store; the app keeps its Tauri command wrappers and a `StoreConfig`.

- store::catalog (pure): `StoreConfig` (app id in the shared catalog, product, user agent, link
  scheme, store URL, public keys, allowed hosts), `AllowedHost` (raw-files-only rule for a code
  host; DEFAULT_HOSTS = waybetter.fyi, cdn1.crt.fyi, git.crt.fyi raw), install-link parsing,
  download allow-list, theme validation, catalog format 2: every item names its `apps`; the whole
  catalog is verified (Ed25519 over the exact bytes with any listed key, so keys rotate; strict
  schema; ids/files unique per app) and each app gets only its own items (another app's item is
  shape-checked but not held to this app's hosts); per-item sha256/size, install records, versions.
- store::install: `Store` (pending links, staged installs with TTL/bound, MIN_REVIEW before a
  commit, commit writes exactly the staged bytes after re-validation via PluginStore::install,
  plugin remove, records file, verified catalog + offline copy verified again when read back,
  StoreView). Plugin start/stop scripts run through a function the app passes.
- store::net (`net` feature): no-redirect, no-cookie reqwest fetch with caps; stage_link,
  stage_item, catalog(refresh).
- Tests: links, per-app allow-lists and schemes, themes, signatures incl. tampering and key
  rotation, multi-app filtering, bad items, staging/commit/remove/records/offline copy on disk,
  fetch refuses anything off the allow-list. test:rust and check:tauri build with `net`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:05:25 +07:00
CRT
63e8a9c0b6 host: palette commands and developer mode watcher
plugins::commands: the palette commands a site tab reports (validated and
capped as a whole) and the page script that runs one, now built from the
SiteSpec (runtime global, primary origin) instead of a hard-coded x.com.
devmode: the debounced polling watcher (plugins, custom.css, one theme
file), reload order, DevError and theme-file reading; the app turns the
document into its own theme state through a callback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:02:07 +07:00
CRT
0d6e7d3323 host: saved workspaces validation
Named tab/pane/deck layouts, bounded and well-formed (slug ids, 1-60
character names, known tab kinds, http(s) URLs, size caps), moved from
the wayBetter for X app unchanged with its tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:59:20 +07:00
CRT
8ff27a629f host: settings backup envelope and encrypted folder sync
backup: the versioned .waybetter envelope (format/version checks, only the
app's registered sections kept), secret-key scrubbing, the plugins /
customCss / accounts sections every app has, plugin import checks and
atomic writes. sync: Argon2id + XChaCha20-Poly1305 snapshots of the
settings backup in a folder the owner syncs, moved unchanged from the
wayBetter for X app with its tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:58:15 +07:00
CRT
5f62b471f0 i18n: dependency-free message runtime, catalog checks, plugin strings, native strings
- runtime/i18n.ts: ICU-lite messages (placeholders as text, Intl.PluralRules plurals with exact
  forms, selectordinal, select, number/date/time), fallback chains (pt-BR -> pt -> en, ar-x-fm ->
  ar), X and legacy code aliases, RTL by locale, lazy catalogs, live onChange. Apostrophes are
  literal except right before { } # or another apostrophe.
- runtime/i18nCheck.ts: rules for contributed catalogs (shape, keys, syntax, placeholder parity,
  select options, markup/control characters, lengths). An argument English only uses to choose a
  plural form may be dropped; missing plural forms are warnings; meta "allowEmpty" fragments.
- runtime/pluginI18n.ts: api.i18n bound per plugin (register/t/locale/dir/onChange/number/date/
  relative/list); the UI kit's own words follow the app's language. pageLang.ts reports the
  site's <html lang>. shell/messages.ts: shell-model words.
- waybetter-host i18n.rs: NativeStrings (validated plain text, caps, one-pass {name} fill, page
  payload by prefix), system_locales via sys-locale; TabState.lang.
- JSON Schemas for catalogs and translator notes; npm exports waybetter/i18n, /i18n/check,
  /i18n/plugin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:53:48 +07:00
CRT
9313f249af host: tab placements skip unchanged bounds, one SetBounds per move, and follow window resizes natively
layout.rs (headless, tested): Placements remembers each tab webview's last rectangle and
visibility, so a tabs_sync that changes nothing makes no native call and one that moves one
pane touches one webview; Frame/follow_rect estimate where panes go when the window size
changes (outer margins fixed, proportional in between, shared borders stay shared).

host.rs: place_tab/forget_tab use it; place_child is a single set_bounds (one WebView2
SetBounds instead of a move then a resize, each a page re-layout); follow_window_resizes
re-places visible tabs from the window's own Resized events on the main thread, before the
shell lays out again, and keeps WindowMetrics current so a sync needs no blocking round
trips for the window size; layout_window says when a layout was made for a stale size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 03:23:44 +07:00
CRT
0ad52a4ce7 host: raise_shell on Linux and macOS; tab stills before a hide (Windows)
Linux: the shell becomes a GtkOverlay layer itself (an empty box is the base) and is
re-ordered to the top or bottom of the overlay; a tab placed while it is raised goes under
it. macOS: the shell's WKWebView is re-ordered among its superview's subviews
(addSubview:positioned:relativeTo:), keeping the first responder if it was in the shell.
Both now report support, so overlays no longer hide the tabs there either (the app makes the
shell webview transparent; macOS needs tauri's macos-private-api).

Windows: a tab webview about to be hidden hands the shell its last frame (WebView2
CapturePreview, JPEG, `tab-still` event), for its pane to show instead of an outline while
no webview is on screen there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 02:39:48 +07:00
CRT
7f3a4b2428 host: raise_shell puts the shell webview above or below the tab webviews (Windows, SetWindowPos on its WebView2 container)
Shell overlays can then draw over the panes while they stay on screen.
Returns false elsewhere, where the caller hides the tabs instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 02:00:34 +07:00
CRT
74f4d02944 runtime: plugin commands for the command palette (api.commands / scope.commands: register, list, run, owner = the starting plugin, removed on stop, caps, in-page shortcuts); host: start/stop scripts own and drop a plugin's commands; dev_start_script with sourceURL + devError for live reload; devErrors (file:line from a start failure)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 01:35:44 +07:00
CRT
ee7907740a Plugin permissions and app installs: @permissions header, per-plugin API views, validated install/remove
- host plugins.rs: PERMISSIONS, parse_permissions, ScriptPlugin.permissions/problems (None = legacy,
  full API), validate_plugin (file name, 2 MiB, UTF-8 text without binary bytes, header with @name
  and @version, valid @permissions, network:none refused when the code calls network APIs),
  source_findings, PluginStore::install (atomic rename, never enables, keeps the enabled state on
  update) / remove / is_enabled / file_hash. start/stop scripts run the module body, construction,
  start() and stop() inside window.<global>.withPlugin(id, grants, fn) when the runtime has it.
- runtime permissions.ts: parsePermissions, scopePluginApi (undeclared gated members throw a
  PluginPermissionError; scope().ui gated like api.ui), createPluginContext (the view of the plugin
  whose code runs; legacy = full API). A declaration and guard rail, not a sandbox.
- The `commands` permission name comes from the parallel palette-commands work (its API lands there).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 01:08:52 +07:00
CRT
395b27c937 host: route graceful closes of helper windows to the app window (Windows)
tao's thread event target and the single-instance message window are visible
1 px unowned top-level windows. taskkill /PID (WM_CLOSE, no /F) closes only the
first such window in z-order; a minimized app window sinks below them, so the
close destroyed a helper and the app kept running. route_helper_closes
subclasses those helpers and forwards WM_CLOSE to the app window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:27:10 +07:00
CRT
67851cd8f5 create-waybetter: npm create waybetter scaffolds plugins (JS/TS), themes and whole apps
- plugin: *.plugin.js with the store header and start/stop on api.scope, storage, observe,
  onKey, the UI kit (toast with action, ui.h settings dialog) and api.theme/--wb-* vars;
  --ts makes a TypeScript project typed by the waybetter package (build keeps the header first)
- theme: JSON with "$schema" -> theme.schema.json, palette derived like themeFromColors,
  font roles (family, tweet), presets
- app: Tauri 2 + waybetter-host + runtime + shell models: SiteSpec, SiteAdapter, shell/tab-chrome/
  site-tab capabilities, tabs_sync, plugin store, a site-only command; presets for youtube.com,
  kick.com, twitch.tv with placeholder selectors; host crate via submodule, path or git
- tests generate into temp dirs: headers via parse_meta (new plugin_meta example), plugin code
  against createCorePluginApi's members, themes against the schema, app TS with tsc against the
  built package, app Rust with cargo metadata/check/test
- wired into bun run check (typecheck incl. checkJs, build:packages, test:create) and pack:check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:17:32 +07:00
CRT
b3f2500314 Deck columns (Power User Mode): shell deckModel (bounded decks, paging layout, focus, unread totals), runtime column mode (hide site chrome, fill width, filters, unread tracking, no requests), host ColumnConfig bounds, TabRole, deck shortcuts, capped unread in TabState; column boxes skip rail rules
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:55:57 +07:00
CRT
1486f31a66 host: place child webviews on Linux in a GtkOverlay over the shell
tauri 2 (runtime-wry build_gtk with the window's GtkBox) packs every child
webview into the window box, stacked and with set_position/set_size ignored,
so tab webviews landed under the shell instead of over their panes.
place_child/attach_shell move them into a GtkOverlay (shell as base layer)
and allocate each at its exact rect via get-child-position; placed_rect
reports the placement (GTK children have no position). Windows/macOS keep
set_position/set_size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:40:51 +07:00
CRT
edf5171435 host: low memory only for parked tabs, not for tabs hidden behind a shell overlay; skip unchanged targets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:42:34 +07:00
CRT
3ef4a27a6b host: hidden tab webviews use the WebView2 low memory target
set_tab_visible shows/hides a tab webview and sets
ICoreWebView2_19::SetMemoryUsageTargetLevel (LOW when hidden, NORMAL when
shown). Hidden tabs keep running; the browser trims their working set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:35:57 +07:00
CRT
b55b66c4ca Tabs: accept embedded favicons up to 64 KiB (a theme's replacement logo was dropped)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 04:14:25 +07:00
CRTkafa
b13d9acf14 host: read_json takes the runtime as impl Runtime so callers can name only T
read_json::<T>(app, name) did not compile with two explicit generic
parameters; the app's call sites name only the value type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 03:18:45 +07:00
CRTkafa
41267a22fd Extract the wayBetter framework core from wayBetter for X
Site-agnostic parts of the X app, behind a small site adapter:

- crates/waybetter-host: webview-label security policy and tab rect
  clamp, account profiles with native-owned accounts (AccountRules),
  plugin store with bundled-plugin upgrades (PluginHost), signed
  data-only hotfix channel, update scheduling and crash handshake, tab
  payloads/link routing/tab-chrome validation, and (feature "tauri")
  webview helpers and the Windows system menu.
- packages/runtime: DOM hub, request hub, plugin API core, OKLab theme
  color model, split-view rail hiding, link routing and account
  reporting, each configured by a SiteAdapter.
- packages/shell: address bar, pane, link-drop, profile, palette and
  update-chip models, and window controls with the Tauri API injected.

Tests moved/adapted from the app: 45 Rust (headless), 40 TypeScript.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 03:01:33 +07:00