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>
This commit is contained in:
CRTkafa 2026-09-28 03:01:33 +07:00
commit 41267a22fd
48 changed files with 9768 additions and 0 deletions

View file

@ -0,0 +1,227 @@
//! Tab payloads between the shell and native code, link routing and tab-chrome validation.
//! Everything a tab page sends is validated here before it reaches the shell.
use serde::{Deserialize, Serialize};
use url::Url;
/// Shell events emitted by the host.
pub const EVENT_TAB_STATE: &str = "tab-state";
pub const EVENT_TAB_OPEN: &str = "tab-open";
pub const EVENT_TAB_SHORTCUT: &str = "tab-shortcut";
pub const EVENT_LINK_DRAG: &str = "link-drag";
pub const EVENT_PROFILES_CHANGED: &str = "profiles-changed";
/// One pane as the shell lays it out (`tabs_sync`).
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TabLayout {
pub id: String,
/// Used only when the tab's webview does not exist yet.
pub url: String,
/// Account profile; fixed per webview (a change recreates the tab's webview).
#[serde(default = "default_profile")]
pub profile: String,
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
pub visible: bool,
/// Another site pane of the same account already shows the site's navigation rail.
#[serde(default)]
pub nav_redundant: bool,
}
fn default_profile() -> String {
crate::profiles::DEFAULT_PROFILE.into()
}
/// A tab's address-bar state for the shell (`tab-state`); absent fields are unchanged.
#[derive(Clone, Serialize, Default, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TabState {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub loading: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub favicon: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub focused: Option<bool>,
}
impl TabState {
/// What a tab reported about itself, bounded. The URL is never the page's claim: callers pass
/// the webview's own URL (and only when the page reported a change).
pub fn reported(id: String, url: Option<String>, title: Option<String>, favicon: Option<String>, focused: Option<bool>) -> Self {
let clip = |value: Option<String>, max: usize| value.filter(|v| v.len() <= max);
Self {
id,
url,
title: clip(title, 512),
favicon: clip(favicon, 2048).filter(|href| href.starts_with("https://") || href.starts_with("data:image/")),
focused,
loading: None,
}
}
}
/// A new-window request from a tab that the shell opens as a tab or a pane (`tab-open`).
#[derive(Clone, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OpenRequest {
pub url: String,
pub from: Option<String>,
pub background: bool,
/// Open in a pane beside `from` (the "pane" link setting) instead of a tab in its group.
pub beside: bool,
}
/// Only plain http(s) URLs without credentials open in tabs.
pub fn parse_web_url(value: &str) -> Result<Url, String> {
let url = Url::parse(value).map_err(|_| "VALIDATION: invalid URL")?;
if !matches!(url.scheme(), "http" | "https") || !url.username().is_empty() || url.password().is_some() {
return Err("VALIDATION: only plain http(s) URLs open in tabs".into());
}
Ok(url)
}
/// Where links to other sites open: beside the pane, in a tab or in the system browser.
pub fn valid_external_mode(mode: &str) -> bool {
matches!(mode, "pane" | "tab" | "browser")
}
/// Where clicked site pages open: "same" keeps the site's own in-place navigation.
pub fn valid_site_mode(mode: &str) -> bool {
matches!(mode, "same" | "pane" | "tab" | "browser")
}
/// Where a new-window request (target=_blank, window.open) opens. "same" only concerns ordinary
/// clicks, so an explicit new window on the site still gets a tab.
pub fn new_window_mode<'a>(on_site: bool, site_mode: &'a str, external_mode: &'a str) -> &'a str {
if on_site {
match site_mode {
"same" => "tab",
mode => mode,
}
} else {
external_mode
}
}
/// Browser shortcuts a tab may forward to the shell while it has keyboard focus.
pub const SHORTCUTS: [&str; 15] = [
"command-palette", "new-tab", "close-tab", "reopen-tab", "focus-address", "next-tab", "prev-tab", "reload",
"back", "forward", "split-right", "split-down", "open-panel", "find", "hard-reload",
];
pub fn valid_shortcut(shortcut: &str) -> bool {
SHORTCUTS.contains(&shortcut)
}
/// `tab_link_drag`: `start` carries the dragged link (validated here, bounded), `enter`/`end` none.
pub fn link_drag(phase: &str, url: Option<String>) -> Result<Option<String>, String> {
if !matches!(phase, "start" | "enter" | "end") {
return Err("VALIDATION: unknown drag phase".into());
}
match (phase, url) {
("start", Some(url)) if url.len() <= 4096 => Ok(Some(parse_web_url(&url)?.to_string())),
("start", _) => Err("VALIDATION: a link drag needs an http(s) URL".into()),
_ => Ok(None),
}
}
/// `back | forward | reload | stop | focus`, the tab actions the shell may request.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TabAction {
Back,
Forward,
Reload,
Stop,
Focus,
}
impl TabAction {
pub fn parse(action: &str) -> Result<Self, String> {
Ok(match action {
"back" => Self::Back,
"forward" => Self::Forward,
"reload" => Self::Reload,
"stop" => Self::Stop,
"focus" => Self::Focus,
_ => return Err("VALIDATION: unknown tab action".into()),
})
}
}
/// Hands a JSON value to a member of the page runtime: `window.<global>.<member>.<method>(value)`
/// when the runtime exists, else parked in `window.<queue>` for the runtime to pick up at start.
/// `json` must be serialized JSON (never page-supplied text).
pub fn deliver_script(global: &str, member: &str, method: &str, queue: &str, json: &str) -> String {
format!("(()=>{{const v={json};const R=window.{global};if(R&&R.{member})R.{member}.{method}(v);else window.{queue}=v;}})();")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_plain_web_urls_open() {
assert!(parse_web_url("https://example.com/a?b").is_ok());
for bad in ["javascript:alert(1)", "file:///c:/x", "https://u:p@example.com/", "about:blank", "nope"] {
assert!(parse_web_url(bad).is_err(), "{bad}");
}
}
#[test]
fn link_modes_route_new_windows() {
assert!(valid_external_mode("pane") && !valid_external_mode("same"));
assert!(valid_site_mode("same") && !valid_site_mode("popup"));
assert_eq!(new_window_mode(true, "same", "browser"), "tab");
assert_eq!(new_window_mode(true, "pane", "browser"), "pane");
assert_eq!(new_window_mode(false, "same", "browser"), "browser");
}
#[test]
fn tab_chrome_input_is_validated() {
assert!(valid_shortcut("new-tab") && !valid_shortcut("eval"));
assert_eq!(link_drag("start", Some("https://a.test/x".into())).unwrap().as_deref(), Some("https://a.test/x"));
assert!(link_drag("start", None).is_err());
assert!(link_drag("start", Some(format!("https://a.test/{}", "a".repeat(4096)))).is_err());
assert!(link_drag("start", Some("javascript:x".into())).is_err());
assert_eq!(link_drag("end", Some("https://a.test/".into())).unwrap(), None);
assert!(link_drag("drop", None).is_err());
assert_eq!(TabAction::parse("reload").unwrap(), TabAction::Reload);
assert!(TabAction::parse("close").is_err());
}
#[test]
fn reports_are_bounded() {
let state = TabState::reported(
"a".into(),
None,
Some("t".repeat(513)),
Some("javascript:alert(1)".into()),
Some(true),
);
assert_eq!((state.title, state.favicon, state.focused), (None, None, Some(true)));
let state = TabState::reported("a".into(), None, Some("Home".into()), Some("data:image/png;base64,AA".into()), None);
assert_eq!(state.favicon.as_deref(), Some("data:image/png;base64,AA"));
}
#[test]
fn layouts_default_to_the_default_profile() {
let layout: TabLayout = serde_json::from_str(r#"{"id":"a","url":"https://a.test/","x":0,"y":0,"width":1,"height":1,"visible":true}"#).unwrap();
assert_eq!((layout.profile.as_str(), layout.nav_redundant), ("default", false));
}
#[test]
fn delivery_parks_values_until_the_runtime_exists() {
assert_eq!(
deliver_script("Demo", "pane", "set", "__demoPane", r#"{"redundantNav":true}"#),
r#"(()=>{const v={"redundantNav":true};const R=window.Demo;if(R&&R.pane)R.pane.set(v);else window.__demoPane=v;})();"#
);
}
}