@tenphi/tasty 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ssr/async-storage.js → async-storage-B7_o6FKt.js} +2 -2
- package/dist/async-storage-B7_o6FKt.js.map +1 -0
- package/dist/{ssr/collector.js → collector-DrgDE7QB.js} +5 -10
- package/dist/collector-DrgDE7QB.js.map +1 -0
- package/dist/{ssr/collector.d.ts → collector-LuU1vZ68.d.ts} +3 -3
- package/dist/config-_aQ_PZ-P.js +10131 -0
- package/dist/config-_aQ_PZ-P.js.map +1 -0
- package/dist/config-vuCRkBWX.d.ts +884 -0
- package/dist/{ssr/context.js → context-CkSg-kDT.js} +11 -3
- package/dist/context-CkSg-kDT.js.map +1 -0
- package/dist/core/index.d.ts +5 -34
- package/dist/core/index.js +6 -27
- package/dist/core-BqO8pplb.js +1592 -0
- package/dist/core-BqO8pplb.js.map +1 -0
- package/dist/{zero/extractor.js → css-writer-D--REwtp.js} +74 -11
- package/dist/css-writer-D--REwtp.js.map +1 -0
- package/dist/{ssr/format-global-rules.js → format-global-rules-Dbc_1tc3.js} +2 -2
- package/dist/format-global-rules-Dbc_1tc3.js.map +1 -0
- package/dist/format-rules-xwteB7a1.js +143 -0
- package/dist/format-rules-xwteB7a1.js.map +1 -0
- package/dist/{ssr/hydrate.js → hydrate-BvPT4ndL.js} +3 -3
- package/dist/hydrate-BvPT4ndL.js.map +1 -0
- package/dist/index-ZRxZWzlj.d.ts +1602 -0
- package/dist/{styles/types.d.ts → index-dUtwpOux.d.ts} +707 -5
- package/dist/index.d.ts +5 -51
- package/dist/index.js +732 -36
- package/dist/index.js.map +1 -0
- package/dist/keyframes-ClPFWy33.js +587 -0
- package/dist/keyframes-ClPFWy33.js.map +1 -0
- package/dist/{utils/merge-styles.js → merge-styles-BUQsEpbv.js} +3 -4
- package/dist/merge-styles-BUQsEpbv.js.map +1 -0
- package/dist/{utils/merge-styles.d.ts → merge-styles-CtDJMhpJ.d.ts} +3 -3
- package/dist/{utils/resolve-recipes.js → resolve-recipes-C0-AMzCz.js} +4 -6
- package/dist/resolve-recipes-C0-AMzCz.js.map +1 -0
- package/dist/ssr/astro-client.js +1 -1
- package/dist/ssr/astro.js +4 -4
- package/dist/ssr/index.d.ts +44 -4
- package/dist/ssr/index.js +4 -4
- package/dist/ssr/next.d.ts +1 -1
- package/dist/ssr/next.js +6 -6
- package/dist/ssr/next.js.map +1 -1
- package/dist/static/index.d.ts +91 -5
- package/dist/static/index.js +49 -3
- package/dist/static/index.js.map +1 -0
- package/dist/zero/babel.d.ts +1 -1
- package/dist/zero/babel.js +10 -6
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +67 -3
- package/dist/zero/index.js +1 -2
- package/docs/injector.md +2 -2
- package/package.json +10 -9
- package/dist/_virtual/_rolldown/runtime.js +0 -7
- package/dist/chunks/cacheKey.d.ts +0 -1
- package/dist/chunks/cacheKey.js +0 -77
- package/dist/chunks/cacheKey.js.map +0 -1
- package/dist/chunks/definitions.d.ts +0 -37
- package/dist/chunks/definitions.js +0 -258
- package/dist/chunks/definitions.js.map +0 -1
- package/dist/chunks/index.d.ts +0 -1
- package/dist/chunks/renderChunk.d.ts +0 -1
- package/dist/chunks/renderChunk.js +0 -59
- package/dist/chunks/renderChunk.js.map +0 -1
- package/dist/compute-styles.d.ts +0 -31
- package/dist/compute-styles.js +0 -322
- package/dist/compute-styles.js.map +0 -1
- package/dist/config.d.ts +0 -407
- package/dist/config.js +0 -591
- package/dist/config.js.map +0 -1
- package/dist/counter-style/index.js +0 -51
- package/dist/counter-style/index.js.map +0 -1
- package/dist/debug.d.ts +0 -89
- package/dist/debug.js +0 -453
- package/dist/debug.js.map +0 -1
- package/dist/font-face/index.js +0 -63
- package/dist/font-face/index.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -7
- package/dist/hooks/useCounterStyle.d.ts +0 -36
- package/dist/hooks/useCounterStyle.js +0 -65
- package/dist/hooks/useCounterStyle.js.map +0 -1
- package/dist/hooks/useFontFace.d.ts +0 -45
- package/dist/hooks/useFontFace.js +0 -66
- package/dist/hooks/useFontFace.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -46
- package/dist/hooks/useGlobalStyles.js +0 -88
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -58
- package/dist/hooks/useKeyframes.js +0 -55
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -81
- package/dist/hooks/useProperty.js +0 -96
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -22
- package/dist/hooks/useRawCSS.js +0 -103
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -31
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -182
- package/dist/injector/index.js +0 -185
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -198
- package/dist/injector/injector.js +0 -651
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -132
- package/dist/injector/sheet-manager.js +0 -699
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -235
- package/dist/keyframes/index.js +0 -206
- package/dist/keyframes/index.js.map +0 -1
- package/dist/parser/classify.js +0 -319
- package/dist/parser/classify.js.map +0 -1
- package/dist/parser/const.js +0 -60
- package/dist/parser/const.js.map +0 -1
- package/dist/parser/lru.js +0 -109
- package/dist/parser/lru.js.map +0 -1
- package/dist/parser/parser.d.ts +0 -25
- package/dist/parser/parser.js +0 -115
- package/dist/parser/parser.js.map +0 -1
- package/dist/parser/tokenizer.js +0 -69
- package/dist/parser/tokenizer.js.map +0 -1
- package/dist/parser/types.d.ts +0 -51
- package/dist/parser/types.js +0 -46
- package/dist/parser/types.js.map +0 -1
- package/dist/pipeline/conditions.d.ts +0 -134
- package/dist/pipeline/conditions.js +0 -406
- package/dist/pipeline/conditions.js.map +0 -1
- package/dist/pipeline/exclusive.js +0 -389
- package/dist/pipeline/exclusive.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -55
- package/dist/pipeline/index.js +0 -749
- package/dist/pipeline/index.js.map +0 -1
- package/dist/pipeline/materialize-contradictions.js +0 -125
- package/dist/pipeline/materialize-contradictions.js.map +0 -1
- package/dist/pipeline/materialize.js +0 -1038
- package/dist/pipeline/materialize.js.map +0 -1
- package/dist/pipeline/parseStateKey.d.ts +0 -15
- package/dist/pipeline/parseStateKey.js +0 -446
- package/dist/pipeline/parseStateKey.js.map +0 -1
- package/dist/pipeline/simplify.js +0 -725
- package/dist/pipeline/simplify.js.map +0 -1
- package/dist/pipeline/warnings.js +0 -18
- package/dist/pipeline/warnings.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/okhsl-plugin.d.ts +0 -35
- package/dist/plugins/okhsl-plugin.js +0 -97
- package/dist/plugins/okhsl-plugin.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -87
- package/dist/properties/index.js +0 -222
- package/dist/properties/index.js.map +0 -1
- package/dist/properties/property-type-resolver.d.ts +0 -24
- package/dist/properties/property-type-resolver.js +0 -90
- package/dist/properties/property-type-resolver.js.map +0 -1
- package/dist/rsc-cache.js +0 -79
- package/dist/rsc-cache.js.map +0 -1
- package/dist/ssr/async-storage.d.ts +0 -17
- package/dist/ssr/async-storage.js.map +0 -1
- package/dist/ssr/collect-auto-properties.js +0 -58
- package/dist/ssr/collect-auto-properties.js.map +0 -1
- package/dist/ssr/collector.js.map +0 -1
- package/dist/ssr/context.js.map +0 -1
- package/dist/ssr/format-global-rules.js.map +0 -1
- package/dist/ssr/format-keyframes.js +0 -69
- package/dist/ssr/format-keyframes.js.map +0 -1
- package/dist/ssr/format-property.js +0 -49
- package/dist/ssr/format-property.js.map +0 -1
- package/dist/ssr/format-rules.js +0 -73
- package/dist/ssr/format-rules.js.map +0 -1
- package/dist/ssr/hydrate.d.ts +0 -29
- package/dist/ssr/hydrate.js.map +0 -1
- package/dist/ssr/ssr-collector-ref.js +0 -29
- package/dist/ssr/ssr-collector-ref.js.map +0 -1
- package/dist/states/index.d.ts +0 -49
- package/dist/states/index.js +0 -170
- package/dist/states/index.js.map +0 -1
- package/dist/static/tastyStatic.d.ts +0 -46
- package/dist/static/tastyStatic.js +0 -30
- package/dist/static/tastyStatic.js.map +0 -1
- package/dist/static/types.d.ts +0 -49
- package/dist/static/types.js +0 -24
- package/dist/static/types.js.map +0 -1
- package/dist/styles/border.d.ts +0 -25
- package/dist/styles/border.js +0 -120
- package/dist/styles/border.js.map +0 -1
- package/dist/styles/color.d.ts +0 -14
- package/dist/styles/color.js +0 -26
- package/dist/styles/color.js.map +0 -1
- package/dist/styles/const.js +0 -17
- package/dist/styles/const.js.map +0 -1
- package/dist/styles/createStyle.js +0 -79
- package/dist/styles/createStyle.js.map +0 -1
- package/dist/styles/dimension.js +0 -109
- package/dist/styles/dimension.js.map +0 -1
- package/dist/styles/directional.js +0 -133
- package/dist/styles/directional.js.map +0 -1
- package/dist/styles/display.d.ts +0 -30
- package/dist/styles/display.js +0 -73
- package/dist/styles/display.js.map +0 -1
- package/dist/styles/fade.d.ts +0 -15
- package/dist/styles/fade.js +0 -62
- package/dist/styles/fade.js.map +0 -1
- package/dist/styles/fill.d.ts +0 -42
- package/dist/styles/fill.js +0 -51
- package/dist/styles/fill.js.map +0 -1
- package/dist/styles/flow.d.ts +0 -16
- package/dist/styles/flow.js +0 -12
- package/dist/styles/flow.js.map +0 -1
- package/dist/styles/gap.d.ts +0 -31
- package/dist/styles/gap.js +0 -38
- package/dist/styles/gap.js.map +0 -1
- package/dist/styles/height.d.ts +0 -17
- package/dist/styles/height.js +0 -19
- package/dist/styles/height.js.map +0 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/styles/index.js +0 -8
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/inset.d.ts +0 -24
- package/dist/styles/inset.js +0 -34
- package/dist/styles/inset.js.map +0 -1
- package/dist/styles/list.d.ts +0 -16
- package/dist/styles/list.js +0 -100
- package/dist/styles/list.js.map +0 -1
- package/dist/styles/margin.d.ts +0 -24
- package/dist/styles/margin.js +0 -32
- package/dist/styles/margin.js.map +0 -1
- package/dist/styles/outline.d.ts +0 -29
- package/dist/styles/outline.js +0 -55
- package/dist/styles/outline.js.map +0 -1
- package/dist/styles/padding.d.ts +0 -24
- package/dist/styles/padding.js +0 -32
- package/dist/styles/padding.js.map +0 -1
- package/dist/styles/placement.d.ts +0 -37
- package/dist/styles/placement.js +0 -74
- package/dist/styles/placement.js.map +0 -1
- package/dist/styles/predefined.d.ts +0 -71
- package/dist/styles/predefined.js +0 -237
- package/dist/styles/predefined.js.map +0 -1
- package/dist/styles/preset.d.ts +0 -52
- package/dist/styles/preset.js +0 -127
- package/dist/styles/preset.js.map +0 -1
- package/dist/styles/radius.d.ts +0 -12
- package/dist/styles/radius.js +0 -83
- package/dist/styles/radius.js.map +0 -1
- package/dist/styles/scrollMargin.d.ts +0 -24
- package/dist/styles/scrollMargin.js +0 -32
- package/dist/styles/scrollMargin.js.map +0 -1
- package/dist/styles/scrollbar.d.ts +0 -25
- package/dist/styles/scrollbar.js +0 -51
- package/dist/styles/scrollbar.js.map +0 -1
- package/dist/styles/shadow.d.ts +0 -14
- package/dist/styles/shadow.js +0 -25
- package/dist/styles/shadow.js.map +0 -1
- package/dist/styles/shared.js +0 -17
- package/dist/styles/shared.js.map +0 -1
- package/dist/styles/transition.d.ts +0 -14
- package/dist/styles/transition.js +0 -159
- package/dist/styles/transition.js.map +0 -1
- package/dist/styles/width.d.ts +0 -17
- package/dist/styles/width.js +0 -19
- package/dist/styles/width.js.map +0 -1
- package/dist/tasty.d.ts +0 -134
- package/dist/tasty.js +0 -248
- package/dist/tasty.js.map +0 -1
- package/dist/types.d.ts +0 -184
- package/dist/utils/cache-wrapper.js +0 -21
- package/dist/utils/cache-wrapper.js.map +0 -1
- package/dist/utils/case-converter.js +0 -8
- package/dist/utils/case-converter.js.map +0 -1
- package/dist/utils/color-math.d.ts +0 -46
- package/dist/utils/color-math.js +0 -749
- package/dist/utils/color-math.js.map +0 -1
- package/dist/utils/color-space.d.ts +0 -5
- package/dist/utils/color-space.js +0 -228
- package/dist/utils/color-space.js.map +0 -1
- package/dist/utils/colors.d.ts +0 -5
- package/dist/utils/colors.js +0 -10
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/css-types.d.ts +0 -7
- package/dist/utils/deps-equal.js +0 -15
- package/dist/utils/deps-equal.js.map +0 -1
- package/dist/utils/dotize.d.ts +0 -26
- package/dist/utils/dotize.js +0 -122
- package/dist/utils/dotize.js.map +0 -1
- package/dist/utils/filter-base-props.d.ts +0 -15
- package/dist/utils/filter-base-props.js +0 -45
- package/dist/utils/filter-base-props.js.map +0 -1
- package/dist/utils/get-display-name.d.ts +0 -7
- package/dist/utils/get-display-name.js +0 -10
- package/dist/utils/get-display-name.js.map +0 -1
- package/dist/utils/has-keys.js +0 -13
- package/dist/utils/has-keys.js.map +0 -1
- package/dist/utils/hash.js +0 -14
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/is-dev-env.js +0 -19
- package/dist/utils/is-dev-env.js.map +0 -1
- package/dist/utils/is-valid-element-type.js +0 -15
- package/dist/utils/is-valid-element-type.js.map +0 -1
- package/dist/utils/merge-styles.js.map +0 -1
- package/dist/utils/mod-attrs.d.ts +0 -6
- package/dist/utils/mod-attrs.js +0 -20
- package/dist/utils/mod-attrs.js.map +0 -1
- package/dist/utils/process-tokens.d.ts +0 -17
- package/dist/utils/process-tokens.js +0 -83
- package/dist/utils/process-tokens.js.map +0 -1
- package/dist/utils/resolve-recipes.d.ts +0 -17
- package/dist/utils/resolve-recipes.js.map +0 -1
- package/dist/utils/selector-transform.js +0 -32
- package/dist/utils/selector-transform.js.map +0 -1
- package/dist/utils/string.js +0 -8
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/styles.d.ts +0 -99
- package/dist/utils/styles.js +0 -220
- package/dist/utils/styles.js.map +0 -1
- package/dist/utils/typography.d.ts +0 -58
- package/dist/utils/typography.js +0 -51
- package/dist/utils/typography.js.map +0 -1
- package/dist/utils/warnings.d.ts +0 -16
- package/dist/utils/warnings.js +0 -16
- package/dist/utils/warnings.js.map +0 -1
- package/dist/zero/css-writer.d.ts +0 -45
- package/dist/zero/css-writer.js +0 -73
- package/dist/zero/css-writer.js.map +0 -1
- package/dist/zero/extractor.d.ts +0 -24
- package/dist/zero/extractor.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
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 OwnCondition,\n RootCondition,\n} from './conditions';\nimport {\n and,\n createOwnCondition,\n createParentCondition,\n createRootCondition,\n falseCondition,\n getConditionUniqueId,\n not,\n or,\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: most are leaves, but @root / @own / @parent wrap a\n // nested condition that benefits from the same simplification (e.g.\n // `@root(schema=dark & schema=light)` → inner is FALSE → wrapper is FALSE).\n // Only descend on non-negated wrappers; negated wrappers would need\n // additional reasoning (`!@root(FALSE)` = TRUE) that isn't worth the\n // surface area for the current bug.\n if (node.kind === 'state') {\n if (\n (node.type === 'root' || node.type === 'own' || node.type === 'parent') &&\n !node.negated\n ) {\n const simplifiedInner = simplifyInner(node.innerCondition);\n if (simplifiedInner.kind === 'false') return falseCondition();\n if (simplifiedInner === node.innerCondition) return node;\n if (node.type === 'root') {\n return createRootCondition(simplifiedInner, false, node.raw);\n }\n if (node.type === 'own') {\n return createOwnCondition(simplifiedInner, false, node.raw);\n }\n return createParentCondition(\n simplifiedInner,\n node.direct,\n false,\n node.raw,\n );\n }\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 // ─── Pass 1: flatten + identity/annihilator ────────────────────────────\n // Flatten nested ANDs, drop TRUE children, short-circuit on any 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 // ─── Pass 2: filter contradictions ─────────────────────────────────────\n // Any of these indicates the conjunction can never be satisfied.\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 // ─── Pass 3: redundancy elimination + canonicalization ─────────────────\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 // ─── Pass 4: boolean-algebra reductions ────────────────────────────────\n\n // Apply absorption: A & (A | B) → A\n terms = applyAbsorptionAnd(terms);\n\n // Apply consensus/resolution: (A | B) & (A | !B) → A\n terms = applyConsensusAnd(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 // ─── Pass 1: flatten + identity/annihilator ────────────────────────────\n // Flatten nested ORs, drop FALSE children, short-circuit on any TRUE.\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 // ─── Pass 2: filter tautologies ────────────────────────────────────────\n // A | !A means the disjunction is always satisfied.\n if (hasTautology(terms)) {\n return trueCondition();\n }\n\n // ─── Pass 3: redundancy elimination + canonicalization ─────────────────\n\n // Deduplicate\n terms = deduplicateTerms(terms);\n\n // Sort for canonical form\n terms = sortTerms(terms);\n\n // ─── Pass 4: boolean-algebra reductions ────────────────────────────────\n\n // Apply absorption: A | (A & B) → A\n terms = applyAbsorptionOr(terms);\n\n // Apply complementary factoring: (A & B) | (A & !B) → A\n terms = applyComplementaryFactoring(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 // Top-level modifiers: `schema=dark & schema=light`.\n if (\n 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 return true;\n }\n\n // @root scope (single :root element):\n // `@root(schema=dark) & @root(schema=light)` cannot match anything because\n // there is only one document root.\n if (hasNestedModifierConflict(terms, 'root')) return true;\n\n // @own scope (single sub-element / styled element under `&`):\n // `@own(schema=dark) & @own(schema=light)` is impossible for the same\n // reason — both refer to the same element.\n if (hasNestedModifierConflict(terms, 'own')) return true;\n\n // NOTE: @parent is intentionally NOT checked. Different ancestor elements\n // can hold different attribute values, so two `@parent(schema=...)` calls\n // can both match (different ancestors), even when the values differ.\n\n return false;\n}\n\n/**\n * Collect modifier conditions inside non-negated state wrappers of `kind`\n * and check for the same value-conflict pattern as the top-level pass.\n *\n * Modifiers from multiple sibling wrappers are combined — both wrappers\n * scope to the same element (one :root, one own scope), so an attribute\n * conflict across wrappers is still impossible.\n */\nfunction hasNestedModifierConflict(\n terms: ConditionNode[],\n wrapperType: 'root' | 'own',\n): boolean {\n const innerTerms: ConditionNode[] = [];\n for (const term of terms) {\n if (term.kind === 'state' && term.type === wrapperType && !term.negated) {\n flattenAndIntoArray(\n (term as RootCondition | OwnCondition).innerCondition,\n innerTerms,\n );\n }\n }\n if (innerTerms.length < 2) return false;\n return hasGroupedValueConflict<ModifierCondition>(\n innerTerms,\n (t) => (t.kind === 'state' && t.type === 'modifier' ? t : null),\n (t) => t.attribute,\n (t) => t.value,\n );\n}\n\n/**\n * Flatten a top-level AND tree into a flat list of conjuncts. Non-AND nodes\n * are pushed as-is.\n */\nfunction flattenAndIntoArray(node: ConditionNode, into: ConditionNode[]): void {\n if (node.kind === 'compound' && node.operator === 'AND') {\n for (const child of node.children) flattenAndIntoArray(child, into);\n } else {\n into.push(node);\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// Complementary Factoring\n// ============================================================================\n\n/**\n * Apply complementary factoring: (A & B) | (A & !B) → A\n *\n * Finds pairs of AND compounds that share all children except one,\n * where the differing child is complementary (X vs !X).\n * Replaces the pair with the common terms.\n *\n * This is applied iteratively until no more reductions are possible,\n * since factoring can expose further simplification opportunities.\n */\nfunction applyComplementaryFactoring(terms: ConditionNode[]): ConditionNode[] {\n let changed = true;\n\n while (changed) {\n changed = false;\n\n for (let i = 0; i < terms.length; i++) {\n const a = terms[i];\n if (a.kind !== 'compound' || a.operator !== 'AND') continue;\n\n for (let j = i + 1; j < terms.length; j++) {\n const b = terms[j];\n if (b.kind !== 'compound' || b.operator !== 'AND') continue;\n\n const factored = tryFactorPair(a.children, b.children);\n if (factored) {\n const replacement = simplifyInner(factored);\n terms = [\n ...terms.slice(0, i),\n ...terms.slice(i + 1, j),\n ...terms.slice(j + 1),\n replacement,\n ];\n changed = true;\n break;\n }\n }\n\n if (changed) break;\n }\n }\n\n return terms;\n}\n\n/**\n * Try to factor two AND children lists.\n *\n * Extracts the common children (by uniqueId). If the remaining\n * (non-common) parts of each side OR to TRUE, the common part alone\n * is sufficient: `(common & restA) | (common & restB) → common`\n * when `restA | restB → TRUE`.\n *\n * Also handles the simpler case where exactly one child is\n * complementary: `[A, B, C]` and `[A, !B, C]` → `AND(A, C)`.\n */\nfunction tryFactorPair(\n aChildren: ConditionNode[],\n bChildren: ConditionNode[],\n): ConditionNode | null {\n const aIds = aChildren.map((c) => getConditionUniqueId(c));\n const bIds = bChildren.map((c) => getConditionUniqueId(c));\n\n const bIdSet = new Set(bIds);\n const aIdSet = new Set(aIds);\n\n // Extract common children (present in both by uniqueId)\n const commonIndicesA: number[] = [];\n const restIndicesA: number[] = [];\n for (let i = 0; i < aIds.length; i++) {\n if (bIdSet.has(aIds[i])) {\n commonIndicesA.push(i);\n } else {\n restIndicesA.push(i);\n }\n }\n\n const restIndicesB: number[] = [];\n for (let i = 0; i < bIds.length; i++) {\n if (!aIdSet.has(bIds[i])) {\n restIndicesB.push(i);\n }\n }\n\n // Must have at least one common child and differing parts on both sides\n if (commonIndicesA.length === 0) return null;\n if (restIndicesA.length === 0 && restIndicesB.length === 0) return null;\n\n // Build the \"rest\" conditions for each side\n const restA =\n restIndicesA.length === 0\n ? trueCondition()\n : restIndicesA.length === 1\n ? aChildren[restIndicesA[0]]\n : and(...restIndicesA.map((i) => aChildren[i]));\n\n const restB =\n restIndicesB.length === 0\n ? trueCondition()\n : restIndicesB.length === 1\n ? bChildren[restIndicesB[0]]\n : and(...restIndicesB.map((i) => bChildren[i]));\n\n // Check if restA | restB simplifies to TRUE\n const combined = simplifyInner({\n kind: 'compound',\n operator: 'OR',\n children: [restA, restB],\n });\n\n if (combined.kind !== 'true') {\n // Direct complement check for compound conditions.\n // hasTautology only detects leaf-level A/!A pairs, so compound\n // complements like @hc / !@hc (which expand via De Morgan into\n // structurally different trees) are missed. Compare the simplified\n // negation of one rest against the simplified other rest instead.\n const simplifiedRestB = simplifyInner(restB);\n const negRestA = simplifyInner(not(restA));\n\n if (\n getConditionUniqueId(negRestA) !== getConditionUniqueId(simplifiedRestB)\n ) {\n return null;\n }\n }\n\n // restA | restB = TRUE → (common & restA) | (common & restB) = common\n const common = commonIndicesA.map((i) => aChildren[i]);\n\n if (common.length === 0) {\n return trueCondition();\n }\n if (common.length === 1) {\n return common[0];\n }\n return and(...common);\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 * another term already present (simple or compound).\n *\n * For AND context: A & (A | B) → A (absorbs OR compounds)\n * For OR context: A | (A & B) → A (absorbs AND compounds)\n *\n * After flattening, a compound A = OR(X, Y) becomes [X, Y, ...] in the\n * outer OR. A child AND(A, B) = AND(OR(X, Y), B) still references the\n * original un-flattened compound. We reconstruct possible compound\n * absorbers from the flattened terms so absorption works across nesting.\n */\nfunction applyAbsorption(\n terms: ConditionNode[],\n absorbedOperator: 'OR' | 'AND',\n): ConditionNode[] {\n // Collect IDs of ALL top-level terms as potential absorbers.\n const absorberIds = new Set<string>();\n for (const term of terms) {\n absorberIds.add(getConditionUniqueId(term));\n }\n\n // Reconstruct compound absorbers: if all children of a compound node\n // (found inside an absorbable term) are themselves absorbers, then\n // that compound is also an absorber. This handles the case where\n // A = OR(X, Y) was flattened into [X, Y, ...] at the top level,\n // but appears un-flattened as a child of AND(A, B).\n let changed = true;\n while (changed) {\n changed = false;\n for (const term of terms) {\n if (term.kind !== 'compound' || term.operator !== absorbedOperator) {\n continue;\n }\n for (const child of term.children) {\n if (child.kind !== 'compound') continue;\n const childId = getConditionUniqueId(child);\n if (absorberIds.has(childId)) continue;\n if (\n child.children.every((c) => absorberIds.has(getConditionUniqueId(c)))\n ) {\n absorberIds.add(childId);\n changed = true;\n }\n }\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 (absorberIds.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\n// ============================================================================\n// Consensus / Resolution (AND dual of complementary factoring)\n// ============================================================================\n\n/**\n * Apply the consensus/resolution rule for AND:\n * (A | B) & (A | !B) → A\n *\n * This is the dual of complementary factoring in OR context:\n * (A & B) | (A & !B) → A\n *\n * Extracts common children from two OR terms. If the remaining\n * parts of each side AND to FALSE, the common part alone is\n * sufficient.\n */\nfunction applyConsensusAnd(terms: ConditionNode[]): ConditionNode[] {\n let changed = true;\n\n while (changed) {\n changed = false;\n\n for (let i = 0; i < terms.length; i++) {\n const a = terms[i];\n if (a.kind !== 'compound' || a.operator !== 'OR') continue;\n\n for (let j = i + 1; j < terms.length; j++) {\n const b = terms[j];\n if (b.kind !== 'compound' || b.operator !== 'OR') continue;\n\n const resolved = tryResolvePair(a.children, b.children);\n if (resolved) {\n const replacement = simplifyInner(resolved);\n terms = [\n ...terms.slice(0, i),\n ...terms.slice(i + 1, j),\n ...terms.slice(j + 1),\n replacement,\n ];\n changed = true;\n break;\n }\n }\n\n if (changed) break;\n }\n }\n\n return terms;\n}\n\n/**\n * Try to resolve two OR children lists.\n *\n * Extracts common children (by uniqueId). If the remaining\n * (non-common) parts AND to FALSE, the common part alone\n * is sufficient: `(common | restA) & (common | restB) → common`\n * when `restA & restB → FALSE`.\n */\nfunction tryResolvePair(\n aChildren: ConditionNode[],\n bChildren: ConditionNode[],\n): ConditionNode | null {\n const aIds = aChildren.map((c) => getConditionUniqueId(c));\n const bIds = bChildren.map((c) => getConditionUniqueId(c));\n\n const bIdSet = new Set(bIds);\n const aIdSet = new Set(aIds);\n\n const commonIndicesA: number[] = [];\n const restIndicesA: number[] = [];\n for (let i = 0; i < aIds.length; i++) {\n if (bIdSet.has(aIds[i])) {\n commonIndicesA.push(i);\n } else {\n restIndicesA.push(i);\n }\n }\n\n const restIndicesB: number[] = [];\n for (let i = 0; i < bIds.length; i++) {\n if (!aIdSet.has(bIds[i])) {\n restIndicesB.push(i);\n }\n }\n\n if (commonIndicesA.length === 0) return null;\n if (restIndicesA.length === 0 && restIndicesB.length === 0) return null;\n\n const restA =\n restIndicesA.length === 0\n ? falseCondition()\n : restIndicesA.length === 1\n ? aChildren[restIndicesA[0]]\n : or(...restIndicesA.map((i) => aChildren[i]));\n\n const restB =\n restIndicesB.length === 0\n ? falseCondition()\n : restIndicesB.length === 1\n ? bChildren[restIndicesB[0]]\n : or(...restIndicesB.map((i) => bChildren[i]));\n\n // Check if restA & restB simplifies to FALSE\n const combined = simplifyInner({\n kind: 'compound',\n operator: 'AND',\n children: [restA, restB],\n });\n\n if (combined.kind !== 'false') {\n // Direct complement check for compound conditions\n const simplifiedRestB = simplifyInner(restB);\n const negRestA = simplifyInner(not(restA));\n\n if (\n getConditionUniqueId(negRestA) !== getConditionUniqueId(simplifiedRestB)\n ) {\n return null;\n }\n }\n\n // restA & restB = FALSE → (common | restA) & (common | restB) = common\n const common = commonIndicesA.map((i) => aChildren[i]);\n\n if (common.length === 0) {\n return falseCondition();\n }\n if (common.length === 1) {\n return common[0];\n }\n return or(...common);\n}\n"],"mappings":"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"}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
//#endregion
|
|
16
|
-
export { emitWarning };
|
|
17
|
-
|
|
18
|
-
//# sourceMappingURL=warnings.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/plugins/index.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
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
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { Lru } from "../parser/lru.js";
|
|
2
|
-
import { okhslToSrgb } from "../utils/color-math.js";
|
|
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
|
-
const conversionCache = new Lru(500);
|
|
17
|
-
const clamp = (value, min, max) => Math.max(Math.min(value, max), min);
|
|
18
|
-
/**
|
|
19
|
-
* Parse an angle value with optional unit.
|
|
20
|
-
* Supports: deg, turn, rad, or unitless (treated as degrees).
|
|
21
|
-
*/
|
|
22
|
-
const parseAngle = (value) => {
|
|
23
|
-
const match = value.match(/^([+-]?\d*\.?\d+)(deg|turn|rad)?$/);
|
|
24
|
-
if (!match) return 0;
|
|
25
|
-
const num = parseFloat(match[1]);
|
|
26
|
-
switch (match[2]) {
|
|
27
|
-
case "turn": return num * 360;
|
|
28
|
-
case "rad": return num * 180 / Math.PI;
|
|
29
|
-
default: return num;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* Parse a percentage value (e.g., "50%") to a 0-1 range.
|
|
34
|
-
*/
|
|
35
|
-
const parsePercentage = (value) => {
|
|
36
|
-
const match = value.match(/^([+-]?\d*\.?\d+)%?$/);
|
|
37
|
-
if (!match) return 0;
|
|
38
|
-
const num = parseFloat(match[1]);
|
|
39
|
-
return value.includes("%") ? num / 100 : num;
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* The okhsl function handler for tasty parser.
|
|
43
|
-
* Receives parsed style groups and returns an RGB color string.
|
|
44
|
-
*/
|
|
45
|
-
const okhslFunc = (groups) => {
|
|
46
|
-
if (groups.length === 0 || groups[0].all.length < 3) {
|
|
47
|
-
console.warn("[okhsl] Expected 3 values (H S L), got:", groups);
|
|
48
|
-
return "rgb(0% 0% 0%)";
|
|
49
|
-
}
|
|
50
|
-
const group = groups[0];
|
|
51
|
-
const tokens = group.all;
|
|
52
|
-
const alpha = group.parts.length > 1 && group.parts[1].all.length > 0 ? group.parts[1].output : void 0;
|
|
53
|
-
const cacheKey = tokens.slice(0, 3).join(" ") + (alpha ? ` / ${alpha}` : "");
|
|
54
|
-
const cached = conversionCache.get(cacheKey);
|
|
55
|
-
if (cached) return cached;
|
|
56
|
-
const h = parseAngle(tokens[0]);
|
|
57
|
-
const s = parsePercentage(tokens[1]);
|
|
58
|
-
const l = parsePercentage(tokens[2]);
|
|
59
|
-
const [r, g, b] = okhslToSrgb(h, clamp(s, 0, 1), clamp(l, 0, 1));
|
|
60
|
-
const format = (n) => {
|
|
61
|
-
const pct = n * 100;
|
|
62
|
-
return parseFloat(pct.toFixed(1)).toString() + "%";
|
|
63
|
-
};
|
|
64
|
-
const result = alpha ? `rgb(${format(r)} ${format(g)} ${format(b)} / ${alpha})` : `rgb(${format(r)} ${format(g)} ${format(b)})`;
|
|
65
|
-
conversionCache.set(cacheKey, result);
|
|
66
|
-
return result;
|
|
67
|
-
};
|
|
68
|
-
/**
|
|
69
|
-
* OKHSL Plugin for Tasty.
|
|
70
|
-
*
|
|
71
|
-
* Adds support for the `okhsl()` color function in tasty styles.
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```ts
|
|
75
|
-
* import { configure } from '@tenphi/tasty';
|
|
76
|
-
* import { okhslPlugin } from '@tenphi/tasty';
|
|
77
|
-
*
|
|
78
|
-
* configure({
|
|
79
|
-
* plugins: [okhslPlugin()],
|
|
80
|
-
* });
|
|
81
|
-
*
|
|
82
|
-
* // Now you can use okhsl in styles:
|
|
83
|
-
* const Box = tasty({
|
|
84
|
-
* styles: {
|
|
85
|
-
* fill: 'okhsl(240 50% 50%)',
|
|
86
|
-
* },
|
|
87
|
-
* });
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
const okhslPlugin = () => ({
|
|
91
|
-
name: "okhsl",
|
|
92
|
-
funcs: { okhsl: okhslFunc }
|
|
93
|
-
});
|
|
94
|
-
//#endregion
|
|
95
|
-
export { okhslFunc, okhslPlugin };
|
|
96
|
-
|
|
97
|
-
//# sourceMappingURL=okhsl-plugin.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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\nimport { Lru } from '../parser/lru';\nimport { okhslToSrgb } from '../utils/color-math';\n\nimport type { StyleDetails } from '../parser/types';\nimport type { TastyPlugin, TastyPluginFactory } from './types';\n\nconst conversionCache = new Lru<string, string>(500);\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.max(Math.min(value, max), min);\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 return value.includes('%') ? num / 100 : num;\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 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 const alpha =\n group.parts.length > 1 && group.parts[1].all.length > 0\n ? group.parts[1].output\n : undefined;\n\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\nexport { okhslFunc };\n"],"mappings":";;;;;;;;;;;;;;;AAmBA,MAAM,kBAAkB,IAAI,IAAoB,IAAI;AAEpD,MAAM,SAAS,OAAe,KAAa,QACzC,KAAK,IAAI,KAAK,IAAI,OAAO,IAAI,EAAE,IAAI;;;;;AAMrC,MAAM,cAAc,UAA0B;CAC5C,MAAM,QAAQ,MAAM,MAAM,oCAAoC;AAC9D,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,MAAM,WAAW,MAAM,GAAG;AAGhC,SAFa,MAAM,IAEnB;EACE,KAAK,OACH,QAAO,MAAM;EACf,KAAK,MACH,QAAQ,MAAM,MAAO,KAAK;EAE5B,QACE,QAAO;;;;;;AAOb,MAAM,mBAAmB,UAA0B;CACjD,MAAM,QAAQ,MAAM,MAAM,uBAAuB;AACjD,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,MAAM,WAAW,MAAM,GAAG;AAChC,QAAO,MAAM,SAAS,IAAI,GAAG,MAAM,MAAM;;;;;;AAO3C,MAAM,aAAa,WAAmC;AACpD,KAAI,OAAO,WAAW,KAAK,OAAO,GAAG,IAAI,SAAS,GAAG;AACnD,UAAQ,KAAK,2CAA2C,OAAO;AAC/D,SAAO;;CAGT,MAAM,QAAQ,OAAO;CACrB,MAAM,SAAS,MAAM;CAErB,MAAM,QACJ,MAAM,MAAM,SAAS,KAAK,MAAM,MAAM,GAAG,IAAI,SAAS,IAClD,MAAM,MAAM,GAAG,SACf,KAAA;CAEN,MAAM,WAAW,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,IAAI,QAAQ,MAAM,UAAU;CACzE,MAAM,SAAS,gBAAgB,IAAI,SAAS;AAC5C,KAAI,OAAQ,QAAO;CAEnB,MAAM,IAAI,WAAW,OAAO,GAAG;CAC/B,MAAM,IAAI,gBAAgB,OAAO,GAAG;CACpC,MAAM,IAAI,gBAAgB,OAAO,GAAG;CAEpC,MAAM,CAAC,GAAG,GAAG,KAAK,YAAY,GAAG,MAAM,GAAG,GAAG,EAAE,EAAE,MAAM,GAAG,GAAG,EAAE,CAAC;CAEhE,MAAM,UAAU,MAAsB;EACpC,MAAM,MAAM,IAAI;AAChB,SAAO,WAAW,IAAI,QAAQ,EAAE,CAAC,CAAC,UAAU,GAAG;;CAGjD,MAAM,SAAS,QACX,OAAO,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,KAAK,MAAM,KACtD,OAAO,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC;AAE/C,iBAAgB,IAAI,UAAU,OAAO;AACrC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;AAyBT,MAAa,qBAAsD;CACjE,MAAM;CACN,OAAO,EACL,OAAO,WACR;CACF"}
|
package/dist/plugins/types.d.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { StyleDetails, UnitHandler } from "../parser/types.js";
|
|
2
|
-
import { StyleHandlerDefinition } from "../utils/styles.js";
|
|
3
|
-
import { ConfigTokens, RecipeStyles, Styles } from "../styles/types.js";
|
|
4
|
-
import { TypographyPreset } from "../utils/typography.js";
|
|
5
|
-
|
|
6
|
-
//#region src/plugins/types.d.ts
|
|
7
|
-
/**
|
|
8
|
-
* A tasty plugin that extends the style system with custom functions, units, states, or handlers.
|
|
9
|
-
*/
|
|
10
|
-
interface TastyPlugin {
|
|
11
|
-
/** Unique name for the plugin (used for debugging and conflict detection) */
|
|
12
|
-
name: string;
|
|
13
|
-
/** Custom functions that transform parsed style groups into CSS values */
|
|
14
|
-
funcs?: Record<string, (groups: StyleDetails[]) => string>;
|
|
15
|
-
/** Custom units that transform numeric values (e.g., `2x` → `calc(2 * var(--gap))`) */
|
|
16
|
-
units?: Record<string, string | UnitHandler>;
|
|
17
|
-
/** Custom state aliases (e.g., `'@mobile': '@media(w < 768px)'`) */
|
|
18
|
-
states?: Record<string, string>;
|
|
19
|
-
/**
|
|
20
|
-
* Custom style handlers that transform style properties into CSS declarations.
|
|
21
|
-
* Handlers replace built-in handlers for the same style name.
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* handlers: {
|
|
25
|
-
* // Simple handler - lookup style inferred from key
|
|
26
|
-
* fill: ({ fill }) => fill ? { 'background-color': fill } : undefined,
|
|
27
|
-
* // Multi-property handler
|
|
28
|
-
* spacing: [['gap', 'padding'], ({ gap, padding }) => ({ ... })],
|
|
29
|
-
* }
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
handlers?: Record<string, StyleHandlerDefinition>;
|
|
33
|
-
/**
|
|
34
|
-
* Design tokens injected as CSS custom properties on `:root`.
|
|
35
|
-
* Values are parsed through the Tasty DSL. Supports state maps.
|
|
36
|
-
* - `$name` → `--name` CSS custom property
|
|
37
|
-
* - `#name` → `--name-color` and `--name-color-{colorSpace}`
|
|
38
|
-
*/
|
|
39
|
-
tokens?: ConfigTokens;
|
|
40
|
-
/** Predefined tokens replaced during style parsing (`$name` or `#name`) */
|
|
41
|
-
replaceTokens?: Record<`$${string}` | `#${string}`, string | number>;
|
|
42
|
-
/**
|
|
43
|
-
* Predefined style recipes -- named style bundles that can be applied via `recipe` style property.
|
|
44
|
-
* Recipe values are flat tasty styles (no sub-element keys).
|
|
45
|
-
* @example
|
|
46
|
-
* ```ts
|
|
47
|
-
* recipes: {
|
|
48
|
-
* card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
|
|
49
|
-
* elevated: { shadow: '2x 2x 4x #shadow' },
|
|
50
|
-
* }
|
|
51
|
-
* ```
|
|
52
|
-
*/
|
|
53
|
-
recipes?: Record<string, RecipeStyles>;
|
|
54
|
-
/**
|
|
55
|
-
* Typography presets — shorthand for `generateTypographyTokens()`.
|
|
56
|
-
* Generated tokens are merged under explicit `tokens` (tokens win on conflict).
|
|
57
|
-
*/
|
|
58
|
-
presets?: Record<string, TypographyPreset>;
|
|
59
|
-
/**
|
|
60
|
-
* Global Tasty styles keyed by CSS selector.
|
|
61
|
-
* Supports the full Tasty style syntax.
|
|
62
|
-
*/
|
|
63
|
-
globalStyles?: Record<string, Styles>;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* A factory function that creates a TastyPlugin.
|
|
67
|
-
* Can optionally accept configuration options.
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```ts
|
|
71
|
-
* // Plugin without options
|
|
72
|
-
* const okhslPlugin: TastyPluginFactory = () => ({
|
|
73
|
-
* name: 'okhsl',
|
|
74
|
-
* funcs: { okhsl: okhslFunc },
|
|
75
|
-
* });
|
|
76
|
-
*
|
|
77
|
-
* // Plugin with options
|
|
78
|
-
* const debugPlugin: TastyPluginFactory<{ verbose: boolean }> = (options) => ({
|
|
79
|
-
* name: 'debug',
|
|
80
|
-
* funcs: { debug: createDebugFunc(options.verbose) },
|
|
81
|
-
* });
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
|
-
type TastyPluginFactory<TOptions = void> = TOptions extends void ? () => TastyPlugin : (options: TOptions) => TastyPlugin;
|
|
85
|
-
//#endregion
|
|
86
|
-
export { TastyPlugin, TastyPluginFactory };
|
|
87
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/properties/index.js
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { RE_NUMBER, RE_RAW_UNIT } from "../parser/const.js";
|
|
2
|
-
import "../utils/color-space.js";
|
|
3
|
-
//#region src/properties/index.ts
|
|
4
|
-
const PROPERTIES_KEY = "@properties";
|
|
5
|
-
/**
|
|
6
|
-
* Valid CSS custom property name pattern (after the -- prefix).
|
|
7
|
-
* Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.
|
|
8
|
-
*/
|
|
9
|
-
const VALID_PROPERTY_NAME_PATTERN = /^[a-z_][a-z0-9-_]*$/i;
|
|
10
|
-
/**
|
|
11
|
-
* Validate a CSS custom property name (the part after --).
|
|
12
|
-
* Returns true if the name is valid for use as a CSS custom property.
|
|
13
|
-
*/
|
|
14
|
-
function isValidPropertyName(name) {
|
|
15
|
-
return VALID_PROPERTY_NAME_PATTERN.test(name);
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Check if styles object has local @properties definition.
|
|
19
|
-
* Fast path: single property lookup.
|
|
20
|
-
*/
|
|
21
|
-
function hasLocalProperties(styles) {
|
|
22
|
-
return PROPERTIES_KEY in styles;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Extract local @properties from styles object.
|
|
26
|
-
* Returns null if no local properties (fast path).
|
|
27
|
-
*/
|
|
28
|
-
function extractLocalProperties(styles) {
|
|
29
|
-
const properties = styles[PROPERTIES_KEY];
|
|
30
|
-
if (!properties || typeof properties !== "object") return null;
|
|
31
|
-
return properties;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Parse a property token name and return the CSS property name and whether it's a color.
|
|
35
|
-
* Supports tasty token syntax and validates the property name.
|
|
36
|
-
*
|
|
37
|
-
* Token formats:
|
|
38
|
-
* - `$name` → { cssName: '--name', isColor: false }
|
|
39
|
-
* - `#name` → { cssName: '--name-color', isColor: true }
|
|
40
|
-
* - `--name` → { cssName: '--name', isColor: false } (legacy, auto-detect color by suffix)
|
|
41
|
-
* - `name` → { cssName: '--name', isColor: false } (legacy)
|
|
42
|
-
*
|
|
43
|
-
* @param token - The property token to parse
|
|
44
|
-
* @returns Parsed result with cssName, isColor, isValid, and optional error
|
|
45
|
-
*/
|
|
46
|
-
function parsePropertyToken(token) {
|
|
47
|
-
if (!token || typeof token !== "string") return {
|
|
48
|
-
cssName: "",
|
|
49
|
-
isColor: false,
|
|
50
|
-
isValid: false,
|
|
51
|
-
error: "Property token must be a non-empty string"
|
|
52
|
-
};
|
|
53
|
-
let name;
|
|
54
|
-
let isColor;
|
|
55
|
-
if (token.startsWith("$")) {
|
|
56
|
-
name = token.slice(1);
|
|
57
|
-
isColor = false;
|
|
58
|
-
} else if (token.startsWith("#")) {
|
|
59
|
-
name = token.slice(1);
|
|
60
|
-
isColor = true;
|
|
61
|
-
} else if (token.startsWith("--")) {
|
|
62
|
-
name = token.slice(2);
|
|
63
|
-
isColor = token.endsWith("-color");
|
|
64
|
-
} else {
|
|
65
|
-
name = token;
|
|
66
|
-
isColor = token.endsWith("-color");
|
|
67
|
-
}
|
|
68
|
-
if (!name) return {
|
|
69
|
-
cssName: "",
|
|
70
|
-
isColor,
|
|
71
|
-
isValid: false,
|
|
72
|
-
error: "Property name cannot be empty"
|
|
73
|
-
};
|
|
74
|
-
if (!isValidPropertyName(name)) return {
|
|
75
|
-
cssName: "",
|
|
76
|
-
isColor,
|
|
77
|
-
isValid: false,
|
|
78
|
-
error: `Invalid property name "${name}". Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.`
|
|
79
|
-
};
|
|
80
|
-
let cssName;
|
|
81
|
-
if (token.startsWith("#")) cssName = `--${name}-color`;
|
|
82
|
-
else cssName = `--${name}`;
|
|
83
|
-
return {
|
|
84
|
-
cssName,
|
|
85
|
-
isColor,
|
|
86
|
-
isValid: true
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Normalize a property definition to a consistent string representation.
|
|
91
|
-
* Used for comparing definitions to detect type conflicts.
|
|
92
|
-
*
|
|
93
|
-
* Only `syntax` and `inherits` are compared — `initialValue` is intentionally
|
|
94
|
-
* excluded because different components may set different defaults for the
|
|
95
|
-
* same typed property (e.g. auto-inferred `0px` vs explicit `6px`).
|
|
96
|
-
*
|
|
97
|
-
* Keys are sorted alphabetically to ensure consistent comparison:
|
|
98
|
-
* { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }
|
|
99
|
-
*/
|
|
100
|
-
function normalizePropertyDefinition(def) {
|
|
101
|
-
const normalized = {};
|
|
102
|
-
if (def.inherits !== void 0) normalized.inherits = def.inherits;
|
|
103
|
-
if (def.syntax !== void 0) normalized.syntax = def.syntax;
|
|
104
|
-
return JSON.stringify(normalized);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Get the effective property definition for a token.
|
|
108
|
-
* For color tokens (#name), auto-sets syntax to '<color>' and defaults initialValue to 'transparent'.
|
|
109
|
-
*
|
|
110
|
-
* @param token - Property token ($name, #name, --name, or plain name)
|
|
111
|
-
* @param userDefinition - User-provided definition options
|
|
112
|
-
* @returns Effective definition with cssName, definition, isValid, and optional error
|
|
113
|
-
*/
|
|
114
|
-
function getEffectiveDefinition(token, userDefinition) {
|
|
115
|
-
const parsed = parsePropertyToken(token);
|
|
116
|
-
if (!parsed.isValid) return {
|
|
117
|
-
cssName: "",
|
|
118
|
-
definition: userDefinition,
|
|
119
|
-
isColor: false,
|
|
120
|
-
isValid: false,
|
|
121
|
-
error: parsed.error
|
|
122
|
-
};
|
|
123
|
-
if (parsed.isColor) return {
|
|
124
|
-
cssName: parsed.cssName,
|
|
125
|
-
definition: {
|
|
126
|
-
syntax: "<color>",
|
|
127
|
-
inherits: userDefinition.inherits,
|
|
128
|
-
initialValue: userDefinition.initialValue ?? "transparent"
|
|
129
|
-
},
|
|
130
|
-
isColor: true,
|
|
131
|
-
isValid: true
|
|
132
|
-
};
|
|
133
|
-
return {
|
|
134
|
-
cssName: parsed.cssName,
|
|
135
|
-
definition: userDefinition,
|
|
136
|
-
isColor: false,
|
|
137
|
-
isValid: true
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
const UNIT_TO_SYNTAX = {};
|
|
141
|
-
const LENGTH_UNITS = [
|
|
142
|
-
"px",
|
|
143
|
-
"em",
|
|
144
|
-
"rem",
|
|
145
|
-
"vw",
|
|
146
|
-
"vh",
|
|
147
|
-
"vmin",
|
|
148
|
-
"vmax",
|
|
149
|
-
"ch",
|
|
150
|
-
"ex",
|
|
151
|
-
"cap",
|
|
152
|
-
"ic",
|
|
153
|
-
"lh",
|
|
154
|
-
"rlh",
|
|
155
|
-
"svw",
|
|
156
|
-
"svh",
|
|
157
|
-
"lvw",
|
|
158
|
-
"lvh",
|
|
159
|
-
"dvw",
|
|
160
|
-
"dvh",
|
|
161
|
-
"cqw",
|
|
162
|
-
"cqh",
|
|
163
|
-
"cqi",
|
|
164
|
-
"cqb",
|
|
165
|
-
"cqmin",
|
|
166
|
-
"cqmax"
|
|
167
|
-
];
|
|
168
|
-
const ANGLE_UNITS = [
|
|
169
|
-
"deg",
|
|
170
|
-
"rad",
|
|
171
|
-
"grad",
|
|
172
|
-
"turn"
|
|
173
|
-
];
|
|
174
|
-
const TIME_UNITS = ["ms", "s"];
|
|
175
|
-
for (const u of LENGTH_UNITS) UNIT_TO_SYNTAX[u] = {
|
|
176
|
-
syntax: "<length-percentage>",
|
|
177
|
-
initialValue: "0px"
|
|
178
|
-
};
|
|
179
|
-
UNIT_TO_SYNTAX["%"] = {
|
|
180
|
-
syntax: "<length-percentage>",
|
|
181
|
-
initialValue: "0px"
|
|
182
|
-
};
|
|
183
|
-
for (const u of ANGLE_UNITS) UNIT_TO_SYNTAX[u] = {
|
|
184
|
-
syntax: "<angle>",
|
|
185
|
-
initialValue: "0deg"
|
|
186
|
-
};
|
|
187
|
-
for (const u of TIME_UNITS) UNIT_TO_SYNTAX[u] = {
|
|
188
|
-
syntax: "<time>",
|
|
189
|
-
initialValue: "0s"
|
|
190
|
-
};
|
|
191
|
-
/**
|
|
192
|
-
* Infer CSS @property syntax from a concrete value.
|
|
193
|
-
* Detects numeric types: \<number\>, \<length-percentage\>, \<angle\>, \<time\>.
|
|
194
|
-
* Length and percentage values both map to \<length-percentage\> for maximum flexibility.
|
|
195
|
-
* Color properties are handled separately via the #name token convention
|
|
196
|
-
* (--name-color gets \<color\> syntax automatically in getEffectiveDefinition).
|
|
197
|
-
*
|
|
198
|
-
* @param value - The CSS value to infer from (e.g. '10px', '1', '45deg')
|
|
199
|
-
* @returns Inferred syntax and initial value, or null if not inferable
|
|
200
|
-
*/
|
|
201
|
-
function inferSyntaxFromValue(value) {
|
|
202
|
-
if (!value || typeof value !== "string") return null;
|
|
203
|
-
const trimmed = value.trim();
|
|
204
|
-
if (!trimmed) return null;
|
|
205
|
-
if (RE_NUMBER.test(trimmed)) {
|
|
206
|
-
if (parseFloat(trimmed) === 0) return null;
|
|
207
|
-
return {
|
|
208
|
-
syntax: "<number>",
|
|
209
|
-
initialValue: "0"
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
const unitMatch = trimmed.match(RE_RAW_UNIT);
|
|
213
|
-
if (unitMatch) {
|
|
214
|
-
const mapping = UNIT_TO_SYNTAX[unitMatch[2]];
|
|
215
|
-
if (mapping) return mapping;
|
|
216
|
-
}
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
//#endregion
|
|
220
|
-
export { extractLocalProperties, getEffectiveDefinition, hasLocalProperties, inferSyntaxFromValue, normalizePropertyDefinition, parsePropertyToken };
|
|
221
|
-
|
|
222
|
-
//# sourceMappingURL=index.js.map
|