@tenphi/tasty 0.0.0-snapshot.09c74e2
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/LICENSE +21 -0
- package/README.md +539 -0
- package/dist/_virtual/_rolldown/runtime.js +8 -0
- package/dist/chunks/cacheKey.js +70 -0
- package/dist/chunks/cacheKey.js.map +1 -0
- package/dist/chunks/definitions.d.ts +37 -0
- package/dist/chunks/definitions.js +260 -0
- package/dist/chunks/definitions.js.map +1 -0
- package/dist/chunks/renderChunk.js +61 -0
- package/dist/chunks/renderChunk.js.map +1 -0
- package/dist/config.d.ts +280 -0
- package/dist/config.js +403 -0
- package/dist/config.js.map +1 -0
- package/dist/core/index.d.ts +33 -0
- package/dist/core/index.js +26 -0
- package/dist/debug.d.ts +204 -0
- package/dist/debug.js +733 -0
- package/dist/debug.js.map +1 -0
- package/dist/hooks/useGlobalStyles.d.ts +27 -0
- package/dist/hooks/useGlobalStyles.js +56 -0
- package/dist/hooks/useGlobalStyles.js.map +1 -0
- package/dist/hooks/useKeyframes.d.ts +56 -0
- package/dist/hooks/useKeyframes.js +54 -0
- package/dist/hooks/useKeyframes.js.map +1 -0
- package/dist/hooks/useProperty.d.ts +79 -0
- package/dist/hooks/useProperty.js +91 -0
- package/dist/hooks/useProperty.js.map +1 -0
- package/dist/hooks/useRawCSS.d.ts +53 -0
- package/dist/hooks/useRawCSS.js +28 -0
- package/dist/hooks/useRawCSS.js.map +1 -0
- package/dist/hooks/useStyles.d.ts +40 -0
- package/dist/hooks/useStyles.js +169 -0
- package/dist/hooks/useStyles.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +33 -0
- package/dist/injector/index.d.ts +157 -0
- package/dist/injector/index.js +154 -0
- package/dist/injector/index.js.map +1 -0
- package/dist/injector/injector.d.ts +139 -0
- package/dist/injector/injector.js +404 -0
- package/dist/injector/injector.js.map +1 -0
- package/dist/injector/sheet-manager.d.ts +127 -0
- package/dist/injector/sheet-manager.js +714 -0
- package/dist/injector/sheet-manager.js.map +1 -0
- package/dist/injector/types.d.ts +135 -0
- package/dist/keyframes/index.js +206 -0
- package/dist/keyframes/index.js.map +1 -0
- package/dist/parser/classify.js +319 -0
- package/dist/parser/classify.js.map +1 -0
- package/dist/parser/const.js +33 -0
- package/dist/parser/const.js.map +1 -0
- package/dist/parser/lru.js +109 -0
- package/dist/parser/lru.js.map +1 -0
- package/dist/parser/parser.d.ts +25 -0
- package/dist/parser/parser.js +116 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/tokenizer.js +69 -0
- package/dist/parser/tokenizer.js.map +1 -0
- package/dist/parser/types.d.ts +51 -0
- package/dist/parser/types.js +46 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/pipeline/conditions.d.ts +134 -0
- package/dist/pipeline/conditions.js +406 -0
- package/dist/pipeline/conditions.js.map +1 -0
- package/dist/pipeline/exclusive.js +231 -0
- package/dist/pipeline/exclusive.js.map +1 -0
- package/dist/pipeline/index.d.ts +53 -0
- package/dist/pipeline/index.js +660 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/materialize.js +844 -0
- package/dist/pipeline/materialize.js.map +1 -0
- package/dist/pipeline/parseStateKey.d.ts +15 -0
- package/dist/pipeline/parseStateKey.js +438 -0
- package/dist/pipeline/parseStateKey.js.map +1 -0
- package/dist/pipeline/simplify.js +516 -0
- package/dist/pipeline/simplify.js.map +1 -0
- package/dist/pipeline/warnings.js +18 -0
- package/dist/pipeline/warnings.js.map +1 -0
- package/dist/plugins/okhsl-plugin.d.ts +35 -0
- package/dist/plugins/okhsl-plugin.js +371 -0
- package/dist/plugins/okhsl-plugin.js.map +1 -0
- package/dist/plugins/types.d.ts +69 -0
- package/dist/properties/index.js +158 -0
- package/dist/properties/index.js.map +1 -0
- package/dist/states/index.d.ts +49 -0
- package/dist/states/index.js +416 -0
- package/dist/states/index.js.map +1 -0
- package/dist/static/index.d.ts +5 -0
- package/dist/static/index.js +5 -0
- package/dist/static/tastyStatic.d.ts +46 -0
- package/dist/static/tastyStatic.js +31 -0
- package/dist/static/tastyStatic.js.map +1 -0
- package/dist/static/types.d.ts +49 -0
- package/dist/static/types.js +24 -0
- package/dist/static/types.js.map +1 -0
- package/dist/styles/align.d.ts +15 -0
- package/dist/styles/align.js +14 -0
- package/dist/styles/align.js.map +1 -0
- package/dist/styles/border.d.ts +25 -0
- package/dist/styles/border.js +114 -0
- package/dist/styles/border.js.map +1 -0
- package/dist/styles/color.d.ts +14 -0
- package/dist/styles/color.js +23 -0
- package/dist/styles/color.js.map +1 -0
- package/dist/styles/createStyle.js +77 -0
- package/dist/styles/createStyle.js.map +1 -0
- package/dist/styles/dimension.js +97 -0
- package/dist/styles/dimension.js.map +1 -0
- package/dist/styles/display.d.ts +37 -0
- package/dist/styles/display.js +67 -0
- package/dist/styles/display.js.map +1 -0
- package/dist/styles/fade.d.ts +15 -0
- package/dist/styles/fade.js +58 -0
- package/dist/styles/fade.js.map +1 -0
- package/dist/styles/fill.d.ts +42 -0
- package/dist/styles/fill.js +52 -0
- package/dist/styles/fill.js.map +1 -0
- package/dist/styles/flow.d.ts +16 -0
- package/dist/styles/flow.js +12 -0
- package/dist/styles/flow.js.map +1 -0
- package/dist/styles/gap.d.ts +31 -0
- package/dist/styles/gap.js +37 -0
- package/dist/styles/gap.js.map +1 -0
- package/dist/styles/height.d.ts +17 -0
- package/dist/styles/height.js +20 -0
- package/dist/styles/height.js.map +1 -0
- package/dist/styles/index.d.ts +2 -0
- package/dist/styles/index.js +9 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/styles/inset.d.ts +52 -0
- package/dist/styles/inset.js +150 -0
- package/dist/styles/inset.js.map +1 -0
- package/dist/styles/justify.d.ts +15 -0
- package/dist/styles/justify.js +14 -0
- package/dist/styles/justify.js.map +1 -0
- package/dist/styles/list.d.ts +16 -0
- package/dist/styles/list.js +98 -0
- package/dist/styles/list.js.map +1 -0
- package/dist/styles/margin.d.ts +24 -0
- package/dist/styles/margin.js +104 -0
- package/dist/styles/margin.js.map +1 -0
- package/dist/styles/outline.d.ts +29 -0
- package/dist/styles/outline.js +65 -0
- package/dist/styles/outline.js.map +1 -0
- package/dist/styles/padding.d.ts +24 -0
- package/dist/styles/padding.js +104 -0
- package/dist/styles/padding.js.map +1 -0
- package/dist/styles/predefined.d.ts +73 -0
- package/dist/styles/predefined.js +241 -0
- package/dist/styles/predefined.js.map +1 -0
- package/dist/styles/preset.d.ts +47 -0
- package/dist/styles/preset.js +126 -0
- package/dist/styles/preset.js.map +1 -0
- package/dist/styles/radius.d.ts +14 -0
- package/dist/styles/radius.js +51 -0
- package/dist/styles/radius.js.map +1 -0
- package/dist/styles/scrollbar.d.ts +21 -0
- package/dist/styles/scrollbar.js +112 -0
- package/dist/styles/scrollbar.js.map +1 -0
- package/dist/styles/shadow.d.ts +14 -0
- package/dist/styles/shadow.js +24 -0
- package/dist/styles/shadow.js.map +1 -0
- package/dist/styles/styledScrollbar.d.ts +47 -0
- package/dist/styles/styledScrollbar.js +38 -0
- package/dist/styles/styledScrollbar.js.map +1 -0
- package/dist/styles/transition.d.ts +14 -0
- package/dist/styles/transition.js +158 -0
- package/dist/styles/transition.js.map +1 -0
- package/dist/styles/types.d.ts +498 -0
- package/dist/styles/width.d.ts +17 -0
- package/dist/styles/width.js +20 -0
- package/dist/styles/width.js.map +1 -0
- package/dist/tasty.d.ts +982 -0
- package/dist/tasty.js +206 -0
- package/dist/tasty.js.map +1 -0
- package/dist/tokens/typography.d.ts +19 -0
- package/dist/tokens/typography.js +237 -0
- package/dist/tokens/typography.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/utils/cache-wrapper.js +26 -0
- package/dist/utils/cache-wrapper.js.map +1 -0
- package/dist/utils/case-converter.js +8 -0
- package/dist/utils/case-converter.js.map +1 -0
- package/dist/utils/colors.d.ts +5 -0
- package/dist/utils/colors.js +9 -0
- package/dist/utils/colors.js.map +1 -0
- package/dist/utils/css-types.d.ts +7 -0
- package/dist/utils/dotize.d.ts +26 -0
- package/dist/utils/dotize.js +122 -0
- package/dist/utils/dotize.js.map +1 -0
- package/dist/utils/filter-base-props.d.ts +15 -0
- package/dist/utils/filter-base-props.js +45 -0
- package/dist/utils/filter-base-props.js.map +1 -0
- package/dist/utils/get-display-name.d.ts +7 -0
- package/dist/utils/get-display-name.js +10 -0
- package/dist/utils/get-display-name.js.map +1 -0
- package/dist/utils/hsl-to-rgb.js +38 -0
- package/dist/utils/hsl-to-rgb.js.map +1 -0
- package/dist/utils/is-dev-env.js +19 -0
- package/dist/utils/is-dev-env.js.map +1 -0
- package/dist/utils/is-valid-element-type.js +15 -0
- package/dist/utils/is-valid-element-type.js.map +1 -0
- package/dist/utils/merge-styles.d.ts +7 -0
- package/dist/utils/merge-styles.js +146 -0
- package/dist/utils/merge-styles.js.map +1 -0
- package/dist/utils/mod-attrs.d.ts +8 -0
- package/dist/utils/mod-attrs.js +21 -0
- package/dist/utils/mod-attrs.js.map +1 -0
- package/dist/utils/okhsl-to-rgb.js +296 -0
- package/dist/utils/okhsl-to-rgb.js.map +1 -0
- package/dist/utils/process-tokens.d.ts +31 -0
- package/dist/utils/process-tokens.js +171 -0
- package/dist/utils/process-tokens.js.map +1 -0
- package/dist/utils/resolve-recipes.d.ts +17 -0
- package/dist/utils/resolve-recipes.js +147 -0
- package/dist/utils/resolve-recipes.js.map +1 -0
- package/dist/utils/string.js +8 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/utils/styles.d.ts +178 -0
- package/dist/utils/styles.js +590 -0
- package/dist/utils/styles.js.map +1 -0
- package/dist/utils/typography.d.ts +36 -0
- package/dist/utils/typography.js +53 -0
- package/dist/utils/typography.js.map +1 -0
- package/dist/utils/warnings.d.ts +16 -0
- package/dist/utils/warnings.js +16 -0
- package/dist/utils/warnings.js.map +1 -0
- package/dist/zero/babel.d.ts +108 -0
- package/dist/zero/babel.js +282 -0
- package/dist/zero/babel.js.map +1 -0
- package/dist/zero/css-writer.d.ts +45 -0
- package/dist/zero/css-writer.js +74 -0
- package/dist/zero/css-writer.js.map +1 -0
- package/dist/zero/extractor.d.ts +24 -0
- package/dist/zero/extractor.js +150 -0
- package/dist/zero/extractor.js.map +1 -0
- package/dist/zero/index.d.ts +3 -0
- package/dist/zero/index.js +4 -0
- package/dist/zero/next.d.ts +60 -0
- package/dist/zero/next.js +78 -0
- package/dist/zero/next.js.map +1 -0
- package/package.json +189 -0
- package/tasty.config.ts +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simplify.js","names":[],"sources":["../../src/pipeline/simplify.ts"],"sourcesContent":["/**\n * Condition Simplification Engine\n *\n * Simplifies condition trees by applying boolean algebra rules,\n * detecting contradictions, merging ranges, and deduplicating terms.\n *\n * This is critical for:\n * 1. Detecting invalid combinations (A & !A → FALSE)\n * 2. Reducing CSS output size\n * 3. Producing cleaner selectors\n */\n\nimport { Lru } from '../parser/lru';\n\nimport type {\n ConditionNode,\n ContainerCondition,\n MediaCondition,\n ModifierCondition,\n NumericBound,\n} from './conditions';\nimport {\n falseCondition,\n getConditionUniqueId,\n trueCondition,\n} from './conditions';\n\n// ============================================================================\n// Caching\n// ============================================================================\n\nconst simplifyCache = new Lru<string, ConditionNode>(5000);\n\n// ============================================================================\n// Main Simplify Function\n// ============================================================================\n\n/**\n * Simplify a condition tree aggressively.\n *\n * This applies all possible simplification rules:\n * - Boolean algebra (identity, annihilator, idempotent, absorption)\n * - Contradiction detection (A & !A → FALSE)\n * - Tautology detection (A | !A → TRUE)\n * - Range intersection for numeric queries\n * - Attribute value conflict detection\n * - Deduplication and sorting\n */\nexport function simplifyCondition(node: ConditionNode): ConditionNode {\n // Check cache\n const key = getConditionUniqueId(node);\n const cached = simplifyCache.get(key);\n if (cached) {\n return cached;\n }\n\n const result = simplifyInner(node);\n\n // Cache result\n simplifyCache.set(key, result);\n\n return result;\n}\n\n/**\n * Clear the simplify cache (for testing)\n */\nexport function clearSimplifyCache(): void {\n simplifyCache.clear();\n}\n\n// ============================================================================\n// Inner Simplification\n// ============================================================================\n\nfunction simplifyInner(node: ConditionNode): ConditionNode {\n // Base cases\n if (node.kind === 'true' || node.kind === 'false') {\n return node;\n }\n\n // State conditions - return as-is (they're already leaf nodes)\n if (node.kind === 'state') {\n return node;\n }\n\n // Compound conditions - recursively simplify\n if (node.kind === 'compound') {\n // First, recursively simplify all children\n const simplifiedChildren = node.children.map((c) => simplifyInner(c));\n\n // Then apply compound-specific simplifications\n if (node.operator === 'AND') {\n return simplifyAnd(simplifiedChildren);\n } else {\n return simplifyOr(simplifiedChildren);\n }\n }\n\n return node;\n}\n\n// ============================================================================\n// AND Simplification\n// ============================================================================\n\nfunction simplifyAnd(children: ConditionNode[]): ConditionNode {\n let terms: ConditionNode[] = [];\n\n // Flatten nested ANDs and handle TRUE/FALSE\n for (const child of children) {\n if (child.kind === 'false') {\n // AND with FALSE → FALSE\n return falseCondition();\n }\n if (child.kind === 'true') {\n // AND with TRUE → skip (identity)\n continue;\n }\n if (child.kind === 'compound' && child.operator === 'AND') {\n // Flatten nested AND\n terms.push(...child.children);\n } else {\n terms.push(child);\n }\n }\n\n // Empty → TRUE\n if (terms.length === 0) {\n return trueCondition();\n }\n\n // Single term → return it\n if (terms.length === 1) {\n return terms[0];\n }\n\n // Check for contradictions\n if (hasContradiction(terms)) {\n return falseCondition();\n }\n\n // Check for range contradictions in media/container queries\n if (hasRangeContradiction(terms)) {\n return falseCondition();\n }\n\n // Check for attribute value conflicts\n if (hasAttributeConflict(terms)) {\n return falseCondition();\n }\n\n // Check for container style query conflicts\n if (hasContainerStyleConflict(terms)) {\n return falseCondition();\n }\n\n // Remove redundant negations implied by positive terms\n // e.g., style(--variant: danger) implies NOT style(--variant: success)\n // and style(--variant: danger) implies style(--variant) (existence)\n terms = removeImpliedNegations(terms);\n\n // Deduplicate (by uniqueId)\n terms = deduplicateTerms(terms);\n\n // Try to merge numeric ranges\n terms = mergeRanges(terms);\n\n // Sort for canonical form\n terms = sortTerms(terms);\n\n // Apply absorption: A & (A | B) → A\n terms = applyAbsorptionAnd(terms);\n\n if (terms.length === 0) {\n return trueCondition();\n }\n if (terms.length === 1) {\n return terms[0];\n }\n\n return {\n kind: 'compound',\n operator: 'AND',\n children: terms,\n };\n}\n\n// ============================================================================\n// OR Simplification\n// ============================================================================\n\nfunction simplifyOr(children: ConditionNode[]): ConditionNode {\n let terms: ConditionNode[] = [];\n\n // Flatten nested ORs and handle TRUE/FALSE\n for (const child of children) {\n if (child.kind === 'true') {\n // OR with TRUE → TRUE\n return trueCondition();\n }\n if (child.kind === 'false') {\n // OR with FALSE → skip (identity)\n continue;\n }\n if (child.kind === 'compound' && child.operator === 'OR') {\n // Flatten nested OR\n terms.push(...child.children);\n } else {\n terms.push(child);\n }\n }\n\n // Empty → FALSE\n if (terms.length === 0) {\n return falseCondition();\n }\n\n // Single term → return it\n if (terms.length === 1) {\n return terms[0];\n }\n\n // Check for tautologies (A | !A)\n if (hasTautology(terms)) {\n return trueCondition();\n }\n\n // Deduplicate\n terms = deduplicateTerms(terms);\n\n // Sort for canonical form\n terms = sortTerms(terms);\n\n // Apply absorption: A | (A & B) → A\n terms = applyAbsorptionOr(terms);\n\n if (terms.length === 0) {\n return falseCondition();\n }\n if (terms.length === 1) {\n return terms[0];\n }\n\n return {\n kind: 'compound',\n operator: 'OR',\n children: terms,\n };\n}\n\n// ============================================================================\n// Contradiction Detection\n// ============================================================================\n\n/**\n * Check if any pair of terms has complementary negation (A and !A).\n * Used for both contradiction detection (in AND) and tautology detection (in OR),\n * since the underlying check is identical: the context determines the semantics.\n */\nfunction hasComplementaryPair(terms: ConditionNode[]): boolean {\n const uniqueIds = new Set<string>();\n\n for (const term of terms) {\n if (term.kind !== 'state') continue;\n\n const id = term.uniqueId;\n const negatedId = term.negated ? id.slice(1) : `!${id}`;\n\n if (uniqueIds.has(negatedId)) {\n return true;\n }\n uniqueIds.add(id);\n }\n\n return false;\n}\n\nconst hasContradiction = hasComplementaryPair;\nconst hasTautology = hasComplementaryPair;\n\n// ============================================================================\n// Range Contradiction Detection\n// ============================================================================\n\n/**\n * Effective bounds computed from conditions (including negated single-bound conditions)\n */\ninterface EffectiveBounds {\n lowerBound: number | null;\n lowerInclusive: boolean;\n upperBound: number | null;\n upperInclusive: boolean;\n}\n\n/**\n * Excluded range from a negated range condition\n */\ninterface ExcludedRange {\n lower: number;\n lowerInclusive: boolean;\n upper: number;\n upperInclusive: boolean;\n}\n\n/**\n * Check for range contradictions in media/container queries\n * e.g., @media(w < 400px) & @media(w > 800px) → FALSE\n *\n * Also handles negated conditions:\n * - Single-bound negations are inverted (not (w < 600px) → w >= 600px)\n * - Range negations create excluded ranges that are checked against positive bounds\n */\nfunction hasRangeContradiction(terms: ConditionNode[]): boolean {\n // Group by dimension, separating positive and negated conditions\n const mediaByDim = new Map<\n string,\n { positive: MediaCondition[]; negated: MediaCondition[] }\n >();\n const containerByDim = new Map<\n string,\n { positive: ContainerCondition[]; negated: ContainerCondition[] }\n >();\n\n for (const term of terms) {\n if (term.kind !== 'state') continue;\n\n if (term.type === 'media' && term.subtype === 'dimension') {\n const key = term.dimension || 'width';\n if (!mediaByDim.has(key)) {\n mediaByDim.set(key, { positive: [], negated: [] });\n }\n const group = mediaByDim.get(key)!;\n if (term.negated) {\n group.negated.push(term);\n } else {\n group.positive.push(term);\n }\n }\n\n if (term.type === 'container' && term.subtype === 'dimension') {\n const key = `${term.containerName || '_'}:${term.dimension || 'width'}`;\n if (!containerByDim.has(key)) {\n containerByDim.set(key, { positive: [], negated: [] });\n }\n const group = containerByDim.get(key)!;\n if (term.negated) {\n group.negated.push(term);\n } else {\n group.positive.push(term);\n }\n }\n }\n\n // Check each dimension group for impossible ranges\n for (const group of mediaByDim.values()) {\n if (rangesAreImpossibleWithNegations(group.positive, group.negated)) {\n return true;\n }\n }\n\n for (const group of containerByDim.values()) {\n if (rangesAreImpossibleWithNegations(group.positive, group.negated)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if conditions are impossible, including negated conditions.\n *\n * For negated single-bound conditions:\n * not (w < 600px) → w >= 600px (inverted to lower bound)\n * not (w >= 800px) → w < 800px (inverted to upper bound)\n *\n * For negated range conditions:\n * not (400px <= w < 800px) → excludes [400, 800)\n * If the effective bounds fall entirely within an excluded range, it's impossible.\n */\nfunction rangesAreImpossibleWithNegations(\n positive: (MediaCondition | ContainerCondition)[],\n negated: (MediaCondition | ContainerCondition)[],\n): boolean {\n // Start with bounds from positive conditions\n const bounds = computeEffectiveBounds(positive);\n\n // Apply inverted bounds from single-bound negated conditions\n // and collect excluded ranges from range negated conditions\n const excludedRanges: ExcludedRange[] = [];\n\n for (const cond of negated) {\n const hasLower = cond.lowerBound?.valueNumeric != null;\n const hasUpper = cond.upperBound?.valueNumeric != null;\n\n if (hasLower && hasUpper) {\n // Range negation: not (lower <= w < upper) excludes [lower, upper)\n excludedRanges.push({\n lower: cond.lowerBound!.valueNumeric!,\n lowerInclusive: cond.lowerBound!.inclusive,\n upper: cond.upperBound!.valueNumeric!,\n upperInclusive: cond.upperBound!.inclusive,\n });\n } else if (hasUpper) {\n // not (w < upper) → w >= upper (becomes lower bound)\n // not (w <= upper) → w > upper (becomes lower bound, exclusive)\n const value = cond.upperBound!.valueNumeric!;\n const inclusive = !cond.upperBound!.inclusive; // flip inclusivity\n\n if (bounds.lowerBound === null || value > bounds.lowerBound) {\n bounds.lowerBound = value;\n bounds.lowerInclusive = inclusive;\n } else if (value === bounds.lowerBound && !inclusive) {\n bounds.lowerInclusive = false;\n }\n } else if (hasLower) {\n // not (w >= lower) → w < lower (becomes upper bound)\n // not (w > lower) → w <= lower (becomes upper bound, inclusive)\n const value = cond.lowerBound!.valueNumeric!;\n const inclusive = !cond.lowerBound!.inclusive; // flip inclusivity\n\n if (bounds.upperBound === null || value < bounds.upperBound) {\n bounds.upperBound = value;\n bounds.upperInclusive = inclusive;\n } else if (value === bounds.upperBound && !inclusive) {\n bounds.upperInclusive = false;\n }\n }\n }\n\n // Check if effective bounds are impossible on their own\n if (bounds.lowerBound !== null && bounds.upperBound !== null) {\n if (bounds.lowerBound > bounds.upperBound) {\n return true;\n }\n if (\n bounds.lowerBound === bounds.upperBound &&\n (!bounds.lowerInclusive || !bounds.upperInclusive)\n ) {\n return true;\n }\n }\n\n // Check if effective bounds fall entirely within any excluded range\n if (\n bounds.lowerBound !== null &&\n bounds.upperBound !== null &&\n excludedRanges.length > 0\n ) {\n for (const excluded of excludedRanges) {\n if (boundsWithinExcludedRange(bounds, excluded)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Compute effective bounds from positive (non-negated) conditions\n */\nfunction computeEffectiveBounds(\n conditions: (MediaCondition | ContainerCondition)[],\n): EffectiveBounds {\n let lowerBound: number | null = null;\n let lowerInclusive = false;\n let upperBound: number | null = null;\n let upperInclusive = false;\n\n for (const cond of conditions) {\n if (cond.lowerBound?.valueNumeric != null) {\n const value = cond.lowerBound.valueNumeric;\n const inclusive = cond.lowerBound.inclusive;\n\n if (lowerBound === null || value > lowerBound) {\n lowerBound = value;\n lowerInclusive = inclusive;\n } else if (value === lowerBound && !inclusive) {\n lowerInclusive = false;\n }\n }\n\n if (cond.upperBound?.valueNumeric != null) {\n const value = cond.upperBound.valueNumeric;\n const inclusive = cond.upperBound.inclusive;\n\n if (upperBound === null || value < upperBound) {\n upperBound = value;\n upperInclusive = inclusive;\n } else if (value === upperBound && !inclusive) {\n upperInclusive = false;\n }\n }\n }\n\n return { lowerBound, lowerInclusive, upperBound, upperInclusive };\n}\n\n/**\n * Check if effective bounds fall entirely within an excluded range.\n *\n * For example:\n * Effective: [400, 800)\n * Excluded: [400, 800)\n * → bounds fall entirely within excluded range → impossible\n */\nfunction boundsWithinExcludedRange(\n bounds: EffectiveBounds,\n excluded: ExcludedRange,\n): boolean {\n if (bounds.lowerBound === null || bounds.upperBound === null) {\n return false;\n }\n\n // Check if bounds.lower >= excluded.lower\n let lowerOk = false;\n if (bounds.lowerBound > excluded.lower) {\n lowerOk = true;\n } else if (bounds.lowerBound === excluded.lower) {\n // If excluded includes lower, and bounds includes or excludes lower, it's within\n // If excluded excludes lower, bounds must also exclude it to be within\n lowerOk = excluded.lowerInclusive || !bounds.lowerInclusive;\n }\n\n // Check if bounds.upper <= excluded.upper\n let upperOk = false;\n if (bounds.upperBound < excluded.upper) {\n upperOk = true;\n } else if (bounds.upperBound === excluded.upper) {\n // If excluded includes upper, and bounds includes or excludes upper, it's within\n // If excluded excludes upper, bounds must also exclude it to be within\n upperOk = excluded.upperInclusive || !bounds.upperInclusive;\n }\n\n return lowerOk && upperOk;\n}\n\n// ============================================================================\n// Attribute Conflict Detection\n// ============================================================================\n\n/**\n * Check for attribute value conflicts\n * e.g., [data-theme=\"dark\"] & [data-theme=\"light\"] → FALSE\n * e.g., [data-theme=\"dark\"] & ![data-theme] → FALSE\n */\n/**\n * Generic value-conflict checker for grouped conditions.\n *\n * Groups terms by a key, splits into positive/negated, then checks:\n * 1. Multiple distinct positive values → conflict\n * 2. Positive value + negated existence (value === undefined) → conflict\n * 3. Positive value + negated same value → conflict\n */\nfunction hasGroupedValueConflict<T extends { negated: boolean }>(\n terms: ConditionNode[],\n match: (term: ConditionNode) => T | null,\n groupKey: (term: T) => string,\n getValue: (term: T) => string | undefined,\n): boolean {\n const groups = new Map<string, { positive: T[]; negated: T[] }>();\n\n for (const term of terms) {\n const matched = match(term);\n if (!matched) continue;\n\n const key = groupKey(matched);\n let group = groups.get(key);\n if (!group) {\n group = { positive: [], negated: [] };\n groups.set(key, group);\n }\n\n if (matched.negated) {\n group.negated.push(matched);\n } else {\n group.positive.push(matched);\n }\n }\n\n for (const [, group] of groups) {\n const positiveValues = group.positive\n .map(getValue)\n .filter((v) => v !== undefined);\n if (new Set(positiveValues).size > 1) return true;\n\n const hasPositiveValue = positiveValues.length > 0;\n const hasNegatedExistence = group.negated.some(\n (t) => getValue(t) === undefined,\n );\n if (hasPositiveValue && hasNegatedExistence) return true;\n\n for (const pos of group.positive) {\n const posVal = getValue(pos);\n if (posVal !== undefined) {\n for (const neg of group.negated) {\n if (getValue(neg) === posVal) return true;\n }\n }\n }\n }\n\n return false;\n}\n\nfunction hasAttributeConflict(terms: ConditionNode[]): boolean {\n return hasGroupedValueConflict<ModifierCondition>(\n terms,\n (t) => (t.kind === 'state' && t.type === 'modifier' ? t : null),\n (t) => t.attribute,\n (t) => t.value,\n );\n}\n\nfunction hasContainerStyleConflict(terms: ConditionNode[]): boolean {\n return hasGroupedValueConflict<ContainerCondition>(\n terms,\n (t) =>\n t.kind === 'state' && t.type === 'container' && t.subtype === 'style'\n ? t\n : null,\n (t) => `${t.containerName || '_'}:${t.property}`,\n (t) => t.propertyValue,\n );\n}\n\n// ============================================================================\n// Implied Negation Removal\n// ============================================================================\n\n/**\n * Remove negations that are implied by positive terms.\n *\n * Key optimizations:\n * 1. style(--variant: danger) implies NOT style(--variant: success)\n * → If we have style(--variant: danger) & not style(--variant: success),\n * the negation is redundant and can be removed.\n *\n * 2. [data-theme=\"dark\"] implies NOT [data-theme=\"light\"]\n * → Same logic for attribute selectors.\n *\n * This produces cleaner CSS:\n * Before: @container style(--variant: danger) and (not style(--variant: success))\n * After: @container style(--variant: danger)\n */\n/**\n * Collect positive values from terms and build a \"is this negation implied?\" check.\n *\n * A negation is implied (redundant) when a positive term for the same group\n * already pins a specific value, making \"NOT other-value\" obvious.\n * e.g. style(--variant: danger) implies NOT style(--variant: success).\n */\nfunction buildImpliedNegationCheck(\n terms: ConditionNode[],\n): (term: ConditionNode) => boolean {\n const positiveValues = new Map<string, string>();\n\n for (const term of terms) {\n if (term.kind !== 'state' || term.negated) continue;\n\n if (term.type === 'container' && term.subtype === 'style') {\n if (term.propertyValue !== undefined) {\n positiveValues.set(\n `c:${term.containerName || '_'}:${term.property}`,\n term.propertyValue,\n );\n }\n } else if (term.type === 'modifier' && term.value !== undefined) {\n positiveValues.set(`m:${term.attribute}`, term.value);\n }\n }\n\n return (term: ConditionNode): boolean => {\n if (term.kind !== 'state' || !term.negated) return false;\n\n if (term.type === 'container' && term.subtype === 'style') {\n if (term.propertyValue === undefined) return false;\n const pos = positiveValues.get(\n `c:${term.containerName || '_'}:${term.property}`,\n );\n return pos !== undefined && term.propertyValue !== pos;\n }\n\n if (term.type === 'modifier' && term.value !== undefined) {\n const pos = positiveValues.get(`m:${term.attribute}`);\n return pos !== undefined && term.value !== pos;\n }\n\n return false;\n };\n}\n\nfunction removeImpliedNegations(terms: ConditionNode[]): ConditionNode[] {\n const isImplied = buildImpliedNegationCheck(terms);\n return terms.filter((t) => !isImplied(t));\n}\n\n// ============================================================================\n// Deduplication\n// ============================================================================\n\nfunction deduplicateTerms(terms: ConditionNode[]): ConditionNode[] {\n const seen = new Set<string>();\n const result: ConditionNode[] = [];\n\n for (const term of terms) {\n const id = getConditionUniqueId(term);\n if (!seen.has(id)) {\n seen.add(id);\n result.push(term);\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Range Merging\n// ============================================================================\n\n/**\n * Merge compatible range conditions\n * e.g., @media(w >= 400px) & @media(w <= 800px) → @media(400px <= w <= 800px)\n */\nfunction mergeRanges(terms: ConditionNode[]): ConditionNode[] {\n // Group media conditions by dimension\n const mediaByDim = new Map<\n string,\n { conditions: MediaCondition[]; indices: number[] }\n >();\n const containerByDim = new Map<\n string,\n { conditions: ContainerCondition[]; indices: number[] }\n >();\n\n terms.forEach((term, index) => {\n if (term.kind !== 'state') return;\n\n if (\n term.type === 'media' &&\n term.subtype === 'dimension' &&\n !term.negated\n ) {\n const key = term.dimension || 'width';\n if (!mediaByDim.has(key)) {\n mediaByDim.set(key, { conditions: [], indices: [] });\n }\n const group = mediaByDim.get(key)!;\n group.conditions.push(term);\n group.indices.push(index);\n }\n\n if (\n term.type === 'container' &&\n term.subtype === 'dimension' &&\n !term.negated\n ) {\n const key = `${term.containerName || '_'}:${term.dimension || 'width'}`;\n if (!containerByDim.has(key)) {\n containerByDim.set(key, { conditions: [], indices: [] });\n }\n const group = containerByDim.get(key)!;\n group.conditions.push(term);\n group.indices.push(index);\n }\n });\n\n // Track indices to remove\n const indicesToRemove = new Set<number>();\n const mergedTerms: ConditionNode[] = [];\n\n // Merge media conditions\n for (const [_dim, group] of mediaByDim) {\n if (group.conditions.length > 1) {\n const merged = mergeMediaRanges(group.conditions);\n if (merged) {\n group.indices.forEach((i) => indicesToRemove.add(i));\n mergedTerms.push(merged);\n }\n }\n }\n\n // Merge container conditions\n for (const [, group] of containerByDim) {\n if (group.conditions.length > 1) {\n const merged = mergeContainerRanges(group.conditions);\n if (merged) {\n group.indices.forEach((i) => indicesToRemove.add(i));\n mergedTerms.push(merged);\n }\n }\n }\n\n // Build result\n const result: ConditionNode[] = [];\n terms.forEach((term, index) => {\n if (!indicesToRemove.has(index)) {\n result.push(term);\n }\n });\n result.push(...mergedTerms);\n\n return result;\n}\n\n/**\n * Tighten bounds by picking the most restrictive lower and upper bounds\n * from a set of conditions that have lowerBound/upperBound fields.\n */\nfunction tightenBounds(\n conditions: { lowerBound?: NumericBound; upperBound?: NumericBound }[],\n): { lowerBound?: NumericBound; upperBound?: NumericBound } {\n let lowerBound: NumericBound | undefined;\n let upperBound: NumericBound | undefined;\n\n for (const cond of conditions) {\n if (cond.lowerBound) {\n if (\n !lowerBound ||\n (cond.lowerBound.valueNumeric ?? -Infinity) >\n (lowerBound.valueNumeric ?? -Infinity)\n ) {\n lowerBound = cond.lowerBound;\n }\n }\n if (cond.upperBound) {\n if (\n !upperBound ||\n (cond.upperBound.valueNumeric ?? Infinity) <\n (upperBound.valueNumeric ?? Infinity)\n ) {\n upperBound = cond.upperBound;\n }\n }\n }\n\n return { lowerBound, upperBound };\n}\n\nfunction appendBoundsToUniqueId(\n parts: string[],\n lowerBound?: NumericBound,\n upperBound?: NumericBound,\n): void {\n if (lowerBound) {\n parts.push(lowerBound.inclusive ? '>=' : '>');\n parts.push(lowerBound.value);\n }\n if (upperBound) {\n parts.push(upperBound.inclusive ? '<=' : '<');\n parts.push(upperBound.value);\n }\n}\n\nfunction mergeDimensionRanges<T extends MediaCondition | ContainerCondition>(\n conditions: T[],\n idPrefix: string[],\n): T | null {\n if (conditions.length === 0) return null;\n\n const { lowerBound, upperBound } = tightenBounds(conditions);\n const base = conditions[0];\n\n const parts = [...idPrefix];\n appendBoundsToUniqueId(parts, lowerBound, upperBound);\n\n return {\n ...base,\n negated: false,\n raw: buildMergedRaw(base.dimension || 'width', lowerBound, upperBound),\n uniqueId: parts.join(':'),\n lowerBound,\n upperBound,\n };\n}\n\nfunction mergeMediaRanges(conditions: MediaCondition[]): MediaCondition | null {\n const dim = conditions[0]?.dimension ?? 'width';\n return mergeDimensionRanges(conditions, ['media', 'dim', dim]);\n}\n\nfunction mergeContainerRanges(\n conditions: ContainerCondition[],\n): ContainerCondition | null {\n const base = conditions[0];\n if (!base) return null;\n const name = base.containerName || '_';\n const dim = base.dimension ?? 'width';\n return mergeDimensionRanges(conditions, ['container', 'dim', name, dim]);\n}\n\nfunction buildMergedRaw(\n dimension: string,\n lowerBound?: NumericBound,\n upperBound?: NumericBound,\n): string {\n if (lowerBound && upperBound) {\n const lowerOp = lowerBound.inclusive ? '<=' : '<';\n const upperOp = upperBound.inclusive ? '<=' : '<';\n return `@media(${lowerBound.value} ${lowerOp} ${dimension} ${upperOp} ${upperBound.value})`;\n } else if (upperBound) {\n const op = upperBound.inclusive ? '<=' : '<';\n return `@media(${dimension} ${op} ${upperBound.value})`;\n } else if (lowerBound) {\n const op = lowerBound.inclusive ? '>=' : '>';\n return `@media(${dimension} ${op} ${lowerBound.value})`;\n }\n return '@media()';\n}\n\n// ============================================================================\n// Sorting\n// ============================================================================\n\nfunction sortTerms(terms: ConditionNode[]): ConditionNode[] {\n const withIds = terms.map((t) => [getConditionUniqueId(t), t] as const);\n withIds.sort((a, b) => a[0].localeCompare(b[0]));\n return withIds.map(([, t]) => t);\n}\n\n// ============================================================================\n// Absorption\n// ============================================================================\n\n/**\n * Apply the absorption law: removes compound terms that are absorbed by\n * a simple term already present.\n *\n * For AND context: A & (A | B) → A (absorbs OR compounds)\n * For OR context: A | (A & B) → A (absorbs AND compounds)\n */\nfunction applyAbsorption(\n terms: ConditionNode[],\n absorbedOperator: 'OR' | 'AND',\n): ConditionNode[] {\n const simpleIds = new Set<string>();\n for (const term of terms) {\n if (term.kind !== 'compound') {\n simpleIds.add(getConditionUniqueId(term));\n }\n }\n\n return terms.filter((term) => {\n if (term.kind === 'compound' && term.operator === absorbedOperator) {\n for (const child of term.children) {\n if (simpleIds.has(getConditionUniqueId(child))) {\n return false;\n }\n }\n }\n return true;\n });\n}\n\nfunction applyAbsorptionAnd(terms: ConditionNode[]): ConditionNode[] {\n return applyAbsorption(terms, 'OR');\n}\n\nfunction applyAbsorptionOr(terms: ConditionNode[]): ConditionNode[] {\n return applyAbsorption(terms, 'AND');\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/pipeline/warnings.ts
|
|
2
|
+
const defaultWarningHandler = (warning) => {
|
|
3
|
+
console.warn(`[Tasty] ${warning.message}`);
|
|
4
|
+
};
|
|
5
|
+
let warningHandler = defaultWarningHandler;
|
|
6
|
+
/**
|
|
7
|
+
* Emit a structured pipeline warning via the configured handler.
|
|
8
|
+
*/
|
|
9
|
+
function emitWarning(code, message) {
|
|
10
|
+
warningHandler({
|
|
11
|
+
code,
|
|
12
|
+
message
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { emitWarning };
|
|
18
|
+
//# sourceMappingURL=warnings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warnings.js","names":[],"sources":["../../src/pipeline/warnings.ts"],"sourcesContent":["/**\n * Structured warning system for the pipeline.\n *\n * Provides typed warning codes and a configurable handler so consumers\n * can programmatically intercept, suppress, or reroute warnings.\n */\n\nexport type TastyWarningCode = 'INVALID_SELECTOR_AFFIX' | 'XOR_CHAIN_TOO_LONG';\n\nexport interface TastyWarning {\n code: TastyWarningCode;\n message: string;\n}\n\nexport type TastyWarningHandler = (warning: TastyWarning) => void;\n\nconst defaultWarningHandler: TastyWarningHandler = (warning) => {\n console.warn(`[Tasty] ${warning.message}`);\n};\n\nlet warningHandler: TastyWarningHandler = defaultWarningHandler;\n\n/**\n * Set a custom warning handler for pipeline warnings.\n * Returns a function that restores the previous handler.\n */\nexport function setWarningHandler(handler: TastyWarningHandler): () => void {\n const previous = warningHandler;\n warningHandler = handler;\n return () => {\n warningHandler = previous;\n };\n}\n\n/**\n * Emit a structured pipeline warning via the configured handler.\n */\nexport function emitWarning(code: TastyWarningCode, message: string): void {\n warningHandler({ code, message });\n}\n"],"mappings":";AAgBA,MAAM,yBAA8C,YAAY;AAC9D,SAAQ,KAAK,WAAW,QAAQ,UAAU;;AAG5C,IAAI,iBAAsC;;;;AAiB1C,SAAgB,YAAY,MAAwB,SAAuB;AACzE,gBAAe;EAAE;EAAM;EAAS,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { StyleDetails } from "../parser/types.js";
|
|
2
|
+
import { TastyPluginFactory } from "./types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/plugins/okhsl-plugin.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The okhsl function handler for tasty parser.
|
|
7
|
+
* Receives parsed style groups and returns an RGB color string.
|
|
8
|
+
*/
|
|
9
|
+
declare const okhslFunc: (groups: StyleDetails[]) => string;
|
|
10
|
+
/**
|
|
11
|
+
* OKHSL Plugin for Tasty.
|
|
12
|
+
*
|
|
13
|
+
* Adds support for the `okhsl()` color function in tasty styles.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { configure } from '@tenphi/tasty';
|
|
18
|
+
* import { okhslPlugin } from '@tenphi/tasty';
|
|
19
|
+
*
|
|
20
|
+
* configure({
|
|
21
|
+
* plugins: [okhslPlugin()],
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Now you can use okhsl in styles:
|
|
25
|
+
* const Box = tasty({
|
|
26
|
+
* styles: {
|
|
27
|
+
* fill: 'okhsl(240 50% 50%)',
|
|
28
|
+
* },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare const okhslPlugin: TastyPluginFactory;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { okhslFunc, okhslPlugin };
|
|
35
|
+
//# sourceMappingURL=okhsl-plugin.d.ts.map
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { Lru } from "../parser/lru.js";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/okhsl-plugin.ts
|
|
4
|
+
/**
|
|
5
|
+
* OKHSL Plugin for Tasty
|
|
6
|
+
*
|
|
7
|
+
* Converts OKHSL color syntax to RGB notation.
|
|
8
|
+
* Supports angle units: deg, turn, rad, or unitless (degrees).
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* okhsl(240.5 50% 50%)
|
|
12
|
+
* okhsl(240.5deg 50% 50%)
|
|
13
|
+
* okhsl(0.25turn 50% 50%)
|
|
14
|
+
* okhsl(1.57rad 50% 50%)
|
|
15
|
+
*
|
|
16
|
+
* Conversion math inlined from @texel/color to avoid external dependencies.
|
|
17
|
+
*/
|
|
18
|
+
const conversionCache = new Lru(500);
|
|
19
|
+
const OKLab_to_LMS_M = [
|
|
20
|
+
[
|
|
21
|
+
1,
|
|
22
|
+
.3963377773761749,
|
|
23
|
+
.2158037573099136
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
1,
|
|
27
|
+
-.1055613458156586,
|
|
28
|
+
-.0638541728258133
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
1,
|
|
32
|
+
-.0894841775298119,
|
|
33
|
+
-1.2914855480194092
|
|
34
|
+
]
|
|
35
|
+
];
|
|
36
|
+
const LMS_to_linear_sRGB_M = [
|
|
37
|
+
[
|
|
38
|
+
4.076741636075959,
|
|
39
|
+
-3.307711539258062,
|
|
40
|
+
.2309699031821041
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
-1.2684379732850313,
|
|
44
|
+
2.6097573492876878,
|
|
45
|
+
-.3413193760026569
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
-.004196076138675526,
|
|
49
|
+
-.703418617935936,
|
|
50
|
+
1.7076146940746113
|
|
51
|
+
]
|
|
52
|
+
];
|
|
53
|
+
const OKLab_to_linear_sRGB_coefficients = [
|
|
54
|
+
[[-1.8817030993265873, -.8093650129914302], [
|
|
55
|
+
1.19086277,
|
|
56
|
+
1.76576728,
|
|
57
|
+
.59662641,
|
|
58
|
+
.75515197,
|
|
59
|
+
.56771245
|
|
60
|
+
]],
|
|
61
|
+
[[1.8144407988010998, -1.194452667805235], [
|
|
62
|
+
.73956515,
|
|
63
|
+
-.45954404,
|
|
64
|
+
.08285427,
|
|
65
|
+
.12541073,
|
|
66
|
+
-.14503204
|
|
67
|
+
]],
|
|
68
|
+
[[.13110757611180954, 1.813339709266608], [
|
|
69
|
+
1.35733652,
|
|
70
|
+
-.00915799,
|
|
71
|
+
-1.1513021,
|
|
72
|
+
-.50559606,
|
|
73
|
+
.00692167
|
|
74
|
+
]]
|
|
75
|
+
];
|
|
76
|
+
const TAU = 2 * Math.PI;
|
|
77
|
+
const clamp = (value, min, max) => Math.max(Math.min(value, max), min);
|
|
78
|
+
const constrainAngle = (angle) => (angle % 360 + 360) % 360;
|
|
79
|
+
const sRGBLinearToGamma = (val) => {
|
|
80
|
+
const sign = val < 0 ? -1 : 1;
|
|
81
|
+
const abs = Math.abs(val);
|
|
82
|
+
return abs > .0031308 ? sign * (1.055 * Math.pow(abs, 1 / 2.4) - .055) : 12.92 * val;
|
|
83
|
+
};
|
|
84
|
+
const dot3 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
85
|
+
const transform = (input, matrix) => [
|
|
86
|
+
dot3(input, matrix[0]),
|
|
87
|
+
dot3(input, matrix[1]),
|
|
88
|
+
dot3(input, matrix[2])
|
|
89
|
+
];
|
|
90
|
+
const cubed3 = (lms) => [
|
|
91
|
+
lms[0] * lms[0] * lms[0],
|
|
92
|
+
lms[1] * lms[1] * lms[1],
|
|
93
|
+
lms[2] * lms[2] * lms[2]
|
|
94
|
+
];
|
|
95
|
+
const OKLabToLinearSRGB = (lab) => {
|
|
96
|
+
return transform(cubed3(transform(lab, OKLab_to_LMS_M)), LMS_to_linear_sRGB_M);
|
|
97
|
+
};
|
|
98
|
+
const K1 = .206;
|
|
99
|
+
const K2 = .03;
|
|
100
|
+
const K3 = (1 + K1) / (1 + K2);
|
|
101
|
+
const toeInv = (x) => (x ** 2 + K1 * x) / (K3 * (x + K2));
|
|
102
|
+
const dotXY = (a, b) => a[0] * b[0] + a[1] * b[1];
|
|
103
|
+
const computeMaxSaturationOKLC = (a, b) => {
|
|
104
|
+
const okCoeff = OKLab_to_linear_sRGB_coefficients;
|
|
105
|
+
const lmsToRgb = LMS_to_linear_sRGB_M;
|
|
106
|
+
const tmp2 = [a, b];
|
|
107
|
+
const tmp3 = [
|
|
108
|
+
0,
|
|
109
|
+
a,
|
|
110
|
+
b
|
|
111
|
+
];
|
|
112
|
+
let chnlCoeff;
|
|
113
|
+
let chnlLMS;
|
|
114
|
+
if (dotXY(okCoeff[0][0], tmp2) > 1) {
|
|
115
|
+
chnlCoeff = okCoeff[0][1];
|
|
116
|
+
chnlLMS = lmsToRgb[0];
|
|
117
|
+
} else if (dotXY(okCoeff[1][0], tmp2) > 1) {
|
|
118
|
+
chnlCoeff = okCoeff[1][1];
|
|
119
|
+
chnlLMS = lmsToRgb[1];
|
|
120
|
+
} else {
|
|
121
|
+
chnlCoeff = okCoeff[2][1];
|
|
122
|
+
chnlLMS = lmsToRgb[2];
|
|
123
|
+
}
|
|
124
|
+
const [k0, k1, k2, k3, k4] = chnlCoeff;
|
|
125
|
+
const [wl, wm, ws] = chnlLMS;
|
|
126
|
+
let sat = k0 + k1 * a + k2 * b + k3 * (a * a) + k4 * a * b;
|
|
127
|
+
const dotYZ = (mat, vec) => mat[1] * vec[1] + mat[2] * vec[2];
|
|
128
|
+
const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);
|
|
129
|
+
const km = dotYZ(OKLab_to_LMS_M[1], tmp3);
|
|
130
|
+
const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);
|
|
131
|
+
const l_ = 1 + sat * kl;
|
|
132
|
+
const m_ = 1 + sat * km;
|
|
133
|
+
const s_ = 1 + sat * ks;
|
|
134
|
+
const l = l_ * l_ * l_;
|
|
135
|
+
const m = m_ * m_ * m_;
|
|
136
|
+
const s = s_ * s_ * s_;
|
|
137
|
+
const lds = 3 * kl * (l_ * l_);
|
|
138
|
+
const mds = 3 * km * (m_ * m_);
|
|
139
|
+
const sds = 3 * ks * (s_ * s_);
|
|
140
|
+
const lds2 = 6 * (kl * kl) * l_;
|
|
141
|
+
const mds2 = 6 * (km * km) * m_;
|
|
142
|
+
const sds2 = 6 * (ks * ks) * s_;
|
|
143
|
+
const f = wl * l + wm * m + ws * s;
|
|
144
|
+
const f1 = wl * lds + wm * mds + ws * sds;
|
|
145
|
+
const f2 = wl * lds2 + wm * mds2 + ws * sds2;
|
|
146
|
+
sat = sat - f * f1 / (f1 * f1 - .5 * f * f2);
|
|
147
|
+
return sat;
|
|
148
|
+
};
|
|
149
|
+
const findCuspOKLCH = (a, b) => {
|
|
150
|
+
const S_cusp = computeMaxSaturationOKLC(a, b);
|
|
151
|
+
const rgb_at_max = OKLabToLinearSRGB([
|
|
152
|
+
1,
|
|
153
|
+
S_cusp * a,
|
|
154
|
+
S_cusp * b
|
|
155
|
+
]);
|
|
156
|
+
const L_cusp = Math.cbrt(1 / Math.max(Math.max(rgb_at_max[0], rgb_at_max[1]), Math.max(rgb_at_max[2], 0)));
|
|
157
|
+
return [L_cusp, L_cusp * S_cusp];
|
|
158
|
+
};
|
|
159
|
+
const findGamutIntersectionOKLCH = (a, b, l1, c1, l0, cusp) => {
|
|
160
|
+
const lmsToRgb = LMS_to_linear_sRGB_M;
|
|
161
|
+
const tmp3 = [
|
|
162
|
+
0,
|
|
163
|
+
a,
|
|
164
|
+
b
|
|
165
|
+
];
|
|
166
|
+
const floatMax = Number.MAX_VALUE;
|
|
167
|
+
let t;
|
|
168
|
+
const dotYZ = (mat, vec) => mat[1] * vec[1] + mat[2] * vec[2];
|
|
169
|
+
const dotXYZ = (vec, x, y, z) => vec[0] * x + vec[1] * y + vec[2] * z;
|
|
170
|
+
if ((l1 - l0) * cusp[1] - (cusp[0] - l0) * c1 <= 0) {
|
|
171
|
+
const denom = c1 * cusp[0] + cusp[1] * (l0 - l1);
|
|
172
|
+
t = denom === 0 ? 0 : cusp[1] * l0 / denom;
|
|
173
|
+
} else {
|
|
174
|
+
const denom = c1 * (cusp[0] - 1) + cusp[1] * (l0 - l1);
|
|
175
|
+
t = denom === 0 ? 0 : cusp[1] * (l0 - 1) / denom;
|
|
176
|
+
const dl = l1 - l0;
|
|
177
|
+
const dc = c1;
|
|
178
|
+
const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);
|
|
179
|
+
const km = dotYZ(OKLab_to_LMS_M[1], tmp3);
|
|
180
|
+
const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);
|
|
181
|
+
const ldt_ = dl + dc * kl;
|
|
182
|
+
const mdt_ = dl + dc * km;
|
|
183
|
+
const sdt_ = dl + dc * ks;
|
|
184
|
+
const L = l0 * (1 - t) + t * l1;
|
|
185
|
+
const C = t * c1;
|
|
186
|
+
const l_ = L + C * kl;
|
|
187
|
+
const m_ = L + C * km;
|
|
188
|
+
const s_ = L + C * ks;
|
|
189
|
+
const l = l_ * l_ * l_;
|
|
190
|
+
const m = m_ * m_ * m_;
|
|
191
|
+
const s = s_ * s_ * s_;
|
|
192
|
+
const ldt = 3 * ldt_ * l_ * l_;
|
|
193
|
+
const mdt = 3 * mdt_ * m_ * m_;
|
|
194
|
+
const sdt = 3 * sdt_ * s_ * s_;
|
|
195
|
+
const ldt2 = 6 * ldt_ * ldt_ * l_;
|
|
196
|
+
const mdt2 = 6 * mdt_ * mdt_ * m_;
|
|
197
|
+
const sdt2 = 6 * sdt_ * sdt_ * s_;
|
|
198
|
+
const r_ = dotXYZ(lmsToRgb[0], l, m, s) - 1;
|
|
199
|
+
const r1 = dotXYZ(lmsToRgb[0], ldt, mdt, sdt);
|
|
200
|
+
const r2 = dotXYZ(lmsToRgb[0], ldt2, mdt2, sdt2);
|
|
201
|
+
const ur = r1 / (r1 * r1 - .5 * r_ * r2);
|
|
202
|
+
let tr = -r_ * ur;
|
|
203
|
+
const g_ = dotXYZ(lmsToRgb[1], l, m, s) - 1;
|
|
204
|
+
const g1 = dotXYZ(lmsToRgb[1], ldt, mdt, sdt);
|
|
205
|
+
const g2 = dotXYZ(lmsToRgb[1], ldt2, mdt2, sdt2);
|
|
206
|
+
const ug = g1 / (g1 * g1 - .5 * g_ * g2);
|
|
207
|
+
let tg = -g_ * ug;
|
|
208
|
+
const b_ = dotXYZ(lmsToRgb[2], l, m, s) - 1;
|
|
209
|
+
const b1 = dotXYZ(lmsToRgb[2], ldt, mdt, sdt);
|
|
210
|
+
const b2 = dotXYZ(lmsToRgb[2], ldt2, mdt2, sdt2);
|
|
211
|
+
const ub = b1 / (b1 * b1 - .5 * b_ * b2);
|
|
212
|
+
let tb = -b_ * ub;
|
|
213
|
+
tr = ur >= 0 ? tr : floatMax;
|
|
214
|
+
tg = ug >= 0 ? tg : floatMax;
|
|
215
|
+
tb = ub >= 0 ? tb : floatMax;
|
|
216
|
+
t += Math.min(tr, Math.min(tg, tb));
|
|
217
|
+
}
|
|
218
|
+
return t;
|
|
219
|
+
};
|
|
220
|
+
const computeSt = (cusp) => {
|
|
221
|
+
const l = cusp[0];
|
|
222
|
+
const c = cusp[1];
|
|
223
|
+
return [c / l, c / (1 - l)];
|
|
224
|
+
};
|
|
225
|
+
const computeStMid = (a, b) => {
|
|
226
|
+
return [.11516993 + 1 / (7.4477897 + 4.1590124 * b + a * (-2.19557347 + 1.75198401 * b + a * (-2.13704948 - 10.02301043 * b + a * (-4.24894561 + 5.38770819 * b + 4.69891013 * a)))), .11239642 + 1 / (1.6132032 - .68124379 * b + a * (.40370612 + .90148123 * b + a * (-.27087943 + .6122399 * b + a * (.00299215 - .45399568 * b - .14661872 * a))))];
|
|
227
|
+
};
|
|
228
|
+
const getCs = (L, a, b, cusp) => {
|
|
229
|
+
const cMax = findGamutIntersectionOKLCH(a, b, L, 1, L, cusp);
|
|
230
|
+
const stMax = computeSt(cusp);
|
|
231
|
+
const k = cMax / Math.min(L * stMax[0], (1 - L) * stMax[1]);
|
|
232
|
+
const stMid = computeStMid(a, b);
|
|
233
|
+
let ca = L * stMid[0];
|
|
234
|
+
let cb = (1 - L) * stMid[1];
|
|
235
|
+
const cMid = .9 * k * Math.sqrt(Math.sqrt(1 / (1 / ca ** 4 + 1 / cb ** 4)));
|
|
236
|
+
ca = L * .4;
|
|
237
|
+
cb = (1 - L) * .8;
|
|
238
|
+
return [
|
|
239
|
+
Math.sqrt(1 / (1 / ca ** 2 + 1 / cb ** 2)),
|
|
240
|
+
cMid,
|
|
241
|
+
cMax
|
|
242
|
+
];
|
|
243
|
+
};
|
|
244
|
+
const OKHSLToOKLab = (hsl) => {
|
|
245
|
+
let h = hsl[0];
|
|
246
|
+
const s = hsl[1];
|
|
247
|
+
const l = hsl[2];
|
|
248
|
+
const L = toeInv(l);
|
|
249
|
+
let a = 0;
|
|
250
|
+
let b = 0;
|
|
251
|
+
h = constrainAngle(h) / 360;
|
|
252
|
+
if (L !== 0 && L !== 1 && s !== 0) {
|
|
253
|
+
const a_ = Math.cos(TAU * h);
|
|
254
|
+
const b_ = Math.sin(TAU * h);
|
|
255
|
+
const [c0, cMid, cMax] = getCs(L, a_, b_, findCuspOKLCH(a_, b_));
|
|
256
|
+
const mid = .8;
|
|
257
|
+
const midInv = 1.25;
|
|
258
|
+
let t, k0, k1, k2;
|
|
259
|
+
if (s < mid) {
|
|
260
|
+
t = midInv * s;
|
|
261
|
+
k0 = 0;
|
|
262
|
+
k1 = mid * c0;
|
|
263
|
+
k2 = 1 - k1 / cMid;
|
|
264
|
+
} else {
|
|
265
|
+
t = 5 * (s - .8);
|
|
266
|
+
k0 = cMid;
|
|
267
|
+
k1 = .2 * cMid ** 2 * 1.25 ** 2 / c0;
|
|
268
|
+
k2 = 1 - k1 / (cMax - cMid);
|
|
269
|
+
}
|
|
270
|
+
const c = k0 + t * k1 / (1 - k2 * t);
|
|
271
|
+
a = c * a_;
|
|
272
|
+
b = c * b_;
|
|
273
|
+
}
|
|
274
|
+
return [
|
|
275
|
+
L,
|
|
276
|
+
a,
|
|
277
|
+
b
|
|
278
|
+
];
|
|
279
|
+
};
|
|
280
|
+
const okhslToSRGB = (h, s, l) => {
|
|
281
|
+
const linearRGB = OKLabToLinearSRGB(OKHSLToOKLab([
|
|
282
|
+
h,
|
|
283
|
+
s,
|
|
284
|
+
l
|
|
285
|
+
]));
|
|
286
|
+
return [
|
|
287
|
+
clamp(sRGBLinearToGamma(linearRGB[0]), 0, 1),
|
|
288
|
+
clamp(sRGBLinearToGamma(linearRGB[1]), 0, 1),
|
|
289
|
+
clamp(sRGBLinearToGamma(linearRGB[2]), 0, 1)
|
|
290
|
+
];
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* Parse an angle value with optional unit.
|
|
294
|
+
* Supports: deg, turn, rad, or unitless (treated as degrees).
|
|
295
|
+
*/
|
|
296
|
+
const parseAngle = (value) => {
|
|
297
|
+
const match = value.match(/^([+-]?\d*\.?\d+)(deg|turn|rad)?$/);
|
|
298
|
+
if (!match) return 0;
|
|
299
|
+
const num = parseFloat(match[1]);
|
|
300
|
+
switch (match[2]) {
|
|
301
|
+
case "turn": return num * 360;
|
|
302
|
+
case "rad": return num * 180 / Math.PI;
|
|
303
|
+
default: return num;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Parse a percentage value (e.g., "50%") to a 0-1 range.
|
|
308
|
+
*/
|
|
309
|
+
const parsePercentage = (value) => {
|
|
310
|
+
const match = value.match(/^([+-]?\d*\.?\d+)%?$/);
|
|
311
|
+
if (!match) return 0;
|
|
312
|
+
const num = parseFloat(match[1]);
|
|
313
|
+
return value.includes("%") ? num / 100 : num;
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* The okhsl function handler for tasty parser.
|
|
317
|
+
* Receives parsed style groups and returns an RGB color string.
|
|
318
|
+
*/
|
|
319
|
+
const okhslFunc = (groups) => {
|
|
320
|
+
if (groups.length === 0 || groups[0].all.length < 3) {
|
|
321
|
+
console.warn("[okhsl] Expected 3 values (H S L), got:", groups);
|
|
322
|
+
return "rgb(0% 0% 0%)";
|
|
323
|
+
}
|
|
324
|
+
const group = groups[0];
|
|
325
|
+
const tokens = group.all;
|
|
326
|
+
const alpha = group.parts.length > 1 && group.parts[1].all.length > 0 ? group.parts[1].output : void 0;
|
|
327
|
+
const cacheKey = tokens.slice(0, 3).join(" ") + (alpha ? ` / ${alpha}` : "");
|
|
328
|
+
const cached = conversionCache.get(cacheKey);
|
|
329
|
+
if (cached) return cached;
|
|
330
|
+
const h = parseAngle(tokens[0]);
|
|
331
|
+
const s = parsePercentage(tokens[1]);
|
|
332
|
+
const l = parsePercentage(tokens[2]);
|
|
333
|
+
const [r, g, b] = okhslToSRGB(h, clamp(s, 0, 1), clamp(l, 0, 1));
|
|
334
|
+
const format = (n) => {
|
|
335
|
+
const pct = n * 100;
|
|
336
|
+
return parseFloat(pct.toFixed(1)).toString() + "%";
|
|
337
|
+
};
|
|
338
|
+
const result = alpha ? `rgb(${format(r)} ${format(g)} ${format(b)} / ${alpha})` : `rgb(${format(r)} ${format(g)} ${format(b)})`;
|
|
339
|
+
conversionCache.set(cacheKey, result);
|
|
340
|
+
return result;
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* OKHSL Plugin for Tasty.
|
|
344
|
+
*
|
|
345
|
+
* Adds support for the `okhsl()` color function in tasty styles.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```ts
|
|
349
|
+
* import { configure } from '@tenphi/tasty';
|
|
350
|
+
* import { okhslPlugin } from '@tenphi/tasty';
|
|
351
|
+
*
|
|
352
|
+
* configure({
|
|
353
|
+
* plugins: [okhslPlugin()],
|
|
354
|
+
* });
|
|
355
|
+
*
|
|
356
|
+
* // Now you can use okhsl in styles:
|
|
357
|
+
* const Box = tasty({
|
|
358
|
+
* styles: {
|
|
359
|
+
* fill: 'okhsl(240 50% 50%)',
|
|
360
|
+
* },
|
|
361
|
+
* });
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
const okhslPlugin = () => ({
|
|
365
|
+
name: "okhsl",
|
|
366
|
+
funcs: { okhsl: okhslFunc }
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
//#endregion
|
|
370
|
+
export { okhslFunc, okhslPlugin };
|
|
371
|
+
//# sourceMappingURL=okhsl-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"okhsl-plugin.js","names":[],"sources":["../../src/plugins/okhsl-plugin.ts"],"sourcesContent":["/**\n * OKHSL Plugin for Tasty\n *\n * Converts OKHSL color syntax to RGB notation.\n * Supports angle units: deg, turn, rad, or unitless (degrees).\n *\n * Examples:\n * okhsl(240.5 50% 50%)\n * okhsl(240.5deg 50% 50%)\n * okhsl(0.25turn 50% 50%)\n * okhsl(1.57rad 50% 50%)\n *\n * Conversion math inlined from @texel/color to avoid external dependencies.\n */\n\nimport { Lru } from '../parser/lru';\n\nimport type { StyleDetails } from '../parser/types';\nimport type { TastyPlugin, TastyPluginFactory } from './types';\n\n// Cache for OKHSL to RGB conversions\nconst conversionCache = new Lru<string, string>(500);\n\n// ============================================================================\n// Conversion Matrices (from texel-color)\n// ============================================================================\n\nconst OKLab_to_LMS_M: [number, number, number][] = [\n [1.0, 0.3963377773761749, 0.2158037573099136],\n [1.0, -0.1055613458156586, -0.0638541728258133],\n [1.0, -0.0894841775298119, -1.2914855480194092],\n];\n\nconst LMS_to_linear_sRGB_M: [number, number, number][] = [\n [4.076741636075959, -3.307711539258062, 0.2309699031821041],\n [-1.2684379732850313, 2.6097573492876878, -0.3413193760026569],\n [-0.004196076138675526, -0.703418617935936, 1.7076146940746113],\n];\n\nconst OKLab_to_linear_sRGB_coefficients: [\n [[number, number], number[]],\n [[number, number], number[]],\n [[number, number], number[]],\n] = [\n [\n [-1.8817030993265873, -0.8093650129914302],\n [1.19086277, 1.76576728, 0.59662641, 0.75515197, 0.56771245],\n ],\n [\n [1.8144407988010998, -1.194452667805235],\n [0.73956515, -0.45954404, 0.08285427, 0.12541073, -0.14503204],\n ],\n [\n [0.13110757611180954, 1.813339709266608],\n [1.35733652, -0.00915799, -1.1513021, -0.50559606, 0.00692167],\n ],\n];\n\n// ============================================================================\n// Math Utilities\n// ============================================================================\n\nconst TAU = 2 * Math.PI;\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.max(Math.min(value, max), min);\n\nconst constrainAngle = (angle: number): number => ((angle % 360) + 360) % 360;\n\nconst copySign = (to: number, from: number): number =>\n Math.sign(to) === Math.sign(from) ? to : -to;\n\nconst _spow = (base: number, exp: number): number =>\n copySign(Math.abs(base) ** exp, base);\n\n// ============================================================================\n// sRGB Gamma Conversion\n// ============================================================================\n\nconst sRGBLinearToGamma = (val: number): number => {\n const sign = val < 0 ? -1 : 1;\n const abs = Math.abs(val);\n return abs > 0.0031308\n ? sign * (1.055 * Math.pow(abs, 1 / 2.4) - 0.055)\n : 12.92 * val;\n};\n\n// ============================================================================\n// Matrix Operations\n// ============================================================================\n\ntype Vec3 = [number, number, number];\n\nconst dot3 = (a: Vec3, b: Vec3): number =>\n a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n\nconst transform = (input: Vec3, matrix: Vec3[]): Vec3 => [\n dot3(input, matrix[0]),\n dot3(input, matrix[1]),\n dot3(input, matrix[2]),\n];\n\nconst cubed3 = (lms: Vec3): Vec3 => [\n lms[0] * lms[0] * lms[0],\n lms[1] * lms[1] * lms[1],\n lms[2] * lms[2] * lms[2],\n];\n\n// ============================================================================\n// OKLab Conversion\n// ============================================================================\n\nconst OKLabToLinearSRGB = (lab: Vec3): Vec3 => {\n const lms = transform(lab, OKLab_to_LMS_M);\n const cubedLms = cubed3(lms);\n return transform(cubedLms, LMS_to_linear_sRGB_M);\n};\n\n// ============================================================================\n// OKHSL to OKLab Conversion (from texel-color)\n// ============================================================================\n\nconst K1 = 0.206;\nconst K2 = 0.03;\nconst K3 = (1.0 + K1) / (1.0 + K2);\n\nconst _toe = (x: number): number =>\n 0.5 *\n (K3 * x - K1 + Math.sqrt((K3 * x - K1) * (K3 * x - K1) + 4 * K2 * K3 * x));\n\nconst toeInv = (x: number): number => (x ** 2 + K1 * x) / (K3 * (x + K2));\n\nconst dotXY = (a: [number, number], b: [number, number]): number =>\n a[0] * b[0] + a[1] * b[1];\n\nconst computeMaxSaturationOKLC = (a: number, b: number): number => {\n const okCoeff = OKLab_to_linear_sRGB_coefficients;\n const lmsToRgb = LMS_to_linear_sRGB_M;\n const tmp2: [number, number] = [a, b];\n const tmp3: Vec3 = [0, a, b];\n\n let chnlCoeff: number[];\n let chnlLMS: Vec3;\n\n if (dotXY(okCoeff[0][0], tmp2) > 1) {\n chnlCoeff = okCoeff[0][1];\n chnlLMS = lmsToRgb[0];\n } else if (dotXY(okCoeff[1][0], tmp2) > 1) {\n chnlCoeff = okCoeff[1][1];\n chnlLMS = lmsToRgb[1];\n } else {\n chnlCoeff = okCoeff[2][1];\n chnlLMS = lmsToRgb[2];\n }\n\n const [k0, k1, k2, k3, k4] = chnlCoeff;\n const [wl, wm, ws] = chnlLMS;\n\n let sat = k0 + k1 * a + k2 * b + k3 * (a * a) + k4 * a * b;\n\n const dotYZ = (mat: Vec3, vec: Vec3): number =>\n mat[1] * vec[1] + mat[2] * vec[2];\n\n const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);\n const km = dotYZ(OKLab_to_LMS_M[1], tmp3);\n const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);\n\n const l_ = 1.0 + sat * kl;\n const m_ = 1.0 + sat * km;\n const s_ = 1.0 + sat * ks;\n\n const l = l_ * l_ * l_;\n const m = m_ * m_ * m_;\n const s = s_ * s_ * s_;\n\n const lds = 3.0 * kl * (l_ * l_);\n const mds = 3.0 * km * (m_ * m_);\n const sds = 3.0 * ks * (s_ * s_);\n\n const lds2 = 6.0 * (kl * kl) * l_;\n const mds2 = 6.0 * (km * km) * m_;\n const sds2 = 6.0 * (ks * ks) * s_;\n\n const f = wl * l + wm * m + ws * s;\n const f1 = wl * lds + wm * mds + ws * sds;\n const f2 = wl * lds2 + wm * mds2 + ws * sds2;\n\n sat = sat - (f * f1) / (f1 * f1 - 0.5 * f * f2);\n\n return sat;\n};\n\nconst findCuspOKLCH = (a: number, b: number): [number, number] => {\n const _lmsToRgb = LMS_to_linear_sRGB_M;\n const S_cusp = computeMaxSaturationOKLC(a, b);\n\n const lab: Vec3 = [1, S_cusp * a, S_cusp * b];\n const rgb_at_max = OKLabToLinearSRGB(lab);\n\n const L_cusp = Math.cbrt(\n 1 /\n Math.max(\n Math.max(rgb_at_max[0], rgb_at_max[1]),\n Math.max(rgb_at_max[2], 0.0),\n ),\n );\n const C_cusp = L_cusp * S_cusp;\n\n return [L_cusp, C_cusp];\n};\n\nconst findGamutIntersectionOKLCH = (\n a: number,\n b: number,\n l1: number,\n c1: number,\n l0: number,\n cusp: [number, number],\n): number => {\n const lmsToRgb = LMS_to_linear_sRGB_M;\n const tmp3: Vec3 = [0, a, b];\n const floatMax = Number.MAX_VALUE;\n\n let t: number;\n\n const dotYZ = (mat: Vec3, vec: Vec3): number =>\n mat[1] * vec[1] + mat[2] * vec[2];\n const dotXYZ = (vec: Vec3, x: number, y: number, z: number): number =>\n vec[0] * x + vec[1] * y + vec[2] * z;\n\n if ((l1 - l0) * cusp[1] - (cusp[0] - l0) * c1 <= 0.0) {\n const denom = c1 * cusp[0] + cusp[1] * (l0 - l1);\n t = denom === 0 ? 0 : (cusp[1] * l0) / denom;\n } else {\n const denom = c1 * (cusp[0] - 1.0) + cusp[1] * (l0 - l1);\n t = denom === 0 ? 0 : (cusp[1] * (l0 - 1.0)) / denom;\n\n const dl = l1 - l0;\n const dc = c1;\n\n const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);\n const km = dotYZ(OKLab_to_LMS_M[1], tmp3);\n const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);\n\n const ldt_ = dl + dc * kl;\n const mdt_ = dl + dc * km;\n const sdt_ = dl + dc * ks;\n\n const L = l0 * (1.0 - t) + t * l1;\n const C = t * c1;\n\n const l_ = L + C * kl;\n const m_ = L + C * km;\n const s_ = L + C * ks;\n\n const l = l_ * l_ * l_;\n const m = m_ * m_ * m_;\n const s = s_ * s_ * s_;\n\n const ldt = 3 * ldt_ * l_ * l_;\n const mdt = 3 * mdt_ * m_ * m_;\n const sdt = 3 * sdt_ * s_ * s_;\n\n const ldt2 = 6 * ldt_ * ldt_ * l_;\n const mdt2 = 6 * mdt_ * mdt_ * m_;\n const sdt2 = 6 * sdt_ * sdt_ * s_;\n\n const r_ = dotXYZ(lmsToRgb[0], l, m, s) - 1;\n const r1 = dotXYZ(lmsToRgb[0], ldt, mdt, sdt);\n const r2 = dotXYZ(lmsToRgb[0], ldt2, mdt2, sdt2);\n\n const ur = r1 / (r1 * r1 - 0.5 * r_ * r2);\n let tr = -r_ * ur;\n\n const g_ = dotXYZ(lmsToRgb[1], l, m, s) - 1;\n const g1 = dotXYZ(lmsToRgb[1], ldt, mdt, sdt);\n const g2 = dotXYZ(lmsToRgb[1], ldt2, mdt2, sdt2);\n\n const ug = g1 / (g1 * g1 - 0.5 * g_ * g2);\n let tg = -g_ * ug;\n\n const b_ = dotXYZ(lmsToRgb[2], l, m, s) - 1;\n const b1 = dotXYZ(lmsToRgb[2], ldt, mdt, sdt);\n const b2 = dotXYZ(lmsToRgb[2], ldt2, mdt2, sdt2);\n\n const ub = b1 / (b1 * b1 - 0.5 * b_ * b2);\n let tb = -b_ * ub;\n\n tr = ur >= 0.0 ? tr : floatMax;\n tg = ug >= 0.0 ? tg : floatMax;\n tb = ub >= 0.0 ? tb : floatMax;\n\n t += Math.min(tr, Math.min(tg, tb));\n }\n\n return t;\n};\n\nconst computeSt = (cusp: [number, number]): [number, number] => {\n const l = cusp[0];\n const c = cusp[1];\n return [c / l, c / (1 - l)];\n};\n\nconst computeStMid = (a: number, b: number): [number, number] => {\n const s =\n 0.11516993 +\n 1.0 /\n (7.4477897 +\n 4.1590124 * b +\n a *\n (-2.19557347 +\n 1.75198401 * b +\n a *\n (-2.13704948 -\n 10.02301043 * b +\n a * (-4.24894561 + 5.38770819 * b + 4.69891013 * a))));\n\n const t =\n 0.11239642 +\n 1.0 /\n (1.6132032 -\n 0.68124379 * b +\n a *\n (0.40370612 +\n 0.90148123 * b +\n a *\n (-0.27087943 +\n 0.6122399 * b +\n a * (0.00299215 - 0.45399568 * b - 0.14661872 * a))));\n\n return [s, t];\n};\n\nconst getCs = (\n L: number,\n a: number,\n b: number,\n cusp: [number, number],\n): [number, number, number] => {\n const cMax = findGamutIntersectionOKLCH(a, b, L, 1, L, cusp);\n const stMax = computeSt(cusp);\n\n const k = cMax / Math.min(L * stMax[0], (1 - L) * stMax[1]);\n\n const stMid = computeStMid(a, b);\n\n let ca = L * stMid[0];\n let cb = (1.0 - L) * stMid[1];\n const cMid =\n 0.9 * k * Math.sqrt(Math.sqrt(1.0 / (1.0 / ca ** 4 + 1.0 / cb ** 4)));\n\n ca = L * 0.4;\n cb = (1.0 - L) * 0.8;\n const c0 = Math.sqrt(1.0 / (1.0 / ca ** 2 + 1.0 / cb ** 2));\n\n return [c0, cMid, cMax];\n};\n\nconst OKHSLToOKLab = (hsl: Vec3): Vec3 => {\n let h = hsl[0];\n const s = hsl[1];\n const l = hsl[2];\n\n const L = toeInv(l);\n let a = 0;\n let b = 0;\n\n h = constrainAngle(h) / 360.0;\n\n if (L !== 0.0 && L !== 1.0 && s !== 0) {\n const a_ = Math.cos(TAU * h);\n const b_ = Math.sin(TAU * h);\n\n const cusp = findCuspOKLCH(a_, b_);\n const Cs = getCs(L, a_, b_, cusp);\n const [c0, cMid, cMax] = Cs;\n\n const mid = 0.8;\n const midInv = 1.25;\n let t: number, k0: number, k1: number, k2: number;\n\n if (s < mid) {\n t = midInv * s;\n k0 = 0.0;\n k1 = mid * c0;\n k2 = 1.0 - k1 / cMid;\n } else {\n t = 5 * (s - 0.8);\n k0 = cMid;\n k1 = (0.2 * cMid ** 2 * 1.25 ** 2) / c0;\n k2 = 1.0 - k1 / (cMax - cMid);\n }\n\n const c = k0 + (t * k1) / (1.0 - k2 * t);\n\n a = c * a_;\n b = c * b_;\n }\n\n return [L, a, b];\n};\n\n// ============================================================================\n// OKHSL to sRGB Conversion\n// ============================================================================\n\nconst okhslToSRGB = (h: number, s: number, l: number): Vec3 => {\n // h: 0-360, s: 0-1, l: 0-1\n const oklab = OKHSLToOKLab([h, s, l]);\n const linearRGB = OKLabToLinearSRGB(oklab);\n\n // Apply gamma correction and clamp\n return [\n clamp(sRGBLinearToGamma(linearRGB[0]), 0, 1),\n clamp(sRGBLinearToGamma(linearRGB[1]), 0, 1),\n clamp(sRGBLinearToGamma(linearRGB[2]), 0, 1),\n ];\n};\n\n// ============================================================================\n// Parsing Utilities\n// ============================================================================\n\n/**\n * Parse an angle value with optional unit.\n * Supports: deg, turn, rad, or unitless (treated as degrees).\n */\nconst parseAngle = (value: string): number => {\n const match = value.match(/^([+-]?\\d*\\.?\\d+)(deg|turn|rad)?$/);\n if (!match) return 0;\n\n const num = parseFloat(match[1]);\n const unit = match[2];\n\n switch (unit) {\n case 'turn':\n return num * 360;\n case 'rad':\n return (num * 180) / Math.PI;\n case 'deg':\n default:\n return num;\n }\n};\n\n/**\n * Parse a percentage value (e.g., \"50%\") to a 0-1 range.\n */\nconst parsePercentage = (value: string): number => {\n const match = value.match(/^([+-]?\\d*\\.?\\d+)%?$/);\n if (!match) return 0;\n\n const num = parseFloat(match[1]);\n // If the value includes %, divide by 100, otherwise assume 0-1 range\n return value.includes('%') ? num / 100 : num;\n};\n\n// ============================================================================\n// Plugin Implementation\n// ============================================================================\n\n/**\n * The okhsl function handler for tasty parser.\n * Receives parsed style groups and returns an RGB color string.\n */\nconst okhslFunc = (groups: StyleDetails[]): string => {\n // We expect a single group with 3 values: H, S, L\n // and an optional slash-separated alpha part\n if (groups.length === 0 || groups[0].all.length < 3) {\n console.warn('[okhsl] Expected 3 values (H S L), got:', groups);\n return 'rgb(0% 0% 0%)';\n }\n\n const group = groups[0];\n const tokens = group.all;\n\n // Alpha is in the second slash-separated part (e.g., okhsl(240 50% 50% / .5))\n const alpha =\n group.parts.length > 1 && group.parts[1].all.length > 0\n ? group.parts[1].output\n : undefined;\n\n // Create cache key from input tokens\n const cacheKey = tokens.slice(0, 3).join(' ') + (alpha ? ` / ${alpha}` : '');\n const cached = conversionCache.get(cacheKey);\n if (cached) return cached;\n\n const h = parseAngle(tokens[0]);\n const s = parsePercentage(tokens[1]);\n const l = parsePercentage(tokens[2]);\n\n const [r, g, b] = okhslToSRGB(h, clamp(s, 0, 1), clamp(l, 0, 1));\n\n const format = (n: number): string => {\n const pct = n * 100;\n return parseFloat(pct.toFixed(1)).toString() + '%';\n };\n\n const result = alpha\n ? `rgb(${format(r)} ${format(g)} ${format(b)} / ${alpha})`\n : `rgb(${format(r)} ${format(g)} ${format(b)})`;\n\n conversionCache.set(cacheKey, result);\n return result;\n};\n\n/**\n * OKHSL Plugin for Tasty.\n *\n * Adds support for the `okhsl()` color function in tasty styles.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n *\n * // Now you can use okhsl in styles:\n * const Box = tasty({\n * styles: {\n * fill: 'okhsl(240 50% 50%)',\n * },\n * });\n * ```\n */\nexport const okhslPlugin: TastyPluginFactory = (): TastyPlugin => ({\n name: 'okhsl',\n funcs: {\n okhsl: okhslFunc,\n },\n});\n\n// Export the raw function for direct use if needed\nexport { okhslFunc };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { StyleDetails, UnitHandler } from "../parser/types.js";
|
|
2
|
+
import { StyleHandlerDefinition } from "../utils/styles.js";
|
|
3
|
+
import { RecipeStyles } from "../styles/types.js";
|
|
4
|
+
|
|
5
|
+
//#region src/plugins/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A tasty plugin that extends the style system with custom functions, units, states, or handlers.
|
|
8
|
+
*/
|
|
9
|
+
interface TastyPlugin {
|
|
10
|
+
/** Unique name for the plugin (used for debugging and conflict detection) */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Custom functions that transform parsed style groups into CSS values */
|
|
13
|
+
funcs?: Record<string, (groups: StyleDetails[]) => string>;
|
|
14
|
+
/** Custom units that transform numeric values (e.g., `2x` → `calc(2 * var(--gap))`) */
|
|
15
|
+
units?: Record<string, string | UnitHandler>;
|
|
16
|
+
/** Custom state aliases (e.g., `'@mobile': '@media(w < 768px)'`) */
|
|
17
|
+
states?: Record<string, string>;
|
|
18
|
+
/**
|
|
19
|
+
* Custom style handlers that transform style properties into CSS declarations.
|
|
20
|
+
* Handlers replace built-in handlers for the same style name.
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* handlers: {
|
|
24
|
+
* // Simple handler - lookup style inferred from key
|
|
25
|
+
* fill: ({ fill }) => fill ? { 'background-color': fill } : undefined,
|
|
26
|
+
* // Multi-property handler
|
|
27
|
+
* spacing: [['gap', 'padding'], ({ gap, padding }) => ({ ... })],
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
handlers?: Record<string, StyleHandlerDefinition>;
|
|
32
|
+
/** Predefined tokens replaced during style parsing (`$name` or `#name`) */
|
|
33
|
+
tokens?: Record<`$${string}` | `#${string}`, string | number>;
|
|
34
|
+
/**
|
|
35
|
+
* Predefined style recipes -- named style bundles that can be applied via `recipe` style property.
|
|
36
|
+
* Recipe values are flat tasty styles (no sub-element keys).
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* recipes: {
|
|
40
|
+
* card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
|
|
41
|
+
* elevated: { shadow: '2x 2x 4x #shadow' },
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
recipes?: Record<string, RecipeStyles>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A factory function that creates a TastyPlugin.
|
|
49
|
+
* Can optionally accept configuration options.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* // Plugin without options
|
|
54
|
+
* const okhslPlugin: TastyPluginFactory = () => ({
|
|
55
|
+
* name: 'okhsl',
|
|
56
|
+
* funcs: { okhsl: okhslFunc },
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* // Plugin with options
|
|
60
|
+
* const debugPlugin: TastyPluginFactory<{ verbose: boolean }> = (options) => ({
|
|
61
|
+
* name: 'debug',
|
|
62
|
+
* funcs: { debug: createDebugFunc(options.verbose) },
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
type TastyPluginFactory<TOptions = void> = TOptions extends void ? () => TastyPlugin : (options: TOptions) => TastyPlugin;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { TastyPlugin, TastyPluginFactory };
|
|
69
|
+
//# sourceMappingURL=types.d.ts.map
|