@bridge-ds/toolbar 0.3.0 → 0.4.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 +11 -2
- package/dist/{Toolbar-DJUb3p2c.js → Toolbar-ByLHemKF.js} +146 -26
- package/dist/Toolbar-ByLHemKF.js.map +1 -0
- package/dist/{index-CCTqUFz3.js → index-cRndUN9A.js} +3 -3
- package/dist/{index-CCTqUFz3.js.map → index-cRndUN9A.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/markers.d.ts +25 -0
- package/dist/{session-D6oH78KG.js → session-Dvma9jtT.js} +5 -1
- package/dist/session-Dvma9jtT.js.map +1 -0
- package/dist/session.d.ts +3 -0
- package/dist/together.js +1 -1
- package/dist/useMarkedChanges.d.ts +2 -0
- package/package.json +2 -2
- package/dist/Toolbar-DJUb3p2c.js.map +0 -1
- package/dist/session-D6oH78KG.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session-D6oH78KG.js","sources":["../src/session.ts"],"sourcesContent":["// The Bridge toolbar's pure logic: when it shows, which change it follows, what Before and After mean for that\n// change, and the screens it lists. A product opens it only when Bridge Studio links here with `?bridge=<change>`;\n// production renders nothing, except the Before side of a code change, which arrives with a handoff from a preview.\n\nexport interface StorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/**\n * What the toolbar knows about the product and its design system: from bridge.config.json (the product's entry in\n * products[] and previews), read at build time (the Admin demo) or from the design system repository at run time.\n */\nexport interface BridgeBuild {\n /** The repository whose pull requests `?bridge=<n>` names: the product's own repository, else the design system's. */\n owner: string;\n repo: string;\n /** Production Studio (previews.production.docs): the link back and the shipped tokens.css. */\n studio: string | null;\n /** The product in production (products[].production, else previews.production.<key>): Before of a code change. */\n production: string | null;\n /** The product's preview template (products[].preview): only these hosts may hand a code change over to production. */\n preview: string | null;\n /** VERCEL_ENV of the build ('production', 'preview', 'development'), null when unknown. */\n deployEnv: string | null;\n /** The library branch (repo.defaultBranch) and the token files' root (tokens.root): changes tried together. */\n defaultBranch?: string;\n tokensRoot?: string;\n /** The product's key (products[].key) and the design system repository (\"owner/name\"), for the way back to Studio. */\n product?: string;\n ds?: string;\n /** Bridge's origin for GitHub reads (its read proxy); default production Bridge, null to read GitHub directly. */\n proxy?: string | null;\n}\n\nexport const PARAM = 'bridge';\n/** Where After lives for a code change, carried to production on Before. */\nexport const RETURN_PARAM = 'bridge-return';\n/** Scroll position carried across a Before/After navigation. */\nexport const SCROLL_PARAM = 'bridge-scroll';\nexport const SESSION_KEY = 'bridge-admin:change';\nexport const RETURN_KEY = 'bridge-admin:return';\nexport const DOCK_KEY = 'bridge-admin:toolbar-dock';\nexport const OPEN_KEY = 'bridge-admin:toolbar-open';\n/** Changes tried together: the picks of the tokens they set differently (core encodePicks), from Studio. */\nexport const USE_PARAM = 'bridge-use';\nexport const USE_KEY = 'bridge-admin:use';\n/** At most this many changes are tried together. */\nexport const MAX_TOGETHER = 4;\n\nconst safe = <T>(fn: () => T, fallback: T): T => {\n try {\n return fn();\n } catch {\n return fallback;\n }\n};\n\n/**\n * The change the toolbar follows: `?bridge=<n>` wins and is kept for the session (the admin's hash router keeps the\n * query, a reload or a new path does not need it again); `?bridge=off` forgets it. Blocked storage only loses the\n * memory, never the toolbar of the current page.\n */\nexport function readChange(search: string, storage: StorageLike | null): number | null {\n return readChanges(search, storage)[0] ?? null;\n}\n\n/**\n * The change numbers in \"101\" or \"101,118,120\" (an encoded comma too): positive, at most 7 digits, no repeats, at\n * most MAX_TOGETHER. Anything else in the list makes it unreadable (empty), never a guess.\n */\nexport function parseChanges(raw: string | null | undefined): number[] {\n const parts = (raw ?? '').trim().split(/,|%2C/i).map((p) => p.trim());\n if (!parts.every((p) => /^\\d{1,7}$/.test(p) && Number(p) > 0)) return [];\n return [...new Set(parts.map(Number))].slice(0, MAX_TOGETHER);\n}\n\n/**\n * The changes the toolbar follows: one from \"Try it in the product\", 2 to 4 from \"Try them together\". Same memory as\n * one change: `?bridge=` wins and is kept for the session, `?bridge=off` forgets it, blocked storage only loses the\n * memory. The picks (`bridge-use`) travel with the list and are kept, or forgotten, with it.\n */\nexport function readChanges(search: string, storage: StorageLike | null): number[] {\n const params = new URLSearchParams(search);\n if (params.has(PARAM)) {\n const list = parseChanges(params.get(PARAM));\n if (!list.length) {\n if (storage) safe(() => (storage.removeItem(SESSION_KEY), storage.removeItem(USE_KEY)), undefined);\n return [];\n }\n if (storage) {\n safe(() => storage.setItem(SESSION_KEY, list.join(',')), undefined);\n const use = params.get(USE_PARAM);\n safe(() => (use && list.length > 1 ? storage.setItem(USE_KEY, use) : storage.removeItem(USE_KEY)), undefined);\n }\n return list;\n }\n return storage ? parseChanges(safe(() => storage.getItem(SESSION_KEY), null)) : [];\n}\n\n/** The picks that came with the list: from the address, else the session. */\nexport function readUse(search: string, storage: StorageLike | null): string | null {\n const params = new URLSearchParams(search);\n if (params.has(PARAM)) return params.get(USE_PARAM);\n return storage ? safe(() => storage.getItem(USE_KEY), null) : null;\n}\n\n/** A host that matches the product's preview template (`bridge-admin-git-{branch}-team.vercel.app`), https only. */\nexport function isPreviewUrl(raw: string | null | undefined, template: string | null): boolean {\n if (!raw || !template) return false;\n let url: URL;\n let tpl: URL;\n try {\n url = new URL(raw);\n tpl = new URL(template.replace(/\\{[a-z]+\\}/gi, 'x'));\n } catch {\n return false;\n }\n if (url.protocol !== 'https:' || url.username || url.password || url.port) return false;\n const tplHost = /^https?:\\/\\/([^/?#]+)/i.exec(template)?.[1]?.toLowerCase();\n if (!tplHost || tpl.protocol !== 'https:') return false;\n const pattern = new RegExp(`^${tplHost.split(/\\{[a-z]+\\}/i).map((p) => p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')).join('[a-z0-9-]+')}$`);\n return pattern.test(url.hostname.toLowerCase());\n}\n\n/** The preview to return to from production, from `?bridge-return=` or the session. Only a preview host counts. */\nexport function readReturn(search: string, storage: StorageLike | null, template: string | null): string | null {\n const raw = new URLSearchParams(search).get(RETURN_PARAM);\n if (raw !== null) {\n if (isPreviewUrl(raw, template)) {\n const origin = new URL(raw).origin;\n if (storage) safe(() => storage.setItem(RETURN_KEY, origin), undefined);\n return origin;\n }\n return null;\n }\n const stored = storage ? safe(() => storage.getItem(RETURN_KEY), null) : null;\n return isPreviewUrl(stored, template) ? new URL(stored!).origin : null;\n}\n\nexport type Where = 'production' | 'preview' | 'local';\n\n/** Which deployment this page is: the product in production by host or by build, a preview, or a local build. */\nexport function whereAmI(host: string, build: Pick<BridgeBuild, 'production' | 'deployEnv'>): Where {\n const prodHost = build.production ? safe(() => new URL(build.production!).host, null) : null;\n if (build.deployEnv === 'production' || (prodHost && host.toLowerCase() === prodHost.toLowerCase())) return 'production';\n if (build.deployEnv === 'preview') return 'preview';\n return 'local';\n}\n\n/**\n * Whether the toolbar renders. Never without a change from Studio. In production only for the Before side of a code\n * change, that is with a preview to return to: production on its own renders nothing.\n */\nexport function shouldRender(input: { change: number | null; where: Where; returnTo: string | null; together?: boolean }): boolean {\n if (input.change === null) return false;\n // Changes tried together run on production: no preview holds all of them.\n if (input.together) return true;\n if (input.where === 'production') return input.returnTo !== null;\n return true;\n}\n\n/** \"These 3 changes\": the After side of changes tried together, counting the ones switched on. */\nexport const togetherLabel = (n: number) => (n === 1 ? 'This change' : `These ${n} changes`);\n\n/** The one hint line of changes tried together: where they run and why. */\nexport const TOGETHER_HINT = \"The live product, with these changes' tokens applied in your browser only. No preview has all of them together.\";\n\n/** Show what changed found nothing on this screen for changes tried together. */\nexport const TOGETHER_EMPTY_LINE = \"These changes don't touch this screen. Try another screen.\";\n\n/** Switches one change on or off, keeping the order Studio listed them in. */\nexport function toggleIncluded(all: number[], included: number[], n: number, on: boolean): number[] {\n const next = new Set(included);\n if (on) next.add(n);\n else next.delete(n);\n return all.filter((x) => next.has(x));\n}\n\n/** Studio's Changes list, where changes are tried together (and the way back). */\nexport function studioChangesUrl(build: Pick<BridgeBuild, 'studio' | 'owner' | 'repo'>): string {\n return build.studio ? `${build.studio.replace(/\\/$/, '')}/studio/changes/` : `https://github.com/${build.owner}/${build.repo}/pulls`;\n}\n\nexport type ChangeMode = 'tokens' | 'code';\n\n/** Token files (DTCG and the contrast gate) and changesets only: Before swaps tokens live. Anything else is code. */\nexport function changeMode(files: string[]): ChangeMode {\n const tokenOnly = files.length > 0 && files.every((f) => f.endsWith('.tokens.json') || /contrast\\.checks\\.json$/.test(f) || f.startsWith('.changeset/'));\n return tokenOnly ? 'tokens' : 'code';\n}\n\n/** The live tokens (main's build, served by production Studio at `/tokens.css`), or null when not configured. */\nexport function liveTokensUrl(build: Pick<BridgeBuild, 'studio'>): string | null {\n return build.studio ? safe(() => new URL('tokens.css', build.studio!.replace(/\\/?$/, '/')).href, null) : null;\n}\n\n/**\n * Studio's page for the change. A change in a product's own repository (an exploration there) opens as\n * `?exploration=<n>&product=<key>`; a change of the design system repository as `?pr=<n>`.\n */\nexport function studioChangeUrl(build: Pick<BridgeBuild, 'studio' | 'owner' | 'repo' | 'product' | 'ds'>, change: number): string {\n if (!build.studio) return `https://github.com/${build.owner}/${build.repo}/pull/${change}`;\n const studio = build.studio.replace(/\\/$/, '');\n const own = build.product && build.ds && build.ds.toLowerCase() !== `${build.owner}/${build.repo}`.toLowerCase();\n return own ? `${studio}/studio/changes/?exploration=${change}&product=${encodeURIComponent(build.product!)}` : `${studio}/studio/changes/?pr=${change}`;\n}\n\n/** Shape of a visual impact manifest that the toolbar reads (written by @bridge-ds/visual). */\nexport interface ManifestShot {\n kind: 'story' | 'route';\n label: string;\n route?: string;\n tenant?: string;\n theme: string;\n status: string;\n percent: number;\n}\n\nexport interface Screen {\n label: string;\n route: string;\n tenant: string;\n theme: string;\n percent: number;\n}\n\n/** The product screens the change alters, most changed first, one entry per screen, shop and theme. */\nexport function changedScreens(shots: ManifestShot[] | null | undefined): Screen[] {\n if (!Array.isArray(shots)) return [];\n const seen = new Set<string>();\n return shots\n .filter((s) => s.kind === 'route' && s.route && s.tenant && (s.status === 'changed' || s.status === 'added'))\n .sort((a, b) => b.percent - a.percent)\n .filter((s) => {\n const key = `${s.route}|${s.tenant}|${s.theme}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n })\n .map((s) => ({ label: s.label, route: s.route!, tenant: s.tenant!, theme: s.theme, percent: s.percent }));\n}\n\n/**\n * The same page on another deployment (code changes: production for Before, the preview for After), with the\n * change, the way back and the scroll position in the query. The hash route (ra-core) is kept.\n */\nexport function sameScreenOn(target: string, here: { search: string; hash: string }, extra: { change: number; returnTo?: string | null; scroll?: number }): string {\n const url = new URL(target);\n const params = new URLSearchParams(here.search);\n params.delete(RETURN_PARAM);\n params.delete(SCROLL_PARAM);\n params.set(PARAM, String(extra.change));\n if (extra.returnTo) params.set(RETURN_PARAM, extra.returnTo);\n if (extra.scroll && extra.scroll > 0) params.set(SCROLL_PARAM, String(Math.round(extra.scroll)));\n url.pathname = '/';\n url.search = params.toString();\n url.hash = here.hash;\n return url.href;\n}\n\n/** The scroll position a Before/After navigation carried, once: the caller removes it from the address. */\nexport function readScroll(search: string): number | null {\n const raw = new URLSearchParams(search).get(SCROLL_PARAM);\n return raw && /^\\d{1,7}$/.test(raw) ? Number(raw) : null;\n}\n\nexport type Dock = 'start' | 'center' | 'end';\nconst DOCKS: Dock[] = ['start', 'center', 'end'];\n\n/** Bottom right by default: the corner a back office leaves free most often. */\nexport function readDock(storage: StorageLike | null): Dock {\n const v = storage ? safe(() => storage.getItem(DOCK_KEY), null) : null;\n return DOCKS.includes(v as Dock) ? (v as Dock) : 'end';\n}\n\n/** One step left (-1) or right (+1), stopping at the edges. */\nexport function stepDock(dock: Dock, step: -1 | 1): Dock {\n return DOCKS[Math.min(DOCKS.length - 1, Math.max(0, DOCKS.indexOf(dock) + step))]!;\n}\n\n/** The dock nearest to where a drag ended: thirds of the viewport width. */\nexport function dockAt(x: number, width: number): Dock {\n if (width <= 0) return 'center';\n const r = x / width;\n return r < 1 / 3 ? 'start' : r > 2 / 3 ? 'end' : 'center';\n}\n\n/** Title of a change as people read it: conventional prefixes (\"tokens: \") dropped, first letter up. */\nexport function readableTitle(title: string): string {\n const t = title.replace(/^\\s*[a-z]+(\\([^)]*\\))?!?:\\s*/i, '').trim() || title.trim();\n return t.charAt(0).toUpperCase() + t.slice(1);\n}\n\n/** The B shortcut flips Before and After, unless the person is typing or holds a modifier. */\nexport function isFlipKey(e: { key: string; altKey: boolean; ctrlKey: boolean; metaKey: boolean; target: unknown }): boolean {\n if (e.altKey || e.ctrlKey || e.metaKey || e.key.toLowerCase() !== 'b') return false;\n const t = e.target as { tagName?: string; isContentEditable?: boolean; closest?: (s: string) => unknown } | null;\n if (!t) return true;\n if (t.isContentEditable) return false;\n const tag = (t.tagName ?? '').toLowerCase();\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return false;\n return !(t.closest && t.closest('[role=\"combobox\"], [role=\"textbox\"], [role=\"searchbox\"]'));\n}\n\nexport interface Rect {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\nconst overlaps = (a: Rect, b: Rect, margin: number) => a.left < b.right + margin && a.right > b.left - margin && a.top < b.bottom + margin && a.bottom > b.top - margin;\n\n/**\n * Where the toolbar goes so it never covers a primary or submit button of the page: the chosen dock if it is free,\n * else the opposite corner, else the centre, else the chosen dock lifted just above the buttons it would cover. The\n * docks sit inside the area [left, right] (the content column, so it never sits over the sidebar).\n */\nexport function placeToolbar(input: { left: number; right: number; vh: number; w: number; h: number; gutter: number; preferred: Dock; obstacles: Rect[]; margin?: number }): { dock: Dock; lift: number } {\n const { left: areaLeft, right: areaRight, vh, w, h, gutter, preferred, obstacles } = input;\n const margin = input.margin ?? 8;\n const rectAt = (dock: Dock, lift = 0): Rect => {\n const left = dock === 'start' ? areaLeft : dock === 'end' ? areaRight - w : (areaLeft + areaRight - w) / 2;\n const bottom = vh - gutter - lift;\n return { left, right: left + w, top: bottom - h, bottom };\n };\n const opposite: Dock = preferred === 'start' ? 'end' : 'start';\n const order: Dock[] = [preferred, opposite, ...DOCKS.filter((d) => d !== preferred && d !== opposite)];\n for (const dock of order) {\n const r = rectAt(dock);\n if (!obstacles.some((o) => overlaps(r, o, margin))) return { dock, lift: 0 };\n }\n // Every dock covers something: stay put and rise above the highest button in the way.\n const base = rectAt(preferred);\n const inWay = obstacles.filter((o) => o.left < base.right + margin && o.right > base.left - margin && o.bottom > base.top - margin);\n const top = Math.min(...inWay.map((o) => o.top));\n const lift = Math.max(0, Math.ceil(base.bottom - (top - margin)));\n return { dock: preferred, lift: Math.min(lift, Math.max(0, vh - h - 2 * gutter)) };\n}\n\n/** What one token file can change on screen: a theme and a brand, or every one of them (null). */\nfunction fileScope(file: string): { theme: string | null; brand: string | null } | 'none' {\n if (file.startsWith('.changeset/')) return 'none';\n let m = /(?:^|\\/)semantic\\/([a-z0-9-]+)\\.tokens\\.json$/i.exec(file);\n if (m && m[1] !== 'base') return { theme: m[1]!, brand: null };\n m = /(?:^|\\/)brands\\/([a-z0-9-]+)\\.([a-z0-9-]+)\\.tokens\\.json$/i.exec(file);\n if (m) return { theme: m[2]!, brand: m[1]! };\n m = /(?:^|\\/)brands\\/([a-z0-9-]+)\\.tokens\\.json$/i.exec(file);\n if (m) return { theme: null, brand: m[1]! };\n // Primitives, base, density, components and anything else: could change every theme and brand.\n return { theme: null, brand: null };\n}\n\nexport type NoDifference = { kind: 'theme'; here: string; touched: string } | { kind: 'brand'; here: string; touched: string };\n\n/**\n * Whether a token change shows nothing in this theme and brand, from the token files it edits (DTCG layers named\n * semantic/<theme>, brands/<brand>.<theme>, brands/<brand>). Null when it may show, or when that cannot be told:\n * never claims \"no difference\" on a guess.\n */\nexport function noVisibleDifference(files: string[], here: { theme: string; brand: string }): NoDifference | null {\n const scopes = files.map(fileScope).filter((s): s is { theme: string | null; brand: string | null } => s !== 'none');\n if (!scopes.length) return null;\n const hits = (theme: string, brand: string) => scopes.some((s) => (s.theme === null || s.theme === theme) && (s.brand === null || s.brand === brand));\n if (hits(here.theme, here.brand)) return null;\n const theme = scopes.find((s) => s.theme !== null && s.theme !== here.theme && (s.brand === null || s.brand === here.brand))?.theme;\n if (theme) return { kind: 'theme', here: here.theme, touched: theme };\n const brand = scopes.find((s) => s.brand !== null && s.brand !== here.brand)?.brand;\n return brand ? { kind: 'brand', here: here.brand, touched: brand } : null;\n}\n\n/** \"darker default background.\" The change title as a short phrase after \"This change:\". */\nexport function changeSummary(title: string): string {\n const t = readableTitle(title).replace(/[.\\s]+$/, '');\n if (!t) return '';\n const lower = /^[A-Z][a-z]/.test(t) ? t.charAt(0).toLowerCase() + t.slice(1) : t;\n return `${lower}.`;\n}\n\nexport interface TokenChange {\n /** Token path, \"color.bg.default\". */\n path: string;\n /** CSS variable the token builds to, \"--color-bg-default\". */\n cssVar: string;\n /** The role it plays, what designers call it: \"Page canvas\" (its description), \"Brand primary\" (a palette role). */\n name: string;\n /** The exact step, second: \"Primary 700\" for a palette step, null for a semantic token. */\n step: string | null;\n type: string | null;\n /** The theme and brand its file applies to (null: every one). */\n theme: string | null;\n brand: string | null;\n}\n\ntype TokenNode = { $value?: unknown; $type?: string; $description?: string; [k: string]: unknown };\ntype Leaf = { value: string; type: string | null; description: string | null };\n\nfunction leaves(node: unknown, path: string[] = [], inheritedType: string | null = null, out = new Map<string, Leaf>()): Map<string, Leaf> {\n if (!node || typeof node !== 'object') return out;\n const n = node as TokenNode;\n const type = typeof n.$type === 'string' ? n.$type : inheritedType;\n if ('$value' in n) {\n out.set(path.join('.'), { value: JSON.stringify(n.$value), type, description: typeof n.$description === 'string' ? n.$description : null });\n return out;\n }\n for (const [k, v] of Object.entries(n)) if (!k.startsWith('$')) leaves(v, [...path, k], type, out);\n return out;\n}\n\n/** Palette roles a brand re-points (brands/<brand>.tokens.json), in designers' words. */\nconst PALETTE_ROLES: Record<string, string> = { primary: 'Brand primary', link: 'Link', neutral: 'Neutral', accent: 'Accent', success: 'Success', warning: 'Warning', danger: 'Danger', info: 'Info', categorical: 'Chart colours' };\n\n/** The role a token plays: its description's first phrase, a palette role (\"Brand primary\"), else its path in words. */\nexport function roleOfToken(path: string, description: string | null): string {\n const first = description?.split(/[.:;(]/)[0]?.trim();\n if (first && first.length <= 40) return first;\n const m = /^palette\\.([a-z-]+)\\./.exec(path);\n if (m) return PALETTE_ROLES[m[1]!] ?? designerName(`palette.${m[1]}`, null);\n return designerName(path, null);\n}\n\n/** The palette step, shown second: \"palette.primary.700\" -> \"Primary 700\"; null for other tokens. */\nexport function stepOfToken(path: string): string | null {\n return path.startsWith('palette.') ? designerName(path, null) : null;\n}\n\n/** Designer name of a token: the first phrase of its description, else its path in words (\"Bg default\"). */\nexport function designerName(path: string, description: string | null): string {\n const first = description?.split(/[.:;(]/)[0]?.trim();\n if (first && first.length <= 40) return first;\n const words = path.replace(/^(color|palette)\\./, '').split('.').join(' ');\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\n/**\n * The tokens one file changes between two versions of it (parsed DTCG JSON, null when the file is added or removed):\n * every leaf whose $value differs, with the theme and brand its layer applies to.\n */\nexport function tokenChanges(file: string, before: unknown, after: unknown): TokenChange[] {\n const scope = fileScope(file);\n if (scope === 'none') return [];\n const a = leaves(before);\n const b = leaves(after);\n const out: TokenChange[] = [];\n for (const path of new Set([...a.keys(), ...b.keys()])) {\n const x = a.get(path);\n const y = b.get(path);\n if (x?.value === y?.value) continue;\n out.push({\n path,\n cssVar: `--${path.split('.').join('-')}`,\n name: roleOfToken(path, y?.description ?? x?.description ?? null),\n step: stepOfToken(path),\n type: y?.type ?? x?.type ?? null,\n theme: scope.theme,\n brand: scope.brand,\n });\n }\n return out;\n}\n\n/** WCAG contrast ratio between two rgb() colours, or null when either is not rgb (a non-colour value). */\nexport function contrastRatio(a: string, b: string): number | null {\n const lum = (c: string) => {\n const m = /^rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/.exec(c.trim());\n if (!m) return null;\n const [r, g, bl] = [m[1], m[2], m[3]].map((n) => {\n const v = Number(n) / 255;\n return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * bl;\n };\n const x = lum(a);\n const y = lum(b);\n if (x === null || y === null) return null;\n return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05);\n}\n\n/** Two colours under 1.1:1 read as the same at a glance: the swatch says \"Subtle change\" so it does not look broken. */\nexport const isSubtle = (a: string, b: string): boolean => {\n const r = contrastRatio(a, b);\n return r !== null && r < 1.1;\n};\n\n/** rgb(245, 247, 249) -> #f5f7f9; anything else as is. */\nexport function toHex(color: string): string {\n const m = /^rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*([\\d.]+))?\\)$/.exec(color.trim());\n if (!m) return color;\n const hex = [m[1], m[2], m[3]].map((n) => Number(n).toString(16).padStart(2, '0')).join('');\n return m[4] !== undefined && Number(m[4]) < 1 ? `#${hex}${Math.round(Number(m[4]) * 255).toString(16).padStart(2, '0')}` : `#${hex}`;\n}\n\n/** \"from Darker default background\", \"from #101 and #118\": which change a token's value comes from (changes tried together). */\nexport function fromText(from: number[], titleOf: (n: number) => string | null): string {\n const names = from.map((n) => titleOf(n) ?? `#${n}`);\n return `from ${names.length <= 2 ? names.join(' and ') : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`}`;\n}\n"],"names":["lift","theme","brand"],"mappings":"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"}
|