meno-core 1.0.38 → 1.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-astro.ts +914 -0
- package/dist/build-static.js +2 -2
- package/dist/chunks/{chunk-UR7L5UZ3.js → chunk-HNAS6BSS.js} +2 -2
- package/dist/chunks/{chunk-EUCAKI5U.js → chunk-W6HDII4T.js} +8 -1
- package/dist/chunks/{chunk-EUCAKI5U.js.map → chunk-W6HDII4T.js.map} +2 -2
- package/dist/chunks/{chunk-JACS3C25.js → chunk-WK5XLASY.js} +2 -2
- package/dist/entries/server-router.js +2 -2
- package/dist/lib/client/index.js +5 -3
- package/dist/lib/client/index.js.map +2 -2
- package/dist/lib/server/index.js +1840 -5
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +5 -3
- package/dist/lib/shared/index.js.map +2 -2
- package/lib/client/theme.ts +4 -1
- package/lib/server/astro/componentEmitter.ts +208 -0
- package/lib/server/astro/cssCollector.ts +147 -0
- package/lib/server/astro/index.ts +5 -0
- package/lib/server/astro/nodeToAstro.ts +771 -0
- package/lib/server/astro/pageEmitter.ts +190 -0
- package/lib/server/astro/tailwindMapper.ts +547 -0
- package/lib/server/index.ts +3 -0
- package/lib/server/ssr/htmlGenerator.ts +3 -0
- package/lib/server/ssr/ssrRenderer.test.ts +8 -4
- package/lib/server/ssr/ssrRenderer.ts +1 -3
- package/lib/shared/themeDefaults.test.ts +1 -1
- package/lib/shared/themeDefaults.ts +4 -1
- package/package.json +1 -1
- /package/dist/chunks/{chunk-UR7L5UZ3.js.map → chunk-HNAS6BSS.js.map} +0 -0
- /package/dist/chunks/{chunk-JACS3C25.js.map → chunk-WK5XLASY.js.map} +0 -0
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind CSS Class Mapper for Astro Export
|
|
3
|
+
* Converts CSS property:value pairs to Tailwind utility classes.
|
|
4
|
+
* Uses exact-match table for common values, arbitrary value fallback for the rest.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
StyleObject,
|
|
9
|
+
ResponsiveStyleObject,
|
|
10
|
+
StyleMapping,
|
|
11
|
+
} from '../../shared/types/styles';
|
|
12
|
+
import type { BreakpointConfig } from '../../shared/breakpoints';
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Helpers
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
function isStyleMapping(value: unknown): value is StyleMapping {
|
|
19
|
+
return (
|
|
20
|
+
typeof value === 'object' &&
|
|
21
|
+
value !== null &&
|
|
22
|
+
'_mapping' in value &&
|
|
23
|
+
(value as StyleMapping)._mapping === true
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hasTemplateExpression(value: string): boolean {
|
|
28
|
+
return /\{\{.+?\}\}/.test(value);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isResponsiveStyle(
|
|
32
|
+
style: StyleObject | ResponsiveStyleObject
|
|
33
|
+
): style is ResponsiveStyleObject {
|
|
34
|
+
return 'base' in style || 'tablet' in style || 'mobile' in style;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Exact match table: property+value → Tailwind class
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
const exactMatches: Record<string, Record<string, string>> = {
|
|
42
|
+
display: {
|
|
43
|
+
flex: 'flex',
|
|
44
|
+
grid: 'grid',
|
|
45
|
+
block: 'block',
|
|
46
|
+
none: 'hidden',
|
|
47
|
+
inline: 'inline',
|
|
48
|
+
'inline-block': 'inline-block',
|
|
49
|
+
'inline-flex': 'inline-flex',
|
|
50
|
+
'inline-grid': 'inline-grid',
|
|
51
|
+
},
|
|
52
|
+
flexDirection: {
|
|
53
|
+
column: 'flex-col',
|
|
54
|
+
row: 'flex-row',
|
|
55
|
+
'column-reverse': 'flex-col-reverse',
|
|
56
|
+
'row-reverse': 'flex-row-reverse',
|
|
57
|
+
},
|
|
58
|
+
justifyContent: {
|
|
59
|
+
center: 'justify-center',
|
|
60
|
+
'flex-start': 'justify-start',
|
|
61
|
+
'flex-end': 'justify-end',
|
|
62
|
+
'space-between': 'justify-between',
|
|
63
|
+
'space-around': 'justify-around',
|
|
64
|
+
'space-evenly': 'justify-evenly',
|
|
65
|
+
// camelCase aliases (component data uses camelCase)
|
|
66
|
+
spaceBetween: 'justify-between',
|
|
67
|
+
spaceAround: 'justify-around',
|
|
68
|
+
spaceEvenly: 'justify-evenly',
|
|
69
|
+
flexStart: 'justify-start',
|
|
70
|
+
flexEnd: 'justify-end',
|
|
71
|
+
},
|
|
72
|
+
alignItems: {
|
|
73
|
+
center: 'items-center',
|
|
74
|
+
'flex-start': 'items-start',
|
|
75
|
+
'flex-end': 'items-end',
|
|
76
|
+
stretch: 'items-stretch',
|
|
77
|
+
baseline: 'items-baseline',
|
|
78
|
+
// camelCase aliases
|
|
79
|
+
flexStart: 'items-start',
|
|
80
|
+
flexEnd: 'items-end',
|
|
81
|
+
},
|
|
82
|
+
alignContent: {
|
|
83
|
+
center: 'content-center',
|
|
84
|
+
'flex-start': 'content-start',
|
|
85
|
+
'flex-end': 'content-end',
|
|
86
|
+
'space-between': 'content-between',
|
|
87
|
+
'space-around': 'content-around',
|
|
88
|
+
stretch: 'content-stretch',
|
|
89
|
+
},
|
|
90
|
+
alignSelf: {
|
|
91
|
+
auto: 'self-auto',
|
|
92
|
+
center: 'self-center',
|
|
93
|
+
'flex-start': 'self-start',
|
|
94
|
+
'flex-end': 'self-end',
|
|
95
|
+
stretch: 'self-stretch',
|
|
96
|
+
},
|
|
97
|
+
position: {
|
|
98
|
+
relative: 'relative',
|
|
99
|
+
absolute: 'absolute',
|
|
100
|
+
fixed: 'fixed',
|
|
101
|
+
sticky: 'sticky',
|
|
102
|
+
static: 'static',
|
|
103
|
+
},
|
|
104
|
+
overflow: {
|
|
105
|
+
hidden: 'overflow-hidden',
|
|
106
|
+
auto: 'overflow-auto',
|
|
107
|
+
scroll: 'overflow-scroll',
|
|
108
|
+
visible: 'overflow-visible',
|
|
109
|
+
},
|
|
110
|
+
overflowX: {
|
|
111
|
+
hidden: 'overflow-x-hidden',
|
|
112
|
+
auto: 'overflow-x-auto',
|
|
113
|
+
scroll: 'overflow-x-scroll',
|
|
114
|
+
visible: 'overflow-x-visible',
|
|
115
|
+
},
|
|
116
|
+
overflowY: {
|
|
117
|
+
hidden: 'overflow-y-hidden',
|
|
118
|
+
auto: 'overflow-y-auto',
|
|
119
|
+
scroll: 'overflow-y-scroll',
|
|
120
|
+
visible: 'overflow-y-visible',
|
|
121
|
+
},
|
|
122
|
+
cursor: {
|
|
123
|
+
pointer: 'cursor-pointer',
|
|
124
|
+
default: 'cursor-default',
|
|
125
|
+
'not-allowed': 'cursor-not-allowed',
|
|
126
|
+
grab: 'cursor-grab',
|
|
127
|
+
grabbing: 'cursor-grabbing',
|
|
128
|
+
text: 'cursor-text',
|
|
129
|
+
move: 'cursor-move',
|
|
130
|
+
wait: 'cursor-wait',
|
|
131
|
+
},
|
|
132
|
+
textAlign: {
|
|
133
|
+
center: 'text-center',
|
|
134
|
+
left: 'text-left',
|
|
135
|
+
right: 'text-right',
|
|
136
|
+
justify: 'text-justify',
|
|
137
|
+
},
|
|
138
|
+
textDecoration: {
|
|
139
|
+
none: 'no-underline',
|
|
140
|
+
underline: 'underline',
|
|
141
|
+
'line-through': 'line-through',
|
|
142
|
+
overline: 'overline',
|
|
143
|
+
},
|
|
144
|
+
textTransform: {
|
|
145
|
+
uppercase: 'uppercase',
|
|
146
|
+
lowercase: 'lowercase',
|
|
147
|
+
capitalize: 'capitalize',
|
|
148
|
+
none: 'normal-case',
|
|
149
|
+
},
|
|
150
|
+
objectFit: {
|
|
151
|
+
cover: 'object-cover',
|
|
152
|
+
contain: 'object-contain',
|
|
153
|
+
fill: 'object-fill',
|
|
154
|
+
none: 'object-none',
|
|
155
|
+
'scale-down': 'object-scale-down',
|
|
156
|
+
},
|
|
157
|
+
objectPosition: {
|
|
158
|
+
center: 'object-center',
|
|
159
|
+
top: 'object-top',
|
|
160
|
+
bottom: 'object-bottom',
|
|
161
|
+
left: 'object-left',
|
|
162
|
+
right: 'object-right',
|
|
163
|
+
},
|
|
164
|
+
flexWrap: {
|
|
165
|
+
wrap: 'flex-wrap',
|
|
166
|
+
nowrap: 'flex-nowrap',
|
|
167
|
+
'wrap-reverse': 'flex-wrap-reverse',
|
|
168
|
+
},
|
|
169
|
+
pointerEvents: {
|
|
170
|
+
none: 'pointer-events-none',
|
|
171
|
+
auto: 'pointer-events-auto',
|
|
172
|
+
},
|
|
173
|
+
userSelect: {
|
|
174
|
+
none: 'select-none',
|
|
175
|
+
auto: 'select-auto',
|
|
176
|
+
text: 'select-text',
|
|
177
|
+
all: 'select-all',
|
|
178
|
+
},
|
|
179
|
+
visibility: {
|
|
180
|
+
hidden: 'invisible',
|
|
181
|
+
visible: 'visible',
|
|
182
|
+
},
|
|
183
|
+
whiteSpace: {
|
|
184
|
+
normal: 'whitespace-normal',
|
|
185
|
+
nowrap: 'whitespace-nowrap',
|
|
186
|
+
pre: 'whitespace-pre',
|
|
187
|
+
'pre-wrap': 'whitespace-pre-wrap',
|
|
188
|
+
'pre-line': 'whitespace-pre-line',
|
|
189
|
+
},
|
|
190
|
+
wordBreak: {
|
|
191
|
+
'break-all': 'break-all',
|
|
192
|
+
'break-word': 'break-words',
|
|
193
|
+
normal: 'break-normal',
|
|
194
|
+
},
|
|
195
|
+
listStyleType: {
|
|
196
|
+
none: 'list-none',
|
|
197
|
+
disc: 'list-disc',
|
|
198
|
+
decimal: 'list-decimal',
|
|
199
|
+
},
|
|
200
|
+
listStylePosition: {
|
|
201
|
+
inside: 'list-inside',
|
|
202
|
+
outside: 'list-outside',
|
|
203
|
+
},
|
|
204
|
+
boxSizing: {
|
|
205
|
+
'border-box': 'box-border',
|
|
206
|
+
'content-box': 'box-content',
|
|
207
|
+
},
|
|
208
|
+
gridAutoFlow: {
|
|
209
|
+
row: 'grid-flow-row',
|
|
210
|
+
column: 'grid-flow-col',
|
|
211
|
+
dense: 'grid-flow-dense',
|
|
212
|
+
'row dense': 'grid-flow-row-dense',
|
|
213
|
+
'column dense': 'grid-flow-col-dense',
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Single-value exact matches (property → class when value matches)
|
|
218
|
+
const singleValueMatches: Record<string, string> = {
|
|
219
|
+
// width/height 100%
|
|
220
|
+
'width:100%': 'w-full',
|
|
221
|
+
'height:100%': 'h-full',
|
|
222
|
+
'width:100vw': 'w-screen',
|
|
223
|
+
'height:100vh': 'h-screen',
|
|
224
|
+
'width:auto': 'w-auto',
|
|
225
|
+
'height:auto': 'h-auto',
|
|
226
|
+
'width:fit-content': 'w-fit',
|
|
227
|
+
'height:fit-content': 'h-fit',
|
|
228
|
+
'width:min-content': 'w-min',
|
|
229
|
+
'height:min-content': 'h-min',
|
|
230
|
+
'width:max-content': 'w-max',
|
|
231
|
+
'height:max-content': 'h-max',
|
|
232
|
+
'maxWidth:100%': 'max-w-full',
|
|
233
|
+
'maxWidth:none': 'max-w-none',
|
|
234
|
+
'maxHeight:100%': 'max-h-full',
|
|
235
|
+
'maxHeight:none': 'max-h-none',
|
|
236
|
+
'minWidth:0': 'min-w-0',
|
|
237
|
+
'minHeight:0': 'min-h-0',
|
|
238
|
+
'margin:auto': 'm-auto',
|
|
239
|
+
'margin:0 auto': 'mx-auto',
|
|
240
|
+
'marginLeft:auto': 'ml-auto',
|
|
241
|
+
'marginRight:auto': 'mr-auto',
|
|
242
|
+
'marginInline:auto': 'mx-auto',
|
|
243
|
+
'borderRadius:50%': 'rounded-full',
|
|
244
|
+
'borderRadius:9999px': 'rounded-full',
|
|
245
|
+
'borderRadius:0': 'rounded-none',
|
|
246
|
+
'flex:1': 'flex-1',
|
|
247
|
+
'flex:none': 'flex-none',
|
|
248
|
+
'flex:auto': 'flex-auto',
|
|
249
|
+
'flexGrow:0': 'grow-0',
|
|
250
|
+
'flexGrow:1': 'grow',
|
|
251
|
+
'flexShrink:0': 'shrink-0',
|
|
252
|
+
'flexShrink:1': 'shrink',
|
|
253
|
+
'opacity:0': 'opacity-0',
|
|
254
|
+
'opacity:1': 'opacity-100',
|
|
255
|
+
'zIndex:0': 'z-0',
|
|
256
|
+
'zIndex:10': 'z-10',
|
|
257
|
+
'zIndex:20': 'z-20',
|
|
258
|
+
'zIndex:30': 'z-30',
|
|
259
|
+
'zIndex:40': 'z-40',
|
|
260
|
+
'zIndex:50': 'z-50',
|
|
261
|
+
'inset:0': 'inset-0',
|
|
262
|
+
'top:0': 'top-0',
|
|
263
|
+
'right:0': 'right-0',
|
|
264
|
+
'bottom:0': 'bottom-0',
|
|
265
|
+
'left:0': 'left-0',
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// Arbitrary value prefix map: CSS property → Tailwind prefix
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
|
|
272
|
+
const arbitraryPrefixMap: Record<string, string> = {
|
|
273
|
+
// Spacing
|
|
274
|
+
padding: 'p',
|
|
275
|
+
paddingTop: 'pt',
|
|
276
|
+
paddingRight: 'pr',
|
|
277
|
+
paddingBottom: 'pb',
|
|
278
|
+
paddingLeft: 'pl',
|
|
279
|
+
paddingInline: 'px',
|
|
280
|
+
paddingBlock: 'py',
|
|
281
|
+
margin: 'm',
|
|
282
|
+
marginTop: 'mt',
|
|
283
|
+
marginRight: 'mr',
|
|
284
|
+
marginBottom: 'mb',
|
|
285
|
+
marginLeft: 'ml',
|
|
286
|
+
marginInline: 'mx',
|
|
287
|
+
marginBlock: 'my',
|
|
288
|
+
gap: 'gap',
|
|
289
|
+
rowGap: 'gap-y',
|
|
290
|
+
columnGap: 'gap-x',
|
|
291
|
+
|
|
292
|
+
// Sizing
|
|
293
|
+
width: 'w',
|
|
294
|
+
height: 'h',
|
|
295
|
+
maxWidth: 'max-w',
|
|
296
|
+
maxHeight: 'max-h',
|
|
297
|
+
minWidth: 'min-w',
|
|
298
|
+
minHeight: 'min-h',
|
|
299
|
+
|
|
300
|
+
// Typography
|
|
301
|
+
fontSize: 'text',
|
|
302
|
+
fontWeight: 'font',
|
|
303
|
+
fontFamily: 'font',
|
|
304
|
+
lineHeight: 'leading',
|
|
305
|
+
letterSpacing: 'tracking',
|
|
306
|
+
|
|
307
|
+
// Borders
|
|
308
|
+
borderRadius: 'rounded',
|
|
309
|
+
borderTopLeftRadius: 'rounded-tl',
|
|
310
|
+
borderTopRightRadius: 'rounded-tr',
|
|
311
|
+
borderBottomLeftRadius: 'rounded-bl',
|
|
312
|
+
borderBottomRightRadius: 'rounded-br',
|
|
313
|
+
border: 'border',
|
|
314
|
+
borderTop: 'border-t',
|
|
315
|
+
borderRight: 'border-r',
|
|
316
|
+
borderBottom: 'border-b',
|
|
317
|
+
borderLeft: 'border-l',
|
|
318
|
+
borderColor: 'border',
|
|
319
|
+
|
|
320
|
+
// Colors
|
|
321
|
+
color: 'text',
|
|
322
|
+
backgroundColor: 'bg',
|
|
323
|
+
background: 'bg',
|
|
324
|
+
backgroundImage: 'bg',
|
|
325
|
+
|
|
326
|
+
// Effects
|
|
327
|
+
opacity: 'opacity',
|
|
328
|
+
boxShadow: 'shadow',
|
|
329
|
+
textShadow: '[text-shadow]',
|
|
330
|
+
filter: '[filter]',
|
|
331
|
+
backdropFilter: 'backdrop',
|
|
332
|
+
transform: '[transform]',
|
|
333
|
+
transformOrigin: 'origin',
|
|
334
|
+
transition: 'transition',
|
|
335
|
+
mixBlendMode: 'mix-blend',
|
|
336
|
+
clipPath: '[clip-path]',
|
|
337
|
+
|
|
338
|
+
// Positioning
|
|
339
|
+
top: 'top',
|
|
340
|
+
right: 'right',
|
|
341
|
+
bottom: 'bottom',
|
|
342
|
+
left: 'left',
|
|
343
|
+
inset: 'inset',
|
|
344
|
+
zIndex: 'z',
|
|
345
|
+
|
|
346
|
+
// Grid
|
|
347
|
+
gridTemplateColumns: 'grid-cols',
|
|
348
|
+
gridTemplateRows: 'grid-rows',
|
|
349
|
+
gridColumn: 'col',
|
|
350
|
+
gridRow: 'row',
|
|
351
|
+
gridAutoRows: 'auto-rows',
|
|
352
|
+
gridAutoColumns: 'auto-cols',
|
|
353
|
+
|
|
354
|
+
// Flexbox extras
|
|
355
|
+
flexGrow: 'grow',
|
|
356
|
+
flexShrink: 'shrink',
|
|
357
|
+
flexBasis: 'basis',
|
|
358
|
+
order: 'order',
|
|
359
|
+
flex: 'flex',
|
|
360
|
+
|
|
361
|
+
// Aspect ratio
|
|
362
|
+
aspectRatio: 'aspect',
|
|
363
|
+
|
|
364
|
+
// Outline
|
|
365
|
+
outline: 'outline',
|
|
366
|
+
outlineWidth: 'outline',
|
|
367
|
+
outlineOffset: 'outline-offset',
|
|
368
|
+
outlineColor: 'outline',
|
|
369
|
+
|
|
370
|
+
// Other
|
|
371
|
+
accentColor: 'accent',
|
|
372
|
+
textIndent: '[text-indent]',
|
|
373
|
+
verticalAlign: 'align',
|
|
374
|
+
overflowWrap: '[overflow-wrap]',
|
|
375
|
+
scrollBehavior: 'scroll',
|
|
376
|
+
resize: 'resize',
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// ---------------------------------------------------------------------------
|
|
380
|
+
// Core conversion functions
|
|
381
|
+
// ---------------------------------------------------------------------------
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Convert a single CSS property:value pair to a Tailwind class.
|
|
385
|
+
* Returns null if the value should be skipped (mappings, templates).
|
|
386
|
+
*/
|
|
387
|
+
export function propertyToTailwind(
|
|
388
|
+
property: string,
|
|
389
|
+
value: string | number
|
|
390
|
+
): string | null {
|
|
391
|
+
const strValue = String(value);
|
|
392
|
+
|
|
393
|
+
// Skip empty values — they produce broken classes like gap-[], mt-[]
|
|
394
|
+
if (strValue === '') return null;
|
|
395
|
+
|
|
396
|
+
// Skip template expressions — these must be handled via inline style
|
|
397
|
+
if (hasTemplateExpression(strValue)) return null;
|
|
398
|
+
|
|
399
|
+
// Check single-value exact matches first
|
|
400
|
+
const singleKey = `${property}:${strValue}`;
|
|
401
|
+
if (singleValueMatches[singleKey]) {
|
|
402
|
+
return singleValueMatches[singleKey];
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Check exact match table
|
|
406
|
+
if (exactMatches[property]?.[strValue]) {
|
|
407
|
+
return exactMatches[property][strValue];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Color variable handling:
|
|
411
|
+
// "var(--text)" → "text-[var(--text)]"
|
|
412
|
+
// Bare color name like "text" → "text-[var(--text)]"
|
|
413
|
+
if (property === 'color' || property === 'backgroundColor' || property === 'borderColor') {
|
|
414
|
+
const prefix = property === 'color' ? 'text' : property === 'backgroundColor' ? 'bg' : 'border';
|
|
415
|
+
if (strValue.includes('var(')) {
|
|
416
|
+
return `${prefix}-[${strValue}]`;
|
|
417
|
+
}
|
|
418
|
+
// Bare name (not a hex, rgb, or number-starting value)
|
|
419
|
+
if (!strValue.match(/^[#\d]/) && !strValue.includes('rgb') && !strValue.includes('hsl')) {
|
|
420
|
+
return `${prefix}-[var(--${strValue})]`;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Arbitrary value fallback
|
|
425
|
+
const twPrefix = arbitraryPrefixMap[property];
|
|
426
|
+
if (!twPrefix) {
|
|
427
|
+
// Unknown property: use arbitrary property syntax
|
|
428
|
+
const cssProp = property.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
429
|
+
const sanitized = strValue.replace(/\s+/g, '_');
|
|
430
|
+
return `[${cssProp}:${sanitized}]`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// For prefixes that start with '[' it's an arbitrary property
|
|
434
|
+
if (twPrefix.startsWith('[')) {
|
|
435
|
+
const sanitized = strValue.replace(/\s+/g, '_');
|
|
436
|
+
return `${twPrefix.slice(0, -1)}:${sanitized}]`;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Standard arbitrary value: prefix-[value]
|
|
440
|
+
const sanitized = strValue.replace(/\s+/g, '_');
|
|
441
|
+
|
|
442
|
+
// Font family names need quotes in arbitrary syntax for Tailwind to parse correctly
|
|
443
|
+
if (property === 'fontFamily' && !sanitized.includes(',') && !sanitized.startsWith("'")) {
|
|
444
|
+
return `${twPrefix}-['${sanitized}']`;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return `${twPrefix}-[${sanitized}]`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Convert a flat style object to an array of Tailwind classes.
|
|
452
|
+
* Skips StyleMapping values (handled separately via class:list).
|
|
453
|
+
* Returns { classes, dynamicStyles } where dynamicStyles are template-expression
|
|
454
|
+
* styles that need inline style attributes.
|
|
455
|
+
*/
|
|
456
|
+
export function stylesToTailwind(
|
|
457
|
+
style: StyleObject | Record<string, string | number>
|
|
458
|
+
): { classes: string[]; dynamicStyles: Record<string, string> } {
|
|
459
|
+
const classes: string[] = [];
|
|
460
|
+
const dynamicStyles: Record<string, string> = {};
|
|
461
|
+
|
|
462
|
+
for (const [prop, value] of Object.entries(style)) {
|
|
463
|
+
if (isStyleMapping(value)) continue;
|
|
464
|
+
|
|
465
|
+
const strValue = String(value);
|
|
466
|
+
|
|
467
|
+
// Template expressions go to dynamic/inline styles
|
|
468
|
+
if (hasTemplateExpression(strValue)) {
|
|
469
|
+
const cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
470
|
+
dynamicStyles[cssProp] = strValue;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const twClass = propertyToTailwind(prop, value);
|
|
475
|
+
if (twClass) {
|
|
476
|
+
classes.push(twClass);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return { classes, dynamicStyles };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Convert a responsive style object to Tailwind classes with responsive prefixes.
|
|
485
|
+
* Uses desktop-first max-width breakpoints.
|
|
486
|
+
*
|
|
487
|
+
* - base styles → no prefix (desktop default)
|
|
488
|
+
* - tablet styles → max-[{breakpoint}px]: prefix
|
|
489
|
+
* - mobile styles → max-[{breakpoint}px]: prefix
|
|
490
|
+
*/
|
|
491
|
+
export function responsiveStylesToTailwind(
|
|
492
|
+
style: StyleObject | ResponsiveStyleObject | null | undefined,
|
|
493
|
+
breakpoints: BreakpointConfig
|
|
494
|
+
): { classes: string[]; dynamicStyles: Record<string, string> } {
|
|
495
|
+
if (!style) return { classes: [], dynamicStyles: {} };
|
|
496
|
+
|
|
497
|
+
const allClasses: string[] = [];
|
|
498
|
+
const allDynamicStyles: Record<string, string> = {};
|
|
499
|
+
|
|
500
|
+
if (isResponsiveStyle(style)) {
|
|
501
|
+
const responsive = style as ResponsiveStyleObject;
|
|
502
|
+
|
|
503
|
+
// Base styles (no prefix)
|
|
504
|
+
if (responsive.base) {
|
|
505
|
+
const { classes, dynamicStyles } = stylesToTailwind(responsive.base);
|
|
506
|
+
allClasses.push(...classes);
|
|
507
|
+
Object.assign(allDynamicStyles, dynamicStyles);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Tablet styles
|
|
511
|
+
if (responsive.tablet) {
|
|
512
|
+
const bpValue = breakpoints.tablet?.breakpoint ?? 1024;
|
|
513
|
+
const prefix = `max-[${bpValue}px]:`;
|
|
514
|
+
const { classes, dynamicStyles } = stylesToTailwind(responsive.tablet);
|
|
515
|
+
allClasses.push(...classes.map(cls => `${prefix}${cls}`));
|
|
516
|
+
// Dynamic styles for tablet — just merge (can't do responsive inline styles easily)
|
|
517
|
+
Object.assign(allDynamicStyles, dynamicStyles);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Mobile styles
|
|
521
|
+
if (responsive.mobile) {
|
|
522
|
+
const bpValue = breakpoints.mobile?.breakpoint ?? 540;
|
|
523
|
+
const prefix = `max-[${bpValue}px]:`;
|
|
524
|
+
const { classes, dynamicStyles } = stylesToTailwind(responsive.mobile);
|
|
525
|
+
allClasses.push(...classes.map(cls => `${prefix}${cls}`));
|
|
526
|
+
Object.assign(allDynamicStyles, dynamicStyles);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Handle any other custom breakpoints
|
|
530
|
+
for (const [bpName, bpStyle] of Object.entries(responsive)) {
|
|
531
|
+
if (bpName === 'base' || bpName === 'tablet' || bpName === 'mobile' || !bpStyle) continue;
|
|
532
|
+
const bpValue = breakpoints[bpName]?.breakpoint;
|
|
533
|
+
if (!bpValue) continue;
|
|
534
|
+
const prefix = `max-[${bpValue}px]:`;
|
|
535
|
+
const { classes, dynamicStyles } = stylesToTailwind(bpStyle);
|
|
536
|
+
allClasses.push(...classes.map(cls => `${prefix}${cls}`));
|
|
537
|
+
Object.assign(allDynamicStyles, dynamicStyles);
|
|
538
|
+
}
|
|
539
|
+
} else {
|
|
540
|
+
// Flat style object — treat as base
|
|
541
|
+
const { classes, dynamicStyles } = stylesToTailwind(style as StyleObject);
|
|
542
|
+
allClasses.push(...classes);
|
|
543
|
+
Object.assign(allDynamicStyles, dynamicStyles);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return { classes: allClasses, dynamicStyles: allDynamicStyles };
|
|
547
|
+
}
|
package/lib/server/index.ts
CHANGED
|
@@ -62,6 +62,9 @@ export { migrateTemplatesDirectory } from './migrateTemplates';
|
|
|
62
62
|
// Static build
|
|
63
63
|
export { buildStaticPages } from '../../build-static';
|
|
64
64
|
|
|
65
|
+
// Astro export
|
|
66
|
+
export { buildAstroProject } from '../../build-astro';
|
|
67
|
+
|
|
65
68
|
// Utilities
|
|
66
69
|
export * from './utils';
|
|
67
70
|
|
|
@@ -2327,7 +2327,7 @@ describe('ssrRenderer', () => {
|
|
|
2327
2327
|
expect(html).toContain('sizes="(max-width: 768px) 100vw, 50vw"');
|
|
2328
2328
|
});
|
|
2329
2329
|
|
|
2330
|
-
test('picture element splits classes
|
|
2330
|
+
test('picture element splits classes: layout on picture, objf/objp on img', async () => {
|
|
2331
2331
|
mockImageMetadataMap.set('/split.jpg', {
|
|
2332
2332
|
srcset: '/split.webp 400w',
|
|
2333
2333
|
avifSrcset: '/split.avif 400w',
|
|
@@ -2336,11 +2336,15 @@ describe('ssrRenderer', () => {
|
|
|
2336
2336
|
const node = {
|
|
2337
2337
|
type: 'node',
|
|
2338
2338
|
tag: 'img',
|
|
2339
|
-
attributes: { src: '/split.jpg', alt: 'Split', className: 'w-full objf-cover' },
|
|
2339
|
+
attributes: { src: '/split.jpg', alt: 'Split', className: 'w-full objf-cover objp-center' },
|
|
2340
2340
|
};
|
|
2341
2341
|
const html = await render(node);
|
|
2342
|
-
|
|
2343
|
-
|
|
2342
|
+
// Layout classes go on <picture>
|
|
2343
|
+
expect(html).toContain('<picture class="w-full">');
|
|
2344
|
+
// Image-specific classes go on <img>
|
|
2345
|
+
expect(html).toContain('class="objf-cover objp-center"');
|
|
2346
|
+
// No display:contents
|
|
2347
|
+
expect(html).not.toContain('display:contents');
|
|
2344
2348
|
});
|
|
2345
2349
|
});
|
|
2346
2350
|
|
|
@@ -1461,7 +1461,7 @@ function renderImageElement(
|
|
|
1461
1461
|
}
|
|
1462
1462
|
|
|
1463
1463
|
// Render as <picture> element if AVIF is available
|
|
1464
|
-
//
|
|
1464
|
+
// Layout classes go on <picture> (block container), image-specific classes on <img>
|
|
1465
1465
|
if (metadata?.avifSrcset) {
|
|
1466
1466
|
// Image-specific class prefixes that should stay on <img>
|
|
1467
1467
|
const imgClassPrefixes = [
|
|
@@ -1471,11 +1471,9 @@ function renderImageElement(
|
|
|
1471
1471
|
// Opacity classes (o-NUMBER) go on img, but overflow (o-h, o-a, o-s, o-v) stays on picture
|
|
1472
1472
|
const opacityPattern = /^o-\d/;
|
|
1473
1473
|
|
|
1474
|
-
// Parse classes from classAttr
|
|
1475
1474
|
const classMatch = classAttr.match(/class="([^"]*)"/);
|
|
1476
1475
|
const allClasses = classMatch ? classMatch[1].split(/\s+/).filter(Boolean) : [];
|
|
1477
1476
|
|
|
1478
|
-
// Split classes between picture and img
|
|
1479
1477
|
const imgClasses: string[] = [];
|
|
1480
1478
|
const pictureClasses: string[] = [];
|
|
1481
1479
|
|
|
@@ -88,7 +88,7 @@ describe('themeDefaults', () => {
|
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
test('should have code syntax colors', () => {
|
|
91
|
-
expect(DEFAULT_PROPS_PANEL_COLORS.codeString).toBe('#
|
|
91
|
+
expect(DEFAULT_PROPS_PANEL_COLORS.codeString).toBe('#ffffff');
|
|
92
92
|
expect(DEFAULT_PROPS_PANEL_COLORS.codeNumber).toBe('#b5cea8');
|
|
93
93
|
expect(DEFAULT_PROPS_PANEL_COLORS.codeKey).toBe('#9cdcfe');
|
|
94
94
|
expect(DEFAULT_PROPS_PANEL_COLORS.codeType).toBe('#4ec9b0');
|
|
@@ -24,6 +24,7 @@ export interface PropsPanelColors {
|
|
|
24
24
|
inputBackground: string;
|
|
25
25
|
inputBorder: string;
|
|
26
26
|
hoverBackground: string;
|
|
27
|
+
variableBackground?: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -87,7 +88,7 @@ export const DEFAULT_PROPS_PANEL_COLORS: PropsPanelColors = {
|
|
|
87
88
|
text: '#cccccc',
|
|
88
89
|
textSecondary: '#cccccc',
|
|
89
90
|
textMuted: '#888888',
|
|
90
|
-
codeString: '#
|
|
91
|
+
codeString: '#ffffff',
|
|
91
92
|
codeNumber: '#b5cea8',
|
|
92
93
|
codeKey: '#9cdcfe',
|
|
93
94
|
codeType: '#4ec9b0',
|
|
@@ -99,6 +100,7 @@ export const DEFAULT_PROPS_PANEL_COLORS: PropsPanelColors = {
|
|
|
99
100
|
inputBackground: '#2a2a2a',
|
|
100
101
|
inputBorder: '#444444',
|
|
101
102
|
hoverBackground: '#2a2d2e',
|
|
103
|
+
variableBackground: '#2a2a2a',
|
|
102
104
|
};
|
|
103
105
|
|
|
104
106
|
/**
|
|
@@ -125,5 +127,6 @@ export const LIGHT_PROPS_PANEL_COLORS: PropsPanelColors = {
|
|
|
125
127
|
inputBackground: '#f5f7fa',
|
|
126
128
|
inputBorder: '#d0d0d0',
|
|
127
129
|
hoverBackground: '#f0f0f0',
|
|
130
|
+
variableBackground: '#ebedf0',
|
|
128
131
|
};
|
|
129
132
|
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|