waybetter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +71 -0
  3. package/dist/runtime/accountReport.d.ts +20 -0
  4. package/dist/runtime/accountReport.js +26 -0
  5. package/dist/runtime/accountReport.js.map +1 -0
  6. package/dist/runtime/domHub.d.ts +37 -0
  7. package/dist/runtime/domHub.js +134 -0
  8. package/dist/runtime/domHub.js.map +1 -0
  9. package/dist/runtime/linkRouting.d.ts +13 -0
  10. package/dist/runtime/linkRouting.js +35 -0
  11. package/dist/runtime/linkRouting.js.map +1 -0
  12. package/dist/runtime/netHub.d.ts +55 -0
  13. package/dist/runtime/netHub.js +268 -0
  14. package/dist/runtime/netHub.js.map +1 -0
  15. package/dist/runtime/paneNav.d.ts +35 -0
  16. package/dist/runtime/paneNav.js +311 -0
  17. package/dist/runtime/paneNav.js.map +1 -0
  18. package/dist/runtime/pluginApi.d.ts +59 -0
  19. package/dist/runtime/pluginApi.js +279 -0
  20. package/dist/runtime/pluginApi.js.map +1 -0
  21. package/dist/runtime/site.d.ts +32 -0
  22. package/dist/runtime/site.js +20 -0
  23. package/dist/runtime/site.js.map +1 -0
  24. package/dist/runtime/themeTokens.d.ts +64 -0
  25. package/dist/runtime/themeTokens.js +175 -0
  26. package/dist/runtime/themeTokens.js.map +1 -0
  27. package/dist/shell/addressModel.d.ts +34 -0
  28. package/dist/shell/addressModel.js +66 -0
  29. package/dist/shell/addressModel.js.map +1 -0
  30. package/dist/shell/chrome.d.ts +3 -0
  31. package/dist/shell/chrome.js +6 -0
  32. package/dist/shell/chrome.js.map +1 -0
  33. package/dist/shell/linkDrop.d.ts +29 -0
  34. package/dist/shell/linkDrop.js +57 -0
  35. package/dist/shell/linkDrop.js.map +1 -0
  36. package/dist/shell/palette.d.ts +13 -0
  37. package/dist/shell/palette.js +51 -0
  38. package/dist/shell/palette.js.map +1 -0
  39. package/dist/shell/paneModel.d.ts +33 -0
  40. package/dist/shell/paneModel.js +56 -0
  41. package/dist/shell/paneModel.js.map +1 -0
  42. package/dist/shell/profileModel.d.ts +52 -0
  43. package/dist/shell/profileModel.js +91 -0
  44. package/dist/shell/profileModel.js.map +1 -0
  45. package/dist/shell/updateModel.d.ts +35 -0
  46. package/dist/shell/updateModel.js +32 -0
  47. package/dist/shell/updateModel.js.map +1 -0
  48. package/dist/shell/windowControls.d.ts +24 -0
  49. package/dist/shell/windowControls.js +22 -0
  50. package/dist/shell/windowControls.js.map +1 -0
  51. package/package.json +112 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paneNav.js","sourceRoot":"","sources":["../../../runtime/src/paneNav.ts"],"names":[],"mappings":"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","sourcesContent":["// Split view: site panes of one account side by side each draw the site's whole navigation rail.\n// Native code tells every site pane whether another pane already shows it (`redundantNav`, the\n// leftmost pane of that account wins). With the feature on, such a pane lifts the rail out of the\n// layout (the content re-centers in the freed width), parks it off-screen, and slides it back in\n// over the page while the pointer rests on the pane's left edge. Which element is the rail is the\n// site adapter's `RailSite`.\n\nimport { watchDom, type DomWatchHandle } from './domHub.ts';\n\nexport type PaneNavHandle = { set(state: unknown): void; setEnabled(enabled: boolean): void; dispose(): void };\nexport type RailPaint = { width: number; background: string; line: string; accent: string; rtl: boolean };\n\nexport type RailSite = {\n /** Prefix of the ids/attributes this module owns (`<prefix>-pane-nav`, `data-<prefix>-pane-nav`, ...). */\n prefix: string;\n /** The site's classic rail element. */\n rail: string;\n /** A link inside the rail, used to find it when the classic rail element is absent. */\n anchor: string;\n /** The content column that re-centers once the rail is parked. */\n main: string;\n /** An element whose background is the site accent (for the edge handle), if any. */\n accentSource?: string;\n /** Open while a modal sheet is up: the rail never slides in over it. */\n modalOpen: string;\n /** One of the site's menus (portalled out of the rail) is open. */\n menuOpen: string;\n /** Paint before the page has been read. */\n paint: RailPaint;\n};\n\n/** Resting on the edge this long slides the rail in; crossing it on the way to the divider does not. */\nconst PEEK_DELAY = 140;\n/** The rail stays this long after the pointer leaves it (and while one of its menus is open). */\nconst LEAVE_DELAY = 280;\n\nconst names = (site: RailSite) => ({\n style: `${site.prefix}-pane-nav`,\n edge: `${site.prefix}-rail-edge`,\n /** On <html>: \"hidden\" (rail parked off-screen) or \"peek\" (rail slid in over the page). */\n state: `data-${site.prefix}-pane-nav`,\n /** Marks the rail when the site draws it as something other than its classic rail element. */\n rail: `data-${site.prefix}-rail`,\n});\n\n/** Pure: the stylesheet for a pane whose rail is parked. RTL pages keep the rail on the right. */\nexport function buildPaneNavCss(site: RailSite, paint: RailPaint = site.paint): string {\n const { edge, state, rail } = names(site);\n const RAIL = `:is(${site.rail}, [${rail}])`;\n const ease = 'cubic-bezier(.2, .8, .2, 1)';\n const [side, other, sign] = paint.rtl ? ['right', 'left', 1] : ['left', 'right', -1];\n return `\nhtml[${state}] ${RAIL} {\n position: fixed !important; top: 0 !important; bottom: 0 !important; ${side}: 0 !important; ${other}: auto !important;\n width: ${paint.width}px !important; min-width: 0 !important; max-width: none !important; height: 100vh !important;\n flex: none !important; margin: 0 !important; overflow: visible !important; z-index: 40 !important;\n background-color: ${paint.background} !important;\n transform: translateX(${sign * 100}%); visibility: hidden;\n transition: transform .2s ${ease}, box-shadow .2s ease, visibility 0s linear .2s;\n}\nhtml[${state}=\"peek\"] ${RAIL} {\n transform: translateX(0); visibility: visible;\n box-shadow: ${-sign}px 0 0 ${paint.line}, ${-sign * 16}px 0 40px rgba(0, 0, 0, .3);\n transition: transform .2s ${ease}, box-shadow .2s ease;\n}\nhtml[${state}] ${RAIL} ~ ${site.main} { align-items: center !important; }\n#${edge} { position: fixed; ${side}: 0; top: 0; bottom: 0; width: 10px; z-index: 41; }\n#${edge}::after {\n content: \"\"; position: absolute; ${side}: 0; top: 50%; width: 3px; height: 56px; margin-top: -28px;\n border-radius: ${paint.rtl ? '3px 0 0 3px' : '0 3px 3px 0'}; background: ${paint.accent}; opacity: 0; transform: scaleY(.5);\n transition: opacity .15s ease, transform .15s ease;\n}\n#${edge}:hover::after { opacity: .9; transform: scaleY(1); }\nhtml[${state}=\"peek\"] #${edge} { pointer-events: none; }\n@media (prefers-reduced-motion: reduce) {\n html[${state}] ${RAIL}, #${edge}::after { transition: none !important; }\n}`.trim();\n}\n\nconst TRANSPARENT = /^(transparent|rgba\\([^)]*,\\s*0\\))$/;\nconst isTransparent = (color: string | null | undefined) => !color || TRANSPARENT.test(color.replace(/\\s+/g, ' ').trim());\n\n/** `rgb(r, g, b)` / `rgba(...)` at a new alpha; anything else falls back to a neutral line. */\nexport function withAlpha(color: string, alpha: number): string {\n const parts = color.match(/\\d+(?:\\.\\d+)?/g);\n return parts && parts.length >= 3 ? `rgba(${parts[0]}, ${parts[1]}, ${parts[2]}, ${alpha})` : `rgba(128, 128, 128, ${alpha})`;\n}\n\nexport function installPaneNav(win: Window, site: RailSite): PaneNavHandle {\n const doc = win.document;\n const ids = names(site);\n // Installed at document start, before the parser has created <html>: never cache it.\n const root = (): HTMLElement | null => doc.documentElement;\n let redundant = false;\n let enabled = true;\n let active = false;\n let disposed = false;\n let rail: HTMLElement | null = null;\n let edge: HTMLElement | null = null;\n let watch: DomWatchHandle | null = null;\n let paint: RailPaint = { ...site.paint };\n let peekTimer = 0;\n let leaveTimer = 0;\n let resizeFrame = 0;\n /** A press inside the rail may have opened one of its menus (portalled elsewhere). */\n let railMenu = false;\n const isRtl = () => root()?.getAttribute('dir') === 'rtl';\n\n const ensureStyle = () => {\n let style = doc.getElementById(ids.style) as HTMLStyleElement | null;\n if (!style) {\n const parent = doc.head || root();\n if (!parent) return;\n style = doc.createElement('style');\n style.id = ids.style;\n parent.appendChild(style);\n }\n const css = buildPaneNavCss(site, paint);\n if (style.textContent !== css) style.textContent = css;\n };\n\n /** The site's rail: its classic element, else the sibling of <main> holding the anchor link. */\n const findRail = (): HTMLElement | null => {\n const classic = doc.querySelector<HTMLElement>(site.rail);\n if (classic) return classic;\n if (rail?.isConnected) return rail;\n const main = doc.querySelector('main');\n const anchor = doc.querySelector(site.anchor);\n if (!main || !anchor) return null;\n for (let el = anchor.parentElement; el && el !== doc.body; el = el.parentElement) {\n const parent = el.parentElement;\n if (!parent?.contains(main) || el.contains(main)) continue;\n // Only a tall column at the start edge: never a bottom tab bar or a page-wide wrapper.\n const box = el.getBoundingClientRect();\n const tall = box.height >= win.innerHeight * 0.5;\n const atStart = isRtl() ? box.right >= win.innerWidth - 8 : box.left <= 8;\n return tall && atStart && box.width <= win.innerWidth * 0.45 ? el : null;\n }\n return null;\n };\n\n const railWidth = (el: HTMLElement): number => {\n const column = el.firstElementChild?.getBoundingClientRect().width ?? 0;\n const nav = el.querySelector('nav')?.getBoundingClientRect().width ?? 0;\n const width = Math.round(Math.max(column, nav));\n return width >= 48 && width <= 400 ? width : paint.width;\n };\n\n /** Reads the page colors for the slid-in rail (layout reads: only on state changes, never per mutation). */\n const repaint = () => {\n try {\n const body = doc.body ? win.getComputedStyle(doc.body) : null;\n const html = root() ? win.getComputedStyle(root()!) : null;\n let background = body?.backgroundColor ?? '';\n if (isTransparent(background)) background = html?.backgroundColor ?? '';\n if (isTransparent(background)) background = /dark/.test(html?.colorScheme ?? '') ? 'rgb(0, 0, 0)' : 'rgb(255, 255, 255)';\n const button = site.accentSource ? doc.querySelector(site.accentSource) : null;\n const fill = button ? win.getComputedStyle(button).backgroundColor : '';\n paint = {\n width: rail ? railWidth(rail) : paint.width,\n background,\n line: withAlpha(body?.color ?? '', 0.16),\n accent: isTransparent(fill) ? site.paint.accent : fill,\n rtl: isRtl(),\n };\n } catch { /* keep the last paint */ }\n ensureStyle();\n };\n\n const state = () => root()?.getAttribute(ids.state) ?? null;\n const setState = (value: 'hidden' | 'peek') => { if (state() !== value) root()?.setAttribute(ids.state, value); };\n const menuOpen = () => !!doc.querySelector(site.menuOpen);\n\n const unpeek = () => {\n win.clearTimeout(peekTimer);\n win.clearTimeout(leaveTimer);\n railMenu = false;\n if (active) setState('hidden');\n };\n const tryLeave = () => {\n if (state() !== 'peek') return;\n // Only the rail's own menus hold it out; sites portal them like every other menu, so \"open\" is\n // judged only after a press inside the rail and ends when that menu closes.\n const holding = railMenu && menuOpen();\n if (!holding) railMenu = false;\n if (rail?.matches(':hover') || holding) { leaveTimer = win.setTimeout(tryLeave, LEAVE_DELAY); return; }\n unpeek();\n };\n const peek = () => {\n win.clearTimeout(peekTimer);\n // Never over one of the site's modal sheets: the rail would sit above them.\n if (!active || !rail || doc.querySelector(site.modalOpen)) return;\n repaint();\n setState('peek');\n win.clearTimeout(leaveTimer);\n leaveTimer = win.setTimeout(tryLeave, LEAVE_DELAY * 4);\n };\n\n const onEdgeEnter = () => { win.clearTimeout(peekTimer); peekTimer = win.setTimeout(peek, PEEK_DELAY); };\n const onEdgeLeave = () => { if (state() !== 'peek') win.clearTimeout(peekTimer); };\n const onRailEnter = () => win.clearTimeout(leaveTimer);\n const onRailPress = () => { railMenu = true; };\n const onRailLeave = () => { win.clearTimeout(leaveTimer); leaveTimer = win.setTimeout(tryLeave, LEAVE_DELAY); };\n // A nav link navigates or opens a sheet: get out of the way.\n const onRailClick = (event: Event) => {\n if ((event.target as Element | null)?.closest?.('a[href]')) win.setTimeout(unpeek, 0);\n };\n const onKey = (event: KeyboardEvent) => { if (event.key === 'Escape' && state() === 'peek') unpeek(); };\n const onResize = () => {\n if (resizeFrame) return;\n resizeFrame = win.requestAnimationFrame(() => {\n resizeFrame = 0;\n if (active && rail) { paint = { ...paint, width: railWidth(rail) }; ensureStyle(); }\n });\n };\n\n const bindRail = (next: HTMLElement | null) => {\n if (next === rail) return;\n if (rail) {\n rail.removeAttribute(ids.rail);\n rail.removeEventListener('pointerenter', onRailEnter);\n rail.removeEventListener('pointerleave', onRailLeave);\n rail.removeEventListener('pointerdown', onRailPress, true);\n rail.removeEventListener('click', onRailClick, true);\n }\n rail = next;\n if (!rail) return;\n if (!rail.matches(site.rail)) rail.setAttribute(ids.rail, '');\n rail.addEventListener('pointerenter', onRailEnter);\n rail.addEventListener('pointerleave', onRailLeave);\n rail.addEventListener('pointerdown', onRailPress, true);\n rail.addEventListener('click', onRailClick, true);\n paint = { ...paint, width: railWidth(rail), rtl: isRtl() };\n ensureStyle();\n };\n\n const syncEdge = () => {\n if (!doc.body) return;\n if (edge?.isConnected) return;\n edge = doc.createElement('div');\n edge.id = ids.edge;\n edge.setAttribute('aria-hidden', 'true');\n edge.addEventListener('pointerenter', onEdgeEnter);\n edge.addEventListener('pointerleave', onEdgeLeave);\n edge.addEventListener('click', peek);\n doc.body.appendChild(edge);\n };\n\n // Sites re-render their layout on route and breakpoint changes: keep the rail marked and the edge in place.\n const sync = () => {\n if (!active || disposed) return;\n // The first run after <html> exists parks the rail (activation may predate the document).\n ensureStyle();\n if (state() !== 'peek') setState('hidden');\n bindRail(findRail());\n syncEdge();\n // The page direction can switch with the display language, without a reload.\n if (paint.rtl !== isRtl()) { paint = { ...paint, rtl: isRtl() }; ensureStyle(); }\n };\n\n const activate = () => {\n // The rail's width is measured while it is still in the layout, before it is parked.\n if (doc.body) bindRail(findRail());\n ensureStyle();\n setState('hidden');\n win.addEventListener('keydown', onKey, true);\n win.addEventListener('resize', onResize);\n watch = watchDom(win as unknown as Parameters<typeof watchDom>[0], sync);\n watch.schedule();\n };\n\n const deactivate = () => {\n win.clearTimeout(peekTimer);\n win.clearTimeout(leaveTimer);\n railMenu = false;\n if (resizeFrame) win.cancelAnimationFrame(resizeFrame);\n resizeFrame = 0;\n watch?.dispose();\n watch = null;\n win.removeEventListener('keydown', onKey, true);\n win.removeEventListener('resize', onResize);\n root()?.removeAttribute(ids.state);\n bindRail(null);\n edge?.remove();\n edge = null;\n doc.getElementById(ids.style)?.remove();\n };\n\n const apply = () => {\n const next = enabled && redundant && !disposed;\n if (next === active) return;\n active = next;\n if (active) activate();\n else deactivate();\n };\n\n return {\n set(value) {\n const next = (value as { redundantNav?: unknown } | null)?.redundantNav;\n if (typeof next !== 'boolean') return;\n redundant = next;\n apply();\n },\n setEnabled(value) {\n enabled = value;\n apply();\n },\n dispose() {\n disposed = true;\n apply();\n },\n };\n}\n"]}
@@ -0,0 +1,59 @@
1
+ export type Dispose = () => void;
2
+ export type PluginStorage = {
3
+ get<T>(key: string, fallback: T): T;
4
+ /** Returns false when the value could not be stored (quota, storage disabled). */
5
+ set(key: string, value: unknown): boolean;
6
+ remove(key: string): void;
7
+ keys(): string[];
8
+ };
9
+ /** Collects disposers so a plugin's `stop()` is a single `scope.dispose()`. */
10
+ export type PluginScope = {
11
+ css(id: string, css: string): void;
12
+ on(target: EventTarget, type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
13
+ observe(selector: string, callback: (element: Element) => void): void;
14
+ onRouteChange(callback: (href: string, previous: string) => void): void;
15
+ onKey(callback: (event: KeyboardEvent, item: Element | null) => void): void;
16
+ /** Tracks a node the plugin inserted; it is removed on dispose. */
17
+ add<T extends Node>(node: T): T;
18
+ /** Runs `fn` on dispose (restore attributes, clear timers, ...). */
19
+ defer(fn: Dispose): void;
20
+ dispose(): void;
21
+ };
22
+ export type CorePluginApi = {
23
+ injectCSS(id: string, css: string): void;
24
+ clearCSS(id: string): void;
25
+ waitFor(selector: string, timeoutMs?: number): Promise<Element>;
26
+ /** Namespaced JSON storage in this tab's localStorage (`<storagePrefix><namespace>:<key>`). */
27
+ storage(namespace: string): PluginStorage;
28
+ /** Called after the page changes route (pushState/replaceState/back/forward). */
29
+ onRouteChange(callback: (href: string, previous: string) => void): Dispose;
30
+ /** Calls `callback` once for every element matching `selector`, now and as the page renders more. */
31
+ observe(selector: string, callback: (element: Element) => void, root?: ParentNode): Dispose;
32
+ /**
33
+ * Single-key shortcuts: `callback(event, focusedItem)` runs only when the user is not typing, no
34
+ * Ctrl/Meta/Alt is held and the key is not the second half of one of the site's chords.
35
+ */
36
+ onKey(callback: (event: KeyboardEvent, item: Element | null) => void): Dispose;
37
+ copyText(text: string): Promise<boolean>;
38
+ toast(message: string, ms?: number): void;
39
+ scope(): PluginScope;
40
+ };
41
+ export type PluginApiSite = {
42
+ /** The runtime global, for error messages (`<name>.waitFor timed out`). */
43
+ name: string;
44
+ /** Console prefix, e.g. "wayBetter for X". */
45
+ product: string;
46
+ /** localStorage key prefix, e.g. `bt-plugin:`. */
47
+ storagePrefix: string;
48
+ /** `data-*` key (camelCase) marking plugin `<style>` elements with their id. */
49
+ styleDataKey: string;
50
+ toastId: string;
51
+ toastBackground: string;
52
+ /** The item a single-key shortcut acts on: the focused element's closest match (a post, a message). */
53
+ itemSelector: string;
54
+ /** Keys that start a two-key chord of the site's own (their second key is never a shortcut). */
55
+ chordKeys: readonly string[];
56
+ };
57
+ /** True when a key event belongs to text entry, not to page shortcuts. */
58
+ export declare function isTypingTarget(target: EventTarget | null): boolean;
59
+ export declare function createCorePluginApi(win: Window & typeof globalThis, site: PluginApiSite): CorePluginApi;
@@ -0,0 +1,279 @@
1
+ // `window.<Runtime>.api`: small helpers shared by `*.plugin.js` files so each plugin does not
2
+ // re-implement DOM watching, route tracking, local storage and cleanup. Everything here is local to
3
+ // the page: no network requests, no account actions. A site adapter adds its own readers (e.g. a
4
+ // post/message parser) on top of this core.
5
+ const NAMESPACE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
6
+ const ROUTE_POLL_MS = 300;
7
+ const CHORD_MS = 1500;
8
+ /** True when a key event belongs to text entry, not to page shortcuts. */
9
+ export function isTypingTarget(target) {
10
+ const element = target;
11
+ if (!element || typeof element.closest !== 'function')
12
+ return false;
13
+ return Boolean(element.isContentEditable) || /^(INPUT|TEXTAREA|SELECT)$/i.test(element.nodeName) || Boolean(element.closest('[contenteditable="true"]'));
14
+ }
15
+ export function createCorePluginApi(win, site) {
16
+ const doc = win.document;
17
+ const styleNodes = new Map();
18
+ const report = (error) => console.error(`[${site.product}] plugin callback failed`, error);
19
+ const routeListeners = new Set();
20
+ let lastHref = win.location.href;
21
+ let routeTimer = null;
22
+ const checkRoute = () => {
23
+ const href = win.location.href;
24
+ if (href === lastHref)
25
+ return;
26
+ const previous = lastHref;
27
+ lastHref = href;
28
+ for (const listener of [...routeListeners]) {
29
+ try {
30
+ listener(href, previous);
31
+ }
32
+ catch (error) {
33
+ report(error);
34
+ }
35
+ }
36
+ };
37
+ const api = {
38
+ injectCSS(id, css) {
39
+ let node = styleNodes.get(id);
40
+ if (!node?.isConnected) {
41
+ node = doc.createElement('style');
42
+ node.dataset[site.styleDataKey] = id;
43
+ (doc.head || doc.documentElement).appendChild(node);
44
+ styleNodes.set(id, node);
45
+ }
46
+ node.textContent = css;
47
+ },
48
+ clearCSS(id) {
49
+ styleNodes.get(id)?.remove();
50
+ styleNodes.delete(id);
51
+ },
52
+ waitFor(selector, timeoutMs = 15000) {
53
+ return new Promise((resolve, reject) => {
54
+ const found = doc.querySelector(selector);
55
+ if (found)
56
+ return resolve(found);
57
+ const observer = new win.MutationObserver(() => {
58
+ const match = doc.querySelector(selector);
59
+ if (match) {
60
+ observer.disconnect();
61
+ clearTimeout(timer);
62
+ resolve(match);
63
+ }
64
+ });
65
+ const timer = setTimeout(() => { observer.disconnect(); reject(new Error(`${site.name}.waitFor timed out: ${selector}`)); }, timeoutMs);
66
+ observer.observe(doc.documentElement, { childList: true, subtree: true });
67
+ });
68
+ },
69
+ storage(namespace) {
70
+ if (!NAMESPACE.test(namespace))
71
+ throw new Error(`${site.name}.storage: invalid namespace ${JSON.stringify(namespace)}`);
72
+ const prefix = `${site.storagePrefix}${namespace}:`;
73
+ const store = () => { try {
74
+ return win.localStorage;
75
+ }
76
+ catch {
77
+ return null;
78
+ } };
79
+ return {
80
+ get(key, fallback) {
81
+ try {
82
+ const raw = store()?.getItem(prefix + key);
83
+ return raw == null ? fallback : JSON.parse(raw);
84
+ }
85
+ catch {
86
+ return fallback;
87
+ }
88
+ },
89
+ set(key, value) {
90
+ try {
91
+ const target = store();
92
+ if (!target)
93
+ return false;
94
+ target.setItem(prefix + key, JSON.stringify(value));
95
+ return true;
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ },
101
+ remove(key) {
102
+ try {
103
+ store()?.removeItem(prefix + key);
104
+ }
105
+ catch { /* storage unavailable */ }
106
+ },
107
+ keys() {
108
+ const target = store();
109
+ if (!target)
110
+ return [];
111
+ const keys = [];
112
+ for (let i = 0; i < target.length; i += 1) {
113
+ const key = target.key(i);
114
+ if (key?.startsWith(prefix))
115
+ keys.push(key.slice(prefix.length));
116
+ }
117
+ return keys;
118
+ },
119
+ };
120
+ },
121
+ onRouteChange(callback) {
122
+ routeListeners.add(callback);
123
+ if (!routeTimer) {
124
+ lastHref = win.location.href;
125
+ routeTimer = setInterval(checkRoute, ROUTE_POLL_MS);
126
+ win.addEventListener('popstate', checkRoute);
127
+ }
128
+ return () => {
129
+ routeListeners.delete(callback);
130
+ if (routeListeners.size === 0 && routeTimer) {
131
+ clearInterval(routeTimer);
132
+ routeTimer = null;
133
+ win.removeEventListener('popstate', checkRoute);
134
+ }
135
+ };
136
+ },
137
+ observe(selector, callback, root) {
138
+ const seen = new WeakSet();
139
+ let frame = 0;
140
+ let disposed = false;
141
+ const scan = () => {
142
+ frame = 0;
143
+ if (disposed)
144
+ return;
145
+ for (const element of Array.from((root ?? doc).querySelectorAll(selector))) {
146
+ if (seen.has(element))
147
+ continue;
148
+ seen.add(element);
149
+ try {
150
+ callback(element);
151
+ }
152
+ catch (error) {
153
+ report(error);
154
+ }
155
+ }
156
+ };
157
+ const observer = new win.MutationObserver(() => {
158
+ if (!frame)
159
+ frame = win.requestAnimationFrame(scan);
160
+ });
161
+ observer.observe(root ?? doc.documentElement, { childList: true, subtree: true });
162
+ scan();
163
+ return () => {
164
+ disposed = true;
165
+ observer.disconnect();
166
+ if (frame)
167
+ win.cancelAnimationFrame(frame);
168
+ };
169
+ },
170
+ onKey(callback) {
171
+ let chordAt = 0;
172
+ const listener = (event) => {
173
+ if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.altKey || event.repeat)
174
+ return;
175
+ if (isTypingTarget(event.target) || isTypingTarget(doc.activeElement))
176
+ return;
177
+ const now = Date.now();
178
+ const inChord = now - chordAt < CHORD_MS;
179
+ const chordKey = site.chordKeys.includes(event.key);
180
+ chordAt = chordKey && !inChord ? now : 0;
181
+ if (inChord || chordKey)
182
+ return;
183
+ const item = doc.activeElement?.closest?.(site.itemSelector) ?? null;
184
+ try {
185
+ callback(event, item);
186
+ }
187
+ catch (error) {
188
+ report(error);
189
+ }
190
+ };
191
+ doc.addEventListener('keydown', listener);
192
+ return () => doc.removeEventListener('keydown', listener);
193
+ },
194
+ async copyText(text) {
195
+ try {
196
+ await win.navigator.clipboard.writeText(text);
197
+ return true;
198
+ }
199
+ catch {
200
+ const area = doc.createElement('textarea');
201
+ area.value = text;
202
+ Object.assign(area.style, { position: 'fixed', opacity: '0', pointerEvents: 'none' });
203
+ doc.body.appendChild(area);
204
+ area.select();
205
+ let ok = false;
206
+ try {
207
+ ok = doc.execCommand('copy');
208
+ }
209
+ catch {
210
+ ok = false;
211
+ }
212
+ area.remove();
213
+ return ok;
214
+ }
215
+ },
216
+ toast(message, ms = 2200) {
217
+ let node = doc.getElementById(site.toastId);
218
+ if (!node) {
219
+ node = doc.createElement('div');
220
+ node.id = site.toastId;
221
+ node.setAttribute('role', 'status');
222
+ Object.assign(node.style, {
223
+ position: 'fixed', left: '50%', bottom: '28px', transform: 'translateX(-50%)', zIndex: '2147483646',
224
+ padding: '10px 16px', borderRadius: '10px', background: site.toastBackground, color: '#fff',
225
+ font: '600 14px/1.3 system-ui, sans-serif', boxShadow: '0 6px 24px rgba(0,0,0,.35)', maxWidth: '80vw',
226
+ });
227
+ doc.body.appendChild(node);
228
+ }
229
+ node.textContent = message;
230
+ if (node._wbTimer)
231
+ clearTimeout(node._wbTimer);
232
+ const current = node;
233
+ node._wbTimer = win.setTimeout(() => current.remove(), ms);
234
+ },
235
+ scope() {
236
+ let disposers = [];
237
+ return {
238
+ css(id, css) {
239
+ api.injectCSS(id, css);
240
+ disposers.push(() => api.clearCSS(id));
241
+ },
242
+ on(target, type, listener, options) {
243
+ target.addEventListener(type, listener, options);
244
+ disposers.push(() => target.removeEventListener(type, listener, options));
245
+ },
246
+ observe(selector, callback) {
247
+ disposers.push(api.observe(selector, callback));
248
+ },
249
+ onRouteChange(callback) {
250
+ disposers.push(api.onRouteChange(callback));
251
+ },
252
+ onKey(callback) {
253
+ disposers.push(api.onKey(callback));
254
+ },
255
+ add(node) {
256
+ disposers.push(() => node.remove?.());
257
+ return node;
258
+ },
259
+ defer(fn) {
260
+ disposers.push(fn);
261
+ },
262
+ dispose() {
263
+ const pending = disposers.reverse();
264
+ disposers = [];
265
+ for (const fn of pending) {
266
+ try {
267
+ fn();
268
+ }
269
+ catch (error) {
270
+ report(error);
271
+ }
272
+ }
273
+ },
274
+ };
275
+ },
276
+ };
277
+ return api;
278
+ }
279
+ //# sourceMappingURL=pluginApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginApi.js","sourceRoot":"","sources":["../../../runtime/src/pluginApi.ts"],"names":[],"mappings":"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","sourcesContent":["// `window.<Runtime>.api`: small helpers shared by `*.plugin.js` files so each plugin does not\n// re-implement DOM watching, route tracking, local storage and cleanup. Everything here is local to\n// the page: no network requests, no account actions. A site adapter adds its own readers (e.g. a\n// post/message parser) on top of this core.\n\nexport type Dispose = () => void;\n\nexport type PluginStorage = {\n get<T>(key: string, fallback: T): T;\n /** Returns false when the value could not be stored (quota, storage disabled). */\n set(key: string, value: unknown): boolean;\n remove(key: string): void;\n keys(): string[];\n};\n\n/** Collects disposers so a plugin's `stop()` is a single `scope.dispose()`. */\nexport type PluginScope = {\n css(id: string, css: string): void;\n on(target: EventTarget, type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;\n observe(selector: string, callback: (element: Element) => void): void;\n onRouteChange(callback: (href: string, previous: string) => void): void;\n onKey(callback: (event: KeyboardEvent, item: Element | null) => void): void;\n /** Tracks a node the plugin inserted; it is removed on dispose. */\n add<T extends Node>(node: T): T;\n /** Runs `fn` on dispose (restore attributes, clear timers, ...). */\n defer(fn: Dispose): void;\n dispose(): void;\n};\n\nexport type CorePluginApi = {\n injectCSS(id: string, css: string): void;\n clearCSS(id: string): void;\n waitFor(selector: string, timeoutMs?: number): Promise<Element>;\n /** Namespaced JSON storage in this tab's localStorage (`<storagePrefix><namespace>:<key>`). */\n storage(namespace: string): PluginStorage;\n /** Called after the page changes route (pushState/replaceState/back/forward). */\n onRouteChange(callback: (href: string, previous: string) => void): Dispose;\n /** Calls `callback` once for every element matching `selector`, now and as the page renders more. */\n observe(selector: string, callback: (element: Element) => void, root?: ParentNode): Dispose;\n /**\n * Single-key shortcuts: `callback(event, focusedItem)` runs only when the user is not typing, no\n * Ctrl/Meta/Alt is held and the key is not the second half of one of the site's chords.\n */\n onKey(callback: (event: KeyboardEvent, item: Element | null) => void): Dispose;\n copyText(text: string): Promise<boolean>;\n toast(message: string, ms?: number): void;\n scope(): PluginScope;\n};\n\nexport type PluginApiSite = {\n /** The runtime global, for error messages (`<name>.waitFor timed out`). */\n name: string;\n /** Console prefix, e.g. \"wayBetter for X\". */\n product: string;\n /** localStorage key prefix, e.g. `bt-plugin:`. */\n storagePrefix: string;\n /** `data-*` key (camelCase) marking plugin `<style>` elements with their id. */\n styleDataKey: string;\n toastId: string;\n toastBackground: string;\n /** The item a single-key shortcut acts on: the focused element's closest match (a post, a message). */\n itemSelector: string;\n /** Keys that start a two-key chord of the site's own (their second key is never a shortcut). */\n chordKeys: readonly string[];\n};\n\nconst NAMESPACE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;\nconst ROUTE_POLL_MS = 300;\nconst CHORD_MS = 1500;\n\n/** True when a key event belongs to text entry, not to page shortcuts. */\nexport function isTypingTarget(target: EventTarget | null): boolean {\n const element = target as (Element & { isContentEditable?: boolean }) | null;\n if (!element || typeof element.closest !== 'function') return false;\n return Boolean(element.isContentEditable) || /^(INPUT|TEXTAREA|SELECT)$/i.test(element.nodeName) || Boolean(element.closest('[contenteditable=\"true\"]'));\n}\n\nexport function createCorePluginApi(win: Window & typeof globalThis, site: PluginApiSite): CorePluginApi {\n const doc = win.document;\n const styleNodes = new Map<string, HTMLStyleElement>();\n const report = (error: unknown) => console.error(`[${site.product}] plugin callback failed`, error);\n\n const routeListeners = new Set<(href: string, previous: string) => void>();\n let lastHref = win.location.href;\n let routeTimer: ReturnType<typeof setInterval> | null = null;\n const checkRoute = () => {\n const href = win.location.href;\n if (href === lastHref) return;\n const previous = lastHref;\n lastHref = href;\n for (const listener of [...routeListeners]) {\n try { listener(href, previous); } catch (error) { report(error); }\n }\n };\n\n const api: CorePluginApi = {\n injectCSS(id, css) {\n let node = styleNodes.get(id);\n if (!node?.isConnected) {\n node = doc.createElement('style');\n node.dataset[site.styleDataKey] = id;\n (doc.head || doc.documentElement).appendChild(node);\n styleNodes.set(id, node);\n }\n node.textContent = css;\n },\n clearCSS(id) {\n styleNodes.get(id)?.remove();\n styleNodes.delete(id);\n },\n waitFor(selector, timeoutMs = 15000) {\n return new Promise((resolve, reject) => {\n const found = doc.querySelector(selector);\n if (found) return resolve(found);\n const observer = new win.MutationObserver(() => {\n const match = doc.querySelector(selector);\n if (match) { observer.disconnect(); clearTimeout(timer); resolve(match); }\n });\n const timer = setTimeout(() => { observer.disconnect(); reject(new Error(`${site.name}.waitFor timed out: ${selector}`)); }, timeoutMs);\n observer.observe(doc.documentElement, { childList: true, subtree: true });\n });\n },\n storage(namespace) {\n if (!NAMESPACE.test(namespace)) throw new Error(`${site.name}.storage: invalid namespace ${JSON.stringify(namespace)}`);\n const prefix = `${site.storagePrefix}${namespace}:`;\n const store = () => { try { return win.localStorage; } catch { return null; } };\n return {\n get(key, fallback) {\n try {\n const raw = store()?.getItem(prefix + key);\n return raw == null ? fallback : JSON.parse(raw);\n } catch { return fallback; }\n },\n set(key, value) {\n try {\n const target = store();\n if (!target) return false;\n target.setItem(prefix + key, JSON.stringify(value));\n return true;\n } catch { return false; }\n },\n remove(key) {\n try { store()?.removeItem(prefix + key); } catch { /* storage unavailable */ }\n },\n keys() {\n const target = store();\n if (!target) return [];\n const keys: string[] = [];\n for (let i = 0; i < target.length; i += 1) {\n const key = target.key(i);\n if (key?.startsWith(prefix)) keys.push(key.slice(prefix.length));\n }\n return keys;\n },\n };\n },\n onRouteChange(callback) {\n routeListeners.add(callback);\n if (!routeTimer) {\n lastHref = win.location.href;\n routeTimer = setInterval(checkRoute, ROUTE_POLL_MS);\n win.addEventListener('popstate', checkRoute);\n }\n return () => {\n routeListeners.delete(callback);\n if (routeListeners.size === 0 && routeTimer) {\n clearInterval(routeTimer);\n routeTimer = null;\n win.removeEventListener('popstate', checkRoute);\n }\n };\n },\n observe(selector, callback, root) {\n const seen = new WeakSet<Element>();\n let frame = 0;\n let disposed = false;\n const scan = () => {\n frame = 0;\n if (disposed) return;\n for (const element of Array.from((root ?? doc).querySelectorAll(selector))) {\n if (seen.has(element)) continue;\n seen.add(element);\n try { callback(element); } catch (error) { report(error); }\n }\n };\n const observer = new win.MutationObserver(() => {\n if (!frame) frame = win.requestAnimationFrame(scan);\n });\n observer.observe((root as Node | undefined) ?? doc.documentElement, { childList: true, subtree: true });\n scan();\n return () => {\n disposed = true;\n observer.disconnect();\n if (frame) win.cancelAnimationFrame(frame);\n };\n },\n onKey(callback) {\n let chordAt = 0;\n const listener = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.altKey || event.repeat) return;\n if (isTypingTarget(event.target) || isTypingTarget(doc.activeElement)) return;\n const now = Date.now();\n const inChord = now - chordAt < CHORD_MS;\n const chordKey = site.chordKeys.includes(event.key);\n chordAt = chordKey && !inChord ? now : 0;\n if (inChord || chordKey) return;\n const item = doc.activeElement?.closest?.(site.itemSelector) ?? null;\n try { callback(event, item); } catch (error) { report(error); }\n };\n doc.addEventListener('keydown', listener);\n return () => doc.removeEventListener('keydown', listener);\n },\n async copyText(text) {\n try {\n await win.navigator.clipboard.writeText(text);\n return true;\n } catch {\n const area = doc.createElement('textarea');\n area.value = text;\n Object.assign(area.style, { position: 'fixed', opacity: '0', pointerEvents: 'none' });\n doc.body.appendChild(area);\n area.select();\n let ok = false;\n try { ok = doc.execCommand('copy'); } catch { ok = false; }\n area.remove();\n return ok;\n }\n },\n toast(message, ms = 2200) {\n let node = doc.getElementById(site.toastId) as (HTMLElement & { _wbTimer?: number }) | null;\n if (!node) {\n node = doc.createElement('div');\n node.id = site.toastId;\n node.setAttribute('role', 'status');\n Object.assign(node.style, {\n position: 'fixed', left: '50%', bottom: '28px', transform: 'translateX(-50%)', zIndex: '2147483646',\n padding: '10px 16px', borderRadius: '10px', background: site.toastBackground, color: '#fff',\n font: '600 14px/1.3 system-ui, sans-serif', boxShadow: '0 6px 24px rgba(0,0,0,.35)', maxWidth: '80vw',\n });\n doc.body.appendChild(node);\n }\n node.textContent = message;\n if (node._wbTimer) clearTimeout(node._wbTimer);\n const current = node;\n node._wbTimer = win.setTimeout(() => current.remove(), ms);\n },\n scope() {\n let disposers: Dispose[] = [];\n return {\n css(id, css) {\n api.injectCSS(id, css);\n disposers.push(() => api.clearCSS(id));\n },\n on(target, type, listener, options) {\n target.addEventListener(type, listener, options);\n disposers.push(() => target.removeEventListener(type, listener, options));\n },\n observe(selector, callback) {\n disposers.push(api.observe(selector, callback));\n },\n onRouteChange(callback) {\n disposers.push(api.onRouteChange(callback));\n },\n onKey(callback) {\n disposers.push(api.onKey(callback));\n },\n add(node) {\n disposers.push(() => (node as unknown as ChildNode).remove?.());\n return node;\n },\n defer(fn) {\n disposers.push(fn);\n },\n dispose() {\n const pending = disposers.reverse();\n disposers = [];\n for (const fn of pending) {\n try { fn(); } catch (error) { report(error); }\n }\n },\n };\n },\n };\n return api;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import type { AccountSite } from './accountReport.ts';
2
+ import type { LinkSite } from './linkRouting.ts';
3
+ import type { RailSite } from './paneNav.ts';
4
+ import type { PluginApiSite } from './pluginApi.ts';
5
+ import type { SourceAxis } from './themeTokens.ts';
6
+ export type SiteAdapter = {
7
+ /** e.g. "wayBetter for X"; console prefixes and toasts. */
8
+ product: string;
9
+ /** Exact hosts of the site's origin (must match the native `SiteSpec.hosts`). */
10
+ hosts: readonly string[];
11
+ /** The runtime's window global (`window.<runtimeGlobal>`), plugins included. */
12
+ runtimeGlobal: string;
13
+ /** Pages where heavy-page-sensitive DOM features pause (e.g. a WASM chat); null = none. */
14
+ heavyPath: RegExp | null;
15
+ /** Request URL substrings that are the site's API calls; only these reach the net hub stages. */
16
+ apiMarks: string[];
17
+ /** Extra URL substrings that reach XHR gates only. */
18
+ xhrMarks: string[];
19
+ plugins: PluginApiSite;
20
+ /** Split-view rail hiding; omit when the site has no navigation rail. */
21
+ rail?: RailSite;
22
+ /** Signed-in account reader for the account switcher. */
23
+ account?: AccountSite;
24
+ /** Which in-page clicks the "links to site pages" setting may divert. */
25
+ links?: LinkSite;
26
+ /** The site's own palette per color mode, the source the theme engine maps from. */
27
+ themeSources?: Record<string, SourceAxis>;
28
+ };
29
+ /** Applies the adapter's global knobs (DOM hub, net hub). Call once, before features start. */
30
+ export declare function configureRuntime(site: SiteAdapter): void;
31
+ /** Whether `href` is on the site's origin (https, one of `hosts`). */
32
+ export declare function isSiteUrl(site: Pick<SiteAdapter, 'hosts'>, href: string): boolean;
@@ -0,0 +1,20 @@
1
+ // The page side of a site adapter. A wayBetter app for one site is mostly this object (plus the
2
+ // native `SiteSpec` in Rust and its Tauri capability files); every runtime module takes its piece.
3
+ import { configureDomHub } from "./domHub.js";
4
+ import { configureNetHub } from "./netHub.js";
5
+ /** Applies the adapter's global knobs (DOM hub, net hub). Call once, before features start. */
6
+ export function configureRuntime(site) {
7
+ configureDomHub({ label: site.product, heavyPath: site.heavyPath });
8
+ configureNetHub({ apiMarks: site.apiMarks, xhrMarks: site.xhrMarks });
9
+ }
10
+ /** Whether `href` is on the site's origin (https, one of `hosts`). */
11
+ export function isSiteUrl(site, href) {
12
+ try {
13
+ const url = new URL(href);
14
+ return url.protocol === 'https:' && site.hosts.includes(url.hostname) && !url.username && !url.password;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ //# sourceMappingURL=site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site.js","sourceRoot":"","sources":["../../../runtime/src/site.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,mGAAmG;AAEnG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA+B9C,+FAA+F;AAC/F,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IACpE,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,IAAgC,EAAE,IAAY;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC1G,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["// The page side of a site adapter. A wayBetter app for one site is mostly this object (plus the\n// native `SiteSpec` in Rust and its Tauri capability files); every runtime module takes its piece.\n\nimport { configureDomHub } from './domHub.ts';\nimport { configureNetHub } from './netHub.ts';\nimport type { AccountSite } from './accountReport.ts';\nimport type { LinkSite } from './linkRouting.ts';\nimport type { RailSite } from './paneNav.ts';\nimport type { PluginApiSite } from './pluginApi.ts';\nimport type { SourceAxis } from './themeTokens.ts';\n\nexport type SiteAdapter = {\n /** e.g. \"wayBetter for X\"; console prefixes and toasts. */\n product: string;\n /** Exact hosts of the site's origin (must match the native `SiteSpec.hosts`). */\n hosts: readonly string[];\n /** The runtime's window global (`window.<runtimeGlobal>`), plugins included. */\n runtimeGlobal: string;\n /** Pages where heavy-page-sensitive DOM features pause (e.g. a WASM chat); null = none. */\n heavyPath: RegExp | null;\n /** Request URL substrings that are the site's API calls; only these reach the net hub stages. */\n apiMarks: string[];\n /** Extra URL substrings that reach XHR gates only. */\n xhrMarks: string[];\n plugins: PluginApiSite;\n /** Split-view rail hiding; omit when the site has no navigation rail. */\n rail?: RailSite;\n /** Signed-in account reader for the account switcher. */\n account?: AccountSite;\n /** Which in-page clicks the \"links to site pages\" setting may divert. */\n links?: LinkSite;\n /** The site's own palette per color mode, the source the theme engine maps from. */\n themeSources?: Record<string, SourceAxis>;\n};\n\n/** Applies the adapter's global knobs (DOM hub, net hub). Call once, before features start. */\nexport function configureRuntime(site: SiteAdapter): void {\n configureDomHub({ label: site.product, heavyPath: site.heavyPath });\n configureNetHub({ apiMarks: site.apiMarks, xhrMarks: site.xhrMarks });\n}\n\n/** Whether `href` is on the site's origin (https, one of `hosts`). */\nexport function isSiteUrl(site: Pick<SiteAdapter, 'hosts'>, href: string): boolean {\n try {\n const url = new URL(href);\n return url.protocol === 'https:' && site.hosts.includes(url.hostname) && !url.username && !url.password;\n } catch {\n return false;\n }\n}\n"]}
@@ -0,0 +1,64 @@
1
+ export type Oklab = [number, number, number];
2
+ type Rgb = [number, number, number];
3
+ export type AxisRole = 'bg' | 'surface' | 'elevated' | 'border' | 'muted' | 'text';
4
+ export type SourceAxis = Record<AxisRole | 'accent', string[]>;
5
+ export type TargetAxis = Partial<Record<AxisRole | 'accent', string>>;
6
+ export declare function rgbToOklab([r, g, b]: Rgb): Oklab;
7
+ export declare function oklabToRgb([L, A, B]: Oklab): Rgb;
8
+ export type ColorModel = {
9
+ sourceBg: Oklab;
10
+ sourceText: Oklab;
11
+ /** Sorted by `t`: the site's position on its bg→text axis and the theme color placed there. */
12
+ anchors: Array<{
13
+ t: number;
14
+ color: Oklab;
15
+ }>;
16
+ /** The site's listed role colors (`r,g,b`) and the theme color they become, exactly. */
17
+ exact: Map<string, Rgb>;
18
+ sourceAccent: Oklab;
19
+ accent: Oklab | null;
20
+ };
21
+ /**
22
+ * How a color is used decides which site role wins where the site reuses one value for two roles (Default's
23
+ * 239,243,244 is both a raised surface and a border): backgrounds read it as a surface, borders as
24
+ * a border, text as text.
25
+ */
26
+ export type Usage = 'background' | 'border' | 'text';
27
+ /**
28
+ * The model for mapping the site's palette (`source`, one site mode) onto a theme's axis colors for one usage.
29
+ * Every color the site lists for a role is pinned to the theme's color for that role (so a surface the site uses
30
+ * stays exactly the theme's surface), and everything the site paints between them is interpolated.
31
+ */
32
+ export declare function buildColorModel(source: SourceAxis, target: TargetAxis, usage?: Usage): ColorModel | null;
33
+ /** Where a neutral sits on the site's axis (0 = the site background, 1 = the site text), or null when it is off the axis. */
34
+ export declare function axisPosition(model: ColorModel, rgb: Rgb): number | null;
35
+ /** The theme color for one of the site's colors, or null to leave it as the site painted it. */
36
+ export declare function projectColor(model: ColorModel, rgb: Rgb): Rgb | null;
37
+ /**
38
+ * The base structure every theme is built on: where a raised surface, a hover/elevated surface, a
39
+ * border and secondary text sit between a theme's background (0) and its text (1), in OKLab
40
+ * lightness. Taken from the built-in themes (their median), so a theme that only names its
41
+ * background, text and accent gets the same proportions as the rest.
42
+ */
43
+ export declare const BASE_STEPS: Record<'dark' | 'light', Record<'surface' | 'elevated' | 'border' | 'muted', number>>;
44
+ /** Fills in the roles a theme did not name from its background, text and the base structure. */
45
+ export declare function deriveColors<T extends {
46
+ bg: string;
47
+ text: string;
48
+ accent: string;
49
+ surface?: string;
50
+ elevated?: string;
51
+ border?: string;
52
+ muted?: string;
53
+ }>(seed: T): T & Record<'surface' | 'elevated' | 'border' | 'muted', string>;
54
+ /** Where a theme's roles sit on its own background→text axis (0 = bg, 1 = text). */
55
+ export declare function rolePositions(colors: {
56
+ bg: string;
57
+ text: string;
58
+ } & Record<'surface' | 'elevated' | 'border' | 'muted', string>): {
59
+ surface: number;
60
+ elevated: number;
61
+ border: number;
62
+ muted: number;
63
+ };
64
+ export {};