@enadhq/enad-react-sdk 1.2.0 → 1.3.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.
Files changed (85) hide show
  1. package/dist/client/storefront/blocks/card-video.mjs +1 -1
  2. package/dist/client/storefront/blocks/card-video.mjs.map +1 -1
  3. package/dist/client/storefront/blocks/gallery-with-link-blocks.d.ts.map +1 -1
  4. package/dist/client/storefront/blocks/gallery-with-link-blocks.mjs +13 -5
  5. package/dist/client/storefront/blocks/gallery-with-link-blocks.mjs.map +1 -1
  6. package/dist/client/storefront/blocks/gallery.d.ts +10 -1
  7. package/dist/client/storefront/blocks/gallery.d.ts.map +1 -1
  8. package/dist/client/storefront/blocks/gallery.mjs +51 -27
  9. package/dist/client/storefront/blocks/gallery.mjs.map +1 -1
  10. package/dist/client/storefront/blocks/hero.d.ts +12 -1
  11. package/dist/client/storefront/blocks/hero.d.ts.map +1 -1
  12. package/dist/client/storefront/blocks/hero.mjs +143 -145
  13. package/dist/client/storefront/blocks/hero.mjs.map +1 -1
  14. package/dist/client/storefront/blocks/link-block-small.d.ts.map +1 -1
  15. package/dist/client/storefront/blocks/link-block-small.mjs +1 -1
  16. package/dist/client/storefront/blocks/link-block-small.mjs.map +1 -1
  17. package/dist/client/storefront/blocks/link-block.d.ts.map +1 -1
  18. package/dist/client/storefront/blocks/link-block.mjs +4 -4
  19. package/dist/client/storefront/blocks/link-block.mjs.map +1 -1
  20. package/dist/client/storefront/blocks/product-card-parts.d.ts +1 -1
  21. package/dist/client/storefront/blocks/product-card-parts.d.ts.map +1 -1
  22. package/dist/client/storefront/blocks/product-card-parts.mjs +2 -2
  23. package/dist/client/storefront/blocks/product-card-parts.mjs.map +1 -1
  24. package/dist/client/storefront/blocks/product-card.d.ts +10 -1
  25. package/dist/client/storefront/blocks/product-card.d.ts.map +1 -1
  26. package/dist/client/storefront/blocks/product-card.mjs +122 -116
  27. package/dist/client/storefront/blocks/product-card.mjs.map +1 -1
  28. package/dist/client/storefront/blocks/product-image.mjs +2 -2
  29. package/dist/client/storefront/blocks/product-image.mjs.map +1 -1
  30. package/dist/client/storefront/blocks/text-content-with-image.d.ts +14 -1
  31. package/dist/client/storefront/blocks/text-content-with-image.d.ts.map +1 -1
  32. package/dist/client/storefront/blocks/text-content-with-image.mjs +141 -164
  33. package/dist/client/storefront/blocks/text-content-with-image.mjs.map +1 -1
  34. package/dist/client/storefront/carousel/swipeable-carousel.d.ts +5 -1
  35. package/dist/client/storefront/carousel/swipeable-carousel.d.ts.map +1 -1
  36. package/dist/client/storefront/carousel/swipeable-carousel.mjs +2 -1
  37. package/dist/client/storefront/carousel/swipeable-carousel.mjs.map +1 -1
  38. package/dist/client/storefront/components/product-recommendations.d.ts.map +1 -1
  39. package/dist/client/storefront/components/product-recommendations.mjs +29 -37
  40. package/dist/client/storefront/components/product-recommendations.mjs.map +1 -1
  41. package/dist/client/storefront/filters/filter-chip.d.ts +5 -2
  42. package/dist/client/storefront/filters/filter-chip.d.ts.map +1 -1
  43. package/dist/client/storefront/filters/filter-chip.mjs +5 -3
  44. package/dist/client/storefront/filters/filter-chip.mjs.map +1 -1
  45. package/dist/client/storefront/filters/filter-panel.mjs +1 -1
  46. package/dist/client/storefront/index.d.ts +12 -1
  47. package/dist/client/storefront/index.mjs +12 -1
  48. package/dist/client/storefront/layout/header.d.ts.map +1 -1
  49. package/dist/client/storefront/layout/header.mjs +1 -1
  50. package/dist/client/storefront/layout/header.mjs.map +1 -1
  51. package/dist/client/storefront/layout/mobile-menu-drawer.mjs +1 -1
  52. package/dist/client/storefront/primitives/block-heading.d.ts +40 -0
  53. package/dist/client/storefront/primitives/block-heading.d.ts.map +1 -0
  54. package/dist/client/storefront/primitives/block-heading.mjs +43 -0
  55. package/dist/client/storefront/primitives/block-heading.mjs.map +1 -0
  56. package/dist/client/storefront/primitives/cta-group.d.ts +25 -0
  57. package/dist/client/storefront/primitives/cta-group.d.ts.map +1 -0
  58. package/dist/client/storefront/primitives/cta-group.mjs +27 -0
  59. package/dist/client/storefront/primitives/cta-group.mjs.map +1 -0
  60. package/dist/client/storefront/primitives/image-with-hover.d.ts +18 -0
  61. package/dist/client/storefront/primitives/image-with-hover.d.ts.map +1 -0
  62. package/dist/client/storefront/primitives/image-with-hover.mjs +16 -0
  63. package/dist/client/storefront/primitives/image-with-hover.mjs.map +1 -0
  64. package/dist/client/storefront/primitives/index.d.ts +4 -1
  65. package/dist/client/storefront/primitives/index.mjs +4 -1
  66. package/dist/client/theme/apply.d.ts +1 -1
  67. package/dist/client/theme/apply.d.ts.map +1 -1
  68. package/dist/client/theme/apply.mjs +0 -12
  69. package/dist/client/theme/apply.mjs.map +1 -1
  70. package/dist/client/theme/cli.mjs +0 -16
  71. package/dist/client/theme/cli.mjs.map +1 -1
  72. package/dist/client/theme/codec.d.ts.map +1 -1
  73. package/dist/client/theme/codec.mjs +0 -2
  74. package/dist/client/theme/codec.mjs.map +1 -1
  75. package/dist/client/theme/defaults.d.ts +0 -2
  76. package/dist/client/theme/defaults.mjs +0 -2
  77. package/dist/client/theme/defaults.mjs.map +1 -1
  78. package/dist/client/ui/carousel.d.ts +9 -1
  79. package/dist/client/ui/carousel.d.ts.map +1 -1
  80. package/dist/client/ui/carousel.mjs +18 -2
  81. package/dist/client/ui/carousel.mjs.map +1 -1
  82. package/dist/client/ui-resolver/index.d.ts +2 -2
  83. package/dist/client/ui-resolver/index.mjs +2 -2
  84. package/dist/styles.css +1 -1
  85. package/package.json +3 -2
@@ -1 +1 @@
1
- {"version":3,"file":"apply.mjs","names":[],"sources":["../../../src/client/theme/apply.ts"],"sourcesContent":["import type { ThemeHash } from './codec';\nimport { SDK_DEFAULTS, TOKEN_DEFAULTS } from './defaults';\n\nexport interface GenerateOptions {\n colorSpace?: 'hsl' | 'oklch';\n}\n\nfunction buildLightColorDiffs(hash: ThemeHash): string[] {\n const lightOverrides = (hash.lightColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(lightOverrides)) {\n if (value !== SDK_DEFAULTS.light[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildBaseTokenDiffs(hash: ThemeHash): string[] {\n const diffs: string[] = [];\n\n const radius = hash.radius as number | undefined;\n if (radius != null && radius !== 0.75) {\n diffs.push(` --radius: ${radius}rem;`);\n }\n\n const fontBody = hash.fontBody as string | undefined;\n if (fontBody && fontBody !== 'Inter') {\n diffs.push(` --font-sans: \"${fontBody}\", sans-serif;`);\n }\n\n const fontHeading = hash.fontHeading as string | undefined;\n if (fontHeading && fontHeading !== fontBody && fontHeading !== 'Inter') {\n diffs.push(` --font-heading: \"${fontHeading}\", sans-serif;`);\n }\n\n return diffs;\n}\n\nfunction buildDarkColorDiffs(hash: ThemeHash): string[] {\n const darkOverrides = (hash.darkColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(darkOverrides)) {\n if (value !== SDK_DEFAULTS.dark[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildTokenSections(hash: ThemeHash): string[] {\n const lines: string[] = [];\n\n const tokenSections: Array<{ comment: string; entries: Array<[string, string, string]> }> = [\n {\n comment: 'Shape',\n entries: [\n ['--enad-button-radius', hash.buttonRadius as string, TOKEN_DEFAULTS.buttonRadius],\n ['--enad-card-radius', hash.cardRadius as string, TOKEN_DEFAULTS.cardRadius],\n ['--enad-input-radius', hash.inputRadius as string, TOKEN_DEFAULTS.inputRadius],\n ['--enad-image-radius', hash.imageRadius as string, TOKEN_DEFAULTS.imageRadius],\n ],\n },\n {\n comment: 'Depth',\n entries: [\n ['--enad-shadow-color', hash.shadowColor as string, TOKEN_DEFAULTS.shadowColor],\n ],\n },\n {\n comment: 'Typography',\n entries: [\n ['--enad-heading-weight', hash.headingWeight as string, TOKEN_DEFAULTS.headingWeight],\n ['--enad-heading-tracking', hash.headingTracking as string, TOKEN_DEFAULTS.headingTracking],\n ['--enad-body-weight', hash.bodyWeight as string, TOKEN_DEFAULTS.bodyWeight],\n ['--enad-heading-transform', hash.headingTransform as string, TOKEN_DEFAULTS.headingTransform],\n ],\n },\n {\n comment: 'Borders',\n entries: [\n ['--enad-border-width', hash.borderWidth as string, TOKEN_DEFAULTS.borderWidth],\n ],\n },\n {\n comment: 'Overlay',\n entries: [\n ['--enad-overlay-color', hash.overlayColor as string, TOKEN_DEFAULTS.overlayColor],\n ['--enad-overlay-from-opacity', hash.overlayFromOpacity as string, TOKEN_DEFAULTS.overlayFromOpacity],\n ['--enad-overlay-to-opacity', hash.overlayToOpacity as string, TOKEN_DEFAULTS.overlayToOpacity],\n ],\n },\n {\n comment: 'Motion',\n entries: [\n ['--enad-image-hover-scale', hash.imageHoverScale as string, TOKEN_DEFAULTS.imageHoverScale],\n ['--enad-image-hover-duration', hash.imageHoverDuration as string, TOKEN_DEFAULTS.imageHoverDuration],\n ],\n },\n ];\n\n for (const section of tokenSections) {\n const sectionLines: string[] = [];\n for (const [varName, value, defaultValue] of section.entries) {\n if (value != null && value !== defaultValue) {\n sectionLines.push(` ${varName}: ${value};`);\n }\n }\n if (sectionLines.length > 0) {\n lines.push('', `/* ${section.comment} */`, ':root {', ...sectionLines, '}');\n }\n }\n\n return lines;\n}\n\n/**\n * Generate CSS variable declarations from a decoded theme hash.\n * Merges the hash onto SDK defaults and only emits values that differ.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function generateThemeCSS(hash: ThemeHash, _options: GenerateOptions = {}): string {\n const lines: string[] = ['/* Generated by enad-theme */'];\n\n // --- Light color diffs + base tokens ---\n const lightDiffs = [...buildLightColorDiffs(hash), ...buildBaseTokenDiffs(hash)];\n if (lightDiffs.length > 0) {\n lines.push('', ':root {', ...lightDiffs, '}');\n }\n\n // --- Dark color diffs ---\n const darkDiffs = buildDarkColorDiffs(hash);\n if (darkDiffs.length > 0) {\n lines.push('', '.dark {', ...darkDiffs, '}');\n }\n\n // --- Token sections ---\n lines.push(...buildTokenSections(hash));\n\n return lines.join('\\n');\n}\n\n// Sentinel markers for idempotent file writes\nexport const MARKER_START = '/* === enad-theme:start === */';\nexport const MARKER_END = '/* === enad-theme:end === */';\n\n/**\n * Insert or replace a theme block in a CSS file's content.\n * Returns the updated file content.\n */\nexport function applyThemeBlock(fileContent: string, cssBlock: string): string {\n const startIdx = fileContent.indexOf(MARKER_START);\n const endIdx = fileContent.indexOf(MARKER_END);\n\n const block = `${MARKER_START}\\n${cssBlock}\\n${MARKER_END}`;\n\n if (startIdx !== -1 && endIdx !== -1) {\n return fileContent.slice(0, startIdx) + block + fileContent.slice(endIdx + MARKER_END.length);\n }\n\n // Append with a blank line separator\n const separator = fileContent.length > 0 && !fileContent.endsWith('\\n') ? '\\n' : '';\n return fileContent + separator + '\\n' + block + '\\n';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"apply.mjs","names":[],"sources":["../../../src/client/theme/apply.ts"],"sourcesContent":["import type { ThemeHash } from \"./codec\";\nimport { SDK_DEFAULTS, TOKEN_DEFAULTS } from \"./defaults\";\n\nexport interface GenerateOptions {\n colorSpace?: \"hsl\" | \"oklch\";\n}\n\nfunction buildLightColorDiffs(hash: ThemeHash): string[] {\n const lightOverrides = (hash.lightColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(lightOverrides)) {\n if (value !== SDK_DEFAULTS.light[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildBaseTokenDiffs(hash: ThemeHash): string[] {\n const diffs: string[] = [];\n\n const radius = hash.radius as number | undefined;\n if (radius != null && radius !== 0.75) {\n diffs.push(` --radius: ${radius}rem;`);\n }\n\n const fontBody = hash.fontBody as string | undefined;\n if (fontBody && fontBody !== \"Inter\") {\n diffs.push(` --font-sans: \"${fontBody}\", sans-serif;`);\n }\n\n const fontHeading = hash.fontHeading as string | undefined;\n if (fontHeading && fontHeading !== fontBody && fontHeading !== \"Inter\") {\n diffs.push(` --font-heading: \"${fontHeading}\", sans-serif;`);\n }\n\n return diffs;\n}\n\nfunction buildDarkColorDiffs(hash: ThemeHash): string[] {\n const darkOverrides = (hash.darkColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(darkOverrides)) {\n if (value !== SDK_DEFAULTS.dark[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildTokenSections(hash: ThemeHash): string[] {\n const lines: string[] = [];\n\n const tokenSections: Array<{ comment: string; entries: Array<[string, string, string]> }> = [\n {\n comment: \"Shape\",\n entries: [\n [\"--enad-button-radius\", hash.buttonRadius as string, TOKEN_DEFAULTS.buttonRadius],\n [\"--enad-card-radius\", hash.cardRadius as string, TOKEN_DEFAULTS.cardRadius],\n [\"--enad-input-radius\", hash.inputRadius as string, TOKEN_DEFAULTS.inputRadius],\n [\"--enad-image-radius\", hash.imageRadius as string, TOKEN_DEFAULTS.imageRadius],\n ],\n },\n {\n comment: \"Depth\",\n entries: [[\"--enad-shadow-color\", hash.shadowColor as string, TOKEN_DEFAULTS.shadowColor]],\n },\n {\n comment: \"Typography\",\n entries: [\n [\"--enad-heading-weight\", hash.headingWeight as string, TOKEN_DEFAULTS.headingWeight],\n [\"--enad-heading-tracking\", hash.headingTracking as string, TOKEN_DEFAULTS.headingTracking],\n [\"--enad-body-weight\", hash.bodyWeight as string, TOKEN_DEFAULTS.bodyWeight],\n [\n \"--enad-heading-transform\",\n hash.headingTransform as string,\n TOKEN_DEFAULTS.headingTransform,\n ],\n ],\n },\n {\n comment: \"Borders\",\n entries: [[\"--enad-border-width\", hash.borderWidth as string, TOKEN_DEFAULTS.borderWidth]],\n },\n {\n comment: \"Overlay\",\n entries: [\n [\"--enad-overlay-color\", hash.overlayColor as string, TOKEN_DEFAULTS.overlayColor],\n [\n \"--enad-overlay-from-opacity\",\n hash.overlayFromOpacity as string,\n TOKEN_DEFAULTS.overlayFromOpacity,\n ],\n [\n \"--enad-overlay-to-opacity\",\n hash.overlayToOpacity as string,\n TOKEN_DEFAULTS.overlayToOpacity,\n ],\n ],\n },\n ];\n\n for (const section of tokenSections) {\n const sectionLines: string[] = [];\n for (const [varName, value, defaultValue] of section.entries) {\n if (value != null && value !== defaultValue) {\n sectionLines.push(` ${varName}: ${value};`);\n }\n }\n if (sectionLines.length > 0) {\n lines.push(\"\", `/* ${section.comment} */`, \":root {\", ...sectionLines, \"}\");\n }\n }\n\n return lines;\n}\n\n/**\n * Generate CSS variable declarations from a decoded theme hash.\n * Merges the hash onto SDK defaults and only emits values that differ.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function generateThemeCSS(hash: ThemeHash, _options: GenerateOptions = {}): string {\n const lines: string[] = [\"/* Generated by enad-theme */\"];\n\n // --- Light color diffs + base tokens ---\n const lightDiffs = [...buildLightColorDiffs(hash), ...buildBaseTokenDiffs(hash)];\n if (lightDiffs.length > 0) {\n lines.push(\"\", \":root {\", ...lightDiffs, \"}\");\n }\n\n // --- Dark color diffs ---\n const darkDiffs = buildDarkColorDiffs(hash);\n if (darkDiffs.length > 0) {\n lines.push(\"\", \".dark {\", ...darkDiffs, \"}\");\n }\n\n // --- Token sections ---\n lines.push(...buildTokenSections(hash));\n\n return lines.join(\"\\n\");\n}\n\n// Sentinel markers for idempotent file writes\nexport const MARKER_START = \"/* === enad-theme:start === */\";\nexport const MARKER_END = \"/* === enad-theme:end === */\";\n\n/**\n * Insert or replace a theme block in a CSS file's content.\n * Returns the updated file content.\n */\nexport function applyThemeBlock(fileContent: string, cssBlock: string): string {\n const startIdx = fileContent.indexOf(MARKER_START);\n const endIdx = fileContent.indexOf(MARKER_END);\n\n const block = `${MARKER_START}\\n${cssBlock}\\n${MARKER_END}`;\n\n if (startIdx !== -1 && endIdx !== -1) {\n return fileContent.slice(0, startIdx) + block + fileContent.slice(endIdx + MARKER_END.length);\n }\n\n // Append with a blank line separator\n const separator = fileContent.length > 0 && !fileContent.endsWith(\"\\n\") ? \"\\n\" : \"\";\n return fileContent + separator + \"\\n\" + block + \"\\n\";\n}\n"],"mappings":"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"}
@@ -32,8 +32,6 @@ const KEY_MAP = {
32
32
  oc: "overlayColor",
33
33
  of: "overlayFromOpacity",
34
34
  ot: "overlayToOpacity",
35
- hs: "imageHoverScale",
36
- hd: "imageHoverDuration",
37
35
  v: "variants",
38
36
  xs: "componentSet"
39
37
  };
@@ -149,8 +147,6 @@ const TOKEN_DEFAULTS = {
149
147
  overlayColor: "black",
150
148
  overlayFromOpacity: "0.5",
151
149
  overlayToOpacity: "0",
152
- imageHoverScale: "1.05",
153
- imageHoverDuration: "300ms",
154
150
  variants: {
155
151
  hero: "overlay",
156
152
  linkBlock: "overlay",
@@ -272,18 +268,6 @@ function buildTokenSections(hash) {
272
268
  TOKEN_DEFAULTS.overlayToOpacity
273
269
  ]
274
270
  ]
275
- },
276
- {
277
- comment: "Motion",
278
- entries: [[
279
- "--enad-image-hover-scale",
280
- hash.imageHoverScale,
281
- TOKEN_DEFAULTS.imageHoverScale
282
- ], [
283
- "--enad-image-hover-duration",
284
- hash.imageHoverDuration,
285
- TOKEN_DEFAULTS.imageHoverDuration
286
- ]]
287
271
  }
288
272
  ];
289
273
  for (const section of tokenSections) {
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["msgpackDecode"],"sources":["../../../src/client/theme/codec.ts","../../../src/client/theme/defaults.ts","../../../src/client/theme/apply.ts","../../../src/client/theme/scan.ts","../../../src/client/theme/cli.ts"],"sourcesContent":["import { decode as msgpackDecode, encode as msgpackEncode } from \"@msgpack/msgpack\";\n\n/**\n * Loose bag of theme values. The decoder returns whatever keys are present;\n * the consumer merges onto its own defaults. This keeps the format\n * forward/backward compatible: new keys are silently ignored by old decoders,\n * and removed keys are filled from defaults by the consumer.\n */\nexport type ThemeHash = Record<string, unknown>;\n\n// Current format version. Prepended as a single ASCII char before the base64url payload.\nconst FORMAT_VERSION = \"1\";\n\n// ---------------------------------------------------------------------------\n// Short key mapping\n// ---------------------------------------------------------------------------\n\n/** Short key -> full key */\nexport const KEY_MAP: Record<string, string> = {\n c: \"colorize\",\n i: \"intensity\",\n d: \"delight\",\n r: \"radius\",\n bf: \"fontBody\",\n hf: \"fontHeading\",\n ic: \"iconSet\",\n cs: \"colorSpace\",\n dm: \"darkMode\",\n lc: \"lightColors\",\n dc: \"darkColors\",\n br: \"buttonRadius\",\n cr: \"cardRadius\",\n ir: \"inputRadius\",\n mr: \"imageRadius\",\n sp: \"shadowPreset\",\n sc: \"shadowColor\",\n hw: \"headingWeight\",\n ht: \"headingTracking\",\n bw: \"bodyWeight\",\n hx: \"headingTransform\",\n bdr: \"borderWidth\",\n oc: \"overlayColor\",\n of: \"overlayFromOpacity\",\n ot: \"overlayToOpacity\",\n hs: \"imageHoverScale\",\n hd: \"imageHoverDuration\",\n v: \"variants\",\n xs: \"componentSet\",\n};\n\n/** Full key -> short key (inverse of KEY_MAP) */\nexport const KEY_MAP_REVERSE: Record<string, string> = Object.fromEntries(\n Object.entries(KEY_MAP).map(([short, full]) => [full, short]),\n);\n\n// ---------------------------------------------------------------------------\n// Base64url helpers (RFC 4648 §5, no padding)\n// ---------------------------------------------------------------------------\n\nfunction toBase64url(buf: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < buf.length; i++) {\n binary += String.fromCharCode(buf[i]!);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64url(str: string): Uint8Array {\n const base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = base64 + \"=\".repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Encode / Decode\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a theme state diff into a portable hash string.\n *\n * The input should only contain non-default values. The encoder maps\n * full keys to short keys, packs with MessagePack, and encodes as\n * `<version><base64url>`.\n */\nexport function encodeThemeHash(diff: ThemeHash): string {\n const compact: Record<string, unknown> = {};\n for (const [fullKey, value] of Object.entries(diff)) {\n const shortKey = KEY_MAP_REVERSE[fullKey] ?? fullKey;\n compact[shortKey] = value;\n }\n const packed = msgpackEncode(compact);\n return FORMAT_VERSION + toBase64url(packed);\n}\n\n/**\n * Decode a hash string back into a key-value bag with full key names.\n *\n * Returns partial data -- the consumer is responsible for merging onto\n * its own defaults. Unknown keys are preserved for forward compatibility.\n * Returns an empty object on invalid input (never throws).\n */\nexport function decodeThemeHash(hash: string): ThemeHash {\n try {\n if (!hash || hash.length < 2) return {};\n\n const version = hash[0];\n if (version !== FORMAT_VERSION) return {};\n\n const payload = hash.slice(1);\n const bytes = fromBase64url(payload);\n const compact = msgpackDecode(bytes) as Record<string, unknown>;\n\n if (typeof compact !== \"object\" || compact === null) return {};\n\n const result: ThemeHash = {};\n for (const [key, value] of Object.entries(compact)) {\n const fullKey = KEY_MAP[key] ?? key;\n result[fullKey] = value;\n }\n return result;\n } catch {\n return {};\n }\n}\n","/**\n * SDK default color tokens. These are the canonical defaults used by\n * the theme codec to compute diffs (only non-default values are encoded).\n */\nexport const SDK_DEFAULTS = {\n light: {\n \"--background\": \"#ffffff\",\n \"--foreground\": \"#111827\",\n \"--card\": \"#ffffff\",\n \"--card-foreground\": \"#111827\",\n \"--popover\": \"#ffffff\",\n \"--popover-foreground\": \"#111827\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#f1f5f9\",\n \"--secondary-foreground\": \"#0f172a\",\n \"--muted\": \"#f3f4f6\",\n \"--muted-foreground\": \"#636b78\",\n \"--accent\": \"#dbeafe\",\n \"--accent-foreground\": \"#1e3a8a\",\n \"--accent-muted\": \"#eff6ff\",\n \"--accent-muted-foreground\": \"#1e40af\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#e5e7eb\",\n \"--input\": \"#e5e7eb\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n dark: {\n \"--background\": \"#111827\",\n \"--foreground\": \"#f9fafb\",\n \"--card\": \"#1f2937\",\n \"--card-foreground\": \"#f9fafb\",\n \"--popover\": \"#1f2937\",\n \"--popover-foreground\": \"#f9fafb\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#1e293b\",\n \"--secondary-foreground\": \"#e2e8f0\",\n \"--muted\": \"#374151\",\n \"--muted-foreground\": \"#b0b8c4\",\n \"--accent\": \"#1e3a5f\",\n \"--accent-foreground\": \"#bfdbfe\",\n \"--accent-muted\": \"#172554\",\n \"--accent-muted-foreground\": \"#93c5fd\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#374151\",\n \"--input\": \"#374151\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n};\n\n/** Default values for design tokens (non-color). */\nexport const TOKEN_DEFAULTS = {\n colorize: 50,\n intensity: 50,\n delight: 50,\n radius: 0.75,\n fontBody: \"Inter\",\n fontHeading: \"Inter\",\n iconSet: \"Phosphor\",\n colorSpace: \"hsl\",\n darkMode: false,\n componentSet: \"default\" as const,\n\n // Shape\n buttonRadius: \"9999px\",\n cardRadius: \"var(--radius)\",\n inputRadius: \"var(--radius)\",\n imageRadius: \"0px\",\n\n // Depth\n shadowPreset: \"none\",\n shadowColor: \"oklch(0 0 0 / 0.1)\",\n\n // Typography\n headingWeight: \"600\",\n headingTracking: \"0em\",\n bodyWeight: \"400\",\n headingTransform: \"none\",\n\n // Border\n borderWidth: \"1px\",\n\n // Overlay\n overlayColor: \"black\",\n overlayFromOpacity: \"0.5\",\n overlayToOpacity: \"0\",\n\n // Motion\n imageHoverScale: \"1.05\",\n imageHoverDuration: \"300ms\",\n\n // Variants\n variants: {\n hero: \"overlay\",\n linkBlock: \"overlay\",\n productCard: \"gallery\",\n contentWithImage: \"side-by-side\",\n gallery: \"grid\",\n textContent: \"default\",\n },\n} as const;\n","import type { ThemeHash } from './codec';\nimport { SDK_DEFAULTS, TOKEN_DEFAULTS } from './defaults';\n\nexport interface GenerateOptions {\n colorSpace?: 'hsl' | 'oklch';\n}\n\nfunction buildLightColorDiffs(hash: ThemeHash): string[] {\n const lightOverrides = (hash.lightColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(lightOverrides)) {\n if (value !== SDK_DEFAULTS.light[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildBaseTokenDiffs(hash: ThemeHash): string[] {\n const diffs: string[] = [];\n\n const radius = hash.radius as number | undefined;\n if (radius != null && radius !== 0.75) {\n diffs.push(` --radius: ${radius}rem;`);\n }\n\n const fontBody = hash.fontBody as string | undefined;\n if (fontBody && fontBody !== 'Inter') {\n diffs.push(` --font-sans: \"${fontBody}\", sans-serif;`);\n }\n\n const fontHeading = hash.fontHeading as string | undefined;\n if (fontHeading && fontHeading !== fontBody && fontHeading !== 'Inter') {\n diffs.push(` --font-heading: \"${fontHeading}\", sans-serif;`);\n }\n\n return diffs;\n}\n\nfunction buildDarkColorDiffs(hash: ThemeHash): string[] {\n const darkOverrides = (hash.darkColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(darkOverrides)) {\n if (value !== SDK_DEFAULTS.dark[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildTokenSections(hash: ThemeHash): string[] {\n const lines: string[] = [];\n\n const tokenSections: Array<{ comment: string; entries: Array<[string, string, string]> }> = [\n {\n comment: 'Shape',\n entries: [\n ['--enad-button-radius', hash.buttonRadius as string, TOKEN_DEFAULTS.buttonRadius],\n ['--enad-card-radius', hash.cardRadius as string, TOKEN_DEFAULTS.cardRadius],\n ['--enad-input-radius', hash.inputRadius as string, TOKEN_DEFAULTS.inputRadius],\n ['--enad-image-radius', hash.imageRadius as string, TOKEN_DEFAULTS.imageRadius],\n ],\n },\n {\n comment: 'Depth',\n entries: [\n ['--enad-shadow-color', hash.shadowColor as string, TOKEN_DEFAULTS.shadowColor],\n ],\n },\n {\n comment: 'Typography',\n entries: [\n ['--enad-heading-weight', hash.headingWeight as string, TOKEN_DEFAULTS.headingWeight],\n ['--enad-heading-tracking', hash.headingTracking as string, TOKEN_DEFAULTS.headingTracking],\n ['--enad-body-weight', hash.bodyWeight as string, TOKEN_DEFAULTS.bodyWeight],\n ['--enad-heading-transform', hash.headingTransform as string, TOKEN_DEFAULTS.headingTransform],\n ],\n },\n {\n comment: 'Borders',\n entries: [\n ['--enad-border-width', hash.borderWidth as string, TOKEN_DEFAULTS.borderWidth],\n ],\n },\n {\n comment: 'Overlay',\n entries: [\n ['--enad-overlay-color', hash.overlayColor as string, TOKEN_DEFAULTS.overlayColor],\n ['--enad-overlay-from-opacity', hash.overlayFromOpacity as string, TOKEN_DEFAULTS.overlayFromOpacity],\n ['--enad-overlay-to-opacity', hash.overlayToOpacity as string, TOKEN_DEFAULTS.overlayToOpacity],\n ],\n },\n {\n comment: 'Motion',\n entries: [\n ['--enad-image-hover-scale', hash.imageHoverScale as string, TOKEN_DEFAULTS.imageHoverScale],\n ['--enad-image-hover-duration', hash.imageHoverDuration as string, TOKEN_DEFAULTS.imageHoverDuration],\n ],\n },\n ];\n\n for (const section of tokenSections) {\n const sectionLines: string[] = [];\n for (const [varName, value, defaultValue] of section.entries) {\n if (value != null && value !== defaultValue) {\n sectionLines.push(` ${varName}: ${value};`);\n }\n }\n if (sectionLines.length > 0) {\n lines.push('', `/* ${section.comment} */`, ':root {', ...sectionLines, '}');\n }\n }\n\n return lines;\n}\n\n/**\n * Generate CSS variable declarations from a decoded theme hash.\n * Merges the hash onto SDK defaults and only emits values that differ.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function generateThemeCSS(hash: ThemeHash, _options: GenerateOptions = {}): string {\n const lines: string[] = ['/* Generated by enad-theme */'];\n\n // --- Light color diffs + base tokens ---\n const lightDiffs = [...buildLightColorDiffs(hash), ...buildBaseTokenDiffs(hash)];\n if (lightDiffs.length > 0) {\n lines.push('', ':root {', ...lightDiffs, '}');\n }\n\n // --- Dark color diffs ---\n const darkDiffs = buildDarkColorDiffs(hash);\n if (darkDiffs.length > 0) {\n lines.push('', '.dark {', ...darkDiffs, '}');\n }\n\n // --- Token sections ---\n lines.push(...buildTokenSections(hash));\n\n return lines.join('\\n');\n}\n\n// Sentinel markers for idempotent file writes\nexport const MARKER_START = '/* === enad-theme:start === */';\nexport const MARKER_END = '/* === enad-theme:end === */';\n\n/**\n * Insert or replace a theme block in a CSS file's content.\n * Returns the updated file content.\n */\nexport function applyThemeBlock(fileContent: string, cssBlock: string): string {\n const startIdx = fileContent.indexOf(MARKER_START);\n const endIdx = fileContent.indexOf(MARKER_END);\n\n const block = `${MARKER_START}\\n${cssBlock}\\n${MARKER_END}`;\n\n if (startIdx !== -1 && endIdx !== -1) {\n return fileContent.slice(0, startIdx) + block + fileContent.slice(endIdx + MARKER_END.length);\n }\n\n // Append with a blank line separator\n const separator = fileContent.length > 0 && !fileContent.endsWith('\\n') ? '\\n' : '';\n return fileContent + separator + '\\n' + block + '\\n';\n}\n","export interface ConflictWarning {\n file: string;\n line: number;\n variable: string;\n existingValue: string;\n}\n\n/**\n * Scan CSS file contents for existing declarations of the given custom properties.\n * Uses simple regex matching -- no CSS parser needed since custom property\n * declarations have unambiguous syntax.\n */\nexport function scanConflicts(\n files: Array<{ path: string; content: string }>,\n variables: string[],\n): ConflictWarning[] {\n if (variables.length === 0) return [];\n\n const warnings: ConflictWarning[] = [];\n // Match lines like ` --variable-name: value;` or `--variable-name: value`\n const varSet = new Set(variables);\n\n for (const file of files) {\n const lines = file.content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const match = line.match(/^\\s*(--[\\w-]+)\\s*:\\s*(.+?)\\s*;?\\s*$/);\n if (match && varSet.has(match[1]!)) {\n warnings.push({\n file: file.path,\n line: i + 1,\n variable: match[1]!,\n existingValue: match[2]!,\n });\n }\n }\n }\n\n return warnings;\n}\n","import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';\nimport { resolve, join } from 'node:path';\nimport { argv, exit, stderr, stdout } from 'node:process';\n\nimport { decodeThemeHash } from './codec';\nimport { applyThemeBlock, generateThemeCSS, MARKER_START } from './apply';\nimport { scanConflicts } from './scan';\n\ninterface ParsedArgs {\n command: string;\n hash: string;\n target: string;\n colorSpace: 'hsl' | 'oklch';\n scanDir: string;\n dryRun: boolean;\n}\n\nfunction printUsage() {\n stderr.write(`\nUsage: enad-theme apply <hash> [options]\n\nOptions:\n --target <file> Target CSS file (default: src/globals.css)\n --color-space <cs> Color space: hsl | oklch (default: hsl)\n --scan <dir> Directory to scan for conflicts (default: src)\n --dry-run Print CSS to stdout without writing\n --help Show this help message\n\nExamples:\n enad-theme apply 1k5YWJj...\n enad-theme apply 1k5YWJj... --target app/globals.css --color-space oklch\n enad-theme apply 1k5YWJj... --dry-run\n`);\n}\n\nfunction parseArgs(args: string[]): ParsedArgs | null {\n if (args.length === 0 || args.includes('--help')) {\n printUsage();\n exit(0);\n }\n\n const command = args[0]!;\n if (command !== 'apply') {\n stderr.write(`Unknown command: ${command}\\nRun enad-theme --help for usage.\\n`);\n exit(1);\n }\n\n const hash = args[1];\n if (!hash) {\n stderr.write('Error: missing theme hash argument.\\n');\n exit(1);\n }\n\n let target = 'src/globals.css';\n let colorSpace: 'hsl' | 'oklch' = 'hsl';\n let scanDir = 'src';\n let dryRun = false;\n\n for (let i = 2; i < args.length; i++) {\n switch (args[i]) {\n case '--target':\n target = args[++i] ?? target;\n break;\n case '--color-space':\n colorSpace = (args[++i] as 'hsl' | 'oklch') ?? colorSpace;\n break;\n case '--scan':\n scanDir = args[++i] ?? scanDir;\n break;\n case '--dry-run':\n dryRun = true;\n break;\n }\n }\n\n return { command, hash, target, colorSpace, scanDir, dryRun };\n}\n\nfunction readExistingFile(targetPath: string): string {\n try {\n return readFileSync(targetPath, 'utf-8');\n } catch {\n // File doesn't exist yet, we'll create it\n return '';\n }\n}\n\nfunction writeOutput(targetPath: string, target: string, existing: string, css: string) {\n const updated = applyThemeBlock(existing, css);\n writeFileSync(targetPath, updated, 'utf-8');\n\n const isUpdate = existing.includes(MARKER_START);\n stderr.write(\n isUpdate\n ? `Updated theme block in ${target}\\n`\n : `Wrote theme block to ${target}\\n`,\n );\n}\n\nfunction scanForConflicts(scanDir: string, targetPath: string, css: string) {\n // Extract variable names from the generated CSS\n const varPattern = /^\\s*(--[\\w-]+)\\s*:/gm;\n const variables: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = varPattern.exec(css)) !== null) {\n variables.push(match[1]!);\n }\n\n if (variables.length === 0) return;\n\n // Simple recursive CSS file scan\n const cssFiles: Array<{ path: string; content: string }> = [];\n\n function walk(dir: string) {\n try {\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n try {\n const stat = statSync(full);\n if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {\n walk(full);\n } else if (stat.isFile() && full.endsWith('.css') && resolve(full) !== targetPath) {\n cssFiles.push({ path: full, content: readFileSync(full, 'utf-8') });\n }\n } catch {\n // Skip inaccessible files\n }\n }\n } catch {\n // Skip inaccessible directories\n }\n }\n\n walk(resolve(scanDir));\n\n const warnings = scanConflicts(cssFiles, variables);\n if (warnings.length > 0) {\n stderr.write(`\\nConflicting declarations found:\\n`);\n for (const w of warnings) {\n stderr.write(` ${w.file}:${w.line} ${w.variable}: ${w.existingValue}\\n`);\n }\n stderr.write(`\\nThese existing declarations may override or conflict with the theme.\\n`);\n }\n}\n\nfunction main() {\n const parsed = parseArgs(argv.slice(2));\n if (!parsed) return;\n\n const { hash, target, colorSpace, scanDir, dryRun } = parsed;\n\n // Decode\n const decoded = decodeThemeHash(hash);\n if (Object.keys(decoded).length === 0) {\n stderr.write('Error: failed to decode theme hash. Check that the hash is valid.\\n');\n exit(1);\n }\n\n // Generate CSS\n const css = generateThemeCSS(decoded, { colorSpace });\n\n if (dryRun) {\n stdout.write(css + '\\n');\n exit(0);\n }\n\n // Write to target file\n const targetPath = resolve(target);\n const existing = readExistingFile(targetPath);\n writeOutput(targetPath, target, existing, css);\n\n scanForConflicts(scanDir, targetPath, css);\n}\n\nmain();\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["msgpackDecode"],"sources":["../../../src/client/theme/codec.ts","../../../src/client/theme/defaults.ts","../../../src/client/theme/apply.ts","../../../src/client/theme/scan.ts","../../../src/client/theme/cli.ts"],"sourcesContent":["import { decode as msgpackDecode, encode as msgpackEncode } from \"@msgpack/msgpack\";\n\n/**\n * Loose bag of theme values. The decoder returns whatever keys are present;\n * the consumer merges onto its own defaults. This keeps the format\n * forward/backward compatible: new keys are silently ignored by old decoders,\n * and removed keys are filled from defaults by the consumer.\n */\nexport type ThemeHash = Record<string, unknown>;\n\n// Current format version. Prepended as a single ASCII char before the base64url payload.\nconst FORMAT_VERSION = \"1\";\n\n// ---------------------------------------------------------------------------\n// Short key mapping\n// ---------------------------------------------------------------------------\n\n/** Short key -> full key */\nexport const KEY_MAP: Record<string, string> = {\n c: \"colorize\",\n i: \"intensity\",\n d: \"delight\",\n r: \"radius\",\n bf: \"fontBody\",\n hf: \"fontHeading\",\n ic: \"iconSet\",\n cs: \"colorSpace\",\n dm: \"darkMode\",\n lc: \"lightColors\",\n dc: \"darkColors\",\n br: \"buttonRadius\",\n cr: \"cardRadius\",\n ir: \"inputRadius\",\n mr: \"imageRadius\",\n sp: \"shadowPreset\",\n sc: \"shadowColor\",\n hw: \"headingWeight\",\n ht: \"headingTracking\",\n bw: \"bodyWeight\",\n hx: \"headingTransform\",\n bdr: \"borderWidth\",\n oc: \"overlayColor\",\n of: \"overlayFromOpacity\",\n ot: \"overlayToOpacity\",\n v: \"variants\",\n xs: \"componentSet\",\n};\n\n/** Full key -> short key (inverse of KEY_MAP) */\nexport const KEY_MAP_REVERSE: Record<string, string> = Object.fromEntries(\n Object.entries(KEY_MAP).map(([short, full]) => [full, short]),\n);\n\n// ---------------------------------------------------------------------------\n// Base64url helpers (RFC 4648 §5, no padding)\n// ---------------------------------------------------------------------------\n\nfunction toBase64url(buf: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < buf.length; i++) {\n binary += String.fromCharCode(buf[i]!);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64url(str: string): Uint8Array {\n const base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = base64 + \"=\".repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Encode / Decode\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a theme state diff into a portable hash string.\n *\n * The input should only contain non-default values. The encoder maps\n * full keys to short keys, packs with MessagePack, and encodes as\n * `<version><base64url>`.\n */\nexport function encodeThemeHash(diff: ThemeHash): string {\n const compact: Record<string, unknown> = {};\n for (const [fullKey, value] of Object.entries(diff)) {\n const shortKey = KEY_MAP_REVERSE[fullKey] ?? fullKey;\n compact[shortKey] = value;\n }\n const packed = msgpackEncode(compact);\n return FORMAT_VERSION + toBase64url(packed);\n}\n\n/**\n * Decode a hash string back into a key-value bag with full key names.\n *\n * Returns partial data -- the consumer is responsible for merging onto\n * its own defaults. Unknown keys are preserved for forward compatibility.\n * Returns an empty object on invalid input (never throws).\n */\nexport function decodeThemeHash(hash: string): ThemeHash {\n try {\n if (!hash || hash.length < 2) return {};\n\n const version = hash[0];\n if (version !== FORMAT_VERSION) return {};\n\n const payload = hash.slice(1);\n const bytes = fromBase64url(payload);\n const compact = msgpackDecode(bytes) as Record<string, unknown>;\n\n if (typeof compact !== \"object\" || compact === null) return {};\n\n const result: ThemeHash = {};\n for (const [key, value] of Object.entries(compact)) {\n const fullKey = KEY_MAP[key] ?? key;\n result[fullKey] = value;\n }\n return result;\n } catch {\n return {};\n }\n}\n","/**\n * SDK default color tokens. These are the canonical defaults used by\n * the theme codec to compute diffs (only non-default values are encoded).\n */\nexport const SDK_DEFAULTS = {\n light: {\n \"--background\": \"#ffffff\",\n \"--foreground\": \"#111827\",\n \"--card\": \"#ffffff\",\n \"--card-foreground\": \"#111827\",\n \"--popover\": \"#ffffff\",\n \"--popover-foreground\": \"#111827\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#f1f5f9\",\n \"--secondary-foreground\": \"#0f172a\",\n \"--muted\": \"#f3f4f6\",\n \"--muted-foreground\": \"#636b78\",\n \"--accent\": \"#dbeafe\",\n \"--accent-foreground\": \"#1e3a8a\",\n \"--accent-muted\": \"#eff6ff\",\n \"--accent-muted-foreground\": \"#1e40af\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#e5e7eb\",\n \"--input\": \"#e5e7eb\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n dark: {\n \"--background\": \"#111827\",\n \"--foreground\": \"#f9fafb\",\n \"--card\": \"#1f2937\",\n \"--card-foreground\": \"#f9fafb\",\n \"--popover\": \"#1f2937\",\n \"--popover-foreground\": \"#f9fafb\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#1e293b\",\n \"--secondary-foreground\": \"#e2e8f0\",\n \"--muted\": \"#374151\",\n \"--muted-foreground\": \"#b0b8c4\",\n \"--accent\": \"#1e3a5f\",\n \"--accent-foreground\": \"#bfdbfe\",\n \"--accent-muted\": \"#172554\",\n \"--accent-muted-foreground\": \"#93c5fd\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#374151\",\n \"--input\": \"#374151\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n};\n\n/** Default values for design tokens (non-color). */\nexport const TOKEN_DEFAULTS = {\n colorize: 50,\n intensity: 50,\n delight: 50,\n radius: 0.75,\n fontBody: \"Inter\",\n fontHeading: \"Inter\",\n iconSet: \"Phosphor\",\n colorSpace: \"hsl\",\n darkMode: false,\n componentSet: \"default\" as const,\n\n // Shape\n buttonRadius: \"9999px\",\n cardRadius: \"var(--radius)\",\n inputRadius: \"var(--radius)\",\n imageRadius: \"0px\",\n\n // Depth\n shadowPreset: \"none\",\n shadowColor: \"oklch(0 0 0 / 0.1)\",\n\n // Typography\n headingWeight: \"600\",\n headingTracking: \"0em\",\n bodyWeight: \"400\",\n headingTransform: \"none\",\n\n // Border\n borderWidth: \"1px\",\n\n // Overlay\n overlayColor: \"black\",\n overlayFromOpacity: \"0.5\",\n overlayToOpacity: \"0\",\n\n // Variants\n variants: {\n hero: \"overlay\",\n linkBlock: \"overlay\",\n productCard: \"gallery\",\n contentWithImage: \"side-by-side\",\n gallery: \"grid\",\n textContent: \"default\",\n },\n} as const;\n","import type { ThemeHash } from \"./codec\";\nimport { SDK_DEFAULTS, TOKEN_DEFAULTS } from \"./defaults\";\n\nexport interface GenerateOptions {\n colorSpace?: \"hsl\" | \"oklch\";\n}\n\nfunction buildLightColorDiffs(hash: ThemeHash): string[] {\n const lightOverrides = (hash.lightColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(lightOverrides)) {\n if (value !== SDK_DEFAULTS.light[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildBaseTokenDiffs(hash: ThemeHash): string[] {\n const diffs: string[] = [];\n\n const radius = hash.radius as number | undefined;\n if (radius != null && radius !== 0.75) {\n diffs.push(` --radius: ${radius}rem;`);\n }\n\n const fontBody = hash.fontBody as string | undefined;\n if (fontBody && fontBody !== \"Inter\") {\n diffs.push(` --font-sans: \"${fontBody}\", sans-serif;`);\n }\n\n const fontHeading = hash.fontHeading as string | undefined;\n if (fontHeading && fontHeading !== fontBody && fontHeading !== \"Inter\") {\n diffs.push(` --font-heading: \"${fontHeading}\", sans-serif;`);\n }\n\n return diffs;\n}\n\nfunction buildDarkColorDiffs(hash: ThemeHash): string[] {\n const darkOverrides = (hash.darkColors ?? {}) as Record<string, string>;\n const diffs: string[] = [];\n for (const [token, value] of Object.entries(darkOverrides)) {\n if (value !== SDK_DEFAULTS.dark[token]) {\n diffs.push(` ${token}: ${value};`);\n }\n }\n return diffs;\n}\n\nfunction buildTokenSections(hash: ThemeHash): string[] {\n const lines: string[] = [];\n\n const tokenSections: Array<{ comment: string; entries: Array<[string, string, string]> }> = [\n {\n comment: \"Shape\",\n entries: [\n [\"--enad-button-radius\", hash.buttonRadius as string, TOKEN_DEFAULTS.buttonRadius],\n [\"--enad-card-radius\", hash.cardRadius as string, TOKEN_DEFAULTS.cardRadius],\n [\"--enad-input-radius\", hash.inputRadius as string, TOKEN_DEFAULTS.inputRadius],\n [\"--enad-image-radius\", hash.imageRadius as string, TOKEN_DEFAULTS.imageRadius],\n ],\n },\n {\n comment: \"Depth\",\n entries: [[\"--enad-shadow-color\", hash.shadowColor as string, TOKEN_DEFAULTS.shadowColor]],\n },\n {\n comment: \"Typography\",\n entries: [\n [\"--enad-heading-weight\", hash.headingWeight as string, TOKEN_DEFAULTS.headingWeight],\n [\"--enad-heading-tracking\", hash.headingTracking as string, TOKEN_DEFAULTS.headingTracking],\n [\"--enad-body-weight\", hash.bodyWeight as string, TOKEN_DEFAULTS.bodyWeight],\n [\n \"--enad-heading-transform\",\n hash.headingTransform as string,\n TOKEN_DEFAULTS.headingTransform,\n ],\n ],\n },\n {\n comment: \"Borders\",\n entries: [[\"--enad-border-width\", hash.borderWidth as string, TOKEN_DEFAULTS.borderWidth]],\n },\n {\n comment: \"Overlay\",\n entries: [\n [\"--enad-overlay-color\", hash.overlayColor as string, TOKEN_DEFAULTS.overlayColor],\n [\n \"--enad-overlay-from-opacity\",\n hash.overlayFromOpacity as string,\n TOKEN_DEFAULTS.overlayFromOpacity,\n ],\n [\n \"--enad-overlay-to-opacity\",\n hash.overlayToOpacity as string,\n TOKEN_DEFAULTS.overlayToOpacity,\n ],\n ],\n },\n ];\n\n for (const section of tokenSections) {\n const sectionLines: string[] = [];\n for (const [varName, value, defaultValue] of section.entries) {\n if (value != null && value !== defaultValue) {\n sectionLines.push(` ${varName}: ${value};`);\n }\n }\n if (sectionLines.length > 0) {\n lines.push(\"\", `/* ${section.comment} */`, \":root {\", ...sectionLines, \"}\");\n }\n }\n\n return lines;\n}\n\n/**\n * Generate CSS variable declarations from a decoded theme hash.\n * Merges the hash onto SDK defaults and only emits values that differ.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function generateThemeCSS(hash: ThemeHash, _options: GenerateOptions = {}): string {\n const lines: string[] = [\"/* Generated by enad-theme */\"];\n\n // --- Light color diffs + base tokens ---\n const lightDiffs = [...buildLightColorDiffs(hash), ...buildBaseTokenDiffs(hash)];\n if (lightDiffs.length > 0) {\n lines.push(\"\", \":root {\", ...lightDiffs, \"}\");\n }\n\n // --- Dark color diffs ---\n const darkDiffs = buildDarkColorDiffs(hash);\n if (darkDiffs.length > 0) {\n lines.push(\"\", \".dark {\", ...darkDiffs, \"}\");\n }\n\n // --- Token sections ---\n lines.push(...buildTokenSections(hash));\n\n return lines.join(\"\\n\");\n}\n\n// Sentinel markers for idempotent file writes\nexport const MARKER_START = \"/* === enad-theme:start === */\";\nexport const MARKER_END = \"/* === enad-theme:end === */\";\n\n/**\n * Insert or replace a theme block in a CSS file's content.\n * Returns the updated file content.\n */\nexport function applyThemeBlock(fileContent: string, cssBlock: string): string {\n const startIdx = fileContent.indexOf(MARKER_START);\n const endIdx = fileContent.indexOf(MARKER_END);\n\n const block = `${MARKER_START}\\n${cssBlock}\\n${MARKER_END}`;\n\n if (startIdx !== -1 && endIdx !== -1) {\n return fileContent.slice(0, startIdx) + block + fileContent.slice(endIdx + MARKER_END.length);\n }\n\n // Append with a blank line separator\n const separator = fileContent.length > 0 && !fileContent.endsWith(\"\\n\") ? \"\\n\" : \"\";\n return fileContent + separator + \"\\n\" + block + \"\\n\";\n}\n","export interface ConflictWarning {\n file: string;\n line: number;\n variable: string;\n existingValue: string;\n}\n\n/**\n * Scan CSS file contents for existing declarations of the given custom properties.\n * Uses simple regex matching -- no CSS parser needed since custom property\n * declarations have unambiguous syntax.\n */\nexport function scanConflicts(\n files: Array<{ path: string; content: string }>,\n variables: string[],\n): ConflictWarning[] {\n if (variables.length === 0) return [];\n\n const warnings: ConflictWarning[] = [];\n // Match lines like ` --variable-name: value;` or `--variable-name: value`\n const varSet = new Set(variables);\n\n for (const file of files) {\n const lines = file.content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const match = line.match(/^\\s*(--[\\w-]+)\\s*:\\s*(.+?)\\s*;?\\s*$/);\n if (match && varSet.has(match[1]!)) {\n warnings.push({\n file: file.path,\n line: i + 1,\n variable: match[1]!,\n existingValue: match[2]!,\n });\n }\n }\n }\n\n return warnings;\n}\n","import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';\nimport { resolve, join } from 'node:path';\nimport { argv, exit, stderr, stdout } from 'node:process';\n\nimport { decodeThemeHash } from './codec';\nimport { applyThemeBlock, generateThemeCSS, MARKER_START } from './apply';\nimport { scanConflicts } from './scan';\n\ninterface ParsedArgs {\n command: string;\n hash: string;\n target: string;\n colorSpace: 'hsl' | 'oklch';\n scanDir: string;\n dryRun: boolean;\n}\n\nfunction printUsage() {\n stderr.write(`\nUsage: enad-theme apply <hash> [options]\n\nOptions:\n --target <file> Target CSS file (default: src/globals.css)\n --color-space <cs> Color space: hsl | oklch (default: hsl)\n --scan <dir> Directory to scan for conflicts (default: src)\n --dry-run Print CSS to stdout without writing\n --help Show this help message\n\nExamples:\n enad-theme apply 1k5YWJj...\n enad-theme apply 1k5YWJj... --target app/globals.css --color-space oklch\n enad-theme apply 1k5YWJj... --dry-run\n`);\n}\n\nfunction parseArgs(args: string[]): ParsedArgs | null {\n if (args.length === 0 || args.includes('--help')) {\n printUsage();\n exit(0);\n }\n\n const command = args[0]!;\n if (command !== 'apply') {\n stderr.write(`Unknown command: ${command}\\nRun enad-theme --help for usage.\\n`);\n exit(1);\n }\n\n const hash = args[1];\n if (!hash) {\n stderr.write('Error: missing theme hash argument.\\n');\n exit(1);\n }\n\n let target = 'src/globals.css';\n let colorSpace: 'hsl' | 'oklch' = 'hsl';\n let scanDir = 'src';\n let dryRun = false;\n\n for (let i = 2; i < args.length; i++) {\n switch (args[i]) {\n case '--target':\n target = args[++i] ?? target;\n break;\n case '--color-space':\n colorSpace = (args[++i] as 'hsl' | 'oklch') ?? colorSpace;\n break;\n case '--scan':\n scanDir = args[++i] ?? scanDir;\n break;\n case '--dry-run':\n dryRun = true;\n break;\n }\n }\n\n return { command, hash, target, colorSpace, scanDir, dryRun };\n}\n\nfunction readExistingFile(targetPath: string): string {\n try {\n return readFileSync(targetPath, 'utf-8');\n } catch {\n // File doesn't exist yet, we'll create it\n return '';\n }\n}\n\nfunction writeOutput(targetPath: string, target: string, existing: string, css: string) {\n const updated = applyThemeBlock(existing, css);\n writeFileSync(targetPath, updated, 'utf-8');\n\n const isUpdate = existing.includes(MARKER_START);\n stderr.write(\n isUpdate\n ? `Updated theme block in ${target}\\n`\n : `Wrote theme block to ${target}\\n`,\n );\n}\n\nfunction scanForConflicts(scanDir: string, targetPath: string, css: string) {\n // Extract variable names from the generated CSS\n const varPattern = /^\\s*(--[\\w-]+)\\s*:/gm;\n const variables: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = varPattern.exec(css)) !== null) {\n variables.push(match[1]!);\n }\n\n if (variables.length === 0) return;\n\n // Simple recursive CSS file scan\n const cssFiles: Array<{ path: string; content: string }> = [];\n\n function walk(dir: string) {\n try {\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n try {\n const stat = statSync(full);\n if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {\n walk(full);\n } else if (stat.isFile() && full.endsWith('.css') && resolve(full) !== targetPath) {\n cssFiles.push({ path: full, content: readFileSync(full, 'utf-8') });\n }\n } catch {\n // Skip inaccessible files\n }\n }\n } catch {\n // Skip inaccessible directories\n }\n }\n\n walk(resolve(scanDir));\n\n const warnings = scanConflicts(cssFiles, variables);\n if (warnings.length > 0) {\n stderr.write(`\\nConflicting declarations found:\\n`);\n for (const w of warnings) {\n stderr.write(` ${w.file}:${w.line} ${w.variable}: ${w.existingValue}\\n`);\n }\n stderr.write(`\\nThese existing declarations may override or conflict with the theme.\\n`);\n }\n}\n\nfunction main() {\n const parsed = parseArgs(argv.slice(2));\n if (!parsed) return;\n\n const { hash, target, colorSpace, scanDir, dryRun } = parsed;\n\n // Decode\n const decoded = decodeThemeHash(hash);\n if (Object.keys(decoded).length === 0) {\n stderr.write('Error: failed to decode theme hash. Check that the hash is valid.\\n');\n exit(1);\n }\n\n // Generate CSS\n const css = generateThemeCSS(decoded, { colorSpace });\n\n if (dryRun) {\n stdout.write(css + '\\n');\n exit(0);\n }\n\n // Write to target file\n const targetPath = resolve(target);\n const existing = readExistingFile(targetPath);\n writeOutput(targetPath, target, existing, css);\n\n scanForConflicts(scanDir, targetPath, css);\n}\n\nmain();\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"codec.d.ts","names":[],"sources":["../../../src/client/theme/codec.ts"],"mappings":";;AAQA;;;;;KAAY,SAAA,GAAY,MAAA;;cAUX,OAAA,EAAS,MAAA;;cAiCT,eAAA,EAAiB,MAAA;AAA9B;;;;;AAsCA;;AAtCA,iBAsCgB,eAAA,CAAgB,IAAA,EAAM,SAAA;;;AAiBtC;;;;;iBAAgB,eAAA,CAAgB,IAAA,WAAe,SAAA"}
1
+ {"version":3,"file":"codec.d.ts","names":[],"sources":["../../../src/client/theme/codec.ts"],"mappings":";;AAQA;;;;;KAAY,SAAA,GAAY,MAAA;;cAUX,OAAA,EAAS,MAAA;;cA+BT,eAAA,EAAiB,MAAA;AAA9B;;;;;AAsCA;;AAtCA,iBAsCgB,eAAA,CAAgB,IAAA,EAAM,SAAA;;;AAiBtC;;;;;iBAAgB,eAAA,CAAgB,IAAA,WAAe,SAAA"}
@@ -28,8 +28,6 @@ const KEY_MAP = {
28
28
  oc: "overlayColor",
29
29
  of: "overlayFromOpacity",
30
30
  ot: "overlayToOpacity",
31
- hs: "imageHoverScale",
32
- hd: "imageHoverDuration",
33
31
  v: "variants",
34
32
  xs: "componentSet"
35
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"codec.mjs","names":["msgpackEncode","msgpackDecode"],"sources":["../../../src/client/theme/codec.ts"],"sourcesContent":["import { decode as msgpackDecode, encode as msgpackEncode } from \"@msgpack/msgpack\";\n\n/**\n * Loose bag of theme values. The decoder returns whatever keys are present;\n * the consumer merges onto its own defaults. This keeps the format\n * forward/backward compatible: new keys are silently ignored by old decoders,\n * and removed keys are filled from defaults by the consumer.\n */\nexport type ThemeHash = Record<string, unknown>;\n\n// Current format version. Prepended as a single ASCII char before the base64url payload.\nconst FORMAT_VERSION = \"1\";\n\n// ---------------------------------------------------------------------------\n// Short key mapping\n// ---------------------------------------------------------------------------\n\n/** Short key -> full key */\nexport const KEY_MAP: Record<string, string> = {\n c: \"colorize\",\n i: \"intensity\",\n d: \"delight\",\n r: \"radius\",\n bf: \"fontBody\",\n hf: \"fontHeading\",\n ic: \"iconSet\",\n cs: \"colorSpace\",\n dm: \"darkMode\",\n lc: \"lightColors\",\n dc: \"darkColors\",\n br: \"buttonRadius\",\n cr: \"cardRadius\",\n ir: \"inputRadius\",\n mr: \"imageRadius\",\n sp: \"shadowPreset\",\n sc: \"shadowColor\",\n hw: \"headingWeight\",\n ht: \"headingTracking\",\n bw: \"bodyWeight\",\n hx: \"headingTransform\",\n bdr: \"borderWidth\",\n oc: \"overlayColor\",\n of: \"overlayFromOpacity\",\n ot: \"overlayToOpacity\",\n hs: \"imageHoverScale\",\n hd: \"imageHoverDuration\",\n v: \"variants\",\n xs: \"componentSet\",\n};\n\n/** Full key -> short key (inverse of KEY_MAP) */\nexport const KEY_MAP_REVERSE: Record<string, string> = Object.fromEntries(\n Object.entries(KEY_MAP).map(([short, full]) => [full, short]),\n);\n\n// ---------------------------------------------------------------------------\n// Base64url helpers (RFC 4648 §5, no padding)\n// ---------------------------------------------------------------------------\n\nfunction toBase64url(buf: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < buf.length; i++) {\n binary += String.fromCharCode(buf[i]!);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64url(str: string): Uint8Array {\n const base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = base64 + \"=\".repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Encode / Decode\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a theme state diff into a portable hash string.\n *\n * The input should only contain non-default values. The encoder maps\n * full keys to short keys, packs with MessagePack, and encodes as\n * `<version><base64url>`.\n */\nexport function encodeThemeHash(diff: ThemeHash): string {\n const compact: Record<string, unknown> = {};\n for (const [fullKey, value] of Object.entries(diff)) {\n const shortKey = KEY_MAP_REVERSE[fullKey] ?? fullKey;\n compact[shortKey] = value;\n }\n const packed = msgpackEncode(compact);\n return FORMAT_VERSION + toBase64url(packed);\n}\n\n/**\n * Decode a hash string back into a key-value bag with full key names.\n *\n * Returns partial data -- the consumer is responsible for merging onto\n * its own defaults. Unknown keys are preserved for forward compatibility.\n * Returns an empty object on invalid input (never throws).\n */\nexport function decodeThemeHash(hash: string): ThemeHash {\n try {\n if (!hash || hash.length < 2) return {};\n\n const version = hash[0];\n if (version !== FORMAT_VERSION) return {};\n\n const payload = hash.slice(1);\n const bytes = fromBase64url(payload);\n const compact = msgpackDecode(bytes) as Record<string, unknown>;\n\n if (typeof compact !== \"object\" || compact === null) return {};\n\n const result: ThemeHash = {};\n for (const [key, value] of Object.entries(compact)) {\n const fullKey = KEY_MAP[key] ?? key;\n result[fullKey] = value;\n }\n return result;\n } catch {\n return {};\n }\n}\n"],"mappings":";;AAWA,MAAM,iBAAiB;;AAOvB,MAAa,UAAkC;CAC7C,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,GAAG;CACH,IAAI;CACL;;AAGD,MAAa,kBAA0C,OAAO,YAC5D,OAAO,QAAQ,QAAQ,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,MAAM,MAAM,CAAC,CAC9D;AAMD,SAAS,YAAY,KAAyB;CAC5C,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,IAC9B,WAAU,OAAO,aAAa,IAAI,GAAI;AAExC,QAAO,KAAK,OAAO,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,GAAG;;AAGhF,SAAS,cAAc,KAAyB;CAC9C,MAAM,SAAS,IAAI,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI;CACxD,MAAM,SAAS,SAAS,IAAI,QAAQ,IAAK,OAAO,SAAS,KAAM,EAAE;CACjE,MAAM,SAAS,KAAK,OAAO;CAC3B,MAAM,QAAQ,IAAI,WAAW,OAAO,OAAO;AAC3C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,OAAM,KAAK,OAAO,WAAW,EAAE;AAEjC,QAAO;;;;;;;;;AAcT,SAAgB,gBAAgB,MAAyB;CACvD,MAAM,UAAmC,EAAE;AAC3C,MAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,KAAK,EAAE;EACnD,MAAM,WAAW,gBAAgB,YAAY;AAC7C,UAAQ,YAAY;;AAGtB,QAAO,iBAAiB,YADTA,OAAc,QAAQ,CACM;;;;;;;;;AAU7C,SAAgB,gBAAgB,MAAyB;AACvD,KAAI;AACF,MAAI,CAAC,QAAQ,KAAK,SAAS,EAAG,QAAO,EAAE;AAGvC,MADgB,KAAK,OACL,eAAgB,QAAO,EAAE;EAIzC,MAAM,UAAUC,OADF,cADE,KAAK,MAAM,EAAE,CACO,CACA;AAEpC,MAAI,OAAO,YAAY,YAAY,YAAY,KAAM,QAAO,EAAE;EAE9D,MAAM,SAAoB,EAAE;AAC5B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;GAClD,MAAM,UAAU,QAAQ,QAAQ;AAChC,UAAO,WAAW;;AAEpB,SAAO;SACD;AACN,SAAO,EAAE"}
1
+ {"version":3,"file":"codec.mjs","names":["msgpackEncode","msgpackDecode"],"sources":["../../../src/client/theme/codec.ts"],"sourcesContent":["import { decode as msgpackDecode, encode as msgpackEncode } from \"@msgpack/msgpack\";\n\n/**\n * Loose bag of theme values. The decoder returns whatever keys are present;\n * the consumer merges onto its own defaults. This keeps the format\n * forward/backward compatible: new keys are silently ignored by old decoders,\n * and removed keys are filled from defaults by the consumer.\n */\nexport type ThemeHash = Record<string, unknown>;\n\n// Current format version. Prepended as a single ASCII char before the base64url payload.\nconst FORMAT_VERSION = \"1\";\n\n// ---------------------------------------------------------------------------\n// Short key mapping\n// ---------------------------------------------------------------------------\n\n/** Short key -> full key */\nexport const KEY_MAP: Record<string, string> = {\n c: \"colorize\",\n i: \"intensity\",\n d: \"delight\",\n r: \"radius\",\n bf: \"fontBody\",\n hf: \"fontHeading\",\n ic: \"iconSet\",\n cs: \"colorSpace\",\n dm: \"darkMode\",\n lc: \"lightColors\",\n dc: \"darkColors\",\n br: \"buttonRadius\",\n cr: \"cardRadius\",\n ir: \"inputRadius\",\n mr: \"imageRadius\",\n sp: \"shadowPreset\",\n sc: \"shadowColor\",\n hw: \"headingWeight\",\n ht: \"headingTracking\",\n bw: \"bodyWeight\",\n hx: \"headingTransform\",\n bdr: \"borderWidth\",\n oc: \"overlayColor\",\n of: \"overlayFromOpacity\",\n ot: \"overlayToOpacity\",\n v: \"variants\",\n xs: \"componentSet\",\n};\n\n/** Full key -> short key (inverse of KEY_MAP) */\nexport const KEY_MAP_REVERSE: Record<string, string> = Object.fromEntries(\n Object.entries(KEY_MAP).map(([short, full]) => [full, short]),\n);\n\n// ---------------------------------------------------------------------------\n// Base64url helpers (RFC 4648 §5, no padding)\n// ---------------------------------------------------------------------------\n\nfunction toBase64url(buf: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < buf.length; i++) {\n binary += String.fromCharCode(buf[i]!);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64url(str: string): Uint8Array {\n const base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = base64 + \"=\".repeat((4 - (base64.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n// ---------------------------------------------------------------------------\n// Encode / Decode\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a theme state diff into a portable hash string.\n *\n * The input should only contain non-default values. The encoder maps\n * full keys to short keys, packs with MessagePack, and encodes as\n * `<version><base64url>`.\n */\nexport function encodeThemeHash(diff: ThemeHash): string {\n const compact: Record<string, unknown> = {};\n for (const [fullKey, value] of Object.entries(diff)) {\n const shortKey = KEY_MAP_REVERSE[fullKey] ?? fullKey;\n compact[shortKey] = value;\n }\n const packed = msgpackEncode(compact);\n return FORMAT_VERSION + toBase64url(packed);\n}\n\n/**\n * Decode a hash string back into a key-value bag with full key names.\n *\n * Returns partial data -- the consumer is responsible for merging onto\n * its own defaults. Unknown keys are preserved for forward compatibility.\n * Returns an empty object on invalid input (never throws).\n */\nexport function decodeThemeHash(hash: string): ThemeHash {\n try {\n if (!hash || hash.length < 2) return {};\n\n const version = hash[0];\n if (version !== FORMAT_VERSION) return {};\n\n const payload = hash.slice(1);\n const bytes = fromBase64url(payload);\n const compact = msgpackDecode(bytes) as Record<string, unknown>;\n\n if (typeof compact !== \"object\" || compact === null) return {};\n\n const result: ThemeHash = {};\n for (const [key, value] of Object.entries(compact)) {\n const fullKey = KEY_MAP[key] ?? key;\n result[fullKey] = value;\n }\n return result;\n } catch {\n return {};\n }\n}\n"],"mappings":";;AAWA,MAAM,iBAAiB;;AAOvB,MAAa,UAAkC;CAC7C,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,GAAG;CACH,IAAI;CACL;;AAGD,MAAa,kBAA0C,OAAO,YAC5D,OAAO,QAAQ,QAAQ,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,MAAM,MAAM,CAAC,CAC9D;AAMD,SAAS,YAAY,KAAyB;CAC5C,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,IAC9B,WAAU,OAAO,aAAa,IAAI,GAAI;AAExC,QAAO,KAAK,OAAO,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,GAAG;;AAGhF,SAAS,cAAc,KAAyB;CAC9C,MAAM,SAAS,IAAI,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI;CACxD,MAAM,SAAS,SAAS,IAAI,QAAQ,IAAK,OAAO,SAAS,KAAM,EAAE;CACjE,MAAM,SAAS,KAAK,OAAO;CAC3B,MAAM,QAAQ,IAAI,WAAW,OAAO,OAAO;AAC3C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,OAAM,KAAK,OAAO,WAAW,EAAE;AAEjC,QAAO;;;;;;;;;AAcT,SAAgB,gBAAgB,MAAyB;CACvD,MAAM,UAAmC,EAAE;AAC3C,MAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,KAAK,EAAE;EACnD,MAAM,WAAW,gBAAgB,YAAY;AAC7C,UAAQ,YAAY;;AAGtB,QAAO,iBAAiB,YADTA,OAAc,QAAQ,CACM;;;;;;;;;AAU7C,SAAgB,gBAAgB,MAAyB;AACvD,KAAI;AACF,MAAI,CAAC,QAAQ,KAAK,SAAS,EAAG,QAAO,EAAE;AAGvC,MADgB,KAAK,OACL,eAAgB,QAAO,EAAE;EAIzC,MAAM,UAAUC,OADF,cADE,KAAK,MAAM,EAAE,CACO,CACA;AAEpC,MAAI,OAAO,YAAY,YAAY,YAAY,KAAM,QAAO,EAAE;EAE9D,MAAM,SAAoB,EAAE;AAC5B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;GAClD,MAAM,UAAU,QAAQ,QAAQ;AAChC,UAAO,WAAW;;AAEpB,SAAO;SACD;AACN,SAAO,EAAE"}
@@ -33,8 +33,6 @@ declare const TOKEN_DEFAULTS: {
33
33
  readonly overlayColor: "black";
34
34
  readonly overlayFromOpacity: "0.5";
35
35
  readonly overlayToOpacity: "0";
36
- readonly imageHoverScale: "1.05";
37
- readonly imageHoverDuration: "300ms";
38
36
  readonly variants: {
39
37
  readonly hero: "overlay";
40
38
  readonly linkBlock: "overlay";
@@ -77,8 +77,6 @@ const TOKEN_DEFAULTS = {
77
77
  overlayColor: "black",
78
78
  overlayFromOpacity: "0.5",
79
79
  overlayToOpacity: "0",
80
- imageHoverScale: "1.05",
81
- imageHoverDuration: "300ms",
82
80
  variants: {
83
81
  hero: "overlay",
84
82
  linkBlock: "overlay",
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.mjs","names":[],"sources":["../../../src/client/theme/defaults.ts"],"sourcesContent":["/**\n * SDK default color tokens. These are the canonical defaults used by\n * the theme codec to compute diffs (only non-default values are encoded).\n */\nexport const SDK_DEFAULTS = {\n light: {\n \"--background\": \"#ffffff\",\n \"--foreground\": \"#111827\",\n \"--card\": \"#ffffff\",\n \"--card-foreground\": \"#111827\",\n \"--popover\": \"#ffffff\",\n \"--popover-foreground\": \"#111827\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#f1f5f9\",\n \"--secondary-foreground\": \"#0f172a\",\n \"--muted\": \"#f3f4f6\",\n \"--muted-foreground\": \"#636b78\",\n \"--accent\": \"#dbeafe\",\n \"--accent-foreground\": \"#1e3a8a\",\n \"--accent-muted\": \"#eff6ff\",\n \"--accent-muted-foreground\": \"#1e40af\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#e5e7eb\",\n \"--input\": \"#e5e7eb\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n dark: {\n \"--background\": \"#111827\",\n \"--foreground\": \"#f9fafb\",\n \"--card\": \"#1f2937\",\n \"--card-foreground\": \"#f9fafb\",\n \"--popover\": \"#1f2937\",\n \"--popover-foreground\": \"#f9fafb\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#1e293b\",\n \"--secondary-foreground\": \"#e2e8f0\",\n \"--muted\": \"#374151\",\n \"--muted-foreground\": \"#b0b8c4\",\n \"--accent\": \"#1e3a5f\",\n \"--accent-foreground\": \"#bfdbfe\",\n \"--accent-muted\": \"#172554\",\n \"--accent-muted-foreground\": \"#93c5fd\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#374151\",\n \"--input\": \"#374151\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n};\n\n/** Default values for design tokens (non-color). */\nexport const TOKEN_DEFAULTS = {\n colorize: 50,\n intensity: 50,\n delight: 50,\n radius: 0.75,\n fontBody: \"Inter\",\n fontHeading: \"Inter\",\n iconSet: \"Phosphor\",\n colorSpace: \"hsl\",\n darkMode: false,\n componentSet: \"default\" as const,\n\n // Shape\n buttonRadius: \"9999px\",\n cardRadius: \"var(--radius)\",\n inputRadius: \"var(--radius)\",\n imageRadius: \"0px\",\n\n // Depth\n shadowPreset: \"none\",\n shadowColor: \"oklch(0 0 0 / 0.1)\",\n\n // Typography\n headingWeight: \"600\",\n headingTracking: \"0em\",\n bodyWeight: \"400\",\n headingTransform: \"none\",\n\n // Border\n borderWidth: \"1px\",\n\n // Overlay\n overlayColor: \"black\",\n overlayFromOpacity: \"0.5\",\n overlayToOpacity: \"0\",\n\n // Motion\n imageHoverScale: \"1.05\",\n imageHoverDuration: \"300ms\",\n\n // Variants\n variants: {\n hero: \"overlay\",\n linkBlock: \"overlay\",\n productCard: \"gallery\",\n contentWithImage: \"side-by-side\",\n gallery: \"grid\",\n textContent: \"default\",\n },\n} as const;\n"],"mappings":";;;;;AAIA,MAAa,eAAe;CAC1B,OAAO;EACL,gBAAgB;EAChB,gBAAgB;EAChB,UAAU;EACV,qBAAqB;EACrB,aAAa;EACb,wBAAwB;EACxB,aAAa;EACb,wBAAwB;EACxB,eAAe;EACf,0BAA0B;EAC1B,WAAW;EACX,sBAAsB;EACtB,YAAY;EACZ,uBAAuB;EACvB,kBAAkB;EAClB,6BAA6B;EAC7B,iBAAiB;EACjB,4BAA4B;EAC5B,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACD,MAAM;EACJ,gBAAgB;EAChB,gBAAgB;EAChB,UAAU;EACV,qBAAqB;EACrB,aAAa;EACb,wBAAwB;EACxB,aAAa;EACb,wBAAwB;EACxB,eAAe;EACf,0BAA0B;EAC1B,WAAW;EACX,sBAAsB;EACtB,YAAY;EACZ,uBAAuB;EACvB,kBAAkB;EAClB,6BAA6B;EAC7B,iBAAiB;EACjB,4BAA4B;EAC5B,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACF;;AAGD,MAAa,iBAAiB;CAC5B,UAAU;CACV,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACV,aAAa;CACb,SAAS;CACT,YAAY;CACZ,UAAU;CACV,cAAc;CAGd,cAAc;CACd,YAAY;CACZ,aAAa;CACb,aAAa;CAGb,cAAc;CACd,aAAa;CAGb,eAAe;CACf,iBAAiB;CACjB,YAAY;CACZ,kBAAkB;CAGlB,aAAa;CAGb,cAAc;CACd,oBAAoB;CACpB,kBAAkB;CAGlB,iBAAiB;CACjB,oBAAoB;CAGpB,UAAU;EACR,MAAM;EACN,WAAW;EACX,aAAa;EACb,kBAAkB;EAClB,SAAS;EACT,aAAa;EACd;CACF"}
1
+ {"version":3,"file":"defaults.mjs","names":[],"sources":["../../../src/client/theme/defaults.ts"],"sourcesContent":["/**\n * SDK default color tokens. These are the canonical defaults used by\n * the theme codec to compute diffs (only non-default values are encoded).\n */\nexport const SDK_DEFAULTS = {\n light: {\n \"--background\": \"#ffffff\",\n \"--foreground\": \"#111827\",\n \"--card\": \"#ffffff\",\n \"--card-foreground\": \"#111827\",\n \"--popover\": \"#ffffff\",\n \"--popover-foreground\": \"#111827\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#f1f5f9\",\n \"--secondary-foreground\": \"#0f172a\",\n \"--muted\": \"#f3f4f6\",\n \"--muted-foreground\": \"#636b78\",\n \"--accent\": \"#dbeafe\",\n \"--accent-foreground\": \"#1e3a8a\",\n \"--accent-muted\": \"#eff6ff\",\n \"--accent-muted-foreground\": \"#1e40af\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#e5e7eb\",\n \"--input\": \"#e5e7eb\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n dark: {\n \"--background\": \"#111827\",\n \"--foreground\": \"#f9fafb\",\n \"--card\": \"#1f2937\",\n \"--card-foreground\": \"#f9fafb\",\n \"--popover\": \"#1f2937\",\n \"--popover-foreground\": \"#f9fafb\",\n \"--primary\": \"#2563eb\",\n \"--primary-foreground\": \"#ffffff\",\n \"--secondary\": \"#1e293b\",\n \"--secondary-foreground\": \"#e2e8f0\",\n \"--muted\": \"#374151\",\n \"--muted-foreground\": \"#b0b8c4\",\n \"--accent\": \"#1e3a5f\",\n \"--accent-foreground\": \"#bfdbfe\",\n \"--accent-muted\": \"#172554\",\n \"--accent-muted-foreground\": \"#93c5fd\",\n \"--destructive\": \"#dc2626\",\n \"--destructive-foreground\": \"#ffffff\",\n \"--border\": \"#374151\",\n \"--input\": \"#374151\",\n \"--ring\": \"#2563eb\",\n } as Record<string, string>,\n};\n\n/** Default values for design tokens (non-color). */\nexport const TOKEN_DEFAULTS = {\n colorize: 50,\n intensity: 50,\n delight: 50,\n radius: 0.75,\n fontBody: \"Inter\",\n fontHeading: \"Inter\",\n iconSet: \"Phosphor\",\n colorSpace: \"hsl\",\n darkMode: false,\n componentSet: \"default\" as const,\n\n // Shape\n buttonRadius: \"9999px\",\n cardRadius: \"var(--radius)\",\n inputRadius: \"var(--radius)\",\n imageRadius: \"0px\",\n\n // Depth\n shadowPreset: \"none\",\n shadowColor: \"oklch(0 0 0 / 0.1)\",\n\n // Typography\n headingWeight: \"600\",\n headingTracking: \"0em\",\n bodyWeight: \"400\",\n headingTransform: \"none\",\n\n // Border\n borderWidth: \"1px\",\n\n // Overlay\n overlayColor: \"black\",\n overlayFromOpacity: \"0.5\",\n overlayToOpacity: \"0\",\n\n // Variants\n variants: {\n hero: \"overlay\",\n linkBlock: \"overlay\",\n productCard: \"gallery\",\n contentWithImage: \"side-by-side\",\n gallery: \"grid\",\n textContent: \"default\",\n },\n} as const;\n"],"mappings":";;;;;AAIA,MAAa,eAAe;CAC1B,OAAO;EACL,gBAAgB;EAChB,gBAAgB;EAChB,UAAU;EACV,qBAAqB;EACrB,aAAa;EACb,wBAAwB;EACxB,aAAa;EACb,wBAAwB;EACxB,eAAe;EACf,0BAA0B;EAC1B,WAAW;EACX,sBAAsB;EACtB,YAAY;EACZ,uBAAuB;EACvB,kBAAkB;EAClB,6BAA6B;EAC7B,iBAAiB;EACjB,4BAA4B;EAC5B,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACD,MAAM;EACJ,gBAAgB;EAChB,gBAAgB;EAChB,UAAU;EACV,qBAAqB;EACrB,aAAa;EACb,wBAAwB;EACxB,aAAa;EACb,wBAAwB;EACxB,eAAe;EACf,0BAA0B;EAC1B,WAAW;EACX,sBAAsB;EACtB,YAAY;EACZ,uBAAuB;EACvB,kBAAkB;EAClB,6BAA6B;EAC7B,iBAAiB;EACjB,4BAA4B;EAC5B,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACF;;AAGD,MAAa,iBAAiB;CAC5B,UAAU;CACV,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACV,aAAa;CACb,SAAS;CACT,YAAY;CACZ,UAAU;CACV,cAAc;CAGd,cAAc;CACd,YAAY;CACZ,aAAa;CACb,aAAa;CAGb,cAAc;CACd,aAAa;CAGb,eAAe;CACf,iBAAiB;CACjB,YAAY;CACZ,kBAAkB;CAGlB,aAAa;CAGb,cAAc;CACd,oBAAoB;CACpB,kBAAkB;CAGlB,UAAU;EACR,MAAM;EACN,WAAW;EACX,aAAa;EACb,kBAAkB;EAClB,SAAS;EACT,aAAa;EACd;CACF"}
@@ -8,10 +8,17 @@ type CarouselApi = UseEmblaCarouselType[1];
8
8
  type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
9
9
  type CarouselOptions = UseCarouselParameters[0];
10
10
  type CarouselPlugin = UseCarouselParameters[1];
11
+ type CarouselWheelAxis = "vertical" | "horizontal" | "both" | false;
11
12
  type CarouselProps = {
12
13
  opts?: CarouselOptions;
13
14
  plugins?: CarouselPlugin;
14
15
  orientation?: "horizontal" | "vertical";
16
+ /** Controls which wheel/trackpad axes scroll the carousel.
17
+ * - `"vertical"` — vertical scroll hijacks carousel (default, mouse-wheel-friendly)
18
+ * - `"horizontal"` — horizontal trackpad swipes scroll carousel, vertical passes through
19
+ * - `"both"` — both axes scroll the carousel
20
+ * - `false` — no wheel gestures (touch drag still works) */
21
+ wheelAxis?: CarouselWheelAxis;
15
22
  setApi?: (api: CarouselApi) => void;
16
23
  };
17
24
  declare function Carousel({
@@ -19,6 +26,7 @@ declare function Carousel({
19
26
  opts,
20
27
  setApi,
21
28
  plugins,
29
+ wheelAxis,
22
30
  className,
23
31
  children,
24
32
  ...props
@@ -44,5 +52,5 @@ declare function CarouselNext({
44
52
  ...props
45
53
  }: React$1.ComponentProps<typeof Button>): react_jsx_runtime0.JSX.Element;
46
54
  //#endregion
47
- export { Carousel, type CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious };
55
+ export { Carousel, type CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, type CarouselWheelAxis };
48
56
  //# sourceMappingURL=carousel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"carousel.d.ts","names":[],"sources":["../../../src/client/ui/carousel.tsx"],"mappings":";;;;;;KASK,WAAA,GAAc,oBAAA;AAAA,KACd,qBAAA,GAAwB,UAAA,QAAkB,gBAAA;AAAA,KAC1C,eAAA,GAAkB,qBAAA;AAAA,KAClB,cAAA,GAAiB,qBAAA;AAAA,KAEjB,aAAA;EACH,IAAA,GAAO,eAAA;EACP,OAAA,GAAU,cAAA;EACV,WAAA;EACA,MAAA,IAAU,GAAA,EAAK,WAAA;AAAA;AAAA,iBAwBR,QAAA,CAAA;EACP,WAAA;EACA,IAAA;EACA,MAAA;EACA,OAAA;EACA,SAAA;EACA,QAAA;EAAA,GACG;AAAA,GACF,OAAA,CAAM,cAAA,UAAwB,aAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAiFrC,eAAA,CAAA;EAAkB,SAAA;EAAA,GAAc;AAAA,GAAS,OAAA,CAAM,cAAA,UAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAapE,YAAA,CAAA;EAAe,SAAA;EAAA,GAAc;AAAA,GAAS,OAAA,CAAM,cAAA,UAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAkBjE,gBAAA,CAAA;EACP,SAAA;EACA,OAAA;EACA,IAAA;EAAA,GACG;AAAA,GACF,OAAA,CAAM,cAAA,QAAsB,MAAA,IAAO,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBA0B7B,YAAA,CAAA;EACP,SAAA;EACA,OAAA;EACA,IAAA;EAAA,GACG;AAAA,GACF,OAAA,CAAM,cAAA,QAAsB,MAAA,IAAO,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"carousel.d.ts","names":[],"sources":["../../../src/client/ui/carousel.tsx"],"mappings":";;;;;;KAUK,WAAA,GAAc,oBAAA;AAAA,KACd,qBAAA,GAAwB,UAAA,QAAkB,gBAAA;AAAA,KAC1C,eAAA,GAAkB,qBAAA;AAAA,KAClB,cAAA,GAAiB,qBAAA;AAAA,KAEjB,iBAAA;AAAA,KAEA,aAAA;EACH,IAAA,GAAO,eAAA;EACP,OAAA,GAAU,cAAA;EACV,WAAA;EATwB;;;;AAAqC;EAe7D,SAAA,GAAY,iBAAA;EACZ,MAAA,IAAU,GAAA,EAAK,WAAA;AAAA;AAAA,iBAwBR,QAAA,CAAA;EACP,WAAA;EACA,IAAA;EACA,MAAA;EACA,OAAA;EACA,SAAA;EACA,SAAA;EACA,QAAA;EAAA,GACG;AAAA,GACF,OAAA,CAAM,cAAA,UAAwB,aAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAwGrC,eAAA,CAAA;EAAkB,SAAA;EAAA,GAAc;AAAA,GAAS,OAAA,CAAM,cAAA,UAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAapE,YAAA,CAAA;EAAe,SAAA;EAAA,GAAc;AAAA,GAAS,OAAA,CAAM,cAAA,UAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBAkBjE,gBAAA,CAAA;EACP,SAAA;EACA,OAAA;EACA,IAAA;EAAA,GACG;AAAA,GACF,OAAA,CAAM,cAAA,QAAsB,MAAA,IAAO,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBA0B7B,YAAA,CAAA;EACP,SAAA;EACA,OAAA;EACA,IAAA;EAAA,GACG;AAAA,GACF,OAAA,CAAM,cAAA,QAAsB,MAAA,IAAO,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -5,6 +5,7 @@ import { Button } from "./button.mjs";
5
5
  import * as React$1 from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import useEmblaCarousel from "embla-carousel-react";
8
+ import { WheelGesturesPlugin } from "embla-carousel-wheel-gestures";
8
9
  //#region src/client/ui/carousel.tsx
9
10
  const CarouselContext = React$1.createContext(null);
10
11
  function useCarousel() {
@@ -12,11 +13,26 @@ function useCarousel() {
12
13
  if (!context) throw new Error("useCarousel must be used within a <Carousel />");
13
14
  return context;
14
15
  }
15
- function Carousel({ orientation = "horizontal", opts, setApi, plugins, className, children, ...props }) {
16
+ function Carousel({ orientation = "horizontal", opts, setApi, plugins, wheelAxis = "vertical", className, children, ...props }) {
17
+ const allPlugins = React$1.useMemo(() => {
18
+ if (orientation !== "horizontal" || wheelAxis === false) return [...plugins ?? []];
19
+ const wheel = [];
20
+ if (wheelAxis === "vertical" || wheelAxis === "both") wheel.push(WheelGesturesPlugin({ forceWheelAxis: "y" }));
21
+ if (wheelAxis === "horizontal" || wheelAxis === "both") {
22
+ const horizontal = WheelGesturesPlugin();
23
+ if (wheelAxis === "both") horizontal.name = "wheelGesturesHorizontal";
24
+ wheel.push(horizontal);
25
+ }
26
+ return [...wheel, ...plugins ?? []];
27
+ }, [
28
+ orientation,
29
+ plugins,
30
+ wheelAxis
31
+ ]);
16
32
  const [carouselRef, api] = useEmblaCarousel({
17
33
  ...opts,
18
34
  axis: orientation === "horizontal" ? "x" : "y"
19
- }, plugins);
35
+ }, allPlugins);
20
36
  const [canScrollPrev, setCanScrollPrev] = React$1.useState(false);
21
37
  const [canScrollNext, setCanScrollNext] = React$1.useState(false);
22
38
  const onSelect = React$1.useCallback((api) => {
@@ -1 +1 @@
1
- {"version":3,"file":"carousel.mjs","names":["React"],"sources":["../../../src/client/ui/carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport useEmblaCarousel, { type UseEmblaCarouselType } from \"embla-carousel-react\";\nimport { useIcon } from \"../icons/icon-context\";\n\nimport { cn } from \"./utils\";\nimport { Button } from \"./button\";\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\ntype CarouselOptions = UseCarouselParameters[0];\ntype CarouselPlugin = UseCarouselParameters[1];\n\ntype CarouselProps = {\n opts?: CarouselOptions;\n plugins?: CarouselPlugin;\n orientation?: \"horizontal\" | \"vertical\";\n setApi?: (api: CarouselApi) => void;\n};\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n api: ReturnType<typeof useEmblaCarousel>[1];\n scrollPrev: () => void;\n scrollNext: () => void;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n} & CarouselProps;\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null);\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext);\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\");\n }\n\n return context;\n}\n\nfunction Carousel({\n orientation = \"horizontal\",\n opts,\n setApi,\n plugins,\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & CarouselProps) {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n },\n plugins,\n );\n const [canScrollPrev, setCanScrollPrev] = React.useState(false);\n const [canScrollNext, setCanScrollNext] = React.useState(false);\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) return;\n setCanScrollPrev(api.canScrollPrev());\n setCanScrollNext(api.canScrollNext());\n }, []);\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev();\n }, [api]);\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext();\n }, [api]);\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n scrollPrev();\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault();\n scrollNext();\n }\n },\n [scrollPrev, scrollNext],\n );\n\n React.useEffect(() => {\n if (!api || !setApi) return;\n setApi(api);\n }, [api, setApi]);\n\n React.useEffect(() => {\n if (!api) return;\n onSelect(api);\n api.on(\"reInit\", onSelect);\n api.on(\"select\", onSelect);\n\n return () => {\n api?.off(\"select\", onSelect);\n };\n }, [api, onSelect]);\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation: orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n onKeyDownCapture={handleKeyDown}\n className={cn(\"relative\", className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n data-slot=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n );\n}\n\nfunction CarouselContent({ className, ...props }: React.ComponentProps<\"div\">) {\n const { carouselRef, orientation } = useCarousel();\n\n return (\n <div ref={carouselRef} className=\"overflow-hidden\" data-slot=\"carousel-content\">\n <div\n className={cn(\"flex\", orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\", className)}\n {...props}\n />\n </div>\n );\n}\n\nfunction CarouselItem({ className, ...props }: React.ComponentProps<\"div\">) {\n const { orientation } = useCarousel();\n\n return (\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n data-slot=\"carousel-item\"\n className={cn(\n \"min-w-0 shrink-0 grow-0 basis-full\",\n orientation === \"horizontal\" ? \"pl-4\" : \"pt-4\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CarouselPrevious({\n className,\n variant = \"outlined\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel();\n const ArrowLeftIcon = useIcon(\"arrowLeft\");\n\n return (\n <Button\n data-slot=\"carousel-previous\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -left-12 -translate-y-1/2\"\n : \"-top-12 left-1/2 -translate-x-1/2 rotate-90\",\n className,\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <ArrowLeftIcon />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n );\n}\n\nfunction CarouselNext({\n className,\n variant = \"outlined\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollNext, canScrollNext } = useCarousel();\n const ArrowRightIcon = useIcon(\"arrowRight\");\n\n return (\n <Button\n data-slot=\"carousel-next\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -right-12 -translate-y-1/2\"\n : \"-bottom-12 left-1/2 -translate-x-1/2 rotate-90\",\n className,\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <ArrowRightIcon />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n );\n}\n\nexport {\n type CarouselApi,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n};\n"],"mappings":";;;;;;;;AA8BA,MAAM,kBAAkBA,QAAM,cAA2C,KAAK;AAE9E,SAAS,cAAc;CACrB,MAAM,UAAUA,QAAM,WAAW,gBAAgB;AAEjD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,iDAAiD;AAGnE,QAAO;;AAGT,SAAS,SAAS,EAChB,cAAc,cACd,MACA,QACA,SACA,WACA,UACA,GAAG,SAC2C;CAC9C,MAAM,CAAC,aAAa,OAAO,iBACzB;EACE,GAAG;EACH,MAAM,gBAAgB,eAAe,MAAM;EAC5C,EACD,QACD;CACD,MAAM,CAAC,eAAe,oBAAoBA,QAAM,SAAS,MAAM;CAC/D,MAAM,CAAC,eAAe,oBAAoBA,QAAM,SAAS,MAAM;CAE/D,MAAM,WAAWA,QAAM,aAAa,QAAqB;AACvD,MAAI,CAAC,IAAK;AACV,mBAAiB,IAAI,eAAe,CAAC;AACrC,mBAAiB,IAAI,eAAe,CAAC;IACpC,EAAE,CAAC;CAEN,MAAM,aAAaA,QAAM,kBAAkB;AACzC,OAAK,YAAY;IAChB,CAAC,IAAI,CAAC;CAET,MAAM,aAAaA,QAAM,kBAAkB;AACzC,OAAK,YAAY;IAChB,CAAC,IAAI,CAAC;CAET,MAAM,gBAAgBA,QAAM,aACzB,UAA+C;AAC9C,MAAI,MAAM,QAAQ,aAAa;AAC7B,SAAM,gBAAgB;AACtB,eAAY;aACH,MAAM,QAAQ,cAAc;AACrC,SAAM,gBAAgB;AACtB,eAAY;;IAGhB,CAAC,YAAY,WAAW,CACzB;AAED,SAAM,gBAAgB;AACpB,MAAI,CAAC,OAAO,CAAC,OAAQ;AACrB,SAAO,IAAI;IACV,CAAC,KAAK,OAAO,CAAC;AAEjB,SAAM,gBAAgB;AACpB,MAAI,CAAC,IAAK;AACV,WAAS,IAAI;AACb,MAAI,GAAG,UAAU,SAAS;AAC1B,MAAI,GAAG,UAAU,SAAS;AAE1B,eAAa;AACX,QAAK,IAAI,UAAU,SAAS;;IAE7B,CAAC,KAAK,SAAS,CAAC;AAEnB,QACE,oBAAC,gBAAgB,UAAjB;EACE,OAAO;GACL;GACK;GACL;GACA,aAAa,gBAAgB,MAAM,SAAS,MAAM,aAAa;GAC/D;GACA;GACA;GACA;GACD;YAED,oBAAC,OAAD;GACE,kBAAkB;GAClB,WAAW,GAAG,YAAY,UAAU;GACpC,MAAK;GACL,wBAAqB;GACrB,aAAU;GACV,GAAI;GAEH;GACG,CAAA;EACmB,CAAA;;AAI/B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAsC;CAC7E,MAAM,EAAE,aAAa,gBAAgB,aAAa;AAElD,QACE,oBAAC,OAAD;EAAK,KAAK;EAAa,WAAU;EAAkB,aAAU;YAC3D,oBAAC,OAAD;GACE,WAAW,GAAG,QAAQ,gBAAgB,eAAe,UAAU,kBAAkB,UAAU;GAC3F,GAAI;GACJ,CAAA;EACE,CAAA;;AAIV,SAAS,aAAa,EAAE,WAAW,GAAG,SAAsC;CAC1E,MAAM,EAAE,gBAAgB,aAAa;AAErC,QACE,oBAAC,OAAD;EACE,MAAK;EACL,wBAAqB;EACrB,aAAU;EACV,WAAW,GACT,sCACA,gBAAgB,eAAe,SAAS,QACxC,UACD;EACD,GAAI;EACJ,CAAA;;AAIN,SAAS,iBAAiB,EACxB,WACA,UAAU,YACV,OAAO,QACP,GAAG,SACmC;CACtC,MAAM,EAAE,aAAa,YAAY,kBAAkB,aAAa;CAChE,MAAM,gBAAgB,QAAQ,YAAY;AAE1C,QACE,qBAAC,QAAD;EACE,aAAU;EACD;EACH;EACN,WAAW,GACT,gCACA,gBAAgB,eACZ,sCACA,+CACJ,UACD;EACD,UAAU,CAAC;EACX,SAAS;EACT,GAAI;YAbN,CAeE,oBAAC,eAAD,EAAiB,CAAA,EACjB,oBAAC,QAAD;GAAM,WAAU;aAAU;GAAqB,CAAA,CACxC;;;AAIb,SAAS,aAAa,EACpB,WACA,UAAU,YACV,OAAO,QACP,GAAG,SACmC;CACtC,MAAM,EAAE,aAAa,YAAY,kBAAkB,aAAa;CAChE,MAAM,iBAAiB,QAAQ,aAAa;AAE5C,QACE,qBAAC,QAAD;EACE,aAAU;EACD;EACH;EACN,WAAW,GACT,gCACA,gBAAgB,eACZ,uCACA,kDACJ,UACD;EACD,UAAU,CAAC;EACX,SAAS;EACT,GAAI;YAbN,CAeE,oBAAC,gBAAD,EAAkB,CAAA,EAClB,oBAAC,QAAD;GAAM,WAAU;aAAU;GAAiB,CAAA,CACpC"}
1
+ {"version":3,"file":"carousel.mjs","names":["React"],"sources":["../../../src/client/ui/carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport useEmblaCarousel, { type UseEmblaCarouselType } from \"embla-carousel-react\";\nimport { WheelGesturesPlugin } from \"embla-carousel-wheel-gestures\";\nimport { useIcon } from \"../icons/icon-context\";\n\nimport { cn } from \"./utils\";\nimport { Button } from \"./button\";\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\ntype CarouselOptions = UseCarouselParameters[0];\ntype CarouselPlugin = UseCarouselParameters[1];\n\ntype CarouselWheelAxis = \"vertical\" | \"horizontal\" | \"both\" | false;\n\ntype CarouselProps = {\n opts?: CarouselOptions;\n plugins?: CarouselPlugin;\n orientation?: \"horizontal\" | \"vertical\";\n /** Controls which wheel/trackpad axes scroll the carousel.\n * - `\"vertical\"` — vertical scroll hijacks carousel (default, mouse-wheel-friendly)\n * - `\"horizontal\"` — horizontal trackpad swipes scroll carousel, vertical passes through\n * - `\"both\"` — both axes scroll the carousel\n * - `false` — no wheel gestures (touch drag still works) */\n wheelAxis?: CarouselWheelAxis;\n setApi?: (api: CarouselApi) => void;\n};\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n api: ReturnType<typeof useEmblaCarousel>[1];\n scrollPrev: () => void;\n scrollNext: () => void;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n} & CarouselProps;\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null);\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext);\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\");\n }\n\n return context;\n}\n\nfunction Carousel({\n orientation = \"horizontal\",\n opts,\n setApi,\n plugins,\n wheelAxis = \"vertical\",\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & CarouselProps) {\n const allPlugins = React.useMemo(() => {\n if (orientation !== \"horizontal\" || wheelAxis === false) {\n return [...(plugins ?? [])];\n }\n\n const wheel: ReturnType<typeof WheelGesturesPlugin>[] = [];\n\n if (wheelAxis === \"vertical\" || wheelAxis === \"both\") {\n wheel.push(WheelGesturesPlugin({ forceWheelAxis: \"y\" }));\n }\n\n if (wheelAxis === \"horizontal\" || wheelAxis === \"both\") {\n const horizontal = WheelGesturesPlugin();\n // Rename so Embla doesn't deduplicate when using both axes\n if (wheelAxis === \"both\") {\n (horizontal as Record<string, unknown>).name = \"wheelGesturesHorizontal\";\n }\n wheel.push(horizontal);\n }\n\n return [...wheel, ...(plugins ?? [])];\n }, [orientation, plugins, wheelAxis]);\n\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n },\n allPlugins,\n );\n const [canScrollPrev, setCanScrollPrev] = React.useState(false);\n const [canScrollNext, setCanScrollNext] = React.useState(false);\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) return;\n setCanScrollPrev(api.canScrollPrev());\n setCanScrollNext(api.canScrollNext());\n }, []);\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev();\n }, [api]);\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext();\n }, [api]);\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n scrollPrev();\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault();\n scrollNext();\n }\n },\n [scrollPrev, scrollNext],\n );\n\n React.useEffect(() => {\n if (!api || !setApi) return;\n setApi(api);\n }, [api, setApi]);\n\n React.useEffect(() => {\n if (!api) return;\n onSelect(api);\n api.on(\"reInit\", onSelect);\n api.on(\"select\", onSelect);\n\n return () => {\n api?.off(\"select\", onSelect);\n };\n }, [api, onSelect]);\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation: orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n onKeyDownCapture={handleKeyDown}\n className={cn(\"relative\", className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n data-slot=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n );\n}\n\nfunction CarouselContent({ className, ...props }: React.ComponentProps<\"div\">) {\n const { carouselRef, orientation } = useCarousel();\n\n return (\n <div ref={carouselRef} className=\"overflow-hidden\" data-slot=\"carousel-content\">\n <div\n className={cn(\"flex\", orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\", className)}\n {...props}\n />\n </div>\n );\n}\n\nfunction CarouselItem({ className, ...props }: React.ComponentProps<\"div\">) {\n const { orientation } = useCarousel();\n\n return (\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n data-slot=\"carousel-item\"\n className={cn(\n \"min-w-0 shrink-0 grow-0 basis-full\",\n orientation === \"horizontal\" ? \"pl-4\" : \"pt-4\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CarouselPrevious({\n className,\n variant = \"outlined\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel();\n const ArrowLeftIcon = useIcon(\"arrowLeft\");\n\n return (\n <Button\n data-slot=\"carousel-previous\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -left-12 -translate-y-1/2\"\n : \"-top-12 left-1/2 -translate-x-1/2 rotate-90\",\n className,\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <ArrowLeftIcon />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n );\n}\n\nfunction CarouselNext({\n className,\n variant = \"outlined\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollNext, canScrollNext } = useCarousel();\n const ArrowRightIcon = useIcon(\"arrowRight\");\n\n return (\n <Button\n data-slot=\"carousel-next\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -right-12 -translate-y-1/2\"\n : \"-bottom-12 left-1/2 -translate-x-1/2 rotate-90\",\n className,\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <ArrowRightIcon />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n );\n}\n\nexport {\n type CarouselApi,\n type CarouselWheelAxis,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n};\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { ComponentName, ComponentOverrides, ComponentSet, ComponentSetProvider, CustomComponentMap, VariantDefaults, useComponentSet, useCustomComponent, useVariantDefault } from "./context.js";
2
+ import { SemanticColor, Size, Variant } from "./types.js";
3
+ import { ButtonProps, ButtonSize, ButtonVariant, buttonRecipe, buttonVariants } from "../ui/button.js";
2
4
  import { Accordion, AccordionContent, AccordionGroup, AccordionItem, AccordionTrigger } from "./accordion.js";
3
5
  import { Alert, AlertDescription, AlertTitle } from "./alert.js";
4
6
  import { Avatar, AvatarFallback, AvatarImage } from "./avatar.js";
5
7
  import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "./breadcrumb.js";
6
- import { SemanticColor, Size, Variant } from "./types.js";
7
- import { ButtonProps, ButtonSize, ButtonVariant, buttonRecipe, buttonVariants } from "../ui/button.js";
8
8
  import { Button } from "./button.js";
9
9
  import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, cardRecipe } from "./card.js";
10
10
  import { Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "./carousel.js";
@@ -10,18 +10,18 @@ import { Checkbox } from "./checkbox.mjs";
10
10
  import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "./pagination.mjs";
11
11
  import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "./breadcrumb.mjs";
12
12
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "./popover.mjs";
13
+ import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "./carousel.mjs";
14
+ import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "./sheet.mjs";
13
15
  import { toggleRecipe, toggleVariants } from "../ui/toggle.mjs";
14
16
  import { Accordion, AccordionContent, AccordionGroup, AccordionItem, AccordionTrigger } from "./accordion.mjs";
15
17
  import { Alert, AlertDescription, AlertTitle } from "./alert.mjs";
16
18
  import { Avatar, AvatarFallback, AvatarImage } from "./avatar.mjs";
17
19
  import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, cardRecipe } from "./card.mjs";
18
- import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "./carousel.mjs";
19
20
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./collapsible.mjs";
20
21
  import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./dialog.mjs";
21
22
  import { HoverCard, HoverCardContent, HoverCardTrigger } from "./hover-card.mjs";
22
23
  import { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, navigationMenuTriggerStyle } from "./navigation-menu.mjs";
23
24
  import { Separator } from "./separator.mjs";
24
- import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "./sheet.mjs";
25
25
  import { Toggle } from "./toggle.mjs";
26
26
  import { ToggleGroup, ToggleGroupItem } from "./toggle-group.mjs";
27
27
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip.mjs";