@nuasite/cms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/README.md +237 -0
  2. package/dist/src/build-processor.d.ts +20 -0
  3. package/dist/src/build-processor.d.ts.map +1 -0
  4. package/dist/src/collection-scanner.d.ts +6 -0
  5. package/dist/src/collection-scanner.d.ts.map +1 -0
  6. package/dist/src/component-registry.d.ts +63 -0
  7. package/dist/src/component-registry.d.ts.map +1 -0
  8. package/dist/src/config.d.ts +24 -0
  9. package/dist/src/config.d.ts.map +1 -0
  10. package/dist/src/dev-middleware.d.ts +20 -0
  11. package/dist/src/dev-middleware.d.ts.map +1 -0
  12. package/dist/src/editor/ai.d.ts +60 -0
  13. package/dist/src/editor/ai.d.ts.map +1 -0
  14. package/dist/src/editor/api.d.ts +140 -0
  15. package/dist/src/editor/api.d.ts.map +1 -0
  16. package/dist/src/editor/color-utils.d.ts +106 -0
  17. package/dist/src/editor/color-utils.d.ts.map +1 -0
  18. package/dist/src/editor/components/ai-chat.d.ts +11 -0
  19. package/dist/src/editor/components/ai-chat.d.ts.map +1 -0
  20. package/dist/src/editor/components/ai-tooltip.d.ts +12 -0
  21. package/dist/src/editor/components/ai-tooltip.d.ts.map +1 -0
  22. package/dist/src/editor/components/attribute-editor.d.ts +5 -0
  23. package/dist/src/editor/components/attribute-editor.d.ts.map +1 -0
  24. package/dist/src/editor/components/block-editor.d.ts +12 -0
  25. package/dist/src/editor/components/block-editor.d.ts.map +1 -0
  26. package/dist/src/editor/components/collections-browser.d.ts +2 -0
  27. package/dist/src/editor/components/collections-browser.d.ts.map +1 -0
  28. package/dist/src/editor/components/color-toolbar.d.ts +12 -0
  29. package/dist/src/editor/components/color-toolbar.d.ts.map +1 -0
  30. package/dist/src/editor/components/confirm-dialog.d.ts +2 -0
  31. package/dist/src/editor/components/confirm-dialog.d.ts.map +1 -0
  32. package/dist/src/editor/components/create-page-modal.d.ts +2 -0
  33. package/dist/src/editor/components/create-page-modal.d.ts.map +1 -0
  34. package/dist/src/editor/components/editable-highlights.d.ts +9 -0
  35. package/dist/src/editor/components/editable-highlights.d.ts.map +1 -0
  36. package/dist/src/editor/components/error-boundary.d.ts +32 -0
  37. package/dist/src/editor/components/error-boundary.d.ts.map +1 -0
  38. package/dist/src/editor/components/fields.d.ts +75 -0
  39. package/dist/src/editor/components/fields.d.ts.map +1 -0
  40. package/dist/src/editor/components/frontmatter-fields.d.ts +29 -0
  41. package/dist/src/editor/components/frontmatter-fields.d.ts.map +1 -0
  42. package/dist/src/editor/components/highlight-overlay.d.ts +64 -0
  43. package/dist/src/editor/components/highlight-overlay.d.ts.map +1 -0
  44. package/dist/src/editor/components/image-overlay.d.ts +12 -0
  45. package/dist/src/editor/components/image-overlay.d.ts.map +1 -0
  46. package/dist/src/editor/components/markdown-editor-overlay.d.ts +6 -0
  47. package/dist/src/editor/components/markdown-editor-overlay.d.ts.map +1 -0
  48. package/dist/src/editor/components/markdown-inline-editor.d.ts +10 -0
  49. package/dist/src/editor/components/markdown-inline-editor.d.ts.map +1 -0
  50. package/dist/src/editor/components/media-library.d.ts +2 -0
  51. package/dist/src/editor/components/media-library.d.ts.map +1 -0
  52. package/dist/src/editor/components/outline.d.ts +21 -0
  53. package/dist/src/editor/components/outline.d.ts.map +1 -0
  54. package/dist/src/editor/components/redirect-countdown.d.ts +2 -0
  55. package/dist/src/editor/components/redirect-countdown.d.ts.map +1 -0
  56. package/dist/src/editor/components/seo-editor.d.ts +2 -0
  57. package/dist/src/editor/components/seo-editor.d.ts.map +1 -0
  58. package/dist/src/editor/components/text-style-toolbar.d.ts +8 -0
  59. package/dist/src/editor/components/text-style-toolbar.d.ts.map +1 -0
  60. package/dist/src/editor/components/toast/toast-container.d.ts +7 -0
  61. package/dist/src/editor/components/toast/toast-container.d.ts.map +1 -0
  62. package/dist/src/editor/components/toast/toast.d.ts +7 -0
  63. package/dist/src/editor/components/toast/toast.d.ts.map +1 -0
  64. package/dist/src/editor/components/toast/types.d.ts +7 -0
  65. package/dist/src/editor/components/toast/types.d.ts.map +1 -0
  66. package/dist/src/editor/components/toolbar.d.ts +21 -0
  67. package/dist/src/editor/components/toolbar.d.ts.map +1 -0
  68. package/dist/src/editor/config.d.ts +4 -0
  69. package/dist/src/editor/config.d.ts.map +1 -0
  70. package/dist/src/editor/constants.d.ts +101 -0
  71. package/dist/src/editor/constants.d.ts.map +1 -0
  72. package/dist/src/editor/context.d.ts +14 -0
  73. package/dist/src/editor/context.d.ts.map +1 -0
  74. package/dist/src/editor/dom.d.ts +77 -0
  75. package/dist/src/editor/dom.d.ts.map +1 -0
  76. package/dist/src/editor/editor.d.ts +64 -0
  77. package/dist/src/editor/editor.d.ts.map +1 -0
  78. package/dist/src/editor/history.d.ts +20 -0
  79. package/dist/src/editor/history.d.ts.map +1 -0
  80. package/dist/src/editor/hooks/index.d.ts +14 -0
  81. package/dist/src/editor/hooks/index.d.ts.map +1 -0
  82. package/dist/src/editor/hooks/useAIHandlers.d.ts +22 -0
  83. package/dist/src/editor/hooks/useAIHandlers.d.ts.map +1 -0
  84. package/dist/src/editor/hooks/useBlockEditorHandlers.d.ts +18 -0
  85. package/dist/src/editor/hooks/useBlockEditorHandlers.d.ts.map +1 -0
  86. package/dist/src/editor/hooks/useElementDetection.d.ts +26 -0
  87. package/dist/src/editor/hooks/useElementDetection.d.ts.map +1 -0
  88. package/dist/src/editor/hooks/useImageHoverDetection.d.ts +12 -0
  89. package/dist/src/editor/hooks/useImageHoverDetection.d.ts.map +1 -0
  90. package/dist/src/editor/hooks/useTextSelection.d.ts +23 -0
  91. package/dist/src/editor/hooks/useTextSelection.d.ts.map +1 -0
  92. package/dist/src/editor/hooks/useTooltipState.d.ts +19 -0
  93. package/dist/src/editor/hooks/useTooltipState.d.ts.map +1 -0
  94. package/dist/src/editor/hooks/utils.d.ts +32 -0
  95. package/dist/src/editor/hooks/utils.d.ts.map +1 -0
  96. package/dist/src/editor/index.d.ts +12 -0
  97. package/dist/src/editor/index.d.ts.map +1 -0
  98. package/dist/src/editor/lib/cn.d.ts +3 -0
  99. package/dist/src/editor/lib/cn.d.ts.map +1 -0
  100. package/dist/src/editor/manifest.d.ts +19 -0
  101. package/dist/src/editor/manifest.d.ts.map +1 -0
  102. package/dist/src/editor/markdown-api.d.ts +36 -0
  103. package/dist/src/editor/markdown-api.d.ts.map +1 -0
  104. package/dist/src/editor/signals.d.ts +242 -0
  105. package/dist/src/editor/signals.d.ts.map +1 -0
  106. package/dist/src/editor/storage.d.ts +27 -0
  107. package/dist/src/editor/storage.d.ts.map +1 -0
  108. package/dist/src/editor/text-styling.d.ts +350 -0
  109. package/dist/src/editor/text-styling.d.ts.map +1 -0
  110. package/dist/src/editor/themes.d.ts +38 -0
  111. package/dist/src/editor/themes.d.ts.map +1 -0
  112. package/dist/src/editor/types.d.ts +454 -0
  113. package/dist/src/editor/types.d.ts.map +1 -0
  114. package/dist/src/error-collector.d.ts +56 -0
  115. package/dist/src/error-collector.d.ts.map +1 -0
  116. package/dist/src/handlers/component-ops.d.ts +34 -0
  117. package/dist/src/handlers/component-ops.d.ts.map +1 -0
  118. package/dist/src/handlers/markdown-ops.d.ts +41 -0
  119. package/dist/src/handlers/markdown-ops.d.ts.map +1 -0
  120. package/dist/src/handlers/request-utils.d.ts +20 -0
  121. package/dist/src/handlers/request-utils.d.ts.map +1 -0
  122. package/dist/src/handlers/source-writer.d.ts +51 -0
  123. package/dist/src/handlers/source-writer.d.ts.map +1 -0
  124. package/dist/src/html-processor.d.ts +63 -0
  125. package/dist/src/html-processor.d.ts.map +1 -0
  126. package/dist/src/index.d.ts +41 -0
  127. package/dist/src/index.d.ts.map +1 -0
  128. package/dist/src/manifest-writer.d.ts +111 -0
  129. package/dist/src/manifest-writer.d.ts.map +1 -0
  130. package/dist/src/media/contember.d.ts +15 -0
  131. package/dist/src/media/contember.d.ts.map +1 -0
  132. package/dist/src/media/local.d.ts +9 -0
  133. package/dist/src/media/local.d.ts.map +1 -0
  134. package/dist/src/media/s3.d.ts +12 -0
  135. package/dist/src/media/s3.d.ts.map +1 -0
  136. package/dist/src/media/types.d.ts +40 -0
  137. package/dist/src/media/types.d.ts.map +1 -0
  138. package/dist/src/preview-generator.d.ts +19 -0
  139. package/dist/src/preview-generator.d.ts.map +1 -0
  140. package/dist/src/seo-processor.d.ts +23 -0
  141. package/dist/src/seo-processor.d.ts.map +1 -0
  142. package/dist/src/source-finder/ast-extractors.d.ts +35 -0
  143. package/dist/src/source-finder/ast-extractors.d.ts.map +1 -0
  144. package/dist/src/source-finder/ast-parser.d.ts +16 -0
  145. package/dist/src/source-finder/ast-parser.d.ts.map +1 -0
  146. package/dist/src/source-finder/cache.d.ts +18 -0
  147. package/dist/src/source-finder/cache.d.ts.map +1 -0
  148. package/dist/src/source-finder/collection-finder.d.ts +29 -0
  149. package/dist/src/source-finder/collection-finder.d.ts.map +1 -0
  150. package/dist/src/source-finder/cross-file-tracker.d.ts +39 -0
  151. package/dist/src/source-finder/cross-file-tracker.d.ts.map +1 -0
  152. package/dist/src/source-finder/element-finder.d.ts +42 -0
  153. package/dist/src/source-finder/element-finder.d.ts.map +1 -0
  154. package/dist/src/source-finder/image-finder.d.ts +24 -0
  155. package/dist/src/source-finder/image-finder.d.ts.map +1 -0
  156. package/dist/src/source-finder/index.d.ts +9 -0
  157. package/dist/src/source-finder/index.d.ts.map +1 -0
  158. package/dist/src/source-finder/search-index.d.ts +27 -0
  159. package/dist/src/source-finder/search-index.d.ts.map +1 -0
  160. package/dist/src/source-finder/snippet-utils.d.ts +90 -0
  161. package/dist/src/source-finder/snippet-utils.d.ts.map +1 -0
  162. package/dist/src/source-finder/source-lookup.d.ts +16 -0
  163. package/dist/src/source-finder/source-lookup.d.ts.map +1 -0
  164. package/dist/src/source-finder/types.d.ts +167 -0
  165. package/dist/src/source-finder/types.d.ts.map +1 -0
  166. package/dist/src/source-finder/variable-extraction.d.ts +37 -0
  167. package/dist/src/source-finder/variable-extraction.d.ts.map +1 -0
  168. package/dist/src/tailwind-colors.d.ts +54 -0
  169. package/dist/src/tailwind-colors.d.ts.map +1 -0
  170. package/dist/src/tsconfig.tsbuildinfo +1 -0
  171. package/dist/src/types.d.ts +367 -0
  172. package/dist/src/types.d.ts.map +1 -0
  173. package/dist/src/utils.d.ts +61 -0
  174. package/dist/src/utils.d.ts.map +1 -0
  175. package/dist/src/vite-plugin.d.ts +14 -0
  176. package/dist/src/vite-plugin.d.ts.map +1 -0
  177. package/dist/types/tsconfig.tsbuildinfo +1 -0
  178. package/package.json +80 -0
  179. package/src/build-processor.ts +784 -0
  180. package/src/collection-scanner.ts +304 -0
  181. package/src/component-registry.ts +393 -0
  182. package/src/config.ts +74 -0
  183. package/src/dev-middleware.ts +525 -0
  184. package/src/dist/src/tsconfig.tsbuildinfo +1 -0
  185. package/src/editor/ai.ts +185 -0
  186. package/src/editor/api.ts +513 -0
  187. package/src/editor/color-utils.ts +556 -0
  188. package/src/editor/components/ai-chat.tsx +632 -0
  189. package/src/editor/components/ai-tooltip.tsx +179 -0
  190. package/src/editor/components/attribute-editor.tsx +596 -0
  191. package/src/editor/components/block-editor.tsx +546 -0
  192. package/src/editor/components/collections-browser.tsx +248 -0
  193. package/src/editor/components/color-toolbar.tsx +314 -0
  194. package/src/editor/components/confirm-dialog.tsx +69 -0
  195. package/src/editor/components/create-page-modal.tsx +163 -0
  196. package/src/editor/components/editable-highlights.tsx +260 -0
  197. package/src/editor/components/error-boundary.tsx +87 -0
  198. package/src/editor/components/fields.tsx +387 -0
  199. package/src/editor/components/frontmatter-fields.tsx +469 -0
  200. package/src/editor/components/highlight-overlay.ts +229 -0
  201. package/src/editor/components/image-overlay.tsx +230 -0
  202. package/src/editor/components/markdown-editor-overlay.tsx +505 -0
  203. package/src/editor/components/markdown-inline-editor.tsx +780 -0
  204. package/src/editor/components/media-library.tsx +297 -0
  205. package/src/editor/components/outline.tsx +402 -0
  206. package/src/editor/components/redirect-countdown.tsx +45 -0
  207. package/src/editor/components/seo-editor.tsx +498 -0
  208. package/src/editor/components/text-style-toolbar.tsx +362 -0
  209. package/src/editor/components/toast/toast-container.tsx +15 -0
  210. package/src/editor/components/toast/toast.tsx +49 -0
  211. package/src/editor/components/toast/types.ts +7 -0
  212. package/src/editor/components/toolbar.tsx +366 -0
  213. package/src/editor/config.ts +12 -0
  214. package/src/editor/constants.ts +106 -0
  215. package/src/editor/context.tsx +38 -0
  216. package/src/editor/dom.ts +357 -0
  217. package/src/editor/editor.ts +1510 -0
  218. package/src/editor/env.d.ts +4 -0
  219. package/src/editor/history.ts +355 -0
  220. package/src/editor/hooks/index.ts +19 -0
  221. package/src/editor/hooks/useAIHandlers.ts +345 -0
  222. package/src/editor/hooks/useBlockEditorHandlers.ts +206 -0
  223. package/src/editor/hooks/useElementDetection.ts +284 -0
  224. package/src/editor/hooks/useImageHoverDetection.ts +102 -0
  225. package/src/editor/hooks/useTextSelection.ts +187 -0
  226. package/src/editor/hooks/useTooltipState.ts +126 -0
  227. package/src/editor/hooks/utils.ts +101 -0
  228. package/src/editor/index.tsx +481 -0
  229. package/src/editor/lib/cn.ts +4 -0
  230. package/src/editor/manifest.ts +25 -0
  231. package/src/editor/markdown-api.ts +209 -0
  232. package/src/editor/signals.ts +1351 -0
  233. package/src/editor/storage.ts +266 -0
  234. package/src/editor/styles.css +465 -0
  235. package/src/editor/text-styling.ts +773 -0
  236. package/src/editor/themes.ts +210 -0
  237. package/src/editor/types.ts +591 -0
  238. package/src/error-collector.ts +106 -0
  239. package/src/handlers/component-ops.ts +463 -0
  240. package/src/handlers/markdown-ops.ts +202 -0
  241. package/src/handlers/request-utils.ts +151 -0
  242. package/src/handlers/source-writer.ts +649 -0
  243. package/src/html-processor.ts +1108 -0
  244. package/src/index.ts +284 -0
  245. package/src/manifest-writer.ts +371 -0
  246. package/src/media/contember.ts +84 -0
  247. package/src/media/local.ts +114 -0
  248. package/src/media/s3.ts +133 -0
  249. package/src/media/types.ts +33 -0
  250. package/src/preview-generator.ts +293 -0
  251. package/src/seo-processor.ts +567 -0
  252. package/src/source-finder/ast-extractors.ts +185 -0
  253. package/src/source-finder/ast-parser.ts +150 -0
  254. package/src/source-finder/cache.ts +76 -0
  255. package/src/source-finder/collection-finder.ts +335 -0
  256. package/src/source-finder/cross-file-tracker.ts +741 -0
  257. package/src/source-finder/element-finder.ts +387 -0
  258. package/src/source-finder/image-finder.ts +283 -0
  259. package/src/source-finder/index.ts +37 -0
  260. package/src/source-finder/search-index.ts +525 -0
  261. package/src/source-finder/snippet-utils.ts +668 -0
  262. package/src/source-finder/source-lookup.ts +200 -0
  263. package/src/source-finder/types.ts +210 -0
  264. package/src/source-finder/variable-extraction.ts +406 -0
  265. package/src/tailwind-colors.ts +874 -0
  266. package/src/tsconfig.json +25 -0
  267. package/src/types.ts +406 -0
  268. package/src/utils.ts +186 -0
  269. package/src/vite-plugin.ts +42 -0
@@ -0,0 +1,556 @@
1
+ import type { Attribute, AvailableColors, TailwindColor } from './types'
2
+
3
+ /**
4
+ * Default Tailwind CSS v4 color names.
5
+ */
6
+ export const DEFAULT_TAILWIND_COLORS = [
7
+ 'slate',
8
+ 'gray',
9
+ 'zinc',
10
+ 'neutral',
11
+ 'stone',
12
+ 'red',
13
+ 'orange',
14
+ 'amber',
15
+ 'yellow',
16
+ 'lime',
17
+ 'green',
18
+ 'emerald',
19
+ 'teal',
20
+ 'cyan',
21
+ 'sky',
22
+ 'blue',
23
+ 'indigo',
24
+ 'violet',
25
+ 'purple',
26
+ 'fuchsia',
27
+ 'pink',
28
+ 'rose',
29
+ ] as const
30
+
31
+ /**
32
+ * Standard Tailwind color shades.
33
+ */
34
+ export const STANDARD_SHADES = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950'] as const
35
+
36
+ /**
37
+ * Special color values that don't have shades.
38
+ */
39
+ export const SPECIAL_COLORS = ['transparent', 'current', 'inherit', 'white', 'black'] as const
40
+
41
+ /**
42
+ * Map of Tailwind color names to their CSS color values for preview.
43
+ * Uses the 500 shade as the representative color.
44
+ * This is a fallback when availableColors is not loaded yet.
45
+ */
46
+ export const COLOR_PREVIEW_MAP: Record<string, string> = {
47
+ // Special colors
48
+ transparent: 'transparent',
49
+ current: 'currentColor',
50
+ inherit: 'inherit',
51
+ white: '#ffffff',
52
+ black: '#000000',
53
+ // Standard colors (500 shade)
54
+ slate: '#64748b',
55
+ gray: '#6b7280',
56
+ zinc: '#71717a',
57
+ neutral: '#737373',
58
+ stone: '#78716c',
59
+ red: '#ef4444',
60
+ orange: '#f97316',
61
+ amber: '#f59e0b',
62
+ yellow: '#eab308',
63
+ lime: '#84cc16',
64
+ green: '#22c55e',
65
+ emerald: '#10b981',
66
+ teal: '#14b8a6',
67
+ cyan: '#06b6d4',
68
+ sky: '#0ea5e9',
69
+ blue: '#3b82f6',
70
+ indigo: '#6366f1',
71
+ violet: '#8b5cf6',
72
+ purple: '#a855f7',
73
+ fuchsia: '#d946ef',
74
+ pink: '#ec4899',
75
+ rose: '#f43f5e',
76
+ // Common custom colors
77
+ primary: '#3b82f6',
78
+ secondary: '#6b7280',
79
+ accent: '#f59e0b',
80
+ }
81
+
82
+ /**
83
+ * Map of shade numbers to their relative lightness.
84
+ * Used to generate preview colors for different shades.
85
+ */
86
+ export const SHADE_LIGHTNESS: Record<string, number> = {
87
+ '50': 0.95,
88
+ '100': 0.9,
89
+ '200': 0.8,
90
+ '300': 0.7,
91
+ '400': 0.6,
92
+ '500': 0.5,
93
+ '600': 0.4,
94
+ '700': 0.3,
95
+ '800': 0.2,
96
+ '900': 0.1,
97
+ '950': 0.05,
98
+ }
99
+
100
+ /**
101
+ * Non-color text-* utility classes that should NOT be treated as color classes.
102
+ * These are Tailwind utilities like text alignment, sizing, wrapping, etc.
103
+ */
104
+ const NON_COLOR_TEXT_CLASSES = new Set([
105
+ // Text alignment
106
+ 'text-left',
107
+ 'text-center',
108
+ 'text-right',
109
+ 'text-justify',
110
+ 'text-start',
111
+ 'text-end',
112
+ // Text wrapping
113
+ 'text-wrap',
114
+ 'text-nowrap',
115
+ 'text-balance',
116
+ 'text-pretty',
117
+ // Text overflow
118
+ 'text-ellipsis',
119
+ 'text-clip',
120
+ // Text transform (these don't start with text- but just in case)
121
+ ])
122
+
123
+ /**
124
+ * Regex patterns to match Tailwind color classes.
125
+ */
126
+ const COLOR_CLASS_PATTERNS = {
127
+ bg: /^bg-([a-z]+)(?:-(\d+))?$/,
128
+ text: /^text-([a-z]+)(?:-(\d+))?$/,
129
+ border: /^border-([a-z]+)(?:-(\d+))?$/,
130
+ hoverBg: /^hover:bg-([a-z]+)(?:-(\d+))?$/,
131
+ hoverText: /^hover:text-([a-z]+)(?:-(\d+))?$/,
132
+ }
133
+
134
+ /**
135
+ * Check if a class is a text color class (not a non-color text utility).
136
+ */
137
+ function isTextColorClass(className: string): boolean {
138
+ if (NON_COLOR_TEXT_CLASSES.has(className)) {
139
+ return false
140
+ }
141
+ return COLOR_CLASS_PATTERNS.text.test(className)
142
+ }
143
+
144
+ /**
145
+ * Parse a color class into its components.
146
+ */
147
+ export function parseColorClass(colorClass: string): {
148
+ prefix: string
149
+ colorName: string
150
+ shade?: string
151
+ isHover: boolean
152
+ } | undefined {
153
+ // Exclude non-color text utility classes
154
+ if (NON_COLOR_TEXT_CLASSES.has(colorClass)) {
155
+ return undefined
156
+ }
157
+
158
+ const isHover = colorClass.startsWith('hover:')
159
+ const classWithoutHover = isHover ? colorClass.slice(6) : colorClass
160
+
161
+ // Also check hover variants of non-color text classes
162
+ if (isHover && NON_COLOR_TEXT_CLASSES.has(`text-${classWithoutHover.slice(5)}`)) {
163
+ return undefined
164
+ }
165
+
166
+ const match = classWithoutHover.match(/^(bg|text|border)-([a-z]+)(?:-(\d+))?$/)
167
+
168
+ if (!match) return undefined
169
+
170
+ return {
171
+ prefix: isHover ? `hover:${match[1]}` : match[1]!,
172
+ colorName: match[2]!,
173
+ shade: match[3],
174
+ isHover,
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Build a color class from components.
180
+ */
181
+ export function buildColorClass(
182
+ prefix: string,
183
+ colorName: string,
184
+ shade?: string,
185
+ ): string {
186
+ if (shade) {
187
+ return `${prefix}-${colorName}-${shade}`
188
+ }
189
+ return `${prefix}-${colorName}`
190
+ }
191
+
192
+ /**
193
+ * Get the color type from a color class.
194
+ */
195
+ export function getColorType(colorClass: string): keyof typeof COLOR_CLASS_PATTERNS | undefined {
196
+ for (const [key, pattern] of Object.entries(COLOR_CLASS_PATTERNS)) {
197
+ // For text type, use the helper to exclude non-color text utilities
198
+ if (key === 'text' || key === 'hoverText') {
199
+ if (key === 'text' && isTextColorClass(colorClass)) {
200
+ return key as keyof typeof COLOR_CLASS_PATTERNS
201
+ }
202
+ if (key === 'hoverText' && colorClass.startsWith('hover:') && isTextColorClass(colorClass.slice(6))) {
203
+ return key as keyof typeof COLOR_CLASS_PATTERNS
204
+ }
205
+ } else if (pattern.test(colorClass)) {
206
+ return key as keyof typeof COLOR_CLASS_PATTERNS
207
+ }
208
+ }
209
+ return undefined
210
+ }
211
+
212
+ /**
213
+ * Check if a class is a color class.
214
+ */
215
+ export function isColorClass(className: string): boolean {
216
+ // Check text color separately to exclude non-color text utilities
217
+ if (COLOR_CLASS_PATTERNS.text.test(className)) {
218
+ return isTextColorClass(className)
219
+ }
220
+ if (COLOR_CLASS_PATTERNS.hoverText.test(className)) {
221
+ return isTextColorClass(className.slice(6)) // Remove 'hover:' prefix
222
+ }
223
+ return Object.entries(COLOR_CLASS_PATTERNS)
224
+ .filter(([key]) => key !== 'text' && key !== 'hoverText')
225
+ .some(([, pattern]) => pattern.test(className))
226
+ }
227
+
228
+ /**
229
+ * Get a preview CSS color for a Tailwind color.
230
+ */
231
+ export function getColorPreview(colorName: string, shade?: string): string {
232
+ // Special colors without shades
233
+ if (SPECIAL_COLORS.includes(colorName as any)) {
234
+ return COLOR_PREVIEW_MAP[colorName] || colorName
235
+ }
236
+
237
+ // Get base color
238
+ const baseColor = COLOR_PREVIEW_MAP[colorName]
239
+ if (!baseColor) {
240
+ // Unknown color, return a placeholder
241
+ return '#888888'
242
+ }
243
+
244
+ // If no shade specified, return the base (500) color
245
+ if (!shade) {
246
+ return baseColor
247
+ }
248
+
249
+ // For now, return the base color
250
+ // A more sophisticated implementation would adjust lightness based on shade
251
+ return baseColor
252
+ }
253
+
254
+ /**
255
+ * Resolve a color to its CSS value using availableColors from the manifest.
256
+ */
257
+ export function resolveColorValue(
258
+ colorName: string,
259
+ shade: string | undefined,
260
+ availableColors: AvailableColors | undefined,
261
+ ): string | undefined {
262
+ // Special colors
263
+ if (colorName === 'white') return '#ffffff'
264
+ if (colorName === 'black') return '#000000'
265
+ if (colorName === 'transparent') return 'transparent'
266
+ if (colorName === 'current') return 'currentColor'
267
+ if (colorName === 'inherit') return 'inherit'
268
+
269
+ if (!availableColors) {
270
+ // Fallback to preview map
271
+ return COLOR_PREVIEW_MAP[colorName]
272
+ }
273
+
274
+ // Find the color in availableColors
275
+ const color = availableColors.colors.find(c => c.name === colorName)
276
+ if (!color) return undefined
277
+
278
+ // Get the value for the shade (or empty string for colors without shades)
279
+ const shadeKey = shade || '500'
280
+ return color.values[shadeKey]
281
+ }
282
+
283
+ /**
284
+ * Replace a color class in an element's class list.
285
+ * Returns the old class that was replaced, or undefined if not found.
286
+ */
287
+ export function replaceColorClass(
288
+ element: HTMLElement,
289
+ colorType: 'bg' | 'text' | 'border' | 'hoverBg' | 'hoverText',
290
+ newColorName: string,
291
+ newShade?: string,
292
+ ): { oldClass: string; newClass: string } | undefined {
293
+ const classes = element.className.split(/\s+/).filter(Boolean)
294
+ const pattern = COLOR_CLASS_PATTERNS[colorType]
295
+
296
+ let oldClass: string | undefined
297
+ const newClasses: string[] = []
298
+
299
+ for (const cls of classes) {
300
+ // For text types, use the helper to exclude non-color text utilities
301
+ let isColorMatch: boolean
302
+ if (colorType === 'text') {
303
+ isColorMatch = isTextColorClass(cls)
304
+ } else if (colorType === 'hoverText') {
305
+ isColorMatch = cls.startsWith('hover:') && isTextColorClass(cls.slice(6))
306
+ } else {
307
+ isColorMatch = pattern.test(cls)
308
+ }
309
+ if (isColorMatch) {
310
+ oldClass = cls
311
+ // Build the new class with the same prefix
312
+ const isHover = colorType.startsWith('hover')
313
+ const prefix = isHover
314
+ ? colorType.replace('hover', 'hover:').toLowerCase().replace('hover:bg', 'hover:bg').replace('hover:text', 'hover:text')
315
+ : colorType
316
+ const newClass = buildColorClass(prefix, newColorName, newShade)
317
+ newClasses.push(newClass)
318
+ } else {
319
+ newClasses.push(cls)
320
+ }
321
+ }
322
+
323
+ if (!oldClass) {
324
+ return undefined
325
+ }
326
+
327
+ const newClass = buildColorClass(
328
+ colorType.startsWith('hover') ? `hover:${colorType.slice(5).toLowerCase()}` : colorType,
329
+ newColorName,
330
+ newShade,
331
+ )
332
+
333
+ element.className = newClasses.join(' ')
334
+
335
+ return { oldClass, newClass }
336
+ }
337
+
338
+ /**
339
+ * Get the current color classes from an element as Record<string, Attribute>.
340
+ */
341
+ export function getElementColorClasses(element: HTMLElement): Record<string, Attribute> {
342
+ const classes = element.className.split(/\s+/).filter(Boolean)
343
+ const colorClasses: Record<string, Attribute> = {}
344
+
345
+ for (const cls of classes) {
346
+ for (const [key, pattern] of Object.entries(COLOR_CLASS_PATTERNS)) {
347
+ // For text types, use the helper to exclude non-color text utilities
348
+ let isColorMatch: boolean
349
+ if (key === 'text') {
350
+ isColorMatch = isTextColorClass(cls)
351
+ } else if (key === 'hoverText') {
352
+ isColorMatch = cls.startsWith('hover:') && isTextColorClass(cls.slice(6))
353
+ } else {
354
+ isColorMatch = pattern.test(cls)
355
+ }
356
+ if (isColorMatch) {
357
+ if (!(key in colorClasses)) {
358
+ colorClasses[key] = { value: cls }
359
+ }
360
+ break
361
+ }
362
+ }
363
+ }
364
+
365
+ return colorClasses
366
+ }
367
+
368
+ /**
369
+ * Inject global hover preview styles once.
370
+ * Uses CSS custom properties with !important to preview hover colors.
371
+ */
372
+ let hoverStylesInjected = false
373
+ function ensureHoverStyles(): void {
374
+ if (hoverStylesInjected) return
375
+ hoverStylesInjected = true
376
+
377
+ const style = document.createElement('style')
378
+ style.textContent = `
379
+ [data-cms-hover-bg]:hover {
380
+ background-color: var(--cms-hover-bg) !important;
381
+ }
382
+ [data-cms-hover-text]:hover {
383
+ color: var(--cms-hover-text) !important;
384
+ }
385
+ `
386
+ document.head.appendChild(style)
387
+ }
388
+
389
+ /**
390
+ * Apply a color change to an element.
391
+ * Updates the DOM immediately with both class and inline style for preview.
392
+ * The inline style ensures the color is visible even if Tailwind hasn't compiled the class.
393
+ */
394
+ export function applyColorChange(
395
+ element: HTMLElement,
396
+ colorType: 'bg' | 'text' | 'border' | 'hoverBg' | 'hoverText',
397
+ newColorName: string,
398
+ newShade: string | undefined,
399
+ availableColors: AvailableColors | undefined,
400
+ ): { oldClass: string; newClass: string } | undefined {
401
+ const classes = element.className.split(/\s+/).filter(Boolean)
402
+ const pattern = COLOR_CLASS_PATTERNS[colorType]
403
+
404
+ let oldClass: string | undefined
405
+ const newClasses: string[] = []
406
+
407
+ // Determine the new class prefix
408
+ const prefix = colorType === 'hoverBg'
409
+ ? 'hover:bg'
410
+ : colorType === 'hoverText'
411
+ ? 'hover:text'
412
+ : colorType
413
+ const newClass = buildColorClass(prefix, newColorName, newShade)
414
+
415
+ for (const cls of classes) {
416
+ // For text types, use the helper to exclude non-color text utilities
417
+ let isColorMatch: boolean
418
+ if (colorType === 'text') {
419
+ isColorMatch = isTextColorClass(cls)
420
+ } else if (colorType === 'hoverText') {
421
+ isColorMatch = cls.startsWith('hover:') && isTextColorClass(cls.slice(6))
422
+ } else {
423
+ isColorMatch = pattern.test(cls)
424
+ }
425
+ if (isColorMatch) {
426
+ oldClass = cls
427
+ // Replace with new class
428
+ newClasses.push(newClass)
429
+ } else {
430
+ newClasses.push(cls)
431
+ }
432
+ }
433
+
434
+ // If no existing color class was found, add the new one
435
+ if (!oldClass) {
436
+ newClasses.push(newClass)
437
+ }
438
+
439
+ element.className = newClasses.join(' ')
440
+
441
+ // Apply inline style for immediate visual feedback
442
+ // This ensures the color is visible even if Tailwind hasn't compiled the new class
443
+ const cssValue = resolveColorValue(newColorName, newShade, availableColors)
444
+ if (cssValue) {
445
+ // For hover states, use CSS custom properties with global :hover rules
446
+ if (colorType === 'hoverBg') {
447
+ ensureHoverStyles()
448
+ element.dataset.cmsHoverBg = ''
449
+ element.style.setProperty('--cms-hover-bg', cssValue)
450
+ } else if (colorType === 'hoverText') {
451
+ ensureHoverStyles()
452
+ element.dataset.cmsHoverText = ''
453
+ element.style.setProperty('--cms-hover-text', cssValue)
454
+ } else {
455
+ // Map color type to CSS property for non-hover states
456
+ const styleProperty = colorType === 'bg'
457
+ ? 'backgroundColor'
458
+ : colorType === 'text'
459
+ ? 'color'
460
+ : colorType === 'border'
461
+ ? 'borderColor'
462
+ : 'color'
463
+ element.style[styleProperty] = cssValue
464
+ }
465
+ }
466
+
467
+ return { oldClass: oldClass || '', newClass }
468
+ }
469
+
470
+ /**
471
+ * Get popular colors for quick selection.
472
+ * Returns a curated list of commonly used colors.
473
+ */
474
+ export function getPopularColors(): Array<{ name: string; shade: string; preview: string }> {
475
+ const popularColorNames = ['blue', 'green', 'red', 'purple', 'orange', 'slate', 'black', 'white']
476
+ const popularShades = ['500', '600', '700']
477
+
478
+ const colors: Array<{ name: string; shade: string; preview: string }> = []
479
+
480
+ // Add special colors first
481
+ colors.push({ name: 'white', shade: '', preview: '#ffffff' })
482
+ colors.push({ name: 'black', shade: '', preview: '#000000' })
483
+
484
+ // Add popular color/shade combinations
485
+ for (const name of popularColorNames) {
486
+ if (name === 'white' || name === 'black') continue
487
+ for (const shade of popularShades) {
488
+ colors.push({
489
+ name,
490
+ shade,
491
+ preview: getColorPreview(name, shade),
492
+ })
493
+ }
494
+ }
495
+
496
+ return colors
497
+ }
498
+
499
+ /**
500
+ * Get all available colors with their shades from manifest.
501
+ */
502
+ export function getAllColorsWithShades(availableColors: AvailableColors | undefined): Array<{
503
+ name: string
504
+ shades: Array<{ shade: string; preview: string }>
505
+ isSpecial: boolean
506
+ }> {
507
+ if (!availableColors) {
508
+ // Return default colors
509
+ const result: Array<{
510
+ name: string
511
+ shades: Array<{ shade: string; preview: string }>
512
+ isSpecial: boolean
513
+ }> = []
514
+
515
+ // Special colors
516
+ for (const color of SPECIAL_COLORS) {
517
+ if (color !== 'current' && color !== 'inherit' && color !== 'transparent') {
518
+ result.push({
519
+ name: color,
520
+ shades: [{ shade: '', preview: getColorPreview(color) }],
521
+ isSpecial: true,
522
+ })
523
+ }
524
+ }
525
+
526
+ // Standard colors
527
+ for (const color of DEFAULT_TAILWIND_COLORS) {
528
+ result.push({
529
+ name: color,
530
+ shades: STANDARD_SHADES.map(shade => ({
531
+ shade,
532
+ preview: getColorPreview(color, shade),
533
+ })),
534
+ isSpecial: false,
535
+ })
536
+ }
537
+
538
+ return result
539
+ }
540
+
541
+ return availableColors.colors.map(color => {
542
+ const shades = Object.keys(color.values)
543
+ const isSpecial = SPECIAL_COLORS.includes(color.name as any)
544
+
545
+ return {
546
+ name: color.name,
547
+ shades: shades.length > 0
548
+ ? shades.map(shade => ({
549
+ shade,
550
+ preview: color.values[shade] || getColorPreview(color.name, shade),
551
+ }))
552
+ : [{ shade: '', preview: color.values[''] || getColorPreview(color.name) }],
553
+ isSpecial,
554
+ }
555
+ })
556
+ }