@tenphi/tasty 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -2
- package/dist/chunks/renderChunk.js.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/hooks/useKeyframes.js.map +1 -1
- package/dist/hooks/useRawCSS.js.map +1 -1
- package/dist/injector/injector.js.map +1 -1
- package/dist/injector/sheet-manager.js.map +1 -1
- package/dist/parser/index.d.ts +3 -0
- package/dist/parser/index.js +4 -0
- package/dist/parser/parser.js.map +1 -1
- package/dist/parser/types.d.ts +6 -1
- package/dist/pipeline/conditions.js +22 -1
- package/dist/pipeline/conditions.js.map +1 -1
- package/dist/pipeline/exclusive.js.map +1 -1
- package/dist/pipeline/index.js +2 -1
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/materialize.js +76 -3
- package/dist/pipeline/materialize.js.map +1 -1
- package/dist/pipeline/parseStateKey.js +23 -2
- package/dist/pipeline/parseStateKey.js.map +1 -1
- package/dist/pipeline/simplify.js.map +1 -1
- package/dist/states/index.d.ts +1 -1
- package/dist/states/index.js +29 -2
- package/dist/states/index.js.map +1 -1
- package/dist/styles/dimension.js.map +1 -1
- package/dist/styles/predefined.js.map +1 -1
- package/dist/styles/radius.js.map +1 -1
- package/dist/styles/scrollbar.js.map +1 -1
- package/dist/styles/shadow.js.map +1 -1
- package/dist/tasty.js.map +1 -1
- package/dist/utils/resolve-recipes.js.map +1 -1
- package/dist/utils/styles.js.map +1 -1
- package/dist/zero/babel.js.map +1 -1
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://www.npmjs.com/package/@tenphi/tasty"><img src="https://img.shields.io/npm/v/@tenphi/tasty.svg" alt="npm version"></a>
|
|
13
|
-
<a href="https://github.com/tenphi/tasty/actions/workflows/ci.yml"><img src="https://github.com/tenphi/tasty/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
14
|
-
<a href="https://github.com/tenphi/tasty/blob/main/LICENSE"><img src="https://img.shields.io/
|
|
13
|
+
<a href="https://github.com/tenphi/tasty/actions/workflows/ci.yml"><img src="https://github.com/tenphi/tasty/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
|
|
14
|
+
<a href="https://github.com/tenphi/tasty/blob/main/LICENSE"><img src="https://img.shields.io/github/license/tenphi/tasty" alt="license"></a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
---
|
|
@@ -226,6 +226,31 @@ module.exports = {
|
|
|
226
226
|
| `lh` | Line height | `1lh` | `var(--line-height)` |
|
|
227
227
|
| `sf` | Stable fraction | `1sf` | `minmax(0, 1fr)` |
|
|
228
228
|
|
|
229
|
+
## `tasty` vs `tastyStatic`
|
|
230
|
+
|
|
231
|
+
Tasty ships two styling APIs with different trade-offs. Pick the one that fits your project:
|
|
232
|
+
|
|
233
|
+
| | `tasty` (runtime) | `tastyStatic` (zero-runtime) |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| **Framework** | React only | Framework-agnostic (requires Babel) |
|
|
236
|
+
| **Import** | `@tenphi/tasty` | `@tenphi/tasty/static` |
|
|
237
|
+
| **Output** | React component | CSS class name (string) |
|
|
238
|
+
| **CSS injection** | At runtime via `<style>` tags | At build time via Babel plugin |
|
|
239
|
+
| **Runtime overhead** | Style generation + injection on mount | None — CSS is pre-extracted |
|
|
240
|
+
| **Requires Babel plugin** | No | Yes (`@tenphi/tasty/babel-plugin`) |
|
|
241
|
+
| **Component creation** | `tasty({ as, styles, ... })` | `tastyStatic({ ... })` returns a class |
|
|
242
|
+
| **Extending components** | `tasty(BaseComponent, { styles })` | `tastyStatic(baseStyle, { ... })` |
|
|
243
|
+
| **Global / selector styles** | `useGlobalStyles(selector, styles)` | `tastyStatic(selector, styles)` |
|
|
244
|
+
| **Style props at runtime** | Yes — `styleProps`, `styles`, `mods` | No — all values must be static |
|
|
245
|
+
| **Dynamic values** | Fully supported | Only via CSS custom properties |
|
|
246
|
+
| **Sub-elements** | Built-in (`elements` + `<C.Title>`) | Manual (use `data-element` + CSS) |
|
|
247
|
+
| **Variants** | Built-in (`variants` option) | Manual (create separate static styles) |
|
|
248
|
+
| **Tokens** | `tokens` prop → inline CSS vars | `processTokens()` helper |
|
|
249
|
+
| **Design tokens & units** | Full support (`#color`, `2x`, `1r`) | Full support (`#color`, `2x`, `1r`) |
|
|
250
|
+
| **State mappings** | Full support (modifiers, media, etc.) | Full support (modifiers, media, etc.) |
|
|
251
|
+
| **Recipes** | Supported via `configure()` | Supported via Babel plugin config |
|
|
252
|
+
| **Best for** | Interactive React apps, design systems | Static sites, landing pages, SSG |
|
|
253
|
+
|
|
229
254
|
## Documentation
|
|
230
255
|
|
|
231
256
|
- [Runtime API (tasty)](docs/tasty.md) — Full runtime styling documentation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderChunk.js","names":[],"sources":["../../src/chunks/renderChunk.ts"],"sourcesContent":["/**\n * Chunk-specific style rendering.\n *\n * Renders styles for a specific chunk by filtering the styles object\n * to only include relevant keys before passing to renderStyles.\n */\n\nimport type { RenderResult} from '../pipeline';\nimport { renderStyles } from '../pipeline';\nimport { extractLocalPredefinedStates } from '../states';\nimport type { Styles } from '../styles/types';\n\nimport { CHUNK_NAMES } from './definitions';\n\n/**\n * Render styles for a specific chunk.\n *\n * Creates a filtered styles object containing only the keys for this chunk,\n * then delegates to the existing renderStyles function.\n *\n * IMPORTANT: Local predefined states (e.g., '@mobile': '@media(w < 600px)')\n * are always included in the filtered styles, regardless of which chunk is\n * being rendered. This ensures that state references like '@mobile' in any\n * chunk can be properly resolved by the pipeline.\n *\n * @param styles - The full styles object\n * @param chunkName - Name of the chunk being rendered\n * @param styleKeys - Keys of styles belonging to this chunk\n * @returns RenderResult with rules for this chunk\n */\nexport function renderStylesForChunk(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): RenderResult {\n // Empty chunk - return empty result\n if (styleKeys.length === 0) {\n return { rules: [], className: '' };\n }\n\n // For subcomponents, we need to preserve the nested structure\n if (chunkName === CHUNK_NAMES.SUBCOMPONENTS) {\n return renderSubcomponentsChunk(styles, styleKeys);\n }\n\n // Extract local predefined states from the full styles object\n // These must be included for state resolution to work across chunks\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n\n // For regular chunks, create a filtered styles object\n // This is memory-efficient: we only create a shallow copy with filtered keys\n const filteredStyles: Styles = {};\n\n // First, add local predefined states so they're available for resolution\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n // Then add the chunk's style keys\n for (const key of styleKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n // Delegate to existing renderStyles\n return renderStyles(filteredStyles);\n}\n\n/**\n * Render the subcomponents chunk.\n *\n * Subcomponents (selectors like Label, &::before, etc.) contain nested\n * style objects that need to be preserved in their entirety.\n *\n * @param styles - The full styles object\n * @param selectorKeys - Keys of selectors in this chunk\n * @returns RenderResult with rules for all subcomponents\n */\nfunction renderSubcomponentsChunk(\n styles: Styles,\n selectorKeys: string[],\n): RenderResult {\n // Extract local predefined states from the full styles object\n // These must be included for state resolution to work in nested styles\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n\n // Create a styles object containing only the selector keys\n const filteredStyles: Styles = {};\n\n // First, add local predefined states so they're available for resolution\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n // Then add the selector keys\n for (const key of selectorKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n // Also copy the $ helper if present (used for selector combinators)\n if (styles.$ !== undefined) {\n filteredStyles.$ = styles.$;\n }\n\n // Delegate to existing renderStyles\n return renderStyles(filteredStyles);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,qBACd,QACA,WACA,WACc;AAEd,KAAI,UAAU,WAAW,EACvB,QAAO;EAAE,OAAO,EAAE;EAAE,WAAW;EAAI;AAIrC,KAAI,cAAc,YAAY,cAC5B,QAAO,yBAAyB,QAAQ,UAAU;CAKpD,MAAM,wBAAwB,6BAA6B,OAAO;CAIlE,MAAM,iBAAyB,EAAE;AAGjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAIxB,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OACZ,gBAAe,OAAO;;AAK1B,QAAO,aAAa,eAAe;;;;;;;;;;;;AAarC,SAAS,yBACP,QACA,cACc;CAGd,MAAM,wBAAwB,6BAA6B,OAAO;CAGlE,MAAM,iBAAyB,EAAE;AAGjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAIxB,MAAK,MAAM,OAAO,cAAc;EAC9B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OACZ,gBAAe,OAAO;;AAK1B,KAAI,OAAO,MAAM,OACf,gBAAe,IAAI,OAAO;AAI5B,QAAO,aAAa,eAAe"}
|
|
1
|
+
{"version":3,"file":"renderChunk.js","names":[],"sources":["../../src/chunks/renderChunk.ts"],"sourcesContent":["/**\n * Chunk-specific style rendering.\n *\n * Renders styles for a specific chunk by filtering the styles object\n * to only include relevant keys before passing to renderStyles.\n */\n\nimport type { RenderResult } from '../pipeline';\nimport { renderStyles } from '../pipeline';\nimport { extractLocalPredefinedStates } from '../states';\nimport type { Styles } from '../styles/types';\n\nimport { CHUNK_NAMES } from './definitions';\n\n/**\n * Render styles for a specific chunk.\n *\n * Creates a filtered styles object containing only the keys for this chunk,\n * then delegates to the existing renderStyles function.\n *\n * IMPORTANT: Local predefined states (e.g., '@mobile': '@media(w < 600px)')\n * are always included in the filtered styles, regardless of which chunk is\n * being rendered. This ensures that state references like '@mobile' in any\n * chunk can be properly resolved by the pipeline.\n *\n * @param styles - The full styles object\n * @param chunkName - Name of the chunk being rendered\n * @param styleKeys - Keys of styles belonging to this chunk\n * @returns RenderResult with rules for this chunk\n */\nexport function renderStylesForChunk(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): RenderResult {\n // Empty chunk - return empty result\n if (styleKeys.length === 0) {\n return { rules: [], className: '' };\n }\n\n // For subcomponents, we need to preserve the nested structure\n if (chunkName === CHUNK_NAMES.SUBCOMPONENTS) {\n return renderSubcomponentsChunk(styles, styleKeys);\n }\n\n // Extract local predefined states from the full styles object\n // These must be included for state resolution to work across chunks\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n\n // For regular chunks, create a filtered styles object\n // This is memory-efficient: we only create a shallow copy with filtered keys\n const filteredStyles: Styles = {};\n\n // First, add local predefined states so they're available for resolution\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n // Then add the chunk's style keys\n for (const key of styleKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n // Delegate to existing renderStyles\n return renderStyles(filteredStyles);\n}\n\n/**\n * Render the subcomponents chunk.\n *\n * Subcomponents (selectors like Label, &::before, etc.) contain nested\n * style objects that need to be preserved in their entirety.\n *\n * @param styles - The full styles object\n * @param selectorKeys - Keys of selectors in this chunk\n * @returns RenderResult with rules for all subcomponents\n */\nfunction renderSubcomponentsChunk(\n styles: Styles,\n selectorKeys: string[],\n): RenderResult {\n // Extract local predefined states from the full styles object\n // These must be included for state resolution to work in nested styles\n const localPredefinedStates = extractLocalPredefinedStates(styles);\n\n // Create a styles object containing only the selector keys\n const filteredStyles: Styles = {};\n\n // First, add local predefined states so they're available for resolution\n for (const [key, value] of Object.entries(localPredefinedStates)) {\n filteredStyles[key] = value;\n }\n\n // Then add the selector keys\n for (const key of selectorKeys) {\n const value = styles[key];\n if (value !== undefined) {\n filteredStyles[key] = value;\n }\n }\n\n // Also copy the $ helper if present (used for selector combinators)\n if (styles.$ !== undefined) {\n filteredStyles.$ = styles.$;\n }\n\n // Delegate to existing renderStyles\n return renderStyles(filteredStyles);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,qBACd,QACA,WACA,WACc;AAEd,KAAI,UAAU,WAAW,EACvB,QAAO;EAAE,OAAO,EAAE;EAAE,WAAW;EAAI;AAIrC,KAAI,cAAc,YAAY,cAC5B,QAAO,yBAAyB,QAAQ,UAAU;CAKpD,MAAM,wBAAwB,6BAA6B,OAAO;CAIlE,MAAM,iBAAyB,EAAE;AAGjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAIxB,MAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OACZ,gBAAe,OAAO;;AAK1B,QAAO,aAAa,eAAe;;;;;;;;;;;;AAarC,SAAS,yBACP,QACA,cACc;CAGd,MAAM,wBAAwB,6BAA6B,OAAO;CAGlE,MAAM,iBAAyB,EAAE;AAGjC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,sBAAsB,CAC9D,gBAAe,OAAO;AAIxB,MAAK,MAAM,OAAO,cAAc;EAC9B,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OACZ,gBAAe,OAAO;;AAK1B,KAAI,OAAO,MAAM,OACf,gBAAe,IAAI,OAAO;AAI5B,QAAO,aAAa,eAAe"}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Tasty Configuration Module\n *\n * Centralizes all tasty configuration, including:\n * - Style injector settings (nonce, cleanup thresholds, etc.)\n * - Global predefined states for advanced state mapping\n * - stylesGenerated flag that locks configuration after first style generation\n *\n * Configuration must be done BEFORE any styles are generated.\n * After the first `inject()` call, configuration is locked and attempts to\n * reconfigure will emit a warning and be ignored.\n */\n\nimport { StyleInjector } from './injector/injector';\nimport { clearPipelineCache, isSelector } from './pipeline';\nimport { setGlobalPredefinedStates } from './states';\nimport {\n normalizeHandlerDefinition,\n registerHandler,\n resetHandlers,\n} from './styles/predefined';\nimport { isDevEnv } from './utils/is-dev-env';\nimport {\n CUSTOM_UNITS,\n getGlobalFuncs,\n getGlobalParser,\n normalizeColorTokenValue,\n resetGlobalPredefinedTokens,\n setGlobalPredefinedTokens,\n} from './utils/styles';\n\nimport type { KeyframesSteps, PropertyDefinition } from './injector/types';\nimport type { StyleDetails, UnitHandler } from './parser/types';\nimport type { TastyPlugin } from './plugins/types';\nimport type { RecipeStyles } from './styles/types';\nimport type { StyleHandlerDefinition } from './utils/styles';\n\n/**\n * Configuration options for the Tasty style system\n */\nexport interface TastyConfig {\n /** CSP nonce for style elements */\n nonce?: string;\n /** Maximum rules per stylesheet (default: 8192) */\n maxRulesPerSheet?: number;\n /** Threshold for bulk cleanup of unused styles (default: 500) */\n unusedStylesThreshold?: number;\n /** Delay before bulk cleanup in ms, ignored if idleCleanup is true (default: 5000) */\n bulkCleanupDelay?: number;\n /** Use requestIdleCallback for cleanup when available (default: true) */\n idleCleanup?: boolean;\n /** Force text injection mode, auto-detected in test environments (default: auto) */\n forceTextInjection?: boolean;\n /** Enable development mode features: performance metrics and debug info (default: auto) */\n devMode?: boolean;\n /**\n * Ratio of unused styles to delete per bulk cleanup run (0..1).\n * Defaults to 0.5 (oldest half) to reduce risk of removing styles\n * that may be restored shortly after being marked unused.\n */\n bulkCleanupBatchRatio?: number;\n /**\n * Minimum age (in ms) a style must remain unused before eligible for deletion.\n * Helps avoid races during rapid mount/unmount cycles. Default: 10000ms.\n */\n unusedStylesMinAgeMs?: number;\n /**\n * Global predefined states for advanced state mapping.\n * These are state aliases that can be used in any component.\n * Example: { '@mobile': '@media(w < 920px)', '@dark': '@root(theme=dark)' }\n */\n states?: Record<string, string>;\n /**\n * Parser LRU cache size (default: 1000).\n * Larger values improve performance for apps with many unique style values.\n */\n parserCacheSize?: number;\n /**\n * Custom units for the style parser (merged with built-in units).\n * Units transform numeric values like `2x` → `calc(2 * var(--gap))`.\n * @example { em: 'em', vw: 'vw', custom: (n) => `${n * 10}px` }\n */\n units?: Record<string, string | UnitHandler>;\n /**\n * Custom functions for the style parser (merged with existing).\n * Functions process parsed style groups and return CSS values.\n * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }\n */\n funcs?: Record<string, (groups: StyleDetails[]) => string>;\n /**\n * Plugins that extend tasty with custom functions, units, or states.\n * Plugins are processed in order, with later plugins overriding earlier ones.\n * @example\n * ```ts\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n * ```\n */\n plugins?: TastyPlugin[];\n /**\n * Global keyframes definitions that can be referenced by animation names in styles.\n * Keys are animation names, values are keyframes step definitions.\n * Keyframes are only injected when actually used in styles.\n * @example\n * ```ts\n * configure({\n * keyframes: {\n * fadeIn: { from: { opacity: 0 }, to: { opacity: 1 } },\n * pulse: { '0%, 100%': { transform: 'scale(1)' }, '50%': { transform: 'scale(1.05)' } },\n * },\n * });\n * ```\n */\n keyframes?: Record<string, KeyframesSteps>;\n /**\n * Global CSS @property definitions for custom properties.\n * Keys use tasty token syntax ($name for properties, #name for colors).\n * Properties are only injected when the component using them is rendered.\n *\n * For color tokens (#name), `syntax: '<color>'` is auto-set and\n * `initialValue` defaults to `'transparent'` if not specified.\n *\n * @example\n * ```ts\n * configure({\n * properties: {\n * '$rotation': { syntax: '<angle>', initialValue: '0deg' },\n * '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },\n * '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set\n * },\n * });\n *\n * // Now use in styles - properties are registered when component renders:\n * const Spinner = tasty({\n * styles: {\n * transform: 'rotate($rotation)',\n * transition: '$$rotation 0.3s', // outputs: --rotation 0.3s\n * },\n * });\n * ```\n */\n properties?: Record<string, PropertyDefinition>;\n /**\n * Custom style handlers that transform style properties into CSS declarations.\n * Handlers replace built-in handlers for the same style name.\n * @example\n * ```ts\n * import { styleHandlers } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * // Override fill with custom behavior\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * // Add new custom style\n * elevation: ({ elevation }) => {\n * const level = parseInt(elevation) || 1;\n * return {\n * 'box-shadow': `0 ${level * 2}px ${level * 4}px rgba(0,0,0,0.1)`,\n * 'z-index': String(level * 100),\n * };\n * },\n * },\n * });\n * ```\n */\n handlers?: Record<string, StyleHandlerDefinition>;\n /**\n * Predefined tokens that are replaced during style parsing.\n * Token values are processed through the parser (like component tokens).\n * Use `$name` for custom properties and `#name` for color tokens.\n *\n * For color tokens (#name), boolean `true` is converted to `transparent`.\n *\n * @example\n * ```ts\n * configure({\n * tokens: {\n * $spacing: '2x',\n * '$card-padding': '4x',\n * '#accent': '#purple',\n * '#surface': '#white',\n * '#overlay': true, // → transparent\n * },\n * });\n *\n * // Now use in styles - tokens are replaced at parse time:\n * const Card = tasty({\n * styles: {\n * padding: '$card-padding', // → calc(4 * var(--gap))\n * fill: '#surface', // → var(--white-color)\n * },\n * });\n * ```\n */\n tokens?: Record<`$${string}`, string | number | boolean> & Record<`#${string}`, string | number | boolean>;\n /**\n * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.\n * Recipe values are flat tasty styles (no sub-element keys). They may contain base styles,\n * tokens (`$name`/`#name` definitions), local states, `@keyframes`, and `@properties`.\n *\n * Components reference recipes via: `recipe: 'name1 name2'` in their styles.\n * Use `|` to separate base recipes from post recipes: `recipe: 'base1 base2 | post1'`.\n * Resolution order: `base_recipes → component styles → post_recipes`.\n *\n * Recipes cannot reference other recipes.\n *\n * @example\n * ```ts\n * configure({\n * recipes: {\n * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },\n * elevated: { shadow: '2x 2x 4x #shadow' },\n * },\n * });\n *\n * // Usage in styles:\n * const Card = tasty({\n * styles: {\n * recipe: 'card elevated',\n * color: '#text', // Overrides recipe values\n * },\n * });\n * ```\n */\n recipes?: Record<string, RecipeStyles>;\n}\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n// ============================================================================\n// Configuration State\n// ============================================================================\n\n// Track whether styles have been generated (locks configuration)\nlet stylesGenerated = false;\n\n// Current configuration (null until first configure() or auto-configured on first use)\nlet currentConfig: TastyConfig | null = null;\n\n// Global keyframes storage (null = no keyframes configured, empty object checked via hasGlobalKeyframes)\nlet globalKeyframes: Record<string, KeyframesSteps> | null = null;\n\n// Global properties storage (null = no properties configured)\nlet globalProperties: Record<string, PropertyDefinition> | null = null;\n\n// Global recipes storage (null = no recipes configured)\nlet globalRecipes: Record<string, RecipeStyles> | null = null;\n\n/**\n * Internal properties required by tasty core features.\n * These are always injected when styles are first generated.\n * Keys use tasty token syntax (#name for colors, $name for other properties).\n *\n * For properties with CSS @property-compatible types (length, time, number, color),\n * an `initialValue` is provided so the property works even without a project-level token.\n */\nexport const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {\n // Used by dual-fill feature to enable CSS transitions on the second fill color\n '#tasty-second-fill': {\n inherits: false,\n initialValue: 'transparent',\n },\n // Current color context variable (set by the color style handler).\n // Companion --current-color-rgb is auto-created.\n '#current': {\n inherits: true,\n initialValue: 'transparent',\n },\n // White and black are fundamental colors that need explicit initial values.\n // Companion -rgb properties are auto-created from the color initial values.\n '#white': {\n inherits: true,\n initialValue: 'rgb(255 255 255)',\n },\n '#black': {\n inherits: true,\n initialValue: 'rgb(0 0 0)',\n },\n\n // ---- Core design tokens used by style handlers ----\n // These provide sensible defaults so tasty works standalone without a design system.\n // Consuming projects (e.g. uikit) override these by defining tokens on :root.\n\n $gap: {\n syntax: '<length>',\n inherits: true,\n initialValue: '4px',\n },\n $radius: {\n syntax: '<length>',\n inherits: true,\n initialValue: '6px',\n },\n '$border-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '1px',\n },\n '$outline-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '3px',\n },\n $transition: {\n syntax: '<time>',\n inherits: true,\n initialValue: '80ms',\n },\n // Used by radius.ts for `radius=\"leaf\"` modifier\n '$sharp-radius': {\n syntax: '<length>',\n inherits: true,\n initialValue: '0px',\n },\n // Used by preset.ts for `preset=\"... strong\"`\n '$bold-font-weight': {\n syntax: '<number>',\n inherits: true,\n initialValue: '700',\n },\n};\n\n/**\n * Internal token defaults that cannot be expressed as CSS @property initial values\n * (e.g. font stacks, keyword colors). These are injected as :root CSS variables.\n * Consuming projects override them by setting their own tokens on :root.\n *\n * Keys are raw CSS custom property names (--name).\n */\nexport const INTERNAL_TOKENS: Record<string, string> = {\n '--font':\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif',\n '--monospace-font':\n 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n // Default border color to the element's current text color\n '--border-color': 'currentColor',\n};\n\n// Global injector instance key\nconst GLOBAL_INJECTOR_KEY = '__TASTY_GLOBAL_INJECTOR__';\n\ninterface TastyGlobalStorage {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n}\n\ndeclare global {\n interface Window {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n }\n\n \n var __TASTY_GLOBAL_INJECTOR__: StyleInjector | undefined;\n}\n\n/**\n * Detect if we're running in a test environment\n */\nexport function isTestEnvironment(): boolean {\n // Check Node.js environment\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') {\n return true;\n }\n\n // Check for test runner globals (safely)\n if (typeof global !== 'undefined') {\n const g = global as unknown as Record<string, unknown>;\n if (g.vi || g.jest || g.expect || g.describe || g.it) {\n return true;\n }\n }\n\n // Check for jsdom environment (common in tests)\n if (\n typeof window !== 'undefined' &&\n window.navigator?.userAgent?.includes('jsdom')\n ) {\n return true;\n }\n\n // Check for other test runners\n if (typeof globalThis !== 'undefined') {\n const gt = globalThis as unknown as Record<string, unknown>;\n if (gt.vitest || gt.mocha) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Create default configuration with optional test environment detection\n */\nfunction createDefaultConfig(isTest?: boolean): TastyConfig {\n return {\n maxRulesPerSheet: 8192,\n unusedStylesThreshold: 500,\n bulkCleanupDelay: 5000,\n idleCleanup: true,\n forceTextInjection: isTest ?? false,\n devMode: isDevEnv(),\n bulkCleanupBatchRatio: 0.5,\n unusedStylesMinAgeMs: 10000,\n };\n}\n\n// ============================================================================\n// stylesGenerated Flag Management\n// ============================================================================\n\n/**\n * Mark that styles have been generated (called by injector on first inject)\n * This locks the configuration - no further changes allowed.\n * Also injects internal and global properties.\n */\nexport function markStylesGenerated(): void {\n if (stylesGenerated) return; // Already marked, skip\n\n stylesGenerated = true;\n\n const injector = getGlobalInjector();\n\n // Inject internal properties required by tasty core features\n for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {\n injector.property(token, definition);\n }\n\n // Inject internal token defaults as :root CSS variables.\n // Use injectGlobal (not injectRawCSS) so the rule goes through the same\n // injection path as all other tasty styles (consistent in both DOM and text/test mode).\n const internalTokenEntries = Object.entries(INTERNAL_TOKENS);\n if (internalTokenEntries.length > 0) {\n const declarations = internalTokenEntries\n .map(([name, value]) => `${name}: ${value}`)\n .join('; ');\n injector.injectGlobal([{ selector: ':root', declarations }]);\n }\n\n // Inject global properties if any were configured\n // Properties are permanent and only need to be injected once\n if (globalProperties && Object.keys(globalProperties).length > 0) {\n for (const [token, definition] of Object.entries(globalProperties)) {\n injector.property(token, definition);\n }\n }\n}\n\n/**\n * Check if styles have been generated (configuration is locked)\n */\nexport function hasStylesGenerated(): boolean {\n return stylesGenerated;\n}\n\n/**\n * Reset styles generated flag (for testing only)\n */\nexport function resetStylesGenerated(): void {\n stylesGenerated = false;\n emittedWarnings.clear();\n}\n\n// ============================================================================\n// Global Keyframes Management\n// ============================================================================\n\n/**\n * Check if any global keyframes are configured.\n * Fast path: returns false if no keyframes were ever set.\n */\nexport function hasGlobalKeyframes(): boolean {\n return globalKeyframes !== null && Object.keys(globalKeyframes).length > 0;\n}\n\n/**\n * Get global keyframes configuration.\n * Returns null if no keyframes configured (fast path for zero-overhead).\n */\nexport function getGlobalKeyframes(): Record<string, KeyframesSteps> | null {\n return globalKeyframes;\n}\n\n/**\n * Set global keyframes (called from configure).\n * Internal use only.\n */\nfunction setGlobalKeyframes(keyframes: Record<string, KeyframesSteps>): void {\n if (stylesGenerated) {\n warnOnce(\n 'keyframes-after-styles',\n `[Tasty] Cannot update keyframes after styles have been generated.\\n` +\n `The new keyframes will be ignored.`,\n );\n return;\n }\n globalKeyframes = keyframes;\n}\n\n// ============================================================================\n// Global Properties Management\n// ============================================================================\n\n/**\n * Check if any global properties are configured.\n * Fast path: returns false if no properties were ever set.\n */\nexport function hasGlobalProperties(): boolean {\n return globalProperties !== null && Object.keys(globalProperties).length > 0;\n}\n\n/**\n * Get global properties configuration.\n * Returns null if no properties configured (fast path for zero-overhead).\n */\nexport function getGlobalProperties(): Record<\n string,\n PropertyDefinition\n> | null {\n return globalProperties;\n}\n\n/**\n * Set global properties (called from configure).\n * Internal use only.\n */\nfunction setGlobalProperties(\n properties: Record<string, PropertyDefinition>,\n): void {\n if (stylesGenerated) {\n warnOnce(\n 'properties-after-styles',\n `[Tasty] Cannot update properties after styles have been generated.\\n` +\n `The new properties will be ignored.`,\n );\n return;\n }\n globalProperties = properties;\n}\n\n// ============================================================================\n// Global Recipes Management\n// ============================================================================\n\n/**\n * Check if any global recipes are configured.\n * Fast path: returns false if no recipes were ever set.\n */\nexport function hasGlobalRecipes(): boolean {\n return globalRecipes !== null && Object.keys(globalRecipes).length > 0;\n}\n\n/**\n * Get global recipes configuration.\n * Returns null if no recipes configured (fast path for zero-overhead).\n */\nexport function getGlobalRecipes(): Record<string, RecipeStyles> | null {\n return globalRecipes;\n}\n\n/**\n * Set global recipes (called from configure).\n * Internal use only.\n */\nfunction setGlobalRecipes(recipes: Record<string, RecipeStyles>): void {\n if (stylesGenerated) {\n warnOnce(\n 'recipes-after-styles',\n `[Tasty] Cannot update recipes after styles have been generated.\\n` +\n `The new recipes will be ignored.`,\n );\n return;\n }\n\n // Dev-mode validation\n if (devMode) {\n for (const [name, recipeStyles] of Object.entries(recipes)) {\n for (const key of Object.keys(recipeStyles)) {\n if (isSelector(key)) {\n warnOnce(\n `recipe-selector-${name}-${key}`,\n `[Tasty] Recipe \"${name}\" contains sub-element key \"${key}\". ` +\n `Recipes must be flat styles without sub-element keys. ` +\n `Remove the sub-element key from the recipe definition.`,\n );\n }\n if (key === 'recipe') {\n warnOnce(\n `recipe-recursive-${name}`,\n `[Tasty] Recipe \"${name}\" contains a \"recipe\" key. ` +\n `Recipes cannot reference other recipes. ` +\n `Use space-separated names for composition: recipe: 'base elevated'.`,\n );\n }\n }\n }\n }\n\n globalRecipes = recipes;\n}\n\n/**\n * Check if configuration is locked (styles have been generated)\n */\nexport function isConfigLocked(): boolean {\n return stylesGenerated;\n}\n\n// ============================================================================\n// Configuration API\n// ============================================================================\n\n/**\n * Configure the Tasty style system.\n *\n * Must be called BEFORE any styles are generated (before first render that uses tasty).\n * After styles are generated, configuration is locked and calls to configure() will\n * emit a warning and be ignored.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n *\n * // Configure before app renders\n * configure({\n * nonce: 'abc123',\n * states: {\n * '@mobile': '@media(w < 768px)',\n * '@dark': '@root(theme=dark)',\n * },\n * });\n * ```\n */\nexport function configure(config: Partial<TastyConfig> = {}): void {\n if (stylesGenerated) {\n warnOnce(\n 'configure-after-styles',\n `[Tasty] Cannot call configure() after styles have been generated.\\n` +\n `Configuration must be done before the first render. The configuration will be ignored.`,\n );\n return;\n }\n\n // Collect merged values from plugins first, then override with direct config\n let mergedStates: Record<string, string> = {};\n let mergedUnits: Record<string, string | UnitHandler> = {};\n let mergedFuncs: Record<string, (groups: StyleDetails[]) => string> = {};\n let mergedHandlers: Record<string, StyleHandlerDefinition> = {};\n let mergedTokens: Record<string, string | number | boolean> = {};\n let mergedRecipes: Record<string, RecipeStyles> = {};\n\n // Process plugins in order\n if (config.plugins) {\n for (const plugin of config.plugins) {\n if (plugin.states) {\n mergedStates = { ...mergedStates, ...plugin.states };\n }\n if (plugin.units) {\n mergedUnits = { ...mergedUnits, ...plugin.units };\n }\n if (plugin.funcs) {\n mergedFuncs = { ...mergedFuncs, ...plugin.funcs };\n }\n if (plugin.handlers) {\n mergedHandlers = { ...mergedHandlers, ...plugin.handlers };\n }\n if (plugin.tokens) {\n mergedTokens = { ...mergedTokens, ...plugin.tokens };\n }\n if (plugin.recipes) {\n mergedRecipes = { ...mergedRecipes, ...plugin.recipes };\n }\n }\n }\n\n // Direct config overrides plugins\n if (config.states) {\n mergedStates = { ...mergedStates, ...config.states };\n }\n if (config.units) {\n mergedUnits = { ...mergedUnits, ...config.units };\n }\n if (config.funcs) {\n mergedFuncs = { ...mergedFuncs, ...config.funcs };\n }\n if (config.handlers) {\n mergedHandlers = { ...mergedHandlers, ...config.handlers };\n }\n if (config.tokens) {\n mergedTokens = { ...mergedTokens, ...config.tokens };\n }\n if (config.recipes) {\n mergedRecipes = { ...mergedRecipes, ...config.recipes };\n }\n\n // Handle predefined states\n if (Object.keys(mergedStates).length > 0) {\n setGlobalPredefinedStates(mergedStates);\n }\n\n // Handle parser configuration (merge semantics - extend, not replace)\n const parser = getGlobalParser();\n\n if (config.parserCacheSize !== undefined) {\n parser.updateOptions({ cacheSize: config.parserCacheSize });\n }\n\n if (Object.keys(mergedUnits).length > 0) {\n // Merge with existing units\n const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;\n parser.setUnits({ ...currentUnits, ...mergedUnits });\n }\n\n if (Object.keys(mergedFuncs).length > 0) {\n // Merge with existing funcs\n const currentFuncs = getGlobalFuncs();\n const finalFuncs = { ...currentFuncs, ...mergedFuncs };\n parser.setFuncs(finalFuncs);\n // Also update the global registry so customFunc() continues to work\n Object.assign(currentFuncs, mergedFuncs);\n }\n\n // Handle keyframes\n if (config.keyframes) {\n setGlobalKeyframes(config.keyframes);\n }\n\n // Handle properties\n if (config.properties) {\n setGlobalProperties(config.properties);\n }\n\n // Handle custom handlers\n if (Object.keys(mergedHandlers).length > 0) {\n for (const [name, definition] of Object.entries(mergedHandlers)) {\n const handler = normalizeHandlerDefinition(name, definition);\n registerHandler(handler);\n }\n }\n\n // Handle predefined tokens\n // Note: Tokens are processed by the classifier, not here.\n // We just store the raw values; the classifier will process them when encountered.\n if (Object.keys(mergedTokens).length > 0) {\n // Store tokens (keys are normalized to lowercase by setGlobalPredefinedTokens)\n const processedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(mergedTokens)) {\n if (key.startsWith('#')) {\n // Color token - use shared helper for boolean handling\n const normalized = normalizeColorTokenValue(value);\n if (normalized === null) continue; // Skip false values\n processedTokens[key] = String(normalized);\n } else if (value === false) {\n // Skip false values for non-color tokens\n continue;\n } else {\n processedTokens[key] = String(value);\n }\n }\n setGlobalPredefinedTokens(processedTokens);\n }\n\n // Handle recipes\n if (Object.keys(mergedRecipes).length > 0) {\n setGlobalRecipes(mergedRecipes);\n }\n\n \n const {\n states: _states,\n parserCacheSize: _parserCacheSize,\n units: _units,\n funcs: _funcs,\n plugins: _plugins,\n keyframes: _keyframes,\n properties: _properties,\n handlers: _handlers,\n tokens: _tokens,\n recipes: _recipes,\n ...injectorConfig\n } = config;\n \n\n const fullConfig: TastyConfig = {\n ...createDefaultConfig(),\n ...currentConfig,\n ...injectorConfig,\n };\n\n // Store the config\n currentConfig = fullConfig;\n\n // Create/replace the global injector\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n storage[GLOBAL_INJECTOR_KEY] = new StyleInjector(fullConfig);\n}\n\n/**\n * Get the current configuration.\n * If not configured, returns default configuration.\n */\nexport function getConfig(): TastyConfig {\n if (!currentConfig) {\n currentConfig = createDefaultConfig(isTestEnvironment());\n }\n return currentConfig;\n}\n\n/**\n * Get the global injector instance.\n * Auto-configures with defaults if not already configured.\n */\nexport function getGlobalInjector(): StyleInjector {\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n\n if (!storage[GLOBAL_INJECTOR_KEY]) {\n configure();\n }\n\n return storage[GLOBAL_INJECTOR_KEY]!;\n}\n\n/**\n * Reset configuration (for testing only).\n * Clears the global injector and allows reconfiguration.\n */\nexport function resetConfig(): void {\n stylesGenerated = false;\n currentConfig = null;\n globalKeyframes = null;\n globalProperties = null;\n globalRecipes = null;\n resetGlobalPredefinedTokens();\n resetHandlers();\n clearPipelineCache();\n emittedWarnings.clear();\n\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n delete storage[GLOBAL_INJECTOR_KEY];\n}\n\n// Re-export TastyConfig as StyleInjectorConfig for backward compatibility\nexport type { TastyConfig as StyleInjectorConfig };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Tasty Configuration Module\n *\n * Centralizes all tasty configuration, including:\n * - Style injector settings (nonce, cleanup thresholds, etc.)\n * - Global predefined states for advanced state mapping\n * - stylesGenerated flag that locks configuration after first style generation\n *\n * Configuration must be done BEFORE any styles are generated.\n * After the first `inject()` call, configuration is locked and attempts to\n * reconfigure will emit a warning and be ignored.\n */\n\nimport { StyleInjector } from './injector/injector';\nimport { clearPipelineCache, isSelector } from './pipeline';\nimport { setGlobalPredefinedStates } from './states';\nimport {\n normalizeHandlerDefinition,\n registerHandler,\n resetHandlers,\n} from './styles/predefined';\nimport { isDevEnv } from './utils/is-dev-env';\nimport {\n CUSTOM_UNITS,\n getGlobalFuncs,\n getGlobalParser,\n normalizeColorTokenValue,\n resetGlobalPredefinedTokens,\n setGlobalPredefinedTokens,\n} from './utils/styles';\n\nimport type { KeyframesSteps, PropertyDefinition } from './injector/types';\nimport type { StyleDetails, UnitHandler } from './parser/types';\nimport type { TastyPlugin } from './plugins/types';\nimport type { RecipeStyles } from './styles/types';\nimport type { StyleHandlerDefinition } from './utils/styles';\n\n/**\n * Configuration options for the Tasty style system\n */\nexport interface TastyConfig {\n /** CSP nonce for style elements */\n nonce?: string;\n /** Maximum rules per stylesheet (default: 8192) */\n maxRulesPerSheet?: number;\n /** Threshold for bulk cleanup of unused styles (default: 500) */\n unusedStylesThreshold?: number;\n /** Delay before bulk cleanup in ms, ignored if idleCleanup is true (default: 5000) */\n bulkCleanupDelay?: number;\n /** Use requestIdleCallback for cleanup when available (default: true) */\n idleCleanup?: boolean;\n /** Force text injection mode, auto-detected in test environments (default: auto) */\n forceTextInjection?: boolean;\n /** Enable development mode features: performance metrics and debug info (default: auto) */\n devMode?: boolean;\n /**\n * Ratio of unused styles to delete per bulk cleanup run (0..1).\n * Defaults to 0.5 (oldest half) to reduce risk of removing styles\n * that may be restored shortly after being marked unused.\n */\n bulkCleanupBatchRatio?: number;\n /**\n * Minimum age (in ms) a style must remain unused before eligible for deletion.\n * Helps avoid races during rapid mount/unmount cycles. Default: 10000ms.\n */\n unusedStylesMinAgeMs?: number;\n /**\n * Global predefined states for advanced state mapping.\n * These are state aliases that can be used in any component.\n * Example: { '@mobile': '@media(w < 920px)', '@dark': '@root(theme=dark)' }\n */\n states?: Record<string, string>;\n /**\n * Parser LRU cache size (default: 1000).\n * Larger values improve performance for apps with many unique style values.\n */\n parserCacheSize?: number;\n /**\n * Custom units for the style parser (merged with built-in units).\n * Units transform numeric values like `2x` → `calc(2 * var(--gap))`.\n * @example { em: 'em', vw: 'vw', custom: (n) => `${n * 10}px` }\n */\n units?: Record<string, string | UnitHandler>;\n /**\n * Custom functions for the style parser (merged with existing).\n * Functions process parsed style groups and return CSS values.\n * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }\n */\n funcs?: Record<string, (groups: StyleDetails[]) => string>;\n /**\n * Plugins that extend tasty with custom functions, units, or states.\n * Plugins are processed in order, with later plugins overriding earlier ones.\n * @example\n * ```ts\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n * ```\n */\n plugins?: TastyPlugin[];\n /**\n * Global keyframes definitions that can be referenced by animation names in styles.\n * Keys are animation names, values are keyframes step definitions.\n * Keyframes are only injected when actually used in styles.\n * @example\n * ```ts\n * configure({\n * keyframes: {\n * fadeIn: { from: { opacity: 0 }, to: { opacity: 1 } },\n * pulse: { '0%, 100%': { transform: 'scale(1)' }, '50%': { transform: 'scale(1.05)' } },\n * },\n * });\n * ```\n */\n keyframes?: Record<string, KeyframesSteps>;\n /**\n * Global CSS @property definitions for custom properties.\n * Keys use tasty token syntax ($name for properties, #name for colors).\n * Properties are only injected when the component using them is rendered.\n *\n * For color tokens (#name), `syntax: '<color>'` is auto-set and\n * `initialValue` defaults to `'transparent'` if not specified.\n *\n * @example\n * ```ts\n * configure({\n * properties: {\n * '$rotation': { syntax: '<angle>', initialValue: '0deg' },\n * '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },\n * '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set\n * },\n * });\n *\n * // Now use in styles - properties are registered when component renders:\n * const Spinner = tasty({\n * styles: {\n * transform: 'rotate($rotation)',\n * transition: '$$rotation 0.3s', // outputs: --rotation 0.3s\n * },\n * });\n * ```\n */\n properties?: Record<string, PropertyDefinition>;\n /**\n * Custom style handlers that transform style properties into CSS declarations.\n * Handlers replace built-in handlers for the same style name.\n * @example\n * ```ts\n * import { styleHandlers } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * // Override fill with custom behavior\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * // Add new custom style\n * elevation: ({ elevation }) => {\n * const level = parseInt(elevation) || 1;\n * return {\n * 'box-shadow': `0 ${level * 2}px ${level * 4}px rgba(0,0,0,0.1)`,\n * 'z-index': String(level * 100),\n * };\n * },\n * },\n * });\n * ```\n */\n handlers?: Record<string, StyleHandlerDefinition>;\n /**\n * Predefined tokens that are replaced during style parsing.\n * Token values are processed through the parser (like component tokens).\n * Use `$name` for custom properties and `#name` for color tokens.\n *\n * For color tokens (#name), boolean `true` is converted to `transparent`.\n *\n * @example\n * ```ts\n * configure({\n * tokens: {\n * $spacing: '2x',\n * '$card-padding': '4x',\n * '#accent': '#purple',\n * '#surface': '#white',\n * '#overlay': true, // → transparent\n * },\n * });\n *\n * // Now use in styles - tokens are replaced at parse time:\n * const Card = tasty({\n * styles: {\n * padding: '$card-padding', // → calc(4 * var(--gap))\n * fill: '#surface', // → var(--white-color)\n * },\n * });\n * ```\n */\n tokens?: Record<`$${string}`, string | number | boolean> &\n Record<`#${string}`, string | number | boolean>;\n /**\n * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.\n * Recipe values are flat tasty styles (no sub-element keys). They may contain base styles,\n * tokens (`$name`/`#name` definitions), local states, `@keyframes`, and `@properties`.\n *\n * Components reference recipes via: `recipe: 'name1 name2'` in their styles.\n * Use `|` to separate base recipes from post recipes: `recipe: 'base1 base2 | post1'`.\n * Resolution order: `base_recipes → component styles → post_recipes`.\n *\n * Recipes cannot reference other recipes.\n *\n * @example\n * ```ts\n * configure({\n * recipes: {\n * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },\n * elevated: { shadow: '2x 2x 4x #shadow' },\n * },\n * });\n *\n * // Usage in styles:\n * const Card = tasty({\n * styles: {\n * recipe: 'card elevated',\n * color: '#text', // Overrides recipe values\n * },\n * });\n * ```\n */\n recipes?: Record<string, RecipeStyles>;\n}\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n// ============================================================================\n// Configuration State\n// ============================================================================\n\n// Track whether styles have been generated (locks configuration)\nlet stylesGenerated = false;\n\n// Current configuration (null until first configure() or auto-configured on first use)\nlet currentConfig: TastyConfig | null = null;\n\n// Global keyframes storage (null = no keyframes configured, empty object checked via hasGlobalKeyframes)\nlet globalKeyframes: Record<string, KeyframesSteps> | null = null;\n\n// Global properties storage (null = no properties configured)\nlet globalProperties: Record<string, PropertyDefinition> | null = null;\n\n// Global recipes storage (null = no recipes configured)\nlet globalRecipes: Record<string, RecipeStyles> | null = null;\n\n/**\n * Internal properties required by tasty core features.\n * These are always injected when styles are first generated.\n * Keys use tasty token syntax (#name for colors, $name for other properties).\n *\n * For properties with CSS @property-compatible types (length, time, number, color),\n * an `initialValue` is provided so the property works even without a project-level token.\n */\nexport const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {\n // Used by dual-fill feature to enable CSS transitions on the second fill color\n '#tasty-second-fill': {\n inherits: false,\n initialValue: 'transparent',\n },\n // Current color context variable (set by the color style handler).\n // Companion --current-color-rgb is auto-created.\n '#current': {\n inherits: true,\n initialValue: 'transparent',\n },\n // White and black are fundamental colors that need explicit initial values.\n // Companion -rgb properties are auto-created from the color initial values.\n '#white': {\n inherits: true,\n initialValue: 'rgb(255 255 255)',\n },\n '#black': {\n inherits: true,\n initialValue: 'rgb(0 0 0)',\n },\n\n // ---- Core design tokens used by style handlers ----\n // These provide sensible defaults so tasty works standalone without a design system.\n // Consuming projects (e.g. uikit) override these by defining tokens on :root.\n\n $gap: {\n syntax: '<length>',\n inherits: true,\n initialValue: '4px',\n },\n $radius: {\n syntax: '<length>',\n inherits: true,\n initialValue: '6px',\n },\n '$border-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '1px',\n },\n '$outline-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '3px',\n },\n $transition: {\n syntax: '<time>',\n inherits: true,\n initialValue: '80ms',\n },\n // Used by radius.ts for `radius=\"leaf\"` modifier\n '$sharp-radius': {\n syntax: '<length>',\n inherits: true,\n initialValue: '0px',\n },\n // Used by preset.ts for `preset=\"... strong\"`\n '$bold-font-weight': {\n syntax: '<number>',\n inherits: true,\n initialValue: '700',\n },\n};\n\n/**\n * Internal token defaults that cannot be expressed as CSS @property initial values\n * (e.g. font stacks, keyword colors). These are injected as :root CSS variables.\n * Consuming projects override them by setting their own tokens on :root.\n *\n * Keys are raw CSS custom property names (--name).\n */\nexport const INTERNAL_TOKENS: Record<string, string> = {\n '--font':\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif',\n '--monospace-font':\n 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n // Default border color to the element's current text color\n '--border-color': 'currentColor',\n};\n\n// Global injector instance key\nconst GLOBAL_INJECTOR_KEY = '__TASTY_GLOBAL_INJECTOR__';\n\ninterface TastyGlobalStorage {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n}\n\ndeclare global {\n interface Window {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n }\n\n var __TASTY_GLOBAL_INJECTOR__: StyleInjector | undefined;\n}\n\n/**\n * Detect if we're running in a test environment\n */\nexport function isTestEnvironment(): boolean {\n // Check Node.js environment\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') {\n return true;\n }\n\n // Check for test runner globals (safely)\n if (typeof global !== 'undefined') {\n const g = global as unknown as Record<string, unknown>;\n if (g.vi || g.jest || g.expect || g.describe || g.it) {\n return true;\n }\n }\n\n // Check for jsdom environment (common in tests)\n if (\n typeof window !== 'undefined' &&\n window.navigator?.userAgent?.includes('jsdom')\n ) {\n return true;\n }\n\n // Check for other test runners\n if (typeof globalThis !== 'undefined') {\n const gt = globalThis as unknown as Record<string, unknown>;\n if (gt.vitest || gt.mocha) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Create default configuration with optional test environment detection\n */\nfunction createDefaultConfig(isTest?: boolean): TastyConfig {\n return {\n maxRulesPerSheet: 8192,\n unusedStylesThreshold: 500,\n bulkCleanupDelay: 5000,\n idleCleanup: true,\n forceTextInjection: isTest ?? false,\n devMode: isDevEnv(),\n bulkCleanupBatchRatio: 0.5,\n unusedStylesMinAgeMs: 10000,\n };\n}\n\n// ============================================================================\n// stylesGenerated Flag Management\n// ============================================================================\n\n/**\n * Mark that styles have been generated (called by injector on first inject)\n * This locks the configuration - no further changes allowed.\n * Also injects internal and global properties.\n */\nexport function markStylesGenerated(): void {\n if (stylesGenerated) return; // Already marked, skip\n\n stylesGenerated = true;\n\n const injector = getGlobalInjector();\n\n // Inject internal properties required by tasty core features\n for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {\n injector.property(token, definition);\n }\n\n // Inject internal token defaults as :root CSS variables.\n // Use injectGlobal (not injectRawCSS) so the rule goes through the same\n // injection path as all other tasty styles (consistent in both DOM and text/test mode).\n const internalTokenEntries = Object.entries(INTERNAL_TOKENS);\n if (internalTokenEntries.length > 0) {\n const declarations = internalTokenEntries\n .map(([name, value]) => `${name}: ${value}`)\n .join('; ');\n injector.injectGlobal([{ selector: ':root', declarations }]);\n }\n\n // Inject global properties if any were configured\n // Properties are permanent and only need to be injected once\n if (globalProperties && Object.keys(globalProperties).length > 0) {\n for (const [token, definition] of Object.entries(globalProperties)) {\n injector.property(token, definition);\n }\n }\n}\n\n/**\n * Check if styles have been generated (configuration is locked)\n */\nexport function hasStylesGenerated(): boolean {\n return stylesGenerated;\n}\n\n/**\n * Reset styles generated flag (for testing only)\n */\nexport function resetStylesGenerated(): void {\n stylesGenerated = false;\n emittedWarnings.clear();\n}\n\n// ============================================================================\n// Global Keyframes Management\n// ============================================================================\n\n/**\n * Check if any global keyframes are configured.\n * Fast path: returns false if no keyframes were ever set.\n */\nexport function hasGlobalKeyframes(): boolean {\n return globalKeyframes !== null && Object.keys(globalKeyframes).length > 0;\n}\n\n/**\n * Get global keyframes configuration.\n * Returns null if no keyframes configured (fast path for zero-overhead).\n */\nexport function getGlobalKeyframes(): Record<string, KeyframesSteps> | null {\n return globalKeyframes;\n}\n\n/**\n * Set global keyframes (called from configure).\n * Internal use only.\n */\nfunction setGlobalKeyframes(keyframes: Record<string, KeyframesSteps>): void {\n if (stylesGenerated) {\n warnOnce(\n 'keyframes-after-styles',\n `[Tasty] Cannot update keyframes after styles have been generated.\\n` +\n `The new keyframes will be ignored.`,\n );\n return;\n }\n globalKeyframes = keyframes;\n}\n\n// ============================================================================\n// Global Properties Management\n// ============================================================================\n\n/**\n * Check if any global properties are configured.\n * Fast path: returns false if no properties were ever set.\n */\nexport function hasGlobalProperties(): boolean {\n return globalProperties !== null && Object.keys(globalProperties).length > 0;\n}\n\n/**\n * Get global properties configuration.\n * Returns null if no properties configured (fast path for zero-overhead).\n */\nexport function getGlobalProperties(): Record<\n string,\n PropertyDefinition\n> | null {\n return globalProperties;\n}\n\n/**\n * Set global properties (called from configure).\n * Internal use only.\n */\nfunction setGlobalProperties(\n properties: Record<string, PropertyDefinition>,\n): void {\n if (stylesGenerated) {\n warnOnce(\n 'properties-after-styles',\n `[Tasty] Cannot update properties after styles have been generated.\\n` +\n `The new properties will be ignored.`,\n );\n return;\n }\n globalProperties = properties;\n}\n\n// ============================================================================\n// Global Recipes Management\n// ============================================================================\n\n/**\n * Check if any global recipes are configured.\n * Fast path: returns false if no recipes were ever set.\n */\nexport function hasGlobalRecipes(): boolean {\n return globalRecipes !== null && Object.keys(globalRecipes).length > 0;\n}\n\n/**\n * Get global recipes configuration.\n * Returns null if no recipes configured (fast path for zero-overhead).\n */\nexport function getGlobalRecipes(): Record<string, RecipeStyles> | null {\n return globalRecipes;\n}\n\n/**\n * Set global recipes (called from configure).\n * Internal use only.\n */\nfunction setGlobalRecipes(recipes: Record<string, RecipeStyles>): void {\n if (stylesGenerated) {\n warnOnce(\n 'recipes-after-styles',\n `[Tasty] Cannot update recipes after styles have been generated.\\n` +\n `The new recipes will be ignored.`,\n );\n return;\n }\n\n // Dev-mode validation\n if (devMode) {\n for (const [name, recipeStyles] of Object.entries(recipes)) {\n for (const key of Object.keys(recipeStyles)) {\n if (isSelector(key)) {\n warnOnce(\n `recipe-selector-${name}-${key}`,\n `[Tasty] Recipe \"${name}\" contains sub-element key \"${key}\". ` +\n `Recipes must be flat styles without sub-element keys. ` +\n `Remove the sub-element key from the recipe definition.`,\n );\n }\n if (key === 'recipe') {\n warnOnce(\n `recipe-recursive-${name}`,\n `[Tasty] Recipe \"${name}\" contains a \"recipe\" key. ` +\n `Recipes cannot reference other recipes. ` +\n `Use space-separated names for composition: recipe: 'base elevated'.`,\n );\n }\n }\n }\n }\n\n globalRecipes = recipes;\n}\n\n/**\n * Check if configuration is locked (styles have been generated)\n */\nexport function isConfigLocked(): boolean {\n return stylesGenerated;\n}\n\n// ============================================================================\n// Configuration API\n// ============================================================================\n\n/**\n * Configure the Tasty style system.\n *\n * Must be called BEFORE any styles are generated (before first render that uses tasty).\n * After styles are generated, configuration is locked and calls to configure() will\n * emit a warning and be ignored.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n *\n * // Configure before app renders\n * configure({\n * nonce: 'abc123',\n * states: {\n * '@mobile': '@media(w < 768px)',\n * '@dark': '@root(theme=dark)',\n * },\n * });\n * ```\n */\nexport function configure(config: Partial<TastyConfig> = {}): void {\n if (stylesGenerated) {\n warnOnce(\n 'configure-after-styles',\n `[Tasty] Cannot call configure() after styles have been generated.\\n` +\n `Configuration must be done before the first render. The configuration will be ignored.`,\n );\n return;\n }\n\n // Collect merged values from plugins first, then override with direct config\n let mergedStates: Record<string, string> = {};\n let mergedUnits: Record<string, string | UnitHandler> = {};\n let mergedFuncs: Record<string, (groups: StyleDetails[]) => string> = {};\n let mergedHandlers: Record<string, StyleHandlerDefinition> = {};\n let mergedTokens: Record<string, string | number | boolean> = {};\n let mergedRecipes: Record<string, RecipeStyles> = {};\n\n // Process plugins in order\n if (config.plugins) {\n for (const plugin of config.plugins) {\n if (plugin.states) {\n mergedStates = { ...mergedStates, ...plugin.states };\n }\n if (plugin.units) {\n mergedUnits = { ...mergedUnits, ...plugin.units };\n }\n if (plugin.funcs) {\n mergedFuncs = { ...mergedFuncs, ...plugin.funcs };\n }\n if (plugin.handlers) {\n mergedHandlers = { ...mergedHandlers, ...plugin.handlers };\n }\n if (plugin.tokens) {\n mergedTokens = { ...mergedTokens, ...plugin.tokens };\n }\n if (plugin.recipes) {\n mergedRecipes = { ...mergedRecipes, ...plugin.recipes };\n }\n }\n }\n\n // Direct config overrides plugins\n if (config.states) {\n mergedStates = { ...mergedStates, ...config.states };\n }\n if (config.units) {\n mergedUnits = { ...mergedUnits, ...config.units };\n }\n if (config.funcs) {\n mergedFuncs = { ...mergedFuncs, ...config.funcs };\n }\n if (config.handlers) {\n mergedHandlers = { ...mergedHandlers, ...config.handlers };\n }\n if (config.tokens) {\n mergedTokens = { ...mergedTokens, ...config.tokens };\n }\n if (config.recipes) {\n mergedRecipes = { ...mergedRecipes, ...config.recipes };\n }\n\n // Handle predefined states\n if (Object.keys(mergedStates).length > 0) {\n setGlobalPredefinedStates(mergedStates);\n }\n\n // Handle parser configuration (merge semantics - extend, not replace)\n const parser = getGlobalParser();\n\n if (config.parserCacheSize !== undefined) {\n parser.updateOptions({ cacheSize: config.parserCacheSize });\n }\n\n if (Object.keys(mergedUnits).length > 0) {\n // Merge with existing units\n const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;\n parser.setUnits({ ...currentUnits, ...mergedUnits });\n }\n\n if (Object.keys(mergedFuncs).length > 0) {\n // Merge with existing funcs\n const currentFuncs = getGlobalFuncs();\n const finalFuncs = { ...currentFuncs, ...mergedFuncs };\n parser.setFuncs(finalFuncs);\n // Also update the global registry so customFunc() continues to work\n Object.assign(currentFuncs, mergedFuncs);\n }\n\n // Handle keyframes\n if (config.keyframes) {\n setGlobalKeyframes(config.keyframes);\n }\n\n // Handle properties\n if (config.properties) {\n setGlobalProperties(config.properties);\n }\n\n // Handle custom handlers\n if (Object.keys(mergedHandlers).length > 0) {\n for (const [name, definition] of Object.entries(mergedHandlers)) {\n const handler = normalizeHandlerDefinition(name, definition);\n registerHandler(handler);\n }\n }\n\n // Handle predefined tokens\n // Note: Tokens are processed by the classifier, not here.\n // We just store the raw values; the classifier will process them when encountered.\n if (Object.keys(mergedTokens).length > 0) {\n // Store tokens (keys are normalized to lowercase by setGlobalPredefinedTokens)\n const processedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(mergedTokens)) {\n if (key.startsWith('#')) {\n // Color token - use shared helper for boolean handling\n const normalized = normalizeColorTokenValue(value);\n if (normalized === null) continue; // Skip false values\n processedTokens[key] = String(normalized);\n } else if (value === false) {\n // Skip false values for non-color tokens\n continue;\n } else {\n processedTokens[key] = String(value);\n }\n }\n setGlobalPredefinedTokens(processedTokens);\n }\n\n // Handle recipes\n if (Object.keys(mergedRecipes).length > 0) {\n setGlobalRecipes(mergedRecipes);\n }\n\n const {\n states: _states,\n parserCacheSize: _parserCacheSize,\n units: _units,\n funcs: _funcs,\n plugins: _plugins,\n keyframes: _keyframes,\n properties: _properties,\n handlers: _handlers,\n tokens: _tokens,\n recipes: _recipes,\n ...injectorConfig\n } = config;\n\n const fullConfig: TastyConfig = {\n ...createDefaultConfig(),\n ...currentConfig,\n ...injectorConfig,\n };\n\n // Store the config\n currentConfig = fullConfig;\n\n // Create/replace the global injector\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n storage[GLOBAL_INJECTOR_KEY] = new StyleInjector(fullConfig);\n}\n\n/**\n * Get the current configuration.\n * If not configured, returns default configuration.\n */\nexport function getConfig(): TastyConfig {\n if (!currentConfig) {\n currentConfig = createDefaultConfig(isTestEnvironment());\n }\n return currentConfig;\n}\n\n/**\n * Get the global injector instance.\n * Auto-configures with defaults if not already configured.\n */\nexport function getGlobalInjector(): StyleInjector {\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n\n if (!storage[GLOBAL_INJECTOR_KEY]) {\n configure();\n }\n\n return storage[GLOBAL_INJECTOR_KEY]!;\n}\n\n/**\n * Reset configuration (for testing only).\n * Clears the global injector and allows reconfiguration.\n */\nexport function resetConfig(): void {\n stylesGenerated = false;\n currentConfig = null;\n globalKeyframes = null;\n globalProperties = null;\n globalRecipes = null;\n resetGlobalPredefinedTokens();\n resetHandlers();\n clearPipelineCache();\n emittedWarnings.clear();\n\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n delete storage[GLOBAL_INJECTOR_KEY];\n}\n\n// Re-export TastyConfig as StyleInjectorConfig for backward compatibility\nexport type { TastyConfig as StyleInjectorConfig };\n"],"mappings":"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"}
|
package/dist/debug.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.js","names":[],"sources":["../src/debug.ts"],"sourcesContent":["/* eslint-disable no-console */\n/**\n * Debug utilities for inspecting tasty-generated CSS at runtime\n */\n\nimport { CHUNK_NAMES } from './chunks/definitions';\nimport { getCssTextForNode, injector } from './injector';\nimport type { CleanupStats } from './injector/types';\nimport { isDevEnv } from './utils/is-dev-env';\n\ndeclare global {\n interface Window {\n tastyDebug?: typeof tastyDebug;\n }\n}\n\n// Type definitions for the new API\ntype CSSTarget =\n | 'all' // tasty CSS + tasty global CSS + raw CSS\n | 'global' // only tasty global CSS\n | 'active' // tasty CSS for classes currently in DOM\n | 'unused' // tasty CSS with refCount = 0 (still in cache but not actively used)\n | 'page' // ALL CSS on the page across stylesheets (not only tasty)\n | string // 't123' tasty class or a CSS selector\n | string[] // array of tasty classes like ['t1', 't2']\n | Element; // a DOM element\n\ninterface CssOptions {\n root?: Document | ShadowRoot;\n prettify?: boolean; // default: true\n log?: boolean; // default: false\n}\n\ninterface ChunkInfo {\n className: string;\n chunkName: string | null;\n}\n\ninterface InspectResult {\n element?: Element | null;\n classes: string[]; // tasty classes found on the element\n chunks: ChunkInfo[]; // chunk information per class (with chunking enabled)\n css: string; // full, prettified CSS affecting the element\n size: number; // characters in css\n rules: number; // number of rule blocks\n}\n\ninterface CacheMetrics {\n hits: number;\n misses: number;\n bulkCleanups: number;\n totalInsertions: number;\n totalUnused: number;\n stylesCleanedUp: number;\n cleanupHistory: {\n timestamp: number;\n classesDeleted: number;\n cssSize: number;\n rulesDeleted: number;\n }[];\n startTime: number;\n\n // Calculated metrics\n unusedHits?: number; // calculated as current unused count\n}\n\ninterface CacheStatus {\n classes: {\n active: string[]; // classes with refCount > 0 and present in DOM\n unused: string[]; // classes with refCount = 0 but still in cache\n all: string[]; // union of both\n };\n metrics: CacheMetrics | null;\n}\n\ninterface Definitions {\n properties: string[]; // defined via @property\n keyframes: { name: string; refCount: number }[];\n}\n\ninterface SummaryOptions {\n root?: Document | ShadowRoot;\n log?: boolean;\n includePageCSS?:\n | false // do not include page-level CSS stats (default)\n | true // include sizes/counts only\n | 'all'; // include stats and return full page CSS string\n}\n\ninterface Summary {\n // Classes\n activeClasses: string[];\n unusedClasses: string[];\n totalStyledClasses: string[];\n\n // Tasty CSS sizes\n activeCSSSize: number;\n unusedCSSSize: number;\n globalCSSSize: number; // injectGlobal() CSS\n rawCSSSize: number; // injectRawCSS() / useRawCSS() CSS\n keyframesCSSSize: number; // @keyframes CSS\n propertyCSSSize: number; // @property CSS\n totalCSSSize: number; // all tasty CSS (active + unused + global + raw + keyframes + property)\n\n // Tasty CSS payloads\n activeCSS: string;\n unusedCSS: string;\n globalCSS: string; // injectGlobal() CSS\n rawCSS: string; // injectRawCSS() / useRawCSS() CSS\n keyframesCSS: string; // @keyframes CSS\n propertyCSS: string; // @property CSS\n allCSS: string; // all tasty CSS combined\n\n // Rule counts\n globalRuleCount: number;\n rawRuleCount: number;\n keyframesRuleCount: number;\n propertyRuleCount: number;\n\n // Page-level CSS (across all stylesheets, not only tasty) — shown when includePageCSS != false\n page?: {\n css?: string; // present only when includePageCSS === 'all'\n cssSize: number; // total characters\n ruleCount: number; // approximate rule count\n stylesheetCount: number; // stylesheets scanned (CORS-safe)\n skippedStylesheets: number; // stylesheets skipped due to cross-origin/CORS\n };\n\n // Metrics & definitions\n metrics: CacheMetrics | null;\n definedProperties: string[];\n definedKeyframes: { name: string; refCount: number }[];\n propertyCount: number;\n keyframeCount: number;\n\n // Cleanup summary\n cleanupSummary: {\n enabled: boolean;\n totalCleanups: number;\n totalClassesDeleted: number;\n totalCssDeleted: number;\n totalRulesDeleted: number;\n averageClassesPerCleanup: number;\n averageCssPerCleanup: number;\n averageRulesPerCleanup: number;\n lastCleanup?: {\n timestamp: number;\n date: string;\n classesDeleted: number;\n cssSize: number;\n rulesDeleted: number;\n };\n };\n\n // Chunk breakdown (style chunking optimization)\n chunkBreakdown: {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n };\n}\n\n/**\n * Pretty-print CSS with proper indentation and formatting\n */\nfunction prettifyCSS(css: string): string {\n if (!css || css.trim() === '') {\n return '';\n }\n\n // First, normalize whitespace but preserve structure\n let formatted = css.replace(/\\s+/g, ' ').trim();\n\n // Add newlines after opening braces\n formatted = formatted.replace(/\\s*\\{\\s*/g, ' {\\n');\n\n // Add newlines after semicolons (but not inside strings or functions)\n formatted = formatted.replace(/;(?![^\"']*[\"'][^\"']*$)(?![^()]*\\))/g, ';\\n');\n\n // Add newlines before closing braces\n formatted = formatted.replace(/\\s*\\}\\s*/g, '\\n}\\n');\n\n // Handle comma-separated selectors (only outside of property values)\n // This regex looks for commas that are:\n // 1. Not inside quotes\n // 2. Not inside parentheses (CSS functions)\n // 3. Not followed by a colon (not in a property value)\n formatted = formatted.replace(\n /,(?![^\"']*[\"'][^\"']*$)(?![^()]*\\))(?=.*:.*\\{|.*\\{)/g,\n ',\\n',\n );\n\n // Process line by line for proper indentation\n const lines = formatted.split('\\n');\n let indentLevel = 0;\n const indentSize = 2;\n\n const formattedLines = lines.map((line) => {\n const trimmed = line.trim();\n if (!trimmed) return '';\n\n // Handle closing braces - decrease indent first\n if (trimmed === '}') {\n indentLevel = Math.max(0, indentLevel - 1);\n return ' '.repeat(indentLevel * indentSize) + trimmed;\n }\n\n // Current line with proper indentation\n const indent = ' '.repeat(indentLevel * indentSize);\n const result = indent + trimmed;\n\n // Handle opening braces - increase indent for next line\n if (trimmed.endsWith('{')) {\n indentLevel++;\n }\n\n return result;\n });\n\n // Clean up the result and ensure proper spacing\n let result = formattedLines\n .filter((line) => line.trim()) // Remove empty lines\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n') // Max 2 consecutive newlines\n .trim();\n\n // Final cleanup: ensure single spaces in function calls\n result = result.replace(/,\\s+/g, ', ');\n\n return result;\n}\n\n// Helper functions\nfunction findAllTastyClasses(root: Document | ShadowRoot = document): string[] {\n const classes = new Set<string>();\n const elements = (root as Document).querySelectorAll?.('[class]') || [];\n\n elements.forEach((element) => {\n const classList = element.getAttribute('class');\n if (classList) {\n const tastyClasses = classList\n .split(/\\s+/)\n .filter((cls) => /^t\\d+$/.test(cls));\n tastyClasses.forEach((cls) => classes.add(cls));\n }\n });\n\n return Array.from(classes).sort((a, b) => {\n const aNum = parseInt(a.slice(1));\n const bNum = parseInt(b.slice(1));\n return aNum - bNum;\n });\n}\n\nfunction findAllStyledClasses(\n root: Document | ShadowRoot = document,\n): string[] {\n // Extract tasty classes from all CSS text by parsing selectors\n const allCSS = injector.instance.getCssText({ root });\n const classes = new Set<string>();\n\n // Simple regex to find .t{number} class selectors\n const classRegex = /\\.t(\\d+)/g;\n let match;\n while ((match = classRegex.exec(allCSS)) !== null) {\n classes.add(`t${match[1]}`);\n }\n\n return Array.from(classes).sort((a, b) => {\n const aNum = parseInt(a.slice(1));\n const bNum = parseInt(b.slice(1));\n return aNum - bNum;\n });\n}\n\nfunction extractCSSRules(\n css: string,\n): { selector: string; declarations: string }[] {\n const rules: { selector: string; declarations: string }[] = [];\n\n // Remove comments\n const cleanCSS = css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n\n let i = 0;\n while (i < cleanCSS.length) {\n // Skip whitespace\n while (i < cleanCSS.length && /\\s/.test(cleanCSS[i])) {\n i++;\n }\n if (i >= cleanCSS.length) break;\n\n // Find selector start\n const selectorStart = i;\n let braceDepth = 0;\n let inString = false;\n let stringChar = '';\n\n // Find opening brace\n while (i < cleanCSS.length) {\n const char = cleanCSS[i];\n if (inString) {\n if (char === stringChar && cleanCSS[i - 1] !== '\\\\') {\n inString = false;\n }\n } else {\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n } else if (char === '{') {\n braceDepth++;\n if (braceDepth === 1) break;\n }\n }\n i++;\n }\n\n if (i >= cleanCSS.length) break;\n const selector = cleanCSS.substring(selectorStart, i).trim();\n i++; // Skip opening brace\n\n // Find matching closing brace\n const contentStart = i;\n braceDepth = 1;\n inString = false;\n\n while (i < cleanCSS.length && braceDepth > 0) {\n const char = cleanCSS[i];\n if (inString) {\n if (char === stringChar && cleanCSS[i - 1] !== '\\\\') {\n inString = false;\n }\n } else {\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n } else if (char === '{') {\n braceDepth++;\n } else if (char === '}') {\n braceDepth--;\n }\n }\n i++;\n }\n\n const content = cleanCSS.substring(contentStart, i - 1).trim();\n if (content && selector) {\n rules.push({ selector, declarations: content });\n }\n }\n\n return rules;\n}\n\nfunction getGlobalCSS(root: Document | ShadowRoot = document): string {\n const allCSS = injector.instance.getCssText({ root });\n const rules = extractCSSRules(allCSS);\n\n const globalRules = rules.filter((rule) => {\n const selectors = rule.selector.split(',').map((s) => s.trim());\n return !selectors.every((selector) => {\n const cleanSelector = selector.replace(/[.#:\\s>+~[\\]()]/g, ' ');\n const parts = cleanSelector.split(/\\s+/).filter(Boolean);\n return parts.length > 0 && parts.every((part) => /^t\\d+$/.test(part));\n });\n });\n\n const globalCSS = globalRules\n .map((rule) => `${rule.selector} { ${rule.declarations} }`)\n .join('\\n');\n return prettifyCSS(globalCSS);\n}\n\nfunction getPageCSS(options?: {\n root?: Document | ShadowRoot;\n includeCrossOrigin?: boolean;\n}): string {\n const root = options?.root || document;\n const includeCrossOrigin = options?.includeCrossOrigin ?? false;\n\n const cssChunks: string[] = [];\n\n try {\n if ('styleSheets' in root) {\n const styleSheets = Array.from((root as Document).styleSheets);\n\n for (const sheet of styleSheets) {\n try {\n if (sheet.cssRules) {\n const rules = Array.from(sheet.cssRules);\n cssChunks.push(rules.map((rule) => rule.cssText).join('\\n'));\n }\n } catch {\n // Cross-origin sheet or other access error\n if (includeCrossOrigin) {\n cssChunks.push(\n `/* Cross-origin stylesheet: ${sheet.href || 'inline'} */`,\n );\n }\n }\n }\n }\n } catch {\n // Fallback error handling\n }\n\n return cssChunks.join('\\n');\n}\n\nfunction getPageStats(options?: {\n root?: Document | ShadowRoot;\n includeCrossOrigin?: boolean;\n}): {\n cssSize: number;\n ruleCount: number;\n stylesheetCount: number;\n skippedStylesheets: number;\n} {\n const root = options?.root || document;\n \n const _includeCrossOrigin = options?.includeCrossOrigin ?? false;\n\n let cssSize = 0;\n let ruleCount = 0;\n let stylesheetCount = 0;\n let skippedStylesheets = 0;\n\n try {\n if ('styleSheets' in root) {\n const styleSheets = Array.from((root as Document).styleSheets);\n stylesheetCount = styleSheets.length;\n\n for (const sheet of styleSheets) {\n try {\n if (sheet.cssRules) {\n const rules = Array.from(sheet.cssRules);\n ruleCount += rules.length;\n cssSize += rules.reduce(\n (sum, rule) => sum + rule.cssText.length,\n 0,\n );\n }\n } catch {\n skippedStylesheets++;\n }\n }\n }\n } catch {\n // Fallback error handling\n }\n\n return { cssSize, ruleCount, stylesheetCount, skippedStylesheets };\n}\n\n// ============================================================================\n// Chunk-aware helpers (for style chunking optimization)\n// ============================================================================\n\n/**\n * Extract chunk name from a cache key.\n *\n * Cache keys have the format: \"chunkName\\0key:value\\0key:value...\"\n * or \"[states:...]\\0chunkName\\0...\" for predefined states.\n *\n * @param cacheKey - The cache key to parse\n * @returns The chunk name, or null if not found\n */\nfunction extractChunkNameFromCacheKey(cacheKey: string): string | null {\n // Cache keys are separated by \\0 (null character)\n const parts = cacheKey.split('\\0');\n\n for (const part of parts) {\n // Skip predefined states prefix\n if (part.startsWith('[states:')) continue;\n // First non-states part that doesn't contain : is the chunk name\n if (!part.includes(':') && part.length > 0) {\n return part;\n }\n }\n return null;\n}\n\n/**\n * Get chunk info for a className by reverse-looking up its cache key.\n *\n * @param className - The tasty class name (e.g., \"t0\", \"t123\")\n * @param root - The document or shadow root to search in\n * @returns Object with chunk name and cache key, or nulls if not found\n */\nfunction getChunkForClassName(\n className: string,\n root: Document | ShadowRoot = document,\n): { chunkName: string | null; cacheKey: string | null } {\n const registry = injector.instance._sheetManager?.getRegistry(\n root,\n );\n if (!registry) {\n return { chunkName: null, cacheKey: null };\n }\n\n // Reverse lookup: find the cache key for this className\n for (const [cacheKey, cn] of registry.cacheKeyToClassName) {\n if (cn === className) {\n return {\n chunkName: extractChunkNameFromCacheKey(cacheKey),\n cacheKey,\n };\n }\n }\n return { chunkName: null, cacheKey: null };\n}\n\n/**\n * Get chunk breakdown statistics for all styles.\n *\n * @param root - The document or shadow root to search in\n * @returns Object with breakdown by chunk type and totals\n */\nfunction getChunkBreakdown(root: Document | ShadowRoot = document): {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n} {\n const registry = injector.instance._sheetManager?.getRegistry(\n root,\n );\n\n if (!registry) {\n return {\n byChunk: {},\n totalChunkTypes: 0,\n };\n }\n\n const byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n > = {};\n\n // Group classes by chunk\n for (const [cacheKey, className] of registry.cacheKeyToClassName) {\n const chunkName = extractChunkNameFromCacheKey(cacheKey) || 'unknown';\n\n if (!byChunk[chunkName]) {\n byChunk[chunkName] = { classes: [], cssSize: 0, ruleCount: 0 };\n }\n\n byChunk[chunkName].classes.push(className);\n\n // Get CSS for this class\n const css = injector.instance.getCssTextForClasses([className], { root });\n byChunk[chunkName].cssSize += css.length;\n byChunk[chunkName].ruleCount += (css.match(/\\{[^}]*\\}/g) || []).length;\n }\n\n // Sort classes within each chunk for consistency\n for (const entry of Object.values(byChunk)) {\n entry.classes.sort((a, b) => {\n const aNum = parseInt(a.slice(1));\n const bNum = parseInt(b.slice(1));\n return aNum - bNum;\n });\n }\n\n return {\n byChunk,\n totalChunkTypes: Object.keys(byChunk).length,\n };\n}\n\n/**\n * Concise tastyDebug API for inspecting styles at runtime\n */\nexport const tastyDebug = {\n // 1) One function to get CSS from anywhere\n css(target: CSSTarget, opts?: CssOptions): string {\n const { root = document, prettify = true, log = false } = opts || {};\n let css = '';\n\n if (typeof target === 'string') {\n if (target === 'all') {\n css = injector.instance.getCssText({ root });\n } else if (target === 'global') {\n css = getGlobalCSS(root);\n } else if (target === 'active') {\n const activeClasses = findAllTastyClasses(root);\n css = injector.instance.getCssTextForClasses(activeClasses, { root });\n } else if (target === 'unused') {\n // Get unused classes (refCount = 0) from the registry\n const registry = injector.instance._sheetManager?.getRegistry(root);\n const unusedClasses: string[] = registry\n ? Array.from(\n registry.refCounts.entries() as IterableIterator<\n [string, number]\n >,\n )\n .filter(([, refCount]: [string, number]) => refCount === 0)\n .map(([className]: [string, number]) => className)\n : [];\n css = injector.instance.getCssTextForClasses(unusedClasses, { root });\n } else if (target === 'page') {\n css = getPageCSS({ root, includeCrossOrigin: true });\n } else if (/^t\\d+$/.test(target)) {\n // Single tasty class\n css = injector.instance.getCssTextForClasses([target], { root });\n } else {\n // CSS selector - find element and get its CSS\n const element = (root as Document).querySelector?.(target);\n if (element) {\n css = getCssTextForNode(element, { root });\n }\n }\n } else if (Array.isArray(target)) {\n // Array of tasty classes\n css = injector.instance.getCssTextForClasses(target, { root });\n } else if (target instanceof Element) {\n // DOM element\n css = getCssTextForNode(target, { root });\n }\n\n const result = prettify ? prettifyCSS(css) : css;\n\n if (log) {\n console.group(\n `🎨 CSS for ${Array.isArray(target) ? `[${target.join(', ')}]` : target}`,\n );\n console.log(result || '(empty)');\n console.groupEnd();\n }\n\n return result;\n },\n\n // 2) Element-level inspection\n inspect(\n target: string | Element,\n opts?: { root?: Document | ShadowRoot },\n ): InspectResult {\n const { root = document } = opts || {};\n const element =\n typeof target === 'string'\n ? (root as Document).querySelector?.(target)\n : target;\n\n if (!element) {\n return {\n element: null,\n classes: [],\n chunks: [],\n css: '',\n size: 0,\n rules: 0,\n };\n }\n\n const classList = element.getAttribute('class') || '';\n const tastyClasses = classList\n .split(/\\s+/)\n .filter((cls) => /^t\\d+$/.test(cls));\n\n // Get chunk info for each tasty class\n const chunks: ChunkInfo[] = tastyClasses.map((className) => ({\n className,\n chunkName: getChunkForClassName(className, root).chunkName,\n }));\n\n const css = getCssTextForNode(element, { root });\n const prettifiedCSS = prettifyCSS(css);\n const ruleCount = (css.match(/\\{[^}]*\\}/g) || []).length;\n\n return {\n element,\n classes: tastyClasses,\n chunks,\n css: prettifiedCSS,\n size: css.length,\n rules: ruleCount,\n };\n },\n\n // 3) Cache + metrics at a glance\n cache(opts?: {\n root?: Document | ShadowRoot;\n includeHistory?: boolean;\n }): CacheStatus {\n const { root = document } = opts || {};\n const activeClasses = findAllTastyClasses(root);\n \n const _allClasses = findAllStyledClasses(root);\n // Get unused classes (refCount = 0) from the registry\n const registry = injector.instance._sheetManager?.getRegistry(\n root,\n );\n const unusedClasses: string[] = registry\n ? Array.from(\n registry.refCounts.entries() as IterableIterator<[string, number]>,\n )\n .filter(([, refCount]: [string, number]) => refCount === 0)\n .map(([className]: [string, number]) => className)\n : [];\n\n return {\n classes: {\n active: activeClasses,\n unused: unusedClasses,\n all: [...activeClasses, ...unusedClasses],\n },\n metrics: injector.instance.getMetrics({ root }),\n };\n },\n\n // 4) Cleanup + metrics utilities\n cleanup(opts?: { root?: Document | ShadowRoot }): void {\n const { root } = opts || {};\n injector.instance.cleanup(root);\n },\n\n metrics(opts?: { root?: Document | ShadowRoot }): CacheMetrics | null {\n const { root } = opts || {};\n return injector.instance.getMetrics({ root });\n },\n\n resetMetrics(opts?: { root?: Document | ShadowRoot }): void {\n const { root } = opts || {};\n injector.instance.resetMetrics({ root });\n },\n\n // 5) Chunk breakdown (style chunking optimization)\n /**\n * Get breakdown of styles by chunk type.\n *\n * With style chunking enabled, styles are split into logical chunks\n * (appearance, font, dimension, container, etc.) for better caching\n * and CSS reuse.\n *\n * @param opts - Options including root document/shadow root\n * @returns Breakdown by chunk type with class counts and CSS sizes\n */\n chunks(opts?: { root?: Document | ShadowRoot; log?: boolean }): {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n totalClasses: number;\n } {\n const { root = document, log = false } = opts || {};\n const breakdown = getChunkBreakdown(root);\n\n const totalClasses = Object.values(breakdown.byChunk).reduce(\n (sum, entry) => sum + entry.classes.length,\n 0,\n );\n\n if (log) {\n console.group('🧩 Style Chunk Breakdown');\n\n // Define display order matching CHUNK_NAMES\n const displayOrder = [\n CHUNK_NAMES.COMBINED, // non-chunked styles (e.g., @starting-style)\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n ];\n\n // Show chunks in order\n for (const chunkName of displayOrder) {\n const data = breakdown.byChunk[chunkName];\n if (data) {\n const sizeStr =\n data.cssSize > 1024\n ? `${(data.cssSize / 1024).toFixed(1)}KB`\n : `${data.cssSize}B`;\n console.log(\n ` • ${chunkName}: ${data.classes.length} classes, ${sizeStr}, ${data.ruleCount} rules`,\n );\n }\n }\n\n // Show any unknown chunks\n for (const [chunkName, data] of Object.entries(breakdown.byChunk)) {\n if (\n !displayOrder.includes(chunkName as (typeof displayOrder)[number])\n ) {\n const sizeStr =\n data.cssSize > 1024\n ? `${(data.cssSize / 1024).toFixed(1)}KB`\n : `${data.cssSize}B`;\n console.log(\n ` • ${chunkName}: ${data.classes.length} classes, ${sizeStr}, ${data.ruleCount} rules`,\n );\n }\n }\n\n console.log(\n `📊 Total: ${totalClasses} classes across ${breakdown.totalChunkTypes} chunk types`,\n );\n console.groupEnd();\n }\n\n return {\n ...breakdown,\n totalClasses,\n };\n },\n\n // 6) Get CSS for specific global types\n getGlobalTypeCSS(\n type: 'global' | 'raw' | 'keyframes' | 'property',\n opts?: { root?: Document | ShadowRoot },\n ): { css: string; ruleCount: number; size: number } {\n const { root = document } = opts || {};\n const registry = injector.instance._sheetManager?.getRegistry(\n root,\n );\n\n if (!registry) {\n return { css: '', ruleCount: 0, size: 0 };\n }\n\n const cssChunks: string[] = [];\n let ruleCount = 0;\n\n if (type === 'keyframes') {\n // Handle keyframes separately - they're stored in keyframesCache\n for (const [, entry] of registry.keyframesCache) {\n const info = entry.info;\n const sheet = registry.sheets[info.sheetIndex];\n const styleSheet = sheet?.sheet?.sheet;\n\n if (styleSheet && info.ruleIndex < styleSheet.cssRules.length) {\n const rule = styleSheet.cssRules[info.ruleIndex] as\n | CSSRule\n | undefined;\n if (rule) {\n cssChunks.push(rule.cssText);\n ruleCount++;\n }\n } else if (info.cssText) {\n cssChunks.push(info.cssText);\n ruleCount++;\n }\n }\n } else {\n // Handle other global types stored in globalRules\n const prefix =\n type === 'global' ? 'global:' : type === 'raw' ? 'raw:' : 'property:';\n\n for (const [key, ruleInfo] of registry.globalRules) {\n if (key.startsWith(prefix)) {\n const sheet = registry.sheets[ruleInfo.sheetIndex];\n const styleSheet = sheet?.sheet?.sheet;\n if (styleSheet) {\n const start = Math.max(0, ruleInfo.ruleIndex);\n const end = Math.min(\n styleSheet.cssRules.length - 1,\n (ruleInfo.endRuleIndex as number) ?? ruleInfo.ruleIndex,\n );\n\n if (\n start >= 0 &&\n end >= start &&\n start < styleSheet.cssRules.length\n ) {\n for (let i = start; i <= end; i++) {\n const rule = styleSheet.cssRules[i] as CSSRule | undefined;\n if (rule) {\n cssChunks.push(rule.cssText);\n ruleCount++;\n }\n }\n }\n } else if (ruleInfo.cssText && ruleInfo.cssText.length) {\n // Fallback in environments without CSSOM access\n cssChunks.push(...ruleInfo.cssText);\n ruleCount += ruleInfo.cssText.length;\n }\n }\n }\n }\n\n const rawCSS = cssChunks.join('\\n');\n\n return {\n css: prettifyCSS(rawCSS),\n ruleCount,\n size: rawCSS.length, // Use raw CSS size for consistent calculations\n };\n },\n\n // 6) Defined @property and keyframes\n defs(opts?: { root?: Document | ShadowRoot }): Definitions {\n const { root = document } = opts || {};\n\n // Get properties from injector if available, otherwise scan CSS\n let properties: string[] = [];\n try {\n const registry = injector.instance._sheetManager?.getRegistry(\n root,\n );\n if (registry?.injectedProperties) {\n properties = Array.from(\n (registry.injectedProperties as Map<string, string>).keys(),\n ).sort();\n }\n } catch {\n // Fallback: scan CSS for @property rules\n const allCSS = injector.instance.getCssText({ root });\n const propRegex = /@property\\s+(--[a-z0-9-]+)/gi;\n const propSet = new Set<string>();\n let match;\n while ((match = propRegex.exec(allCSS)) !== null) {\n propSet.add(match[1]);\n }\n properties = Array.from(propSet).sort();\n }\n\n // Get keyframes\n let keyframes: { name: string; refCount: number }[] = [];\n try {\n const registry = injector.instance._sheetManager?.getRegistry(\n root,\n );\n if (registry) {\n for (const entry of registry.keyframesCache.values()) {\n keyframes.push({\n name: entry.name,\n refCount: entry.refCount,\n });\n }\n keyframes.sort((a, b) => a.name.localeCompare(b.name));\n }\n } catch {\n // Fallback: scan CSS for @keyframes rules\n const allCSS = injector.instance.getCssText({ root });\n const keyframesRegex = /@keyframes\\s+([a-zA-Z0-9_-]+)/gi;\n const keyframesSet = new Set<string>();\n let match;\n while ((match = keyframesRegex.exec(allCSS)) !== null) {\n keyframesSet.add(match[1]);\n }\n keyframes = Array.from(keyframesSet)\n .sort()\n .map((name) => ({ name, refCount: 1 }));\n }\n\n return { properties, keyframes };\n },\n\n // 7) One-shot overview\n summary(opts?: SummaryOptions): Summary {\n const { root = document, log = false, includePageCSS = false } = opts || {};\n const cacheStatus = this.cache({ root });\n const definitions = this.defs({ root });\n const metrics = this.metrics({ root });\n\n const activeCSS = this.css('active', { root, prettify: false });\n const unusedCSS = this.css('unused', { root, prettify: false });\n const allCSS = this.css('all', { root, prettify: false });\n\n // Calculate global CSS by subtracting class-based CSS from total\n const classCSSSize = activeCSS.length + unusedCSS.length;\n const totalGlobalCSSSize = allCSS.length - classCSSSize;\n\n // Get CSS for each global type separately for display purposes\n const globalData = this.getGlobalTypeCSS('global', { root });\n const rawData = this.getGlobalTypeCSS('raw', { root });\n const keyframesData = this.getGlobalTypeCSS('keyframes', { root });\n const propertyData = this.getGlobalTypeCSS('property', { root });\n\n // Use the calculated sizes to avoid double-counting\n const globalTypesTotalSize =\n globalData.size + rawData.size + keyframesData.size + propertyData.size;\n\n // Build cleanup summary from metrics\n const cleanupSummary = {\n enabled: !!metrics,\n totalCleanups: metrics?.cleanupHistory?.length || 0,\n totalClassesDeleted:\n metrics?.cleanupHistory?.reduce(\n (sum, c) => sum + c.classesDeleted,\n 0,\n ) || 0,\n totalCssDeleted:\n metrics?.cleanupHistory?.reduce((sum, c) => sum + c.cssSize, 0) || 0,\n totalRulesDeleted:\n metrics?.cleanupHistory?.reduce((sum, c) => sum + c.rulesDeleted, 0) ||\n 0,\n averageClassesPerCleanup: 0,\n averageCssPerCleanup: 0,\n averageRulesPerCleanup: 0,\n lastCleanup: undefined as\n | (CleanupStats & { date: string })\n | undefined,\n };\n\n if (cleanupSummary.totalCleanups > 0) {\n cleanupSummary.averageClassesPerCleanup =\n cleanupSummary.totalClassesDeleted / cleanupSummary.totalCleanups;\n cleanupSummary.averageCssPerCleanup =\n cleanupSummary.totalCssDeleted / cleanupSummary.totalCleanups;\n cleanupSummary.averageRulesPerCleanup =\n cleanupSummary.totalRulesDeleted / cleanupSummary.totalCleanups;\n\n const lastCleanup =\n metrics?.cleanupHistory?.[metrics.cleanupHistory.length - 1];\n if (lastCleanup) {\n cleanupSummary.lastCleanup = {\n ...lastCleanup,\n date: new Date(lastCleanup.timestamp).toISOString(),\n };\n }\n }\n\n let page: Summary['page'] | undefined;\n if (includePageCSS) {\n const pageStats = getPageStats({ root, includeCrossOrigin: true });\n page = {\n ...pageStats,\n css:\n includePageCSS === 'all'\n ? getPageCSS({ root, includeCrossOrigin: true })\n : undefined,\n };\n }\n\n // If individual extraction matches total, use individual sizes\n // Otherwise, proportionally scale the individual sizes to match the total\n const useIndividualSizes =\n Math.abs(globalTypesTotalSize - totalGlobalCSSSize) < 100;\n\n let adjustedGlobalSizes;\n if (useIndividualSizes) {\n adjustedGlobalSizes = {\n globalCSSSize: globalData.size,\n rawCSSSize: rawData.size,\n keyframesCSSSize: keyframesData.size,\n propertyCSSSize: propertyData.size,\n };\n } else {\n // Scale proportionally to match the actual total\n const scaleFactor = totalGlobalCSSSize / globalTypesTotalSize;\n adjustedGlobalSizes = {\n globalCSSSize: Math.round(globalData.size * scaleFactor),\n rawCSSSize: Math.round(rawData.size * scaleFactor),\n keyframesCSSSize: Math.round(keyframesData.size * scaleFactor),\n propertyCSSSize: Math.round(propertyData.size * scaleFactor),\n };\n }\n\n // Get chunk breakdown\n const chunkBreakdown = getChunkBreakdown(root);\n\n const summary: Summary = {\n activeClasses: cacheStatus.classes.active,\n unusedClasses: cacheStatus.classes.unused,\n totalStyledClasses: cacheStatus.classes.all,\n activeCSSSize: activeCSS.length,\n unusedCSSSize: unusedCSS.length,\n ...adjustedGlobalSizes,\n totalCSSSize: allCSS.length,\n activeCSS: prettifyCSS(activeCSS),\n unusedCSS: prettifyCSS(unusedCSS),\n globalCSS: globalData.css,\n rawCSS: rawData.css,\n keyframesCSS: keyframesData.css,\n propertyCSS: propertyData.css,\n allCSS: prettifyCSS(allCSS),\n globalRuleCount: globalData.ruleCount,\n rawRuleCount: rawData.ruleCount,\n keyframesRuleCount: keyframesData.ruleCount,\n propertyRuleCount: propertyData.ruleCount,\n page,\n metrics,\n definedProperties: definitions.properties,\n definedKeyframes: definitions.keyframes,\n propertyCount: definitions.properties.length,\n keyframeCount: definitions.keyframes.length,\n cleanupSummary,\n chunkBreakdown,\n };\n\n if (log) {\n console.group('🎨 Comprehensive Tasty Debug Summary');\n console.log(`📊 Style Cache Status:`);\n console.log(\n ` • Active classes (in DOM): ${summary.activeClasses.length}`,\n );\n console.log(\n ` • Unused classes (refCount = 0): ${summary.unusedClasses.length}`,\n );\n console.log(\n ` • Total styled classes: ${summary.totalStyledClasses.length}`,\n );\n console.log(`💾 CSS Size:`);\n console.log(` • Active CSS: ${summary.activeCSSSize} characters`);\n console.log(` • Unused CSS: ${summary.unusedCSSSize} characters`);\n console.log(\n ` • Global CSS (injectGlobal): ${summary.globalCSSSize} characters (${summary.globalRuleCount} rules)`,\n );\n console.log(\n ` • Raw CSS (injectRawCSS/useRawCSS): ${summary.rawCSSSize} characters (${summary.rawRuleCount} rules)`,\n );\n console.log(\n ` • Keyframes CSS: ${summary.keyframesCSSSize} characters (${summary.keyframesRuleCount} rules)`,\n );\n console.log(\n ` • Property CSS: ${summary.propertyCSSSize} characters (${summary.propertyRuleCount} rules)`,\n );\n\n // Show breakdown calculation\n const calculatedTotal =\n summary.activeCSSSize +\n summary.unusedCSSSize +\n summary.globalCSSSize +\n summary.rawCSSSize +\n summary.keyframesCSSSize +\n summary.propertyCSSSize;\n console.log(` • Calculated Total: ${calculatedTotal} characters`);\n console.log(` • Actual Total: ${summary.totalCSSSize} characters`);\n\n const difference = Math.abs(calculatedTotal - summary.totalCSSSize);\n if (difference > 100) {\n console.warn(\n ` ⚠️ Size mismatch: ${difference} characters difference`,\n );\n\n // Debug: show what might be missing\n console.group('🔍 Debugging size mismatch:');\n console.log(\n `Active + Unused = ${summary.activeCSSSize + summary.unusedCSSSize}`,\n );\n console.log(\n `All Global Types = ${summary.globalCSSSize + summary.rawCSSSize + summary.keyframesCSSSize + summary.propertyCSSSize}`,\n );\n console.log(\n `Class-based vs Total difference = ${summary.totalCSSSize - (summary.activeCSSSize + summary.unusedCSSSize)}`,\n );\n\n // Show scaling information\n console.log(`Raw global extraction total: ${globalTypesTotalSize}`);\n console.log(`Calculated global size: ${totalGlobalCSSSize}`);\n console.log(`Used individual sizes: ${useIndividualSizes}`);\n if (!useIndividualSizes) {\n console.log(\n `Scale factor applied: ${(totalGlobalCSSSize / globalTypesTotalSize).toFixed(3)}`,\n );\n }\n console.groupEnd();\n }\n\n if (page) {\n console.log(`📄 Page CSS:`);\n console.log(` • Total page CSS: ${page.cssSize} characters`);\n console.log(` • Total page rules: ${page.ruleCount}`);\n console.log(\n ` • Stylesheets: ${page.stylesheetCount} (${page.skippedStylesheets} skipped)`,\n );\n }\n\n console.log('🏷️ Properties & Keyframes:');\n console.log(` • Defined @property: ${summary.propertyCount}`);\n console.log(` • Defined @keyframes: ${summary.keyframeCount}`);\n\n if (metrics) {\n console.log(`⚡ Performance Metrics:`);\n console.log(` • Cache hits: ${metrics.hits}`);\n console.log(` • Cache misses: ${metrics.misses}`);\n console.log(` • Cached style reuses: ${metrics.unusedHits}`);\n const hitRate =\n metrics.hits + metrics.misses > 0\n ? (\n ((metrics.hits + (metrics.unusedHits || 0)) /\n (metrics.hits + metrics.misses)) *\n 100\n ).toFixed(1)\n : '0';\n console.log(` • Overall cache hit rate: ${hitRate}%`);\n }\n\n // Show chunk breakdown\n if (summary.chunkBreakdown.totalChunkTypes > 0) {\n console.log('🧩 Style Chunk Breakdown:');\n const displayOrder = [\n CHUNK_NAMES.COMBINED, // non-chunked styles (e.g., @starting-style)\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n ];\n for (const chunkName of displayOrder) {\n const data = summary.chunkBreakdown.byChunk[chunkName];\n if (data) {\n const sizeStr =\n data.cssSize > 1024\n ? `${(data.cssSize / 1024).toFixed(1)}KB`\n : `${data.cssSize}B`;\n console.log(\n ` • ${chunkName}: ${data.classes.length} classes, ${sizeStr}, ${data.ruleCount} rules`,\n );\n }\n }\n }\n\n console.log('🔍 Details:');\n console.log(' • Active classes:', summary.activeClasses);\n console.log(' • Unused classes:', summary.unusedClasses);\n console.groupEnd();\n }\n\n return summary;\n },\n\n // 8) Page-level CSS helpers\n pageCSS(opts?: {\n root?: Document | ShadowRoot;\n prettify?: boolean;\n log?: boolean;\n includeCrossOrigin?: boolean;\n }): string {\n const {\n root = document,\n prettify = true,\n log = false,\n includeCrossOrigin = true,\n } = opts || {};\n const css = getPageCSS({ root, includeCrossOrigin });\n const result = prettify ? prettifyCSS(css) : css;\n\n if (log) {\n console.group('📄 Page CSS (All Stylesheets)');\n console.log(result || '(empty)');\n console.groupEnd();\n }\n\n return result;\n },\n\n pageStats(opts?: {\n root?: Document | ShadowRoot;\n includeCrossOrigin?: boolean;\n }): {\n cssSize: number;\n ruleCount: number;\n stylesheetCount: number;\n skippedStylesheets: number;\n } {\n const { root = document, includeCrossOrigin = true } = opts || {};\n return getPageStats({ root, includeCrossOrigin });\n },\n\n // 9) Install globally\n install(): void {\n if (\n typeof window !== 'undefined' &&\n window.tastyDebug !== tastyDebug\n ) {\n window.tastyDebug = tastyDebug;\n console.log(\n '🎨 tastyDebug installed on window. Run tastyDebug.help() for quick start guide.',\n );\n }\n },\n\n // 10) Beautiful console logging with collapsible CSS\n log(target: CSSTarget, opts?: CssOptions & { title?: string }): void {\n const { title, ...cssOpts } = opts || {};\n const css = tastyDebug.css(target, cssOpts);\n\n if (!css.trim()) {\n console.warn(`🎨 No CSS found for target: ${String(target)}`);\n return;\n }\n\n const targetStr = Array.isArray(target)\n ? target.join(', ')\n : String(target);\n const displayTitle = title || `CSS for \"${targetStr}\"`;\n\n // Get some stats about the CSS\n const lines = css.split('\\n').length;\n const size = new Blob([css]).size;\n const sizeStr = size > 1024 ? `${(size / 1024).toFixed(1)}KB` : `${size}B`;\n\n // Count CSS rules (blocks with opening braces)\n const ruleCount = (css.match(/\\{/g) || []).length;\n\n console.group(\n `🎨 ${displayTitle} (${ruleCount} rules, ${lines} lines, ${sizeStr})`,\n );\n\n // Detect sub-elements in CSS\n const subElementMatches = css.match(/\\[data-element=\"([^\"]+)\"\\]/g) || [];\n const subElements = [\n ...new Set(\n subElementMatches\n .map((match) => match.match(/\\[data-element=\"([^\"]+)\"\\]/)?.[1])\n .filter(Boolean),\n ),\n ];\n\n if (subElements.length > 0) {\n console.log(`🧩 Sub-elements found: ${subElements.join(', ')}`);\n\n // Show stats and CSS for each sub-element\n subElements.forEach((element) => {\n \n const _elementSelector = `[data-element=\"${element}\"]`;\n const elementRegex = new RegExp(\n `[^}]*\\\\[data-element=\"${element.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\"\\\\][^{]*\\\\{[^}]*\\\\}`,\n 'gm',\n );\n const elementCSS = (css.match(elementRegex) || []).join('\\n');\n\n if (elementCSS) {\n const elementRules = (elementCSS.match(/\\{/g) || []).length;\n const elementLines = elementCSS.split('\\n').length;\n const elementSize = new Blob([elementCSS]).size;\n const elementSizeStr =\n elementSize > 1024\n ? `${(elementSize / 1024).toFixed(1)}KB`\n : `${elementSize}B`;\n\n console.groupCollapsed(\n `🧩 ${element} (${elementRules} rules, ${elementLines} lines, ${elementSizeStr})`,\n );\n console.log(\n `%c${elementCSS}`,\n 'color: #666; font-family: monospace; font-size: 12px; white-space: pre;',\n );\n console.groupEnd();\n }\n });\n }\n\n // Full CSS in collapsible group (hidden by default)\n console.groupCollapsed('📄 Full CSS (click to expand)');\n console.log(\n `%c${css}`,\n 'color: #666; font-family: monospace; font-size: 12px; white-space: pre;',\n );\n console.groupEnd();\n\n console.groupEnd();\n },\n\n // 12) Show help and usage examples\n help(): void {\n console.group('🎨 tastyDebug - Quick Start Guide');\n console.log('💡 Essential commands:');\n console.log(\n ' • tastyDebug.summary({ log: true }) - comprehensive overview',\n );\n console.log(' • tastyDebug.chunks({ log: true }) - style chunk breakdown');\n console.log(' • tastyDebug.log(\"active\") - beautiful CSS display');\n console.log(' • tastyDebug.css(\"active\") - get active CSS');\n console.log(\n ' • tastyDebug.inspect(\".my-element\") - element inspection with chunk info',\n );\n console.log(' • tastyDebug.cache() - cache status');\n console.log(' • tastyDebug.defs() - defined properties & keyframes');\n console.log(' • tastyDebug.pageCSS({ log: true }) - all page CSS');\n console.log('');\n console.log('📖 Common targets for css()/log():');\n console.log(' • \"all\" - all tasty CSS + global CSS');\n console.log(' • \"active\" - CSS for classes in DOM');\n console.log(' • \"unused\" - CSS for classes with refCount = 0');\n console.log(' • \"global\" - only global CSS (injectGlobal)');\n console.log(' • \"page\" - ALL page CSS (including non-tasty)');\n console.log(' • \"t123\" - specific tasty class');\n console.log(' • [\".my-selector\"] - CSS selector');\n console.log('');\n console.log('🔧 Available options:');\n console.log(' • { log: true } - auto-log results to console');\n console.log(' • { title: \"Custom\" } - custom title for log()');\n console.log(' • { root: shadowRoot } - target Shadow DOM');\n console.log(' • { prettify: false } - skip CSS formatting');\n console.log('');\n console.log('🧩 Style Chunking:');\n console.log(\n ' Elements have multiple classes (one per chunk: appearance, font, dimension, etc.)',\n );\n console.log(\n ' • tastyDebug.chunks({ log: true }) - breakdown by chunk type',\n );\n console.log(\n ' • tastyDebug.inspect() - shows which chunk each class belongs to',\n );\n console.log(\n ' Chunk types: combined (non-chunked), appearance, font, dimension, container, scrollbar, position, misc, subcomponents',\n );\n console.groupEnd();\n },\n};\n\n/**\n * Auto-install in development\n */\nif (typeof window !== 'undefined' && isDevEnv()) {\n tastyDebug.install();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"debug.js","names":[],"sources":["../src/debug.ts"],"sourcesContent":["/* eslint-disable no-console */\n/**\n * Debug utilities for inspecting tasty-generated CSS at runtime\n */\n\nimport { CHUNK_NAMES } from './chunks/definitions';\nimport { getCssTextForNode, injector } from './injector';\nimport type { CleanupStats } from './injector/types';\nimport { isDevEnv } from './utils/is-dev-env';\n\ndeclare global {\n interface Window {\n tastyDebug?: typeof tastyDebug;\n }\n}\n\n// Type definitions for the new API\ntype CSSTarget =\n | 'all' // tasty CSS + tasty global CSS + raw CSS\n | 'global' // only tasty global CSS\n | 'active' // tasty CSS for classes currently in DOM\n | 'unused' // tasty CSS with refCount = 0 (still in cache but not actively used)\n | 'page' // ALL CSS on the page across stylesheets (not only tasty)\n | string // 't123' tasty class or a CSS selector\n | string[] // array of tasty classes like ['t1', 't2']\n | Element; // a DOM element\n\ninterface CssOptions {\n root?: Document | ShadowRoot;\n prettify?: boolean; // default: true\n log?: boolean; // default: false\n}\n\ninterface ChunkInfo {\n className: string;\n chunkName: string | null;\n}\n\ninterface InspectResult {\n element?: Element | null;\n classes: string[]; // tasty classes found on the element\n chunks: ChunkInfo[]; // chunk information per class (with chunking enabled)\n css: string; // full, prettified CSS affecting the element\n size: number; // characters in css\n rules: number; // number of rule blocks\n}\n\ninterface CacheMetrics {\n hits: number;\n misses: number;\n bulkCleanups: number;\n totalInsertions: number;\n totalUnused: number;\n stylesCleanedUp: number;\n cleanupHistory: {\n timestamp: number;\n classesDeleted: number;\n cssSize: number;\n rulesDeleted: number;\n }[];\n startTime: number;\n\n // Calculated metrics\n unusedHits?: number; // calculated as current unused count\n}\n\ninterface CacheStatus {\n classes: {\n active: string[]; // classes with refCount > 0 and present in DOM\n unused: string[]; // classes with refCount = 0 but still in cache\n all: string[]; // union of both\n };\n metrics: CacheMetrics | null;\n}\n\ninterface Definitions {\n properties: string[]; // defined via @property\n keyframes: { name: string; refCount: number }[];\n}\n\ninterface SummaryOptions {\n root?: Document | ShadowRoot;\n log?: boolean;\n includePageCSS?:\n | false // do not include page-level CSS stats (default)\n | true // include sizes/counts only\n | 'all'; // include stats and return full page CSS string\n}\n\ninterface Summary {\n // Classes\n activeClasses: string[];\n unusedClasses: string[];\n totalStyledClasses: string[];\n\n // Tasty CSS sizes\n activeCSSSize: number;\n unusedCSSSize: number;\n globalCSSSize: number; // injectGlobal() CSS\n rawCSSSize: number; // injectRawCSS() / useRawCSS() CSS\n keyframesCSSSize: number; // @keyframes CSS\n propertyCSSSize: number; // @property CSS\n totalCSSSize: number; // all tasty CSS (active + unused + global + raw + keyframes + property)\n\n // Tasty CSS payloads\n activeCSS: string;\n unusedCSS: string;\n globalCSS: string; // injectGlobal() CSS\n rawCSS: string; // injectRawCSS() / useRawCSS() CSS\n keyframesCSS: string; // @keyframes CSS\n propertyCSS: string; // @property CSS\n allCSS: string; // all tasty CSS combined\n\n // Rule counts\n globalRuleCount: number;\n rawRuleCount: number;\n keyframesRuleCount: number;\n propertyRuleCount: number;\n\n // Page-level CSS (across all stylesheets, not only tasty) — shown when includePageCSS != false\n page?: {\n css?: string; // present only when includePageCSS === 'all'\n cssSize: number; // total characters\n ruleCount: number; // approximate rule count\n stylesheetCount: number; // stylesheets scanned (CORS-safe)\n skippedStylesheets: number; // stylesheets skipped due to cross-origin/CORS\n };\n\n // Metrics & definitions\n metrics: CacheMetrics | null;\n definedProperties: string[];\n definedKeyframes: { name: string; refCount: number }[];\n propertyCount: number;\n keyframeCount: number;\n\n // Cleanup summary\n cleanupSummary: {\n enabled: boolean;\n totalCleanups: number;\n totalClassesDeleted: number;\n totalCssDeleted: number;\n totalRulesDeleted: number;\n averageClassesPerCleanup: number;\n averageCssPerCleanup: number;\n averageRulesPerCleanup: number;\n lastCleanup?: {\n timestamp: number;\n date: string;\n classesDeleted: number;\n cssSize: number;\n rulesDeleted: number;\n };\n };\n\n // Chunk breakdown (style chunking optimization)\n chunkBreakdown: {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n };\n}\n\n/**\n * Pretty-print CSS with proper indentation and formatting\n */\nfunction prettifyCSS(css: string): string {\n if (!css || css.trim() === '') {\n return '';\n }\n\n // First, normalize whitespace but preserve structure\n let formatted = css.replace(/\\s+/g, ' ').trim();\n\n // Add newlines after opening braces\n formatted = formatted.replace(/\\s*\\{\\s*/g, ' {\\n');\n\n // Add newlines after semicolons (but not inside strings or functions)\n formatted = formatted.replace(/;(?![^\"']*[\"'][^\"']*$)(?![^()]*\\))/g, ';\\n');\n\n // Add newlines before closing braces\n formatted = formatted.replace(/\\s*\\}\\s*/g, '\\n}\\n');\n\n // Handle comma-separated selectors (only outside of property values)\n // This regex looks for commas that are:\n // 1. Not inside quotes\n // 2. Not inside parentheses (CSS functions)\n // 3. Not followed by a colon (not in a property value)\n formatted = formatted.replace(\n /,(?![^\"']*[\"'][^\"']*$)(?![^()]*\\))(?=.*:.*\\{|.*\\{)/g,\n ',\\n',\n );\n\n // Process line by line for proper indentation\n const lines = formatted.split('\\n');\n let indentLevel = 0;\n const indentSize = 2;\n\n const formattedLines = lines.map((line) => {\n const trimmed = line.trim();\n if (!trimmed) return '';\n\n // Handle closing braces - decrease indent first\n if (trimmed === '}') {\n indentLevel = Math.max(0, indentLevel - 1);\n return ' '.repeat(indentLevel * indentSize) + trimmed;\n }\n\n // Current line with proper indentation\n const indent = ' '.repeat(indentLevel * indentSize);\n const result = indent + trimmed;\n\n // Handle opening braces - increase indent for next line\n if (trimmed.endsWith('{')) {\n indentLevel++;\n }\n\n return result;\n });\n\n // Clean up the result and ensure proper spacing\n let result = formattedLines\n .filter((line) => line.trim()) // Remove empty lines\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n') // Max 2 consecutive newlines\n .trim();\n\n // Final cleanup: ensure single spaces in function calls\n result = result.replace(/,\\s+/g, ', ');\n\n return result;\n}\n\n// Helper functions\nfunction findAllTastyClasses(root: Document | ShadowRoot = document): string[] {\n const classes = new Set<string>();\n const elements = (root as Document).querySelectorAll?.('[class]') || [];\n\n elements.forEach((element) => {\n const classList = element.getAttribute('class');\n if (classList) {\n const tastyClasses = classList\n .split(/\\s+/)\n .filter((cls) => /^t\\d+$/.test(cls));\n tastyClasses.forEach((cls) => classes.add(cls));\n }\n });\n\n return Array.from(classes).sort((a, b) => {\n const aNum = parseInt(a.slice(1));\n const bNum = parseInt(b.slice(1));\n return aNum - bNum;\n });\n}\n\nfunction findAllStyledClasses(\n root: Document | ShadowRoot = document,\n): string[] {\n // Extract tasty classes from all CSS text by parsing selectors\n const allCSS = injector.instance.getCssText({ root });\n const classes = new Set<string>();\n\n // Simple regex to find .t{number} class selectors\n const classRegex = /\\.t(\\d+)/g;\n let match;\n while ((match = classRegex.exec(allCSS)) !== null) {\n classes.add(`t${match[1]}`);\n }\n\n return Array.from(classes).sort((a, b) => {\n const aNum = parseInt(a.slice(1));\n const bNum = parseInt(b.slice(1));\n return aNum - bNum;\n });\n}\n\nfunction extractCSSRules(\n css: string,\n): { selector: string; declarations: string }[] {\n const rules: { selector: string; declarations: string }[] = [];\n\n // Remove comments\n const cleanCSS = css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n\n let i = 0;\n while (i < cleanCSS.length) {\n // Skip whitespace\n while (i < cleanCSS.length && /\\s/.test(cleanCSS[i])) {\n i++;\n }\n if (i >= cleanCSS.length) break;\n\n // Find selector start\n const selectorStart = i;\n let braceDepth = 0;\n let inString = false;\n let stringChar = '';\n\n // Find opening brace\n while (i < cleanCSS.length) {\n const char = cleanCSS[i];\n if (inString) {\n if (char === stringChar && cleanCSS[i - 1] !== '\\\\') {\n inString = false;\n }\n } else {\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n } else if (char === '{') {\n braceDepth++;\n if (braceDepth === 1) break;\n }\n }\n i++;\n }\n\n if (i >= cleanCSS.length) break;\n const selector = cleanCSS.substring(selectorStart, i).trim();\n i++; // Skip opening brace\n\n // Find matching closing brace\n const contentStart = i;\n braceDepth = 1;\n inString = false;\n\n while (i < cleanCSS.length && braceDepth > 0) {\n const char = cleanCSS[i];\n if (inString) {\n if (char === stringChar && cleanCSS[i - 1] !== '\\\\') {\n inString = false;\n }\n } else {\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n } else if (char === '{') {\n braceDepth++;\n } else if (char === '}') {\n braceDepth--;\n }\n }\n i++;\n }\n\n const content = cleanCSS.substring(contentStart, i - 1).trim();\n if (content && selector) {\n rules.push({ selector, declarations: content });\n }\n }\n\n return rules;\n}\n\nfunction getGlobalCSS(root: Document | ShadowRoot = document): string {\n const allCSS = injector.instance.getCssText({ root });\n const rules = extractCSSRules(allCSS);\n\n const globalRules = rules.filter((rule) => {\n const selectors = rule.selector.split(',').map((s) => s.trim());\n return !selectors.every((selector) => {\n const cleanSelector = selector.replace(/[.#:\\s>+~[\\]()]/g, ' ');\n const parts = cleanSelector.split(/\\s+/).filter(Boolean);\n return parts.length > 0 && parts.every((part) => /^t\\d+$/.test(part));\n });\n });\n\n const globalCSS = globalRules\n .map((rule) => `${rule.selector} { ${rule.declarations} }`)\n .join('\\n');\n return prettifyCSS(globalCSS);\n}\n\nfunction getPageCSS(options?: {\n root?: Document | ShadowRoot;\n includeCrossOrigin?: boolean;\n}): string {\n const root = options?.root || document;\n const includeCrossOrigin = options?.includeCrossOrigin ?? false;\n\n const cssChunks: string[] = [];\n\n try {\n if ('styleSheets' in root) {\n const styleSheets = Array.from((root as Document).styleSheets);\n\n for (const sheet of styleSheets) {\n try {\n if (sheet.cssRules) {\n const rules = Array.from(sheet.cssRules);\n cssChunks.push(rules.map((rule) => rule.cssText).join('\\n'));\n }\n } catch {\n // Cross-origin sheet or other access error\n if (includeCrossOrigin) {\n cssChunks.push(\n `/* Cross-origin stylesheet: ${sheet.href || 'inline'} */`,\n );\n }\n }\n }\n }\n } catch {\n // Fallback error handling\n }\n\n return cssChunks.join('\\n');\n}\n\nfunction getPageStats(options?: {\n root?: Document | ShadowRoot;\n includeCrossOrigin?: boolean;\n}): {\n cssSize: number;\n ruleCount: number;\n stylesheetCount: number;\n skippedStylesheets: number;\n} {\n const root = options?.root || document;\n\n const _includeCrossOrigin = options?.includeCrossOrigin ?? false;\n\n let cssSize = 0;\n let ruleCount = 0;\n let stylesheetCount = 0;\n let skippedStylesheets = 0;\n\n try {\n if ('styleSheets' in root) {\n const styleSheets = Array.from((root as Document).styleSheets);\n stylesheetCount = styleSheets.length;\n\n for (const sheet of styleSheets) {\n try {\n if (sheet.cssRules) {\n const rules = Array.from(sheet.cssRules);\n ruleCount += rules.length;\n cssSize += rules.reduce(\n (sum, rule) => sum + rule.cssText.length,\n 0,\n );\n }\n } catch {\n skippedStylesheets++;\n }\n }\n }\n } catch {\n // Fallback error handling\n }\n\n return { cssSize, ruleCount, stylesheetCount, skippedStylesheets };\n}\n\n// ============================================================================\n// Chunk-aware helpers (for style chunking optimization)\n// ============================================================================\n\n/**\n * Extract chunk name from a cache key.\n *\n * Cache keys have the format: \"chunkName\\0key:value\\0key:value...\"\n * or \"[states:...]\\0chunkName\\0...\" for predefined states.\n *\n * @param cacheKey - The cache key to parse\n * @returns The chunk name, or null if not found\n */\nfunction extractChunkNameFromCacheKey(cacheKey: string): string | null {\n // Cache keys are separated by \\0 (null character)\n const parts = cacheKey.split('\\0');\n\n for (const part of parts) {\n // Skip predefined states prefix\n if (part.startsWith('[states:')) continue;\n // First non-states part that doesn't contain : is the chunk name\n if (!part.includes(':') && part.length > 0) {\n return part;\n }\n }\n return null;\n}\n\n/**\n * Get chunk info for a className by reverse-looking up its cache key.\n *\n * @param className - The tasty class name (e.g., \"t0\", \"t123\")\n * @param root - The document or shadow root to search in\n * @returns Object with chunk name and cache key, or nulls if not found\n */\nfunction getChunkForClassName(\n className: string,\n root: Document | ShadowRoot = document,\n): { chunkName: string | null; cacheKey: string | null } {\n const registry = injector.instance._sheetManager?.getRegistry(root);\n if (!registry) {\n return { chunkName: null, cacheKey: null };\n }\n\n // Reverse lookup: find the cache key for this className\n for (const [cacheKey, cn] of registry.cacheKeyToClassName) {\n if (cn === className) {\n return {\n chunkName: extractChunkNameFromCacheKey(cacheKey),\n cacheKey,\n };\n }\n }\n return { chunkName: null, cacheKey: null };\n}\n\n/**\n * Get chunk breakdown statistics for all styles.\n *\n * @param root - The document or shadow root to search in\n * @returns Object with breakdown by chunk type and totals\n */\nfunction getChunkBreakdown(root: Document | ShadowRoot = document): {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n} {\n const registry = injector.instance._sheetManager?.getRegistry(root);\n\n if (!registry) {\n return {\n byChunk: {},\n totalChunkTypes: 0,\n };\n }\n\n const byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n > = {};\n\n // Group classes by chunk\n for (const [cacheKey, className] of registry.cacheKeyToClassName) {\n const chunkName = extractChunkNameFromCacheKey(cacheKey) || 'unknown';\n\n if (!byChunk[chunkName]) {\n byChunk[chunkName] = { classes: [], cssSize: 0, ruleCount: 0 };\n }\n\n byChunk[chunkName].classes.push(className);\n\n // Get CSS for this class\n const css = injector.instance.getCssTextForClasses([className], { root });\n byChunk[chunkName].cssSize += css.length;\n byChunk[chunkName].ruleCount += (css.match(/\\{[^}]*\\}/g) || []).length;\n }\n\n // Sort classes within each chunk for consistency\n for (const entry of Object.values(byChunk)) {\n entry.classes.sort((a, b) => {\n const aNum = parseInt(a.slice(1));\n const bNum = parseInt(b.slice(1));\n return aNum - bNum;\n });\n }\n\n return {\n byChunk,\n totalChunkTypes: Object.keys(byChunk).length,\n };\n}\n\n/**\n * Concise tastyDebug API for inspecting styles at runtime\n */\nexport const tastyDebug = {\n // 1) One function to get CSS from anywhere\n css(target: CSSTarget, opts?: CssOptions): string {\n const { root = document, prettify = true, log = false } = opts || {};\n let css = '';\n\n if (typeof target === 'string') {\n if (target === 'all') {\n css = injector.instance.getCssText({ root });\n } else if (target === 'global') {\n css = getGlobalCSS(root);\n } else if (target === 'active') {\n const activeClasses = findAllTastyClasses(root);\n css = injector.instance.getCssTextForClasses(activeClasses, { root });\n } else if (target === 'unused') {\n // Get unused classes (refCount = 0) from the registry\n const registry = injector.instance._sheetManager?.getRegistry(root);\n const unusedClasses: string[] = registry\n ? Array.from(\n registry.refCounts.entries() as IterableIterator<\n [string, number]\n >,\n )\n .filter(([, refCount]: [string, number]) => refCount === 0)\n .map(([className]: [string, number]) => className)\n : [];\n css = injector.instance.getCssTextForClasses(unusedClasses, { root });\n } else if (target === 'page') {\n css = getPageCSS({ root, includeCrossOrigin: true });\n } else if (/^t\\d+$/.test(target)) {\n // Single tasty class\n css = injector.instance.getCssTextForClasses([target], { root });\n } else {\n // CSS selector - find element and get its CSS\n const element = (root as Document).querySelector?.(target);\n if (element) {\n css = getCssTextForNode(element, { root });\n }\n }\n } else if (Array.isArray(target)) {\n // Array of tasty classes\n css = injector.instance.getCssTextForClasses(target, { root });\n } else if (target instanceof Element) {\n // DOM element\n css = getCssTextForNode(target, { root });\n }\n\n const result = prettify ? prettifyCSS(css) : css;\n\n if (log) {\n console.group(\n `🎨 CSS for ${Array.isArray(target) ? `[${target.join(', ')}]` : target}`,\n );\n console.log(result || '(empty)');\n console.groupEnd();\n }\n\n return result;\n },\n\n // 2) Element-level inspection\n inspect(\n target: string | Element,\n opts?: { root?: Document | ShadowRoot },\n ): InspectResult {\n const { root = document } = opts || {};\n const element =\n typeof target === 'string'\n ? (root as Document).querySelector?.(target)\n : target;\n\n if (!element) {\n return {\n element: null,\n classes: [],\n chunks: [],\n css: '',\n size: 0,\n rules: 0,\n };\n }\n\n const classList = element.getAttribute('class') || '';\n const tastyClasses = classList\n .split(/\\s+/)\n .filter((cls) => /^t\\d+$/.test(cls));\n\n // Get chunk info for each tasty class\n const chunks: ChunkInfo[] = tastyClasses.map((className) => ({\n className,\n chunkName: getChunkForClassName(className, root).chunkName,\n }));\n\n const css = getCssTextForNode(element, { root });\n const prettifiedCSS = prettifyCSS(css);\n const ruleCount = (css.match(/\\{[^}]*\\}/g) || []).length;\n\n return {\n element,\n classes: tastyClasses,\n chunks,\n css: prettifiedCSS,\n size: css.length,\n rules: ruleCount,\n };\n },\n\n // 3) Cache + metrics at a glance\n cache(opts?: {\n root?: Document | ShadowRoot;\n includeHistory?: boolean;\n }): CacheStatus {\n const { root = document } = opts || {};\n const activeClasses = findAllTastyClasses(root);\n\n const _allClasses = findAllStyledClasses(root);\n // Get unused classes (refCount = 0) from the registry\n const registry = injector.instance._sheetManager?.getRegistry(root);\n const unusedClasses: string[] = registry\n ? Array.from(\n registry.refCounts.entries() as IterableIterator<[string, number]>,\n )\n .filter(([, refCount]: [string, number]) => refCount === 0)\n .map(([className]: [string, number]) => className)\n : [];\n\n return {\n classes: {\n active: activeClasses,\n unused: unusedClasses,\n all: [...activeClasses, ...unusedClasses],\n },\n metrics: injector.instance.getMetrics({ root }),\n };\n },\n\n // 4) Cleanup + metrics utilities\n cleanup(opts?: { root?: Document | ShadowRoot }): void {\n const { root } = opts || {};\n injector.instance.cleanup(root);\n },\n\n metrics(opts?: { root?: Document | ShadowRoot }): CacheMetrics | null {\n const { root } = opts || {};\n return injector.instance.getMetrics({ root });\n },\n\n resetMetrics(opts?: { root?: Document | ShadowRoot }): void {\n const { root } = opts || {};\n injector.instance.resetMetrics({ root });\n },\n\n // 5) Chunk breakdown (style chunking optimization)\n /**\n * Get breakdown of styles by chunk type.\n *\n * With style chunking enabled, styles are split into logical chunks\n * (appearance, font, dimension, container, etc.) for better caching\n * and CSS reuse.\n *\n * @param opts - Options including root document/shadow root\n * @returns Breakdown by chunk type with class counts and CSS sizes\n */\n chunks(opts?: { root?: Document | ShadowRoot; log?: boolean }): {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n totalClasses: number;\n } {\n const { root = document, log = false } = opts || {};\n const breakdown = getChunkBreakdown(root);\n\n const totalClasses = Object.values(breakdown.byChunk).reduce(\n (sum, entry) => sum + entry.classes.length,\n 0,\n );\n\n if (log) {\n console.group('🧩 Style Chunk Breakdown');\n\n // Define display order matching CHUNK_NAMES\n const displayOrder = [\n CHUNK_NAMES.COMBINED, // non-chunked styles (e.g., @starting-style)\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n ];\n\n // Show chunks in order\n for (const chunkName of displayOrder) {\n const data = breakdown.byChunk[chunkName];\n if (data) {\n const sizeStr =\n data.cssSize > 1024\n ? `${(data.cssSize / 1024).toFixed(1)}KB`\n : `${data.cssSize}B`;\n console.log(\n ` • ${chunkName}: ${data.classes.length} classes, ${sizeStr}, ${data.ruleCount} rules`,\n );\n }\n }\n\n // Show any unknown chunks\n for (const [chunkName, data] of Object.entries(breakdown.byChunk)) {\n if (\n !displayOrder.includes(chunkName as (typeof displayOrder)[number])\n ) {\n const sizeStr =\n data.cssSize > 1024\n ? `${(data.cssSize / 1024).toFixed(1)}KB`\n : `${data.cssSize}B`;\n console.log(\n ` • ${chunkName}: ${data.classes.length} classes, ${sizeStr}, ${data.ruleCount} rules`,\n );\n }\n }\n\n console.log(\n `📊 Total: ${totalClasses} classes across ${breakdown.totalChunkTypes} chunk types`,\n );\n console.groupEnd();\n }\n\n return {\n ...breakdown,\n totalClasses,\n };\n },\n\n // 6) Get CSS for specific global types\n getGlobalTypeCSS(\n type: 'global' | 'raw' | 'keyframes' | 'property',\n opts?: { root?: Document | ShadowRoot },\n ): { css: string; ruleCount: number; size: number } {\n const { root = document } = opts || {};\n const registry = injector.instance._sheetManager?.getRegistry(root);\n\n if (!registry) {\n return { css: '', ruleCount: 0, size: 0 };\n }\n\n const cssChunks: string[] = [];\n let ruleCount = 0;\n\n if (type === 'keyframes') {\n // Handle keyframes separately - they're stored in keyframesCache\n for (const [, entry] of registry.keyframesCache) {\n const info = entry.info;\n const sheet = registry.sheets[info.sheetIndex];\n const styleSheet = sheet?.sheet?.sheet;\n\n if (styleSheet && info.ruleIndex < styleSheet.cssRules.length) {\n const rule = styleSheet.cssRules[info.ruleIndex] as\n | CSSRule\n | undefined;\n if (rule) {\n cssChunks.push(rule.cssText);\n ruleCount++;\n }\n } else if (info.cssText) {\n cssChunks.push(info.cssText);\n ruleCount++;\n }\n }\n } else {\n // Handle other global types stored in globalRules\n const prefix =\n type === 'global' ? 'global:' : type === 'raw' ? 'raw:' : 'property:';\n\n for (const [key, ruleInfo] of registry.globalRules) {\n if (key.startsWith(prefix)) {\n const sheet = registry.sheets[ruleInfo.sheetIndex];\n const styleSheet = sheet?.sheet?.sheet;\n if (styleSheet) {\n const start = Math.max(0, ruleInfo.ruleIndex);\n const end = Math.min(\n styleSheet.cssRules.length - 1,\n (ruleInfo.endRuleIndex as number) ?? ruleInfo.ruleIndex,\n );\n\n if (\n start >= 0 &&\n end >= start &&\n start < styleSheet.cssRules.length\n ) {\n for (let i = start; i <= end; i++) {\n const rule = styleSheet.cssRules[i] as CSSRule | undefined;\n if (rule) {\n cssChunks.push(rule.cssText);\n ruleCount++;\n }\n }\n }\n } else if (ruleInfo.cssText && ruleInfo.cssText.length) {\n // Fallback in environments without CSSOM access\n cssChunks.push(...ruleInfo.cssText);\n ruleCount += ruleInfo.cssText.length;\n }\n }\n }\n }\n\n const rawCSS = cssChunks.join('\\n');\n\n return {\n css: prettifyCSS(rawCSS),\n ruleCount,\n size: rawCSS.length, // Use raw CSS size for consistent calculations\n };\n },\n\n // 6) Defined @property and keyframes\n defs(opts?: { root?: Document | ShadowRoot }): Definitions {\n const { root = document } = opts || {};\n\n // Get properties from injector if available, otherwise scan CSS\n let properties: string[] = [];\n try {\n const registry = injector.instance._sheetManager?.getRegistry(root);\n if (registry?.injectedProperties) {\n properties = Array.from(\n (registry.injectedProperties as Map<string, string>).keys(),\n ).sort();\n }\n } catch {\n // Fallback: scan CSS for @property rules\n const allCSS = injector.instance.getCssText({ root });\n const propRegex = /@property\\s+(--[a-z0-9-]+)/gi;\n const propSet = new Set<string>();\n let match;\n while ((match = propRegex.exec(allCSS)) !== null) {\n propSet.add(match[1]);\n }\n properties = Array.from(propSet).sort();\n }\n\n // Get keyframes\n let keyframes: { name: string; refCount: number }[] = [];\n try {\n const registry = injector.instance._sheetManager?.getRegistry(root);\n if (registry) {\n for (const entry of registry.keyframesCache.values()) {\n keyframes.push({\n name: entry.name,\n refCount: entry.refCount,\n });\n }\n keyframes.sort((a, b) => a.name.localeCompare(b.name));\n }\n } catch {\n // Fallback: scan CSS for @keyframes rules\n const allCSS = injector.instance.getCssText({ root });\n const keyframesRegex = /@keyframes\\s+([a-zA-Z0-9_-]+)/gi;\n const keyframesSet = new Set<string>();\n let match;\n while ((match = keyframesRegex.exec(allCSS)) !== null) {\n keyframesSet.add(match[1]);\n }\n keyframes = Array.from(keyframesSet)\n .sort()\n .map((name) => ({ name, refCount: 1 }));\n }\n\n return { properties, keyframes };\n },\n\n // 7) One-shot overview\n summary(opts?: SummaryOptions): Summary {\n const { root = document, log = false, includePageCSS = false } = opts || {};\n const cacheStatus = this.cache({ root });\n const definitions = this.defs({ root });\n const metrics = this.metrics({ root });\n\n const activeCSS = this.css('active', { root, prettify: false });\n const unusedCSS = this.css('unused', { root, prettify: false });\n const allCSS = this.css('all', { root, prettify: false });\n\n // Calculate global CSS by subtracting class-based CSS from total\n const classCSSSize = activeCSS.length + unusedCSS.length;\n const totalGlobalCSSSize = allCSS.length - classCSSSize;\n\n // Get CSS for each global type separately for display purposes\n const globalData = this.getGlobalTypeCSS('global', { root });\n const rawData = this.getGlobalTypeCSS('raw', { root });\n const keyframesData = this.getGlobalTypeCSS('keyframes', { root });\n const propertyData = this.getGlobalTypeCSS('property', { root });\n\n // Use the calculated sizes to avoid double-counting\n const globalTypesTotalSize =\n globalData.size + rawData.size + keyframesData.size + propertyData.size;\n\n // Build cleanup summary from metrics\n const cleanupSummary = {\n enabled: !!metrics,\n totalCleanups: metrics?.cleanupHistory?.length || 0,\n totalClassesDeleted:\n metrics?.cleanupHistory?.reduce(\n (sum, c) => sum + c.classesDeleted,\n 0,\n ) || 0,\n totalCssDeleted:\n metrics?.cleanupHistory?.reduce((sum, c) => sum + c.cssSize, 0) || 0,\n totalRulesDeleted:\n metrics?.cleanupHistory?.reduce((sum, c) => sum + c.rulesDeleted, 0) ||\n 0,\n averageClassesPerCleanup: 0,\n averageCssPerCleanup: 0,\n averageRulesPerCleanup: 0,\n lastCleanup: undefined as (CleanupStats & { date: string }) | undefined,\n };\n\n if (cleanupSummary.totalCleanups > 0) {\n cleanupSummary.averageClassesPerCleanup =\n cleanupSummary.totalClassesDeleted / cleanupSummary.totalCleanups;\n cleanupSummary.averageCssPerCleanup =\n cleanupSummary.totalCssDeleted / cleanupSummary.totalCleanups;\n cleanupSummary.averageRulesPerCleanup =\n cleanupSummary.totalRulesDeleted / cleanupSummary.totalCleanups;\n\n const lastCleanup =\n metrics?.cleanupHistory?.[metrics.cleanupHistory.length - 1];\n if (lastCleanup) {\n cleanupSummary.lastCleanup = {\n ...lastCleanup,\n date: new Date(lastCleanup.timestamp).toISOString(),\n };\n }\n }\n\n let page: Summary['page'] | undefined;\n if (includePageCSS) {\n const pageStats = getPageStats({ root, includeCrossOrigin: true });\n page = {\n ...pageStats,\n css:\n includePageCSS === 'all'\n ? getPageCSS({ root, includeCrossOrigin: true })\n : undefined,\n };\n }\n\n // If individual extraction matches total, use individual sizes\n // Otherwise, proportionally scale the individual sizes to match the total\n const useIndividualSizes =\n Math.abs(globalTypesTotalSize - totalGlobalCSSSize) < 100;\n\n let adjustedGlobalSizes;\n if (useIndividualSizes) {\n adjustedGlobalSizes = {\n globalCSSSize: globalData.size,\n rawCSSSize: rawData.size,\n keyframesCSSSize: keyframesData.size,\n propertyCSSSize: propertyData.size,\n };\n } else {\n // Scale proportionally to match the actual total\n const scaleFactor = totalGlobalCSSSize / globalTypesTotalSize;\n adjustedGlobalSizes = {\n globalCSSSize: Math.round(globalData.size * scaleFactor),\n rawCSSSize: Math.round(rawData.size * scaleFactor),\n keyframesCSSSize: Math.round(keyframesData.size * scaleFactor),\n propertyCSSSize: Math.round(propertyData.size * scaleFactor),\n };\n }\n\n // Get chunk breakdown\n const chunkBreakdown = getChunkBreakdown(root);\n\n const summary: Summary = {\n activeClasses: cacheStatus.classes.active,\n unusedClasses: cacheStatus.classes.unused,\n totalStyledClasses: cacheStatus.classes.all,\n activeCSSSize: activeCSS.length,\n unusedCSSSize: unusedCSS.length,\n ...adjustedGlobalSizes,\n totalCSSSize: allCSS.length,\n activeCSS: prettifyCSS(activeCSS),\n unusedCSS: prettifyCSS(unusedCSS),\n globalCSS: globalData.css,\n rawCSS: rawData.css,\n keyframesCSS: keyframesData.css,\n propertyCSS: propertyData.css,\n allCSS: prettifyCSS(allCSS),\n globalRuleCount: globalData.ruleCount,\n rawRuleCount: rawData.ruleCount,\n keyframesRuleCount: keyframesData.ruleCount,\n propertyRuleCount: propertyData.ruleCount,\n page,\n metrics,\n definedProperties: definitions.properties,\n definedKeyframes: definitions.keyframes,\n propertyCount: definitions.properties.length,\n keyframeCount: definitions.keyframes.length,\n cleanupSummary,\n chunkBreakdown,\n };\n\n if (log) {\n console.group('🎨 Comprehensive Tasty Debug Summary');\n console.log(`📊 Style Cache Status:`);\n console.log(\n ` • Active classes (in DOM): ${summary.activeClasses.length}`,\n );\n console.log(\n ` • Unused classes (refCount = 0): ${summary.unusedClasses.length}`,\n );\n console.log(\n ` • Total styled classes: ${summary.totalStyledClasses.length}`,\n );\n console.log(`💾 CSS Size:`);\n console.log(` • Active CSS: ${summary.activeCSSSize} characters`);\n console.log(` • Unused CSS: ${summary.unusedCSSSize} characters`);\n console.log(\n ` • Global CSS (injectGlobal): ${summary.globalCSSSize} characters (${summary.globalRuleCount} rules)`,\n );\n console.log(\n ` • Raw CSS (injectRawCSS/useRawCSS): ${summary.rawCSSSize} characters (${summary.rawRuleCount} rules)`,\n );\n console.log(\n ` • Keyframes CSS: ${summary.keyframesCSSSize} characters (${summary.keyframesRuleCount} rules)`,\n );\n console.log(\n ` • Property CSS: ${summary.propertyCSSSize} characters (${summary.propertyRuleCount} rules)`,\n );\n\n // Show breakdown calculation\n const calculatedTotal =\n summary.activeCSSSize +\n summary.unusedCSSSize +\n summary.globalCSSSize +\n summary.rawCSSSize +\n summary.keyframesCSSSize +\n summary.propertyCSSSize;\n console.log(` • Calculated Total: ${calculatedTotal} characters`);\n console.log(` • Actual Total: ${summary.totalCSSSize} characters`);\n\n const difference = Math.abs(calculatedTotal - summary.totalCSSSize);\n if (difference > 100) {\n console.warn(\n ` ⚠️ Size mismatch: ${difference} characters difference`,\n );\n\n // Debug: show what might be missing\n console.group('🔍 Debugging size mismatch:');\n console.log(\n `Active + Unused = ${summary.activeCSSSize + summary.unusedCSSSize}`,\n );\n console.log(\n `All Global Types = ${summary.globalCSSSize + summary.rawCSSSize + summary.keyframesCSSSize + summary.propertyCSSSize}`,\n );\n console.log(\n `Class-based vs Total difference = ${summary.totalCSSSize - (summary.activeCSSSize + summary.unusedCSSSize)}`,\n );\n\n // Show scaling information\n console.log(`Raw global extraction total: ${globalTypesTotalSize}`);\n console.log(`Calculated global size: ${totalGlobalCSSSize}`);\n console.log(`Used individual sizes: ${useIndividualSizes}`);\n if (!useIndividualSizes) {\n console.log(\n `Scale factor applied: ${(totalGlobalCSSSize / globalTypesTotalSize).toFixed(3)}`,\n );\n }\n console.groupEnd();\n }\n\n if (page) {\n console.log(`📄 Page CSS:`);\n console.log(` • Total page CSS: ${page.cssSize} characters`);\n console.log(` • Total page rules: ${page.ruleCount}`);\n console.log(\n ` • Stylesheets: ${page.stylesheetCount} (${page.skippedStylesheets} skipped)`,\n );\n }\n\n console.log('🏷️ Properties & Keyframes:');\n console.log(` • Defined @property: ${summary.propertyCount}`);\n console.log(` • Defined @keyframes: ${summary.keyframeCount}`);\n\n if (metrics) {\n console.log(`⚡ Performance Metrics:`);\n console.log(` • Cache hits: ${metrics.hits}`);\n console.log(` • Cache misses: ${metrics.misses}`);\n console.log(` • Cached style reuses: ${metrics.unusedHits}`);\n const hitRate =\n metrics.hits + metrics.misses > 0\n ? (\n ((metrics.hits + (metrics.unusedHits || 0)) /\n (metrics.hits + metrics.misses)) *\n 100\n ).toFixed(1)\n : '0';\n console.log(` • Overall cache hit rate: ${hitRate}%`);\n }\n\n // Show chunk breakdown\n if (summary.chunkBreakdown.totalChunkTypes > 0) {\n console.log('🧩 Style Chunk Breakdown:');\n const displayOrder = [\n CHUNK_NAMES.COMBINED, // non-chunked styles (e.g., @starting-style)\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n ];\n for (const chunkName of displayOrder) {\n const data = summary.chunkBreakdown.byChunk[chunkName];\n if (data) {\n const sizeStr =\n data.cssSize > 1024\n ? `${(data.cssSize / 1024).toFixed(1)}KB`\n : `${data.cssSize}B`;\n console.log(\n ` • ${chunkName}: ${data.classes.length} classes, ${sizeStr}, ${data.ruleCount} rules`,\n );\n }\n }\n }\n\n console.log('🔍 Details:');\n console.log(' • Active classes:', summary.activeClasses);\n console.log(' • Unused classes:', summary.unusedClasses);\n console.groupEnd();\n }\n\n return summary;\n },\n\n // 8) Page-level CSS helpers\n pageCSS(opts?: {\n root?: Document | ShadowRoot;\n prettify?: boolean;\n log?: boolean;\n includeCrossOrigin?: boolean;\n }): string {\n const {\n root = document,\n prettify = true,\n log = false,\n includeCrossOrigin = true,\n } = opts || {};\n const css = getPageCSS({ root, includeCrossOrigin });\n const result = prettify ? prettifyCSS(css) : css;\n\n if (log) {\n console.group('📄 Page CSS (All Stylesheets)');\n console.log(result || '(empty)');\n console.groupEnd();\n }\n\n return result;\n },\n\n pageStats(opts?: {\n root?: Document | ShadowRoot;\n includeCrossOrigin?: boolean;\n }): {\n cssSize: number;\n ruleCount: number;\n stylesheetCount: number;\n skippedStylesheets: number;\n } {\n const { root = document, includeCrossOrigin = true } = opts || {};\n return getPageStats({ root, includeCrossOrigin });\n },\n\n // 9) Install globally\n install(): void {\n if (typeof window !== 'undefined' && window.tastyDebug !== tastyDebug) {\n window.tastyDebug = tastyDebug;\n console.log(\n '🎨 tastyDebug installed on window. Run tastyDebug.help() for quick start guide.',\n );\n }\n },\n\n // 10) Beautiful console logging with collapsible CSS\n log(target: CSSTarget, opts?: CssOptions & { title?: string }): void {\n const { title, ...cssOpts } = opts || {};\n const css = tastyDebug.css(target, cssOpts);\n\n if (!css.trim()) {\n console.warn(`🎨 No CSS found for target: ${String(target)}`);\n return;\n }\n\n const targetStr = Array.isArray(target)\n ? target.join(', ')\n : String(target);\n const displayTitle = title || `CSS for \"${targetStr}\"`;\n\n // Get some stats about the CSS\n const lines = css.split('\\n').length;\n const size = new Blob([css]).size;\n const sizeStr = size > 1024 ? `${(size / 1024).toFixed(1)}KB` : `${size}B`;\n\n // Count CSS rules (blocks with opening braces)\n const ruleCount = (css.match(/\\{/g) || []).length;\n\n console.group(\n `🎨 ${displayTitle} (${ruleCount} rules, ${lines} lines, ${sizeStr})`,\n );\n\n // Detect sub-elements in CSS\n const subElementMatches = css.match(/\\[data-element=\"([^\"]+)\"\\]/g) || [];\n const subElements = [\n ...new Set(\n subElementMatches\n .map((match) => match.match(/\\[data-element=\"([^\"]+)\"\\]/)?.[1])\n .filter(Boolean),\n ),\n ];\n\n if (subElements.length > 0) {\n console.log(`🧩 Sub-elements found: ${subElements.join(', ')}`);\n\n // Show stats and CSS for each sub-element\n subElements.forEach((element) => {\n const _elementSelector = `[data-element=\"${element}\"]`;\n const elementRegex = new RegExp(\n `[^}]*\\\\[data-element=\"${element.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\"\\\\][^{]*\\\\{[^}]*\\\\}`,\n 'gm',\n );\n const elementCSS = (css.match(elementRegex) || []).join('\\n');\n\n if (elementCSS) {\n const elementRules = (elementCSS.match(/\\{/g) || []).length;\n const elementLines = elementCSS.split('\\n').length;\n const elementSize = new Blob([elementCSS]).size;\n const elementSizeStr =\n elementSize > 1024\n ? `${(elementSize / 1024).toFixed(1)}KB`\n : `${elementSize}B`;\n\n console.groupCollapsed(\n `🧩 ${element} (${elementRules} rules, ${elementLines} lines, ${elementSizeStr})`,\n );\n console.log(\n `%c${elementCSS}`,\n 'color: #666; font-family: monospace; font-size: 12px; white-space: pre;',\n );\n console.groupEnd();\n }\n });\n }\n\n // Full CSS in collapsible group (hidden by default)\n console.groupCollapsed('📄 Full CSS (click to expand)');\n console.log(\n `%c${css}`,\n 'color: #666; font-family: monospace; font-size: 12px; white-space: pre;',\n );\n console.groupEnd();\n\n console.groupEnd();\n },\n\n // 12) Show help and usage examples\n help(): void {\n console.group('🎨 tastyDebug - Quick Start Guide');\n console.log('💡 Essential commands:');\n console.log(\n ' • tastyDebug.summary({ log: true }) - comprehensive overview',\n );\n console.log(' • tastyDebug.chunks({ log: true }) - style chunk breakdown');\n console.log(' • tastyDebug.log(\"active\") - beautiful CSS display');\n console.log(' • tastyDebug.css(\"active\") - get active CSS');\n console.log(\n ' • tastyDebug.inspect(\".my-element\") - element inspection with chunk info',\n );\n console.log(' • tastyDebug.cache() - cache status');\n console.log(' • tastyDebug.defs() - defined properties & keyframes');\n console.log(' • tastyDebug.pageCSS({ log: true }) - all page CSS');\n console.log('');\n console.log('📖 Common targets for css()/log():');\n console.log(' • \"all\" - all tasty CSS + global CSS');\n console.log(' • \"active\" - CSS for classes in DOM');\n console.log(' • \"unused\" - CSS for classes with refCount = 0');\n console.log(' • \"global\" - only global CSS (injectGlobal)');\n console.log(' • \"page\" - ALL page CSS (including non-tasty)');\n console.log(' • \"t123\" - specific tasty class');\n console.log(' • [\".my-selector\"] - CSS selector');\n console.log('');\n console.log('🔧 Available options:');\n console.log(' • { log: true } - auto-log results to console');\n console.log(' • { title: \"Custom\" } - custom title for log()');\n console.log(' • { root: shadowRoot } - target Shadow DOM');\n console.log(' • { prettify: false } - skip CSS formatting');\n console.log('');\n console.log('🧩 Style Chunking:');\n console.log(\n ' Elements have multiple classes (one per chunk: appearance, font, dimension, etc.)',\n );\n console.log(\n ' • tastyDebug.chunks({ log: true }) - breakdown by chunk type',\n );\n console.log(\n ' • tastyDebug.inspect() - shows which chunk each class belongs to',\n );\n console.log(\n ' Chunk types: combined (non-chunked), appearance, font, dimension, container, scrollbar, position, misc, subcomponents',\n );\n console.groupEnd();\n },\n};\n\n/**\n * Auto-install in development\n */\nif (typeof window !== 'undefined' && isDevEnv()) {\n tastyDebug.install();\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useKeyframes.js","names":[],"sources":["../../src/hooks/useKeyframes.ts"],"sourcesContent":["import { useInsertionEffect, useMemo, useRef } from 'react';\n\nimport { keyframes } from '../injector';\nimport type { KeyframesResult, KeyframesSteps } from '../injector/types';\n\ninterface UseKeyframesOptions {\n name?: string;\n root?: Document | ShadowRoot;\n}\n\n/**\n * Hook to inject CSS @keyframes and return the generated animation name.\n * Handles keyframes injection with proper cleanup on unmount or dependency changes.\n *\n * @example Basic usage - steps object is the dependency\n * ```tsx\n * function MyComponent() {\n * const bounce = useKeyframes({\n * '0%': { transform: 'scale(1)' },\n * '50%': { transform: 'scale(1.1)' },\n * '100%': { transform: 'scale(1)' },\n * });\n *\n * return <div style={{ animation: `${bounce} 1s infinite` }}>Bouncing</div>;\n * }\n * ```\n *\n * @example With custom name\n * ```tsx\n * function MyComponent() {\n * const fadeIn = useKeyframes(\n * { from: { opacity: 0 }, to: { opacity: 1 } },\n * { name: 'fadeIn' }\n * );\n *\n * return <div style={{ animation: `${fadeIn} 0.3s ease-out` }}>Fading in</div>;\n * }\n * ```\n *\n * @example Factory function with dependencies\n * ```tsx\n * function MyComponent({ scale }: { scale: number }) {\n * const pulse = useKeyframes(\n * () => ({\n * '0%': { transform: 'scale(1)' },\n * '100%': { transform: `scale(${scale})` },\n * }),\n * [scale]\n * );\n *\n * return <div style={{ animation: `${pulse} 1s infinite` }}>Pulsing</div>;\n * }\n * ```\n */\n\n// Overload 1: Static steps object\nexport function useKeyframes(\n steps: KeyframesSteps,\n options?: UseKeyframesOptions,\n): string;\n\n// Overload 2: Factory function with dependencies\nexport function useKeyframes(\n factory: () => KeyframesSteps,\n deps: readonly unknown[],\n options?: UseKeyframesOptions,\n): string;\n\n// Implementation\nexport function useKeyframes(\n stepsOrFactory: KeyframesSteps | (() => KeyframesSteps),\n depsOrOptions?: readonly unknown[] | UseKeyframesOptions,\n options?: UseKeyframesOptions,\n): string {\n // Detect which overload is being used\n const isFactory = typeof stepsOrFactory === 'function';\n\n // Parse arguments based on overload\n const deps =\n isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : undefined;\n const opts = isFactory\n ? options\n : (depsOrOptions as UseKeyframesOptions | undefined);\n\n // Memoize the keyframes steps to get a stable reference\n const stepsData = useMemo(\n () => {\n const steps = isFactory\n ? (stepsOrFactory as () => KeyframesSteps)()\n : (stepsOrFactory as KeyframesSteps);\n\n if (!steps || Object.keys(steps).length === 0) {\n return null;\n }\n\n return steps;\n },\n\n isFactory ? deps ?? [] : [stepsOrFactory],\n );\n\n // Store keyframes results for cleanup - we need to track both the render-time\n // injection (for the name) and the effect-time injection (for Strict Mode safety)\n const renderResultRef = useRef<KeyframesResult | null>(null);\n const effectResultRef = useRef<KeyframesResult | null>(null);\n\n // Inject keyframes during render to ensure the animation name is available\n // immediately. The keyframes() function uses reference counting internally,\n // so multiple calls with the same content are deduplicated.\n const name = useMemo(() => {\n // Dispose previous render-time result if deps changed\n renderResultRef.current?.dispose();\n renderResultRef.current = null;\n\n if (!stepsData) {\n return '';\n }\n\n // Inject keyframes synchronously\n const result = keyframes(stepsData, {\n name: opts?.name,\n root: opts?.root,\n });\n\n renderResultRef.current = result;\n\n return result.toString();\n }, [stepsData, opts?.name, opts?.root]);\n\n // Handle injection and cleanup in useInsertionEffect to properly support\n // React 18+ Strict Mode double-invocation (mount → unmount → mount).\n // The effect setup re-injects the keyframes if cleanup was called, ensuring\n // the CSS exists after Strict Mode remounts.\n useInsertionEffect(() => {\n // Dispose previous effect-time result\n effectResultRef.current?.dispose();\n effectResultRef.current = null;\n\n // Re-inject keyframes. This ensures the CSS exists after Strict Mode cleanup.\n // The keyframes() function uses reference counting, so this is idempotent\n // if the CSS wasn't disposed.\n if (stepsData) {\n const result = keyframes(stepsData, {\n name: opts?.name,\n root: opts?.root,\n });\n effectResultRef.current = result;\n }\n\n // Cleanup on unmount or when dependencies change.\n // Dispose both the effect-time and render-time results to properly\n // decrement the reference count.\n return () => {\n effectResultRef.current?.dispose();\n effectResultRef.current = null;\n renderResultRef.current?.dispose();\n renderResultRef.current = null;\n };\n }, [stepsData, opts?.name, opts?.root]);\n\n return name;\n}\n"],"mappings":";;;;AAqEA,SAAgB,aACd,gBACA,eACA,SACQ;CAER,MAAM,YAAY,OAAO,mBAAmB;CAG5C,MAAM,OACJ,aAAa,MAAM,QAAQ,cAAc,GAAG,gBAAgB;CAC9D,MAAM,OAAO,YACT,UACC;CAGL,MAAM,YAAY,cACV;EACJ,MAAM,QAAQ,YACT,gBAAyC,GACzC;AAEL,MAAI,CAAC,SAAS,OAAO,KAAK,MAAM,CAAC,WAAW,EAC1C,QAAO;AAGT,SAAO;IAGT,
|
|
1
|
+
{"version":3,"file":"useKeyframes.js","names":[],"sources":["../../src/hooks/useKeyframes.ts"],"sourcesContent":["import { useInsertionEffect, useMemo, useRef } from 'react';\n\nimport { keyframes } from '../injector';\nimport type { KeyframesResult, KeyframesSteps } from '../injector/types';\n\ninterface UseKeyframesOptions {\n name?: string;\n root?: Document | ShadowRoot;\n}\n\n/**\n * Hook to inject CSS @keyframes and return the generated animation name.\n * Handles keyframes injection with proper cleanup on unmount or dependency changes.\n *\n * @example Basic usage - steps object is the dependency\n * ```tsx\n * function MyComponent() {\n * const bounce = useKeyframes({\n * '0%': { transform: 'scale(1)' },\n * '50%': { transform: 'scale(1.1)' },\n * '100%': { transform: 'scale(1)' },\n * });\n *\n * return <div style={{ animation: `${bounce} 1s infinite` }}>Bouncing</div>;\n * }\n * ```\n *\n * @example With custom name\n * ```tsx\n * function MyComponent() {\n * const fadeIn = useKeyframes(\n * { from: { opacity: 0 }, to: { opacity: 1 } },\n * { name: 'fadeIn' }\n * );\n *\n * return <div style={{ animation: `${fadeIn} 0.3s ease-out` }}>Fading in</div>;\n * }\n * ```\n *\n * @example Factory function with dependencies\n * ```tsx\n * function MyComponent({ scale }: { scale: number }) {\n * const pulse = useKeyframes(\n * () => ({\n * '0%': { transform: 'scale(1)' },\n * '100%': { transform: `scale(${scale})` },\n * }),\n * [scale]\n * );\n *\n * return <div style={{ animation: `${pulse} 1s infinite` }}>Pulsing</div>;\n * }\n * ```\n */\n\n// Overload 1: Static steps object\nexport function useKeyframes(\n steps: KeyframesSteps,\n options?: UseKeyframesOptions,\n): string;\n\n// Overload 2: Factory function with dependencies\nexport function useKeyframes(\n factory: () => KeyframesSteps,\n deps: readonly unknown[],\n options?: UseKeyframesOptions,\n): string;\n\n// Implementation\nexport function useKeyframes(\n stepsOrFactory: KeyframesSteps | (() => KeyframesSteps),\n depsOrOptions?: readonly unknown[] | UseKeyframesOptions,\n options?: UseKeyframesOptions,\n): string {\n // Detect which overload is being used\n const isFactory = typeof stepsOrFactory === 'function';\n\n // Parse arguments based on overload\n const deps =\n isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : undefined;\n const opts = isFactory\n ? options\n : (depsOrOptions as UseKeyframesOptions | undefined);\n\n // Memoize the keyframes steps to get a stable reference\n const stepsData = useMemo(\n () => {\n const steps = isFactory\n ? (stepsOrFactory as () => KeyframesSteps)()\n : (stepsOrFactory as KeyframesSteps);\n\n if (!steps || Object.keys(steps).length === 0) {\n return null;\n }\n\n return steps;\n },\n\n isFactory ? (deps ?? []) : [stepsOrFactory],\n );\n\n // Store keyframes results for cleanup - we need to track both the render-time\n // injection (for the name) and the effect-time injection (for Strict Mode safety)\n const renderResultRef = useRef<KeyframesResult | null>(null);\n const effectResultRef = useRef<KeyframesResult | null>(null);\n\n // Inject keyframes during render to ensure the animation name is available\n // immediately. The keyframes() function uses reference counting internally,\n // so multiple calls with the same content are deduplicated.\n const name = useMemo(() => {\n // Dispose previous render-time result if deps changed\n renderResultRef.current?.dispose();\n renderResultRef.current = null;\n\n if (!stepsData) {\n return '';\n }\n\n // Inject keyframes synchronously\n const result = keyframes(stepsData, {\n name: opts?.name,\n root: opts?.root,\n });\n\n renderResultRef.current = result;\n\n return result.toString();\n }, [stepsData, opts?.name, opts?.root]);\n\n // Handle injection and cleanup in useInsertionEffect to properly support\n // React 18+ Strict Mode double-invocation (mount → unmount → mount).\n // The effect setup re-injects the keyframes if cleanup was called, ensuring\n // the CSS exists after Strict Mode remounts.\n useInsertionEffect(() => {\n // Dispose previous effect-time result\n effectResultRef.current?.dispose();\n effectResultRef.current = null;\n\n // Re-inject keyframes. This ensures the CSS exists after Strict Mode cleanup.\n // The keyframes() function uses reference counting, so this is idempotent\n // if the CSS wasn't disposed.\n if (stepsData) {\n const result = keyframes(stepsData, {\n name: opts?.name,\n root: opts?.root,\n });\n effectResultRef.current = result;\n }\n\n // Cleanup on unmount or when dependencies change.\n // Dispose both the effect-time and render-time results to properly\n // decrement the reference count.\n return () => {\n effectResultRef.current?.dispose();\n effectResultRef.current = null;\n renderResultRef.current?.dispose();\n renderResultRef.current = null;\n };\n }, [stepsData, opts?.name, opts?.root]);\n\n return name;\n}\n"],"mappings":";;;;AAqEA,SAAgB,aACd,gBACA,eACA,SACQ;CAER,MAAM,YAAY,OAAO,mBAAmB;CAG5C,MAAM,OACJ,aAAa,MAAM,QAAQ,cAAc,GAAG,gBAAgB;CAC9D,MAAM,OAAO,YACT,UACC;CAGL,MAAM,YAAY,cACV;EACJ,MAAM,QAAQ,YACT,gBAAyC,GACzC;AAEL,MAAI,CAAC,SAAS,OAAO,KAAK,MAAM,CAAC,WAAW,EAC1C,QAAO;AAGT,SAAO;IAGT,YAAa,QAAQ,EAAE,GAAI,CAAC,eAAe,CAC5C;CAID,MAAM,kBAAkB,OAA+B,KAAK;CAC5D,MAAM,kBAAkB,OAA+B,KAAK;CAK5D,MAAM,OAAO,cAAc;AAEzB,kBAAgB,SAAS,SAAS;AAClC,kBAAgB,UAAU;AAE1B,MAAI,CAAC,UACH,QAAO;EAIT,MAAM,SAAS,UAAU,WAAW;GAClC,MAAM,MAAM;GACZ,MAAM,MAAM;GACb,CAAC;AAEF,kBAAgB,UAAU;AAE1B,SAAO,OAAO,UAAU;IACvB;EAAC;EAAW,MAAM;EAAM,MAAM;EAAK,CAAC;AAMvC,0BAAyB;AAEvB,kBAAgB,SAAS,SAAS;AAClC,kBAAgB,UAAU;AAK1B,MAAI,UAKF,iBAAgB,UAJD,UAAU,WAAW;GAClC,MAAM,MAAM;GACZ,MAAM,MAAM;GACb,CAAC;AAOJ,eAAa;AACX,mBAAgB,SAAS,SAAS;AAClC,mBAAgB,UAAU;AAC1B,mBAAgB,SAAS,SAAS;AAClC,mBAAgB,UAAU;;IAE3B;EAAC;EAAW,MAAM;EAAM,MAAM;EAAK,CAAC;AAEvC,QAAO"}
|