@vettvangur/design-system 0.0.1

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 (37) hide show
  1. package/LICENSE +11 -0
  2. package/README.md +18 -0
  3. package/dist/generate-astro-AcegHzWG.js +476 -0
  4. package/dist/generate-astro-B225JTMY.js +534 -0
  5. package/dist/generate-astro-Btr2jiyr.js +389 -0
  6. package/dist/generate-astro-CpNCOn_3.js +475 -0
  7. package/dist/generate-astro-CqXZcTfi.js +820 -0
  8. package/dist/generate-astro-D-FcwrAx.js +819 -0
  9. package/dist/generate-astro-DfaWTEF_.js +539 -0
  10. package/dist/generate-astro-DwBkft4p.js +820 -0
  11. package/dist/generate-razor--hJCWG13.js +454 -0
  12. package/dist/generate-razor-B0Pid10L.js +441 -0
  13. package/dist/generate-razor-B0n9FUR8.js +441 -0
  14. package/dist/generate-razor-B3a_-uDf.js +441 -0
  15. package/dist/generate-razor-BLNML3CC.js +441 -0
  16. package/dist/generate-razor-CABSDQok.js +454 -0
  17. package/dist/generate-razor-CGhqwpRL.js +413 -0
  18. package/dist/generate-razor-DcAkNKya.js +9 -0
  19. package/dist/generate-razor-DwXDvSTP.js +332 -0
  20. package/dist/generate-razor-IGs8o9fx.js +441 -0
  21. package/dist/generate-razor-nGTa0EVW.js +335 -0
  22. package/dist/generate-tailwind-16Mayr2z.js +1050 -0
  23. package/dist/generate-tailwind-BR1-fpNI.js +1056 -0
  24. package/dist/generate-tailwind-BVpI-hUx.js +1057 -0
  25. package/dist/generate-tailwind-Bd3ltCQR.js +1084 -0
  26. package/dist/generate-tailwind-BljcUYMa.js +1051 -0
  27. package/dist/generate-tailwind-BqZCEvj5.js +1049 -0
  28. package/dist/generate-tailwind-CGwQarzK.js +1059 -0
  29. package/dist/generate-tailwind-Cpb8YNNz.js +1050 -0
  30. package/dist/generate-tailwind-CtUsHnBd.js +1060 -0
  31. package/dist/generate-tailwind-DL0GEqR6.js +1050 -0
  32. package/dist/generate-tailwind-Dr3KLBMD.js +1045 -0
  33. package/dist/generate-tailwind-KzRHwyMG.js +1049 -0
  34. package/dist/generate-tailwind-VnkcDTZP.js +1049 -0
  35. package/dist/index.esm.js +58 -0
  36. package/dist/inputs-CQVgm9Do.js +439 -0
  37. package/package.json +35 -0
package/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Portions of this package rely on third-party open source libraries, which are licensed under their respective open source licenses (such as MIT). Use of those libraries is governed solely by their respective license terms.
2
+
3
+ All original code in this repository is licensed under the terms below:
4
+
5
+ ---
6
+
7
+ Copyright (c) 2025 Vettvangur
8
+
9
+ This software is provided for personal or internal non-commercial use only. You may not modify, distribute, sublicense, or use this software in commercial products or services without explicit written permission from Vettvangur.
10
+
11
+ All rights reserved.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # vettvangur-styleguide
2
+
3
+ Auto-generate styleguide based on project CSS variables and utilities.
4
+
5
+ * Colors
6
+ * Typography
7
+ * Buttons
8
+ * Inputs
9
+
10
+ ### `Commands`
11
+
12
+ Astro:
13
+
14
+ `vettvangur-styleguide astro`
15
+
16
+ Umbraco:
17
+
18
+ `vettvangur-styleguide razor -n=UmbracoStarter`
@@ -0,0 +1,476 @@
1
+ #!/usr/bin/env node
2
+ import { promises } from 'node:fs';
3
+ import path from 'node:path';
4
+ import 'node:url';
5
+ import process from 'node:process';
6
+ import { p as parseColors, a as parseTypography, b as parseButtons, i as inputsSpec } from './inputs-CQVgm9Do.js';
7
+
8
+ // core/astro/generate-astro.mjs
9
+ const CWD = process.cwd();
10
+
11
+ // Generated components
12
+ const OUT_DIR = path.resolve(CWD, 'src/astrobook/components');
13
+ const OUT_PREVIEW = path.join(OUT_DIR, 'Preview.astro'); // <-- new
14
+ const OUT_COLORS = path.join(OUT_DIR, 'Colors.astro');
15
+ const OUT_BODIES = path.join(OUT_DIR, 'Bodies.astro');
16
+ const OUT_HEADLINES = path.join(OUT_DIR, 'Headlines.astro');
17
+ const OUT_BUTTONS = path.join(OUT_DIR, 'Buttons.astro');
18
+ const OUT_INPUTS = path.join(OUT_DIR, 'Inputs.astro');
19
+ const OUT_RICHTEXT = path.join(OUT_DIR, 'Richtext.astro');
20
+
21
+ // Story folders
22
+ const ASTROBOOK_DIR = path.resolve(CWD, 'src/astrobook');
23
+ const OUT_DIR_TYPOGRAPHY = path.join(ASTROBOOK_DIR, 'Typography');
24
+ const OUT_DIR_UI = path.join(ASTROBOOK_DIR, 'UI');
25
+ const OUT_DIR_COLORS = path.join(ASTROBOOK_DIR, 'Colors');
26
+ async function generateAstro() {
27
+ // ensure dirs
28
+ await promises.mkdir(OUT_DIR, {
29
+ recursive: true
30
+ });
31
+ await promises.mkdir(ASTROBOOK_DIR, {
32
+ recursive: true
33
+ });
34
+ await promises.mkdir(OUT_DIR_TYPOGRAPHY, {
35
+ recursive: true
36
+ });
37
+ await promises.mkdir(OUT_DIR_UI, {
38
+ recursive: true
39
+ });
40
+ await promises.mkdir(OUT_DIR_COLORS, {
41
+ recursive: true
42
+ });
43
+
44
+ // write Preview.astro (to a FILE, not the folder)
45
+ await promises.writeFile(OUT_PREVIEW, generatePreview(), 'utf8');
46
+ console.log(`✓ Wrote ${rel(OUT_PREVIEW)} (Preview)`);
47
+
48
+ // write story files
49
+ await renderStoriesAstro();
50
+ const [colors, typography, buttons] = await Promise.all([parseColors(),
51
+ // -> [{ title, color, class }]
52
+ parseTypography(),
53
+ // -> { bodies: [...], headlines: [...] }
54
+ parseButtons() // -> [{ title, class, text }]
55
+ ]);
56
+
57
+ // Colors -> Colors.astro
58
+ if (colors?.length) {
59
+ await promises.writeFile(OUT_COLORS, renderColorsAstro(colors), 'utf8');
60
+ console.log(`✓ Wrote ${rel(OUT_COLORS)} (${colors.length} swatches)`);
61
+ } else {
62
+ console.warn('• No colors parsed — Colors.astro not written');
63
+ }
64
+
65
+ // Bodies -> Bodies.astro
66
+ if (typography?.bodies?.length) {
67
+ await promises.writeFile(OUT_BODIES, renderBodiesAstro(typography.bodies), 'utf8');
68
+ console.log(`✓ Wrote ${rel(OUT_BODIES)} (${typography.bodies.length} bodies)`);
69
+ } else {
70
+ console.warn('• No bodies parsed — Bodies.astro not written');
71
+ }
72
+
73
+ // Headlines -> Headlines.astro
74
+ if (typography?.headlines?.length) {
75
+ await promises.writeFile(OUT_HEADLINES, renderHeadlinesAstro(typography.headlines), 'utf8');
76
+ console.log(`✓ Wrote ${rel(OUT_HEADLINES)} (${typography.headlines.length} headlines)`);
77
+ } else {
78
+ console.warn('• No headlines parsed — Headlines.astro not written');
79
+ }
80
+
81
+ // Buttons -> Buttons.astro
82
+ if (buttons?.length) {
83
+ await promises.writeFile(OUT_BUTTONS, renderButtonsAstro(buttons), 'utf8');
84
+ console.log(`✓ Wrote ${rel(OUT_BUTTONS)} (${buttons.length} buttons)`);
85
+ } else {
86
+ console.warn('• No buttons parsed — Buttons.astro not written');
87
+ }
88
+
89
+ // Inputs -> Inputs.astro
90
+ const hasAnyInputs = Object.values(inputsSpec).some(arr => Array.isArray(arr) && arr.length);
91
+ if (hasAnyInputs) {
92
+ await promises.writeFile(OUT_INPUTS, renderInputsAstro(inputsSpec), 'utf8');
93
+ console.log(`✓ Wrote ${rel(OUT_INPUTS)} (inputs:${inputsSpec.inputs?.length ?? 0} selects:${inputsSpec.selects?.length ?? 0} ` + `checkboxes:${inputsSpec.checkboxes?.length ?? 0} radios:${inputsSpec.radios?.length ?? 0} textareas:${inputsSpec.textareas?.length ?? 0})`);
94
+ } else {
95
+ console.warn('• No input spec items — Inputs.astro not written');
96
+ }
97
+
98
+ // Richtext -> Richtext.astro (static specimen)
99
+ await promises.writeFile(OUT_RICHTEXT, renderRichtextAstro(), 'utf8');
100
+ console.log(`✓ Wrote ${rel(OUT_RICHTEXT)} (Richtext)`);
101
+ }
102
+
103
+ /* ---------- story writers ---------- */
104
+
105
+ async function renderStoriesAstro() {
106
+ const storyBody = generateStoryBody();
107
+ const storyColors = generateStoryColors();
108
+ const storyHeadline = generateStoryHeadline();
109
+ const storyButton = generateStoryButtons();
110
+ const storyInputs = generateStoryInputs();
111
+ const storyRichtext = generateStoryRichtext(); // fixed: TS story
112
+
113
+ const FILE_BODY = path.join(OUT_DIR_TYPOGRAPHY, 'Body.stories.ts');
114
+ const FILE_HEADLINES = path.join(OUT_DIR_TYPOGRAPHY, 'Headlines.stories.ts');
115
+ const FILE_RICHTEXT = path.join(OUT_DIR_TYPOGRAPHY, 'Richtext.stories.ts');
116
+ const FILE_BUTTONS = path.join(OUT_DIR_UI, 'Buttons.stories.ts');
117
+ const FILE_INPUTS = path.join(OUT_DIR_UI, 'Inputs.stories.ts');
118
+ const FILE_COLORS = path.join(OUT_DIR_COLORS, 'Colors.stories.ts');
119
+ await promises.writeFile(FILE_BODY, storyBody, 'utf8');
120
+ await promises.writeFile(FILE_HEADLINES, storyHeadline, 'utf8');
121
+ await promises.writeFile(FILE_RICHTEXT, storyRichtext, 'utf8');
122
+ await promises.writeFile(FILE_BUTTONS, storyButton, 'utf8');
123
+ await promises.writeFile(FILE_INPUTS, storyInputs, 'utf8');
124
+ await promises.writeFile(FILE_COLORS, storyColors, 'utf8');
125
+ console.log('✓ Wrote story files:');
126
+ console.log(` - ${rel(FILE_BODY)}`);
127
+ console.log(` - ${rel(FILE_HEADLINES)}`);
128
+ console.log(` - ${rel(FILE_RICHTEXT)}`);
129
+ console.log(` - ${rel(FILE_BUTTONS)}`);
130
+ console.log(` - ${rel(FILE_INPUTS)}`);
131
+ console.log(` - ${rel(FILE_COLORS)}`);
132
+ }
133
+
134
+ /* ---------- code gens ---------- */
135
+
136
+ function generatePreview() {
137
+ return `
138
+ ---
139
+ /** Auto-generated - do not edit by hand */
140
+ import '@/styles/main.css'
141
+ import Fonts from '@/components/core/utility/Fonts.astro'
142
+ ---
143
+ <Fonts />
144
+ <div class="astrobook-preview bg-background">
145
+ <slot />
146
+ </div>
147
+ `.trimStart();
148
+ }
149
+ function generateStoryBody() {
150
+ return `
151
+ // Auto-generated — do not edit by hand
152
+ import Preview from '@/astrobook/components/Preview.astro'
153
+ import Bodies from '@/astrobook/components/Bodies.astro'
154
+
155
+ export default {
156
+ title: 'Typography/Body',
157
+ component: Bodies,
158
+ }
159
+
160
+ export const Overview = {
161
+ render: () => Preview({ slots: { default: Bodies({}) } }),
162
+ }
163
+ `.trimStart();
164
+ }
165
+ function generateStoryColors() {
166
+ return `
167
+ // Auto-generated — do not edit by hand
168
+ import Preview from '@/astrobook/components/Preview.astro'
169
+ import Colors from '@/astrobook/components/Colors.astro'
170
+
171
+ export default {
172
+ title: 'Colors',
173
+ component: Colors,
174
+ }
175
+
176
+ export const Overview = {
177
+ render: () => Preview({ slots: { default: Colors({}) } }),
178
+ }
179
+ `.trimStart();
180
+ }
181
+ function generateStoryHeadline() {
182
+ return `
183
+ // Auto-generated — do not edit by hand
184
+ import Preview from '@/astrobook/components/Preview.astro'
185
+ import Headlines from '@/astrobook/components/Headlines.astro'
186
+
187
+ export default {
188
+ title: 'Typography/Headlines',
189
+ component: Headlines,
190
+ }
191
+
192
+ export const Overview = {
193
+ render: () => Preview({ slots: { default: Headlines({}) } }),
194
+ }
195
+ `.trimStart();
196
+ }
197
+ function generateStoryButtons() {
198
+ return `
199
+ // Auto-generated — do not edit by hand
200
+ import Preview from '@/astrobook/components/Preview.astro'
201
+ import Buttons from '@/astrobook/components/Buttons.astro'
202
+
203
+ export default {
204
+ title: 'UI/Buttons',
205
+ component: Buttons,
206
+ }
207
+
208
+ export const Overview = {
209
+ render: () => Preview({ slots: { default: Buttons({}) } }),
210
+ }
211
+ `.trimStart();
212
+ }
213
+ function generateStoryInputs() {
214
+ return `
215
+ // Auto-generated — do not edit by hand
216
+ import Preview from '@/astrobook/components/Preview.astro'
217
+ import Inputs from '@/astrobook/components/Inputs.astro'
218
+
219
+ export default {
220
+ title: 'UI/Inputs',
221
+ component: Inputs,
222
+ }
223
+
224
+ export const Overview = {
225
+ render: () => Preview({ slots: { default: Inputs({}) } }),
226
+ }
227
+ `.trimStart();
228
+ }
229
+ function generateStoryRichtext() {
230
+ return `
231
+ // Auto-generated — do not edit by hand
232
+ import Preview from '@/astrobook/components/Preview.astro'
233
+ import Richtext from '@/astrobook/components/Richtext.astro'
234
+
235
+ export default {
236
+ title: 'Typography/Richtext',
237
+ component: Richtext,
238
+ }
239
+
240
+ export const Overview = {
241
+ render: () => Preview({ slots: { default: Richtext({}) } }),
242
+ }
243
+ `.trimStart();
244
+ }
245
+
246
+ /* ---------- renderers ---------- */
247
+
248
+ function renderColorsAstro(colors) {
249
+ const items = colors.map(({
250
+ title,
251
+ color,
252
+ class: klass
253
+ }) => {
254
+ const style = `background:${color};`;
255
+ return `
256
+ <div class="flex items-center gap-4 p-3 rounded border border-black/10 bg-white/5">
257
+ <div class="size-10 rounded shadow-inner ring-1 ring-black/10" style="${style}"></div>
258
+ <div class="flex flex-col text-sm">
259
+ <span class="font-medium">${e(title)}</span>
260
+ <span class="opacity-70">token: <code>--color-${e(klass.replace(/^x-/, ''))}</code></span>
261
+ <span class="opacity-70">class: <code>${e(klass)}</code></span>
262
+ <span class="opacity-70">value: <code>${e(color)}</code></span>
263
+ </div>
264
+ </div>`.trim();
265
+ });
266
+ return `---
267
+ /* Auto-generated — do not edit by hand */
268
+ ---
269
+ <div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
270
+ ${items.join('\n')}
271
+ </div>
272
+ `;
273
+ }
274
+ function renderBodiesAstro(bodies) {
275
+ const items = bodies.map(b => {
276
+ const meta = metaText(b.size, b.lineheight, b.config);
277
+ return ` <p class="${a(b.class)}">${e(b.title)} ${meta}</p>`;
278
+ });
279
+ return `---
280
+ /* Auto-generated — do not edit by hand */
281
+ ---
282
+ <div class="space-y-4">
283
+ ${items.join('\n')}
284
+ </div>
285
+ `;
286
+ }
287
+ function renderHeadlinesAstro(headlines) {
288
+ const lines = headlines.map(h => {
289
+ const meta = metaText(h.size, h.lineheight, h.config);
290
+ return ` <div class="flex items-baseline gap-2">
291
+ <Headline size="2" text="${a(h.title)}" class="${a(h.class)}" />
292
+ ${meta}
293
+ </div>`;
294
+ });
295
+ return `---
296
+ /* Auto-generated — do not edit by hand */
297
+ import Headline from '@/components/core/Headline.astro'
298
+ ---
299
+ <div class="space-y-4">
300
+ ${lines.join('\n')}
301
+ </div>
302
+ `;
303
+ }
304
+ function renderButtonsAstro(buttons) {
305
+ const lines = buttons.map(b => {
306
+ const klass = b.class.trim(); // variants already @apply button; don't prepend "button"
307
+ return ` <div class="flex items-center gap-3">
308
+ <Button text="${a(b.text)}" modifier="${a(klass)}" />
309
+ <span class="text-sm opacity-70">${e(b.title)} — <code>${e(klass)}</code></span>
310
+ </div>`;
311
+ });
312
+ return `---
313
+ /* Auto-generated — do not edit by hand */
314
+ import Button from '@/components/core/button/Button.astro' /* adjust if path differs */
315
+ ---
316
+ <div class="space-y-3">
317
+ ${lines.join('\n')}
318
+ </div>
319
+ `;
320
+ }
321
+ function renderInputsAstro(spec) {
322
+ const {
323
+ inputs = [],
324
+ selects = [],
325
+ checkboxes = [],
326
+ radios = [],
327
+ textareas = []
328
+ } = spec;
329
+ const renderGroup = (title, componentTag, items) => {
330
+ if (!items?.length) {
331
+ return '';
332
+ }
333
+ const rows = items.map(obj => ` <${componentTag} ${attrs(obj)} />`).join('\n');
334
+ return `
335
+ <section>
336
+ <h3 class="text-sm opacity-70 mb-2">${e(title)}</h3>
337
+ ${rows}
338
+ </section>`;
339
+ };
340
+ return `---
341
+ /* Auto-generated — do not edit by hand */
342
+ import Input from '@/components/core/inputs/Input.astro'
343
+ import Select from '@/components/core/inputs/Select.astro'
344
+ import Selection from '@/components/core/inputs/Selection.astro'
345
+ ---
346
+ <div class="space-y-8">
347
+ ${renderGroup('Inputs', 'Input', inputs)}
348
+ ${renderGroup('Selects', 'Select', selects)}
349
+ ${renderGroup('Checkboxes', 'Selection', checkboxes)}
350
+ ${renderGroup('Radios', 'Selection', radios)}
351
+ ${renderGroup('Textareas', 'Select', textareas)}
352
+ </div>
353
+ `;
354
+ }
355
+ function renderRichtextAstro() {
356
+ return `---
357
+ /* Auto-generated — do not edit by hand */
358
+ import Headline from '@/components/core/Headline.astro'
359
+ ---
360
+ <section class="w-full">
361
+ <Headline
362
+ text="Richtext"
363
+ size="1"
364
+ class="headline-1 mb-[60px]"
365
+ />
366
+
367
+ <div class="richtext">
368
+ <h1>h1</h1>
369
+ <h2>h2</h2>
370
+ <h3>h3</h3>
371
+ <h4>h4</h4>
372
+ <h5>h5</h5>
373
+ <p>
374
+ Ófróðlegt þorviðar <a href="https://www.vettvangur.is/" target="_blank">látist kærðu</a>, allfeginn því að kaupmanni, <strong>hraðasta</strong> ættleri <em>farminum</em> þuríði brústeinunum konungsþræll grænar. Hólmganga hrísflekkur, lifum skalla-grímur tíkr. Meiðinn hatast oddleifsdóttir, dólglegast dyflini, torfafóstri torfunni rak jarðarmenið stirðir. Býsn hvé ölseljunni gerði, jafnvitur lög vaskligr, sám hvortveggja ófrelsi trúfasti hallkelsdóttur sneri mund guðmundarsonum. Sekum meginmerkurinnar, arfsali allmikilli sjaldgæf. Uggasyni sættu konungsþræll gæs, alþýðuskap orðtækið stokkar, orkar mund ormláðs skrumaði hriflusonar virðak. Biðjum meiðinn keppa, taliðr uggasyni, skerðan manngjarnlega hofgyðja vilk allmikilli. Ógott hirðum samdóma, heimamaðr endilangt, erlingur kaldur gjósa seimþollr hallkell landkaupi. Torfunni tungu-oddur þorviðar gufárós, ævifús sekur tröllskessa, umbanda rétttrúaður bráðan svörfuði nýt íþrótt dalalönd halli. Flyðruness haldnar aðsóknar skerðan, auðhnykkjanda mannvandur smálöndum sauðarhöfðinu ferjunni.
375
+ </p>
376
+ <ul>
377
+ <li>List item</li>
378
+ <li>List item</li>
379
+ <li>
380
+ List item
381
+ <ul>
382
+ <li>Nested item</li>
383
+ <li>Nested item</li>
384
+ <li>
385
+ Nested item
386
+ <ul>
387
+ <li>Nested even more item</li>
388
+ <li>Nested even more item</li>
389
+ <li>Nested even more item</li>
390
+ </ul>
391
+ </li>
392
+ </ul>
393
+ </li>
394
+ <li>List item</li>
395
+ </ul>
396
+ <ol>
397
+ <li>List item</li>
398
+ <li>
399
+ List item
400
+ <ol>
401
+ <li>Nested item</li>
402
+ <li>Nested item</li>
403
+ <li>Nested item</li>
404
+ </ol>
405
+ </li>
406
+ <li>List item</li>
407
+ <li>List item</li>
408
+ </ol>
409
+ <p>
410
+ Ófróðlegt þorviðar <a href="https://www.vettvangur.is/" target="_blank">látist kærðu</a>, allfeginn því að kaupmanni, hraðasta ættleri farminum þuríði brústeinunum konungsþræll grænar. Hólmganga hrísflekkur, lifum skalla-grímur tíkr. Meiðinn hatast oddleifsdóttir, dólglegast dyflini, torfafóstri torfunni rak jarðarmenið stirðir. Býsn hvé ölseljunni gerði, jafnvitur lög vaskligr, sám hvortveggja ófrelsi trúfasti hallkelsdóttur sneri mund guðmundarsonum. Sekum meginmerkurinnar, arfsali allmikilli sjaldgæf. Uggasyni sættu konungsþræll gæs, alþýðuskap orðtækið stokkar, orkar mund ormláðs skrumaði hriflusonar virðak. Biðjum meiðinn keppa, taliðr uggasyni, skerðan manngjarnlega hofgyðja vilk allmikilli. Ógott hirðum samdóma, heimamaðr endilangt, erlingur kaldur gjósa seimþollr hallkell landkaupi. Torfunni tungu-oddur þorviðar gufárós, ævifús sekur tröllskessa, umbanda rétttrúaður bráðan svörfuði nýt íþrótt dalalönd halli. Flyðruness haldnar aðsóknar skerðan, auðhnykkjanda mannvandur smálöndum sauðarhöfðinu ferjunni.
411
+ </p>
412
+ </div>
413
+ </section>
414
+ `.trimStart();
415
+ }
416
+
417
+ /* ---------- helpers ---------- */
418
+
419
+ function attrs(obj = {}) {
420
+ const parts = [];
421
+ for (const [key, val] of Object.entries(obj)) {
422
+ if (val === '' || val === null || val === undefined || val === false) {
423
+ continue;
424
+ }
425
+ if (typeof val === 'string') {
426
+ parts.push(`${key}="${a(val)}"`);
427
+ } else if (typeof val === 'number') {
428
+ parts.push(`${key}={${val}}`);
429
+ } else if (typeof val === 'boolean') {
430
+ parts.push(`${key}={true}`);
431
+ } else {
432
+ parts.push(`${key}={${JSON.stringify(val)}}`);
433
+ }
434
+ }
435
+ return parts.join(' ');
436
+ }
437
+ function metaText(size, lineheight, config) {
438
+ const basePair = pair({
439
+ size,
440
+ lineHeight: lineheight
441
+ });
442
+ const parts = [];
443
+ if (config?.desktop && (config.desktop.size || config.desktop.lineHeight)) {
444
+ parts.push(`D: ${pair(config.desktop)}`);
445
+ }
446
+ if (config?.mobile && (config.mobile.size || config.mobile.lineHeight)) {
447
+ parts.push(`M: ${pair(config.mobile)}`);
448
+ }
449
+ const label = parts.length ? parts.join(' • ') : basePair;
450
+ if (!label) {
451
+ return '';
452
+ }
453
+ return `<span class="opacity-60 text-sm">${e(label)}</span>`;
454
+ }
455
+ function pair({
456
+ size,
457
+ lineHeight
458
+ } = {}) {
459
+ const s = size ?? null;
460
+ const l = lineHeight ?? null;
461
+ if (!s && !l) {
462
+ return '';
463
+ }
464
+ return `${s ?? '-'} / ${l ?? '-'}`;
465
+ }
466
+ function e(s = '') {
467
+ return String(s).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
468
+ }
469
+ function a(s = '') {
470
+ return String(s).replaceAll('"', '&quot;');
471
+ }
472
+ function rel(p) {
473
+ return path.relative(process.cwd(), p);
474
+ }
475
+
476
+ export { generateAstro as default };