Commit graph

76 commits

Author SHA1 Message Date
CRT
d36e2d8709 i18n: framework catalogs for the 51 languages X offers, and tests
Kit and shell-model words (18 keys) per locale, machine-drafted except English. Tests cover
locale normalization, fallback chains, direction, ICU-lite formatting and quoting, the runtime,
catalog rules (plural-only arguments, missing forms, allowEmpty), plugin strings, and that every
framework catalog passes the checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:53:49 +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
2d4831da0a runtime: styles for created roots are constructed sheets (CSP-proof)
- styles.ts: adoptStyles(root, css) adopts a constructed stylesheet into a shadow root or document
  (not governed by a page's CSP, parsed once, stays with the root when its host is moved);
  <style> fallback where constructable sheets are missing or refused.
- UI kit: the kit's shadow root and each dialog's `css` use it (dialog CSS goes with the dialog).
- Plugin API: api.adoptCSS(root, css) and scope.adoptCSS (removed on dispose); injectCSS keeps its
  <style> and, when a CSP leaves it without a sheet, applies the same rules as an adopted sheet.
- tests/styles.test.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 04:09:14 +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
0344c55fa8 runtime: font roles in the theme API (fonts per role, --wb-font-<role> vars, UI kit uses the dialog/mono roles); theme schema: font.roles with Google Fonts source
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:56:37 +07:00
CRT
abdc29ba55 runtime column: set the column attributes on scan (config delivered at document start has no <html> yet), no scrollbar in columns, unread only above both the read items and what is on screen, a hidden 1 px webview counts as off screen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:42:21 +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
3ae7744387 runtime: plugin UI kit (api.ui: toast, alert, confirm, prompt, dialog, modal) and theme API (api.theme, --wb-* vars); theme JSON Schema; waybetter 0.2.0
The kit draws in one shadow DOM host in the active theme's colors, corners and font and repaints
live; accessible dialogs (focus trap and return, Esc/Enter rules, alertdialog for alerts and
confirms), stacking with inert, toasts with actions, reduced motion, text-only content (ui.h),
and scope.ui closes a plugin's dialogs on dispose. api.toast routes to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:47:09 +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
99aa9bebf6 One system for every app: WhatsApp/Discord-style targets run in the same Tauri shell; site is waybetter.fyi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:40:57 +07:00
CRT
4cf85f6bde Public: README links npm and the site; package points at git.crt.fyi (0.1.1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:12:55 +07:00
CRT
d0cb4a2c06 npm: publish as one package waybetter (waybetter/runtime/*, waybetter/shell/*); @waybetter scope is taken
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 05:54:03 +07:00
CRT
9fa94efe4f README: projects using wayBetter (wayBetter for X first)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 05:46:46 +07:00
CRT
7cd8005027 packages: make runtime and shell publishable to npm
Drop private from @waybetter/runtime and @waybetter/shell (root stays
private), add per-module exports with types/import conditions backed by
a tsc build (rewriteRelativeImportExtensions rewrites .ts specifiers to
.js in dist), files/sideEffects/engines/keywords/publishConfig, a
prepublishOnly that builds and tests, and a README + copied LICENSE per
package. Root gains build:packages and pack:check scripts. dist/ is
gitignored; the desktop app keeps importing raw src/*.ts by relative
path, unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 05:41:01 +07:00
CRT
be27715374 README: web and desktop (Electron/webview app) targets share the runtime
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 04:17: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