Commit graph

30 commits

Author SHA1 Message Date
CRT
e3fe5820df host: no opener link handler in webviews (target=_blank clicks in site tabs were cancelled and never opened)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 06:11:38 +07:00
CRT
788be02a26 Native menu bar (macOS): the shell describes it (menu_set, shell-only), host validates and builds it, a chosen id goes back to the shell (native-menu); App/File/Edit/View/Account/Plugins/Window/Help from the in-app menu's state, words wb.menu.*
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 02:00:15 +07:00
CRT
82d333e350 macOS: the red close button sends the window to the Dock and the app keeps running (Dock click restores, Cmd+Q quits); a pane shown after being parked is shown before it is sized and told to re-lay out (X drew an empty timeline)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:52:24 +07:00
CRT
4d806b647d host: tab_find doc names the Custom Highlight fallback
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:19:12 +07:00
CRT
400f22c40c host: the drafts vault commands (draft_list/save/revisions/status, observation_list, data_export) are the host's (AppConfig::with_vault, commands::VAULT); the app takes the vault through vault_commands::lock
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:17:30 +07:00
CRT
5d19396d89 Store: Open With (files the OS hands over) and the icons and builtin catalog kinds
Open With: store_commands keeps each existing file the OS hands over (launch
argument, a second launch forwarded by single-instance with its working
directory, a macOS file:// open-URL) under a random id (Store::receive_file:
name says what it is, regular file, size cap, 8 kept, 15 minutes) and queues
LinkEvent.opened {id, name, kind}. The shell-only install_stage_opened {id}
reads that one file once and stages it exactly like a dropped file; it takes
an id only (testkit asserts that), so the shell cannot name a path. The shell
shows the same install screen; nothing installs without it. The NSIS template
lists the app under "Open with" for the declared extensions (OpenWithProgids)
and never makes it the default program, unlike Tauri's own macro.

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

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

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

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

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:29:35 +07:00
CRT
409949edb9 Host: the settings backup commands (collect, export, open, apply) are served for every app; SiteHooks::backup_drafts and backup_apply carry what only the app knows; commands::BACKUP, testkit::assert_backup_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:41:12 +07:00
CRT
198b1160f2 String packs: the site tab's delivery layer lives in app.rs, so a host without the net feature builds again (a generated app's cargo check failed on the net-gated store_commands module)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:40:25 +07:00
CRT
3402c1d85d Host: developer mode (dev_* commands and the watcher) is served for every app that sets AppConfig::with_dev; SiteHooks::dev_theme_apply and dev_theme_end carry the app's theme file; commands::DEV, testkit::assert_dev_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:32:54 +07:00
CRT
48ff509b15 Host: workspaces (always) and encrypted folder sync (AppConfig::with_sync) commands; SiteHooks::backup_sections and backup_section_names say what a backup and sync carry; commands::WORKSPACES, commands::SYNC, testkit::assert_sync_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:26:03 +07:00
CRT
e88339bda6 Host: native update commands (update_status, update_check, apply_update, app_ready) and the update loop are served for every app that sets AppConfig::with_updates; commands::UPDATE, testkit::assert_update_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:18:34 +07:00
CRT
825742b964 Repair: the Google Fonts host commands as 7f9e172 meant them (a staging race split them from 43fc8a1)
Two agents staged the same host files at once: 7f9e172 went in with the diagnostics' hunks of app.rs, commands.rs and lib.rs instead of its own, and 43fc8a1 then dropped fonts_commands.rs. This puts back exactly the Google Fonts wiring from the working tree (AppConfig::with_fonts, SiteHooks::fonts_changed, commands::GFONTS, the fonts handler, fonts_commands.rs); the diagnostics stay as committed. theme.rs, still in progress, is not included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:12:43 +07:00
CRT
7f9e172196 Host: Google Fonts commands (gfonts_*) are served for every app that sets AppConfig::with_fonts; SiteHooks::fonts_changed; commands::GFONTS, testkit::assert_fonts_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:11:46 +07:00
CRT
873f8561ca Host: the media panel's commands (gallery folders, previews, the clipboard history and its watcher) are served for every app that sets AppConfig::with_media; commands::MEDIA, media_commands::gallery_file/clip_image for an app's attach command, testkit::assert_media_guards
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 04:02:35 +07:00
CRT
419f60b5b1 Store kind "strings": string packs install like themes and plugins (dropped .strings.json, links, the signed Store), validated as plain data on this side (envelope, plain text, size caps, the app's own site), kept in app data strings/ with their order and which are on (at most 8); licence and credits are optional fields of every catalog item; five shell-only commands (strings_list, strings_read, strings_set, strings_remove, strings_apply); the shell's delivery (packs + the inventory slice) goes into site tabs' session storage by revision, as the first document-start layer of a site tab and never in safe mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:56:54 +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
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
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
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