Commit graph

51 commits

Author SHA1 Message Date
CRT
e831d7a415 Theme engine v3 step 1: themeInvariants, the numeric invariant probe (I1-I8, I10)
A pure measurement over a rendered document (fixtures and live): text and icon contrast over the
painted backdrop, X colour leakage, truncated bar/nav/button labels, bar geometry and hit tests, X line
icons under a pack, empty and nested frames, popover bounds, target sizes. Grew out of theme-qa/probe.js.
2026-10-02 05:00:02 +07:00
CRT
038b08b2d1 Theme engine v3 step 4: inkOn, the contrast solver
themeInk.ts: OKLCH lightness search toward the pole with headroom, hue kept, chroma
cut only for the gamut; several fills (gradient ends, glass grounds); compositing.
reach() now runs on it (8% headroom when a colour has to move, for the site's tints
mixed from these surfaces). Property test: 5,000 random pairs reach 4.5:1 and 3:1;
the live 1.0.2 cases ("New" pill 1.19:1, HoverLabel 2.7:1, like pink 3.84:1) pass.
2026-10-02 04:56:04 +07:00
CRT
c939fd2bc5 Icon names for X's newer rail items: history, studio, articles 2026-10-02 04:09:11 +07:00
CRT
034a2719b7 Theme icons: pixel-art glyphs render with crisp edges
A glyph made only of axis-aligned rectangles (M, h, v, z) is pixel art: its SVG gets
shape-rendering="crispEdges", so a 16-cell glyph stays sharp at sizes that are not a whole multiple
of its grid (X's rail draws 26px icons, the action bar 18.75px).
2026-10-01 16:59:03 +07:00
CRT
13ac2827fe 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.
2026-10-01 15:52:56 +07:00
CRT
22544b8d91 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.
2026-10-01 14:26:29 +07:00
CRT
4a2e3850df 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.
2026-10-01 14:19:36 +07:00
CRT
8a2df3d17d 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.
2026-10-01 10:42:45 +07:00
CRT
b3f160e4c1 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.
2026-10-01 05:34:23 +07:00
CRT
8bbdbecf0d 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.
2026-10-01 05:20:57 +07:00
CRT
2c12aa01fd 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.
2026-10-01 05:02:01 +07:00
CRT
ab6afebae4 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.
2026-10-01 04:56:33 +07:00
CRT
d6f88a4701 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.
2026-10-01 04:48:26 +07:00
CRT
497422788b 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 2026-10-01 04:25:04 +07:00
CRT
610ee5f48d 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.
2026-10-01 04:19:55 +07:00
CRT
7620c26070 App menu and mode words in every catalog: find in page, update check, safe mode, report a problem, Custom/Re-apply
menuMessages.en.json stays the English source of the menu's words; en.json now carries them too
(the i18n test takes them into the source union) and 48 catalogs translate all 51 menu words and
the seven new mode words (plural forms per language for the match count).
2026-10-01 03:50:45 +07:00
CRT
d37ccb71e3 Plugin API: a style injected at document start waits for the document root
WebView2 runs document-start scripts before the document has an element, so
injectCSS threw ("reading 'appendChild'") for every CSS feature that was on at
start, e.g. hiding promoted posts, until the next flags delivery restarted it.
Such styles now wait (one MutationObserver for all of them), are attached as
soon as the document has a root, and get their CSP check then; a style cleared
while waiting is never attached.
2026-10-01 03:39:16 +07:00
CRT
7d30df6aaa 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.
2026-10-01 03:33:41 +07:00
CRT
57524ae2ff String packs: a page gets only the slice of the inventory its packs' keys need (sliceInventory, isPageInventory); a slice does not call other keys new 2026-10-01 03:30:21 +07:00
CRT
e5c60ff87d 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
a8d4fc9d55 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
363a122da2 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
db3fbc8ece 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
f0410567aa 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
6e80b3e2c9 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
1789e07d7a 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
6d3f91bf2e 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
bd6b4860d7 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
d8619c7482 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
f31b00eec7 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
31dabca439 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
a71505d5c0 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
290123f0b4 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
6ef78c0c30 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
fd99901bb2 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
cc5a351fe9 locales: the shell frame's words (wb.*) and the install screen's source line, 51 languages
- 245 wb.* messages for the shell frame (tabs, address bar, new-tab page, palette, account
  switcher, decks, workspaces, chips, media panel, pane themes, backup preview), moved with every
  translation from wayBetter for X's catalogs; `{site}` stands where the English said "X" (10
  keys; the app passes "X", so X reads the same). English source: shell/src/frame/frameMessages.ts.
- wb.install.source.store|link|file: where an install comes from, translated in every locale
  (native code's label is English only).
- runtime/tests/i18n.test.ts: en.json is the kit, shell-model, store and shell-frame source.
2026-09-29 12:46:57 +07:00
CRT
f1588c3acd shell: the Store view and install screen as framework React components, strings in the framework catalogs
Moved from wayBetter for X so every wayBetter app gets the Store UI; the app passes its native
bridge, theme adapter, translator and (optionally) its own controls and button classes.

- storeModel.ts: wire types of the native store commands (InstallPreview, StoreCatalogView,
  LinkEvent, StoreItem with `apps`), permission badges in the site's words, categories, item state
  (install/installed/update/changed locally/built in), search, free theme ids, versions, previews.
- installController.ts: the install screen's state machine (links incl. those queued before the
  shell listened, dropped files, store items, cancel, confirm), generic over the app's theme format
  (`ThemeAdapter`: parse, ids, facts, save) and its bridge (`StoreBackend`: invoke/listen).
- InstallDialog.tsx (`InstallHost`): the screen (Install enabled after 1 s, Enter never installs,
  tabs hidden via setOverlay) and the window-wide drop target. StoreView.tsx: search, kinds,
  categories, cards, permission badges, install/update/remove; default controls or the app's.
- store.css: both, themed with framework `--wb-*` tokens (documented; dark defaults).
- storeMessages.ts + every framework catalog: 120 `wb.store.*`, `wb.install.*`, `wb.permission.*`
  messages moved from the app catalogs with all existing translations; the six that named X take
  `{site}`; generic words the components need copied with their translations.
- npm package: exports for the new modules and store.css, optional react/react-dom peers; @types
  for the typecheck. Tests: models, controller flows with a fake bridge, every looked-up key exists.
2026-09-29 11:08:04 +07:00
CRT
b2744c1a45 pageLang: report X's language even when installed before <html> exists or before the IPC bridge
At document start (where the app installs the X runtime) there is no <html> element yet, so
the lang report never ran and "Follow X" never learned X's language (found live: siteLang
stayed null). It now reads lang again at readystatechange/DOMContentLoaded, and a report
that fails (no bridge yet) is retried with a bounded backoff. Tests.
2026-09-29 11:01:53 +07:00
CRT
c686c376ab 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.
2026-09-29 09:53:49 +07:00
CRT
2b8161fedc 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.
2026-09-29 09:53:48 +07:00
CRT
c404dd3dd0 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.
2026-09-29 04:09:14 +07:00
CRT
0c1ea611e7 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) 2026-09-29 01:35:44 +07:00
CRT
26684e18d7 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).
2026-09-29 01:08:52 +07:00
CRT
b6dd47e0d5 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 2026-09-28 23:56:37 +07:00
CRT
434b187638 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 2026-09-28 23:42:21 +07:00
CRT
376e74884e 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 2026-09-28 22:55:57 +07:00
CRT
f6d52512a0 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.
2026-09-28 21:47:09 +07:00
CRT
e5c91a1563 npm: publish as one package waybetter (waybetter/runtime/*, waybetter/shell/*); @waybetter scope is taken 2026-09-28 05:54:03 +07:00
CRT
faf4e5909e README: projects using wayBetter (wayBetter for X first) 2026-09-28 05:46:46 +07:00
CRT
96512c7bb0 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.
2026-09-28 05:41:01 +07:00