@nuvio/shared 0.5.5 → 1.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.
package/dist/index.js CHANGED
@@ -1,40 +1,1544 @@
1
+ import {
2
+ PCC_BRANDABLE_CATEGORIES,
3
+ PCC_REJECTED_CATEGORIES,
4
+ PCC_SUPPORTED_CATEGORIES,
5
+ defaultPccManifestPath,
6
+ isPccBrandableCategory,
7
+ isPccRejectedCategory,
8
+ isPccSupportedCategory,
9
+ parsePccManifest,
10
+ pccCategoryLabel,
11
+ pccHostsForCategory
12
+ } from "./chunk-3R2WM7JQ.js";
13
+
1
14
  // src/constants.ts
2
15
  var NUVIO_WS_PATH = "/__nuvio/ws";
16
+ var NUVIO_BRAND_PATH = "/__nuvio/brand";
17
+ var NUVIO_PCC_PATH = "/__nuvio/pcc";
3
18
 
4
- // src/protocol.ts
19
+ // src/brand-kit.ts
5
20
  import { z } from "zod";
6
- var PROTOCOL_VERSION = 7;
7
- var riskLevelSchema = z.enum(["safe", "caution", "unsupported"]);
8
- var textTargetSchema = z.object({
21
+ var BRAND_ACCENT_COLORS = ["blue", "purple", "green", "slate", "rose"];
22
+ var BRAND_COLORS = ["none", "neutral", ...BRAND_ACCENT_COLORS];
23
+ var BRAND_RADIUS = ["sharp", "soft", "rounded", "pill"];
24
+ var BRAND_DENSITY = ["compact", "balanced", "spacious"];
25
+ var BRAND_TYPOGRAPHY = ["clean", "bold", "soft"];
26
+ var BRAND_SURFACES = ["white", "muted"];
27
+ var BRAND_BUTTON_VARIANTS = ["solid", "outline"];
28
+ var BRAND_BUTTON_HOVERS = ["none", "darken"];
29
+ var BRAND_CARD_SHADOWS = ["none", "sm", "md"];
30
+ var BRAND_CARD_HOVERS = ["none", "border"];
31
+ var BRAND_APPLY_ACTIONS = [
32
+ "button",
33
+ "card",
34
+ "heading",
35
+ "text",
36
+ "table",
37
+ "form",
38
+ "badge"
39
+ ];
40
+ var BRAND_PRESET_DIMENSIONS_BY_ACTION = {
41
+ button: ["color", "buttonVariant", "radius", "density", "buttonHover"],
42
+ card: ["surface", "color", "radius", "density", "cardShadow", "cardHover"],
43
+ heading: ["color", "typography"],
44
+ text: ["color"],
45
+ table: ["color", "radius"],
46
+ form: ["color", "radius", "density", "surface"],
47
+ badge: ["color"]
48
+ };
49
+ function brandPresetDimensionsForAction(action) {
50
+ return BRAND_PRESET_DIMENSIONS_BY_ACTION[action];
51
+ }
52
+ var BRAND_ACCENT_SLOT_BY_ACTION = {
53
+ button: "fill",
54
+ card: "border",
55
+ heading: "text",
56
+ text: "text",
57
+ table: "border",
58
+ form: "border",
59
+ badge: "tint"
60
+ };
61
+ function getBrandColorSlotLabel(action) {
62
+ const labels = {
63
+ button: "Fill",
64
+ card: "Border color",
65
+ heading: "Text color",
66
+ text: "Text color",
67
+ table: "Border color",
68
+ form: "Field border",
69
+ badge: "Tint"
70
+ };
71
+ return labels[action];
72
+ }
73
+ function brandColorsForAction(action) {
74
+ const slot = BRAND_ACCENT_SLOT_BY_ACTION[action];
75
+ if (slot === "border") {
76
+ return BRAND_COLORS;
77
+ }
78
+ if (slot === "text") {
79
+ return ["neutral", ...BRAND_ACCENT_COLORS];
80
+ }
81
+ if (slot === "tint") {
82
+ return ["neutral", ...BRAND_ACCENT_COLORS];
83
+ }
84
+ return BRAND_ACCENT_COLORS;
85
+ }
86
+ function isBrandAccentColor(color) {
87
+ return BRAND_ACCENT_COLORS.includes(color);
88
+ }
89
+ var BRAND_RADIUS_FIELD_LABEL = "Corners";
90
+ var BRAND_DENSITY_FIELD_LABEL = "Spacing";
91
+ function getBrandTypographyFieldLabel(action) {
92
+ return action === "heading" ? "Heading style" : "Typography";
93
+ }
94
+ var BRAND_SURFACE_FIELD_LABEL = "Surface";
95
+ var BRAND_BUTTON_VARIANT_FIELD_LABEL = "Style";
96
+ var BRAND_BUTTON_HOVER_FIELD_LABEL = "Hover";
97
+ var BRAND_CARD_SHADOW_FIELD_LABEL = "Shadow";
98
+ var BRAND_CARD_HOVER_FIELD_LABEL = "Hover";
99
+ function formatAccentForSummary(action, config) {
100
+ if (config.color === "none" && (action === "card" || action === "table" || action === "form")) {
101
+ return "No border";
102
+ }
103
+ const label = COLOR_LABELS[config.color];
104
+ switch (action) {
105
+ case "button":
106
+ return config.buttonVariant === "outline" ? `${label} outline` : `${label} fill`;
107
+ case "card":
108
+ case "table":
109
+ return `${label} border`;
110
+ case "form":
111
+ return `${label} field border`;
112
+ case "badge":
113
+ return `${label} tint`;
114
+ case "heading":
115
+ case "text":
116
+ return `${label} text`;
117
+ default:
118
+ return label;
119
+ }
120
+ }
121
+ var DEFAULT_BRAND_CONFIG = {
122
+ color: "blue",
123
+ surface: "white",
124
+ buttonVariant: "solid",
125
+ buttonHover: "darken",
126
+ cardShadow: "none",
127
+ cardHover: "none",
128
+ radius: "soft",
129
+ density: "balanced",
130
+ typography: "clean"
131
+ };
132
+ var brandConfigSchema = z.object({
133
+ color: z.enum(BRAND_COLORS),
134
+ surface: z.enum(BRAND_SURFACES),
135
+ buttonVariant: z.enum(BRAND_BUTTON_VARIANTS),
136
+ buttonHover: z.enum(BRAND_BUTTON_HOVERS),
137
+ cardShadow: z.enum(BRAND_CARD_SHADOWS),
138
+ cardHover: z.enum(BRAND_CARD_HOVERS),
139
+ radius: z.enum(BRAND_RADIUS),
140
+ density: z.enum(BRAND_DENSITY),
141
+ typography: z.enum(BRAND_TYPOGRAPHY)
142
+ });
143
+ var brandTokensSchema = z.object({
144
+ accent: z.enum(BRAND_COLORS),
145
+ surface: z.enum(BRAND_SURFACES),
146
+ radius: z.enum(BRAND_RADIUS),
147
+ density: z.enum(BRAND_DENSITY),
148
+ typography: z.enum(BRAND_TYPOGRAPHY),
149
+ buttonVariant: z.enum(BRAND_BUTTON_VARIANTS),
150
+ buttonHover: z.enum(BRAND_BUTTON_HOVERS),
151
+ cardShadow: z.enum(BRAND_CARD_SHADOWS),
152
+ cardHover: z.enum(BRAND_CARD_HOVERS)
153
+ });
154
+ var brandConfigFileV2Schema = z.object({
155
+ version: z.literal(2),
156
+ tokens: brandTokensSchema
157
+ });
158
+ var COLOR_LABELS = {
159
+ none: "None",
160
+ neutral: "Neutral",
161
+ blue: "Blue",
162
+ purple: "Purple",
163
+ green: "Green",
164
+ slate: "Slate",
165
+ rose: "Rose"
166
+ };
167
+ var RADIUS_LABELS = {
168
+ sharp: "Sharp",
169
+ soft: "Soft",
170
+ rounded: "Rounded",
171
+ pill: "Pill"
172
+ };
173
+ var DENSITY_LABELS = {
174
+ compact: "Compact",
175
+ balanced: "Balanced",
176
+ spacious: "Spacious"
177
+ };
178
+ var SURFACE_LABELS = {
179
+ white: "White",
180
+ muted: "Muted"
181
+ };
182
+ var BUTTON_VARIANT_LABELS = {
183
+ solid: "Solid",
184
+ outline: "Outline"
185
+ };
186
+ var BUTTON_HOVER_LABELS = {
187
+ none: "None",
188
+ darken: "Darken"
189
+ };
190
+ var CARD_SHADOW_LABELS = {
191
+ none: "None",
192
+ sm: "Subtle",
193
+ md: "Medium"
194
+ };
195
+ var CARD_HOVER_LABELS = {
196
+ none: "None",
197
+ border: "Border"
198
+ };
199
+ var TYPOGRAPHY_LABELS = {
200
+ clean: "Clean",
201
+ bold: "Bold",
202
+ soft: "Soft"
203
+ };
204
+ var ACTION_LABELS = {
205
+ button: "Button",
206
+ card: "Card",
207
+ heading: "Heading",
208
+ text: "Text",
209
+ table: "Table",
210
+ form: "Form",
211
+ badge: "Badge"
212
+ };
213
+ function colorBgClass(color) {
214
+ if (color === "neutral") {
215
+ return "bg-gray-600";
216
+ }
217
+ return color === "slate" ? "bg-slate-700" : `bg-${color}-600`;
218
+ }
219
+ function colorTextClass(color) {
220
+ if (color === "neutral") {
221
+ return "text-gray-700";
222
+ }
223
+ return color === "slate" ? "text-slate-700" : `text-${color}-600`;
224
+ }
225
+ function colorBorderClass(color) {
226
+ if (color === "none") {
227
+ return null;
228
+ }
229
+ if (color === "neutral") {
230
+ return "border-gray-200";
231
+ }
232
+ return color === "slate" ? "border-slate-300" : `border-${color}-300`;
233
+ }
234
+ function colorAccentBorderClass(color) {
235
+ if (color === "neutral") {
236
+ return "border-gray-600";
237
+ }
238
+ return color === "slate" ? "border-slate-700" : `border-${color}-600`;
239
+ }
240
+ function accentBorderRecipeParts(color) {
241
+ if (color === "none") {
242
+ return ["border-0"];
243
+ }
244
+ const borderColor = colorBorderClass(color);
245
+ if (!borderColor) {
246
+ return [];
247
+ }
248
+ return ["border", borderColor];
249
+ }
250
+ function surfaceBgClass(surface) {
251
+ return surface === "muted" ? "bg-slate-50" : "bg-white";
252
+ }
253
+ function colorBadgeBgClass(color) {
254
+ if (color === "neutral") {
255
+ return "bg-gray-100";
256
+ }
257
+ return color === "slate" ? "bg-slate-100" : `bg-${color}-100`;
258
+ }
259
+ function colorBadgeTextClass(color) {
260
+ if (color === "neutral") {
261
+ return "text-gray-700";
262
+ }
263
+ return color === "slate" ? "text-slate-700" : `text-${color}-700`;
264
+ }
265
+ function radiusClass(radius) {
266
+ const map = {
267
+ sharp: "rounded-none",
268
+ soft: "rounded-md",
269
+ rounded: "rounded-xl",
270
+ pill: "rounded-full"
271
+ };
272
+ return map[radius];
273
+ }
274
+ function densityButtonPadding(density) {
275
+ const map = {
276
+ compact: "px-3 py-1.5",
277
+ balanced: "px-4 py-2",
278
+ spacious: "px-6 py-3"
279
+ };
280
+ return map[density];
281
+ }
282
+ function densityCardPadding(density) {
283
+ const map = {
284
+ compact: "p-4",
285
+ balanced: "p-6",
286
+ spacious: "p-8"
287
+ };
288
+ return map[density];
289
+ }
290
+ function typographyClasses(typography) {
291
+ const map = {
292
+ clean: "text-base font-medium",
293
+ bold: "text-lg font-semibold",
294
+ soft: "text-sm font-normal"
295
+ };
296
+ return map[typography];
297
+ }
298
+ function buttonHoverClass(color, buttonHover, buttonVariant) {
299
+ if (buttonHover === "none" || color === "none" || color === "neutral") {
300
+ return null;
301
+ }
302
+ if (buttonVariant === "outline") {
303
+ return color === "slate" ? "hover:bg-slate-100" : `hover:bg-${color}-50`;
304
+ }
305
+ return color === "slate" ? "hover:bg-slate-800" : `hover:bg-${color}-700`;
306
+ }
307
+ function cardShadowClass(cardShadow) {
308
+ if (cardShadow === "none") {
309
+ return null;
310
+ }
311
+ return cardShadow === "sm" ? "shadow-sm" : "shadow-md";
312
+ }
313
+ function cardHoverBorderClass(color, cardHover) {
314
+ if (cardHover === "none" || color === "none" || color === "neutral") {
315
+ return null;
316
+ }
317
+ return color === "slate" ? "hover:border-slate-400" : `hover:border-${color}-400`;
318
+ }
319
+ function joinRecipeClasses(parts) {
320
+ return parts.filter(Boolean).join(" ").trim();
321
+ }
322
+ function isFormLabelHost(hint) {
323
+ const tag = hint?.tagName?.toLowerCase() ?? "";
324
+ const hostId = hint?.hostId?.toLowerCase() ?? "";
325
+ return tag === "label" || hostId.endsWith(".label");
326
+ }
327
+ function brandFragmentHostHint(entry) {
328
+ return {
329
+ tagName: entry.tagName,
330
+ hierarchyRole: entry.hierarchyRole,
331
+ hostId: entry.id
332
+ };
333
+ }
334
+ function normalizeBrandConfig(input) {
335
+ const parsed = brandConfigSchema.safeParse(input);
336
+ if (parsed.success) {
337
+ return parsed.data;
338
+ }
339
+ const v2 = brandConfigFileV2Schema.safeParse(input);
340
+ if (v2.success) {
341
+ const { tokens } = v2.data;
342
+ return {
343
+ color: tokens.accent,
344
+ surface: tokens.surface,
345
+ buttonVariant: tokens.buttonVariant,
346
+ buttonHover: tokens.buttonHover ?? DEFAULT_BRAND_CONFIG.buttonHover,
347
+ cardShadow: tokens.cardShadow ?? DEFAULT_BRAND_CONFIG.cardShadow,
348
+ cardHover: tokens.cardHover ?? DEFAULT_BRAND_CONFIG.cardHover,
349
+ radius: tokens.radius,
350
+ density: tokens.density,
351
+ typography: tokens.typography
352
+ };
353
+ }
354
+ if (input && typeof input === "object") {
355
+ const partial = input;
356
+ const tokens = partial.version === 2 && partial.tokens && typeof partial.tokens === "object" ? partial.tokens : null;
357
+ const source = tokens ?? partial;
358
+ return {
359
+ color: brandConfigSchema.shape.color.safeParse(source.accent ?? source.color).success ? source.accent ?? source.color : DEFAULT_BRAND_CONFIG.color,
360
+ surface: brandConfigSchema.shape.surface.safeParse(source.surface).success ? source.surface : DEFAULT_BRAND_CONFIG.surface,
361
+ buttonVariant: brandConfigSchema.shape.buttonVariant.safeParse(source.buttonVariant).success ? source.buttonVariant : DEFAULT_BRAND_CONFIG.buttonVariant,
362
+ buttonHover: brandConfigSchema.shape.buttonHover.safeParse(source.buttonHover).success ? source.buttonHover : DEFAULT_BRAND_CONFIG.buttonHover,
363
+ cardShadow: brandConfigSchema.shape.cardShadow.safeParse(source.cardShadow).success ? source.cardShadow : DEFAULT_BRAND_CONFIG.cardShadow,
364
+ cardHover: brandConfigSchema.shape.cardHover.safeParse(source.cardHover).success ? source.cardHover : DEFAULT_BRAND_CONFIG.cardHover,
365
+ radius: brandConfigSchema.shape.radius.safeParse(source.radius).success ? source.radius : DEFAULT_BRAND_CONFIG.radius,
366
+ density: brandConfigSchema.shape.density.safeParse(source.density).success ? source.density : DEFAULT_BRAND_CONFIG.density,
367
+ typography: brandConfigSchema.shape.typography.safeParse(source.typography).success ? source.typography : DEFAULT_BRAND_CONFIG.typography
368
+ };
369
+ }
370
+ return { ...DEFAULT_BRAND_CONFIG };
371
+ }
372
+ function serializeBrandConfig(config) {
373
+ return {
374
+ version: 2,
375
+ tokens: {
376
+ accent: config.color,
377
+ surface: config.surface,
378
+ radius: config.radius,
379
+ density: config.density,
380
+ typography: config.typography,
381
+ buttonVariant: config.buttonVariant,
382
+ buttonHover: config.buttonHover,
383
+ cardShadow: config.cardShadow,
384
+ cardHover: config.cardHover
385
+ }
386
+ };
387
+ }
388
+ function brandConfigsEqual(a, b) {
389
+ return a.color === b.color && a.surface === b.surface && a.buttonVariant === b.buttonVariant && a.buttonHover === b.buttonHover && a.cardShadow === b.cardShadow && a.cardHover === b.cardHover && a.radius === b.radius && a.density === b.density && a.typography === b.typography;
390
+ }
391
+ function buildBrandClassFragment(action, config, hint) {
392
+ switch (action) {
393
+ case "button":
394
+ if (config.buttonVariant === "outline") {
395
+ return joinRecipeClasses([
396
+ "border",
397
+ colorAccentBorderClass(config.color),
398
+ colorTextClass(config.color),
399
+ "bg-transparent",
400
+ radiusClass(config.radius),
401
+ densityButtonPadding(config.density),
402
+ buttonHoverClass(config.color, config.buttonHover, config.buttonVariant)
403
+ ]);
404
+ }
405
+ return joinRecipeClasses([
406
+ colorBgClass(config.color),
407
+ "text-white",
408
+ radiusClass(config.radius),
409
+ densityButtonPadding(config.density),
410
+ buttonHoverClass(config.color, config.buttonHover, config.buttonVariant)
411
+ ]);
412
+ case "card":
413
+ return joinRecipeClasses([
414
+ surfaceBgClass(config.surface),
415
+ ...accentBorderRecipeParts(config.color),
416
+ radiusClass(config.radius),
417
+ densityCardPadding(config.density),
418
+ cardShadowClass(config.cardShadow),
419
+ cardHoverBorderClass(config.color, config.cardHover)
420
+ ]);
421
+ case "heading":
422
+ return [typographyClasses(config.typography), colorTextClass(config.color)].join(" ").trim();
423
+ case "text":
424
+ return joinRecipeClasses(["text-sm font-normal", colorTextClass(config.color)]);
425
+ case "table":
426
+ return joinRecipeClasses([
427
+ "max-w-full",
428
+ ...accentBorderRecipeParts(config.color),
429
+ radiusClass(config.radius)
430
+ ]);
431
+ case "form":
432
+ if (isFormLabelHost(hint)) {
433
+ return "text-sm font-medium text-gray-700";
434
+ }
435
+ return joinRecipeClasses([
436
+ surfaceBgClass(config.surface),
437
+ ...accentBorderRecipeParts(config.color),
438
+ radiusClass(config.radius),
439
+ densityButtonPadding(config.density)
440
+ ]);
441
+ case "badge":
442
+ return [
443
+ "inline-flex",
444
+ "items-center",
445
+ "px-2",
446
+ "py-0.5",
447
+ "text-xs",
448
+ "font-medium",
449
+ "rounded-full",
450
+ colorBadgeBgClass(config.color),
451
+ colorBadgeTextClass(config.color)
452
+ ].join(" ").trim();
453
+ default: {
454
+ const _exhaustive = action;
455
+ return _exhaustive;
456
+ }
457
+ }
458
+ }
459
+ function buildBrandPatchOps(action, config, hint) {
460
+ return [
461
+ {
462
+ kind: "mergeTailwindClassName",
463
+ classNameFragment: buildBrandClassFragment(action, config, hint)
464
+ }
465
+ ];
466
+ }
467
+ function buildBrandPreviewSummary(action, config) {
468
+ if (action === "text") {
469
+ return `${ACTION_LABELS.text} \xB7 ${formatAccentForSummary(action, config)} \xB7 Body size`;
470
+ }
471
+ if (action === "heading") {
472
+ return [
473
+ ACTION_LABELS.heading,
474
+ formatAccentForSummary(action, config),
475
+ TYPOGRAPHY_LABELS[config.typography]
476
+ ].join(" \xB7 ");
477
+ }
478
+ if (action === "card") {
479
+ const parts = [
480
+ ACTION_LABELS.card,
481
+ SURFACE_LABELS[config.surface],
482
+ formatAccentForSummary(action, config),
483
+ RADIUS_LABELS[config.radius],
484
+ DENSITY_LABELS[config.density]
485
+ ];
486
+ if ((config.cardShadow ?? DEFAULT_BRAND_CONFIG.cardShadow) !== "none") {
487
+ parts.push(
488
+ `${CARD_SHADOW_LABELS[config.cardShadow ?? DEFAULT_BRAND_CONFIG.cardShadow]} shadow`
489
+ );
490
+ }
491
+ if ((config.cardHover ?? DEFAULT_BRAND_CONFIG.cardHover) !== "none") {
492
+ parts.push(`${CARD_HOVER_LABELS[config.cardHover ?? DEFAULT_BRAND_CONFIG.cardHover]} hover`);
493
+ }
494
+ return parts.join(" \xB7 ");
495
+ }
496
+ if (action === "table") {
497
+ return [
498
+ ACTION_LABELS.table,
499
+ formatAccentForSummary(action, config),
500
+ RADIUS_LABELS[config.radius]
501
+ ].join(" \xB7 ");
502
+ }
503
+ if (action === "form") {
504
+ return [
505
+ ACTION_LABELS.form,
506
+ formatAccentForSummary(action, config),
507
+ RADIUS_LABELS[config.radius],
508
+ DENSITY_LABELS[config.density]
509
+ ].join(" \xB7 ");
510
+ }
511
+ if (action === "badge") {
512
+ return [
513
+ ACTION_LABELS.badge,
514
+ formatAccentForSummary(action, config),
515
+ RADIUS_LABELS.pill
516
+ ].join(" \xB7 ");
517
+ }
518
+ return [
519
+ ACTION_LABELS.button,
520
+ formatAccentForSummary(action, config),
521
+ BUTTON_VARIANT_LABELS[config.buttonVariant],
522
+ RADIUS_LABELS[config.radius],
523
+ DENSITY_LABELS[config.density],
524
+ (config.buttonHover ?? DEFAULT_BRAND_CONFIG.buttonHover) !== "none" ? `${BUTTON_HOVER_LABELS[config.buttonHover ?? DEFAULT_BRAND_CONFIG.buttonHover]} hover` : null
525
+ ].filter(Boolean).join(" \xB7 ");
526
+ }
527
+ function getBrandColorLabel(color) {
528
+ return COLOR_LABELS[color];
529
+ }
530
+ function getBrandRadiusLabel(radius) {
531
+ return RADIUS_LABELS[radius];
532
+ }
533
+ function getBrandDensityLabel(density) {
534
+ return DENSITY_LABELS[density];
535
+ }
536
+ function getBrandTypographyLabel(typography) {
537
+ return TYPOGRAPHY_LABELS[typography];
538
+ }
539
+ function getBrandSurfaceLabel(surface) {
540
+ return SURFACE_LABELS[surface];
541
+ }
542
+ function getBrandButtonVariantLabel(variant) {
543
+ return BUTTON_VARIANT_LABELS[variant];
544
+ }
545
+ function getBrandButtonHoverLabel(hover) {
546
+ return BUTTON_HOVER_LABELS[hover];
547
+ }
548
+ function getBrandCardShadowLabel(shadow) {
549
+ return CARD_SHADOW_LABELS[shadow];
550
+ }
551
+ function getBrandCardHoverLabel(hover) {
552
+ return CARD_HOVER_LABELS[hover];
553
+ }
554
+
555
+ // src/brand-page-discovery.ts
556
+ var BRAND_PAGE_DISCOVERY_PLURALS = {
557
+ button: "buttons",
558
+ card: "cards",
559
+ heading: "headings",
560
+ text: "text blocks",
561
+ table: "tables",
562
+ form: "forms",
563
+ badge: "badges"
564
+ };
565
+ var BRAND_PAGE_DISCOVERY_SINGULARS = {
566
+ button: "button",
567
+ card: "card",
568
+ heading: "heading",
569
+ text: "text block",
570
+ table: "table",
571
+ form: "form",
572
+ badge: "badge"
573
+ };
574
+ function formatDiscoveryCount(action, count) {
575
+ const label = count === 1 ? BRAND_PAGE_DISCOVERY_SINGULARS[action] : BRAND_PAGE_DISCOVERY_PLURALS[action];
576
+ return `${count} ${label}`;
577
+ }
578
+ function buildBrandPageDiscoveryLine(counts) {
579
+ const parts = [];
580
+ for (const action of BRAND_APPLY_ACTIONS) {
581
+ const count = counts[action] ?? 0;
582
+ if (count > 0) {
583
+ parts.push(formatDiscoveryCount(action, count));
584
+ }
585
+ }
586
+ if (parts.length === 0) {
587
+ return null;
588
+ }
589
+ return `On this page: ${parts.join(" \xB7 ")}`;
590
+ }
591
+
592
+ // src/brand-bulk.ts
593
+ function buildBrandValidateSummary(action, config, count) {
594
+ const base = buildBrandPreviewSummary(action, config);
595
+ const noun = count === 1 ? "element" : "elements";
596
+ return `${base} \xB7 ${count} ${noun}`;
597
+ }
598
+ function buildBrandBulkPreviewSummary(action, config, count) {
599
+ return buildBrandValidateSummary(action, config, count);
600
+ }
601
+ function buildBrandBulkPatchOps(action, config) {
602
+ return buildBrandPatchOps(action, config);
603
+ }
604
+ var PATCHABLE_CLASSNAME_MODES = /* @__PURE__ */ new Set([
605
+ "literal-only",
606
+ "cn-basic",
607
+ "cn-conditional",
608
+ "classnames-static"
609
+ ]);
610
+ function isDuplicateId(id, duplicateIds) {
611
+ return duplicateIds.has(id);
612
+ }
613
+ function isStylePatchableEntry(entry, duplicateIds) {
614
+ if (isDuplicateId(entry.id, duplicateIds)) {
615
+ return false;
616
+ }
617
+ if (entry.hasLiteralClassName === false) {
618
+ return false;
619
+ }
620
+ const mode = entry.classNameMode ?? "literal-only";
621
+ if (mode === "unsupported") {
622
+ return false;
623
+ }
624
+ if (!PATCHABLE_CLASSNAME_MODES.has(mode)) {
625
+ return false;
626
+ }
627
+ if (entry.riskLevel === "unsupported" && entry.hasLiteralClassName !== true) {
628
+ return false;
629
+ }
630
+ return resolveBrandBulkPatchHostId(entry) !== null;
631
+ }
632
+ function resolveBrandBulkPatchHostId(entry) {
633
+ if (entry.patchHostId) {
634
+ return entry.patchHostId;
635
+ }
636
+ if (entry.hasLiteralClassName === true) {
637
+ return entry.id;
638
+ }
639
+ const styleTarget = entry.styleTargets?.find((t) => t.classNamePatchable);
640
+ return styleTarget?.patchHostId ?? null;
641
+ }
642
+ function entryMatchesBrandBulkAction(entry, action) {
643
+ const id = entry.id.toLowerCase();
644
+ const tag = entry.tagName?.toLowerCase() ?? "";
645
+ const role = entry.hierarchyRole;
646
+ switch (action) {
647
+ case "button":
648
+ return role === "button" || tag === "button" || id.endsWith(".button") || id.includes(".cta") || id.includes(".filter") || id.includes(".seeall");
649
+ case "card":
650
+ return role === "card" || id.endsWith(".card") || tag === "card" || tag.startsWith("card");
651
+ case "heading":
652
+ if (id.endsWith(".value")) {
653
+ return false;
654
+ }
655
+ return /^h[1-6]$/.test(tag) || id.endsWith(".title") || id.endsWith(".heading") || id.endsWith(".header") || id.includes(".title");
656
+ case "text":
657
+ if (role === "button" || role === "card") {
658
+ return false;
659
+ }
660
+ if (/^h[1-6]$/.test(tag)) {
661
+ return id.endsWith(".value");
662
+ }
663
+ return role === "text" || tag === "p" || tag === "span" && (id.endsWith(".label") || id.endsWith(".subtitle") || id.includes(".nametext")) || tag === "label" && !id.startsWith("form.") || id.endsWith(".subtitle") || id.includes(".nametext") || entry.textEditable === true && (tag === "span" || tag === "label" || tag === "div");
664
+ case "table":
665
+ return role === "table" || id.endsWith(".table") || id.includes(".header.") || /\.row\./.test(id);
666
+ case "form":
667
+ return role === "form" || role === "input" || tag === "input" || tag === "textarea" || tag === "select" || tag === "label" || id.endsWith(".input") || id.endsWith(".label");
668
+ case "badge":
669
+ return id.endsWith(".badge") || id.includes(".badge.") || tag === "span" && (id.includes("badge") || id.includes("status") || id.includes("chip"));
670
+ default: {
671
+ const _exhaustive = action;
672
+ return _exhaustive;
673
+ }
674
+ }
675
+ }
676
+ function filterBrandBulkCandidates(entries, action, knownIds, duplicateIds, options) {
677
+ const pccHosts = options?.pccHosts;
678
+ if (pccHosts && pccHosts.length > 0) {
679
+ return filterBrandBulkCandidatesFromPccHosts(
680
+ entries,
681
+ pccHosts,
682
+ knownIds,
683
+ duplicateIds
684
+ );
685
+ }
686
+ const seenHostIds = /* @__PURE__ */ new Set();
687
+ const targets = [];
688
+ for (const entry of entries) {
689
+ if (!knownIds.has(entry.id)) {
690
+ continue;
691
+ }
692
+ if (!isStylePatchableEntry(entry, duplicateIds)) {
693
+ continue;
694
+ }
695
+ if (!entryMatchesBrandBulkAction(entry, action)) {
696
+ continue;
697
+ }
698
+ const hostId = resolveBrandBulkPatchHostId(entry);
699
+ if (!hostId || !knownIds.has(hostId)) {
700
+ continue;
701
+ }
702
+ if (seenHostIds.has(hostId)) {
703
+ continue;
704
+ }
705
+ seenHostIds.add(hostId);
706
+ targets.push({
707
+ hostId,
708
+ entryId: entry.id,
709
+ label: entry.id
710
+ });
711
+ }
712
+ return targets;
713
+ }
714
+ function filterBrandBulkCandidatesFromPccHosts(entries, pccHosts, knownIds, duplicateIds) {
715
+ const byId = new Map(entries.map((entry) => [entry.id, entry]));
716
+ const seenHostIds = /* @__PURE__ */ new Set();
717
+ const targets = [];
718
+ for (const hostId of pccHosts) {
719
+ const entry = byId.get(hostId);
720
+ if (!entry || !knownIds.has(hostId)) {
721
+ continue;
722
+ }
723
+ if (!isStylePatchableEntry(entry, duplicateIds)) {
724
+ continue;
725
+ }
726
+ const patchHostId = resolveBrandBulkPatchHostId(entry);
727
+ if (!patchHostId || !knownIds.has(patchHostId)) {
728
+ continue;
729
+ }
730
+ if (seenHostIds.has(patchHostId)) {
731
+ continue;
732
+ }
733
+ seenHostIds.add(patchHostId);
734
+ targets.push({
735
+ hostId: patchHostId,
736
+ entryId: entry.id,
737
+ label: entry.id
738
+ });
739
+ }
740
+ return targets;
741
+ }
742
+ function buildBrandBulkTargetOps(action, config, targets, entries) {
743
+ const byId = entries ? new Map(entries.map((entry) => [entry.id, entry])) : null;
744
+ return targets.map((target) => {
745
+ const entry = byId?.get(target.entryId);
746
+ const hint = entry ? brandFragmentHostHint(entry) : void 0;
747
+ return {
748
+ hostId: target.hostId,
749
+ ops: buildBrandPatchOps(action, config, hint)
750
+ };
751
+ });
752
+ }
753
+
754
+ // src/brand-inspector.ts
755
+ var DIMENSION_LABELS = {
756
+ color: "Color",
757
+ radius: "Radius",
758
+ density: "Density",
759
+ typography: "Typography",
760
+ surface: "Surface",
761
+ buttonVariant: "Style"
762
+ };
763
+ function stripVariantPrefixes(token) {
764
+ let t = token;
765
+ while (t.includes(":")) {
766
+ const idx = t.indexOf(":");
767
+ t = t.slice(idx + 1);
768
+ }
769
+ return t;
770
+ }
771
+ function tokenizeClassName(className) {
772
+ return className.trim().split(/\s+/).filter(Boolean).map(stripVariantPrefixes);
773
+ }
774
+ function colorBgToken(color) {
775
+ if (color === "neutral") {
776
+ return "bg-gray-600";
777
+ }
778
+ return color === "slate" ? "bg-slate-700" : `bg-${color}-600`;
779
+ }
780
+ function colorTextToken(color) {
781
+ if (color === "neutral") {
782
+ return "text-gray-700";
783
+ }
784
+ return color === "slate" ? "text-slate-700" : `text-${color}-600`;
785
+ }
786
+ function colorBorderToken(color) {
787
+ if (color === "none") {
788
+ return null;
789
+ }
790
+ if (color === "neutral") {
791
+ return "border-gray-200";
792
+ }
793
+ return color === "slate" ? "border-slate-300" : `border-${color}-300`;
794
+ }
795
+ var RADIUS_TOKENS = {
796
+ sharp: "rounded-none",
797
+ soft: "rounded-md",
798
+ rounded: "rounded-xl",
799
+ pill: "rounded-full"
800
+ };
801
+ var BUTTON_DENSITY_TOKENS = {
802
+ compact: { px: "px-3", py: "py-1.5" },
803
+ balanced: { px: "px-4", py: "py-2" },
804
+ spacious: { px: "px-6", py: "py-3" }
805
+ };
806
+ var CARD_DENSITY_TOKENS = {
807
+ compact: "p-4",
808
+ balanced: "p-6",
809
+ spacious: "p-8"
810
+ };
811
+ var TYPOGRAPHY_TOKENS = {
812
+ clean: { size: "text-base", weight: "font-medium" },
813
+ bold: { size: "text-lg", weight: "font-semibold" },
814
+ soft: { size: "text-sm", weight: "font-normal" }
815
+ };
816
+ function colorBadgeBgToken(color) {
817
+ if (color === "neutral") {
818
+ return "bg-gray-100";
819
+ }
820
+ return color === "slate" ? "bg-slate-100" : `bg-${color}-100`;
821
+ }
822
+ function colorBadgeTextToken(color) {
823
+ if (color === "neutral") {
824
+ return "text-gray-700";
825
+ }
826
+ return color === "slate" ? "text-slate-700" : `text-${color}-700`;
827
+ }
828
+ function tokenMatchesBrandColor(token, color, prefix) {
829
+ if (color === "none") {
830
+ return false;
831
+ }
832
+ const exact = prefix === "bg-" ? [colorBgToken(color), colorBadgeBgToken(color)] : prefix === "text-" ? [colorTextToken(color), colorBadgeTextToken(color)] : [colorBorderToken(color)].filter((value) => value != null);
833
+ if (exact.includes(token)) {
834
+ return true;
835
+ }
836
+ if (!isBrandAccentColor(color)) {
837
+ return false;
838
+ }
839
+ return token.startsWith(prefix) && token.includes(`-${color}-`);
840
+ }
841
+ function detectCardBorderSlotColor(tokens) {
842
+ let last = { color: null, token: null };
843
+ for (const token of tokens) {
844
+ if (token === "border-gray-200") {
845
+ last = { color: "neutral", token };
846
+ continue;
847
+ }
848
+ if (token === "border-0" || token === "border-transparent") {
849
+ last = { color: "none", token };
850
+ continue;
851
+ }
852
+ for (const color of BRAND_COLORS) {
853
+ if (tokenMatchesBrandColor(token, color, "border-")) {
854
+ last = { color, token };
855
+ }
856
+ }
857
+ }
858
+ return last;
859
+ }
860
+ function detectBorderSlotColor(tokens) {
861
+ let last = { color: null, token: null };
862
+ let lastAccent = { color: null, token: null };
863
+ for (const token of tokens) {
864
+ if (token === "border-gray-200") {
865
+ last = { color: "neutral", token };
866
+ continue;
867
+ }
868
+ if (token === "border-0" || token === "border-transparent") {
869
+ last = { color: "none", token };
870
+ continue;
871
+ }
872
+ for (const color of BRAND_COLORS) {
873
+ if (!tokenMatchesBrandColor(token, color, "border-")) {
874
+ continue;
875
+ }
876
+ last = { color, token };
877
+ if (isBrandAccentColor(color)) {
878
+ lastAccent = { color, token };
879
+ }
880
+ }
881
+ }
882
+ if (lastAccent.color) {
883
+ return lastAccent;
884
+ }
885
+ return last;
886
+ }
887
+ function detectBrandColorForSlot(tokens, slot) {
888
+ if (slot === "border") {
889
+ return detectBorderSlotColor(tokens);
890
+ }
891
+ let result = { color: null, token: null };
892
+ for (const token of tokens) {
893
+ if (slot === "text" && token === "text-gray-700") {
894
+ result = { color: "neutral", token };
895
+ }
896
+ if (slot === "tint" && (token === "bg-gray-100" || token === "text-gray-700")) {
897
+ result = { color: "neutral", token };
898
+ }
899
+ for (const color of BRAND_COLORS) {
900
+ if (slot === "fill" && tokenMatchesBrandColor(token, color, "bg-")) {
901
+ result = { color, token };
902
+ } else if (slot === "text" && tokenMatchesBrandColor(token, color, "text-")) {
903
+ result = { color, token };
904
+ } else if (slot === "tint") {
905
+ if (tokenMatchesBrandColor(token, color, "bg-") || tokenMatchesBrandColor(token, color, "text-")) {
906
+ result = { color, token };
907
+ }
908
+ }
909
+ }
910
+ }
911
+ return result;
912
+ }
913
+ function detectBrandColorForAction(tokens, action) {
914
+ if (action === "card") {
915
+ return detectCardBorderSlotColor(tokens);
916
+ }
917
+ const slot = BRAND_ACCENT_SLOT_BY_ACTION[action];
918
+ if (!slot) {
919
+ return { color: null, token: null };
920
+ }
921
+ return detectBrandColorForSlot(tokens, slot);
922
+ }
923
+ function detectBrandColor(tokens) {
924
+ let result = { color: null, token: null };
925
+ for (const token of tokens) {
926
+ for (const color of BRAND_COLORS) {
927
+ const candidates = [colorBgToken(color), colorTextToken(color), colorBorderToken(color)].filter(
928
+ (value) => value != null
929
+ );
930
+ if (candidates.includes(token)) {
931
+ result = { color, token };
932
+ }
933
+ }
934
+ if (!token.startsWith("bg-") && !token.startsWith("text-") && !token.startsWith("border-")) {
935
+ continue;
936
+ }
937
+ for (const color of BRAND_COLORS) {
938
+ if (!isBrandAccentColor(color)) {
939
+ continue;
940
+ }
941
+ if (token.includes(`-${color}-`)) {
942
+ result = { color, token };
943
+ }
944
+ }
945
+ }
946
+ return result;
947
+ }
948
+ function detectBrandRadius(tokens) {
949
+ let result = { radius: null, token: null };
950
+ let lastRoundedToken = null;
951
+ for (const token of tokens) {
952
+ if (!token.startsWith("rounded")) {
953
+ continue;
954
+ }
955
+ lastRoundedToken = token;
956
+ for (const radius of BRAND_RADIUS) {
957
+ if (token === RADIUS_TOKENS[radius]) {
958
+ result = { radius, token };
959
+ }
960
+ }
961
+ }
962
+ if (result.radius || !lastRoundedToken) {
963
+ return result;
964
+ }
965
+ return { radius: null, token: lastRoundedToken };
966
+ }
967
+ function detectBrandDensityForAction(tokens, action) {
968
+ if (action === "card") {
969
+ let result = { density: null, token: null };
970
+ for (const density of BRAND_DENSITY) {
971
+ const cardToken = CARD_DENSITY_TOKENS[density];
972
+ if (tokens.includes(cardToken)) {
973
+ result = { density, token: cardToken };
974
+ }
975
+ }
976
+ return result;
977
+ }
978
+ if (action === "button" || action === "form") {
979
+ let result = { density: null, token: null };
980
+ for (const density of BRAND_DENSITY) {
981
+ const button = BUTTON_DENSITY_TOKENS[density];
982
+ if (tokens.includes(button.px) && tokens.includes(button.py)) {
983
+ result = { density, token: `${button.px} ${button.py}` };
984
+ }
985
+ }
986
+ return result;
987
+ }
988
+ return { density: null, token: null };
989
+ }
990
+ function detectBrandDensity(tokens) {
991
+ let result = { density: null, token: null };
992
+ for (const density of BRAND_DENSITY) {
993
+ const button = BUTTON_DENSITY_TOKENS[density];
994
+ if (tokens.includes(button.px) && tokens.includes(button.py)) {
995
+ result = { density, token: `${button.px} ${button.py}` };
996
+ }
997
+ }
998
+ for (const density of BRAND_DENSITY) {
999
+ const cardToken = CARD_DENSITY_TOKENS[density];
1000
+ if (tokens.includes(cardToken)) {
1001
+ result = { density, token: cardToken };
1002
+ }
1003
+ }
1004
+ return result;
1005
+ }
1006
+ var BRAND_SIZE_TOKENS = /* @__PURE__ */ new Set(["text-sm", "text-base", "text-lg"]);
1007
+ function detectBrandTypography(tokens) {
1008
+ const size = tokens.filter((token2) => BRAND_SIZE_TOKENS.has(token2)).at(-1) ?? null;
1009
+ const weight = tokens.filter((token2) => token2.startsWith("font-")).at(-1) ?? null;
1010
+ if (!size && !weight) {
1011
+ return { typography: null, token: null };
1012
+ }
1013
+ for (const typography of BRAND_TYPOGRAPHY) {
1014
+ const expected = TYPOGRAPHY_TOKENS[typography];
1015
+ const sizeMatch = !size || size === expected.size;
1016
+ const weightMatch = !weight || weight === expected.weight;
1017
+ if (size && weight) {
1018
+ if (size === expected.size && weight === expected.weight) {
1019
+ return { typography, token: `${expected.size} ${expected.weight}` };
1020
+ }
1021
+ continue;
1022
+ }
1023
+ if (sizeMatch && weightMatch && (size || weight)) {
1024
+ return {
1025
+ typography,
1026
+ token: [size ?? expected.size, weight ?? expected.weight].join(" ")
1027
+ };
1028
+ }
1029
+ }
1030
+ const token = [size, weight].filter(Boolean).join(" ");
1031
+ return { typography: null, token: token || null };
1032
+ }
1033
+ function radiusLabelFromToken(token) {
1034
+ if (!token) {
1035
+ return "Not set";
1036
+ }
1037
+ for (const radius of BRAND_RADIUS) {
1038
+ if (token === RADIUS_TOKENS[radius]) {
1039
+ return getBrandRadiusLabel(radius);
1040
+ }
1041
+ }
1042
+ const suffix = token.replace("rounded-", "");
1043
+ return suffix ? `Custom (${suffix})` : "Custom radius";
1044
+ }
1045
+ function densityLabelFromToken(token) {
1046
+ if (!token) {
1047
+ return "Not set";
1048
+ }
1049
+ for (const density of BRAND_DENSITY) {
1050
+ const button = BUTTON_DENSITY_TOKENS[density];
1051
+ if (token === `${button.px} ${button.py}` || token === CARD_DENSITY_TOKENS[density]) {
1052
+ return getBrandDensityLabel(density);
1053
+ }
1054
+ }
1055
+ return "Custom spacing";
1056
+ }
1057
+ function typographyLabelFromToken(token) {
1058
+ if (!token) {
1059
+ return "Not set";
1060
+ }
1061
+ for (const typography of BRAND_TYPOGRAPHY) {
1062
+ const expected = TYPOGRAPHY_TOKENS[typography];
1063
+ if (token === `${expected.size} ${expected.weight}`) {
1064
+ return getBrandTypographyLabel(typography);
1065
+ }
1066
+ }
1067
+ return "Custom typography";
1068
+ }
1069
+ function colorLabelFromDetection(color, token) {
1070
+ if (color) {
1071
+ return getBrandColorLabel(color);
1072
+ }
1073
+ if (!token) {
1074
+ return "Not set";
1075
+ }
1076
+ return "Custom color";
1077
+ }
1078
+ function buildRow(dimension, status, currentLabel, brandLabel, currentToken, expectedToken) {
1079
+ return {
1080
+ dimension,
1081
+ dimensionLabel: DIMENSION_LABELS[dimension],
1082
+ status,
1083
+ currentLabel,
1084
+ brandLabel,
1085
+ currentToken,
1086
+ expectedToken
1087
+ };
1088
+ }
1089
+ function buildHeadline(matchCount, checkedCount) {
1090
+ if (checkedCount === 0) {
1091
+ return "No brand styles detected on this element.";
1092
+ }
1093
+ if (matchCount === checkedCount) {
1094
+ return "Matches your saved brand.";
1095
+ }
1096
+ if (matchCount === 0) {
1097
+ return "This element does not match your saved brand.";
1098
+ }
1099
+ return `${matchCount} of ${checkedCount} brand traits match your saved brand.`;
1100
+ }
1101
+ var INSPECT_DIMENSIONS_BY_ACTION = {
1102
+ button: ["color", "buttonVariant", "radius", "density"],
1103
+ card: ["surface", "color", "radius", "density"],
1104
+ heading: ["color", "typography"],
1105
+ text: ["color"],
1106
+ table: ["color", "radius"],
1107
+ form: ["color", "radius", "density", "surface"],
1108
+ badge: ["color"]
1109
+ };
1110
+ function detectBrandSurface(tokens) {
1111
+ let result = { surface: null, token: null };
1112
+ if (tokens.includes("bg-slate-50")) {
1113
+ result = { surface: "muted", token: "bg-slate-50" };
1114
+ }
1115
+ if (tokens.includes("bg-white")) {
1116
+ result = { surface: "white", token: "bg-white" };
1117
+ }
1118
+ return result;
1119
+ }
1120
+ function detectBrandButtonVariant(tokens) {
1121
+ if (tokens.includes("bg-transparent")) {
1122
+ return { buttonVariant: "outline", token: "bg-transparent" };
1123
+ }
1124
+ for (const color of BRAND_COLORS) {
1125
+ if (tokens.includes(colorBgToken(color))) {
1126
+ return { buttonVariant: "solid", token: colorBgToken(color) };
1127
+ }
1128
+ }
1129
+ return { buttonVariant: null, token: null };
1130
+ }
1131
+ function inspectRecipeFragmentMatch(className, expectedFragment) {
1132
+ const expectedTokens = tokenizeClassName(expectedFragment);
1133
+ const actualTokens = tokenizeClassName(className);
1134
+ const matchCount = expectedTokens.filter((token) => actualTokens.includes(token)).length;
1135
+ const checkedCount = expectedTokens.length;
1136
+ return {
1137
+ rows: [],
1138
+ matchCount,
1139
+ checkedCount,
1140
+ headline: buildHeadline(matchCount, checkedCount)
1141
+ };
1142
+ }
1143
+ function inferBrandPresetsFromTokens(tokens, action) {
1144
+ const buttonVariantDetection = detectBrandButtonVariant([...tokens]);
1145
+ const colorDetection = action === "button" && buttonVariantDetection.buttonVariant === "outline" ? detectBrandColorForSlot([...tokens], "text") : action ? detectBrandColorForAction([...tokens], action) : detectBrandColor([...tokens]);
1146
+ const radiusDetection = detectBrandRadius([...tokens]);
1147
+ const densityDetection = action ? detectBrandDensityForAction([...tokens], action) : detectBrandDensity([...tokens]);
1148
+ const typographyDetection = detectBrandTypography([...tokens]);
1149
+ const surfaceDetection = detectBrandSurface([...tokens]);
1150
+ const dimensions = action ? new Set(INSPECT_DIMENSIONS_BY_ACTION[action]) : null;
1151
+ const patch = {};
1152
+ if (colorDetection.color && (!dimensions || dimensions.has("color"))) {
1153
+ patch.color = colorDetection.color;
1154
+ }
1155
+ if (surfaceDetection.surface && (!dimensions || dimensions.has("surface"))) {
1156
+ patch.surface = surfaceDetection.surface;
1157
+ }
1158
+ if (buttonVariantDetection.buttonVariant && (!dimensions || dimensions.has("buttonVariant"))) {
1159
+ patch.buttonVariant = buttonVariantDetection.buttonVariant;
1160
+ }
1161
+ if (radiusDetection.radius && (!dimensions || dimensions.has("radius"))) {
1162
+ patch.radius = radiusDetection.radius;
1163
+ }
1164
+ if (densityDetection.density && (!dimensions || dimensions.has("density"))) {
1165
+ patch.density = densityDetection.density;
1166
+ }
1167
+ if (typographyDetection.typography && (!dimensions || dimensions.has("typography"))) {
1168
+ patch.typography = typographyDetection.typography;
1169
+ }
1170
+ return patch;
1171
+ }
1172
+ function inferBrandPresetsFromClassName(className, action) {
1173
+ return inferBrandPresetsFromTokens(tokenizeClassName(className), action);
1174
+ }
1175
+ function inspectBrandMatchForAction(action, className, brand, hint) {
1176
+ if (action === "text") {
1177
+ return inspectRecipeFragmentMatch(className, buildBrandClassFragment("text", brand));
1178
+ }
1179
+ if (action === "form" || action === "badge") {
1180
+ return inspectRecipeFragmentMatch(className, buildBrandClassFragment(action, brand, hint));
1181
+ }
1182
+ const full = inspectBrandMatch(className, brand);
1183
+ const dimensions = new Set(INSPECT_DIMENSIONS_BY_ACTION[action]);
1184
+ const rows = full.rows.filter((row) => dimensions.has(row.dimension));
1185
+ const checkedRows = rows.filter((row) => row.status !== "not_set");
1186
+ const matchCount = checkedRows.filter((row) => row.status === "match").length;
1187
+ return {
1188
+ rows,
1189
+ matchCount,
1190
+ checkedCount: checkedRows.length,
1191
+ headline: buildHeadline(matchCount, checkedRows.length)
1192
+ };
1193
+ }
1194
+ function inspectBrandMatch(className, brand) {
1195
+ const tokens = tokenizeClassName(className);
1196
+ const colorDetection = detectBrandColor(tokens);
1197
+ const radiusDetection = detectBrandRadius(tokens);
1198
+ const densityDetection = detectBrandDensity(tokens);
1199
+ const typographyDetection = detectBrandTypography(tokens);
1200
+ const rows = [
1201
+ buildRow(
1202
+ "color",
1203
+ colorDetection.token == null ? "not_set" : colorDetection.color === brand.color ? "match" : "mismatch",
1204
+ colorLabelFromDetection(colorDetection.color, colorDetection.token),
1205
+ getBrandColorLabel(brand.color),
1206
+ colorDetection.token ?? void 0,
1207
+ colorTextToken(brand.color)
1208
+ ),
1209
+ buildRow(
1210
+ "radius",
1211
+ radiusDetection.token == null ? "not_set" : radiusDetection.radius === brand.radius ? "match" : "mismatch",
1212
+ radiusDetection.radius ? getBrandRadiusLabel(radiusDetection.radius) : radiusLabelFromToken(radiusDetection.token),
1213
+ getBrandRadiusLabel(brand.radius),
1214
+ radiusDetection.token ?? void 0,
1215
+ RADIUS_TOKENS[brand.radius]
1216
+ ),
1217
+ buildRow(
1218
+ "density",
1219
+ densityDetection.token == null ? "not_set" : densityDetection.density === brand.density ? "match" : "mismatch",
1220
+ densityDetection.density ? getBrandDensityLabel(densityDetection.density) : densityLabelFromToken(densityDetection.token),
1221
+ getBrandDensityLabel(brand.density),
1222
+ densityDetection.token ?? void 0,
1223
+ `${BUTTON_DENSITY_TOKENS[brand.density].px} ${BUTTON_DENSITY_TOKENS[brand.density].py}`
1224
+ ),
1225
+ buildRow(
1226
+ "typography",
1227
+ typographyDetection.token == null ? "not_set" : typographyDetection.typography === brand.typography ? "match" : "mismatch",
1228
+ typographyDetection.typography ? getBrandTypographyLabel(typographyDetection.typography) : typographyLabelFromToken(typographyDetection.token),
1229
+ getBrandTypographyLabel(brand.typography),
1230
+ typographyDetection.token ?? void 0,
1231
+ `${TYPOGRAPHY_TOKENS[brand.typography].size} ${TYPOGRAPHY_TOKENS[brand.typography].weight}`
1232
+ )
1233
+ ];
1234
+ const checkedRows = rows.filter((row) => row.status !== "not_set");
1235
+ const matchCount = checkedRows.filter((row) => row.status === "match").length;
1236
+ return {
1237
+ rows,
1238
+ matchCount,
1239
+ checkedCount: checkedRows.length,
1240
+ headline: buildHeadline(matchCount, checkedRows.length)
1241
+ };
1242
+ }
1243
+
1244
+ // src/brand-selection.ts
1245
+ function isNonBrandableNavEntry(entry) {
1246
+ const id = entry.id.toLowerCase();
1247
+ return id.startsWith("nav.") || id.includes(".nav.");
1248
+ }
1249
+ function resolveBrandCategoryForEntry(entry) {
1250
+ if (isNonBrandableNavEntry(entry)) {
1251
+ return null;
1252
+ }
1253
+ for (const action of BRAND_APPLY_ACTIONS) {
1254
+ if (entryMatchesBrandBulkAction(entry, action)) {
1255
+ return action;
1256
+ }
1257
+ }
1258
+ return null;
1259
+ }
1260
+ function isBrandableEntry(entry) {
1261
+ return resolveBrandCategoryForEntry(entry) !== null;
1262
+ }
1263
+
1264
+ // src/evaluate-brand-scan.ts
1265
+ function classifyHostBrandStatus(entry, brand, action) {
1266
+ if (!entry) {
1267
+ return { status: "missing", inspect: null };
1268
+ }
1269
+ const className = entry.classNameValue ?? "";
1270
+ const hint = brandFragmentHostHint(entry);
1271
+ const inspect = inspectBrandMatchForAction(action, className, brand, hint);
1272
+ if (inspect.checkedCount === 0) {
1273
+ return { status: "no_traits", inspect };
1274
+ }
1275
+ if (inspect.matchCount === inspect.checkedCount) {
1276
+ return { status: "on_brand", inspect };
1277
+ }
1278
+ return { status: "off_brand", inspect };
1279
+ }
1280
+ function evaluateBrandPageScan(manifest, entries, brandInput) {
1281
+ const brand = normalizeBrandConfig(brandInput ?? DEFAULT_BRAND_CONFIG);
1282
+ const byId = new Map(entries.map((entry) => [entry.id, entry]));
1283
+ const hosts = [];
1284
+ const categories = [];
1285
+ let onBrandCount = 0;
1286
+ let offBrandCount = 0;
1287
+ let noTraitsCount = 0;
1288
+ let missingCount = 0;
1289
+ const categoryKeys = Object.keys(manifest.categories);
1290
+ for (const category of categoryKeys) {
1291
+ if (!isPccBrandableCategory(category)) {
1292
+ continue;
1293
+ }
1294
+ const config = manifest.categories[category];
1295
+ if (!config) {
1296
+ continue;
1297
+ }
1298
+ let onBrand = 0;
1299
+ let offBrand = 0;
1300
+ let noTraits = 0;
1301
+ let missing = 0;
1302
+ for (const hostId of config.hosts) {
1303
+ const { status, inspect } = classifyHostBrandStatus(
1304
+ byId.get(hostId),
1305
+ brand,
1306
+ category
1307
+ );
1308
+ hosts.push({ hostId, category, status, inspect });
1309
+ if (status === "on_brand") {
1310
+ onBrand += 1;
1311
+ onBrandCount += 1;
1312
+ } else if (status === "off_brand") {
1313
+ offBrand += 1;
1314
+ offBrandCount += 1;
1315
+ } else if (status === "no_traits") {
1316
+ noTraits += 1;
1317
+ noTraitsCount += 1;
1318
+ } else {
1319
+ missing += 1;
1320
+ missingCount += 1;
1321
+ }
1322
+ }
1323
+ categories.push({
1324
+ category,
1325
+ expected: config.hosts.length,
1326
+ onBrand,
1327
+ offBrand,
1328
+ noTraits,
1329
+ missing,
1330
+ pass: offBrand === 0 && missing === 0
1331
+ });
1332
+ }
1333
+ return {
1334
+ page: manifest.page,
1335
+ route: manifest.route,
1336
+ brand,
1337
+ categories,
1338
+ hosts,
1339
+ onBrandCount,
1340
+ offBrandCount,
1341
+ noTraitsCount,
1342
+ missingCount,
1343
+ pass: offBrandCount === 0 && missingCount === 0
1344
+ };
1345
+ }
1346
+ function pccBrandableHostIds(manifest) {
1347
+ const hosts = [];
1348
+ for (const category of Object.keys(manifest.categories)) {
1349
+ if (!isPccBrandableCategory(category)) {
1350
+ continue;
1351
+ }
1352
+ const config = manifest.categories[category];
1353
+ if (config) {
1354
+ hosts.push(...config.hosts);
1355
+ }
1356
+ }
1357
+ return hosts;
1358
+ }
1359
+
1360
+ // src/evaluate-coverage.ts
1361
+ var PATCHABLE_CLASSNAME_MODES2 = /* @__PURE__ */ new Set([
1362
+ "literal-only",
1363
+ "cn-basic",
1364
+ "cn-conditional",
1365
+ "classnames-static"
1366
+ ]);
1367
+ function duplicateIdSet(errors) {
1368
+ return new Set(errors.map((e) => e.id));
1369
+ }
1370
+ function isHostPatchable(entry, duplicateIds) {
1371
+ if (duplicateIds.has(entry.id)) {
1372
+ return { patchable: false, reason: "duplicate_id" };
1373
+ }
1374
+ if (entry.hasLiteralClassName === false) {
1375
+ return { patchable: false, reason: "no_literal_classname" };
1376
+ }
1377
+ const mode = entry.classNameMode ?? "literal-only";
1378
+ if (mode === "unsupported") {
1379
+ return { patchable: false, reason: `classNameMode:${mode}` };
1380
+ }
1381
+ if (!PATCHABLE_CLASSNAME_MODES2.has(mode)) {
1382
+ return { patchable: false, reason: `classNameMode:${mode}` };
1383
+ }
1384
+ if (entry.riskLevel === "unsupported" && entry.hasLiteralClassName !== true) {
1385
+ return { patchable: false, reason: "risk_unsupported" };
1386
+ }
1387
+ const patchHostId = resolveBrandBulkPatchHostId(entry);
1388
+ if (!patchHostId) {
1389
+ return { patchable: false, reason: "no_patch_host" };
1390
+ }
1391
+ return { patchable: true };
1392
+ }
1393
+ function evaluatePageCoverage(manifest, entries, duplicateErrors = []) {
1394
+ const byId = new Map(entries.map((e) => [e.id, e]));
1395
+ const duplicateIds = duplicateIdSet(duplicateErrors);
1396
+ const issues = [];
1397
+ const categories = [];
1398
+ let gateIndexed = 0;
1399
+ let gatePatchable = 0;
1400
+ let gateCategorized = 0;
1401
+ let gateEditable = 0;
1402
+ let gateBrandable = 0;
1403
+ let gateExpected = 0;
1404
+ let brandableCount = 0;
1405
+ let editableOnlyCount = 0;
1406
+ const categoryKeys = Object.keys(manifest.categories);
1407
+ for (const category of categoryKeys) {
1408
+ const config = manifest.categories[category];
1409
+ if (!config) {
1410
+ continue;
1411
+ }
1412
+ let indexed = 0;
1413
+ let patchable = 0;
1414
+ let categorized = 0;
1415
+ let editable = 0;
1416
+ let brandable = 0;
1417
+ for (const hostId of config.hosts) {
1418
+ gateExpected += 1;
1419
+ const entry = byId.get(hostId);
1420
+ if (!entry) {
1421
+ issues.push({ kind: "missing", hostId, category });
1422
+ continue;
1423
+ }
1424
+ if (duplicateIds.has(hostId)) {
1425
+ issues.push({ kind: "duplicate_id", hostId, category, reason: "duplicate_id" });
1426
+ continue;
1427
+ }
1428
+ indexed += 1;
1429
+ gateIndexed += 1;
1430
+ categorized += 1;
1431
+ gateCategorized += 1;
1432
+ const patch = isHostPatchable(entry, duplicateIds);
1433
+ if (!patch.patchable) {
1434
+ issues.push({
1435
+ kind: "unpatchable",
1436
+ hostId,
1437
+ category,
1438
+ reason: patch.reason
1439
+ });
1440
+ continue;
1441
+ }
1442
+ patchable += 1;
1443
+ gatePatchable += 1;
1444
+ editable += 1;
1445
+ gateEditable += 1;
1446
+ if (isPccBrandableCategory(category)) {
1447
+ brandable += 1;
1448
+ gateBrandable += 1;
1449
+ brandableCount += 1;
1450
+ } else {
1451
+ editableOnlyCount += 1;
1452
+ }
1453
+ }
1454
+ const expected = config.hosts.length;
1455
+ const pass2 = (!config.required || expected > 0) && issues.filter((i) => i.category === category).length === 0 && indexed === expected && patchable === expected && editable === expected;
1456
+ categories.push({
1457
+ category,
1458
+ required: config.required,
1459
+ expected,
1460
+ indexed,
1461
+ patchable,
1462
+ categorized,
1463
+ editable,
1464
+ brandable,
1465
+ pass: pass2
1466
+ });
1467
+ }
1468
+ const pass = categories.every((c) => c.pass) && issues.length === 0;
1469
+ return {
1470
+ page: manifest.page,
1471
+ route: manifest.route,
1472
+ categories,
1473
+ gates: {
1474
+ expected: gateExpected,
1475
+ indexed: gateIndexed,
1476
+ patchable: gatePatchable,
1477
+ categorized: gateCategorized,
1478
+ editable: gateEditable,
1479
+ brandable: gateBrandable
1480
+ },
1481
+ issues,
1482
+ brandableCount,
1483
+ editableOnlyCount,
1484
+ pass
1485
+ };
1486
+ }
1487
+
1488
+ // src/pcc-brand-bulk.ts
1489
+ function normalizeAppRoute(route) {
1490
+ const pathOnly = route.split("?")[0]?.split("#")[0] ?? "/";
1491
+ const trimmed = pathOnly.trim() || "/";
1492
+ if (trimmed.length > 1 && trimmed.endsWith("/")) {
1493
+ return trimmed.slice(0, -1);
1494
+ }
1495
+ return trimmed;
1496
+ }
1497
+ function pccHostsForBrandAction(manifest, action) {
1498
+ if (!manifest) {
1499
+ return null;
1500
+ }
1501
+ const hosts = pccHostsForCategory(manifest, action);
1502
+ return hosts.length > 0 ? hosts : null;
1503
+ }
1504
+ function pccManifestMatchesRoute(manifest, route) {
1505
+ return normalizeAppRoute(manifest.route) === normalizeAppRoute(route);
1506
+ }
1507
+
1508
+ // src/protocol.ts
1509
+ import { z as z2 } from "zod";
1510
+ var PROTOCOL_VERSION = 8;
1511
+ var riskLevelSchema = z2.enum(["safe", "caution", "unsupported"]);
1512
+ var textTargetSchema = z2.object({
9
1513
  /** Stable within host: `data-nuvio-id` or `loc:line:column`. */
10
- key: z.string(),
11
- label: z.string(),
12
- file: z.string(),
13
- line: z.number().int(),
14
- column: z.number().int(),
15
- tagName: z.string(),
16
- textEditable: z.boolean(),
17
- textPreview: z.string().optional(),
1514
+ key: z2.string(),
1515
+ label: z2.string(),
1516
+ file: z2.string(),
1517
+ line: z2.number().int(),
1518
+ column: z2.number().int(),
1519
+ tagName: z2.string(),
1520
+ textEditable: z2.boolean(),
1521
+ textPreview: z2.string().optional(),
18
1522
  /** Present when the text node has its own `data-nuvio-id`. */
19
- nuvioId: z.string().optional(),
1523
+ nuvioId: z2.string().optional(),
20
1524
  /** Host id used for `mergeTailwindClassName` when patching styles for this target. */
21
- patchHostId: z.string(),
22
- insideMap: z.boolean().optional()
1525
+ patchHostId: z2.string(),
1526
+ insideMap: z2.boolean().optional()
23
1527
  });
24
- var styleTargetSchema = z.object({
1528
+ var styleTargetSchema = z2.object({
25
1529
  /** Stable key within host: `data-nuvio-id` or `host` for selected container. */
26
- key: z.string(),
27
- label: z.string(),
28
- file: z.string(),
29
- line: z.number().int(),
30
- column: z.number().int(),
31
- tagName: z.string(),
32
- nuvioId: z.string(),
33
- patchHostId: z.string(),
34
- classNamePatchable: z.boolean(),
1530
+ key: z2.string(),
1531
+ label: z2.string(),
1532
+ file: z2.string(),
1533
+ line: z2.number().int(),
1534
+ column: z2.number().int(),
1535
+ tagName: z2.string(),
1536
+ nuvioId: z2.string(),
1537
+ patchHostId: z2.string(),
1538
+ classNamePatchable: z2.boolean(),
35
1539
  riskLevel: riskLevelSchema.optional()
36
1540
  });
37
- var hierarchyRoleSchema = z.enum([
1541
+ var hierarchyRoleSchema = z2.enum([
38
1542
  "section",
39
1543
  "card",
40
1544
  "table",
@@ -47,117 +1551,131 @@ var hierarchyRoleSchema = z.enum([
47
1551
  "media",
48
1552
  "unknown"
49
1553
  ]);
50
- var rowTargetSchema = z.object({
51
- rowKey: z.string(),
52
- nuvioId: z.string(),
53
- label: z.string(),
54
- file: z.string(),
55
- line: z.number().int()
1554
+ var rowTargetSchema = z2.object({
1555
+ rowKey: z2.string(),
1556
+ nuvioId: z2.string(),
1557
+ label: z2.string(),
1558
+ file: z2.string(),
1559
+ line: z2.number().int()
56
1560
  });
57
- var tableMetaSchema = z.object({
58
- dataBinding: z.string(),
59
- file: z.string(),
60
- line: z.number().int(),
61
- columns: z.array(z.string()).optional()
1561
+ var tableMetaSchema = z2.object({
1562
+ dataBinding: z2.string(),
1563
+ file: z2.string(),
1564
+ line: z2.number().int(),
1565
+ columns: z2.array(z2.string()).optional()
62
1566
  });
63
- var tableDataFieldSchema = z.object({
64
- arrayName: z.string(),
65
- rowKey: z.string(),
66
- field: z.string()
1567
+ var tableDataFieldSchema = z2.object({
1568
+ arrayName: z2.string(),
1569
+ rowKey: z2.string(),
1570
+ field: z2.string()
67
1571
  });
68
- var indexEntrySchema = z.object({
69
- id: z.string(),
70
- file: z.string(),
71
- line: z.number().int(),
72
- column: z.number().int(),
1572
+ var classNameModeSchema = z2.enum([
1573
+ "literal-only",
1574
+ "cn-basic",
1575
+ "cn-conditional",
1576
+ "classnames-static",
1577
+ "unsupported"
1578
+ ]);
1579
+ var libraryIdSchema = z2.enum(["shadcn", "tailadmin", "daisyui"]);
1580
+ var indexEntrySchema = z2.object({
1581
+ id: z2.string(),
1582
+ file: z2.string(),
1583
+ line: z2.number().int(),
1584
+ column: z2.number().int(),
73
1585
  /** Source index v2 metadata */
74
- tagName: z.string().optional(),
75
- componentName: z.string().optional(),
76
- hasLiteralClassName: z.boolean().optional(),
77
- classNameValue: z.string().optional(),
78
- textEditable: z.boolean().optional(),
79
- structuralEditable: z.boolean().optional(),
1586
+ tagName: z2.string().optional(),
1587
+ componentName: z2.string().optional(),
1588
+ hasLiteralClassName: z2.boolean().optional(),
1589
+ classNameValue: z2.string().optional(),
1590
+ textEditable: z2.boolean().optional(),
1591
+ structuralEditable: z2.boolean().optional(),
80
1592
  riskLevel: riskLevelSchema.optional(),
81
- unsupportedReasons: z.array(z.string()).optional(),
82
- insideMap: z.boolean().optional(),
1593
+ unsupportedReasons: z2.array(z2.string()).optional(),
1594
+ insideMap: z2.boolean().optional(),
83
1595
  /** Index v3: default id for className patches on this host. */
84
- patchHostId: z.string().optional(),
1596
+ patchHostId: z2.string().optional(),
85
1597
  /** Index v3: preferred text target key in `textTargets`. */
86
- primaryTextTargetKey: z.string().optional(),
1598
+ primaryTextTargetKey: z2.string().optional(),
87
1599
  /** Index v3: descendant (and host) text edit targets. */
88
- textTargets: z.array(textTargetSchema).optional(),
1600
+ textTargets: z2.array(textTargetSchema).optional(),
89
1601
  /** Index v3: explicit style patch targets for this selected host. */
90
- styleTargets: z.array(styleTargetSchema).optional(),
1602
+ styleTargets: z2.array(styleTargetSchema).optional(),
91
1603
  /** Index v3: coarse host role, used for defaults/hints only. */
92
1604
  hierarchyRole: hierarchyRoleSchema.optional(),
93
1605
  /** Index v3: nearest ancestor host id in JSX ownership hierarchy. */
94
- parentHostId: z.string().optional(),
1606
+ parentHostId: z2.string().optional(),
95
1607
  /** Index v3: descendant host ids under this host (if any). */
96
- childTargetIds: z.array(z.string()).optional(),
1608
+ childTargetIds: z2.array(z2.string()).optional(),
97
1609
  /** Index v4: row hosts when this entry is a table section. */
98
- rowTargets: z.array(rowTargetSchema).optional(),
1610
+ rowTargets: z2.array(rowTargetSchema).optional(),
99
1611
  /** Index v4: static table data binding for Tier C. */
100
1612
  tableMeta: tableMetaSchema.optional(),
101
1613
  /** Index v4: when this host maps to a `tableData` field edit. */
102
- tableDataField: tableDataFieldSchema.optional()
1614
+ tableDataField: tableDataFieldSchema.optional(),
1615
+ /** Index v5: detected className expression mode for patch routing. */
1616
+ classNameMode: classNameModeSchema.optional(),
1617
+ /** Index v6: shadcn / TailAdmin / DaisyUI hint for this host. */
1618
+ libraryHint: libraryIdSchema.optional()
103
1619
  });
104
- var runtimeDiagnosticsSchema = z.object({
105
- viteVersion: z.string().optional(),
106
- reactVersion: z.string().optional(),
107
- tailwindVersion: z.string().optional(),
108
- overlayCssMode: z.literal("self-contained").optional()
1620
+ var runtimeDiagnosticsSchema = z2.object({
1621
+ viteVersion: z2.string().optional(),
1622
+ reactVersion: z2.string().optional(),
1623
+ tailwindVersion: z2.string().optional(),
1624
+ overlayCssMode: z2.literal("self-contained").optional(),
1625
+ /** Project-level library detection (v0.8). */
1626
+ detectedLibraries: z2.array(libraryIdSchema).optional()
109
1627
  });
110
- var duplicateIdOccurrenceSchema = z.object({
111
- file: z.string(),
112
- line: z.number().int(),
113
- column: z.number().int()
1628
+ var duplicateIdOccurrenceSchema = z2.object({
1629
+ file: z2.string(),
1630
+ line: z2.number().int(),
1631
+ column: z2.number().int()
114
1632
  });
115
- var duplicateIdErrorSchema = z.object({
116
- id: z.string(),
117
- occurrences: z.array(duplicateIdOccurrenceSchema)
1633
+ var duplicateIdErrorSchema = z2.object({
1634
+ id: z2.string(),
1635
+ occurrences: z2.array(duplicateIdOccurrenceSchema)
118
1636
  });
119
- var clientPingSchema = z.object({
120
- type: z.literal("ping"),
121
- protocolVersion: z.number().int(),
122
- requestId: z.string().min(1)
1637
+ var clientPingSchema = z2.object({
1638
+ type: z2.literal("ping"),
1639
+ protocolVersion: z2.number().int(),
1640
+ requestId: z2.string().min(1)
123
1641
  });
124
- var clientSelectSchema = z.object({
125
- type: z.literal("select"),
126
- protocolVersion: z.number().int(),
127
- requestId: z.string().min(1),
128
- id: z.string().min(1)
1642
+ var clientSelectSchema = z2.object({
1643
+ type: z2.literal("select"),
1644
+ protocolVersion: z2.number().int(),
1645
+ requestId: z2.string().min(1),
1646
+ id: z2.string().min(1)
129
1647
  });
130
- var patchOpSetTextSchema = z.object({
131
- kind: z.literal("setText"),
132
- text: z.string()
1648
+ var patchOpSetTextSchema = z2.object({
1649
+ kind: z2.literal("setText"),
1650
+ text: z2.string()
133
1651
  });
134
- var patchOpMergeTailwindSchema = z.object({
135
- kind: z.literal("mergeTailwindClassName"),
136
- classNameFragment: z.string()
1652
+ var patchOpMergeTailwindSchema = z2.object({
1653
+ kind: z2.literal("mergeTailwindClassName"),
1654
+ classNameFragment: z2.string()
137
1655
  });
138
- var patchOpRemoveTailwindSchema = z.object({
139
- kind: z.literal("removeTailwindClassName"),
140
- classNameFragment: z.string()
1656
+ var patchOpRemoveTailwindSchema = z2.object({
1657
+ kind: z2.literal("removeTailwindClassName"),
1658
+ classNameFragment: z2.string()
141
1659
  });
142
- var patchOpMoveSiblingSchema = z.object({
143
- kind: z.literal("moveSibling"),
144
- direction: z.enum(["up", "down"])
1660
+ var patchOpMoveSiblingSchema = z2.object({
1661
+ kind: z2.literal("moveSibling"),
1662
+ direction: z2.enum(["up", "down"])
145
1663
  });
146
- var patchOpSetHiddenSchema = z.object({
147
- kind: z.literal("setHidden"),
148
- hidden: z.boolean()
1664
+ var patchOpSetHiddenSchema = z2.object({
1665
+ kind: z2.literal("setHidden"),
1666
+ hidden: z2.boolean()
149
1667
  });
150
- var patchOpDuplicateHostSchema = z.object({
151
- kind: z.literal("duplicateHost")
1668
+ var patchOpDuplicateHostSchema = z2.object({
1669
+ kind: z2.literal("duplicateHost")
152
1670
  });
153
- var patchOpSetTableDataFieldSchema = z.object({
154
- kind: z.literal("setTableDataField"),
155
- arrayName: z.string(),
156
- rowKey: z.string(),
157
- field: z.string(),
158
- value: z.string()
1671
+ var patchOpSetTableDataFieldSchema = z2.object({
1672
+ kind: z2.literal("setTableDataField"),
1673
+ arrayName: z2.string(),
1674
+ rowKey: z2.string(),
1675
+ field: z2.string(),
1676
+ value: z2.string()
159
1677
  });
160
- var patchOpSchema = z.discriminatedUnion("kind", [
1678
+ var patchOpSchema = z2.discriminatedUnion("kind", [
161
1679
  patchOpSetTextSchema,
162
1680
  patchOpMergeTailwindSchema,
163
1681
  patchOpRemoveTailwindSchema,
@@ -166,106 +1684,126 @@ var patchOpSchema = z.discriminatedUnion("kind", [
166
1684
  patchOpDuplicateHostSchema,
167
1685
  patchOpSetTableDataFieldSchema
168
1686
  ]);
169
- var breakpointSchema = z.enum(["base", "sm", "md", "lg", "xl"]);
170
- var clientPatchApplySchema = z.object({
171
- type: z.literal("patchApply"),
172
- protocolVersion: z.number().int(),
173
- requestId: z.string().min(1),
174
- id: z.string().min(1),
175
- ops: z.array(patchOpSchema).min(1),
1687
+ var breakpointSchema = z2.enum(["base", "sm", "md", "lg", "xl"]);
1688
+ var clientPatchApplySchema = z2.object({
1689
+ type: z2.literal("patchApply"),
1690
+ protocolVersion: z2.number().int(),
1691
+ requestId: z2.string().min(1),
1692
+ id: z2.string().min(1),
1693
+ ops: z2.array(patchOpSchema).min(1),
176
1694
  /** Optional responsive context for className merges. */
177
1695
  activeBreakpoint: breakpointSchema.optional(),
178
1696
  /** When true, server validates and returns `patchAck` with `diffSummary` but does not write disk or push undo. */
179
- dryRun: z.boolean().optional()
1697
+ dryRun: z2.boolean().optional()
1698
+ });
1699
+ var clientPatchUndoSchema = z2.object({
1700
+ type: z2.literal("patchUndo"),
1701
+ protocolVersion: z2.number().int(),
1702
+ requestId: z2.string().min(1)
180
1703
  });
181
- var clientPatchUndoSchema = z.object({
182
- type: z.literal("patchUndo"),
183
- protocolVersion: z.number().int(),
184
- requestId: z.string().min(1)
1704
+ var clientTagElementSchema = z2.object({
1705
+ type: z2.literal("tagElement"),
1706
+ protocolVersion: z2.number().int(),
1707
+ requestId: z2.string().min(1),
1708
+ file: z2.string().min(1),
1709
+ line: z2.number().int().positive(),
1710
+ column: z2.number().int().nonnegative(),
1711
+ nuvioId: z2.string().min(1)
185
1712
  });
186
- var clientMessageSchema = z.discriminatedUnion("type", [
1713
+ var clientMessageSchema = z2.discriminatedUnion("type", [
187
1714
  clientPingSchema,
188
1715
  clientSelectSchema,
189
1716
  clientPatchApplySchema,
190
- clientPatchUndoSchema
1717
+ clientPatchUndoSchema,
1718
+ clientTagElementSchema
191
1719
  ]);
192
- var serverPongSchema = z.object({
193
- type: z.literal("pong"),
194
- protocolVersion: z.number().int(),
195
- requestId: z.string(),
1720
+ var serverPongSchema = z2.object({
1721
+ type: z2.literal("pong"),
1722
+ protocolVersion: z2.number().int(),
1723
+ requestId: z2.string(),
196
1724
  diagnostics: runtimeDiagnosticsSchema.optional()
197
1725
  });
198
- var serverErrorSchema = z.object({
199
- type: z.literal("error"),
200
- code: z.string(),
201
- message: z.string(),
202
- requestId: z.string().optional()
1726
+ var serverErrorSchema = z2.object({
1727
+ type: z2.literal("error"),
1728
+ code: z2.string(),
1729
+ message: z2.string(),
1730
+ requestId: z2.string().optional()
203
1731
  });
204
- var serverIndexReadySchema = z.object({
205
- type: z.literal("indexReady"),
206
- protocolVersion: z.number().int(),
207
- indexVersion: z.number().int(),
208
- entries: z.array(indexEntrySchema),
209
- duplicateErrors: z.array(duplicateIdErrorSchema),
1732
+ var serverIndexReadySchema = z2.object({
1733
+ type: z2.literal("indexReady"),
1734
+ protocolVersion: z2.number().int(),
1735
+ indexVersion: z2.number().int(),
1736
+ entries: z2.array(indexEntrySchema),
1737
+ duplicateErrors: z2.array(duplicateIdErrorSchema),
210
1738
  diagnostics: runtimeDiagnosticsSchema.optional()
211
1739
  });
212
- var serverSelectAckSchema = z.object({
213
- type: z.literal("selectAck"),
214
- protocolVersion: z.number().int(),
215
- requestId: z.string(),
216
- id: z.string(),
217
- ok: z.boolean(),
218
- file: z.string().optional(),
219
- line: z.number().int().optional(),
220
- column: z.number().int().optional(),
1740
+ var serverSelectAckSchema = z2.object({
1741
+ type: z2.literal("selectAck"),
1742
+ protocolVersion: z2.number().int(),
1743
+ requestId: z2.string(),
1744
+ id: z2.string(),
1745
+ ok: z2.boolean(),
1746
+ file: z2.string().optional(),
1747
+ line: z2.number().int().optional(),
1748
+ column: z2.number().int().optional(),
221
1749
  /** Index v3 snapshot for the selected host (also on `indexReady` entries). */
222
- patchHostId: z.string().optional(),
223
- primaryTextTargetKey: z.string().optional(),
224
- textTargets: z.array(textTargetSchema).optional(),
225
- styleTargets: z.array(styleTargetSchema).optional(),
1750
+ patchHostId: z2.string().optional(),
1751
+ primaryTextTargetKey: z2.string().optional(),
1752
+ textTargets: z2.array(textTargetSchema).optional(),
1753
+ styleTargets: z2.array(styleTargetSchema).optional(),
226
1754
  hierarchyRole: hierarchyRoleSchema.optional(),
227
- parentHostId: z.string().optional(),
228
- childTargetIds: z.array(z.string()).optional(),
229
- rowTargets: z.array(rowTargetSchema).optional(),
1755
+ parentHostId: z2.string().optional(),
1756
+ childTargetIds: z2.array(z2.string()).optional(),
1757
+ rowTargets: z2.array(rowTargetSchema).optional(),
230
1758
  tableMeta: tableMetaSchema.optional(),
231
1759
  tableDataField: tableDataFieldSchema.optional(),
232
- errorCode: z.string().optional(),
233
- errorMessage: z.string().optional()
1760
+ errorCode: z2.string().optional(),
1761
+ errorMessage: z2.string().optional()
234
1762
  });
235
- var serverPatchAckSchema = z.object({
236
- type: z.literal("patchAck"),
237
- protocolVersion: z.number().int(),
238
- requestId: z.string(),
239
- id: z.string(),
240
- ok: z.boolean(),
241
- diffSummary: z.string().optional(),
1763
+ var serverPatchAckSchema = z2.object({
1764
+ type: z2.literal("patchAck"),
1765
+ protocolVersion: z2.number().int(),
1766
+ requestId: z2.string(),
1767
+ id: z2.string(),
1768
+ ok: z2.boolean(),
1769
+ diffSummary: z2.string().optional(),
242
1770
  /** Present when this ack is for a `patchApply` with `dryRun: true`. */
243
- dryRun: z.boolean().optional(),
1771
+ dryRun: z2.boolean().optional(),
244
1772
  /** Absolute path written on successful non-dry apply (for touched-file log). */
245
- writtenFile: z.string().optional(),
1773
+ writtenFile: z2.string().optional(),
246
1774
  /** Server undo stack size after this apply (non-dry success only). */
247
- undoStackDepth: z.number().int().optional(),
248
- errorCode: z.string().optional(),
249
- errorMessage: z.string().optional()
1775
+ undoStackDepth: z2.number().int().optional(),
1776
+ errorCode: z2.string().optional(),
1777
+ errorMessage: z2.string().optional()
250
1778
  });
251
- var serverPatchUndoAckSchema = z.object({
252
- type: z.literal("patchUndoAck"),
253
- protocolVersion: z.number().int(),
254
- requestId: z.string(),
255
- ok: z.boolean(),
256
- file: z.string().optional(),
1779
+ var serverPatchUndoAckSchema = z2.object({
1780
+ type: z2.literal("patchUndoAck"),
1781
+ protocolVersion: z2.number().int(),
1782
+ requestId: z2.string(),
1783
+ ok: z2.boolean(),
1784
+ file: z2.string().optional(),
257
1785
  /** Remaining in-memory undo snapshots after this undo (success only). */
258
- undoStackDepth: z.number().int().optional(),
259
- errorCode: z.string().optional(),
260
- errorMessage: z.string().optional()
1786
+ undoStackDepth: z2.number().int().optional(),
1787
+ errorCode: z2.string().optional(),
1788
+ errorMessage: z2.string().optional()
1789
+ });
1790
+ var serverTagElementAckSchema = z2.object({
1791
+ type: z2.literal("tagElementAck"),
1792
+ protocolVersion: z2.number().int(),
1793
+ requestId: z2.string(),
1794
+ ok: z2.boolean(),
1795
+ id: z2.string().optional(),
1796
+ errorCode: z2.string().optional(),
1797
+ errorMessage: z2.string().optional()
261
1798
  });
262
- var serverMessageSchema = z.discriminatedUnion("type", [
1799
+ var serverMessageSchema = z2.discriminatedUnion("type", [
263
1800
  serverPongSchema,
264
1801
  serverErrorSchema,
265
1802
  serverIndexReadySchema,
266
1803
  serverSelectAckSchema,
267
1804
  serverPatchAckSchema,
268
- serverPatchUndoAckSchema
1805
+ serverPatchUndoAckSchema,
1806
+ serverTagElementAckSchema
269
1807
  ]);
270
1808
  function parseClientMessage(raw) {
271
1809
  let json;
@@ -290,20 +1828,283 @@ function parseServerMessage(raw) {
290
1828
  function serializeServerMessage(msg) {
291
1829
  return JSON.stringify(msg);
292
1830
  }
1831
+
1832
+ // src/library-registry.ts
1833
+ var LIBRARY_IDS = ["shadcn", "tailadmin", "daisyui"];
1834
+ var SHADCN_COMPOUND_TAGS = /* @__PURE__ */ new Set([
1835
+ "Button",
1836
+ "Card",
1837
+ "CardHeader",
1838
+ "CardTitle",
1839
+ "CardDescription",
1840
+ "CardContent",
1841
+ "CardFooter",
1842
+ "Input",
1843
+ "Label",
1844
+ "Badge",
1845
+ "Table",
1846
+ "TableHeader",
1847
+ "TableBody",
1848
+ "TableRow",
1849
+ "TableCell",
1850
+ "TableHead",
1851
+ "Dialog",
1852
+ "DialogTitle",
1853
+ "DialogContent",
1854
+ "DialogHeader"
1855
+ ]);
1856
+ var SHADCN_TAG_SEGMENTS = {
1857
+ Button: "button",
1858
+ Card: "card",
1859
+ CardHeader: "header",
1860
+ CardTitle: "title",
1861
+ CardDescription: "description",
1862
+ CardContent: "content",
1863
+ CardFooter: "footer",
1864
+ Input: "input",
1865
+ Label: "label",
1866
+ Badge: "badge",
1867
+ Table: "table",
1868
+ TableHeader: "header",
1869
+ TableBody: "body",
1870
+ TableRow: "row",
1871
+ TableCell: "cell",
1872
+ TableHead: "header",
1873
+ Dialog: "dialog",
1874
+ DialogTitle: "title",
1875
+ DialogContent: "content",
1876
+ DialogHeader: "header"
1877
+ };
1878
+ var DAISYUI_CLASS_HINTS = /* @__PURE__ */ new Set(["btn", "card", "table", "navbar", "badge", "input"]);
1879
+ function librarySegmentForTag(tagName, libraryId) {
1880
+ if (libraryId === "shadcn") {
1881
+ return SHADCN_TAG_SEGMENTS[tagName];
1882
+ }
1883
+ if (libraryId === "daisyui") {
1884
+ const lower = tagName.toLowerCase();
1885
+ if (DAISYUI_CLASS_HINTS.has(lower)) {
1886
+ return lower;
1887
+ }
1888
+ }
1889
+ return void 0;
1890
+ }
1891
+ function isShadcnCompoundTag(tagName) {
1892
+ return SHADCN_COMPOUND_TAGS.has(tagName);
1893
+ }
1894
+ function inferLibraryFromFilePath(filePath) {
1895
+ const normalized = filePath.replace(/\\/g, "/").toLowerCase();
1896
+ if (normalized.includes("/components/ui/")) {
1897
+ return "shadcn";
1898
+ }
1899
+ if (normalized.includes("/layout/appsidebar") || normalized.includes("/components/ecommerce/") || normalized.includes("tailadmin")) {
1900
+ return "tailadmin";
1901
+ }
1902
+ return void 0;
1903
+ }
1904
+ function resolveEntryLibraryHint(filePath, tagName, detectedLibraries) {
1905
+ const fromPath = inferLibraryFromFilePath(filePath);
1906
+ if (fromPath) {
1907
+ return fromPath;
1908
+ }
1909
+ if (detectedLibraries.includes("shadcn") && isShadcnCompoundTag(tagName)) {
1910
+ return "shadcn";
1911
+ }
1912
+ if (detectedLibraries.includes("daisyui")) {
1913
+ const lower = tagName.toLowerCase();
1914
+ if (DAISYUI_CLASS_HINTS.has(lower)) {
1915
+ return "daisyui";
1916
+ }
1917
+ }
1918
+ if (detectedLibraries.includes("tailadmin")) {
1919
+ return "tailadmin";
1920
+ }
1921
+ return void 0;
1922
+ }
1923
+ function detectShadcnComponentMode(tagName) {
1924
+ if (!tagName) {
1925
+ return null;
1926
+ }
1927
+ if (tagName === "Button") {
1928
+ return "button";
1929
+ }
1930
+ if (tagName === "Card" || tagName.startsWith("Card")) {
1931
+ return "card";
1932
+ }
1933
+ if (tagName.startsWith("Table")) {
1934
+ return "table";
1935
+ }
1936
+ if (tagName === "Input" || tagName === "Label") {
1937
+ return "form";
1938
+ }
1939
+ return null;
1940
+ }
1941
+ function libraryGuidanceForEntry(entry) {
1942
+ if (entry.libraryHint !== "shadcn") {
1943
+ return void 0;
1944
+ }
1945
+ const tag = entry.tagName ?? "";
1946
+ if (isShadcnCompoundTag(tag) && entry.riskLevel === "caution" && !entry.hasLiteralClassName) {
1947
+ return "shadcn component \u2014 add className on the component call site, or tag a native child inside CardContent.";
1948
+ }
1949
+ if (tag.startsWith("Dialog") && entry.riskLevel === "unsupported") {
1950
+ return "Dialog portal content may need a tagged host inside DialogContent.";
1951
+ }
1952
+ return void 0;
1953
+ }
1954
+ function formatLibraryList(libraries) {
1955
+ if (libraries.length === 0) {
1956
+ return "none detected";
1957
+ }
1958
+ return libraries.join(", ");
1959
+ }
1960
+
1961
+ // src/suggest-nuvio-id.ts
1962
+ var NUVIO_ID_PATTERN = /^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$/;
1963
+ function isValidNuvioId(id) {
1964
+ return NUVIO_ID_PATTERN.test(id);
1965
+ }
1966
+ var NATIVE_TAG_SEGMENT = {
1967
+ h1: "title",
1968
+ h2: "title",
1969
+ h3: "title",
1970
+ h4: "title",
1971
+ h5: "title",
1972
+ h6: "title",
1973
+ p: "text",
1974
+ span: "label",
1975
+ button: "button",
1976
+ a: "link",
1977
+ label: "label",
1978
+ td: "cell",
1979
+ th: "header",
1980
+ li: "item",
1981
+ img: "image",
1982
+ input: "input",
1983
+ div: "block",
1984
+ section: "section",
1985
+ article: "article",
1986
+ header: "header",
1987
+ footer: "footer",
1988
+ nav: "nav",
1989
+ main: "main"
1990
+ };
1991
+ function uniqueId(base, existing) {
1992
+ if (!existing.has(base)) {
1993
+ return base;
1994
+ }
1995
+ for (let i = 2; i < 100; i++) {
1996
+ const candidate = `${base}${i}`;
1997
+ if (!existing.has(candidate)) {
1998
+ return candidate;
1999
+ }
2000
+ }
2001
+ return `${base}.copy`;
2002
+ }
2003
+ function suggestNuvioId(options) {
2004
+ const tag = options.tagName;
2005
+ const librarySegment = options.libraryHint ? librarySegmentForTag(tag, options.libraryHint) : void 0;
2006
+ const nativeSegment = NATIVE_TAG_SEGMENT[tag.toLowerCase()];
2007
+ const segment = librarySegment ?? nativeSegment ?? "element";
2008
+ const prefix = options.parentPrefix?.trim();
2009
+ const base = prefix ? `${prefix}.${segment}` : `page.${segment}`;
2010
+ return uniqueId(base, options.existingIds);
2011
+ }
293
2012
  export {
2013
+ BRAND_ACCENT_COLORS,
2014
+ BRAND_ACCENT_SLOT_BY_ACTION,
2015
+ BRAND_APPLY_ACTIONS,
2016
+ BRAND_BUTTON_HOVERS,
2017
+ BRAND_BUTTON_HOVER_FIELD_LABEL,
2018
+ BRAND_BUTTON_VARIANTS,
2019
+ BRAND_BUTTON_VARIANT_FIELD_LABEL,
2020
+ BRAND_CARD_HOVERS,
2021
+ BRAND_CARD_HOVER_FIELD_LABEL,
2022
+ BRAND_CARD_SHADOWS,
2023
+ BRAND_CARD_SHADOW_FIELD_LABEL,
2024
+ BRAND_COLORS,
2025
+ BRAND_DENSITY,
2026
+ BRAND_DENSITY_FIELD_LABEL,
2027
+ BRAND_PAGE_DISCOVERY_PLURALS,
2028
+ BRAND_PRESET_DIMENSIONS_BY_ACTION,
2029
+ BRAND_RADIUS,
2030
+ BRAND_RADIUS_FIELD_LABEL,
2031
+ BRAND_SURFACES,
2032
+ BRAND_SURFACE_FIELD_LABEL,
2033
+ BRAND_TYPOGRAPHY,
2034
+ DEFAULT_BRAND_CONFIG,
2035
+ LIBRARY_IDS,
2036
+ NUVIO_BRAND_PATH,
2037
+ NUVIO_ID_PATTERN,
2038
+ NUVIO_PCC_PATH,
294
2039
  NUVIO_WS_PATH,
2040
+ PCC_BRANDABLE_CATEGORIES,
2041
+ PCC_REJECTED_CATEGORIES,
2042
+ PCC_SUPPORTED_CATEGORIES,
295
2043
  PROTOCOL_VERSION,
2044
+ brandColorsForAction,
2045
+ brandConfigSchema,
2046
+ brandConfigsEqual,
2047
+ brandFragmentHostHint,
2048
+ brandPresetDimensionsForAction,
296
2049
  breakpointSchema,
2050
+ buildBrandBulkPatchOps,
2051
+ buildBrandBulkPreviewSummary,
2052
+ buildBrandBulkTargetOps,
2053
+ buildBrandClassFragment,
2054
+ buildBrandPageDiscoveryLine,
2055
+ buildBrandPatchOps,
2056
+ buildBrandPreviewSummary,
2057
+ buildBrandValidateSummary,
2058
+ classNameModeSchema,
297
2059
  clientMessageSchema,
298
2060
  clientPatchApplySchema,
299
2061
  clientPatchUndoSchema,
300
2062
  clientPingSchema,
301
2063
  clientSelectSchema,
2064
+ clientTagElementSchema,
2065
+ defaultPccManifestPath,
2066
+ detectShadcnComponentMode,
302
2067
  duplicateIdErrorSchema,
303
2068
  duplicateIdOccurrenceSchema,
2069
+ entryMatchesBrandBulkAction,
2070
+ evaluateBrandPageScan,
2071
+ evaluatePageCoverage,
2072
+ filterBrandBulkCandidates,
2073
+ formatLibraryList,
2074
+ getBrandButtonHoverLabel,
2075
+ getBrandButtonVariantLabel,
2076
+ getBrandCardHoverLabel,
2077
+ getBrandCardShadowLabel,
2078
+ getBrandColorLabel,
2079
+ getBrandColorSlotLabel,
2080
+ getBrandDensityLabel,
2081
+ getBrandRadiusLabel,
2082
+ getBrandSurfaceLabel,
2083
+ getBrandTypographyFieldLabel,
2084
+ getBrandTypographyLabel,
304
2085
  hierarchyRoleSchema,
305
2086
  indexEntrySchema,
2087
+ inferBrandPresetsFromClassName,
2088
+ inferBrandPresetsFromTokens,
2089
+ inferLibraryFromFilePath,
2090
+ inspectBrandMatch,
2091
+ inspectBrandMatchForAction,
2092
+ isBrandAccentColor,
2093
+ isBrandableEntry,
2094
+ isHostPatchable,
2095
+ isNonBrandableNavEntry,
2096
+ isPccBrandableCategory,
2097
+ isPccRejectedCategory,
2098
+ isPccSupportedCategory,
2099
+ isShadcnCompoundTag,
2100
+ isValidNuvioId,
2101
+ libraryGuidanceForEntry,
2102
+ libraryIdSchema,
2103
+ librarySegmentForTag,
2104
+ normalizeAppRoute,
2105
+ normalizeBrandConfig,
306
2106
  parseClientMessage,
2107
+ parsePccManifest,
307
2108
  parseServerMessage,
308
2109
  patchOpDuplicateHostSchema,
309
2110
  patchOpMergeTailwindSchema,
@@ -312,9 +2113,18 @@ export {
312
2113
  patchOpSetHiddenSchema,
313
2114
  patchOpSetTableDataFieldSchema,
314
2115
  patchOpSetTextSchema,
2116
+ pccBrandableHostIds,
2117
+ pccCategoryLabel,
2118
+ pccHostsForBrandAction,
2119
+ pccHostsForCategory,
2120
+ pccManifestMatchesRoute,
2121
+ resolveBrandBulkPatchHostId,
2122
+ resolveBrandCategoryForEntry,
2123
+ resolveEntryLibraryHint,
315
2124
  riskLevelSchema,
316
2125
  rowTargetSchema,
317
2126
  runtimeDiagnosticsSchema,
2127
+ serializeBrandConfig,
318
2128
  serializeServerMessage,
319
2129
  serverErrorSchema,
320
2130
  serverIndexReadySchema,
@@ -323,7 +2133,9 @@ export {
323
2133
  serverPatchUndoAckSchema,
324
2134
  serverPongSchema,
325
2135
  serverSelectAckSchema,
2136
+ serverTagElementAckSchema,
326
2137
  styleTargetSchema,
2138
+ suggestNuvioId,
327
2139
  tableDataFieldSchema,
328
2140
  tableMetaSchema,
329
2141
  textTargetSchema