waybetter 0.6.0 → 0.7.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.
- package/README.md +1 -2
- package/bin/waybetter.mjs +2 -5
- package/cli/buildenv.mjs +1 -1
- package/cli/release.mjs +11 -18
- package/cli/releasecheck.mjs +15 -0
- package/cli/remote.mjs +4 -4
- package/cli/store.mjs +80 -28
- package/cli/strings.mjs +2 -0
- package/dist/runtime/stringPackHook.js +2 -2
- package/dist/runtime/stringPackHook.js.map +1 -1
- package/dist/runtime/stringPackTranslate.d.ts +3 -1
- package/dist/runtime/stringPackTranslate.js +6 -3
- package/dist/runtime/stringPackTranslate.js.map +1 -1
- package/dist/runtime/stringPacks.d.ts +53 -5
- package/dist/runtime/stringPacks.js +161 -25
- package/dist/runtime/stringPacks.js.map +1 -1
- package/dist/runtime/themeBar.d.ts +28 -0
- package/dist/runtime/themeBar.js +82 -0
- package/dist/runtime/themeBar.js.map +1 -0
- package/dist/runtime/themeComponents.js +16 -3
- package/dist/runtime/themeComponents.js.map +1 -1
- package/dist/runtime/themeDecor.js +3 -1
- package/dist/runtime/themeDecor.js.map +1 -1
- package/dist/runtime/themeDerive.d.ts +69 -0
- package/dist/runtime/themeDerive.js +117 -0
- package/dist/runtime/themeDerive.js.map +1 -0
- package/dist/runtime/themeDoctor.d.ts +44 -3
- package/dist/runtime/themeDoctor.js +86 -25
- package/dist/runtime/themeDoctor.js.map +1 -1
- package/dist/runtime/themeGuards.d.ts +60 -0
- package/dist/runtime/themeGuards.js +235 -0
- package/dist/runtime/themeGuards.js.map +1 -0
- package/dist/runtime/themeIcons.d.ts +1 -1
- package/dist/runtime/themeIcons.js +13 -5
- package/dist/runtime/themeIcons.js.map +1 -1
- package/dist/runtime/themeInk.d.ts +37 -0
- package/dist/runtime/themeInk.js +111 -0
- package/dist/runtime/themeInk.js.map +1 -0
- package/dist/runtime/themeInvariants.d.ts +39 -0
- package/dist/runtime/themeInvariants.js +273 -0
- package/dist/runtime/themeInvariants.js.map +1 -0
- package/dist/runtime/themeRuntime.d.ts +15 -1
- package/dist/runtime/themeRuntime.js +78 -5
- package/dist/runtime/themeRuntime.js.map +1 -1
- package/dist/runtime/themeSpec.d.ts +5 -1
- package/dist/runtime/themeSpec.js +9 -13
- package/dist/runtime/themeSpec.js.map +1 -1
- package/dist/shell/InstallDialog.js +14 -8
- package/dist/shell/InstallDialog.js.map +1 -1
- package/dist/shell/StoreView.d.ts +12 -0
- package/dist/shell/StoreView.js +9 -4
- package/dist/shell/StoreView.js.map +1 -1
- package/dist/shell/frame/AppMenu.d.ts +26 -3
- package/dist/shell/frame/AppMenu.js +106 -27
- package/dist/shell/frame/AppMenu.js.map +1 -1
- package/dist/shell/frame/FindBar.js +1 -1
- package/dist/shell/frame/FindBar.js.map +1 -1
- package/dist/shell/frame/Shell.d.ts +1 -0
- package/dist/shell/frame/Shell.js +10 -2
- package/dist/shell/frame/Shell.js.map +1 -1
- package/dist/shell/frame/Titlebar.d.ts +2 -1
- package/dist/shell/frame/Titlebar.js +5 -3
- package/dist/shell/frame/Titlebar.js.map +1 -1
- package/dist/shell/frame/WebPanel.js +4 -1
- package/dist/shell/frame/WebPanel.js.map +1 -1
- package/dist/shell/frame/accountSwitcher.css +10 -10
- package/dist/shell/frame/appMenu.css +19 -13
- package/dist/shell/frame/boot.js +2 -0
- package/dist/shell/frame/boot.js.map +1 -1
- package/dist/shell/frame/chromeTheme.d.ts +19 -1
- package/dist/shell/frame/chromeTheme.js +51 -14
- package/dist/shell/frame/chromeTheme.js.map +1 -1
- package/dist/shell/frame/deck.css +30 -30
- package/dist/shell/frame/edges.css +159 -0
- package/dist/shell/frame/index.d.ts +2 -2
- package/dist/shell/frame/index.js +1 -1
- package/dist/shell/frame/index.js.map +1 -1
- package/dist/shell/frame/mediaModel.d.ts +13 -0
- package/dist/shell/frame/mediaModel.js +14 -0
- package/dist/shell/frame/mediaModel.js.map +1 -1
- package/dist/shell/frame/menuMessages.en.json +43 -1
- package/dist/shell/frame/menuModel.d.ts +42 -0
- package/dist/shell/frame/menuModel.js +8 -0
- package/dist/shell/frame/menuModel.js.map +1 -1
- package/dist/shell/frame/nativeMenu.d.ts +5 -0
- package/dist/shell/frame/nativeMenu.js +178 -0
- package/dist/shell/frame/nativeMenu.js.map +1 -0
- package/dist/shell/frame/nativeMenuModel.d.ts +102 -0
- package/dist/shell/frame/nativeMenuModel.js +186 -0
- package/dist/shell/frame/nativeMenuModel.js.map +1 -0
- package/dist/shell/frame/paletteModel.js +3 -1
- package/dist/shell/frame/paletteModel.js.map +1 -1
- package/dist/shell/frame/paneTheme.css +1 -1
- package/dist/shell/frame/reportModel.d.ts +1 -1
- package/dist/shell/frame/reportModel.js +1 -1
- package/dist/shell/frame/reportModel.js.map +1 -1
- package/dist/shell/frame/shell.css +56 -51
- package/dist/shell/frame/state.d.ts +2 -0
- package/dist/shell/frame/state.js +3 -1
- package/dist/shell/frame/state.js.map +1 -1
- package/dist/shell/frame/themeStore.js +21 -6
- package/dist/shell/frame/themeStore.js.map +1 -1
- package/dist/shell/frame/workspaces.css +12 -12
- package/dist/shell/installController.d.ts +16 -1
- package/dist/shell/installController.js +31 -5
- package/dist/shell/installController.js.map +1 -1
- package/dist/shell/panel/Appearance.js +12 -3
- package/dist/shell/panel/Appearance.js.map +1 -1
- package/dist/shell/panel/Settings.d.ts +1 -1
- package/dist/shell/panel/Settings.js +4 -3
- package/dist/shell/panel/Settings.js.map +1 -1
- package/dist/shell/panel/StoreSection.d.ts +2 -1
- package/dist/shell/panel/StoreSection.js +31 -1
- package/dist/shell/panel/StoreSection.js.map +1 -1
- package/dist/shell/panel/StringPacks.d.ts +12 -3
- package/dist/shell/panel/StringPacks.js +33 -8
- package/dist/shell/panel/StringPacks.js.map +1 -1
- package/dist/shell/panel/backup.css +1 -1
- package/dist/shell/panel/builder.css +48 -40
- package/dist/shell/panel/make.css +8 -7
- package/dist/shell/panel/panel.css +401 -401
- package/dist/shell/panel/panelMessages.d.ts +41 -1
- package/dist/shell/panel/panelMessages.js +13 -1
- package/dist/shell/panel/panelMessages.js.map +1 -1
- package/dist/shell/panel/plugins.css +5 -5
- package/dist/shell/panel/strings.css +1 -1
- package/dist/shell/store.css +34 -14
- package/dist/shell/storeMessages.d.ts +24 -0
- package/dist/shell/storeMessages.js +24 -0
- package/dist/shell/storeMessages.js.map +1 -1
- package/dist/shell/storeModel.d.ts +31 -7
- package/dist/shell/storeModel.js +15 -2
- package/dist/shell/storeModel.js.map +1 -1
- package/dist/shell/stringPacksModel.js +7 -2
- package/dist/shell/stringPacksModel.js.map +1 -1
- package/icons.schema.json +9 -0
- package/locales/ar.json +109 -2
- package/locales/bg.json +109 -2
- package/locales/bn.json +109 -2
- package/locales/ca.json +109 -2
- package/locales/cs.json +109 -2
- package/locales/da.json +109 -2
- package/locales/de.json +109 -2
- package/locales/el.json +109 -2
- package/locales/en.json +108 -2
- package/locales/es.json +109 -2
- package/locales/eu.json +109 -2
- package/locales/fa.json +109 -2
- package/locales/fi.json +109 -2
- package/locales/fil.json +109 -2
- package/locales/fr.json +109 -2
- package/locales/ga.json +109 -2
- package/locales/gl.json +109 -2
- package/locales/gu.json +109 -2
- package/locales/ha.json +109 -2
- package/locales/he.json +109 -2
- package/locales/hi.json +109 -2
- package/locales/hr.json +109 -2
- package/locales/hu.json +110 -2
- package/locales/id.json +109 -2
- package/locales/ig.json +109 -2
- package/locales/it.json +109 -2
- package/locales/ja.json +109 -2
- package/locales/kn.json +109 -2
- package/locales/ko.json +109 -2
- package/locales/mr.json +109 -2
- package/locales/ms.json +109 -2
- package/locales/nb.json +109 -2
- package/locales/nl.json +109 -2
- package/locales/pl.json +109 -2
- package/locales/pt.json +109 -2
- package/locales/ro.json +109 -2
- package/locales/ru.json +109 -2
- package/locales/sk.json +109 -2
- package/locales/sr.json +109 -2
- package/locales/sv.json +109 -2
- package/locales/ta.json +109 -2
- package/locales/th.json +109 -2
- package/locales/tr.json +109 -2
- package/locales/uk.json +109 -2
- package/locales/ur.json +109 -2
- package/locales/vi.json +109 -2
- package/locales/yo.json +109 -2
- package/locales/zh-Hans.json +109 -2
- package/locales/zh-Hant.json +109 -2
- package/package.json +2 -3
- package/store-catalog.schema.json +18 -5
- package/theme.schema.json +9 -0
- package/windows/installer.nsi +22 -2
- package/cli/privacy.mjs +0 -768
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeGuards.js","sourceRoot":"","sources":["../../../runtime/src/themeGuards.ts"],"names":[],"mappings":"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","sourcesContent":["// Theme engine v3, step 8 (.omc/plans/theme-engine-v3.md 3.5, 4.2): the layer guard. A site positions\n// its popovers (menus, hover cards, tooltips) from an anchor; a theme that moves the anchor (a bottom\n// taskbar, a top bar) moves them past the window edge. Static CSS cannot see where a portal lands, so\n// this guard measures each new layer root once and shifts it back inside the viewport minus the bar\n// insets. Bounded: it watches only the layer container's children (no subtree attribute observation\n// of the page), measures in an idle callback, at most a few layers per frame, never timeline cells.\n\n/** The viewport box popovers must stay in. */\nexport type Box = { left: number; top: number; right: number; bottom: number };\nexport type Rect = { left: number; top: number; width: number; height: number };\n\n/** Attribute on a shifted layer (a static rule hides its caret, which points at the old anchor). */\nexport const CLAMPED_ATTR = 'data-wb-clamped';\nexport const CLAMP_X = '--wb-clamp-x';\nexport const CLAMP_Y = '--wb-clamp-y';\n\n/**\n * Pure: the shift that brings `rect` inside `box`. A rect larger than the box keeps its start edge\n * inside (left/top) and reports `overflowY`, so the caller can cap its height and let it scroll.\n */\nexport function clampShift(rect: Rect, box: Box): { dx: number; dy: number; overflowY: boolean } {\n const shift = (start: number, size: number, min: number, max: number) => {\n if (size >= max - min) return min - start;\n if (start < min) return min - start;\n if (start + size > max) return max - (start + size);\n return 0;\n };\n return {\n dx: Math.round(shift(rect.left, rect.width, box.left, box.right)),\n dy: Math.round(shift(rect.top, rect.height, box.top, box.bottom)),\n overflowY: rect.height > box.bottom - box.top,\n };\n}\n\n/** The box: the viewport less `margin` on every side and the bar insets top and bottom. */\nexport function viewportBox(width: number, height: number, insets: { top: number; bottom: number }, margin = 8): Box {\n return { left: margin, top: margin + insets.top, right: width - margin, bottom: height - margin - insets.bottom };\n}\n\n/** The static rules the guard relies on (part of the theme sheet). */\nexport function layerGuardCss(): string {\n return `[${CLAMPED_ATTR}]{translate:var(${CLAMP_X},0px) var(${CLAMP_Y},0px) !important}\\n[${CLAMPED_ATTR}=\"scroll\"]{max-height:var(--wb-clamp-h) !important;overflow-y:auto !important}`;\n}\n\nexport type LayerGuardOptions = {\n /** The container the site renders its portals into (X: `#layers`). */\n root: string;\n /** Inside a new layer, the element that is positioned and should be shifted (first match wins). */\n targets: string[];\n /** Bar insets in px (read from the theme's `--wb-bar-inset-top/bottom` by default). */\n insets?: () => { top: number; bottom: number };\n};\n\nconst px = (value: string) => { const n = parseFloat(value); return Number.isFinite(n) ? n : 0; };\n\n/** Watches the layer root; returns a stop function. Off in safe mode (the host never installs it then). */\nexport function installLayerGuard(win: Window, options: LayerGuardOptions): () => void {\n const doc = win.document;\n const insets = options.insets ?? (() => {\n const style = win.getComputedStyle(doc.documentElement);\n return { top: px(style.getPropertyValue('--wb-bar-inset-top')), bottom: px(style.getPropertyValue('--wb-bar-inset-bottom')) };\n });\n const selector = options.targets.join(',');\n const pending = new Set<Element>();\n let scheduled = false;\n const measure = () => {\n scheduled = false;\n let budget = 4;\n for (const layer of [...pending]) {\n if (budget-- <= 0) break;\n pending.delete(layer);\n if (!layer.isConnected) continue;\n for (const target of layer.matches(selector) ? [layer] : [...layer.querySelectorAll(selector)].slice(0, 2)) clamp(target as HTMLElement);\n }\n if (pending.size) schedule();\n };\n // A measured popover is watched for the site moving it again (its inline position): only its own\n // style attribute, and only position changes count (our own clamp variables change it too).\n const placed = new WeakMap<HTMLElement, string>();\n const position = (el: HTMLElement) => `${el.style.left}|${el.style.top}|${el.style.right}|${el.style.bottom}|${el.style.transform}`;\n const Observer = (win as Window & { MutationObserver: typeof MutationObserver }).MutationObserver;\n const moved = new Observer((records: MutationRecord[]) => {\n for (const record of records) {\n const el = record.target as HTMLElement;\n if (el.isConnected && placed.get(el) !== position(el)) { pending.add(el); schedule(); }\n }\n });\n // A popover that grows (a theme's font, content loading in) is measured again too.\n const Resize = (win as Window & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver;\n const sized = Resize ? new Resize(entries => { for (const entry of entries) { const el = entry.target as HTMLElement; if (el.isConnected) { pending.add(el); schedule(); } } }) : null;\n const clamp = (el: HTMLElement) => {\n if (!placed.has(el)) { moved.observe(el, { attributes: true, attributeFilter: ['style'] }); sized?.observe(el); }\n placed.set(el, position(el));\n // Measure without our own shift.\n el.style.removeProperty(CLAMP_X); el.style.removeProperty(CLAMP_Y);\n const r = el.getBoundingClientRect();\n if (r.width < 1 || r.height < 1) return;\n const box = viewportBox(win.innerWidth, win.innerHeight, insets());\n const { dx, dy, overflowY } = clampShift({ left: r.left, top: r.top, width: r.width, height: r.height }, box);\n if (!dx && !dy && !overflowY) { el.removeAttribute(CLAMPED_ATTR); return; }\n el.style.setProperty(CLAMP_X, `${dx}px`); el.style.setProperty(CLAMP_Y, `${dy}px`);\n if (overflowY) el.style.setProperty('--wb-clamp-h', `${Math.max(80, box.bottom - box.top)}px`);\n el.setAttribute(CLAMPED_ATTR, overflowY ? 'scroll' : '');\n };\n const schedule = () => {\n if (scheduled) return;\n scheduled = true;\n const idle = (win as Window & { requestIdleCallback?: (cb: () => void, o?: { timeout: number }) => number }).requestIdleCallback;\n if (idle) idle.call(win, measure, { timeout: 120 }); else win.setTimeout(measure, 16);\n };\n // Only a node that is or holds a popover is worth a measurement (typing in a dialog inserts nodes too).\n const queue = (node: Node | null) => {\n if (!node || node.nodeType !== 1) return;\n const el = node as Element;\n if (!el.matches(selector) && !el.querySelector(selector)) return;\n pending.add(el); schedule();\n // The site positions a portal after it mounts (a layout effect, a measured anchor): measure again\n // once it has settled. Two late passes, no observer on the page's style attributes.\n for (const delay of [120, 450]) win.setTimeout(() => { if (el.isConnected) { pending.add(el); schedule(); } }, delay);\n };\n let observer: MutationObserver | null = null;\n let layers: Element | null = null;\n const attach = () => {\n const found = doc.querySelector(options.root);\n if (found === layers) return;\n observer?.disconnect();\n layers = found;\n if (!found) return;\n // Children of the layer root and their children: a portal mounts its positioned box a level or two down.\n const next = new Observer((records: MutationRecord[]) => { for (const record of records) for (const node of record.addedNodes) queue(node.nodeType === 1 ? ((node as Element).closest(selector) ?? node) : null); });\n next.observe(found, { childList: true, subtree: true });\n observer = next;\n for (const child of found.children) queue(child);\n };\n attach();\n const retry = win.setInterval(attach, 2000);\n const onResize = () => { if (layers) for (const child of layers.children) queue(child); };\n win.addEventListener('resize', onResize);\n return () => { observer?.disconnect(); moved.disconnect(); sized?.disconnect(); win.clearInterval(retry); win.removeEventListener('resize', onResize); };\n}\n\n// ---------------------------------------------------------------------------------------------\n// Frame marker (theme engine v3 4.3, step 10): a framed part (a card, a panel) whose content is all\n// hidden (a feature switch, a plugin, the site itself) collapses instead of leaving an empty frame.\n// Static CSS cannot see `display:none` inside, so the marker sets `data-wb-void` and a static rule\n// hides the part. Watched by a ResizeObserver per framed part (a hidden module changes the part's size)\n// and a child-list observer on the scopes (new parts), never by a timer.\n\nexport const VOID_ATTR = 'data-wb-void';\n/** Elements that make a part non-empty when they render. */\nexport const VISIBLE_INSIDE = 'a,button,h2,img,svg,input,video,[role=\"heading\"],[data-testid=\"tweetText\"]';\n\n/** The static rule the marker relies on. */\nexport const frameMarkerCss = () => `:root:root [${VOID_ATTR}]{display:none !important}`;\n\nexport type FrameMarkerOptions = {\n /** Containers to watch (X: the sidebar column). */\n scopes: string[];\n /** Framed parts inside them (one selector list). */\n parts: string;\n};\n\n/** Pure: whether `part` renders nothing (every candidate inside has no client rect). */\nexport function isVoid(part: Element): boolean {\n return ![...part.querySelectorAll(VISIBLE_INSIDE)].some(el => el.getClientRects().length > 0);\n}\n\nexport function installFrameMarker(win: Window, options: FrameMarkerOptions): () => void {\n const doc = win.document;\n const Resize = (win as Window & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver;\n const Observer = (win as Window & { MutationObserver: typeof MutationObserver }).MutationObserver;\n const watched = new WeakSet<Element>();\n let frame = 0;\n const check = (part: Element) => {\n // A void part is display:none itself: lift the mark to see whether something came back.\n const was = part.hasAttribute(VOID_ATTR);\n if (was) part.removeAttribute(VOID_ATTR);\n if (isVoid(part)) part.setAttribute(VOID_ATTR, '');\n };\n const sweep = () => {\n frame = 0;\n for (const scope of options.scopes) {\n const root = doc.querySelector(scope);\n if (!root) continue;\n for (const part of root.querySelectorAll(options.parts)) {\n if (!watched.has(part)) { watched.add(part); sized?.observe(part); }\n check(part);\n }\n }\n };\n const schedule = () => { if (!frame) frame = win.requestAnimationFrame(sweep); };\n const sized = Resize ? new Resize(schedule) : null;\n // Inner modules change while the part keeps its size only when hidden content was already 0 tall:\n // child-list changes under the scopes cover those, and new parts.\n const added = new Observer(schedule);\n const roots = new WeakSet<Element>();\n const attach = () => {\n for (const scope of options.scopes) {\n const root = doc.querySelector(scope);\n if (root && !roots.has(root)) { roots.add(root); added.observe(root, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class', 'hidden'] }); schedule(); }\n }\n };\n attach();\n // Scopes mount late and are replaced on navigation: looked up again every 2 s (one querySelector per\n // scope; the per-part work is all observer-driven).\n const retry = win.setInterval(attach, 2000);\n return () => { sized?.disconnect(); added.disconnect(); win.clearInterval(retry); if (frame) win.cancelAnimationFrame(frame); for (const el of doc.querySelectorAll(`[${VOID_ATTR}]`)) el.removeAttribute(VOID_ATTR); };\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ColorRole, type RoleColors } from './themeComponents.ts';
|
|
2
2
|
/** Semantic names a pack may draw. `.active` is a selected navigation item, `.on` an action that is set. */
|
|
3
|
-
export declare const ICON_NAMES: readonly ["home", "home.active", "explore", "explore.active", "notifications", "notifications.active", "messages", "messages.active", "grok", "bookmarks", "bookmarks.active", "lists", "communities", "premium", "profile", "profile.active", "more", "compose", "reply", "repost", "repost.on", "like", "like.on", "views", "bookmark", "bookmark.on", "share", "verified", "logo", "caret", "back", "close", "search", "settings", "media", "gif", "poll", "emoji", "schedule", "location", "pinned", "repostContext", "lock"];
|
|
3
|
+
export declare const ICON_NAMES: readonly ["home", "home.active", "explore", "explore.active", "notifications", "notifications.active", "messages", "messages.active", "grok", "bookmarks", "bookmarks.active", "lists", "communities", "premium", "profile", "profile.active", "more", "compose", "reply", "repost", "repost.on", "like", "like.on", "views", "bookmark", "bookmark.on", "share", "verified", "logo", "caret", "back", "close", "search", "settings", "media", "gif", "poll", "emoji", "schedule", "location", "pinned", "repostContext", "lock", "history", "studio", "articles"];
|
|
4
4
|
export type IconName = typeof ICON_NAMES[number];
|
|
5
5
|
export declare const ICON_GROUPS: readonly ["nav", "actions", "composer", "misc"];
|
|
6
6
|
export type IconGroup = typeof ICON_GROUPS[number];
|
|
@@ -19,6 +19,7 @@ export const ICON_NAMES = [
|
|
|
19
19
|
'reply', 'repost', 'repost.on', 'like', 'like.on', 'views', 'bookmark', 'bookmark.on', 'share',
|
|
20
20
|
'verified', 'logo', 'caret', 'back', 'close', 'search', 'settings',
|
|
21
21
|
'media', 'gif', 'poll', 'emoji', 'schedule', 'location', 'pinned', 'repostContext', 'lock',
|
|
22
|
+
'history', 'studio', 'articles',
|
|
22
23
|
];
|
|
23
24
|
export const ICON_GROUPS = ['nav', 'actions', 'composer', 'misc'];
|
|
24
25
|
/** Limits of a pack (the JSON Schema and the Store check say the same). */
|
|
@@ -188,7 +189,11 @@ export function glyphSvg(glyph, style, colors = null) {
|
|
|
188
189
|
defs.push(def);
|
|
189
190
|
return `<path d="${esc(layer.d)}" fill="${attr}"${layer.opacity !== undefined ? ` opacity="${layer.opacity}"` : ''}${layer.rule ? ' fill-rule="evenodd"' : ''}/>`;
|
|
190
191
|
});
|
|
191
|
-
|
|
192
|
+
// Pixel art (every layer made of axis-aligned rectangles: M, h, v, z only) stays crisp when the
|
|
193
|
+
// 24px box is drawn at a size that is not a whole multiple of its grid (X's rail draws 26px icons).
|
|
194
|
+
const pixel = glyph.layers.every(layer => /^[MmHhVvZz0-9eE\s,.+-]+$/.test(layer.d));
|
|
195
|
+
const svg = pixel ? open.replace('<svg ', '<svg shape-rendering="crispEdges" ') : open;
|
|
196
|
+
return `${svg}${defs.length ? `<defs>${defs.join('')}</defs>` : ''}${paths.join('')}</svg>`;
|
|
192
197
|
}
|
|
193
198
|
return null;
|
|
194
199
|
}
|
|
@@ -224,18 +229,21 @@ export function compileIcons(pack, theme, table, ctx) {
|
|
|
224
229
|
const selectors = iconSelectors(matcher).map(one => `${gate}${one}`);
|
|
225
230
|
if (!selectors.length)
|
|
226
231
|
continue;
|
|
227
|
-
|
|
232
|
+
// Drawn in the content box, whole (live 2026-10-01: X's search field icon is a 28x16 box with 12px of
|
|
233
|
+
// padding, and `100%` of that width cut the top and bottom off a pixel glyph and moved it left);
|
|
234
|
+
// a scaled pack is sized by the box's height, the smaller side of every icon box X draws.
|
|
235
|
+
const size = style.scale === 1 ? 'contain' : `auto ${Math.round(style.scale * 100)}%`;
|
|
228
236
|
if ('png' in glyph) {
|
|
229
|
-
colored.push(`${selectors.join(',')}{background:url("${glyph.png}") center / ${size} no-repeat !important;${glyph.pixel ? 'image-rendering:pixelated;' : ''}}`);
|
|
237
|
+
colored.push(`${selectors.join(',')}{background:url("${glyph.png}") center / ${size} no-repeat content-box !important;${glyph.pixel ? 'image-rendering:pixelated;' : ''}}`);
|
|
230
238
|
}
|
|
231
239
|
else {
|
|
232
240
|
const svg = glyphSvg(glyph, style, ctx.colors);
|
|
233
241
|
if (!svg)
|
|
234
242
|
continue;
|
|
235
243
|
if (isColored(glyph))
|
|
236
|
-
colored.push(`${selectors.join(',')}{background:${svgUrl(svg)} center / ${size} no-repeat !important}`);
|
|
244
|
+
colored.push(`${selectors.join(',')}{background:${svgUrl(svg)} center / ${size} no-repeat content-box !important}`);
|
|
237
245
|
else
|
|
238
|
-
masked.push(`${selectors.join(',')}{background-color:currentColor !important;-webkit-mask:${svgUrl(svg)} center / ${size} no-repeat;mask:${svgUrl(svg)} center / ${size} no-repeat}`);
|
|
246
|
+
masked.push(`${selectors.join(',')}{background-color:currentColor !important;-webkit-mask:${svgUrl(svg)} center / ${size} no-repeat content-box;mask:${svgUrl(svg)} center / ${size} no-repeat content-box}`);
|
|
239
247
|
}
|
|
240
248
|
hidden.push(...selectors.map(one => `${one} > *`));
|
|
241
249
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"themeIcons.js","sourceRoot":"","sources":["../../../runtime/src/themeIcons.ts"],"names":[],"mappings":"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","sourcesContent":["// Icon packs: a standalone document (`*.icons.json`) of glyphs by semantic name (`home`, `like`,\n// `reply`), drawn over a site's own icons. A theme only names a pack (a suggestion); the owner's icon\n// choice is separate (\"Follow the theme\", a pack, or the site's own icons). Pure.\n//\n// A pack is data, never an SVG document: a glyph is path data (checked against a path grammar), a few\n// colored layers of path data, or a small PNG. The engine builds the SVG itself, so no element,\n// attribute, script or reference can arrive with a pack.\n//\n// Drawing: nothing in the site's DOM is rewritten. The site's own `<svg>` box gets the glyph as a CSS\n// mask filled with `currentColor` (so the site's color states keep working: a liked heart stays the\n// like color) and its own paths are made transparent. Colored glyphs (layers, PNG) are a background\n// image instead. Which `<svg>` is which icon is the site's table (container first, path signature\n// second). No glyph, no match or no pack: no rule, the site's icon stays.\n\nimport { COLOR_ROLES, type ColorRole, type RoleColors } from './themeComponents.ts';\n\n/** Semantic names a pack may draw. `.active` is a selected navigation item, `.on` an action that is set. */\nexport const ICON_NAMES = [\n 'home', 'home.active', 'explore', 'explore.active', 'notifications', 'notifications.active', 'messages', 'messages.active',\n 'grok', 'bookmarks', 'bookmarks.active', 'lists', 'communities', 'premium', 'profile', 'profile.active', 'more', 'compose',\n 'reply', 'repost', 'repost.on', 'like', 'like.on', 'views', 'bookmark', 'bookmark.on', 'share',\n 'verified', 'logo', 'caret', 'back', 'close', 'search', 'settings',\n 'media', 'gif', 'poll', 'emoji', 'schedule', 'location', 'pinned', 'repostContext', 'lock',\n] as const;\nexport type IconName = typeof ICON_NAMES[number];\nexport const ICON_GROUPS = ['nav', 'actions', 'composer', 'misc'] as const;\nexport type IconGroup = typeof ICON_GROUPS[number];\n\n/** Limits of a pack (the JSON Schema and the Store check say the same). */\nexport const ICON_LIMITS = { names: 64, pathChars: 4000, layers: 6, pngChars: 6000, packBytes: 120_000, labelChars: 12, overrides: 8 } as const;\n/** Path data only: commands, numbers, separators. */\nexport const PATH_GRAMMAR = /^[MmLlHhVvCcSsQqTtAaZz0-9eE\\s,.+-]{1,4000}$/;\nconst PNG_DATA = /^data:image\\/png;base64,[A-Za-z0-9+/]+={0,2}$/;\nconst HEX = /^#[0-9a-f]{6}$/i;\n/** Words a theme or pack may put next to an action: written into CSS strings. */\nconst LABEL_TEXT = /^[\\p{L}\\p{N} [\\]().:+\\-_/#@*~!?]{1,12}$/u;\n\nexport type IconFill = ColorRole | string | { from: string; to: string };\nexport type IconGlyph =\n | { d: string; rule?: 'evenodd'; stroke?: boolean }\n | { layers: Array<{ d: string; fill: IconFill; opacity?: number; rule?: 'evenodd' }> }\n | { png: string; pixel?: boolean };\nexport type IconStyle = { stroke: number; cap: 'round' | 'square' | 'butt'; join: 'round' | 'miter' | 'bevel'; scale: number };\nexport const BASE_ICON_STYLE: IconStyle = { stroke: 1.75, cap: 'round', join: 'round', scale: 1 };\nexport const ICON_LABEL_NAMES = ['reply', 'repost', 'like', 'views', 'bookmark', 'share'] as const;\nexport type IconLabels = Partial<Record<typeof ICON_LABEL_NAMES[number], string>>;\nexport type IconSymbols = { like?: 'heart' | 'star' | 'custom'; separator?: 'dot' | 'bullet' | 'slash' | 'dash' | 'pipe' | 'none' };\n\nexport type IconPack = {\n id: string;\n name: string;\n description: string;\n author: string;\n /** SPDX id, or `own` for the author's own drawings, or a plain name for artwork under other terms. */\n license: string;\n /** Where the drawings come from (a project name or URL as text) and whom to credit. */\n source: string;\n credits: string;\n version: 1;\n style: IconStyle;\n glyphs: Partial<Record<IconName, IconGlyph>>;\n symbols?: IconSymbols;\n labels?: IconLabels;\n};\n\n/** What a theme says about icons: a pack by id, plus small adjustments. Never a whole pack. */\nexport type ThemeIcons = { pack?: string; style?: Partial<IconStyle>; overrides?: Partial<Record<IconName, IconGlyph>>; symbols?: IconSymbols; labels?: IconLabels };\n\nconst obj = (value: unknown): Record<string, unknown> => value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};\nconst plain = (value: unknown, max: number) => typeof value === 'string' ? value.replace(/[\\u0000-\\u001f\\u007f<>]/g, '').trim().slice(0, max) : '';\nconst num = (value: unknown, min: number, max: number, fallback: number) => typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;\nconst path = (value: unknown): value is string => typeof value === 'string' && PATH_GRAMMAR.test(value);\n\nfunction sanitizeFill(value: unknown): IconFill | null {\n if (typeof value === 'string') return (COLOR_ROLES as readonly string[]).includes(value) ? value : HEX.test(value) ? value.toLowerCase() : null;\n const source = obj(value);\n return typeof source.from === 'string' && typeof source.to === 'string' && HEX.test(source.from) && HEX.test(source.to) ? { from: source.from.toLowerCase(), to: source.to.toLowerCase() } : null;\n}\n\n/** One glyph from JSON, or null when it is not valid path data, layers or a small PNG. */\nexport function sanitizeGlyph(value: unknown): IconGlyph | null {\n const source = obj(value);\n if (path(source.d)) return { d: source.d.trim(), ...(source.rule === 'evenodd' ? { rule: 'evenodd' as const } : {}), ...(source.stroke === true ? { stroke: true } : {}) };\n if (Array.isArray(source.layers)) {\n const layers = source.layers.slice(0, ICON_LIMITS.layers).flatMap(raw => {\n const layer = obj(raw), fill = sanitizeFill(layer.fill);\n if (!path(layer.d) || !fill) return [];\n return [{ d: layer.d.trim(), fill, ...(typeof layer.opacity === 'number' ? { opacity: +num(layer.opacity, 0, 1, 1).toFixed(2) } : {}), ...(layer.rule === 'evenodd' ? { rule: 'evenodd' as const } : {}) }];\n });\n return layers.length ? { layers } : null;\n }\n if (typeof source.png === 'string' && source.png.length <= ICON_LIMITS.pngChars + 22 && PNG_DATA.test(source.png)) return { png: source.png, ...(source.pixel === true ? { pixel: true } : {}) };\n return null;\n}\n\nexport const sanitizeIconStyle = (value: unknown, base: IconStyle = BASE_ICON_STYLE): IconStyle => {\n const source = obj(value);\n const pick = <T extends string>(item: unknown, options: readonly T[], fallback: T) => options.includes(item as T) ? item as T : fallback;\n return {\n stroke: +num(source.stroke, 1, 2.5, base.stroke).toFixed(2), cap: pick(source.cap, ['round', 'square', 'butt'] as const, base.cap),\n join: pick(source.join, ['round', 'miter', 'bevel'] as const, base.join), scale: +num(source.scale, 0.8, 1.25, base.scale).toFixed(2),\n };\n};\n\nfunction sanitizeGlyphs(value: unknown, max: number): Partial<Record<IconName, IconGlyph>> {\n const source = obj(value);\n const out: Partial<Record<IconName, IconGlyph>> = {};\n let count = 0;\n for (const name of ICON_NAMES) {\n if (count >= max || !Object.hasOwn(source, name)) continue;\n const glyph = sanitizeGlyph(source[name]);\n if (glyph) { out[name] = glyph; count++; }\n }\n return out;\n}\nfunction sanitizeSymbols(value: unknown): IconSymbols | undefined {\n const source = obj(value);\n const out: IconSymbols = {};\n if (source.like === 'heart' || source.like === 'star' || source.like === 'custom') out.like = source.like;\n if (['dot', 'bullet', 'slash', 'dash', 'pipe', 'none'].includes(source.separator as string)) out.separator = source.separator as IconSymbols['separator'];\n return Object.keys(out).length ? out : undefined;\n}\nfunction sanitizeLabels(value: unknown): IconLabels | undefined {\n const source = obj(value);\n const out: IconLabels = {};\n for (const name of ICON_LABEL_NAMES) if (typeof source[name] === 'string' && LABEL_TEXT.test(source[name] as string)) out[name] = (source[name] as string).trim();\n return Object.keys(out).length ? out : undefined;\n}\n\n/** Any JSON to a valid pack, or null when it has no id, no name or no valid glyph. */\nexport function sanitizeIconPack(input: unknown): IconPack | null {\n const source = obj(input);\n const name = plain(source.name, 60);\n const id = typeof source.id === 'string' && /^[a-z0-9-]{1,40}$/.test(source.id) ? source.id : '';\n const glyphs = sanitizeGlyphs(source.glyphs, ICON_LIMITS.names);\n if (!id || !name || !Object.keys(glyphs).length) return null;\n const symbols = sanitizeSymbols(source.symbols), labels = sanitizeLabels(source.labels);\n return {\n id, name, description: plain(source.description, 200), author: plain(source.author, 60), license: plain(source.license, 60) || 'unknown',\n source: plain(source.source, 200), credits: plain(source.credits, 400), version: 1, style: sanitizeIconStyle(source.style), glyphs,\n ...(symbols ? { symbols } : {}), ...(labels ? { labels } : {}),\n };\n}\n\n/** Parses an imported `*.icons.json`; errors are human-readable. */\nexport function parseIconPackJson(json: string): { pack: IconPack; warnings: string[] } | { error: string } {\n if (json.length > ICON_LIMITS.packBytes) return { error: 'This icon pack is larger than 120 KB.' };\n let raw: unknown;\n try { raw = JSON.parse(json); } catch { return { error: 'This file is not valid JSON.' }; }\n const pack = sanitizeIconPack(raw);\n if (!pack) return { error: 'An icon pack needs an \"id\", a \"name\" and at least one valid glyph.' };\n const dropped = Object.keys(obj(obj(raw).glyphs)).length - Object.keys(pack.glyphs).length;\n const warnings: string[] = [];\n if (dropped > 0) warnings.push(`${dropped} glyph(s) were ignored: unknown names, or drawings that are not plain path data.`);\n if (pack.license === 'unknown') warnings.push('This pack does not say under which license its drawings are.');\n return { pack, warnings };\n}\n\n/** The `icons` group of a theme from JSON. Null when it says nothing valid. */\nexport function sanitizeThemeIcons(value: unknown): ThemeIcons | null {\n const source = obj(value);\n const out: ThemeIcons = {};\n if (typeof source.pack === 'string' && /^[a-z0-9-]{1,40}$/.test(source.pack)) out.pack = source.pack;\n if (Object.keys(obj(source.style)).length) {\n const style = obj(source.style), full = sanitizeIconStyle(style);\n out.style = Object.fromEntries((['stroke', 'cap', 'join', 'scale'] as const).filter(key => key in style).map(key => [key, full[key]])) as Partial<IconStyle>;\n }\n const overrides = sanitizeGlyphs(source.overrides, ICON_LIMITS.overrides);\n if (Object.keys(overrides).length) out.overrides = overrides;\n const symbols = sanitizeSymbols(source.symbols); if (symbols) out.symbols = symbols;\n const labels = sanitizeLabels(source.labels); if (labels) out.labels = labels;\n return Object.keys(out).length ? out : null;\n}\n\n// ---------------------------------------------------------------------------------------------\n// The owner's choice and the catalog\n\n/** `theme`: follow the theme's suggestion; `site`: the site's own icons; otherwise a pack id. */\nexport type IconChoice = 'theme' | 'site' | string;\nexport const normalizeIconChoice = (value: unknown): IconChoice => typeof value === 'string' && /^[a-z0-9-]{1,40}$/.test(value) ? value : 'theme';\n\nexport type IconCatalog = {\n packs: readonly IconPack[];\n get(id: string | undefined): IconPack | null;\n /** The pack that is drawn: the owner's pick, else the theme's suggestion; null for the site's own icons or an unknown pack. */\n resolve(choice: IconChoice, theme: ThemeIcons | null | undefined): IconPack | null;\n};\nexport function createIconCatalog(builtins: readonly IconPack[], installed: readonly IconPack[] = []): IconCatalog {\n const packs = [...builtins, ...installed.filter(pack => !builtins.some(item => item.id === pack.id))];\n const get = (id: string | undefined) => packs.find(pack => pack.id === id) ?? null;\n return { packs, get, resolve: (choice, theme) => choice === 'site' ? null : choice === 'theme' ? get(theme?.pack) : get(choice) };\n}\n\n// ---------------------------------------------------------------------------------------------\n// Drawing\n\nconst esc = (value: string) => value.replace(/[<>&\"']/g, '');\nconst paint = (fill: IconFill, colors: RoleColors | null, id: string): { attr: string; def: string } => {\n if (typeof fill === 'string') return { attr: HEX.test(fill) ? fill : colors?.[fill as ColorRole] ?? '#000000', def: '' };\n return { attr: `url(#${id})`, def: `<linearGradient id=\"${id}\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"${esc(fill.from)}\"/><stop offset=\"1\" stop-color=\"${esc(fill.to)}\"/></linearGradient>` };\n};\n\n/** The SVG document of a glyph, built from validated data. `colors` resolves role fills of layers; a single path is black (it is used as a mask). */\nexport function glyphSvg(glyph: IconGlyph, style: IconStyle, colors: RoleColors | null = null): string | null {\n const open = '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">';\n if ('d' in glyph) {\n const attrs = glyph.stroke\n ? `fill=\"none\" stroke=\"#000\" stroke-width=\"${style.stroke}\" stroke-linecap=\"${style.cap}\" stroke-linejoin=\"${style.join}\"`\n : `fill=\"#000\"${glyph.rule ? ' fill-rule=\"evenodd\"' : ''}`;\n return `${open}<path d=\"${esc(glyph.d)}\" ${attrs}/></svg>`;\n }\n if ('layers' in glyph) {\n const defs: string[] = [];\n const paths = glyph.layers.map((layer, index) => {\n const { attr, def } = paint(layer.fill, colors, `g${index}`);\n if (def) defs.push(def);\n return `<path d=\"${esc(layer.d)}\" fill=\"${attr}\"${layer.opacity !== undefined ? ` opacity=\"${layer.opacity}\"` : ''}${layer.rule ? ' fill-rule=\"evenodd\"' : ''}/>`;\n });\n return `${open}${defs.length ? `<defs>${defs.join('')}</defs>` : ''}${paths.join('')}</svg>`;\n }\n return null;\n}\nconst svgUrl = (svg: string) => `url(\"data:image/svg+xml,${encodeURIComponent(svg).replace(/'/g, '%27').replace(/\"/g, '%22')}\")`;\n/** True for glyphs that carry their own colors (drawn as an image, not as a mask of the current color). */\nexport const isColored = (glyph: IconGlyph) => !('d' in glyph);\n\n/** How a site finds one of its icons: the container that holds it (stable hooks), else the start of its path data. */\nexport type IconMatcher = {\n /** Selector of the icon's `<svg>` by its container (`[data-testid=\"like\"] svg`). */\n ctx?: string;\n /** Prefixes of the site's own path data for this icon, for icons without a container hook. */\n sig?: readonly string[];\n group: IconGroup;\n /** Needed for its group to be drawn at all (a rail never shows half ours, half the site's). */\n core?: boolean;\n};\nexport type IconTable = Partial<Record<IconName, IconMatcher>>;\n\n/** Selectors of the `<svg>` boxes of one icon. */\nexport function iconSelectors(matcher: IconMatcher): string[] {\n const out: string[] = [];\n if (matcher.ctx) out.push(...matcher.ctx.split(/,(?![^(]*\\))/).map(one => one.trim()));\n for (const prefix of matcher.sig ?? []) if (PATH_GRAMMAR.test(prefix)) out.push(`svg:has(> g > path[d^=\"${prefix}\"])`);\n return out;\n}\n\nexport type IconContext = {\n colors: RoleColors | null;\n /** Attribute on `<html>` that lists the groups that are on (`data-wb-icons=\"nav actions\"`); absent: every group draws. */\n gate?: string;\n /** The owner lets themes and packs change icons (default: yes). */\n allowed?: boolean;\n};\n\n/**\n * CSS that draws `pack` over a site's icons. One rule per glyph the pack has and the site's table\n * knows, inside guards: forced colors remove backgrounds (a masked icon would vanish) and an engine\n * without masks would paint solid squares.\n */\nexport function compileIcons(pack: IconPack | null, theme: ThemeIcons | null | undefined, table: IconTable, ctx: IconContext): string {\n if (ctx.allowed === false) return '';\n const glyphs: Partial<Record<IconName, IconGlyph>> = { ...pack?.glyphs, ...theme?.overrides };\n const style = { ...(pack?.style ?? BASE_ICON_STYLE), ...theme?.style };\n const masked: string[] = [], colored: string[] = [], hidden: string[] = [];\n for (const name of ICON_NAMES) {\n const glyph = glyphs[name], matcher = table[name];\n if (!glyph || !matcher) continue;\n const gate = ctx.gate ? `html[${ctx.gate}~=\"${matcher.group}\"] ` : '';\n const selectors = iconSelectors(matcher).map(one => `${gate}${one}`);\n if (!selectors.length) continue;\n const size = `${Math.round(style.scale * 100)}%`;\n if ('png' in glyph) {\n colored.push(`${selectors.join(',')}{background:url(\"${glyph.png}\") center / ${size} no-repeat !important;${glyph.pixel ? 'image-rendering:pixelated;' : ''}}`);\n } else {\n const svg = glyphSvg(glyph, style, ctx.colors);\n if (!svg) continue;\n if (isColored(glyph)) colored.push(`${selectors.join(',')}{background:${svgUrl(svg)} center / ${size} no-repeat !important}`);\n else masked.push(`${selectors.join(',')}{background-color:currentColor !important;-webkit-mask:${svgUrl(svg)} center / ${size} no-repeat;mask:${svgUrl(svg)} center / ${size} no-repeat}`);\n }\n hidden.push(...selectors.map(one => `${one} > *`));\n }\n if (!hidden.length) return '';\n const hide = `${hidden.join(',')}{opacity:0 !important}`;\n const images = colored.length ? `${colored.join('\\n')}\\n` : '';\n // Colored glyphs need no mask support; masked ones are dropped together with their hiding rule where masks are missing.\n if (!masked.length) return `@media (forced-colors:none){\\n${images}${hide}\\n}`;\n return `@media (forced-colors:none){@supports ((mask-image:url(\"\")) or (-webkit-mask-image:url(\"\"))){\\n${images}${masked.join('\\n')}\\n${hide}\\n}}`;\n}\n\n/** The groups of `table` whose core icons are all on the page (`has(selector)` says whether one matches). Empty table entries count as present. */\nexport function iconGroupsPresent(table: IconTable, has: (selector: string) => boolean): IconGroup[] {\n return ICON_GROUPS.filter(group => {\n const core = ICON_NAMES.map(name => table[name]).filter((matcher): matcher is IconMatcher => !!matcher && matcher.group === group && !!matcher.core);\n return core.every(matcher => iconSelectors(matcher).some(selector => { try { return has(selector); } catch { return false; } }));\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"themeIcons.js","sourceRoot":"","sources":["../../../runtime/src/themeIcons.ts"],"names":[],"mappings":"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","sourcesContent":["// Icon packs: a standalone document (`*.icons.json`) of glyphs by semantic name (`home`, `like`,\n// `reply`), drawn over a site's own icons. A theme only names a pack (a suggestion); the owner's icon\n// choice is separate (\"Follow the theme\", a pack, or the site's own icons). Pure.\n//\n// A pack is data, never an SVG document: a glyph is path data (checked against a path grammar), a few\n// colored layers of path data, or a small PNG. The engine builds the SVG itself, so no element,\n// attribute, script or reference can arrive with a pack.\n//\n// Drawing: nothing in the site's DOM is rewritten. The site's own `<svg>` box gets the glyph as a CSS\n// mask filled with `currentColor` (so the site's color states keep working: a liked heart stays the\n// like color) and its own paths are made transparent. Colored glyphs (layers, PNG) are a background\n// image instead. Which `<svg>` is which icon is the site's table (container first, path signature\n// second). No glyph, no match or no pack: no rule, the site's icon stays.\n\nimport { COLOR_ROLES, type ColorRole, type RoleColors } from './themeComponents.ts';\n\n/** Semantic names a pack may draw. `.active` is a selected navigation item, `.on` an action that is set. */\nexport const ICON_NAMES = [\n 'home', 'home.active', 'explore', 'explore.active', 'notifications', 'notifications.active', 'messages', 'messages.active',\n 'grok', 'bookmarks', 'bookmarks.active', 'lists', 'communities', 'premium', 'profile', 'profile.active', 'more', 'compose',\n 'reply', 'repost', 'repost.on', 'like', 'like.on', 'views', 'bookmark', 'bookmark.on', 'share',\n 'verified', 'logo', 'caret', 'back', 'close', 'search', 'settings',\n 'media', 'gif', 'poll', 'emoji', 'schedule', 'location', 'pinned', 'repostContext', 'lock',\n 'history', 'studio', 'articles',\n] as const;\nexport type IconName = typeof ICON_NAMES[number];\nexport const ICON_GROUPS = ['nav', 'actions', 'composer', 'misc'] as const;\nexport type IconGroup = typeof ICON_GROUPS[number];\n\n/** Limits of a pack (the JSON Schema and the Store check say the same). */\nexport const ICON_LIMITS = { names: 64, pathChars: 4000, layers: 6, pngChars: 6000, packBytes: 120_000, labelChars: 12, overrides: 8 } as const;\n/** Path data only: commands, numbers, separators. */\nexport const PATH_GRAMMAR = /^[MmLlHhVvCcSsQqTtAaZz0-9eE\\s,.+-]{1,4000}$/;\nconst PNG_DATA = /^data:image\\/png;base64,[A-Za-z0-9+/]+={0,2}$/;\nconst HEX = /^#[0-9a-f]{6}$/i;\n/** Words a theme or pack may put next to an action: written into CSS strings. */\nconst LABEL_TEXT = /^[\\p{L}\\p{N} [\\]().:+\\-_/#@*~!?]{1,12}$/u;\n\nexport type IconFill = ColorRole | string | { from: string; to: string };\nexport type IconGlyph =\n | { d: string; rule?: 'evenodd'; stroke?: boolean }\n | { layers: Array<{ d: string; fill: IconFill; opacity?: number; rule?: 'evenodd' }> }\n | { png: string; pixel?: boolean };\nexport type IconStyle = { stroke: number; cap: 'round' | 'square' | 'butt'; join: 'round' | 'miter' | 'bevel'; scale: number };\nexport const BASE_ICON_STYLE: IconStyle = { stroke: 1.75, cap: 'round', join: 'round', scale: 1 };\nexport const ICON_LABEL_NAMES = ['reply', 'repost', 'like', 'views', 'bookmark', 'share'] as const;\nexport type IconLabels = Partial<Record<typeof ICON_LABEL_NAMES[number], string>>;\nexport type IconSymbols = { like?: 'heart' | 'star' | 'custom'; separator?: 'dot' | 'bullet' | 'slash' | 'dash' | 'pipe' | 'none' };\n\nexport type IconPack = {\n id: string;\n name: string;\n description: string;\n author: string;\n /** SPDX id, or `own` for the author's own drawings, or a plain name for artwork under other terms. */\n license: string;\n /** Where the drawings come from (a project name or URL as text) and whom to credit. */\n source: string;\n credits: string;\n version: 1;\n style: IconStyle;\n glyphs: Partial<Record<IconName, IconGlyph>>;\n symbols?: IconSymbols;\n labels?: IconLabels;\n};\n\n/** What a theme says about icons: a pack by id, plus small adjustments. Never a whole pack. */\nexport type ThemeIcons = { pack?: string; style?: Partial<IconStyle>; overrides?: Partial<Record<IconName, IconGlyph>>; symbols?: IconSymbols; labels?: IconLabels };\n\nconst obj = (value: unknown): Record<string, unknown> => value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};\nconst plain = (value: unknown, max: number) => typeof value === 'string' ? value.replace(/[\\u0000-\\u001f\\u007f<>]/g, '').trim().slice(0, max) : '';\nconst num = (value: unknown, min: number, max: number, fallback: number) => typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;\nconst path = (value: unknown): value is string => typeof value === 'string' && PATH_GRAMMAR.test(value);\n\nfunction sanitizeFill(value: unknown): IconFill | null {\n if (typeof value === 'string') return (COLOR_ROLES as readonly string[]).includes(value) ? value : HEX.test(value) ? value.toLowerCase() : null;\n const source = obj(value);\n return typeof source.from === 'string' && typeof source.to === 'string' && HEX.test(source.from) && HEX.test(source.to) ? { from: source.from.toLowerCase(), to: source.to.toLowerCase() } : null;\n}\n\n/** One glyph from JSON, or null when it is not valid path data, layers or a small PNG. */\nexport function sanitizeGlyph(value: unknown): IconGlyph | null {\n const source = obj(value);\n if (path(source.d)) return { d: source.d.trim(), ...(source.rule === 'evenodd' ? { rule: 'evenodd' as const } : {}), ...(source.stroke === true ? { stroke: true } : {}) };\n if (Array.isArray(source.layers)) {\n const layers = source.layers.slice(0, ICON_LIMITS.layers).flatMap(raw => {\n const layer = obj(raw), fill = sanitizeFill(layer.fill);\n if (!path(layer.d) || !fill) return [];\n return [{ d: layer.d.trim(), fill, ...(typeof layer.opacity === 'number' ? { opacity: +num(layer.opacity, 0, 1, 1).toFixed(2) } : {}), ...(layer.rule === 'evenodd' ? { rule: 'evenodd' as const } : {}) }];\n });\n return layers.length ? { layers } : null;\n }\n if (typeof source.png === 'string' && source.png.length <= ICON_LIMITS.pngChars + 22 && PNG_DATA.test(source.png)) return { png: source.png, ...(source.pixel === true ? { pixel: true } : {}) };\n return null;\n}\n\nexport const sanitizeIconStyle = (value: unknown, base: IconStyle = BASE_ICON_STYLE): IconStyle => {\n const source = obj(value);\n const pick = <T extends string>(item: unknown, options: readonly T[], fallback: T) => options.includes(item as T) ? item as T : fallback;\n return {\n stroke: +num(source.stroke, 1, 2.5, base.stroke).toFixed(2), cap: pick(source.cap, ['round', 'square', 'butt'] as const, base.cap),\n join: pick(source.join, ['round', 'miter', 'bevel'] as const, base.join), scale: +num(source.scale, 0.8, 1.25, base.scale).toFixed(2),\n };\n};\n\nfunction sanitizeGlyphs(value: unknown, max: number): Partial<Record<IconName, IconGlyph>> {\n const source = obj(value);\n const out: Partial<Record<IconName, IconGlyph>> = {};\n let count = 0;\n for (const name of ICON_NAMES) {\n if (count >= max || !Object.hasOwn(source, name)) continue;\n const glyph = sanitizeGlyph(source[name]);\n if (glyph) { out[name] = glyph; count++; }\n }\n return out;\n}\nfunction sanitizeSymbols(value: unknown): IconSymbols | undefined {\n const source = obj(value);\n const out: IconSymbols = {};\n if (source.like === 'heart' || source.like === 'star' || source.like === 'custom') out.like = source.like;\n if (['dot', 'bullet', 'slash', 'dash', 'pipe', 'none'].includes(source.separator as string)) out.separator = source.separator as IconSymbols['separator'];\n return Object.keys(out).length ? out : undefined;\n}\nfunction sanitizeLabels(value: unknown): IconLabels | undefined {\n const source = obj(value);\n const out: IconLabels = {};\n for (const name of ICON_LABEL_NAMES) if (typeof source[name] === 'string' && LABEL_TEXT.test(source[name] as string)) out[name] = (source[name] as string).trim();\n return Object.keys(out).length ? out : undefined;\n}\n\n/** Any JSON to a valid pack, or null when it has no id, no name or no valid glyph. */\nexport function sanitizeIconPack(input: unknown): IconPack | null {\n const source = obj(input);\n const name = plain(source.name, 60);\n const id = typeof source.id === 'string' && /^[a-z0-9-]{1,40}$/.test(source.id) ? source.id : '';\n const glyphs = sanitizeGlyphs(source.glyphs, ICON_LIMITS.names);\n if (!id || !name || !Object.keys(glyphs).length) return null;\n const symbols = sanitizeSymbols(source.symbols), labels = sanitizeLabels(source.labels);\n return {\n id, name, description: plain(source.description, 200), author: plain(source.author, 60), license: plain(source.license, 60) || 'unknown',\n source: plain(source.source, 200), credits: plain(source.credits, 400), version: 1, style: sanitizeIconStyle(source.style), glyphs,\n ...(symbols ? { symbols } : {}), ...(labels ? { labels } : {}),\n };\n}\n\n/** Parses an imported `*.icons.json`; errors are human-readable. */\nexport function parseIconPackJson(json: string): { pack: IconPack; warnings: string[] } | { error: string } {\n if (json.length > ICON_LIMITS.packBytes) return { error: 'This icon pack is larger than 120 KB.' };\n let raw: unknown;\n try { raw = JSON.parse(json); } catch { return { error: 'This file is not valid JSON.' }; }\n const pack = sanitizeIconPack(raw);\n if (!pack) return { error: 'An icon pack needs an \"id\", a \"name\" and at least one valid glyph.' };\n const dropped = Object.keys(obj(obj(raw).glyphs)).length - Object.keys(pack.glyphs).length;\n const warnings: string[] = [];\n if (dropped > 0) warnings.push(`${dropped} glyph(s) were ignored: unknown names, or drawings that are not plain path data.`);\n if (pack.license === 'unknown') warnings.push('This pack does not say under which license its drawings are.');\n return { pack, warnings };\n}\n\n/** The `icons` group of a theme from JSON. Null when it says nothing valid. */\nexport function sanitizeThemeIcons(value: unknown): ThemeIcons | null {\n const source = obj(value);\n const out: ThemeIcons = {};\n if (typeof source.pack === 'string' && /^[a-z0-9-]{1,40}$/.test(source.pack)) out.pack = source.pack;\n if (Object.keys(obj(source.style)).length) {\n const style = obj(source.style), full = sanitizeIconStyle(style);\n out.style = Object.fromEntries((['stroke', 'cap', 'join', 'scale'] as const).filter(key => key in style).map(key => [key, full[key]])) as Partial<IconStyle>;\n }\n const overrides = sanitizeGlyphs(source.overrides, ICON_LIMITS.overrides);\n if (Object.keys(overrides).length) out.overrides = overrides;\n const symbols = sanitizeSymbols(source.symbols); if (symbols) out.symbols = symbols;\n const labels = sanitizeLabels(source.labels); if (labels) out.labels = labels;\n return Object.keys(out).length ? out : null;\n}\n\n// ---------------------------------------------------------------------------------------------\n// The owner's choice and the catalog\n\n/** `theme`: follow the theme's suggestion; `site`: the site's own icons; otherwise a pack id. */\nexport type IconChoice = 'theme' | 'site' | string;\nexport const normalizeIconChoice = (value: unknown): IconChoice => typeof value === 'string' && /^[a-z0-9-]{1,40}$/.test(value) ? value : 'theme';\n\nexport type IconCatalog = {\n packs: readonly IconPack[];\n get(id: string | undefined): IconPack | null;\n /** The pack that is drawn: the owner's pick, else the theme's suggestion; null for the site's own icons or an unknown pack. */\n resolve(choice: IconChoice, theme: ThemeIcons | null | undefined): IconPack | null;\n};\nexport function createIconCatalog(builtins: readonly IconPack[], installed: readonly IconPack[] = []): IconCatalog {\n const packs = [...builtins, ...installed.filter(pack => !builtins.some(item => item.id === pack.id))];\n const get = (id: string | undefined) => packs.find(pack => pack.id === id) ?? null;\n return { packs, get, resolve: (choice, theme) => choice === 'site' ? null : choice === 'theme' ? get(theme?.pack) : get(choice) };\n}\n\n// ---------------------------------------------------------------------------------------------\n// Drawing\n\nconst esc = (value: string) => value.replace(/[<>&\"']/g, '');\nconst paint = (fill: IconFill, colors: RoleColors | null, id: string): { attr: string; def: string } => {\n if (typeof fill === 'string') return { attr: HEX.test(fill) ? fill : colors?.[fill as ColorRole] ?? '#000000', def: '' };\n return { attr: `url(#${id})`, def: `<linearGradient id=\"${id}\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"${esc(fill.from)}\"/><stop offset=\"1\" stop-color=\"${esc(fill.to)}\"/></linearGradient>` };\n};\n\n/** The SVG document of a glyph, built from validated data. `colors` resolves role fills of layers; a single path is black (it is used as a mask). */\nexport function glyphSvg(glyph: IconGlyph, style: IconStyle, colors: RoleColors | null = null): string | null {\n const open = '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">';\n if ('d' in glyph) {\n const attrs = glyph.stroke\n ? `fill=\"none\" stroke=\"#000\" stroke-width=\"${style.stroke}\" stroke-linecap=\"${style.cap}\" stroke-linejoin=\"${style.join}\"`\n : `fill=\"#000\"${glyph.rule ? ' fill-rule=\"evenodd\"' : ''}`;\n return `${open}<path d=\"${esc(glyph.d)}\" ${attrs}/></svg>`;\n }\n if ('layers' in glyph) {\n const defs: string[] = [];\n const paths = glyph.layers.map((layer, index) => {\n const { attr, def } = paint(layer.fill, colors, `g${index}`);\n if (def) defs.push(def);\n return `<path d=\"${esc(layer.d)}\" fill=\"${attr}\"${layer.opacity !== undefined ? ` opacity=\"${layer.opacity}\"` : ''}${layer.rule ? ' fill-rule=\"evenodd\"' : ''}/>`;\n });\n // Pixel art (every layer made of axis-aligned rectangles: M, h, v, z only) stays crisp when the\n // 24px box is drawn at a size that is not a whole multiple of its grid (X's rail draws 26px icons).\n const pixel = glyph.layers.every(layer => /^[MmHhVvZz0-9eE\\s,.+-]+$/.test(layer.d));\n const svg = pixel ? open.replace('<svg ', '<svg shape-rendering=\"crispEdges\" ') : open;\n return `${svg}${defs.length ? `<defs>${defs.join('')}</defs>` : ''}${paths.join('')}</svg>`;\n }\n return null;\n}\nconst svgUrl = (svg: string) => `url(\"data:image/svg+xml,${encodeURIComponent(svg).replace(/'/g, '%27').replace(/\"/g, '%22')}\")`;\n/** True for glyphs that carry their own colors (drawn as an image, not as a mask of the current color). */\nexport const isColored = (glyph: IconGlyph) => !('d' in glyph);\n\n/** How a site finds one of its icons: the container that holds it (stable hooks), else the start of its path data. */\nexport type IconMatcher = {\n /** Selector of the icon's `<svg>` by its container (`[data-testid=\"like\"] svg`). */\n ctx?: string;\n /** Prefixes of the site's own path data for this icon, for icons without a container hook. */\n sig?: readonly string[];\n group: IconGroup;\n /** Needed for its group to be drawn at all (a rail never shows half ours, half the site's). */\n core?: boolean;\n};\nexport type IconTable = Partial<Record<IconName, IconMatcher>>;\n\n/** Selectors of the `<svg>` boxes of one icon. */\nexport function iconSelectors(matcher: IconMatcher): string[] {\n const out: string[] = [];\n if (matcher.ctx) out.push(...matcher.ctx.split(/,(?![^(]*\\))/).map(one => one.trim()));\n for (const prefix of matcher.sig ?? []) if (PATH_GRAMMAR.test(prefix)) out.push(`svg:has(> g > path[d^=\"${prefix}\"])`);\n return out;\n}\n\nexport type IconContext = {\n colors: RoleColors | null;\n /** Attribute on `<html>` that lists the groups that are on (`data-wb-icons=\"nav actions\"`); absent: every group draws. */\n gate?: string;\n /** The owner lets themes and packs change icons (default: yes). */\n allowed?: boolean;\n};\n\n/**\n * CSS that draws `pack` over a site's icons. One rule per glyph the pack has and the site's table\n * knows, inside guards: forced colors remove backgrounds (a masked icon would vanish) and an engine\n * without masks would paint solid squares.\n */\nexport function compileIcons(pack: IconPack | null, theme: ThemeIcons | null | undefined, table: IconTable, ctx: IconContext): string {\n if (ctx.allowed === false) return '';\n const glyphs: Partial<Record<IconName, IconGlyph>> = { ...pack?.glyphs, ...theme?.overrides };\n const style = { ...(pack?.style ?? BASE_ICON_STYLE), ...theme?.style };\n const masked: string[] = [], colored: string[] = [], hidden: string[] = [];\n for (const name of ICON_NAMES) {\n const glyph = glyphs[name], matcher = table[name];\n if (!glyph || !matcher) continue;\n const gate = ctx.gate ? `html[${ctx.gate}~=\"${matcher.group}\"] ` : '';\n const selectors = iconSelectors(matcher).map(one => `${gate}${one}`);\n if (!selectors.length) continue;\n // Drawn in the content box, whole (live 2026-10-01: X's search field icon is a 28x16 box with 12px of\n // padding, and `100%` of that width cut the top and bottom off a pixel glyph and moved it left);\n // a scaled pack is sized by the box's height, the smaller side of every icon box X draws.\n const size = style.scale === 1 ? 'contain' : `auto ${Math.round(style.scale * 100)}%`;\n if ('png' in glyph) {\n colored.push(`${selectors.join(',')}{background:url(\"${glyph.png}\") center / ${size} no-repeat content-box !important;${glyph.pixel ? 'image-rendering:pixelated;' : ''}}`);\n } else {\n const svg = glyphSvg(glyph, style, ctx.colors);\n if (!svg) continue;\n if (isColored(glyph)) colored.push(`${selectors.join(',')}{background:${svgUrl(svg)} center / ${size} no-repeat content-box !important}`);\n else masked.push(`${selectors.join(',')}{background-color:currentColor !important;-webkit-mask:${svgUrl(svg)} center / ${size} no-repeat content-box;mask:${svgUrl(svg)} center / ${size} no-repeat content-box}`);\n }\n hidden.push(...selectors.map(one => `${one} > *`));\n }\n if (!hidden.length) return '';\n const hide = `${hidden.join(',')}{opacity:0 !important}`;\n const images = colored.length ? `${colored.join('\\n')}\\n` : '';\n // Colored glyphs need no mask support; masked ones are dropped together with their hiding rule where masks are missing.\n if (!masked.length) return `@media (forced-colors:none){\\n${images}${hide}\\n}`;\n return `@media (forced-colors:none){@supports ((mask-image:url(\"\")) or (-webkit-mask-image:url(\"\"))){\\n${images}${masked.join('\\n')}\\n${hide}\\n}}`;\n}\n\n/** The groups of `table` whose core icons are all on the page (`has(selector)` says whether one matches). Empty table entries count as present. */\nexport function iconGroupsPresent(table: IconTable, has: (selector: string) => boolean): IconGroup[] {\n return ICON_GROUPS.filter(group => {\n const core = ICON_NAMES.map(name => table[name]).filter((matcher): matcher is IconMatcher => !!matcher && matcher.group === group && !!matcher.core);\n return core.every(matcher => iconSelectors(matcher).some(selector => { try { return has(selector); } catch { return false; } }));\n });\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type InkResult = {
|
|
2
|
+
ink: string;
|
|
3
|
+
adjusted: boolean;
|
|
4
|
+
ratio: number;
|
|
5
|
+
};
|
|
6
|
+
export type InkOptions = {
|
|
7
|
+
/** Hue and chroma are always kept where the gamut allows; the flag marks a meaning colour (like, repost) for notes. */
|
|
8
|
+
keepHue?: boolean;
|
|
9
|
+
/** A translucent `fill` (`#rrggbbaa`) is composited over this opaque colour first. */
|
|
10
|
+
over?: string;
|
|
11
|
+
};
|
|
12
|
+
/** WCAG targets used across the engine. */
|
|
13
|
+
export declare const TARGET: {
|
|
14
|
+
readonly text: 4.5;
|
|
15
|
+
readonly large: 3;
|
|
16
|
+
readonly nonText: 3;
|
|
17
|
+
readonly high: 7;
|
|
18
|
+
};
|
|
19
|
+
export type Oklch = {
|
|
20
|
+
l: number;
|
|
21
|
+
c: number;
|
|
22
|
+
h: number;
|
|
23
|
+
};
|
|
24
|
+
export declare function hexToOklch(hex: string): Oklch;
|
|
25
|
+
/** OKLCH to `#rrggbb`, reducing chroma (never hue or lightness) until the colour is in sRGB. */
|
|
26
|
+
export declare function oklchToHex(color: Oklch): string;
|
|
27
|
+
/** `#rrggbbaa` over an opaque `#rrggbb` (sRGB space, as browsers composite). `#rrggbb` passes through. */
|
|
28
|
+
export declare function composite(fill: string, over: string): string;
|
|
29
|
+
/** `a` moved `t` of the way to `b` in OKLab (perceptually even; states and tints). */
|
|
30
|
+
export declare function mixOklab(a: string, b: string, t: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* The ink closest to `preferred` that reaches `target` on every one of `fills` (all opaque).
|
|
33
|
+
* Several fills: the ink has to read on each (a gradient's two ends, glass over two grounds).
|
|
34
|
+
*/
|
|
35
|
+
export declare function inkOnEvery(fills: string[], preferred: string, target: number, options?: InkOptions): InkResult;
|
|
36
|
+
/** The ink closest to `preferred` that reaches `target` on `fill`. */
|
|
37
|
+
export declare function inkOn(fill: string, preferred: string, target: number, options?: InkOptions): InkResult;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// The contrast solver of theme engine v3 (.omc/plans/theme-engine-v3.md 3.3): one function for the
|
|
2
|
+
// derivation, the site remap, the layer guard, the shell and the Theme Builder. Pure, no DOM.
|
|
3
|
+
//
|
|
4
|
+
// inkOn(fill, preferred, target) keeps `preferred` when it reads on `fill`; otherwise it moves the
|
|
5
|
+
// colour in OKLCH lightness toward the pole (black or white) with more headroom on that fill, keeping
|
|
6
|
+
// the hue and giving up chroma only where the sRGB gamut forces it, and stops at the first lightness
|
|
7
|
+
// that reaches the target (binary search). Ratios are WCAG 2.x; APCA is not a gate in v3.0.
|
|
8
|
+
import { contrastRatio, hexToRgb } from "./themeApi.js";
|
|
9
|
+
/** WCAG targets used across the engine. */
|
|
10
|
+
export const TARGET = { text: 4.5, large: 3, nonText: 3, high: 7 };
|
|
11
|
+
// ---------------------------------------------------------------------------------------------
|
|
12
|
+
// sRGB <-> OKLab / OKLCH
|
|
13
|
+
const toLinear = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
|
14
|
+
const fromLinear = (c) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);
|
|
15
|
+
export function hexToOklch(hex) {
|
|
16
|
+
const [r, g, b] = hexToRgb(hex).map(v => toLinear(v / 255));
|
|
17
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
18
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
19
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
20
|
+
const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
|
|
21
|
+
const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
|
|
22
|
+
const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
|
|
23
|
+
return { l: L, c: Math.hypot(A, B), h: Math.atan2(B, A) };
|
|
24
|
+
}
|
|
25
|
+
/** Linear sRGB of an OKLCH colour (may be out of gamut). */
|
|
26
|
+
function oklchToLinear({ l, c, h }) {
|
|
27
|
+
const A = c * Math.cos(h), B = c * Math.sin(h);
|
|
28
|
+
const l_ = (l + 0.3963377774 * A + 0.2158037573 * B) ** 3;
|
|
29
|
+
const m_ = (l - 0.1055613458 * A - 0.0638541728 * B) ** 3;
|
|
30
|
+
const s_ = (l - 0.0894841775 * A - 1.291485548 * B) ** 3;
|
|
31
|
+
return [
|
|
32
|
+
4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
|
|
33
|
+
-1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
|
|
34
|
+
-0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_,
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
const inGamut = (rgb) => rgb.every(v => v >= -1e-4 && v <= 1 + 1e-4);
|
|
38
|
+
const hex2 = (v) => Math.round(Math.min(1, Math.max(0, fromLinear(Math.min(1, Math.max(0, v))))) * 255).toString(16).padStart(2, '0');
|
|
39
|
+
/** OKLCH to `#rrggbb`, reducing chroma (never hue or lightness) until the colour is in sRGB. */
|
|
40
|
+
export function oklchToHex(color) {
|
|
41
|
+
let rgb = oklchToLinear(color);
|
|
42
|
+
if (!inGamut(rgb)) {
|
|
43
|
+
let lo = 0, hi = color.c;
|
|
44
|
+
for (let i = 0; i < 16; i++) {
|
|
45
|
+
const mid = (lo + hi) / 2;
|
|
46
|
+
if (inGamut(oklchToLinear({ ...color, c: mid })))
|
|
47
|
+
lo = mid;
|
|
48
|
+
else
|
|
49
|
+
hi = mid;
|
|
50
|
+
}
|
|
51
|
+
rgb = oklchToLinear({ ...color, c: lo });
|
|
52
|
+
}
|
|
53
|
+
return `#${rgb.map(hex2).join('')}`;
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------------------------
|
|
56
|
+
// Compositing
|
|
57
|
+
/** `#rrggbbaa` over an opaque `#rrggbb` (sRGB space, as browsers composite). `#rrggbb` passes through. */
|
|
58
|
+
export function composite(fill, over) {
|
|
59
|
+
if (!/^#[0-9a-f]{8}$/i.test(fill))
|
|
60
|
+
return fill.toLowerCase();
|
|
61
|
+
const alpha = parseInt(fill.slice(7, 9), 16) / 255;
|
|
62
|
+
const top = hexToRgb(fill.slice(0, 7)), bottom = hexToRgb(over);
|
|
63
|
+
return `#${top.map((v, i) => Math.round(v * alpha + bottom[i] * (1 - alpha)).toString(16).padStart(2, '0')).join('')}`;
|
|
64
|
+
}
|
|
65
|
+
/** `a` moved `t` of the way to `b` in OKLab (perceptually even; states and tints). */
|
|
66
|
+
export function mixOklab(a, b, t) {
|
|
67
|
+
const x = hexToOklch(a), y = hexToOklch(b);
|
|
68
|
+
const ax = x.c * Math.cos(x.h), bx = x.c * Math.sin(x.h), ay = y.c * Math.cos(y.h), by = y.c * Math.sin(y.h);
|
|
69
|
+
const A = ax + (ay - ax) * t, B = bx + (by - bx) * t;
|
|
70
|
+
return oklchToHex({ l: x.l + (y.l - x.l) * t, c: Math.hypot(A, B), h: Math.atan2(B, A) });
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------------------------
|
|
73
|
+
// The solver
|
|
74
|
+
/** The worst ratio of `ink` over every fill. */
|
|
75
|
+
const worst = (ink, fills) => Math.min(...fills.map(fill => contrastRatio(ink, fill)));
|
|
76
|
+
/**
|
|
77
|
+
* The ink closest to `preferred` that reaches `target` on every one of `fills` (all opaque).
|
|
78
|
+
* Several fills: the ink has to read on each (a gradient's two ends, glass over two grounds).
|
|
79
|
+
*/
|
|
80
|
+
export function inkOnEvery(fills, preferred, target, options = {}) {
|
|
81
|
+
const grounds = fills.map(fill => composite(fill, options.over ?? '#ffffff'));
|
|
82
|
+
const start = preferred.toLowerCase();
|
|
83
|
+
const startRatio = worst(start, grounds);
|
|
84
|
+
if (startRatio >= target)
|
|
85
|
+
return { ink: start, adjusted: false, ratio: startRatio };
|
|
86
|
+
// The pole with more headroom: the one that reads better on the worst ground.
|
|
87
|
+
const toWhite = worst('#ffffff', grounds) >= worst('#000000', grounds);
|
|
88
|
+
const pole = toWhite ? '#ffffff' : '#000000';
|
|
89
|
+
const poleRatio = worst(pole, grounds);
|
|
90
|
+
if (poleRatio < target)
|
|
91
|
+
return { ink: pole, adjusted: true, ratio: poleRatio };
|
|
92
|
+
const base = hexToOklch(start);
|
|
93
|
+
// Binary search on lightness between the colour and the pole; the ratio grows monotonically along it.
|
|
94
|
+
let lo = base.l, hi = toWhite ? 1 : 0, best = pole;
|
|
95
|
+
for (let i = 0; i < 12; i++) {
|
|
96
|
+
const mid = (lo + hi) / 2;
|
|
97
|
+
const candidate = oklchToHex({ l: mid, c: base.c, h: base.h });
|
|
98
|
+
if (worst(candidate, grounds) >= target) {
|
|
99
|
+
best = candidate;
|
|
100
|
+
hi = mid;
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
lo = mid;
|
|
104
|
+
}
|
|
105
|
+
return { ink: best, adjusted: true, ratio: worst(best, grounds) };
|
|
106
|
+
}
|
|
107
|
+
/** The ink closest to `preferred` that reaches `target` on `fill`. */
|
|
108
|
+
export function inkOn(fill, preferred, target, options = {}) {
|
|
109
|
+
return inkOnEvery([fill], preferred, target, options);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=themeInk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeInk.js","sourceRoot":"","sources":["../../../runtime/src/themeInk.ts"],"names":[],"mappings":"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","sourcesContent":["// The contrast solver of theme engine v3 (.omc/plans/theme-engine-v3.md 3.3): one function for the\n// derivation, the site remap, the layer guard, the shell and the Theme Builder. Pure, no DOM.\n//\n// inkOn(fill, preferred, target) keeps `preferred` when it reads on `fill`; otherwise it moves the\n// colour in OKLCH lightness toward the pole (black or white) with more headroom on that fill, keeping\n// the hue and giving up chroma only where the sRGB gamut forces it, and stops at the first lightness\n// that reaches the target (binary search). Ratios are WCAG 2.x; APCA is not a gate in v3.0.\n\nimport { contrastRatio, hexToRgb } from './themeApi.ts';\n\nexport type InkResult = { ink: string; adjusted: boolean; ratio: number };\nexport type InkOptions = {\n /** Hue and chroma are always kept where the gamut allows; the flag marks a meaning colour (like, repost) for notes. */\n keepHue?: boolean;\n /** A translucent `fill` (`#rrggbbaa`) is composited over this opaque colour first. */\n over?: string;\n};\n\n/** WCAG targets used across the engine. */\nexport const TARGET = { text: 4.5, large: 3, nonText: 3, high: 7 } as const;\n\n// ---------------------------------------------------------------------------------------------\n// sRGB <-> OKLab / OKLCH\n\nconst toLinear = (c: number) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);\nconst fromLinear = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);\n\nexport type Oklch = { l: number; c: number; h: number };\n\nexport function hexToOklch(hex: string): Oklch {\n const [r, g, b] = hexToRgb(hex).map(v => toLinear(v / 255)) as [number, number, number];\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;\n const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;\n const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;\n return { l: L, c: Math.hypot(A, B), h: Math.atan2(B, A) };\n}\n\n/** Linear sRGB of an OKLCH colour (may be out of gamut). */\nfunction oklchToLinear({ l, c, h }: Oklch): [number, number, number] {\n const A = c * Math.cos(h), B = c * Math.sin(h);\n const l_ = (l + 0.3963377774 * A + 0.2158037573 * B) ** 3;\n const m_ = (l - 0.1055613458 * A - 0.0638541728 * B) ** 3;\n const s_ = (l - 0.0894841775 * A - 1.291485548 * B) ** 3;\n return [\n 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,\n -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,\n -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_,\n ];\n}\n\nconst inGamut = (rgb: number[]) => rgb.every(v => v >= -1e-4 && v <= 1 + 1e-4);\nconst hex2 = (v: number) => Math.round(Math.min(1, Math.max(0, fromLinear(Math.min(1, Math.max(0, v))))) * 255).toString(16).padStart(2, '0');\n\n/** OKLCH to `#rrggbb`, reducing chroma (never hue or lightness) until the colour is in sRGB. */\nexport function oklchToHex(color: Oklch): string {\n let rgb = oklchToLinear(color);\n if (!inGamut(rgb)) {\n let lo = 0, hi = color.c;\n for (let i = 0; i < 16; i++) {\n const mid = (lo + hi) / 2;\n if (inGamut(oklchToLinear({ ...color, c: mid }))) lo = mid; else hi = mid;\n }\n rgb = oklchToLinear({ ...color, c: lo });\n }\n return `#${rgb.map(hex2).join('')}`;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Compositing\n\n/** `#rrggbbaa` over an opaque `#rrggbb` (sRGB space, as browsers composite). `#rrggbb` passes through. */\nexport function composite(fill: string, over: string): string {\n if (!/^#[0-9a-f]{8}$/i.test(fill)) return fill.toLowerCase();\n const alpha = parseInt(fill.slice(7, 9), 16) / 255;\n const top = hexToRgb(fill.slice(0, 7)), bottom = hexToRgb(over);\n return `#${top.map((v, i) => Math.round(v * alpha + bottom[i]! * (1 - alpha)).toString(16).padStart(2, '0')).join('')}`;\n}\n\n/** `a` moved `t` of the way to `b` in OKLab (perceptually even; states and tints). */\nexport function mixOklab(a: string, b: string, t: number): string {\n const x = hexToOklch(a), y = hexToOklch(b);\n const ax = x.c * Math.cos(x.h), bx = x.c * Math.sin(x.h), ay = y.c * Math.cos(y.h), by = y.c * Math.sin(y.h);\n const A = ax + (ay - ax) * t, B = bx + (by - bx) * t;\n return oklchToHex({ l: x.l + (y.l - x.l) * t, c: Math.hypot(A, B), h: Math.atan2(B, A) });\n}\n\n// ---------------------------------------------------------------------------------------------\n// The solver\n\n/** The worst ratio of `ink` over every fill. */\nconst worst = (ink: string, fills: string[]) => Math.min(...fills.map(fill => contrastRatio(ink, fill)));\n\n/**\n * The ink closest to `preferred` that reaches `target` on every one of `fills` (all opaque).\n * Several fills: the ink has to read on each (a gradient's two ends, glass over two grounds).\n */\nexport function inkOnEvery(fills: string[], preferred: string, target: number, options: InkOptions = {}): InkResult {\n const grounds = fills.map(fill => composite(fill, options.over ?? '#ffffff'));\n const start = preferred.toLowerCase();\n const startRatio = worst(start, grounds);\n if (startRatio >= target) return { ink: start, adjusted: false, ratio: startRatio };\n // The pole with more headroom: the one that reads better on the worst ground.\n const toWhite = worst('#ffffff', grounds) >= worst('#000000', grounds);\n const pole = toWhite ? '#ffffff' : '#000000';\n const poleRatio = worst(pole, grounds);\n if (poleRatio < target) return { ink: pole, adjusted: true, ratio: poleRatio };\n const base = hexToOklch(start);\n // Binary search on lightness between the colour and the pole; the ratio grows monotonically along it.\n let lo = base.l, hi = toWhite ? 1 : 0, best = pole;\n for (let i = 0; i < 12; i++) {\n const mid = (lo + hi) / 2;\n const candidate = oklchToHex({ l: mid, c: base.c, h: base.h });\n if (worst(candidate, grounds) >= target) { best = candidate; hi = mid; } else lo = mid;\n }\n return { ink: best, adjusted: true, ratio: worst(best, grounds) };\n}\n\n/** The ink closest to `preferred` that reaches `target` on `fill`. */\nexport function inkOn(fill: string, preferred: string, target: number, options: InkOptions = {}): InkResult {\n return inkOnEvery([fill], preferred, target, options);\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type InvariantId = 'I1' | 'I1b' | 'I2' | 'I3' | 'I4' | 'I5' | 'I6' | 'I7' | 'I8' | 'I10';
|
|
2
|
+
export declare const INVARIANT_IDS: InvariantId[];
|
|
3
|
+
export type Rect = [number, number, number, number];
|
|
4
|
+
export interface Finding {
|
|
5
|
+
id: InvariantId;
|
|
6
|
+
sel: string;
|
|
7
|
+
rect: Rect;
|
|
8
|
+
detail: string;
|
|
9
|
+
value?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface InvariantOptions {
|
|
12
|
+
/** Colours (`rgb(r, g, b)`) that are X's own palette; default: X's light and dark neutrals and blue. */
|
|
13
|
+
xColourKeys?: string[];
|
|
14
|
+
/** The X default theme keeps X's colours: I2 off. */
|
|
15
|
+
exemptLeak?: boolean;
|
|
16
|
+
/** The theme draws an icon pack: I5 on. */
|
|
17
|
+
pack?: boolean;
|
|
18
|
+
access?: 'standard' | 'high';
|
|
19
|
+
/** Cap of findings kept per invariant (counts are always complete). */
|
|
20
|
+
keep?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface InvariantReport {
|
|
23
|
+
counts: Record<InvariantId, number>;
|
|
24
|
+
findings: Finding[];
|
|
25
|
+
/** Measured but not failures: text over images, X icons with no slot, X's allowed truncation. */
|
|
26
|
+
notes: {
|
|
27
|
+
unknownBackdrop: number;
|
|
28
|
+
allowedTruncation: number;
|
|
29
|
+
bar: 'none' | 'rail' | 'horizontal';
|
|
30
|
+
barMode?: 'labels' | 'icons';
|
|
31
|
+
};
|
|
32
|
+
viewport: [number, number];
|
|
33
|
+
}
|
|
34
|
+
/** X's own palette (Default and Lights out neutrals, X blue): the leak list until the engine exports X_COLOUR_KEYS. */
|
|
35
|
+
export declare const DEFAULT_X_KEYS: string[];
|
|
36
|
+
type RGBA = [number, number, number, number];
|
|
37
|
+
export declare const contrast: (a: RGBA, b: RGBA) => number;
|
|
38
|
+
export declare function runInvariants(doc?: Document, opts?: InvariantOptions): InvariantReport;
|
|
39
|
+
export {};
|