@ai-matrx/design-system 0.86.367 → 0.86.369
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/CHANGELOG.md +15 -0
- package/dist/accents.cjs +2 -2
- package/dist/accents.cjs.map +1 -1
- package/dist/accents.js +2 -2
- package/dist/accents.js.map +1 -1
- package/dist/carousel.js +5 -5
- package/dist/{chunk-SOCDBDFS.js → chunk-2HB65PD6.js} +2 -2
- package/dist/{chunk-KOUHF4JC.js → chunk-345ESUIP.js} +4 -4
- package/dist/{chunk-LS3C7IUV.js → chunk-ASDPMZNP.js} +2 -2
- package/dist/{chunk-WLKHQGX5.js → chunk-CFFRMRWJ.js} +2 -2
- package/dist/{chunk-TBWKBTPM.js → chunk-F7IUBABK.js} +3 -3
- package/dist/{chunk-3JEAXZKX.js → chunk-FLYZSVTR.js} +2 -2
- package/dist/{chunk-UAIBCPMB.js → chunk-RLTTLDWU.js} +4 -4
- package/dist/{chunk-VG765KBC.js → chunk-U3SUPYNT.js} +2 -2
- package/dist/{chunk-QEQDJBNK.js → chunk-WSEZDHLA.js} +2 -2
- package/dist/{chunk-3YTF4MH2.js → chunk-YA3DNAAC.js} +2 -2
- package/dist/clipboard-fallback-dialog.js +2 -2
- package/dist/controls.css +7 -0
- package/dist/controls.js +5 -5
- package/dist/data-table/data-row-inspector.js +5 -5
- package/dist/data-table/data-row-window.js +6 -6
- package/dist/data-table/index.cjs +5 -1
- package/dist/data-table/index.cjs.map +1 -1
- package/dist/data-table/index.js +13 -9
- package/dist/data-table/index.js.map +1 -1
- package/dist/data-table/pagination.js +3 -3
- package/dist/data-table/uuid-cell.js +3 -3
- package/dist/desktop.js +4 -4
- package/dist/index.cjs +20 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +27 -19
- package/dist/index.js.map +1 -1
- package/dist/item.js +3 -3
- package/dist/lite-table.js +2 -2
- package/dist/record-list.js +3 -3
- package/dist/settings.js +3 -3
- package/dist/swipe-deck-tree.cjs +238 -0
- package/dist/swipe-deck-tree.cjs.map +1 -0
- package/dist/swipe-deck-tree.d.cts +202 -0
- package/dist/swipe-deck-tree.d.ts +202 -0
- package/dist/swipe-deck-tree.js +208 -0
- package/dist/swipe-deck-tree.js.map +1 -0
- package/dist/swipe-deck.cjs.map +1 -1
- package/dist/swipe-deck.d.cts +5 -0
- package/dist/swipe-deck.d.ts +5 -0
- package/dist/swipe-deck.js.map +1 -1
- package/dist/text-input-dialog.js +1 -1
- package/dist/tokens.css +23 -23
- package/dist/web-theme.css +20 -20
- package/dist/writing-field.js +3 -3
- package/package.json +12 -2
- /package/dist/{chunk-SOCDBDFS.js.map → chunk-2HB65PD6.js.map} +0 -0
- /package/dist/{chunk-KOUHF4JC.js.map → chunk-345ESUIP.js.map} +0 -0
- /package/dist/{chunk-LS3C7IUV.js.map → chunk-ASDPMZNP.js.map} +0 -0
- /package/dist/{chunk-WLKHQGX5.js.map → chunk-CFFRMRWJ.js.map} +0 -0
- /package/dist/{chunk-TBWKBTPM.js.map → chunk-F7IUBABK.js.map} +0 -0
- /package/dist/{chunk-3JEAXZKX.js.map → chunk-FLYZSVTR.js.map} +0 -0
- /package/dist/{chunk-UAIBCPMB.js.map → chunk-RLTTLDWU.js.map} +0 -0
- /package/dist/{chunk-VG765KBC.js.map → chunk-U3SUPYNT.js.map} +0 -0
- /package/dist/{chunk-QEQDJBNK.js.map → chunk-WSEZDHLA.js.map} +0 -0
- /package/dist/{chunk-3YTF4MH2.js.map → chunk-YA3DNAAC.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.86.369
|
|
4
|
+
|
|
5
|
+
- `Button variant="meta"` with an `icon`: the glyph is a fine 12px, thin-stroke icon at the heading's size (a section heading's icon-only action, e.g. Pinned's edit). Additive; no consumer action required.
|
|
6
|
+
|
|
7
|
+
## 0.86.368
|
|
8
|
+
|
|
9
|
+
SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
|
|
10
|
+
so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
|
|
11
|
+
Diff: `git diff f77bffa2f8 be7cbbcfd7 -- apps/shared/design-system/src`
|
|
12
|
+
|
|
13
|
+
Changed shipped source files (1):
|
|
14
|
+
- `apps/shared/design-system/src/data-table/multiline-rows.ts`
|
|
15
|
+
|
|
3
16
|
## 0.86.367
|
|
4
17
|
|
|
18
|
+
- `smsBodyHref(body, phone?)` (contact values): a new message with the words already typed and no recipient chosen (`sms:?&body=`, read by Apple and Android). Additive; no consumer action required.
|
|
5
19
|
- `SwipeDeck`: the answered list's "Show me how" is its own quiet button (`matrx-sd-review-help`, 40px, one line, 16px icon) beside "Back to cards"; it borrowed the legend-chip style, which sizes no icon, so the icon drew full size and the words wrapped. No consumer action required.
|
|
6
20
|
- **Contact values: "Search the web" runs in the host's own search, prefetched.** `ContactValuesHost` takes `searchWeb(query)` (open the host's search in place instead of an outbound Google link) and `prefetchWebSearch(query)` → `WebSearchPrefetch` (`get` / `subscribe`; `get` returns the same object until the status changes). The peek starts the prefetch when it opens and the row shows "Searching…", then "N results" / "No results"; the row always sits last in Actions, built by the shell from each value's `webSearch` words, and closes the peek when pressed. Phones search as `949-404-4444`. Peek rows (`PeekRow`, `PeekCardRowsItem`) gain `onSelect`, an in-place action drawn like a link that wins over `href`. Guards: `contact-values.test.tsx` "searches the web in the host's own search", "starts the host's search when the peek opens". Consumer action: pass `searchWeb` (and optionally `prefetchWebSearch`) to keep searches in-app; without them the row stays an outbound link.
|
|
7
21
|
|
|
@@ -1848,6 +1862,7 @@ Changed shipped source files (1):
|
|
|
1848
1862
|
## 0.86.180
|
|
1849
1863
|
### Unreleased
|
|
1850
1864
|
|
|
1865
|
+
- `DeckItem.parent` and the new entry `@ai-matrx/design-system/swipe-deck-tree`: `TREE_KEY` / `TREE_ANSWERS` (Just right, Too broad, Too narrow, Not useful), `treePath`, `treeBreadcrumb`, `treeChildren`, `deckTreeGraph`, and `useDeckTree(seed, answers, connection)` — Too broad asks the host to `split` a card and its children join right after the card on screen, Too narrow (two or more under one parent) asks it to `combine` them. Additive; no consumer action required.
|
|
1851
1866
|
- **Swipe deck: Skip for now, Cmd+Z, and what rode with an answer (2026-10-24).** `DeckAnswer.requeue`: in a bound deck the connection sends the card to the back of the line and keeps no answer (the change is `transient`: the deck stops drawing it the moment the connection resolves, so it is never counted as answered); undo brings it back. Cmd+Z / Ctrl+Z undo (outside fields). The Answered list reads a typed `detail.value` in place of the card's text and a `detail.label` in place of the answer's label ("Competitor" for Related). The Answered corner opens with nothing answered yet (it holds the walkthrough). Guard: `src/swipe-deck/answered-list.test.tsx`. Consumer action: none.
|
|
1852
1867
|
- **Swipe deck: every answer stays one tap from changing it, mid-deck (Arman, 2026-10-24).** The top-right corner is now **Answered** (with the count): the finish screen's list of every answer at any point, each row taking its answer back and returning to that card; Escape or "Back to cards" leaves it, and the card takes no answers while it is open. "Show me how" moved into that list (the `?` key still plays it). `DeckAnswer.hidden`: an answer the card's own controls give (`ctx.answer`) but never drawn as a button ("Related ▸ Partner"). Labels `answered`, `backToCards`. Undo (Z) already walked back up to 100 steps; a binding's `history` carries it past a reload. Guard: `src/swipe-deck/answered-list.test.tsx`. Consumer action: none.
|
|
1853
1868
|
- **`LabeledField` (controls) — the above-label wrapper for any form input (wave 2, 2026-10-22).** Moved from matrx-frontend `components/official/Field` (label, required / optional mark, help via `InfoHint`, description, error, counter, and the EMPTY-LOOKS-EMPTY rule for a required field whose placeholder could pass for an answer). Named `LabeledField` because `Field` in this package is already the input capsule. The error's content menu comes from the existing `ErrorCardMenusProvider` slot (no host import); without a provider the error shows without a menu. Guard: `src/controls/wave2-official-units.test.tsx`. **Consumer action:** none.
|
package/dist/accents.cjs
CHANGED
|
@@ -60,8 +60,8 @@ var ACCENTS = [
|
|
|
60
60
|
];
|
|
61
61
|
var DEFAULT_ACCENT_ID = "blue";
|
|
62
62
|
var ACCENT_CONTRAST_SURFACES = {
|
|
63
|
-
light: "240
|
|
64
|
-
dark: "240 4%
|
|
63
|
+
light: "240 6% 99%",
|
|
64
|
+
dark: "240 4% 12.5%"
|
|
65
65
|
};
|
|
66
66
|
var DARK_TEXT = "240 10% 10%";
|
|
67
67
|
var WHITE_TEXT = "0 0% 100%";
|
package/dist/accents.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/accents.ts"],"sourcesContent":["// THE ACCENT COLORS (custom-experience P4, 2026-10-20) — the curated accent set a person or an\n// organization picks from, the custom-hex path beside it, and the WCAG contrast math that WARNS about a\n// weak choice (it offers, never blocks). Pure: no React, no DOM, server-safe (`./accents` entry).\n//\n// An accent drives the design system's brand role tokens and nothing else:\n// --primary, --primary-foreground, --primary-ink (light, and their .dark twins)\n// `--accent` in this vocabulary is the quiet GRAY hover surface and is never touched; text tokens stay\n// foreground / muted / disabled. The host stamps `accentAttributes(ref)` on a root element (an attribute\n// plus six custom properties); `accent.css` maps them onto the role tokens for light and dark. With no\n// accent nothing is stamped and nothing changes.\n//\n// One rule makes both themes from ONE color (the same rule for a curated and a custom accent):\n// light primary = the color exactly as chosen;\n// dark primary = the same hue and saturation, lightened until it reads on the dark canvas (≥ 3:1);\n// foreground = white or near-black, whichever reads better on that primary;\n// ink = the primary as TEXT on its own tint: darkened (light) / lightened (dark) to ≥ 5.5:1\n// against the canvas (headroom for the 15% tint it sits on).\n\n/** An HSL color as the token vocabulary writes it: `\"210 80% 45%\"`. */\nexport type HslTriplet = string;\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\n/** One curated accent. `hex` is its light-theme primary; the dark twin is derived (see the header). */\nexport interface AccentDef {\n id: string;\n label: string;\n hex: string;\n}\n\n/**\n * THE CURATED SET. Every entry passes AA in both themes (guard: accents.test.ts): white or near-black\n * text on it ≥ 4.5:1, and it reads on the canvas ≥ 3:1. Ids are saved in layouts — never renamed.\n * `blue` is the built-in primary (`--primary: 210 80% 45%`), so choosing it equals choosing nothing.\n */\nexport const ACCENTS: readonly AccentDef[] = [\n { id: \"blue\", label: \"Blue\", hex: \"#1773cf\" },\n { id: \"indigo\", label: \"Indigo\", hex: \"#4f46e5\" },\n { id: \"violet\", label: \"Violet\", hex: \"#7c3aed\" },\n { id: \"purple\", label: \"Purple\", hex: \"#9333ea\" },\n { id: \"pink\", label: \"Pink\", hex: \"#db2777\" },\n { id: \"rose\", label: \"Rose\", hex: \"#e11d48\" },\n { id: \"red\", label: \"Red\", hex: \"#dc2626\" },\n { id: \"orange\", label: \"Orange\", hex: \"#c2410c\" },\n { id: \"amber\", label: \"Amber\", hex: \"#b45309\" },\n { id: \"green\", label: \"Green\", hex: \"#15803d\" },\n { id: \"emerald\", label: \"Emerald\", hex: \"#047857\" },\n { id: \"teal\", label: \"Teal\", hex: \"#0f766e\" },\n { id: \"cyan\", label: \"Cyan\", hex: \"#0e7490\" },\n { id: \"slate\", label: \"Slate\", hex: \"#475569\" },\n];\n\nexport const DEFAULT_ACCENT_ID = \"blue\";\n\n/**\n * The canvases a primary is judged against: the design system's `--background` in light and dark\n * (tokens.css; accents.test.ts fails if they drift apart).\n */\nexport const ACCENT_CONTRAST_SURFACES: Readonly<Record<AccentTheme, HslTriplet>> = {\n light: \"240 5% 96%\",\n dark: \"240 4% 16%\",\n};\n\n/** The dark text a light primary carries (tokens.css `--secondary-foreground`). */\nconst DARK_TEXT: HslTriplet = \"240 10% 10%\";\nconst WHITE_TEXT: HslTriplet = \"0 0% 100%\";\n\n/** WCAG 2.2 AA: 4.5:1 for text, 3:1 for a control's color against what is around it (1.4.11). */\nexport const AA_TEXT = 4.5;\nexport const AA_NON_TEXT = 3;\n/** The ink is judged against the canvas with headroom, because it sits on a 15% tint of itself. */\nconst INK_TARGET = 5.5;\n\nexport type AccentTheme = \"light\" | \"dark\";\n\n/** A saved accent: a curated id, or a custom `#rrggbb`. */\nexport type AccentRef = { id: string } | { hex: string };\n\n// ── color math ─────────────────────────────────────────────────────────────────\n\n/** `#rgb` / `#rrggbb` (any case, `#` optional) → `#rrggbb` lowercase; anything else → null. */\nexport function normalizeHex(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const raw = value.trim().replace(/^#/, \"\");\n if (/^[0-9a-f]{3}$/i.test(raw)) return `#${raw.split(\"\").map((c) => c + c).join(\"\").toLowerCase()}`;\n if (/^[0-9a-f]{6}$/i.test(raw)) return `#${raw.toLowerCase()}`;\n return null;\n}\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const n = Number.parseInt(hex.slice(1), 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\nexport function hexToHsl(hex: string): Hsl {\n const [r, g, b] = hexToRgb(hex).map((v) => v / 255) as [number, number, number];\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n const d = max - min;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n if (max === r) h = ((g - b) / d) % 6;\n else if (max === g) h = (b - r) / d + 2;\n else h = (r - g) / d + 4;\n h *= 60;\n if (h < 0) h += 360;\n }\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nfunction hslToRgb({ h, s, l }: Hsl): [number, number, number] {\n const sat = s / 100;\n const lig = l / 100;\n const c = (1 - Math.abs(2 * lig - 1)) * sat;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = lig - c / 2;\n const [r, g, b] =\n h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] : h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];\n return [(r + m) * 255, (g + m) * 255, (b + m) * 255];\n}\n\nexport function parseTriplet(triplet: HslTriplet): Hsl {\n const [h, s, l] = triplet.replace(/%/g, \"\").trim().split(/\\s+/).map(Number);\n return { h: h ?? 0, s: s ?? 0, l: l ?? 0 };\n}\n\nexport function toTriplet({ h, s, l }: Hsl): HslTriplet {\n return `${h} ${s}% ${l}%`;\n}\n\n/** WCAG relative luminance of an HSL color. */\nexport function relativeLuminance(color: Hsl): number {\n const [r, g, b] = hslToRgb(color).map((v) => {\n const c = v / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio of two colors (1 to 21). */\nexport function contrastRatio(a: Hsl | HslTriplet, b: Hsl | HslTriplet): number {\n const la = relativeLuminance(typeof a === \"string\" ? parseTriplet(a) : a);\n const lb = relativeLuminance(typeof b === \"string\" ? parseTriplet(b) : b);\n const [hi, lo] = la > lb ? [la, lb] : [lb, la];\n return (hi + 0.05) / (lo + 0.05);\n}\n\n/** Moves lightness one point at a time (darker or lighter) until `ok`, within 0–100. */\nfunction walkLightness(start: Hsl, step: 1 | -1, ok: (c: Hsl) => boolean, limit: number): Hsl {\n let c = start;\n while (!ok(c) && (step > 0 ? c.l < limit : c.l > limit)) c = { ...c, l: c.l + step };\n return c;\n}\n\n// ── tokens ─────────────────────────────────────────────────────────────────────\n\nexport interface AccentTokens {\n primary: HslTriplet;\n primaryForeground: HslTriplet;\n primaryInk: HslTriplet;\n}\n\n/** The role tokens one color makes in one theme (the rule in the header of this file). */\nexport function accentTokens(hex: string, theme: AccentTheme): AccentTokens {\n const normalized = normalizeHex(hex) ?? ACCENTS[0]!.hex;\n const chosen = hexToHsl(normalized);\n const canvas = parseTriplet(ACCENT_CONTRAST_SURFACES[theme]);\n const primary =\n theme === \"light\" ? chosen : walkLightness(chosen, 1, (c) => contrastRatio(c, canvas) >= AA_NON_TEXT, 80);\n const onWhite = contrastRatio(primary, WHITE_TEXT);\n const onDark = contrastRatio(primary, DARK_TEXT);\n const ink =\n theme === \"light\"\n ? walkLightness(primary, -1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 10)\n : walkLightness(primary, 1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 92);\n return {\n primary: toTriplet(primary),\n primaryForeground: onWhite >= onDark ? WHITE_TEXT : DARK_TEXT,\n primaryInk: toTriplet(ink),\n };\n}\n\nexport function accentDef(id: unknown): AccentDef | null {\n return ACCENTS.find((a) => a.id === id) ?? null;\n}\n\n/** The color of a saved accent; null when it names no curated id and holds no valid hex. */\nexport function accentHex(ref: AccentRef | null | undefined): string | null {\n if (!ref) return null;\n if (\"id\" in ref) return accentDef(ref.id)?.hex ?? null;\n return normalizeHex(ref.hex);\n}\n\n/** Validates a saved accent: a curated id is kept, a hex is normalized, anything else → null. */\nexport function parseAccentRef(raw: unknown): AccentRef | null {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n if (typeof record.id === \"string\") return accentDef(record.id) ? { id: record.id } : null;\n const hex = normalizeHex(record.hex);\n return hex ? { hex } : null;\n}\n\n// ── the contrast warning (offers, never blocks) ───────────────────────────────\n\nexport interface AccentContrastCheck {\n theme: AccentTheme;\n /** Text on the accent (a primary button's label). AA: ≥ 4.5. */\n text: number;\n /** The accent against the canvas (a link, a ring, a selected tab). AA: ≥ 3. */\n canvas: number;\n ok: boolean;\n}\n\n/** WCAG AA of a color as an accent, in light and dark. A warning to show, never a refusal. */\nexport function accentContrast(hex: string): AccentContrastCheck[] {\n return ([\"light\", \"dark\"] as const).map((theme) => {\n const tokens = accentTokens(hex, theme);\n const text = contrastRatio(tokens.primary, tokens.primaryForeground);\n const canvas = contrastRatio(tokens.primary, ACCENT_CONTRAST_SURFACES[theme]);\n return { theme, text, canvas, ok: text >= AA_TEXT && canvas >= AA_NON_TEXT };\n });\n}\n\n/** One line (≤ 60 characters) for the first failing check; null when AA holds in both themes. */\nexport function accentContrastWarning(hex: string): string | null {\n const failing = accentContrast(hex).find((c) => !c.ok);\n if (!failing) return null;\n const mode = failing.theme === \"light\" ? \"light\" : \"dark\";\n if (failing.text < AA_TEXT) return `Hard to read in ${mode} mode: text ${failing.text.toFixed(1)}:1, AA is 4.5:1`;\n return `Faint in ${mode} mode: ${failing.canvas.toFixed(1)}:1, AA is 3:1`;\n}\n\n// ── what a host stamps ─────────────────────────────────────────────────────────\n\n/** The attribute `accent.css` reads; its value is the curated id or `custom`. */\nexport const ACCENT_ATTRIBUTE = \"data-matrx-accent\" as const;\n\n/** The six custom properties `accent.css` maps onto the role tokens. */\nexport const ACCENT_PROPERTIES = {\n light: { primary: \"--matrx-accent-primary\", primaryForeground: \"--matrx-accent-primary-foreground\", primaryInk: \"--matrx-accent-primary-ink\" },\n dark: { primary: \"--matrx-accent-dark-primary\", primaryForeground: \"--matrx-accent-dark-primary-foreground\", primaryInk: \"--matrx-accent-dark-primary-ink\" },\n} as const;\n\nexport interface AccentAttributes {\n [ACCENT_ATTRIBUTE]?: string;\n style?: Record<string, string>;\n}\n\n/**\n * What a host puts on its root element for an accent: the attribute and the six properties (light and\n * dark), so the server's first frame is already in the accent in either theme. No accent (or the\n * built-in blue) = nothing at all.\n */\nexport function accentAttributes(ref: AccentRef | null | undefined): AccentAttributes {\n const hex = accentHex(ref);\n if (!ref || !hex || (\"id\" in ref && ref.id === DEFAULT_ACCENT_ID)) return {};\n const style: Record<string, string> = {};\n for (const theme of [\"light\", \"dark\"] as const) {\n const tokens = accentTokens(hex, theme);\n const names = ACCENT_PROPERTIES[theme];\n style[names.primary] = tokens.primary;\n style[names.primaryForeground] = tokens.primaryForeground;\n style[names.primaryInk] = tokens.primaryInk;\n }\n return { [ACCENT_ATTRIBUTE]: \"id\" in ref ? ref.id : \"custom\", style };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/accents.ts"],"sourcesContent":["// THE ACCENT COLORS (custom-experience P4, 2026-10-20) — the curated accent set a person or an\n// organization picks from, the custom-hex path beside it, and the WCAG contrast math that WARNS about a\n// weak choice (it offers, never blocks). Pure: no React, no DOM, server-safe (`./accents` entry).\n//\n// An accent drives the design system's brand role tokens and nothing else:\n// --primary, --primary-foreground, --primary-ink (light, and their .dark twins)\n// `--accent` in this vocabulary is the quiet GRAY hover surface and is never touched; text tokens stay\n// foreground / muted / disabled. The host stamps `accentAttributes(ref)` on a root element (an attribute\n// plus six custom properties); `accent.css` maps them onto the role tokens for light and dark. With no\n// accent nothing is stamped and nothing changes.\n//\n// One rule makes both themes from ONE color (the same rule for a curated and a custom accent):\n// light primary = the color exactly as chosen;\n// dark primary = the same hue and saturation, lightened until it reads on the dark canvas (≥ 3:1);\n// foreground = white or near-black, whichever reads better on that primary;\n// ink = the primary as TEXT on its own tint: darkened (light) / lightened (dark) to ≥ 5.5:1\n// against the canvas (headroom for the 15% tint it sits on).\n\n/** An HSL color as the token vocabulary writes it: `\"210 80% 45%\"`. */\nexport type HslTriplet = string;\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\n/** One curated accent. `hex` is its light-theme primary; the dark twin is derived (see the header). */\nexport interface AccentDef {\n id: string;\n label: string;\n hex: string;\n}\n\n/**\n * THE CURATED SET. Every entry passes AA in both themes (guard: accents.test.ts): white or near-black\n * text on it ≥ 4.5:1, and it reads on the canvas ≥ 3:1. Ids are saved in layouts — never renamed.\n * `blue` is the built-in primary (`--primary: 210 80% 45%`), so choosing it equals choosing nothing.\n */\nexport const ACCENTS: readonly AccentDef[] = [\n { id: \"blue\", label: \"Blue\", hex: \"#1773cf\" },\n { id: \"indigo\", label: \"Indigo\", hex: \"#4f46e5\" },\n { id: \"violet\", label: \"Violet\", hex: \"#7c3aed\" },\n { id: \"purple\", label: \"Purple\", hex: \"#9333ea\" },\n { id: \"pink\", label: \"Pink\", hex: \"#db2777\" },\n { id: \"rose\", label: \"Rose\", hex: \"#e11d48\" },\n { id: \"red\", label: \"Red\", hex: \"#dc2626\" },\n { id: \"orange\", label: \"Orange\", hex: \"#c2410c\" },\n { id: \"amber\", label: \"Amber\", hex: \"#b45309\" },\n { id: \"green\", label: \"Green\", hex: \"#15803d\" },\n { id: \"emerald\", label: \"Emerald\", hex: \"#047857\" },\n { id: \"teal\", label: \"Teal\", hex: \"#0f766e\" },\n { id: \"cyan\", label: \"Cyan\", hex: \"#0e7490\" },\n { id: \"slate\", label: \"Slate\", hex: \"#475569\" },\n];\n\nexport const DEFAULT_ACCENT_ID = \"blue\";\n\n/**\n * The canvases a primary is judged against: the design system's `--background` in light and dark\n * (tokens.css; accents.test.ts fails if they drift apart).\n */\nexport const ACCENT_CONTRAST_SURFACES: Readonly<Record<AccentTheme, HslTriplet>> = {\n light: \"240 6% 99%\",\n dark: \"240 4% 12.5%\",\n};\n\n/** The dark text a light primary carries (tokens.css `--secondary-foreground`). */\nconst DARK_TEXT: HslTriplet = \"240 10% 10%\";\nconst WHITE_TEXT: HslTriplet = \"0 0% 100%\";\n\n/** WCAG 2.2 AA: 4.5:1 for text, 3:1 for a control's color against what is around it (1.4.11). */\nexport const AA_TEXT = 4.5;\nexport const AA_NON_TEXT = 3;\n/** The ink is judged against the canvas with headroom, because it sits on a 15% tint of itself. */\nconst INK_TARGET = 5.5;\n\nexport type AccentTheme = \"light\" | \"dark\";\n\n/** A saved accent: a curated id, or a custom `#rrggbb`. */\nexport type AccentRef = { id: string } | { hex: string };\n\n// ── color math ─────────────────────────────────────────────────────────────────\n\n/** `#rgb` / `#rrggbb` (any case, `#` optional) → `#rrggbb` lowercase; anything else → null. */\nexport function normalizeHex(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const raw = value.trim().replace(/^#/, \"\");\n if (/^[0-9a-f]{3}$/i.test(raw)) return `#${raw.split(\"\").map((c) => c + c).join(\"\").toLowerCase()}`;\n if (/^[0-9a-f]{6}$/i.test(raw)) return `#${raw.toLowerCase()}`;\n return null;\n}\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const n = Number.parseInt(hex.slice(1), 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\nexport function hexToHsl(hex: string): Hsl {\n const [r, g, b] = hexToRgb(hex).map((v) => v / 255) as [number, number, number];\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n const d = max - min;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n if (max === r) h = ((g - b) / d) % 6;\n else if (max === g) h = (b - r) / d + 2;\n else h = (r - g) / d + 4;\n h *= 60;\n if (h < 0) h += 360;\n }\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nfunction hslToRgb({ h, s, l }: Hsl): [number, number, number] {\n const sat = s / 100;\n const lig = l / 100;\n const c = (1 - Math.abs(2 * lig - 1)) * sat;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = lig - c / 2;\n const [r, g, b] =\n h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] : h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];\n return [(r + m) * 255, (g + m) * 255, (b + m) * 255];\n}\n\nexport function parseTriplet(triplet: HslTriplet): Hsl {\n const [h, s, l] = triplet.replace(/%/g, \"\").trim().split(/\\s+/).map(Number);\n return { h: h ?? 0, s: s ?? 0, l: l ?? 0 };\n}\n\nexport function toTriplet({ h, s, l }: Hsl): HslTriplet {\n return `${h} ${s}% ${l}%`;\n}\n\n/** WCAG relative luminance of an HSL color. */\nexport function relativeLuminance(color: Hsl): number {\n const [r, g, b] = hslToRgb(color).map((v) => {\n const c = v / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio of two colors (1 to 21). */\nexport function contrastRatio(a: Hsl | HslTriplet, b: Hsl | HslTriplet): number {\n const la = relativeLuminance(typeof a === \"string\" ? parseTriplet(a) : a);\n const lb = relativeLuminance(typeof b === \"string\" ? parseTriplet(b) : b);\n const [hi, lo] = la > lb ? [la, lb] : [lb, la];\n return (hi + 0.05) / (lo + 0.05);\n}\n\n/** Moves lightness one point at a time (darker or lighter) until `ok`, within 0–100. */\nfunction walkLightness(start: Hsl, step: 1 | -1, ok: (c: Hsl) => boolean, limit: number): Hsl {\n let c = start;\n while (!ok(c) && (step > 0 ? c.l < limit : c.l > limit)) c = { ...c, l: c.l + step };\n return c;\n}\n\n// ── tokens ─────────────────────────────────────────────────────────────────────\n\nexport interface AccentTokens {\n primary: HslTriplet;\n primaryForeground: HslTriplet;\n primaryInk: HslTriplet;\n}\n\n/** The role tokens one color makes in one theme (the rule in the header of this file). */\nexport function accentTokens(hex: string, theme: AccentTheme): AccentTokens {\n const normalized = normalizeHex(hex) ?? ACCENTS[0]!.hex;\n const chosen = hexToHsl(normalized);\n const canvas = parseTriplet(ACCENT_CONTRAST_SURFACES[theme]);\n const primary =\n theme === \"light\" ? chosen : walkLightness(chosen, 1, (c) => contrastRatio(c, canvas) >= AA_NON_TEXT, 80);\n const onWhite = contrastRatio(primary, WHITE_TEXT);\n const onDark = contrastRatio(primary, DARK_TEXT);\n const ink =\n theme === \"light\"\n ? walkLightness(primary, -1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 10)\n : walkLightness(primary, 1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 92);\n return {\n primary: toTriplet(primary),\n primaryForeground: onWhite >= onDark ? WHITE_TEXT : DARK_TEXT,\n primaryInk: toTriplet(ink),\n };\n}\n\nexport function accentDef(id: unknown): AccentDef | null {\n return ACCENTS.find((a) => a.id === id) ?? null;\n}\n\n/** The color of a saved accent; null when it names no curated id and holds no valid hex. */\nexport function accentHex(ref: AccentRef | null | undefined): string | null {\n if (!ref) return null;\n if (\"id\" in ref) return accentDef(ref.id)?.hex ?? null;\n return normalizeHex(ref.hex);\n}\n\n/** Validates a saved accent: a curated id is kept, a hex is normalized, anything else → null. */\nexport function parseAccentRef(raw: unknown): AccentRef | null {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n if (typeof record.id === \"string\") return accentDef(record.id) ? { id: record.id } : null;\n const hex = normalizeHex(record.hex);\n return hex ? { hex } : null;\n}\n\n// ── the contrast warning (offers, never blocks) ───────────────────────────────\n\nexport interface AccentContrastCheck {\n theme: AccentTheme;\n /** Text on the accent (a primary button's label). AA: ≥ 4.5. */\n text: number;\n /** The accent against the canvas (a link, a ring, a selected tab). AA: ≥ 3. */\n canvas: number;\n ok: boolean;\n}\n\n/** WCAG AA of a color as an accent, in light and dark. A warning to show, never a refusal. */\nexport function accentContrast(hex: string): AccentContrastCheck[] {\n return ([\"light\", \"dark\"] as const).map((theme) => {\n const tokens = accentTokens(hex, theme);\n const text = contrastRatio(tokens.primary, tokens.primaryForeground);\n const canvas = contrastRatio(tokens.primary, ACCENT_CONTRAST_SURFACES[theme]);\n return { theme, text, canvas, ok: text >= AA_TEXT && canvas >= AA_NON_TEXT };\n });\n}\n\n/** One line (≤ 60 characters) for the first failing check; null when AA holds in both themes. */\nexport function accentContrastWarning(hex: string): string | null {\n const failing = accentContrast(hex).find((c) => !c.ok);\n if (!failing) return null;\n const mode = failing.theme === \"light\" ? \"light\" : \"dark\";\n if (failing.text < AA_TEXT) return `Hard to read in ${mode} mode: text ${failing.text.toFixed(1)}:1, AA is 4.5:1`;\n return `Faint in ${mode} mode: ${failing.canvas.toFixed(1)}:1, AA is 3:1`;\n}\n\n// ── what a host stamps ─────────────────────────────────────────────────────────\n\n/** The attribute `accent.css` reads; its value is the curated id or `custom`. */\nexport const ACCENT_ATTRIBUTE = \"data-matrx-accent\" as const;\n\n/** The six custom properties `accent.css` maps onto the role tokens. */\nexport const ACCENT_PROPERTIES = {\n light: { primary: \"--matrx-accent-primary\", primaryForeground: \"--matrx-accent-primary-foreground\", primaryInk: \"--matrx-accent-primary-ink\" },\n dark: { primary: \"--matrx-accent-dark-primary\", primaryForeground: \"--matrx-accent-dark-primary-foreground\", primaryInk: \"--matrx-accent-dark-primary-ink\" },\n} as const;\n\nexport interface AccentAttributes {\n [ACCENT_ATTRIBUTE]?: string;\n style?: Record<string, string>;\n}\n\n/**\n * What a host puts on its root element for an accent: the attribute and the six properties (light and\n * dark), so the server's first frame is already in the accent in either theme. No accent (or the\n * built-in blue) = nothing at all.\n */\nexport function accentAttributes(ref: AccentRef | null | undefined): AccentAttributes {\n const hex = accentHex(ref);\n if (!ref || !hex || (\"id\" in ref && ref.id === DEFAULT_ACCENT_ID)) return {};\n const style: Record<string, string> = {};\n for (const theme of [\"light\", \"dark\"] as const) {\n const tokens = accentTokens(hex, theme);\n const names = ACCENT_PROPERTIES[theme];\n style[names.primary] = tokens.primary;\n style[names.primaryForeground] = tokens.primaryForeground;\n style[names.primaryInk] = tokens.primaryInk;\n }\n return { [ACCENT_ATTRIBUTE]: \"id\" in ref ? ref.id : \"custom\", style };\n}\n"],"mappings":"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","names":[]}
|
package/dist/accents.js
CHANGED
|
@@ -17,8 +17,8 @@ var ACCENTS = [
|
|
|
17
17
|
];
|
|
18
18
|
var DEFAULT_ACCENT_ID = "blue";
|
|
19
19
|
var ACCENT_CONTRAST_SURFACES = {
|
|
20
|
-
light: "240
|
|
21
|
-
dark: "240 4%
|
|
20
|
+
light: "240 6% 99%",
|
|
21
|
+
dark: "240 4% 12.5%"
|
|
22
22
|
};
|
|
23
23
|
var DARK_TEXT = "240 10% 10%";
|
|
24
24
|
var WHITE_TEXT = "0 0% 100%";
|
package/dist/accents.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/accents.ts"],"sourcesContent":["// THE ACCENT COLORS (custom-experience P4, 2026-10-20) — the curated accent set a person or an\n// organization picks from, the custom-hex path beside it, and the WCAG contrast math that WARNS about a\n// weak choice (it offers, never blocks). Pure: no React, no DOM, server-safe (`./accents` entry).\n//\n// An accent drives the design system's brand role tokens and nothing else:\n// --primary, --primary-foreground, --primary-ink (light, and their .dark twins)\n// `--accent` in this vocabulary is the quiet GRAY hover surface and is never touched; text tokens stay\n// foreground / muted / disabled. The host stamps `accentAttributes(ref)` on a root element (an attribute\n// plus six custom properties); `accent.css` maps them onto the role tokens for light and dark. With no\n// accent nothing is stamped and nothing changes.\n//\n// One rule makes both themes from ONE color (the same rule for a curated and a custom accent):\n// light primary = the color exactly as chosen;\n// dark primary = the same hue and saturation, lightened until it reads on the dark canvas (≥ 3:1);\n// foreground = white or near-black, whichever reads better on that primary;\n// ink = the primary as TEXT on its own tint: darkened (light) / lightened (dark) to ≥ 5.5:1\n// against the canvas (headroom for the 15% tint it sits on).\n\n/** An HSL color as the token vocabulary writes it: `\"210 80% 45%\"`. */\nexport type HslTriplet = string;\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\n/** One curated accent. `hex` is its light-theme primary; the dark twin is derived (see the header). */\nexport interface AccentDef {\n id: string;\n label: string;\n hex: string;\n}\n\n/**\n * THE CURATED SET. Every entry passes AA in both themes (guard: accents.test.ts): white or near-black\n * text on it ≥ 4.5:1, and it reads on the canvas ≥ 3:1. Ids are saved in layouts — never renamed.\n * `blue` is the built-in primary (`--primary: 210 80% 45%`), so choosing it equals choosing nothing.\n */\nexport const ACCENTS: readonly AccentDef[] = [\n { id: \"blue\", label: \"Blue\", hex: \"#1773cf\" },\n { id: \"indigo\", label: \"Indigo\", hex: \"#4f46e5\" },\n { id: \"violet\", label: \"Violet\", hex: \"#7c3aed\" },\n { id: \"purple\", label: \"Purple\", hex: \"#9333ea\" },\n { id: \"pink\", label: \"Pink\", hex: \"#db2777\" },\n { id: \"rose\", label: \"Rose\", hex: \"#e11d48\" },\n { id: \"red\", label: \"Red\", hex: \"#dc2626\" },\n { id: \"orange\", label: \"Orange\", hex: \"#c2410c\" },\n { id: \"amber\", label: \"Amber\", hex: \"#b45309\" },\n { id: \"green\", label: \"Green\", hex: \"#15803d\" },\n { id: \"emerald\", label: \"Emerald\", hex: \"#047857\" },\n { id: \"teal\", label: \"Teal\", hex: \"#0f766e\" },\n { id: \"cyan\", label: \"Cyan\", hex: \"#0e7490\" },\n { id: \"slate\", label: \"Slate\", hex: \"#475569\" },\n];\n\nexport const DEFAULT_ACCENT_ID = \"blue\";\n\n/**\n * The canvases a primary is judged against: the design system's `--background` in light and dark\n * (tokens.css; accents.test.ts fails if they drift apart).\n */\nexport const ACCENT_CONTRAST_SURFACES: Readonly<Record<AccentTheme, HslTriplet>> = {\n light: \"240 5% 96%\",\n dark: \"240 4% 16%\",\n};\n\n/** The dark text a light primary carries (tokens.css `--secondary-foreground`). */\nconst DARK_TEXT: HslTriplet = \"240 10% 10%\";\nconst WHITE_TEXT: HslTriplet = \"0 0% 100%\";\n\n/** WCAG 2.2 AA: 4.5:1 for text, 3:1 for a control's color against what is around it (1.4.11). */\nexport const AA_TEXT = 4.5;\nexport const AA_NON_TEXT = 3;\n/** The ink is judged against the canvas with headroom, because it sits on a 15% tint of itself. */\nconst INK_TARGET = 5.5;\n\nexport type AccentTheme = \"light\" | \"dark\";\n\n/** A saved accent: a curated id, or a custom `#rrggbb`. */\nexport type AccentRef = { id: string } | { hex: string };\n\n// ── color math ─────────────────────────────────────────────────────────────────\n\n/** `#rgb` / `#rrggbb` (any case, `#` optional) → `#rrggbb` lowercase; anything else → null. */\nexport function normalizeHex(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const raw = value.trim().replace(/^#/, \"\");\n if (/^[0-9a-f]{3}$/i.test(raw)) return `#${raw.split(\"\").map((c) => c + c).join(\"\").toLowerCase()}`;\n if (/^[0-9a-f]{6}$/i.test(raw)) return `#${raw.toLowerCase()}`;\n return null;\n}\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const n = Number.parseInt(hex.slice(1), 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\nexport function hexToHsl(hex: string): Hsl {\n const [r, g, b] = hexToRgb(hex).map((v) => v / 255) as [number, number, number];\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n const d = max - min;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n if (max === r) h = ((g - b) / d) % 6;\n else if (max === g) h = (b - r) / d + 2;\n else h = (r - g) / d + 4;\n h *= 60;\n if (h < 0) h += 360;\n }\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nfunction hslToRgb({ h, s, l }: Hsl): [number, number, number] {\n const sat = s / 100;\n const lig = l / 100;\n const c = (1 - Math.abs(2 * lig - 1)) * sat;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = lig - c / 2;\n const [r, g, b] =\n h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] : h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];\n return [(r + m) * 255, (g + m) * 255, (b + m) * 255];\n}\n\nexport function parseTriplet(triplet: HslTriplet): Hsl {\n const [h, s, l] = triplet.replace(/%/g, \"\").trim().split(/\\s+/).map(Number);\n return { h: h ?? 0, s: s ?? 0, l: l ?? 0 };\n}\n\nexport function toTriplet({ h, s, l }: Hsl): HslTriplet {\n return `${h} ${s}% ${l}%`;\n}\n\n/** WCAG relative luminance of an HSL color. */\nexport function relativeLuminance(color: Hsl): number {\n const [r, g, b] = hslToRgb(color).map((v) => {\n const c = v / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio of two colors (1 to 21). */\nexport function contrastRatio(a: Hsl | HslTriplet, b: Hsl | HslTriplet): number {\n const la = relativeLuminance(typeof a === \"string\" ? parseTriplet(a) : a);\n const lb = relativeLuminance(typeof b === \"string\" ? parseTriplet(b) : b);\n const [hi, lo] = la > lb ? [la, lb] : [lb, la];\n return (hi + 0.05) / (lo + 0.05);\n}\n\n/** Moves lightness one point at a time (darker or lighter) until `ok`, within 0–100. */\nfunction walkLightness(start: Hsl, step: 1 | -1, ok: (c: Hsl) => boolean, limit: number): Hsl {\n let c = start;\n while (!ok(c) && (step > 0 ? c.l < limit : c.l > limit)) c = { ...c, l: c.l + step };\n return c;\n}\n\n// ── tokens ─────────────────────────────────────────────────────────────────────\n\nexport interface AccentTokens {\n primary: HslTriplet;\n primaryForeground: HslTriplet;\n primaryInk: HslTriplet;\n}\n\n/** The role tokens one color makes in one theme (the rule in the header of this file). */\nexport function accentTokens(hex: string, theme: AccentTheme): AccentTokens {\n const normalized = normalizeHex(hex) ?? ACCENTS[0]!.hex;\n const chosen = hexToHsl(normalized);\n const canvas = parseTriplet(ACCENT_CONTRAST_SURFACES[theme]);\n const primary =\n theme === \"light\" ? chosen : walkLightness(chosen, 1, (c) => contrastRatio(c, canvas) >= AA_NON_TEXT, 80);\n const onWhite = contrastRatio(primary, WHITE_TEXT);\n const onDark = contrastRatio(primary, DARK_TEXT);\n const ink =\n theme === \"light\"\n ? walkLightness(primary, -1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 10)\n : walkLightness(primary, 1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 92);\n return {\n primary: toTriplet(primary),\n primaryForeground: onWhite >= onDark ? WHITE_TEXT : DARK_TEXT,\n primaryInk: toTriplet(ink),\n };\n}\n\nexport function accentDef(id: unknown): AccentDef | null {\n return ACCENTS.find((a) => a.id === id) ?? null;\n}\n\n/** The color of a saved accent; null when it names no curated id and holds no valid hex. */\nexport function accentHex(ref: AccentRef | null | undefined): string | null {\n if (!ref) return null;\n if (\"id\" in ref) return accentDef(ref.id)?.hex ?? null;\n return normalizeHex(ref.hex);\n}\n\n/** Validates a saved accent: a curated id is kept, a hex is normalized, anything else → null. */\nexport function parseAccentRef(raw: unknown): AccentRef | null {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n if (typeof record.id === \"string\") return accentDef(record.id) ? { id: record.id } : null;\n const hex = normalizeHex(record.hex);\n return hex ? { hex } : null;\n}\n\n// ── the contrast warning (offers, never blocks) ───────────────────────────────\n\nexport interface AccentContrastCheck {\n theme: AccentTheme;\n /** Text on the accent (a primary button's label). AA: ≥ 4.5. */\n text: number;\n /** The accent against the canvas (a link, a ring, a selected tab). AA: ≥ 3. */\n canvas: number;\n ok: boolean;\n}\n\n/** WCAG AA of a color as an accent, in light and dark. A warning to show, never a refusal. */\nexport function accentContrast(hex: string): AccentContrastCheck[] {\n return ([\"light\", \"dark\"] as const).map((theme) => {\n const tokens = accentTokens(hex, theme);\n const text = contrastRatio(tokens.primary, tokens.primaryForeground);\n const canvas = contrastRatio(tokens.primary, ACCENT_CONTRAST_SURFACES[theme]);\n return { theme, text, canvas, ok: text >= AA_TEXT && canvas >= AA_NON_TEXT };\n });\n}\n\n/** One line (≤ 60 characters) for the first failing check; null when AA holds in both themes. */\nexport function accentContrastWarning(hex: string): string | null {\n const failing = accentContrast(hex).find((c) => !c.ok);\n if (!failing) return null;\n const mode = failing.theme === \"light\" ? \"light\" : \"dark\";\n if (failing.text < AA_TEXT) return `Hard to read in ${mode} mode: text ${failing.text.toFixed(1)}:1, AA is 4.5:1`;\n return `Faint in ${mode} mode: ${failing.canvas.toFixed(1)}:1, AA is 3:1`;\n}\n\n// ── what a host stamps ─────────────────────────────────────────────────────────\n\n/** The attribute `accent.css` reads; its value is the curated id or `custom`. */\nexport const ACCENT_ATTRIBUTE = \"data-matrx-accent\" as const;\n\n/** The six custom properties `accent.css` maps onto the role tokens. */\nexport const ACCENT_PROPERTIES = {\n light: { primary: \"--matrx-accent-primary\", primaryForeground: \"--matrx-accent-primary-foreground\", primaryInk: \"--matrx-accent-primary-ink\" },\n dark: { primary: \"--matrx-accent-dark-primary\", primaryForeground: \"--matrx-accent-dark-primary-foreground\", primaryInk: \"--matrx-accent-dark-primary-ink\" },\n} as const;\n\nexport interface AccentAttributes {\n [ACCENT_ATTRIBUTE]?: string;\n style?: Record<string, string>;\n}\n\n/**\n * What a host puts on its root element for an accent: the attribute and the six properties (light and\n * dark), so the server's first frame is already in the accent in either theme. No accent (or the\n * built-in blue) = nothing at all.\n */\nexport function accentAttributes(ref: AccentRef | null | undefined): AccentAttributes {\n const hex = accentHex(ref);\n if (!ref || !hex || (\"id\" in ref && ref.id === DEFAULT_ACCENT_ID)) return {};\n const style: Record<string, string> = {};\n for (const theme of [\"light\", \"dark\"] as const) {\n const tokens = accentTokens(hex, theme);\n const names = ACCENT_PROPERTIES[theme];\n style[names.primary] = tokens.primary;\n style[names.primaryForeground] = tokens.primaryForeground;\n style[names.primaryInk] = tokens.primaryInk;\n }\n return { [ACCENT_ATTRIBUTE]: \"id\" in ref ? ref.id : \"custom\", style };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/accents.ts"],"sourcesContent":["// THE ACCENT COLORS (custom-experience P4, 2026-10-20) — the curated accent set a person or an\n// organization picks from, the custom-hex path beside it, and the WCAG contrast math that WARNS about a\n// weak choice (it offers, never blocks). Pure: no React, no DOM, server-safe (`./accents` entry).\n//\n// An accent drives the design system's brand role tokens and nothing else:\n// --primary, --primary-foreground, --primary-ink (light, and their .dark twins)\n// `--accent` in this vocabulary is the quiet GRAY hover surface and is never touched; text tokens stay\n// foreground / muted / disabled. The host stamps `accentAttributes(ref)` on a root element (an attribute\n// plus six custom properties); `accent.css` maps them onto the role tokens for light and dark. With no\n// accent nothing is stamped and nothing changes.\n//\n// One rule makes both themes from ONE color (the same rule for a curated and a custom accent):\n// light primary = the color exactly as chosen;\n// dark primary = the same hue and saturation, lightened until it reads on the dark canvas (≥ 3:1);\n// foreground = white or near-black, whichever reads better on that primary;\n// ink = the primary as TEXT on its own tint: darkened (light) / lightened (dark) to ≥ 5.5:1\n// against the canvas (headroom for the 15% tint it sits on).\n\n/** An HSL color as the token vocabulary writes it: `\"210 80% 45%\"`. */\nexport type HslTriplet = string;\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\n/** One curated accent. `hex` is its light-theme primary; the dark twin is derived (see the header). */\nexport interface AccentDef {\n id: string;\n label: string;\n hex: string;\n}\n\n/**\n * THE CURATED SET. Every entry passes AA in both themes (guard: accents.test.ts): white or near-black\n * text on it ≥ 4.5:1, and it reads on the canvas ≥ 3:1. Ids are saved in layouts — never renamed.\n * `blue` is the built-in primary (`--primary: 210 80% 45%`), so choosing it equals choosing nothing.\n */\nexport const ACCENTS: readonly AccentDef[] = [\n { id: \"blue\", label: \"Blue\", hex: \"#1773cf\" },\n { id: \"indigo\", label: \"Indigo\", hex: \"#4f46e5\" },\n { id: \"violet\", label: \"Violet\", hex: \"#7c3aed\" },\n { id: \"purple\", label: \"Purple\", hex: \"#9333ea\" },\n { id: \"pink\", label: \"Pink\", hex: \"#db2777\" },\n { id: \"rose\", label: \"Rose\", hex: \"#e11d48\" },\n { id: \"red\", label: \"Red\", hex: \"#dc2626\" },\n { id: \"orange\", label: \"Orange\", hex: \"#c2410c\" },\n { id: \"amber\", label: \"Amber\", hex: \"#b45309\" },\n { id: \"green\", label: \"Green\", hex: \"#15803d\" },\n { id: \"emerald\", label: \"Emerald\", hex: \"#047857\" },\n { id: \"teal\", label: \"Teal\", hex: \"#0f766e\" },\n { id: \"cyan\", label: \"Cyan\", hex: \"#0e7490\" },\n { id: \"slate\", label: \"Slate\", hex: \"#475569\" },\n];\n\nexport const DEFAULT_ACCENT_ID = \"blue\";\n\n/**\n * The canvases a primary is judged against: the design system's `--background` in light and dark\n * (tokens.css; accents.test.ts fails if they drift apart).\n */\nexport const ACCENT_CONTRAST_SURFACES: Readonly<Record<AccentTheme, HslTriplet>> = {\n light: \"240 6% 99%\",\n dark: \"240 4% 12.5%\",\n};\n\n/** The dark text a light primary carries (tokens.css `--secondary-foreground`). */\nconst DARK_TEXT: HslTriplet = \"240 10% 10%\";\nconst WHITE_TEXT: HslTriplet = \"0 0% 100%\";\n\n/** WCAG 2.2 AA: 4.5:1 for text, 3:1 for a control's color against what is around it (1.4.11). */\nexport const AA_TEXT = 4.5;\nexport const AA_NON_TEXT = 3;\n/** The ink is judged against the canvas with headroom, because it sits on a 15% tint of itself. */\nconst INK_TARGET = 5.5;\n\nexport type AccentTheme = \"light\" | \"dark\";\n\n/** A saved accent: a curated id, or a custom `#rrggbb`. */\nexport type AccentRef = { id: string } | { hex: string };\n\n// ── color math ─────────────────────────────────────────────────────────────────\n\n/** `#rgb` / `#rrggbb` (any case, `#` optional) → `#rrggbb` lowercase; anything else → null. */\nexport function normalizeHex(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const raw = value.trim().replace(/^#/, \"\");\n if (/^[0-9a-f]{3}$/i.test(raw)) return `#${raw.split(\"\").map((c) => c + c).join(\"\").toLowerCase()}`;\n if (/^[0-9a-f]{6}$/i.test(raw)) return `#${raw.toLowerCase()}`;\n return null;\n}\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const n = Number.parseInt(hex.slice(1), 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\nexport function hexToHsl(hex: string): Hsl {\n const [r, g, b] = hexToRgb(hex).map((v) => v / 255) as [number, number, number];\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n const d = max - min;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n if (max === r) h = ((g - b) / d) % 6;\n else if (max === g) h = (b - r) / d + 2;\n else h = (r - g) / d + 4;\n h *= 60;\n if (h < 0) h += 360;\n }\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nfunction hslToRgb({ h, s, l }: Hsl): [number, number, number] {\n const sat = s / 100;\n const lig = l / 100;\n const c = (1 - Math.abs(2 * lig - 1)) * sat;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = lig - c / 2;\n const [r, g, b] =\n h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] : h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];\n return [(r + m) * 255, (g + m) * 255, (b + m) * 255];\n}\n\nexport function parseTriplet(triplet: HslTriplet): Hsl {\n const [h, s, l] = triplet.replace(/%/g, \"\").trim().split(/\\s+/).map(Number);\n return { h: h ?? 0, s: s ?? 0, l: l ?? 0 };\n}\n\nexport function toTriplet({ h, s, l }: Hsl): HslTriplet {\n return `${h} ${s}% ${l}%`;\n}\n\n/** WCAG relative luminance of an HSL color. */\nexport function relativeLuminance(color: Hsl): number {\n const [r, g, b] = hslToRgb(color).map((v) => {\n const c = v / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio of two colors (1 to 21). */\nexport function contrastRatio(a: Hsl | HslTriplet, b: Hsl | HslTriplet): number {\n const la = relativeLuminance(typeof a === \"string\" ? parseTriplet(a) : a);\n const lb = relativeLuminance(typeof b === \"string\" ? parseTriplet(b) : b);\n const [hi, lo] = la > lb ? [la, lb] : [lb, la];\n return (hi + 0.05) / (lo + 0.05);\n}\n\n/** Moves lightness one point at a time (darker or lighter) until `ok`, within 0–100. */\nfunction walkLightness(start: Hsl, step: 1 | -1, ok: (c: Hsl) => boolean, limit: number): Hsl {\n let c = start;\n while (!ok(c) && (step > 0 ? c.l < limit : c.l > limit)) c = { ...c, l: c.l + step };\n return c;\n}\n\n// ── tokens ─────────────────────────────────────────────────────────────────────\n\nexport interface AccentTokens {\n primary: HslTriplet;\n primaryForeground: HslTriplet;\n primaryInk: HslTriplet;\n}\n\n/** The role tokens one color makes in one theme (the rule in the header of this file). */\nexport function accentTokens(hex: string, theme: AccentTheme): AccentTokens {\n const normalized = normalizeHex(hex) ?? ACCENTS[0]!.hex;\n const chosen = hexToHsl(normalized);\n const canvas = parseTriplet(ACCENT_CONTRAST_SURFACES[theme]);\n const primary =\n theme === \"light\" ? chosen : walkLightness(chosen, 1, (c) => contrastRatio(c, canvas) >= AA_NON_TEXT, 80);\n const onWhite = contrastRatio(primary, WHITE_TEXT);\n const onDark = contrastRatio(primary, DARK_TEXT);\n const ink =\n theme === \"light\"\n ? walkLightness(primary, -1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 10)\n : walkLightness(primary, 1, (c) => contrastRatio(c, canvas) >= INK_TARGET, 92);\n return {\n primary: toTriplet(primary),\n primaryForeground: onWhite >= onDark ? WHITE_TEXT : DARK_TEXT,\n primaryInk: toTriplet(ink),\n };\n}\n\nexport function accentDef(id: unknown): AccentDef | null {\n return ACCENTS.find((a) => a.id === id) ?? null;\n}\n\n/** The color of a saved accent; null when it names no curated id and holds no valid hex. */\nexport function accentHex(ref: AccentRef | null | undefined): string | null {\n if (!ref) return null;\n if (\"id\" in ref) return accentDef(ref.id)?.hex ?? null;\n return normalizeHex(ref.hex);\n}\n\n/** Validates a saved accent: a curated id is kept, a hex is normalized, anything else → null. */\nexport function parseAccentRef(raw: unknown): AccentRef | null {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n if (typeof record.id === \"string\") return accentDef(record.id) ? { id: record.id } : null;\n const hex = normalizeHex(record.hex);\n return hex ? { hex } : null;\n}\n\n// ── the contrast warning (offers, never blocks) ───────────────────────────────\n\nexport interface AccentContrastCheck {\n theme: AccentTheme;\n /** Text on the accent (a primary button's label). AA: ≥ 4.5. */\n text: number;\n /** The accent against the canvas (a link, a ring, a selected tab). AA: ≥ 3. */\n canvas: number;\n ok: boolean;\n}\n\n/** WCAG AA of a color as an accent, in light and dark. A warning to show, never a refusal. */\nexport function accentContrast(hex: string): AccentContrastCheck[] {\n return ([\"light\", \"dark\"] as const).map((theme) => {\n const tokens = accentTokens(hex, theme);\n const text = contrastRatio(tokens.primary, tokens.primaryForeground);\n const canvas = contrastRatio(tokens.primary, ACCENT_CONTRAST_SURFACES[theme]);\n return { theme, text, canvas, ok: text >= AA_TEXT && canvas >= AA_NON_TEXT };\n });\n}\n\n/** One line (≤ 60 characters) for the first failing check; null when AA holds in both themes. */\nexport function accentContrastWarning(hex: string): string | null {\n const failing = accentContrast(hex).find((c) => !c.ok);\n if (!failing) return null;\n const mode = failing.theme === \"light\" ? \"light\" : \"dark\";\n if (failing.text < AA_TEXT) return `Hard to read in ${mode} mode: text ${failing.text.toFixed(1)}:1, AA is 4.5:1`;\n return `Faint in ${mode} mode: ${failing.canvas.toFixed(1)}:1, AA is 3:1`;\n}\n\n// ── what a host stamps ─────────────────────────────────────────────────────────\n\n/** The attribute `accent.css` reads; its value is the curated id or `custom`. */\nexport const ACCENT_ATTRIBUTE = \"data-matrx-accent\" as const;\n\n/** The six custom properties `accent.css` maps onto the role tokens. */\nexport const ACCENT_PROPERTIES = {\n light: { primary: \"--matrx-accent-primary\", primaryForeground: \"--matrx-accent-primary-foreground\", primaryInk: \"--matrx-accent-primary-ink\" },\n dark: { primary: \"--matrx-accent-dark-primary\", primaryForeground: \"--matrx-accent-dark-primary-foreground\", primaryInk: \"--matrx-accent-dark-primary-ink\" },\n} as const;\n\nexport interface AccentAttributes {\n [ACCENT_ATTRIBUTE]?: string;\n style?: Record<string, string>;\n}\n\n/**\n * What a host puts on its root element for an accent: the attribute and the six properties (light and\n * dark), so the server's first frame is already in the accent in either theme. No accent (or the\n * built-in blue) = nothing at all.\n */\nexport function accentAttributes(ref: AccentRef | null | undefined): AccentAttributes {\n const hex = accentHex(ref);\n if (!ref || !hex || (\"id\" in ref && ref.id === DEFAULT_ACCENT_ID)) return {};\n const style: Record<string, string> = {};\n for (const theme of [\"light\", \"dark\"] as const) {\n const tokens = accentTokens(hex, theme);\n const names = ACCENT_PROPERTIES[theme];\n style[names.primary] = tokens.primary;\n style[names.primaryForeground] = tokens.primaryForeground;\n style[names.primaryInk] = tokens.primaryInk;\n }\n return { [ACCENT_ATTRIBUTE]: \"id\" in ref ? ref.id : \"custom\", style };\n}\n"],"mappings":"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","names":[]}
|
package/dist/carousel.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
IconButton
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-U3SUPYNT.js";
|
|
5
5
|
import "./chunk-6NOLVA4E.js";
|
|
6
6
|
import "./chunk-5IR3BZOS.js";
|
|
7
7
|
import "./chunk-AV5C4YLL.js";
|
|
8
8
|
import "./chunk-XERGQT2M.js";
|
|
9
9
|
import "./chunk-WQ2QCMMA.js";
|
|
10
10
|
import "./chunk-YRF22M5S.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-CFFRMRWJ.js";
|
|
12
12
|
import "./chunk-K4XCB576.js";
|
|
13
13
|
import "./chunk-FEITX4L3.js";
|
|
14
14
|
import "./chunk-DRSBLHOF.js";
|
|
@@ -20,16 +20,16 @@ import {
|
|
|
20
20
|
import "./chunk-IMQ3RX35.js";
|
|
21
21
|
import "./chunk-U24MOJWK.js";
|
|
22
22
|
import "./chunk-NDTDBBMM.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-YA3DNAAC.js";
|
|
24
24
|
import "./chunk-WFBPCMPF.js";
|
|
25
25
|
import {
|
|
26
26
|
Popover,
|
|
27
27
|
PopoverContent,
|
|
28
28
|
PopoverTrigger
|
|
29
29
|
} from "./chunk-OPF265PU.js";
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-BD77DHUO.js";
|
|
30
|
+
import "./chunk-345ESUIP.js";
|
|
32
31
|
import "./chunk-BKCJUFD3.js";
|
|
32
|
+
import "./chunk-BD77DHUO.js";
|
|
33
33
|
import {
|
|
34
34
|
TooltipProvider
|
|
35
35
|
} from "./chunk-PIQJULQB.js";
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
} from "./chunk-WFBPCMPF.js";
|
|
38
38
|
import {
|
|
39
39
|
Button
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-345ESUIP.js";
|
|
41
41
|
import {
|
|
42
42
|
DropdownMenu,
|
|
43
43
|
DropdownMenuTrigger
|
|
@@ -967,4 +967,4 @@ export {
|
|
|
967
967
|
ItemContextMenu,
|
|
968
968
|
ItemRow
|
|
969
969
|
};
|
|
970
|
-
//# sourceMappingURL=chunk-
|
|
970
|
+
//# sourceMappingURL=chunk-2HB65PD6.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
isIconOnly
|
|
4
|
+
} from "./chunk-BKCJUFD3.js";
|
|
2
5
|
import {
|
|
3
6
|
boxStyle,
|
|
4
7
|
useAutoGrow,
|
|
5
8
|
useMergedTextareaRef
|
|
6
9
|
} from "./chunk-BD77DHUO.js";
|
|
7
|
-
import {
|
|
8
|
-
isIconOnly
|
|
9
|
-
} from "./chunk-BKCJUFD3.js";
|
|
10
10
|
import {
|
|
11
11
|
DropdownMenu,
|
|
12
12
|
DropdownMenuContent,
|
|
@@ -463,4 +463,4 @@ export {
|
|
|
463
463
|
Switch,
|
|
464
464
|
Textarea
|
|
465
465
|
};
|
|
466
|
-
//# sourceMappingURL=chunk-
|
|
466
|
+
//# sourceMappingURL=chunk-345ESUIP.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
Select
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-345ESUIP.js";
|
|
6
6
|
import {
|
|
7
7
|
Tooltip,
|
|
8
8
|
TooltipContent,
|
|
@@ -367,4 +367,4 @@ export {
|
|
|
367
367
|
DEFAULT_TABLE_PAGE_SIZES,
|
|
368
368
|
GenericTablePagination
|
|
369
369
|
};
|
|
370
|
-
//# sourceMappingURL=chunk-
|
|
370
|
+
//# sourceMappingURL=chunk-ASDPMZNP.js.map
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
Button,
|
|
10
10
|
ControlRow
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-345ESUIP.js";
|
|
12
12
|
import {
|
|
13
13
|
buttonVariants,
|
|
14
14
|
swallowClick
|
|
@@ -482,4 +482,4 @@ export {
|
|
|
482
482
|
DangerZone,
|
|
483
483
|
PromoBanner
|
|
484
484
|
};
|
|
485
|
-
//# sourceMappingURL=chunk-
|
|
485
|
+
//# sourceMappingURL=chunk-CFFRMRWJ.js.map
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-VQQ3O7J5.js";
|
|
10
10
|
import {
|
|
11
11
|
ConfirmDialog
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-CFFRMRWJ.js";
|
|
13
13
|
import {
|
|
14
14
|
ErrorBox
|
|
15
15
|
} from "./chunk-FEITX4L3.js";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from "./chunk-TDMVH77G.js";
|
|
22
22
|
import {
|
|
23
23
|
Button
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-345ESUIP.js";
|
|
25
25
|
import {
|
|
26
26
|
Tooltip,
|
|
27
27
|
TooltipContent,
|
|
@@ -778,4 +778,4 @@ export {
|
|
|
778
778
|
CanvasLayout,
|
|
779
779
|
ModeSwitch
|
|
780
780
|
};
|
|
781
|
-
//# sourceMappingURL=chunk-
|
|
781
|
+
//# sourceMappingURL=chunk-F7IUBABK.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
Button
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-345ESUIP.js";
|
|
5
5
|
import {
|
|
6
6
|
Tooltip,
|
|
7
7
|
TooltipContent,
|
|
@@ -295,4 +295,4 @@ export {
|
|
|
295
295
|
displayId,
|
|
296
296
|
RecordId
|
|
297
297
|
};
|
|
298
|
-
//# sourceMappingURL=chunk-
|
|
298
|
+
//# sourceMappingURL=chunk-FLYZSVTR.js.map
|
|
@@ -10,17 +10,17 @@ import {
|
|
|
10
10
|
} from "./chunk-CZGZV35G.js";
|
|
11
11
|
import {
|
|
12
12
|
PropertyRow
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YA3DNAAC.js";
|
|
14
14
|
import {
|
|
15
15
|
completeLayeredFilterRules,
|
|
16
16
|
layeredFilterMatchesValue
|
|
17
17
|
} from "./chunk-7ZCD4AZS.js";
|
|
18
18
|
import {
|
|
19
19
|
RecordId
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-FLYZSVTR.js";
|
|
21
21
|
import {
|
|
22
22
|
Button
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-345ESUIP.js";
|
|
24
24
|
import {
|
|
25
25
|
useMatrxDataTableHost
|
|
26
26
|
} from "./chunk-B5D3VIMC.js";
|
|
@@ -671,4 +671,4 @@ export {
|
|
|
671
671
|
isUuidValue,
|
|
672
672
|
DataRowInspector
|
|
673
673
|
};
|
|
674
|
-
//# sourceMappingURL=chunk-
|
|
674
|
+
//# sourceMappingURL=chunk-RLTTLDWU.js.map
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
import {
|
|
50
50
|
Button as Button2,
|
|
51
51
|
Chip
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-345ESUIP.js";
|
|
53
53
|
import {
|
|
54
54
|
Tooltip,
|
|
55
55
|
TooltipContent,
|
|
@@ -4007,4 +4007,4 @@ export {
|
|
|
4007
4007
|
TileCard,
|
|
4008
4008
|
TileCardGrid
|
|
4009
4009
|
};
|
|
4010
|
-
//# sourceMappingURL=chunk-
|
|
4010
|
+
//# sourceMappingURL=chunk-U3SUPYNT.js.map
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
DataRowInspector,
|
|
23
23
|
columnId
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-RLTTLDWU.js";
|
|
25
25
|
import {
|
|
26
26
|
useMatrxDataTableHost
|
|
27
27
|
} from "./chunk-B5D3VIMC.js";
|
|
@@ -305,4 +305,4 @@ export {
|
|
|
305
305
|
withMacLookDefaults,
|
|
306
306
|
DataRowWindow
|
|
307
307
|
};
|
|
308
|
-
//# sourceMappingURL=chunk-
|
|
308
|
+
//# sourceMappingURL=chunk-WSEZDHLA.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-OPF265PU.js";
|
|
7
7
|
import {
|
|
8
8
|
Button
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-345ESUIP.js";
|
|
10
10
|
import {
|
|
11
11
|
AlertTriangleIcon,
|
|
12
12
|
ChevronDownIcon,
|
|
@@ -605,4 +605,4 @@ export {
|
|
|
605
605
|
ConfigurationTable,
|
|
606
606
|
ConfigurationTableRow
|
|
607
607
|
};
|
|
608
|
-
//# sourceMappingURL=chunk-
|
|
608
|
+
//# sourceMappingURL=chunk-YA3DNAAC.js.map
|
|
@@ -15,9 +15,9 @@ import "./chunk-U24MOJWK.js";
|
|
|
15
15
|
import "./chunk-NDTDBBMM.js";
|
|
16
16
|
import {
|
|
17
17
|
Textarea
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-BD77DHUO.js";
|
|
18
|
+
} from "./chunk-345ESUIP.js";
|
|
20
19
|
import "./chunk-BKCJUFD3.js";
|
|
20
|
+
import "./chunk-BD77DHUO.js";
|
|
21
21
|
import "./chunk-MOZRIHJF.js";
|
|
22
22
|
import "./chunk-AKSKVT2M.js";
|
|
23
23
|
import "./chunk-BNXNIN2S.js";
|
package/dist/controls.css
CHANGED
|
@@ -306,6 +306,13 @@
|
|
|
306
306
|
background-color: hsl(var(--muted)) !important;
|
|
307
307
|
color: hsl(var(--foreground)) !important;
|
|
308
308
|
}
|
|
309
|
+
/* meta with a glyph: a FINE icon at the heading's size (12px, thin stroke) — never the
|
|
310
|
+
16px control glyph beside 11px text (owner, 2026-10-24: "just use a fine icon"). */
|
|
311
|
+
.matrx-control-button[data-variant="meta"] > svg {
|
|
312
|
+
width: 0.75rem !important;
|
|
313
|
+
height: 0.75rem !important;
|
|
314
|
+
stroke-width: 1.75 !important;
|
|
315
|
+
}
|
|
309
316
|
/* PRESSED (Button pressed): a toggle in a set. On = a primary tint with a primary
|
|
310
317
|
hairline (the old accent gray, rgb 239,239,240, vanished on tinted cards; pinned in
|
|
311
318
|
pressed-contrast.test.ts), full ink; off = no fill, quiet ink. Out-ranks the
|
package/dist/controls.js
CHANGED
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
spansOf,
|
|
74
74
|
splitForMiddleTruncate,
|
|
75
75
|
stageStates
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-U3SUPYNT.js";
|
|
77
77
|
import "./chunk-6NOLVA4E.js";
|
|
78
78
|
import {
|
|
79
79
|
Input,
|
|
@@ -98,7 +98,7 @@ import {
|
|
|
98
98
|
RowGroup,
|
|
99
99
|
SettingRow,
|
|
100
100
|
Tile
|
|
101
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-CFFRMRWJ.js";
|
|
102
102
|
import "./chunk-K4XCB576.js";
|
|
103
103
|
import "./chunk-FEITX4L3.js";
|
|
104
104
|
import "./chunk-DRSBLHOF.js";
|
|
@@ -119,7 +119,7 @@ import {
|
|
|
119
119
|
StatusToken,
|
|
120
120
|
isCollectedValue,
|
|
121
121
|
valueSummary
|
|
122
|
-
} from "./chunk-
|
|
122
|
+
} from "./chunk-YA3DNAAC.js";
|
|
123
123
|
import "./chunk-WFBPCMPF.js";
|
|
124
124
|
import "./chunk-OPF265PU.js";
|
|
125
125
|
import {
|
|
@@ -141,9 +141,9 @@ import {
|
|
|
141
141
|
Switch,
|
|
142
142
|
Tabs,
|
|
143
143
|
Textarea
|
|
144
|
-
} from "./chunk-
|
|
145
|
-
import "./chunk-BD77DHUO.js";
|
|
144
|
+
} from "./chunk-345ESUIP.js";
|
|
146
145
|
import "./chunk-BKCJUFD3.js";
|
|
146
|
+
import "./chunk-BD77DHUO.js";
|
|
147
147
|
import "./chunk-PIQJULQB.js";
|
|
148
148
|
import "./chunk-MOZRIHJF.js";
|
|
149
149
|
import "./chunk-AKSKVT2M.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
DataRowInspector
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-RLTTLDWU.js";
|
|
5
5
|
import "../chunk-UEQK6PFD.js";
|
|
6
6
|
import "../chunk-PTYKILZE.js";
|
|
7
7
|
import "../chunk-CZGZV35G.js";
|
|
@@ -9,16 +9,16 @@ import "../chunk-KPZX2M2C.js";
|
|
|
9
9
|
import "../chunk-GLZJ7WZK.js";
|
|
10
10
|
import "../chunk-B3LDW3IQ.js";
|
|
11
11
|
import "../chunk-FXT7PZAF.js";
|
|
12
|
-
import "../chunk-
|
|
12
|
+
import "../chunk-YA3DNAAC.js";
|
|
13
13
|
import "../chunk-WFBPCMPF.js";
|
|
14
14
|
import "../chunk-KGW373MK.js";
|
|
15
15
|
import "../chunk-7ZCD4AZS.js";
|
|
16
16
|
import "../chunk-OPF265PU.js";
|
|
17
17
|
import "../chunk-QFP4BUKZ.js";
|
|
18
|
-
import "../chunk-
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-BD77DHUO.js";
|
|
18
|
+
import "../chunk-FLYZSVTR.js";
|
|
19
|
+
import "../chunk-345ESUIP.js";
|
|
21
20
|
import "../chunk-BKCJUFD3.js";
|
|
21
|
+
import "../chunk-BD77DHUO.js";
|
|
22
22
|
import "../chunk-PIQJULQB.js";
|
|
23
23
|
import "../chunk-MOZRIHJF.js";
|
|
24
24
|
import "../chunk-AKSKVT2M.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
DataRowWindow
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-WSEZDHLA.js";
|
|
5
5
|
import "../chunk-YC5GAO35.js";
|
|
6
6
|
import "../chunk-VLLUUOYJ.js";
|
|
7
7
|
import "../chunk-TH6DDCWF.js";
|
|
@@ -10,7 +10,7 @@ import "../chunk-LEPD6TV7.js";
|
|
|
10
10
|
import "../chunk-IMQ3RX35.js";
|
|
11
11
|
import "../chunk-U24MOJWK.js";
|
|
12
12
|
import "../chunk-NDTDBBMM.js";
|
|
13
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-RLTTLDWU.js";
|
|
14
14
|
import "../chunk-UEQK6PFD.js";
|
|
15
15
|
import "../chunk-PTYKILZE.js";
|
|
16
16
|
import "../chunk-CZGZV35G.js";
|
|
@@ -18,16 +18,16 @@ import "../chunk-KPZX2M2C.js";
|
|
|
18
18
|
import "../chunk-GLZJ7WZK.js";
|
|
19
19
|
import "../chunk-B3LDW3IQ.js";
|
|
20
20
|
import "../chunk-FXT7PZAF.js";
|
|
21
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-YA3DNAAC.js";
|
|
22
22
|
import "../chunk-WFBPCMPF.js";
|
|
23
23
|
import "../chunk-KGW373MK.js";
|
|
24
24
|
import "../chunk-7ZCD4AZS.js";
|
|
25
25
|
import "../chunk-OPF265PU.js";
|
|
26
26
|
import "../chunk-QFP4BUKZ.js";
|
|
27
|
-
import "../chunk-
|
|
28
|
-
import "../chunk-
|
|
29
|
-
import "../chunk-BD77DHUO.js";
|
|
27
|
+
import "../chunk-FLYZSVTR.js";
|
|
28
|
+
import "../chunk-345ESUIP.js";
|
|
30
29
|
import "../chunk-BKCJUFD3.js";
|
|
30
|
+
import "../chunk-BD77DHUO.js";
|
|
31
31
|
import "../chunk-PIQJULQB.js";
|
|
32
32
|
import "../chunk-MOZRIHJF.js";
|
|
33
33
|
import "../chunk-AKSKVT2M.js";
|
|
@@ -20375,7 +20375,11 @@ var MULTI = "multi";
|
|
|
20375
20375
|
function cellTextIsMultiline(cell) {
|
|
20376
20376
|
const lineHeight = Number.parseFloat(getComputedStyle(cell).lineHeight);
|
|
20377
20377
|
const minStep = Number.isFinite(lineHeight) ? lineHeight * 0.75 : 10;
|
|
20378
|
-
const walker = cell.ownerDocument.createTreeWalker(
|
|
20378
|
+
const walker = cell.ownerDocument.createTreeWalker(
|
|
20379
|
+
cell,
|
|
20380
|
+
4
|
|
20381
|
+
/* NodeFilter.SHOW_TEXT: the global is gone once a test env tears down */
|
|
20382
|
+
);
|
|
20379
20383
|
const range = cell.ownerDocument.createRange();
|
|
20380
20384
|
if (typeof range.getClientRects !== "function") return false;
|
|
20381
20385
|
let firstTop = null;
|