@seo-console/package 1.0.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.
@@ -0,0 +1,3642 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/components/index.ts
31
+ var components_exports = {};
32
+ __export(components_exports, {
33
+ Button: () => Button,
34
+ Card: () => Card,
35
+ CardContent: () => CardContent,
36
+ CardDescription: () => CardDescription,
37
+ CardFooter: () => CardFooter,
38
+ CardHeader: () => CardHeader,
39
+ CardTitle: () => CardTitle,
40
+ Input: () => Input,
41
+ OGImagePreview: () => OGImagePreview,
42
+ SEORecordForm: () => SEORecordForm,
43
+ SEORecordList: () => SEORecordList,
44
+ Spinner: () => Spinner,
45
+ ValidationDashboard: () => ValidationDashboard
46
+ });
47
+ module.exports = __toCommonJS(components_exports);
48
+
49
+ // src/components/seo/SEORecordList.tsx
50
+ var import_react3 = require("react");
51
+
52
+ // src/components/ui/button.tsx
53
+ var React = __toESM(require("react"));
54
+
55
+ // src/lib/utils.ts
56
+ var import_clsx = require("clsx");
57
+
58
+ // ../../node_modules/tailwind-merge/dist/bundle-mjs.mjs
59
+ var CLASS_PART_SEPARATOR = "-";
60
+ var createClassGroupUtils = (config) => {
61
+ const classMap = createClassMap(config);
62
+ const {
63
+ conflictingClassGroups,
64
+ conflictingClassGroupModifiers
65
+ } = config;
66
+ const getClassGroupId = (className) => {
67
+ const classParts = className.split(CLASS_PART_SEPARATOR);
68
+ if (classParts[0] === "" && classParts.length !== 1) {
69
+ classParts.shift();
70
+ }
71
+ return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className);
72
+ };
73
+ const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {
74
+ const conflicts = conflictingClassGroups[classGroupId] || [];
75
+ if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) {
76
+ return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]];
77
+ }
78
+ return conflicts;
79
+ };
80
+ return {
81
+ getClassGroupId,
82
+ getConflictingClassGroupIds
83
+ };
84
+ };
85
+ var getGroupRecursive = (classParts, classPartObject) => {
86
+ if (classParts.length === 0) {
87
+ return classPartObject.classGroupId;
88
+ }
89
+ const currentClassPart = classParts[0];
90
+ const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);
91
+ const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive(classParts.slice(1), nextClassPartObject) : void 0;
92
+ if (classGroupFromNextClassPart) {
93
+ return classGroupFromNextClassPart;
94
+ }
95
+ if (classPartObject.validators.length === 0) {
96
+ return void 0;
97
+ }
98
+ const classRest = classParts.join(CLASS_PART_SEPARATOR);
99
+ return classPartObject.validators.find(({
100
+ validator
101
+ }) => validator(classRest))?.classGroupId;
102
+ };
103
+ var arbitraryPropertyRegex = /^\[(.+)\]$/;
104
+ var getGroupIdForArbitraryProperty = (className) => {
105
+ if (arbitraryPropertyRegex.test(className)) {
106
+ const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];
107
+ const property = arbitraryPropertyClassName?.substring(0, arbitraryPropertyClassName.indexOf(":"));
108
+ if (property) {
109
+ return "arbitrary.." + property;
110
+ }
111
+ }
112
+ };
113
+ var createClassMap = (config) => {
114
+ const {
115
+ theme,
116
+ prefix
117
+ } = config;
118
+ const classMap = {
119
+ nextPart: /* @__PURE__ */ new Map(),
120
+ validators: []
121
+ };
122
+ const prefixedClassGroupEntries = getPrefixedClassGroupEntries(Object.entries(config.classGroups), prefix);
123
+ prefixedClassGroupEntries.forEach(([classGroupId, classGroup]) => {
124
+ processClassesRecursively(classGroup, classMap, classGroupId, theme);
125
+ });
126
+ return classMap;
127
+ };
128
+ var processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {
129
+ classGroup.forEach((classDefinition) => {
130
+ if (typeof classDefinition === "string") {
131
+ const classPartObjectToEdit = classDefinition === "" ? classPartObject : getPart(classPartObject, classDefinition);
132
+ classPartObjectToEdit.classGroupId = classGroupId;
133
+ return;
134
+ }
135
+ if (typeof classDefinition === "function") {
136
+ if (isThemeGetter(classDefinition)) {
137
+ processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);
138
+ return;
139
+ }
140
+ classPartObject.validators.push({
141
+ validator: classDefinition,
142
+ classGroupId
143
+ });
144
+ return;
145
+ }
146
+ Object.entries(classDefinition).forEach(([key, classGroup2]) => {
147
+ processClassesRecursively(classGroup2, getPart(classPartObject, key), classGroupId, theme);
148
+ });
149
+ });
150
+ };
151
+ var getPart = (classPartObject, path) => {
152
+ let currentClassPartObject = classPartObject;
153
+ path.split(CLASS_PART_SEPARATOR).forEach((pathPart) => {
154
+ if (!currentClassPartObject.nextPart.has(pathPart)) {
155
+ currentClassPartObject.nextPart.set(pathPart, {
156
+ nextPart: /* @__PURE__ */ new Map(),
157
+ validators: []
158
+ });
159
+ }
160
+ currentClassPartObject = currentClassPartObject.nextPart.get(pathPart);
161
+ });
162
+ return currentClassPartObject;
163
+ };
164
+ var isThemeGetter = (func) => func.isThemeGetter;
165
+ var getPrefixedClassGroupEntries = (classGroupEntries, prefix) => {
166
+ if (!prefix) {
167
+ return classGroupEntries;
168
+ }
169
+ return classGroupEntries.map(([classGroupId, classGroup]) => {
170
+ const prefixedClassGroup = classGroup.map((classDefinition) => {
171
+ if (typeof classDefinition === "string") {
172
+ return prefix + classDefinition;
173
+ }
174
+ if (typeof classDefinition === "object") {
175
+ return Object.fromEntries(Object.entries(classDefinition).map(([key, value]) => [prefix + key, value]));
176
+ }
177
+ return classDefinition;
178
+ });
179
+ return [classGroupId, prefixedClassGroup];
180
+ });
181
+ };
182
+ var createLruCache = (maxCacheSize) => {
183
+ if (maxCacheSize < 1) {
184
+ return {
185
+ get: () => void 0,
186
+ set: () => {
187
+ }
188
+ };
189
+ }
190
+ let cacheSize = 0;
191
+ let cache = /* @__PURE__ */ new Map();
192
+ let previousCache = /* @__PURE__ */ new Map();
193
+ const update = (key, value) => {
194
+ cache.set(key, value);
195
+ cacheSize++;
196
+ if (cacheSize > maxCacheSize) {
197
+ cacheSize = 0;
198
+ previousCache = cache;
199
+ cache = /* @__PURE__ */ new Map();
200
+ }
201
+ };
202
+ return {
203
+ get(key) {
204
+ let value = cache.get(key);
205
+ if (value !== void 0) {
206
+ return value;
207
+ }
208
+ if ((value = previousCache.get(key)) !== void 0) {
209
+ update(key, value);
210
+ return value;
211
+ }
212
+ },
213
+ set(key, value) {
214
+ if (cache.has(key)) {
215
+ cache.set(key, value);
216
+ } else {
217
+ update(key, value);
218
+ }
219
+ }
220
+ };
221
+ };
222
+ var IMPORTANT_MODIFIER = "!";
223
+ var createParseClassName = (config) => {
224
+ const {
225
+ separator,
226
+ experimentalParseClassName
227
+ } = config;
228
+ const isSeparatorSingleCharacter = separator.length === 1;
229
+ const firstSeparatorCharacter = separator[0];
230
+ const separatorLength = separator.length;
231
+ const parseClassName = (className) => {
232
+ const modifiers = [];
233
+ let bracketDepth = 0;
234
+ let modifierStart = 0;
235
+ let postfixModifierPosition;
236
+ for (let index = 0; index < className.length; index++) {
237
+ let currentCharacter = className[index];
238
+ if (bracketDepth === 0) {
239
+ if (currentCharacter === firstSeparatorCharacter && (isSeparatorSingleCharacter || className.slice(index, index + separatorLength) === separator)) {
240
+ modifiers.push(className.slice(modifierStart, index));
241
+ modifierStart = index + separatorLength;
242
+ continue;
243
+ }
244
+ if (currentCharacter === "/") {
245
+ postfixModifierPosition = index;
246
+ continue;
247
+ }
248
+ }
249
+ if (currentCharacter === "[") {
250
+ bracketDepth++;
251
+ } else if (currentCharacter === "]") {
252
+ bracketDepth--;
253
+ }
254
+ }
255
+ const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.substring(modifierStart);
256
+ const hasImportantModifier = baseClassNameWithImportantModifier.startsWith(IMPORTANT_MODIFIER);
257
+ const baseClassName = hasImportantModifier ? baseClassNameWithImportantModifier.substring(1) : baseClassNameWithImportantModifier;
258
+ const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : void 0;
259
+ return {
260
+ modifiers,
261
+ hasImportantModifier,
262
+ baseClassName,
263
+ maybePostfixModifierPosition
264
+ };
265
+ };
266
+ if (experimentalParseClassName) {
267
+ return (className) => experimentalParseClassName({
268
+ className,
269
+ parseClassName
270
+ });
271
+ }
272
+ return parseClassName;
273
+ };
274
+ var sortModifiers = (modifiers) => {
275
+ if (modifiers.length <= 1) {
276
+ return modifiers;
277
+ }
278
+ const sortedModifiers = [];
279
+ let unsortedModifiers = [];
280
+ modifiers.forEach((modifier) => {
281
+ const isArbitraryVariant = modifier[0] === "[";
282
+ if (isArbitraryVariant) {
283
+ sortedModifiers.push(...unsortedModifiers.sort(), modifier);
284
+ unsortedModifiers = [];
285
+ } else {
286
+ unsortedModifiers.push(modifier);
287
+ }
288
+ });
289
+ sortedModifiers.push(...unsortedModifiers.sort());
290
+ return sortedModifiers;
291
+ };
292
+ var createConfigUtils = (config) => ({
293
+ cache: createLruCache(config.cacheSize),
294
+ parseClassName: createParseClassName(config),
295
+ ...createClassGroupUtils(config)
296
+ });
297
+ var SPLIT_CLASSES_REGEX = /\s+/;
298
+ var mergeClassList = (classList, configUtils) => {
299
+ const {
300
+ parseClassName,
301
+ getClassGroupId,
302
+ getConflictingClassGroupIds
303
+ } = configUtils;
304
+ const classGroupsInConflict = [];
305
+ const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);
306
+ let result = "";
307
+ for (let index = classNames.length - 1; index >= 0; index -= 1) {
308
+ const originalClassName = classNames[index];
309
+ const {
310
+ modifiers,
311
+ hasImportantModifier,
312
+ baseClassName,
313
+ maybePostfixModifierPosition
314
+ } = parseClassName(originalClassName);
315
+ let hasPostfixModifier = Boolean(maybePostfixModifierPosition);
316
+ let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);
317
+ if (!classGroupId) {
318
+ if (!hasPostfixModifier) {
319
+ result = originalClassName + (result.length > 0 ? " " + result : result);
320
+ continue;
321
+ }
322
+ classGroupId = getClassGroupId(baseClassName);
323
+ if (!classGroupId) {
324
+ result = originalClassName + (result.length > 0 ? " " + result : result);
325
+ continue;
326
+ }
327
+ hasPostfixModifier = false;
328
+ }
329
+ const variantModifier = sortModifiers(modifiers).join(":");
330
+ const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;
331
+ const classId = modifierId + classGroupId;
332
+ if (classGroupsInConflict.includes(classId)) {
333
+ continue;
334
+ }
335
+ classGroupsInConflict.push(classId);
336
+ const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);
337
+ for (let i = 0; i < conflictGroups.length; ++i) {
338
+ const group = conflictGroups[i];
339
+ classGroupsInConflict.push(modifierId + group);
340
+ }
341
+ result = originalClassName + (result.length > 0 ? " " + result : result);
342
+ }
343
+ return result;
344
+ };
345
+ function twJoin() {
346
+ let index = 0;
347
+ let argument;
348
+ let resolvedValue;
349
+ let string = "";
350
+ while (index < arguments.length) {
351
+ if (argument = arguments[index++]) {
352
+ if (resolvedValue = toValue(argument)) {
353
+ string && (string += " ");
354
+ string += resolvedValue;
355
+ }
356
+ }
357
+ }
358
+ return string;
359
+ }
360
+ var toValue = (mix) => {
361
+ if (typeof mix === "string") {
362
+ return mix;
363
+ }
364
+ let resolvedValue;
365
+ let string = "";
366
+ for (let k = 0; k < mix.length; k++) {
367
+ if (mix[k]) {
368
+ if (resolvedValue = toValue(mix[k])) {
369
+ string && (string += " ");
370
+ string += resolvedValue;
371
+ }
372
+ }
373
+ }
374
+ return string;
375
+ };
376
+ function createTailwindMerge(createConfigFirst, ...createConfigRest) {
377
+ let configUtils;
378
+ let cacheGet;
379
+ let cacheSet;
380
+ let functionToCall = initTailwindMerge;
381
+ function initTailwindMerge(classList) {
382
+ const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());
383
+ configUtils = createConfigUtils(config);
384
+ cacheGet = configUtils.cache.get;
385
+ cacheSet = configUtils.cache.set;
386
+ functionToCall = tailwindMerge;
387
+ return tailwindMerge(classList);
388
+ }
389
+ function tailwindMerge(classList) {
390
+ const cachedResult = cacheGet(classList);
391
+ if (cachedResult) {
392
+ return cachedResult;
393
+ }
394
+ const result = mergeClassList(classList, configUtils);
395
+ cacheSet(classList, result);
396
+ return result;
397
+ }
398
+ return function callTailwindMerge() {
399
+ return functionToCall(twJoin.apply(null, arguments));
400
+ };
401
+ }
402
+ var fromTheme = (key) => {
403
+ const themeGetter = (theme) => theme[key] || [];
404
+ themeGetter.isThemeGetter = true;
405
+ return themeGetter;
406
+ };
407
+ var arbitraryValueRegex = /^\[(?:([a-z-]+):)?(.+)\]$/i;
408
+ var fractionRegex = /^\d+\/\d+$/;
409
+ var stringLengths = /* @__PURE__ */ new Set(["px", "full", "screen"]);
410
+ var tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/;
411
+ var lengthUnitRegex = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/;
412
+ var colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch))\(.+\)$/;
413
+ var shadowRegex = /^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;
414
+ var imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/;
415
+ var isLength = (value) => isNumber(value) || stringLengths.has(value) || fractionRegex.test(value);
416
+ var isArbitraryLength = (value) => getIsArbitraryValue(value, "length", isLengthOnly);
417
+ var isNumber = (value) => Boolean(value) && !Number.isNaN(Number(value));
418
+ var isArbitraryNumber = (value) => getIsArbitraryValue(value, "number", isNumber);
419
+ var isInteger = (value) => Boolean(value) && Number.isInteger(Number(value));
420
+ var isPercent = (value) => value.endsWith("%") && isNumber(value.slice(0, -1));
421
+ var isArbitraryValue = (value) => arbitraryValueRegex.test(value);
422
+ var isTshirtSize = (value) => tshirtUnitRegex.test(value);
423
+ var sizeLabels = /* @__PURE__ */ new Set(["length", "size", "percentage"]);
424
+ var isArbitrarySize = (value) => getIsArbitraryValue(value, sizeLabels, isNever);
425
+ var isArbitraryPosition = (value) => getIsArbitraryValue(value, "position", isNever);
426
+ var imageLabels = /* @__PURE__ */ new Set(["image", "url"]);
427
+ var isArbitraryImage = (value) => getIsArbitraryValue(value, imageLabels, isImage);
428
+ var isArbitraryShadow = (value) => getIsArbitraryValue(value, "", isShadow);
429
+ var isAny = () => true;
430
+ var getIsArbitraryValue = (value, label, testValue) => {
431
+ const result = arbitraryValueRegex.exec(value);
432
+ if (result) {
433
+ if (result[1]) {
434
+ return typeof label === "string" ? result[1] === label : label.has(result[1]);
435
+ }
436
+ return testValue(result[2]);
437
+ }
438
+ return false;
439
+ };
440
+ var isLengthOnly = (value) => (
441
+ // `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.
442
+ // For example, `hsl(0 0% 0%)` would be classified as a length without this check.
443
+ // I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.
444
+ lengthUnitRegex.test(value) && !colorFunctionRegex.test(value)
445
+ );
446
+ var isNever = () => false;
447
+ var isShadow = (value) => shadowRegex.test(value);
448
+ var isImage = (value) => imageRegex.test(value);
449
+ var getDefaultConfig = () => {
450
+ const colors = fromTheme("colors");
451
+ const spacing = fromTheme("spacing");
452
+ const blur = fromTheme("blur");
453
+ const brightness = fromTheme("brightness");
454
+ const borderColor = fromTheme("borderColor");
455
+ const borderRadius = fromTheme("borderRadius");
456
+ const borderSpacing = fromTheme("borderSpacing");
457
+ const borderWidth = fromTheme("borderWidth");
458
+ const contrast = fromTheme("contrast");
459
+ const grayscale = fromTheme("grayscale");
460
+ const hueRotate = fromTheme("hueRotate");
461
+ const invert = fromTheme("invert");
462
+ const gap = fromTheme("gap");
463
+ const gradientColorStops = fromTheme("gradientColorStops");
464
+ const gradientColorStopPositions = fromTheme("gradientColorStopPositions");
465
+ const inset = fromTheme("inset");
466
+ const margin = fromTheme("margin");
467
+ const opacity = fromTheme("opacity");
468
+ const padding = fromTheme("padding");
469
+ const saturate = fromTheme("saturate");
470
+ const scale = fromTheme("scale");
471
+ const sepia = fromTheme("sepia");
472
+ const skew = fromTheme("skew");
473
+ const space = fromTheme("space");
474
+ const translate = fromTheme("translate");
475
+ const getOverscroll = () => ["auto", "contain", "none"];
476
+ const getOverflow = () => ["auto", "hidden", "clip", "visible", "scroll"];
477
+ const getSpacingWithAutoAndArbitrary = () => ["auto", isArbitraryValue, spacing];
478
+ const getSpacingWithArbitrary = () => [isArbitraryValue, spacing];
479
+ const getLengthWithEmptyAndArbitrary = () => ["", isLength, isArbitraryLength];
480
+ const getNumberWithAutoAndArbitrary = () => ["auto", isNumber, isArbitraryValue];
481
+ const getPositions = () => ["bottom", "center", "left", "left-bottom", "left-top", "right", "right-bottom", "right-top", "top"];
482
+ const getLineStyles = () => ["solid", "dashed", "dotted", "double", "none"];
483
+ const getBlendModes = () => ["normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"];
484
+ const getAlign = () => ["start", "end", "center", "between", "around", "evenly", "stretch"];
485
+ const getZeroAndEmpty = () => ["", "0", isArbitraryValue];
486
+ const getBreaks = () => ["auto", "avoid", "all", "avoid-page", "page", "left", "right", "column"];
487
+ const getNumberAndArbitrary = () => [isNumber, isArbitraryValue];
488
+ return {
489
+ cacheSize: 500,
490
+ separator: ":",
491
+ theme: {
492
+ colors: [isAny],
493
+ spacing: [isLength, isArbitraryLength],
494
+ blur: ["none", "", isTshirtSize, isArbitraryValue],
495
+ brightness: getNumberAndArbitrary(),
496
+ borderColor: [colors],
497
+ borderRadius: ["none", "", "full", isTshirtSize, isArbitraryValue],
498
+ borderSpacing: getSpacingWithArbitrary(),
499
+ borderWidth: getLengthWithEmptyAndArbitrary(),
500
+ contrast: getNumberAndArbitrary(),
501
+ grayscale: getZeroAndEmpty(),
502
+ hueRotate: getNumberAndArbitrary(),
503
+ invert: getZeroAndEmpty(),
504
+ gap: getSpacingWithArbitrary(),
505
+ gradientColorStops: [colors],
506
+ gradientColorStopPositions: [isPercent, isArbitraryLength],
507
+ inset: getSpacingWithAutoAndArbitrary(),
508
+ margin: getSpacingWithAutoAndArbitrary(),
509
+ opacity: getNumberAndArbitrary(),
510
+ padding: getSpacingWithArbitrary(),
511
+ saturate: getNumberAndArbitrary(),
512
+ scale: getNumberAndArbitrary(),
513
+ sepia: getZeroAndEmpty(),
514
+ skew: getNumberAndArbitrary(),
515
+ space: getSpacingWithArbitrary(),
516
+ translate: getSpacingWithArbitrary()
517
+ },
518
+ classGroups: {
519
+ // Layout
520
+ /**
521
+ * Aspect Ratio
522
+ * @see https://tailwindcss.com/docs/aspect-ratio
523
+ */
524
+ aspect: [{
525
+ aspect: ["auto", "square", "video", isArbitraryValue]
526
+ }],
527
+ /**
528
+ * Container
529
+ * @see https://tailwindcss.com/docs/container
530
+ */
531
+ container: ["container"],
532
+ /**
533
+ * Columns
534
+ * @see https://tailwindcss.com/docs/columns
535
+ */
536
+ columns: [{
537
+ columns: [isTshirtSize]
538
+ }],
539
+ /**
540
+ * Break After
541
+ * @see https://tailwindcss.com/docs/break-after
542
+ */
543
+ "break-after": [{
544
+ "break-after": getBreaks()
545
+ }],
546
+ /**
547
+ * Break Before
548
+ * @see https://tailwindcss.com/docs/break-before
549
+ */
550
+ "break-before": [{
551
+ "break-before": getBreaks()
552
+ }],
553
+ /**
554
+ * Break Inside
555
+ * @see https://tailwindcss.com/docs/break-inside
556
+ */
557
+ "break-inside": [{
558
+ "break-inside": ["auto", "avoid", "avoid-page", "avoid-column"]
559
+ }],
560
+ /**
561
+ * Box Decoration Break
562
+ * @see https://tailwindcss.com/docs/box-decoration-break
563
+ */
564
+ "box-decoration": [{
565
+ "box-decoration": ["slice", "clone"]
566
+ }],
567
+ /**
568
+ * Box Sizing
569
+ * @see https://tailwindcss.com/docs/box-sizing
570
+ */
571
+ box: [{
572
+ box: ["border", "content"]
573
+ }],
574
+ /**
575
+ * Display
576
+ * @see https://tailwindcss.com/docs/display
577
+ */
578
+ display: ["block", "inline-block", "inline", "flex", "inline-flex", "table", "inline-table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", "table-row-group", "table-row", "flow-root", "grid", "inline-grid", "contents", "list-item", "hidden"],
579
+ /**
580
+ * Floats
581
+ * @see https://tailwindcss.com/docs/float
582
+ */
583
+ float: [{
584
+ float: ["right", "left", "none", "start", "end"]
585
+ }],
586
+ /**
587
+ * Clear
588
+ * @see https://tailwindcss.com/docs/clear
589
+ */
590
+ clear: [{
591
+ clear: ["left", "right", "both", "none", "start", "end"]
592
+ }],
593
+ /**
594
+ * Isolation
595
+ * @see https://tailwindcss.com/docs/isolation
596
+ */
597
+ isolation: ["isolate", "isolation-auto"],
598
+ /**
599
+ * Object Fit
600
+ * @see https://tailwindcss.com/docs/object-fit
601
+ */
602
+ "object-fit": [{
603
+ object: ["contain", "cover", "fill", "none", "scale-down"]
604
+ }],
605
+ /**
606
+ * Object Position
607
+ * @see https://tailwindcss.com/docs/object-position
608
+ */
609
+ "object-position": [{
610
+ object: [...getPositions(), isArbitraryValue]
611
+ }],
612
+ /**
613
+ * Overflow
614
+ * @see https://tailwindcss.com/docs/overflow
615
+ */
616
+ overflow: [{
617
+ overflow: getOverflow()
618
+ }],
619
+ /**
620
+ * Overflow X
621
+ * @see https://tailwindcss.com/docs/overflow
622
+ */
623
+ "overflow-x": [{
624
+ "overflow-x": getOverflow()
625
+ }],
626
+ /**
627
+ * Overflow Y
628
+ * @see https://tailwindcss.com/docs/overflow
629
+ */
630
+ "overflow-y": [{
631
+ "overflow-y": getOverflow()
632
+ }],
633
+ /**
634
+ * Overscroll Behavior
635
+ * @see https://tailwindcss.com/docs/overscroll-behavior
636
+ */
637
+ overscroll: [{
638
+ overscroll: getOverscroll()
639
+ }],
640
+ /**
641
+ * Overscroll Behavior X
642
+ * @see https://tailwindcss.com/docs/overscroll-behavior
643
+ */
644
+ "overscroll-x": [{
645
+ "overscroll-x": getOverscroll()
646
+ }],
647
+ /**
648
+ * Overscroll Behavior Y
649
+ * @see https://tailwindcss.com/docs/overscroll-behavior
650
+ */
651
+ "overscroll-y": [{
652
+ "overscroll-y": getOverscroll()
653
+ }],
654
+ /**
655
+ * Position
656
+ * @see https://tailwindcss.com/docs/position
657
+ */
658
+ position: ["static", "fixed", "absolute", "relative", "sticky"],
659
+ /**
660
+ * Top / Right / Bottom / Left
661
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
662
+ */
663
+ inset: [{
664
+ inset: [inset]
665
+ }],
666
+ /**
667
+ * Right / Left
668
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
669
+ */
670
+ "inset-x": [{
671
+ "inset-x": [inset]
672
+ }],
673
+ /**
674
+ * Top / Bottom
675
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
676
+ */
677
+ "inset-y": [{
678
+ "inset-y": [inset]
679
+ }],
680
+ /**
681
+ * Start
682
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
683
+ */
684
+ start: [{
685
+ start: [inset]
686
+ }],
687
+ /**
688
+ * End
689
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
690
+ */
691
+ end: [{
692
+ end: [inset]
693
+ }],
694
+ /**
695
+ * Top
696
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
697
+ */
698
+ top: [{
699
+ top: [inset]
700
+ }],
701
+ /**
702
+ * Right
703
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
704
+ */
705
+ right: [{
706
+ right: [inset]
707
+ }],
708
+ /**
709
+ * Bottom
710
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
711
+ */
712
+ bottom: [{
713
+ bottom: [inset]
714
+ }],
715
+ /**
716
+ * Left
717
+ * @see https://tailwindcss.com/docs/top-right-bottom-left
718
+ */
719
+ left: [{
720
+ left: [inset]
721
+ }],
722
+ /**
723
+ * Visibility
724
+ * @see https://tailwindcss.com/docs/visibility
725
+ */
726
+ visibility: ["visible", "invisible", "collapse"],
727
+ /**
728
+ * Z-Index
729
+ * @see https://tailwindcss.com/docs/z-index
730
+ */
731
+ z: [{
732
+ z: ["auto", isInteger, isArbitraryValue]
733
+ }],
734
+ // Flexbox and Grid
735
+ /**
736
+ * Flex Basis
737
+ * @see https://tailwindcss.com/docs/flex-basis
738
+ */
739
+ basis: [{
740
+ basis: getSpacingWithAutoAndArbitrary()
741
+ }],
742
+ /**
743
+ * Flex Direction
744
+ * @see https://tailwindcss.com/docs/flex-direction
745
+ */
746
+ "flex-direction": [{
747
+ flex: ["row", "row-reverse", "col", "col-reverse"]
748
+ }],
749
+ /**
750
+ * Flex Wrap
751
+ * @see https://tailwindcss.com/docs/flex-wrap
752
+ */
753
+ "flex-wrap": [{
754
+ flex: ["wrap", "wrap-reverse", "nowrap"]
755
+ }],
756
+ /**
757
+ * Flex
758
+ * @see https://tailwindcss.com/docs/flex
759
+ */
760
+ flex: [{
761
+ flex: ["1", "auto", "initial", "none", isArbitraryValue]
762
+ }],
763
+ /**
764
+ * Flex Grow
765
+ * @see https://tailwindcss.com/docs/flex-grow
766
+ */
767
+ grow: [{
768
+ grow: getZeroAndEmpty()
769
+ }],
770
+ /**
771
+ * Flex Shrink
772
+ * @see https://tailwindcss.com/docs/flex-shrink
773
+ */
774
+ shrink: [{
775
+ shrink: getZeroAndEmpty()
776
+ }],
777
+ /**
778
+ * Order
779
+ * @see https://tailwindcss.com/docs/order
780
+ */
781
+ order: [{
782
+ order: ["first", "last", "none", isInteger, isArbitraryValue]
783
+ }],
784
+ /**
785
+ * Grid Template Columns
786
+ * @see https://tailwindcss.com/docs/grid-template-columns
787
+ */
788
+ "grid-cols": [{
789
+ "grid-cols": [isAny]
790
+ }],
791
+ /**
792
+ * Grid Column Start / End
793
+ * @see https://tailwindcss.com/docs/grid-column
794
+ */
795
+ "col-start-end": [{
796
+ col: ["auto", {
797
+ span: ["full", isInteger, isArbitraryValue]
798
+ }, isArbitraryValue]
799
+ }],
800
+ /**
801
+ * Grid Column Start
802
+ * @see https://tailwindcss.com/docs/grid-column
803
+ */
804
+ "col-start": [{
805
+ "col-start": getNumberWithAutoAndArbitrary()
806
+ }],
807
+ /**
808
+ * Grid Column End
809
+ * @see https://tailwindcss.com/docs/grid-column
810
+ */
811
+ "col-end": [{
812
+ "col-end": getNumberWithAutoAndArbitrary()
813
+ }],
814
+ /**
815
+ * Grid Template Rows
816
+ * @see https://tailwindcss.com/docs/grid-template-rows
817
+ */
818
+ "grid-rows": [{
819
+ "grid-rows": [isAny]
820
+ }],
821
+ /**
822
+ * Grid Row Start / End
823
+ * @see https://tailwindcss.com/docs/grid-row
824
+ */
825
+ "row-start-end": [{
826
+ row: ["auto", {
827
+ span: [isInteger, isArbitraryValue]
828
+ }, isArbitraryValue]
829
+ }],
830
+ /**
831
+ * Grid Row Start
832
+ * @see https://tailwindcss.com/docs/grid-row
833
+ */
834
+ "row-start": [{
835
+ "row-start": getNumberWithAutoAndArbitrary()
836
+ }],
837
+ /**
838
+ * Grid Row End
839
+ * @see https://tailwindcss.com/docs/grid-row
840
+ */
841
+ "row-end": [{
842
+ "row-end": getNumberWithAutoAndArbitrary()
843
+ }],
844
+ /**
845
+ * Grid Auto Flow
846
+ * @see https://tailwindcss.com/docs/grid-auto-flow
847
+ */
848
+ "grid-flow": [{
849
+ "grid-flow": ["row", "col", "dense", "row-dense", "col-dense"]
850
+ }],
851
+ /**
852
+ * Grid Auto Columns
853
+ * @see https://tailwindcss.com/docs/grid-auto-columns
854
+ */
855
+ "auto-cols": [{
856
+ "auto-cols": ["auto", "min", "max", "fr", isArbitraryValue]
857
+ }],
858
+ /**
859
+ * Grid Auto Rows
860
+ * @see https://tailwindcss.com/docs/grid-auto-rows
861
+ */
862
+ "auto-rows": [{
863
+ "auto-rows": ["auto", "min", "max", "fr", isArbitraryValue]
864
+ }],
865
+ /**
866
+ * Gap
867
+ * @see https://tailwindcss.com/docs/gap
868
+ */
869
+ gap: [{
870
+ gap: [gap]
871
+ }],
872
+ /**
873
+ * Gap X
874
+ * @see https://tailwindcss.com/docs/gap
875
+ */
876
+ "gap-x": [{
877
+ "gap-x": [gap]
878
+ }],
879
+ /**
880
+ * Gap Y
881
+ * @see https://tailwindcss.com/docs/gap
882
+ */
883
+ "gap-y": [{
884
+ "gap-y": [gap]
885
+ }],
886
+ /**
887
+ * Justify Content
888
+ * @see https://tailwindcss.com/docs/justify-content
889
+ */
890
+ "justify-content": [{
891
+ justify: ["normal", ...getAlign()]
892
+ }],
893
+ /**
894
+ * Justify Items
895
+ * @see https://tailwindcss.com/docs/justify-items
896
+ */
897
+ "justify-items": [{
898
+ "justify-items": ["start", "end", "center", "stretch"]
899
+ }],
900
+ /**
901
+ * Justify Self
902
+ * @see https://tailwindcss.com/docs/justify-self
903
+ */
904
+ "justify-self": [{
905
+ "justify-self": ["auto", "start", "end", "center", "stretch"]
906
+ }],
907
+ /**
908
+ * Align Content
909
+ * @see https://tailwindcss.com/docs/align-content
910
+ */
911
+ "align-content": [{
912
+ content: ["normal", ...getAlign(), "baseline"]
913
+ }],
914
+ /**
915
+ * Align Items
916
+ * @see https://tailwindcss.com/docs/align-items
917
+ */
918
+ "align-items": [{
919
+ items: ["start", "end", "center", "baseline", "stretch"]
920
+ }],
921
+ /**
922
+ * Align Self
923
+ * @see https://tailwindcss.com/docs/align-self
924
+ */
925
+ "align-self": [{
926
+ self: ["auto", "start", "end", "center", "stretch", "baseline"]
927
+ }],
928
+ /**
929
+ * Place Content
930
+ * @see https://tailwindcss.com/docs/place-content
931
+ */
932
+ "place-content": [{
933
+ "place-content": [...getAlign(), "baseline"]
934
+ }],
935
+ /**
936
+ * Place Items
937
+ * @see https://tailwindcss.com/docs/place-items
938
+ */
939
+ "place-items": [{
940
+ "place-items": ["start", "end", "center", "baseline", "stretch"]
941
+ }],
942
+ /**
943
+ * Place Self
944
+ * @see https://tailwindcss.com/docs/place-self
945
+ */
946
+ "place-self": [{
947
+ "place-self": ["auto", "start", "end", "center", "stretch"]
948
+ }],
949
+ // Spacing
950
+ /**
951
+ * Padding
952
+ * @see https://tailwindcss.com/docs/padding
953
+ */
954
+ p: [{
955
+ p: [padding]
956
+ }],
957
+ /**
958
+ * Padding X
959
+ * @see https://tailwindcss.com/docs/padding
960
+ */
961
+ px: [{
962
+ px: [padding]
963
+ }],
964
+ /**
965
+ * Padding Y
966
+ * @see https://tailwindcss.com/docs/padding
967
+ */
968
+ py: [{
969
+ py: [padding]
970
+ }],
971
+ /**
972
+ * Padding Start
973
+ * @see https://tailwindcss.com/docs/padding
974
+ */
975
+ ps: [{
976
+ ps: [padding]
977
+ }],
978
+ /**
979
+ * Padding End
980
+ * @see https://tailwindcss.com/docs/padding
981
+ */
982
+ pe: [{
983
+ pe: [padding]
984
+ }],
985
+ /**
986
+ * Padding Top
987
+ * @see https://tailwindcss.com/docs/padding
988
+ */
989
+ pt: [{
990
+ pt: [padding]
991
+ }],
992
+ /**
993
+ * Padding Right
994
+ * @see https://tailwindcss.com/docs/padding
995
+ */
996
+ pr: [{
997
+ pr: [padding]
998
+ }],
999
+ /**
1000
+ * Padding Bottom
1001
+ * @see https://tailwindcss.com/docs/padding
1002
+ */
1003
+ pb: [{
1004
+ pb: [padding]
1005
+ }],
1006
+ /**
1007
+ * Padding Left
1008
+ * @see https://tailwindcss.com/docs/padding
1009
+ */
1010
+ pl: [{
1011
+ pl: [padding]
1012
+ }],
1013
+ /**
1014
+ * Margin
1015
+ * @see https://tailwindcss.com/docs/margin
1016
+ */
1017
+ m: [{
1018
+ m: [margin]
1019
+ }],
1020
+ /**
1021
+ * Margin X
1022
+ * @see https://tailwindcss.com/docs/margin
1023
+ */
1024
+ mx: [{
1025
+ mx: [margin]
1026
+ }],
1027
+ /**
1028
+ * Margin Y
1029
+ * @see https://tailwindcss.com/docs/margin
1030
+ */
1031
+ my: [{
1032
+ my: [margin]
1033
+ }],
1034
+ /**
1035
+ * Margin Start
1036
+ * @see https://tailwindcss.com/docs/margin
1037
+ */
1038
+ ms: [{
1039
+ ms: [margin]
1040
+ }],
1041
+ /**
1042
+ * Margin End
1043
+ * @see https://tailwindcss.com/docs/margin
1044
+ */
1045
+ me: [{
1046
+ me: [margin]
1047
+ }],
1048
+ /**
1049
+ * Margin Top
1050
+ * @see https://tailwindcss.com/docs/margin
1051
+ */
1052
+ mt: [{
1053
+ mt: [margin]
1054
+ }],
1055
+ /**
1056
+ * Margin Right
1057
+ * @see https://tailwindcss.com/docs/margin
1058
+ */
1059
+ mr: [{
1060
+ mr: [margin]
1061
+ }],
1062
+ /**
1063
+ * Margin Bottom
1064
+ * @see https://tailwindcss.com/docs/margin
1065
+ */
1066
+ mb: [{
1067
+ mb: [margin]
1068
+ }],
1069
+ /**
1070
+ * Margin Left
1071
+ * @see https://tailwindcss.com/docs/margin
1072
+ */
1073
+ ml: [{
1074
+ ml: [margin]
1075
+ }],
1076
+ /**
1077
+ * Space Between X
1078
+ * @see https://tailwindcss.com/docs/space
1079
+ */
1080
+ "space-x": [{
1081
+ "space-x": [space]
1082
+ }],
1083
+ /**
1084
+ * Space Between X Reverse
1085
+ * @see https://tailwindcss.com/docs/space
1086
+ */
1087
+ "space-x-reverse": ["space-x-reverse"],
1088
+ /**
1089
+ * Space Between Y
1090
+ * @see https://tailwindcss.com/docs/space
1091
+ */
1092
+ "space-y": [{
1093
+ "space-y": [space]
1094
+ }],
1095
+ /**
1096
+ * Space Between Y Reverse
1097
+ * @see https://tailwindcss.com/docs/space
1098
+ */
1099
+ "space-y-reverse": ["space-y-reverse"],
1100
+ // Sizing
1101
+ /**
1102
+ * Width
1103
+ * @see https://tailwindcss.com/docs/width
1104
+ */
1105
+ w: [{
1106
+ w: ["auto", "min", "max", "fit", "svw", "lvw", "dvw", isArbitraryValue, spacing]
1107
+ }],
1108
+ /**
1109
+ * Min-Width
1110
+ * @see https://tailwindcss.com/docs/min-width
1111
+ */
1112
+ "min-w": [{
1113
+ "min-w": [isArbitraryValue, spacing, "min", "max", "fit"]
1114
+ }],
1115
+ /**
1116
+ * Max-Width
1117
+ * @see https://tailwindcss.com/docs/max-width
1118
+ */
1119
+ "max-w": [{
1120
+ "max-w": [isArbitraryValue, spacing, "none", "full", "min", "max", "fit", "prose", {
1121
+ screen: [isTshirtSize]
1122
+ }, isTshirtSize]
1123
+ }],
1124
+ /**
1125
+ * Height
1126
+ * @see https://tailwindcss.com/docs/height
1127
+ */
1128
+ h: [{
1129
+ h: [isArbitraryValue, spacing, "auto", "min", "max", "fit", "svh", "lvh", "dvh"]
1130
+ }],
1131
+ /**
1132
+ * Min-Height
1133
+ * @see https://tailwindcss.com/docs/min-height
1134
+ */
1135
+ "min-h": [{
1136
+ "min-h": [isArbitraryValue, spacing, "min", "max", "fit", "svh", "lvh", "dvh"]
1137
+ }],
1138
+ /**
1139
+ * Max-Height
1140
+ * @see https://tailwindcss.com/docs/max-height
1141
+ */
1142
+ "max-h": [{
1143
+ "max-h": [isArbitraryValue, spacing, "min", "max", "fit", "svh", "lvh", "dvh"]
1144
+ }],
1145
+ /**
1146
+ * Size
1147
+ * @see https://tailwindcss.com/docs/size
1148
+ */
1149
+ size: [{
1150
+ size: [isArbitraryValue, spacing, "auto", "min", "max", "fit"]
1151
+ }],
1152
+ // Typography
1153
+ /**
1154
+ * Font Size
1155
+ * @see https://tailwindcss.com/docs/font-size
1156
+ */
1157
+ "font-size": [{
1158
+ text: ["base", isTshirtSize, isArbitraryLength]
1159
+ }],
1160
+ /**
1161
+ * Font Smoothing
1162
+ * @see https://tailwindcss.com/docs/font-smoothing
1163
+ */
1164
+ "font-smoothing": ["antialiased", "subpixel-antialiased"],
1165
+ /**
1166
+ * Font Style
1167
+ * @see https://tailwindcss.com/docs/font-style
1168
+ */
1169
+ "font-style": ["italic", "not-italic"],
1170
+ /**
1171
+ * Font Weight
1172
+ * @see https://tailwindcss.com/docs/font-weight
1173
+ */
1174
+ "font-weight": [{
1175
+ font: ["thin", "extralight", "light", "normal", "medium", "semibold", "bold", "extrabold", "black", isArbitraryNumber]
1176
+ }],
1177
+ /**
1178
+ * Font Family
1179
+ * @see https://tailwindcss.com/docs/font-family
1180
+ */
1181
+ "font-family": [{
1182
+ font: [isAny]
1183
+ }],
1184
+ /**
1185
+ * Font Variant Numeric
1186
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1187
+ */
1188
+ "fvn-normal": ["normal-nums"],
1189
+ /**
1190
+ * Font Variant Numeric
1191
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1192
+ */
1193
+ "fvn-ordinal": ["ordinal"],
1194
+ /**
1195
+ * Font Variant Numeric
1196
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1197
+ */
1198
+ "fvn-slashed-zero": ["slashed-zero"],
1199
+ /**
1200
+ * Font Variant Numeric
1201
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1202
+ */
1203
+ "fvn-figure": ["lining-nums", "oldstyle-nums"],
1204
+ /**
1205
+ * Font Variant Numeric
1206
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1207
+ */
1208
+ "fvn-spacing": ["proportional-nums", "tabular-nums"],
1209
+ /**
1210
+ * Font Variant Numeric
1211
+ * @see https://tailwindcss.com/docs/font-variant-numeric
1212
+ */
1213
+ "fvn-fraction": ["diagonal-fractions", "stacked-fractions"],
1214
+ /**
1215
+ * Letter Spacing
1216
+ * @see https://tailwindcss.com/docs/letter-spacing
1217
+ */
1218
+ tracking: [{
1219
+ tracking: ["tighter", "tight", "normal", "wide", "wider", "widest", isArbitraryValue]
1220
+ }],
1221
+ /**
1222
+ * Line Clamp
1223
+ * @see https://tailwindcss.com/docs/line-clamp
1224
+ */
1225
+ "line-clamp": [{
1226
+ "line-clamp": ["none", isNumber, isArbitraryNumber]
1227
+ }],
1228
+ /**
1229
+ * Line Height
1230
+ * @see https://tailwindcss.com/docs/line-height
1231
+ */
1232
+ leading: [{
1233
+ leading: ["none", "tight", "snug", "normal", "relaxed", "loose", isLength, isArbitraryValue]
1234
+ }],
1235
+ /**
1236
+ * List Style Image
1237
+ * @see https://tailwindcss.com/docs/list-style-image
1238
+ */
1239
+ "list-image": [{
1240
+ "list-image": ["none", isArbitraryValue]
1241
+ }],
1242
+ /**
1243
+ * List Style Type
1244
+ * @see https://tailwindcss.com/docs/list-style-type
1245
+ */
1246
+ "list-style-type": [{
1247
+ list: ["none", "disc", "decimal", isArbitraryValue]
1248
+ }],
1249
+ /**
1250
+ * List Style Position
1251
+ * @see https://tailwindcss.com/docs/list-style-position
1252
+ */
1253
+ "list-style-position": [{
1254
+ list: ["inside", "outside"]
1255
+ }],
1256
+ /**
1257
+ * Placeholder Color
1258
+ * @deprecated since Tailwind CSS v3.0.0
1259
+ * @see https://tailwindcss.com/docs/placeholder-color
1260
+ */
1261
+ "placeholder-color": [{
1262
+ placeholder: [colors]
1263
+ }],
1264
+ /**
1265
+ * Placeholder Opacity
1266
+ * @see https://tailwindcss.com/docs/placeholder-opacity
1267
+ */
1268
+ "placeholder-opacity": [{
1269
+ "placeholder-opacity": [opacity]
1270
+ }],
1271
+ /**
1272
+ * Text Alignment
1273
+ * @see https://tailwindcss.com/docs/text-align
1274
+ */
1275
+ "text-alignment": [{
1276
+ text: ["left", "center", "right", "justify", "start", "end"]
1277
+ }],
1278
+ /**
1279
+ * Text Color
1280
+ * @see https://tailwindcss.com/docs/text-color
1281
+ */
1282
+ "text-color": [{
1283
+ text: [colors]
1284
+ }],
1285
+ /**
1286
+ * Text Opacity
1287
+ * @see https://tailwindcss.com/docs/text-opacity
1288
+ */
1289
+ "text-opacity": [{
1290
+ "text-opacity": [opacity]
1291
+ }],
1292
+ /**
1293
+ * Text Decoration
1294
+ * @see https://tailwindcss.com/docs/text-decoration
1295
+ */
1296
+ "text-decoration": ["underline", "overline", "line-through", "no-underline"],
1297
+ /**
1298
+ * Text Decoration Style
1299
+ * @see https://tailwindcss.com/docs/text-decoration-style
1300
+ */
1301
+ "text-decoration-style": [{
1302
+ decoration: [...getLineStyles(), "wavy"]
1303
+ }],
1304
+ /**
1305
+ * Text Decoration Thickness
1306
+ * @see https://tailwindcss.com/docs/text-decoration-thickness
1307
+ */
1308
+ "text-decoration-thickness": [{
1309
+ decoration: ["auto", "from-font", isLength, isArbitraryLength]
1310
+ }],
1311
+ /**
1312
+ * Text Underline Offset
1313
+ * @see https://tailwindcss.com/docs/text-underline-offset
1314
+ */
1315
+ "underline-offset": [{
1316
+ "underline-offset": ["auto", isLength, isArbitraryValue]
1317
+ }],
1318
+ /**
1319
+ * Text Decoration Color
1320
+ * @see https://tailwindcss.com/docs/text-decoration-color
1321
+ */
1322
+ "text-decoration-color": [{
1323
+ decoration: [colors]
1324
+ }],
1325
+ /**
1326
+ * Text Transform
1327
+ * @see https://tailwindcss.com/docs/text-transform
1328
+ */
1329
+ "text-transform": ["uppercase", "lowercase", "capitalize", "normal-case"],
1330
+ /**
1331
+ * Text Overflow
1332
+ * @see https://tailwindcss.com/docs/text-overflow
1333
+ */
1334
+ "text-overflow": ["truncate", "text-ellipsis", "text-clip"],
1335
+ /**
1336
+ * Text Wrap
1337
+ * @see https://tailwindcss.com/docs/text-wrap
1338
+ */
1339
+ "text-wrap": [{
1340
+ text: ["wrap", "nowrap", "balance", "pretty"]
1341
+ }],
1342
+ /**
1343
+ * Text Indent
1344
+ * @see https://tailwindcss.com/docs/text-indent
1345
+ */
1346
+ indent: [{
1347
+ indent: getSpacingWithArbitrary()
1348
+ }],
1349
+ /**
1350
+ * Vertical Alignment
1351
+ * @see https://tailwindcss.com/docs/vertical-align
1352
+ */
1353
+ "vertical-align": [{
1354
+ align: ["baseline", "top", "middle", "bottom", "text-top", "text-bottom", "sub", "super", isArbitraryValue]
1355
+ }],
1356
+ /**
1357
+ * Whitespace
1358
+ * @see https://tailwindcss.com/docs/whitespace
1359
+ */
1360
+ whitespace: [{
1361
+ whitespace: ["normal", "nowrap", "pre", "pre-line", "pre-wrap", "break-spaces"]
1362
+ }],
1363
+ /**
1364
+ * Word Break
1365
+ * @see https://tailwindcss.com/docs/word-break
1366
+ */
1367
+ break: [{
1368
+ break: ["normal", "words", "all", "keep"]
1369
+ }],
1370
+ /**
1371
+ * Hyphens
1372
+ * @see https://tailwindcss.com/docs/hyphens
1373
+ */
1374
+ hyphens: [{
1375
+ hyphens: ["none", "manual", "auto"]
1376
+ }],
1377
+ /**
1378
+ * Content
1379
+ * @see https://tailwindcss.com/docs/content
1380
+ */
1381
+ content: [{
1382
+ content: ["none", isArbitraryValue]
1383
+ }],
1384
+ // Backgrounds
1385
+ /**
1386
+ * Background Attachment
1387
+ * @see https://tailwindcss.com/docs/background-attachment
1388
+ */
1389
+ "bg-attachment": [{
1390
+ bg: ["fixed", "local", "scroll"]
1391
+ }],
1392
+ /**
1393
+ * Background Clip
1394
+ * @see https://tailwindcss.com/docs/background-clip
1395
+ */
1396
+ "bg-clip": [{
1397
+ "bg-clip": ["border", "padding", "content", "text"]
1398
+ }],
1399
+ /**
1400
+ * Background Opacity
1401
+ * @deprecated since Tailwind CSS v3.0.0
1402
+ * @see https://tailwindcss.com/docs/background-opacity
1403
+ */
1404
+ "bg-opacity": [{
1405
+ "bg-opacity": [opacity]
1406
+ }],
1407
+ /**
1408
+ * Background Origin
1409
+ * @see https://tailwindcss.com/docs/background-origin
1410
+ */
1411
+ "bg-origin": [{
1412
+ "bg-origin": ["border", "padding", "content"]
1413
+ }],
1414
+ /**
1415
+ * Background Position
1416
+ * @see https://tailwindcss.com/docs/background-position
1417
+ */
1418
+ "bg-position": [{
1419
+ bg: [...getPositions(), isArbitraryPosition]
1420
+ }],
1421
+ /**
1422
+ * Background Repeat
1423
+ * @see https://tailwindcss.com/docs/background-repeat
1424
+ */
1425
+ "bg-repeat": [{
1426
+ bg: ["no-repeat", {
1427
+ repeat: ["", "x", "y", "round", "space"]
1428
+ }]
1429
+ }],
1430
+ /**
1431
+ * Background Size
1432
+ * @see https://tailwindcss.com/docs/background-size
1433
+ */
1434
+ "bg-size": [{
1435
+ bg: ["auto", "cover", "contain", isArbitrarySize]
1436
+ }],
1437
+ /**
1438
+ * Background Image
1439
+ * @see https://tailwindcss.com/docs/background-image
1440
+ */
1441
+ "bg-image": [{
1442
+ bg: ["none", {
1443
+ "gradient-to": ["t", "tr", "r", "br", "b", "bl", "l", "tl"]
1444
+ }, isArbitraryImage]
1445
+ }],
1446
+ /**
1447
+ * Background Color
1448
+ * @see https://tailwindcss.com/docs/background-color
1449
+ */
1450
+ "bg-color": [{
1451
+ bg: [colors]
1452
+ }],
1453
+ /**
1454
+ * Gradient Color Stops From Position
1455
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1456
+ */
1457
+ "gradient-from-pos": [{
1458
+ from: [gradientColorStopPositions]
1459
+ }],
1460
+ /**
1461
+ * Gradient Color Stops Via Position
1462
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1463
+ */
1464
+ "gradient-via-pos": [{
1465
+ via: [gradientColorStopPositions]
1466
+ }],
1467
+ /**
1468
+ * Gradient Color Stops To Position
1469
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1470
+ */
1471
+ "gradient-to-pos": [{
1472
+ to: [gradientColorStopPositions]
1473
+ }],
1474
+ /**
1475
+ * Gradient Color Stops From
1476
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1477
+ */
1478
+ "gradient-from": [{
1479
+ from: [gradientColorStops]
1480
+ }],
1481
+ /**
1482
+ * Gradient Color Stops Via
1483
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1484
+ */
1485
+ "gradient-via": [{
1486
+ via: [gradientColorStops]
1487
+ }],
1488
+ /**
1489
+ * Gradient Color Stops To
1490
+ * @see https://tailwindcss.com/docs/gradient-color-stops
1491
+ */
1492
+ "gradient-to": [{
1493
+ to: [gradientColorStops]
1494
+ }],
1495
+ // Borders
1496
+ /**
1497
+ * Border Radius
1498
+ * @see https://tailwindcss.com/docs/border-radius
1499
+ */
1500
+ rounded: [{
1501
+ rounded: [borderRadius]
1502
+ }],
1503
+ /**
1504
+ * Border Radius Start
1505
+ * @see https://tailwindcss.com/docs/border-radius
1506
+ */
1507
+ "rounded-s": [{
1508
+ "rounded-s": [borderRadius]
1509
+ }],
1510
+ /**
1511
+ * Border Radius End
1512
+ * @see https://tailwindcss.com/docs/border-radius
1513
+ */
1514
+ "rounded-e": [{
1515
+ "rounded-e": [borderRadius]
1516
+ }],
1517
+ /**
1518
+ * Border Radius Top
1519
+ * @see https://tailwindcss.com/docs/border-radius
1520
+ */
1521
+ "rounded-t": [{
1522
+ "rounded-t": [borderRadius]
1523
+ }],
1524
+ /**
1525
+ * Border Radius Right
1526
+ * @see https://tailwindcss.com/docs/border-radius
1527
+ */
1528
+ "rounded-r": [{
1529
+ "rounded-r": [borderRadius]
1530
+ }],
1531
+ /**
1532
+ * Border Radius Bottom
1533
+ * @see https://tailwindcss.com/docs/border-radius
1534
+ */
1535
+ "rounded-b": [{
1536
+ "rounded-b": [borderRadius]
1537
+ }],
1538
+ /**
1539
+ * Border Radius Left
1540
+ * @see https://tailwindcss.com/docs/border-radius
1541
+ */
1542
+ "rounded-l": [{
1543
+ "rounded-l": [borderRadius]
1544
+ }],
1545
+ /**
1546
+ * Border Radius Start Start
1547
+ * @see https://tailwindcss.com/docs/border-radius
1548
+ */
1549
+ "rounded-ss": [{
1550
+ "rounded-ss": [borderRadius]
1551
+ }],
1552
+ /**
1553
+ * Border Radius Start End
1554
+ * @see https://tailwindcss.com/docs/border-radius
1555
+ */
1556
+ "rounded-se": [{
1557
+ "rounded-se": [borderRadius]
1558
+ }],
1559
+ /**
1560
+ * Border Radius End End
1561
+ * @see https://tailwindcss.com/docs/border-radius
1562
+ */
1563
+ "rounded-ee": [{
1564
+ "rounded-ee": [borderRadius]
1565
+ }],
1566
+ /**
1567
+ * Border Radius End Start
1568
+ * @see https://tailwindcss.com/docs/border-radius
1569
+ */
1570
+ "rounded-es": [{
1571
+ "rounded-es": [borderRadius]
1572
+ }],
1573
+ /**
1574
+ * Border Radius Top Left
1575
+ * @see https://tailwindcss.com/docs/border-radius
1576
+ */
1577
+ "rounded-tl": [{
1578
+ "rounded-tl": [borderRadius]
1579
+ }],
1580
+ /**
1581
+ * Border Radius Top Right
1582
+ * @see https://tailwindcss.com/docs/border-radius
1583
+ */
1584
+ "rounded-tr": [{
1585
+ "rounded-tr": [borderRadius]
1586
+ }],
1587
+ /**
1588
+ * Border Radius Bottom Right
1589
+ * @see https://tailwindcss.com/docs/border-radius
1590
+ */
1591
+ "rounded-br": [{
1592
+ "rounded-br": [borderRadius]
1593
+ }],
1594
+ /**
1595
+ * Border Radius Bottom Left
1596
+ * @see https://tailwindcss.com/docs/border-radius
1597
+ */
1598
+ "rounded-bl": [{
1599
+ "rounded-bl": [borderRadius]
1600
+ }],
1601
+ /**
1602
+ * Border Width
1603
+ * @see https://tailwindcss.com/docs/border-width
1604
+ */
1605
+ "border-w": [{
1606
+ border: [borderWidth]
1607
+ }],
1608
+ /**
1609
+ * Border Width X
1610
+ * @see https://tailwindcss.com/docs/border-width
1611
+ */
1612
+ "border-w-x": [{
1613
+ "border-x": [borderWidth]
1614
+ }],
1615
+ /**
1616
+ * Border Width Y
1617
+ * @see https://tailwindcss.com/docs/border-width
1618
+ */
1619
+ "border-w-y": [{
1620
+ "border-y": [borderWidth]
1621
+ }],
1622
+ /**
1623
+ * Border Width Start
1624
+ * @see https://tailwindcss.com/docs/border-width
1625
+ */
1626
+ "border-w-s": [{
1627
+ "border-s": [borderWidth]
1628
+ }],
1629
+ /**
1630
+ * Border Width End
1631
+ * @see https://tailwindcss.com/docs/border-width
1632
+ */
1633
+ "border-w-e": [{
1634
+ "border-e": [borderWidth]
1635
+ }],
1636
+ /**
1637
+ * Border Width Top
1638
+ * @see https://tailwindcss.com/docs/border-width
1639
+ */
1640
+ "border-w-t": [{
1641
+ "border-t": [borderWidth]
1642
+ }],
1643
+ /**
1644
+ * Border Width Right
1645
+ * @see https://tailwindcss.com/docs/border-width
1646
+ */
1647
+ "border-w-r": [{
1648
+ "border-r": [borderWidth]
1649
+ }],
1650
+ /**
1651
+ * Border Width Bottom
1652
+ * @see https://tailwindcss.com/docs/border-width
1653
+ */
1654
+ "border-w-b": [{
1655
+ "border-b": [borderWidth]
1656
+ }],
1657
+ /**
1658
+ * Border Width Left
1659
+ * @see https://tailwindcss.com/docs/border-width
1660
+ */
1661
+ "border-w-l": [{
1662
+ "border-l": [borderWidth]
1663
+ }],
1664
+ /**
1665
+ * Border Opacity
1666
+ * @see https://tailwindcss.com/docs/border-opacity
1667
+ */
1668
+ "border-opacity": [{
1669
+ "border-opacity": [opacity]
1670
+ }],
1671
+ /**
1672
+ * Border Style
1673
+ * @see https://tailwindcss.com/docs/border-style
1674
+ */
1675
+ "border-style": [{
1676
+ border: [...getLineStyles(), "hidden"]
1677
+ }],
1678
+ /**
1679
+ * Divide Width X
1680
+ * @see https://tailwindcss.com/docs/divide-width
1681
+ */
1682
+ "divide-x": [{
1683
+ "divide-x": [borderWidth]
1684
+ }],
1685
+ /**
1686
+ * Divide Width X Reverse
1687
+ * @see https://tailwindcss.com/docs/divide-width
1688
+ */
1689
+ "divide-x-reverse": ["divide-x-reverse"],
1690
+ /**
1691
+ * Divide Width Y
1692
+ * @see https://tailwindcss.com/docs/divide-width
1693
+ */
1694
+ "divide-y": [{
1695
+ "divide-y": [borderWidth]
1696
+ }],
1697
+ /**
1698
+ * Divide Width Y Reverse
1699
+ * @see https://tailwindcss.com/docs/divide-width
1700
+ */
1701
+ "divide-y-reverse": ["divide-y-reverse"],
1702
+ /**
1703
+ * Divide Opacity
1704
+ * @see https://tailwindcss.com/docs/divide-opacity
1705
+ */
1706
+ "divide-opacity": [{
1707
+ "divide-opacity": [opacity]
1708
+ }],
1709
+ /**
1710
+ * Divide Style
1711
+ * @see https://tailwindcss.com/docs/divide-style
1712
+ */
1713
+ "divide-style": [{
1714
+ divide: getLineStyles()
1715
+ }],
1716
+ /**
1717
+ * Border Color
1718
+ * @see https://tailwindcss.com/docs/border-color
1719
+ */
1720
+ "border-color": [{
1721
+ border: [borderColor]
1722
+ }],
1723
+ /**
1724
+ * Border Color X
1725
+ * @see https://tailwindcss.com/docs/border-color
1726
+ */
1727
+ "border-color-x": [{
1728
+ "border-x": [borderColor]
1729
+ }],
1730
+ /**
1731
+ * Border Color Y
1732
+ * @see https://tailwindcss.com/docs/border-color
1733
+ */
1734
+ "border-color-y": [{
1735
+ "border-y": [borderColor]
1736
+ }],
1737
+ /**
1738
+ * Border Color S
1739
+ * @see https://tailwindcss.com/docs/border-color
1740
+ */
1741
+ "border-color-s": [{
1742
+ "border-s": [borderColor]
1743
+ }],
1744
+ /**
1745
+ * Border Color E
1746
+ * @see https://tailwindcss.com/docs/border-color
1747
+ */
1748
+ "border-color-e": [{
1749
+ "border-e": [borderColor]
1750
+ }],
1751
+ /**
1752
+ * Border Color Top
1753
+ * @see https://tailwindcss.com/docs/border-color
1754
+ */
1755
+ "border-color-t": [{
1756
+ "border-t": [borderColor]
1757
+ }],
1758
+ /**
1759
+ * Border Color Right
1760
+ * @see https://tailwindcss.com/docs/border-color
1761
+ */
1762
+ "border-color-r": [{
1763
+ "border-r": [borderColor]
1764
+ }],
1765
+ /**
1766
+ * Border Color Bottom
1767
+ * @see https://tailwindcss.com/docs/border-color
1768
+ */
1769
+ "border-color-b": [{
1770
+ "border-b": [borderColor]
1771
+ }],
1772
+ /**
1773
+ * Border Color Left
1774
+ * @see https://tailwindcss.com/docs/border-color
1775
+ */
1776
+ "border-color-l": [{
1777
+ "border-l": [borderColor]
1778
+ }],
1779
+ /**
1780
+ * Divide Color
1781
+ * @see https://tailwindcss.com/docs/divide-color
1782
+ */
1783
+ "divide-color": [{
1784
+ divide: [borderColor]
1785
+ }],
1786
+ /**
1787
+ * Outline Style
1788
+ * @see https://tailwindcss.com/docs/outline-style
1789
+ */
1790
+ "outline-style": [{
1791
+ outline: ["", ...getLineStyles()]
1792
+ }],
1793
+ /**
1794
+ * Outline Offset
1795
+ * @see https://tailwindcss.com/docs/outline-offset
1796
+ */
1797
+ "outline-offset": [{
1798
+ "outline-offset": [isLength, isArbitraryValue]
1799
+ }],
1800
+ /**
1801
+ * Outline Width
1802
+ * @see https://tailwindcss.com/docs/outline-width
1803
+ */
1804
+ "outline-w": [{
1805
+ outline: [isLength, isArbitraryLength]
1806
+ }],
1807
+ /**
1808
+ * Outline Color
1809
+ * @see https://tailwindcss.com/docs/outline-color
1810
+ */
1811
+ "outline-color": [{
1812
+ outline: [colors]
1813
+ }],
1814
+ /**
1815
+ * Ring Width
1816
+ * @see https://tailwindcss.com/docs/ring-width
1817
+ */
1818
+ "ring-w": [{
1819
+ ring: getLengthWithEmptyAndArbitrary()
1820
+ }],
1821
+ /**
1822
+ * Ring Width Inset
1823
+ * @see https://tailwindcss.com/docs/ring-width
1824
+ */
1825
+ "ring-w-inset": ["ring-inset"],
1826
+ /**
1827
+ * Ring Color
1828
+ * @see https://tailwindcss.com/docs/ring-color
1829
+ */
1830
+ "ring-color": [{
1831
+ ring: [colors]
1832
+ }],
1833
+ /**
1834
+ * Ring Opacity
1835
+ * @see https://tailwindcss.com/docs/ring-opacity
1836
+ */
1837
+ "ring-opacity": [{
1838
+ "ring-opacity": [opacity]
1839
+ }],
1840
+ /**
1841
+ * Ring Offset Width
1842
+ * @see https://tailwindcss.com/docs/ring-offset-width
1843
+ */
1844
+ "ring-offset-w": [{
1845
+ "ring-offset": [isLength, isArbitraryLength]
1846
+ }],
1847
+ /**
1848
+ * Ring Offset Color
1849
+ * @see https://tailwindcss.com/docs/ring-offset-color
1850
+ */
1851
+ "ring-offset-color": [{
1852
+ "ring-offset": [colors]
1853
+ }],
1854
+ // Effects
1855
+ /**
1856
+ * Box Shadow
1857
+ * @see https://tailwindcss.com/docs/box-shadow
1858
+ */
1859
+ shadow: [{
1860
+ shadow: ["", "inner", "none", isTshirtSize, isArbitraryShadow]
1861
+ }],
1862
+ /**
1863
+ * Box Shadow Color
1864
+ * @see https://tailwindcss.com/docs/box-shadow-color
1865
+ */
1866
+ "shadow-color": [{
1867
+ shadow: [isAny]
1868
+ }],
1869
+ /**
1870
+ * Opacity
1871
+ * @see https://tailwindcss.com/docs/opacity
1872
+ */
1873
+ opacity: [{
1874
+ opacity: [opacity]
1875
+ }],
1876
+ /**
1877
+ * Mix Blend Mode
1878
+ * @see https://tailwindcss.com/docs/mix-blend-mode
1879
+ */
1880
+ "mix-blend": [{
1881
+ "mix-blend": [...getBlendModes(), "plus-lighter", "plus-darker"]
1882
+ }],
1883
+ /**
1884
+ * Background Blend Mode
1885
+ * @see https://tailwindcss.com/docs/background-blend-mode
1886
+ */
1887
+ "bg-blend": [{
1888
+ "bg-blend": getBlendModes()
1889
+ }],
1890
+ // Filters
1891
+ /**
1892
+ * Filter
1893
+ * @deprecated since Tailwind CSS v3.0.0
1894
+ * @see https://tailwindcss.com/docs/filter
1895
+ */
1896
+ filter: [{
1897
+ filter: ["", "none"]
1898
+ }],
1899
+ /**
1900
+ * Blur
1901
+ * @see https://tailwindcss.com/docs/blur
1902
+ */
1903
+ blur: [{
1904
+ blur: [blur]
1905
+ }],
1906
+ /**
1907
+ * Brightness
1908
+ * @see https://tailwindcss.com/docs/brightness
1909
+ */
1910
+ brightness: [{
1911
+ brightness: [brightness]
1912
+ }],
1913
+ /**
1914
+ * Contrast
1915
+ * @see https://tailwindcss.com/docs/contrast
1916
+ */
1917
+ contrast: [{
1918
+ contrast: [contrast]
1919
+ }],
1920
+ /**
1921
+ * Drop Shadow
1922
+ * @see https://tailwindcss.com/docs/drop-shadow
1923
+ */
1924
+ "drop-shadow": [{
1925
+ "drop-shadow": ["", "none", isTshirtSize, isArbitraryValue]
1926
+ }],
1927
+ /**
1928
+ * Grayscale
1929
+ * @see https://tailwindcss.com/docs/grayscale
1930
+ */
1931
+ grayscale: [{
1932
+ grayscale: [grayscale]
1933
+ }],
1934
+ /**
1935
+ * Hue Rotate
1936
+ * @see https://tailwindcss.com/docs/hue-rotate
1937
+ */
1938
+ "hue-rotate": [{
1939
+ "hue-rotate": [hueRotate]
1940
+ }],
1941
+ /**
1942
+ * Invert
1943
+ * @see https://tailwindcss.com/docs/invert
1944
+ */
1945
+ invert: [{
1946
+ invert: [invert]
1947
+ }],
1948
+ /**
1949
+ * Saturate
1950
+ * @see https://tailwindcss.com/docs/saturate
1951
+ */
1952
+ saturate: [{
1953
+ saturate: [saturate]
1954
+ }],
1955
+ /**
1956
+ * Sepia
1957
+ * @see https://tailwindcss.com/docs/sepia
1958
+ */
1959
+ sepia: [{
1960
+ sepia: [sepia]
1961
+ }],
1962
+ /**
1963
+ * Backdrop Filter
1964
+ * @deprecated since Tailwind CSS v3.0.0
1965
+ * @see https://tailwindcss.com/docs/backdrop-filter
1966
+ */
1967
+ "backdrop-filter": [{
1968
+ "backdrop-filter": ["", "none"]
1969
+ }],
1970
+ /**
1971
+ * Backdrop Blur
1972
+ * @see https://tailwindcss.com/docs/backdrop-blur
1973
+ */
1974
+ "backdrop-blur": [{
1975
+ "backdrop-blur": [blur]
1976
+ }],
1977
+ /**
1978
+ * Backdrop Brightness
1979
+ * @see https://tailwindcss.com/docs/backdrop-brightness
1980
+ */
1981
+ "backdrop-brightness": [{
1982
+ "backdrop-brightness": [brightness]
1983
+ }],
1984
+ /**
1985
+ * Backdrop Contrast
1986
+ * @see https://tailwindcss.com/docs/backdrop-contrast
1987
+ */
1988
+ "backdrop-contrast": [{
1989
+ "backdrop-contrast": [contrast]
1990
+ }],
1991
+ /**
1992
+ * Backdrop Grayscale
1993
+ * @see https://tailwindcss.com/docs/backdrop-grayscale
1994
+ */
1995
+ "backdrop-grayscale": [{
1996
+ "backdrop-grayscale": [grayscale]
1997
+ }],
1998
+ /**
1999
+ * Backdrop Hue Rotate
2000
+ * @see https://tailwindcss.com/docs/backdrop-hue-rotate
2001
+ */
2002
+ "backdrop-hue-rotate": [{
2003
+ "backdrop-hue-rotate": [hueRotate]
2004
+ }],
2005
+ /**
2006
+ * Backdrop Invert
2007
+ * @see https://tailwindcss.com/docs/backdrop-invert
2008
+ */
2009
+ "backdrop-invert": [{
2010
+ "backdrop-invert": [invert]
2011
+ }],
2012
+ /**
2013
+ * Backdrop Opacity
2014
+ * @see https://tailwindcss.com/docs/backdrop-opacity
2015
+ */
2016
+ "backdrop-opacity": [{
2017
+ "backdrop-opacity": [opacity]
2018
+ }],
2019
+ /**
2020
+ * Backdrop Saturate
2021
+ * @see https://tailwindcss.com/docs/backdrop-saturate
2022
+ */
2023
+ "backdrop-saturate": [{
2024
+ "backdrop-saturate": [saturate]
2025
+ }],
2026
+ /**
2027
+ * Backdrop Sepia
2028
+ * @see https://tailwindcss.com/docs/backdrop-sepia
2029
+ */
2030
+ "backdrop-sepia": [{
2031
+ "backdrop-sepia": [sepia]
2032
+ }],
2033
+ // Tables
2034
+ /**
2035
+ * Border Collapse
2036
+ * @see https://tailwindcss.com/docs/border-collapse
2037
+ */
2038
+ "border-collapse": [{
2039
+ border: ["collapse", "separate"]
2040
+ }],
2041
+ /**
2042
+ * Border Spacing
2043
+ * @see https://tailwindcss.com/docs/border-spacing
2044
+ */
2045
+ "border-spacing": [{
2046
+ "border-spacing": [borderSpacing]
2047
+ }],
2048
+ /**
2049
+ * Border Spacing X
2050
+ * @see https://tailwindcss.com/docs/border-spacing
2051
+ */
2052
+ "border-spacing-x": [{
2053
+ "border-spacing-x": [borderSpacing]
2054
+ }],
2055
+ /**
2056
+ * Border Spacing Y
2057
+ * @see https://tailwindcss.com/docs/border-spacing
2058
+ */
2059
+ "border-spacing-y": [{
2060
+ "border-spacing-y": [borderSpacing]
2061
+ }],
2062
+ /**
2063
+ * Table Layout
2064
+ * @see https://tailwindcss.com/docs/table-layout
2065
+ */
2066
+ "table-layout": [{
2067
+ table: ["auto", "fixed"]
2068
+ }],
2069
+ /**
2070
+ * Caption Side
2071
+ * @see https://tailwindcss.com/docs/caption-side
2072
+ */
2073
+ caption: [{
2074
+ caption: ["top", "bottom"]
2075
+ }],
2076
+ // Transitions and Animation
2077
+ /**
2078
+ * Tranisition Property
2079
+ * @see https://tailwindcss.com/docs/transition-property
2080
+ */
2081
+ transition: [{
2082
+ transition: ["none", "all", "", "colors", "opacity", "shadow", "transform", isArbitraryValue]
2083
+ }],
2084
+ /**
2085
+ * Transition Duration
2086
+ * @see https://tailwindcss.com/docs/transition-duration
2087
+ */
2088
+ duration: [{
2089
+ duration: getNumberAndArbitrary()
2090
+ }],
2091
+ /**
2092
+ * Transition Timing Function
2093
+ * @see https://tailwindcss.com/docs/transition-timing-function
2094
+ */
2095
+ ease: [{
2096
+ ease: ["linear", "in", "out", "in-out", isArbitraryValue]
2097
+ }],
2098
+ /**
2099
+ * Transition Delay
2100
+ * @see https://tailwindcss.com/docs/transition-delay
2101
+ */
2102
+ delay: [{
2103
+ delay: getNumberAndArbitrary()
2104
+ }],
2105
+ /**
2106
+ * Animation
2107
+ * @see https://tailwindcss.com/docs/animation
2108
+ */
2109
+ animate: [{
2110
+ animate: ["none", "spin", "ping", "pulse", "bounce", isArbitraryValue]
2111
+ }],
2112
+ // Transforms
2113
+ /**
2114
+ * Transform
2115
+ * @see https://tailwindcss.com/docs/transform
2116
+ */
2117
+ transform: [{
2118
+ transform: ["", "gpu", "none"]
2119
+ }],
2120
+ /**
2121
+ * Scale
2122
+ * @see https://tailwindcss.com/docs/scale
2123
+ */
2124
+ scale: [{
2125
+ scale: [scale]
2126
+ }],
2127
+ /**
2128
+ * Scale X
2129
+ * @see https://tailwindcss.com/docs/scale
2130
+ */
2131
+ "scale-x": [{
2132
+ "scale-x": [scale]
2133
+ }],
2134
+ /**
2135
+ * Scale Y
2136
+ * @see https://tailwindcss.com/docs/scale
2137
+ */
2138
+ "scale-y": [{
2139
+ "scale-y": [scale]
2140
+ }],
2141
+ /**
2142
+ * Rotate
2143
+ * @see https://tailwindcss.com/docs/rotate
2144
+ */
2145
+ rotate: [{
2146
+ rotate: [isInteger, isArbitraryValue]
2147
+ }],
2148
+ /**
2149
+ * Translate X
2150
+ * @see https://tailwindcss.com/docs/translate
2151
+ */
2152
+ "translate-x": [{
2153
+ "translate-x": [translate]
2154
+ }],
2155
+ /**
2156
+ * Translate Y
2157
+ * @see https://tailwindcss.com/docs/translate
2158
+ */
2159
+ "translate-y": [{
2160
+ "translate-y": [translate]
2161
+ }],
2162
+ /**
2163
+ * Skew X
2164
+ * @see https://tailwindcss.com/docs/skew
2165
+ */
2166
+ "skew-x": [{
2167
+ "skew-x": [skew]
2168
+ }],
2169
+ /**
2170
+ * Skew Y
2171
+ * @see https://tailwindcss.com/docs/skew
2172
+ */
2173
+ "skew-y": [{
2174
+ "skew-y": [skew]
2175
+ }],
2176
+ /**
2177
+ * Transform Origin
2178
+ * @see https://tailwindcss.com/docs/transform-origin
2179
+ */
2180
+ "transform-origin": [{
2181
+ origin: ["center", "top", "top-right", "right", "bottom-right", "bottom", "bottom-left", "left", "top-left", isArbitraryValue]
2182
+ }],
2183
+ // Interactivity
2184
+ /**
2185
+ * Accent Color
2186
+ * @see https://tailwindcss.com/docs/accent-color
2187
+ */
2188
+ accent: [{
2189
+ accent: ["auto", colors]
2190
+ }],
2191
+ /**
2192
+ * Appearance
2193
+ * @see https://tailwindcss.com/docs/appearance
2194
+ */
2195
+ appearance: [{
2196
+ appearance: ["none", "auto"]
2197
+ }],
2198
+ /**
2199
+ * Cursor
2200
+ * @see https://tailwindcss.com/docs/cursor
2201
+ */
2202
+ cursor: [{
2203
+ cursor: ["auto", "default", "pointer", "wait", "text", "move", "help", "not-allowed", "none", "context-menu", "progress", "cell", "crosshair", "vertical-text", "alias", "copy", "no-drop", "grab", "grabbing", "all-scroll", "col-resize", "row-resize", "n-resize", "e-resize", "s-resize", "w-resize", "ne-resize", "nw-resize", "se-resize", "sw-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "zoom-in", "zoom-out", isArbitraryValue]
2204
+ }],
2205
+ /**
2206
+ * Caret Color
2207
+ * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities
2208
+ */
2209
+ "caret-color": [{
2210
+ caret: [colors]
2211
+ }],
2212
+ /**
2213
+ * Pointer Events
2214
+ * @see https://tailwindcss.com/docs/pointer-events
2215
+ */
2216
+ "pointer-events": [{
2217
+ "pointer-events": ["none", "auto"]
2218
+ }],
2219
+ /**
2220
+ * Resize
2221
+ * @see https://tailwindcss.com/docs/resize
2222
+ */
2223
+ resize: [{
2224
+ resize: ["none", "y", "x", ""]
2225
+ }],
2226
+ /**
2227
+ * Scroll Behavior
2228
+ * @see https://tailwindcss.com/docs/scroll-behavior
2229
+ */
2230
+ "scroll-behavior": [{
2231
+ scroll: ["auto", "smooth"]
2232
+ }],
2233
+ /**
2234
+ * Scroll Margin
2235
+ * @see https://tailwindcss.com/docs/scroll-margin
2236
+ */
2237
+ "scroll-m": [{
2238
+ "scroll-m": getSpacingWithArbitrary()
2239
+ }],
2240
+ /**
2241
+ * Scroll Margin X
2242
+ * @see https://tailwindcss.com/docs/scroll-margin
2243
+ */
2244
+ "scroll-mx": [{
2245
+ "scroll-mx": getSpacingWithArbitrary()
2246
+ }],
2247
+ /**
2248
+ * Scroll Margin Y
2249
+ * @see https://tailwindcss.com/docs/scroll-margin
2250
+ */
2251
+ "scroll-my": [{
2252
+ "scroll-my": getSpacingWithArbitrary()
2253
+ }],
2254
+ /**
2255
+ * Scroll Margin Start
2256
+ * @see https://tailwindcss.com/docs/scroll-margin
2257
+ */
2258
+ "scroll-ms": [{
2259
+ "scroll-ms": getSpacingWithArbitrary()
2260
+ }],
2261
+ /**
2262
+ * Scroll Margin End
2263
+ * @see https://tailwindcss.com/docs/scroll-margin
2264
+ */
2265
+ "scroll-me": [{
2266
+ "scroll-me": getSpacingWithArbitrary()
2267
+ }],
2268
+ /**
2269
+ * Scroll Margin Top
2270
+ * @see https://tailwindcss.com/docs/scroll-margin
2271
+ */
2272
+ "scroll-mt": [{
2273
+ "scroll-mt": getSpacingWithArbitrary()
2274
+ }],
2275
+ /**
2276
+ * Scroll Margin Right
2277
+ * @see https://tailwindcss.com/docs/scroll-margin
2278
+ */
2279
+ "scroll-mr": [{
2280
+ "scroll-mr": getSpacingWithArbitrary()
2281
+ }],
2282
+ /**
2283
+ * Scroll Margin Bottom
2284
+ * @see https://tailwindcss.com/docs/scroll-margin
2285
+ */
2286
+ "scroll-mb": [{
2287
+ "scroll-mb": getSpacingWithArbitrary()
2288
+ }],
2289
+ /**
2290
+ * Scroll Margin Left
2291
+ * @see https://tailwindcss.com/docs/scroll-margin
2292
+ */
2293
+ "scroll-ml": [{
2294
+ "scroll-ml": getSpacingWithArbitrary()
2295
+ }],
2296
+ /**
2297
+ * Scroll Padding
2298
+ * @see https://tailwindcss.com/docs/scroll-padding
2299
+ */
2300
+ "scroll-p": [{
2301
+ "scroll-p": getSpacingWithArbitrary()
2302
+ }],
2303
+ /**
2304
+ * Scroll Padding X
2305
+ * @see https://tailwindcss.com/docs/scroll-padding
2306
+ */
2307
+ "scroll-px": [{
2308
+ "scroll-px": getSpacingWithArbitrary()
2309
+ }],
2310
+ /**
2311
+ * Scroll Padding Y
2312
+ * @see https://tailwindcss.com/docs/scroll-padding
2313
+ */
2314
+ "scroll-py": [{
2315
+ "scroll-py": getSpacingWithArbitrary()
2316
+ }],
2317
+ /**
2318
+ * Scroll Padding Start
2319
+ * @see https://tailwindcss.com/docs/scroll-padding
2320
+ */
2321
+ "scroll-ps": [{
2322
+ "scroll-ps": getSpacingWithArbitrary()
2323
+ }],
2324
+ /**
2325
+ * Scroll Padding End
2326
+ * @see https://tailwindcss.com/docs/scroll-padding
2327
+ */
2328
+ "scroll-pe": [{
2329
+ "scroll-pe": getSpacingWithArbitrary()
2330
+ }],
2331
+ /**
2332
+ * Scroll Padding Top
2333
+ * @see https://tailwindcss.com/docs/scroll-padding
2334
+ */
2335
+ "scroll-pt": [{
2336
+ "scroll-pt": getSpacingWithArbitrary()
2337
+ }],
2338
+ /**
2339
+ * Scroll Padding Right
2340
+ * @see https://tailwindcss.com/docs/scroll-padding
2341
+ */
2342
+ "scroll-pr": [{
2343
+ "scroll-pr": getSpacingWithArbitrary()
2344
+ }],
2345
+ /**
2346
+ * Scroll Padding Bottom
2347
+ * @see https://tailwindcss.com/docs/scroll-padding
2348
+ */
2349
+ "scroll-pb": [{
2350
+ "scroll-pb": getSpacingWithArbitrary()
2351
+ }],
2352
+ /**
2353
+ * Scroll Padding Left
2354
+ * @see https://tailwindcss.com/docs/scroll-padding
2355
+ */
2356
+ "scroll-pl": [{
2357
+ "scroll-pl": getSpacingWithArbitrary()
2358
+ }],
2359
+ /**
2360
+ * Scroll Snap Align
2361
+ * @see https://tailwindcss.com/docs/scroll-snap-align
2362
+ */
2363
+ "snap-align": [{
2364
+ snap: ["start", "end", "center", "align-none"]
2365
+ }],
2366
+ /**
2367
+ * Scroll Snap Stop
2368
+ * @see https://tailwindcss.com/docs/scroll-snap-stop
2369
+ */
2370
+ "snap-stop": [{
2371
+ snap: ["normal", "always"]
2372
+ }],
2373
+ /**
2374
+ * Scroll Snap Type
2375
+ * @see https://tailwindcss.com/docs/scroll-snap-type
2376
+ */
2377
+ "snap-type": [{
2378
+ snap: ["none", "x", "y", "both"]
2379
+ }],
2380
+ /**
2381
+ * Scroll Snap Type Strictness
2382
+ * @see https://tailwindcss.com/docs/scroll-snap-type
2383
+ */
2384
+ "snap-strictness": [{
2385
+ snap: ["mandatory", "proximity"]
2386
+ }],
2387
+ /**
2388
+ * Touch Action
2389
+ * @see https://tailwindcss.com/docs/touch-action
2390
+ */
2391
+ touch: [{
2392
+ touch: ["auto", "none", "manipulation"]
2393
+ }],
2394
+ /**
2395
+ * Touch Action X
2396
+ * @see https://tailwindcss.com/docs/touch-action
2397
+ */
2398
+ "touch-x": [{
2399
+ "touch-pan": ["x", "left", "right"]
2400
+ }],
2401
+ /**
2402
+ * Touch Action Y
2403
+ * @see https://tailwindcss.com/docs/touch-action
2404
+ */
2405
+ "touch-y": [{
2406
+ "touch-pan": ["y", "up", "down"]
2407
+ }],
2408
+ /**
2409
+ * Touch Action Pinch Zoom
2410
+ * @see https://tailwindcss.com/docs/touch-action
2411
+ */
2412
+ "touch-pz": ["touch-pinch-zoom"],
2413
+ /**
2414
+ * User Select
2415
+ * @see https://tailwindcss.com/docs/user-select
2416
+ */
2417
+ select: [{
2418
+ select: ["none", "text", "all", "auto"]
2419
+ }],
2420
+ /**
2421
+ * Will Change
2422
+ * @see https://tailwindcss.com/docs/will-change
2423
+ */
2424
+ "will-change": [{
2425
+ "will-change": ["auto", "scroll", "contents", "transform", isArbitraryValue]
2426
+ }],
2427
+ // SVG
2428
+ /**
2429
+ * Fill
2430
+ * @see https://tailwindcss.com/docs/fill
2431
+ */
2432
+ fill: [{
2433
+ fill: [colors, "none"]
2434
+ }],
2435
+ /**
2436
+ * Stroke Width
2437
+ * @see https://tailwindcss.com/docs/stroke-width
2438
+ */
2439
+ "stroke-w": [{
2440
+ stroke: [isLength, isArbitraryLength, isArbitraryNumber]
2441
+ }],
2442
+ /**
2443
+ * Stroke
2444
+ * @see https://tailwindcss.com/docs/stroke
2445
+ */
2446
+ stroke: [{
2447
+ stroke: [colors, "none"]
2448
+ }],
2449
+ // Accessibility
2450
+ /**
2451
+ * Screen Readers
2452
+ * @see https://tailwindcss.com/docs/screen-readers
2453
+ */
2454
+ sr: ["sr-only", "not-sr-only"],
2455
+ /**
2456
+ * Forced Color Adjust
2457
+ * @see https://tailwindcss.com/docs/forced-color-adjust
2458
+ */
2459
+ "forced-color-adjust": [{
2460
+ "forced-color-adjust": ["auto", "none"]
2461
+ }]
2462
+ },
2463
+ conflictingClassGroups: {
2464
+ overflow: ["overflow-x", "overflow-y"],
2465
+ overscroll: ["overscroll-x", "overscroll-y"],
2466
+ inset: ["inset-x", "inset-y", "start", "end", "top", "right", "bottom", "left"],
2467
+ "inset-x": ["right", "left"],
2468
+ "inset-y": ["top", "bottom"],
2469
+ flex: ["basis", "grow", "shrink"],
2470
+ gap: ["gap-x", "gap-y"],
2471
+ p: ["px", "py", "ps", "pe", "pt", "pr", "pb", "pl"],
2472
+ px: ["pr", "pl"],
2473
+ py: ["pt", "pb"],
2474
+ m: ["mx", "my", "ms", "me", "mt", "mr", "mb", "ml"],
2475
+ mx: ["mr", "ml"],
2476
+ my: ["mt", "mb"],
2477
+ size: ["w", "h"],
2478
+ "font-size": ["leading"],
2479
+ "fvn-normal": ["fvn-ordinal", "fvn-slashed-zero", "fvn-figure", "fvn-spacing", "fvn-fraction"],
2480
+ "fvn-ordinal": ["fvn-normal"],
2481
+ "fvn-slashed-zero": ["fvn-normal"],
2482
+ "fvn-figure": ["fvn-normal"],
2483
+ "fvn-spacing": ["fvn-normal"],
2484
+ "fvn-fraction": ["fvn-normal"],
2485
+ "line-clamp": ["display", "overflow"],
2486
+ rounded: ["rounded-s", "rounded-e", "rounded-t", "rounded-r", "rounded-b", "rounded-l", "rounded-ss", "rounded-se", "rounded-ee", "rounded-es", "rounded-tl", "rounded-tr", "rounded-br", "rounded-bl"],
2487
+ "rounded-s": ["rounded-ss", "rounded-es"],
2488
+ "rounded-e": ["rounded-se", "rounded-ee"],
2489
+ "rounded-t": ["rounded-tl", "rounded-tr"],
2490
+ "rounded-r": ["rounded-tr", "rounded-br"],
2491
+ "rounded-b": ["rounded-br", "rounded-bl"],
2492
+ "rounded-l": ["rounded-tl", "rounded-bl"],
2493
+ "border-spacing": ["border-spacing-x", "border-spacing-y"],
2494
+ "border-w": ["border-w-s", "border-w-e", "border-w-t", "border-w-r", "border-w-b", "border-w-l"],
2495
+ "border-w-x": ["border-w-r", "border-w-l"],
2496
+ "border-w-y": ["border-w-t", "border-w-b"],
2497
+ "border-color": ["border-color-s", "border-color-e", "border-color-t", "border-color-r", "border-color-b", "border-color-l"],
2498
+ "border-color-x": ["border-color-r", "border-color-l"],
2499
+ "border-color-y": ["border-color-t", "border-color-b"],
2500
+ "scroll-m": ["scroll-mx", "scroll-my", "scroll-ms", "scroll-me", "scroll-mt", "scroll-mr", "scroll-mb", "scroll-ml"],
2501
+ "scroll-mx": ["scroll-mr", "scroll-ml"],
2502
+ "scroll-my": ["scroll-mt", "scroll-mb"],
2503
+ "scroll-p": ["scroll-px", "scroll-py", "scroll-ps", "scroll-pe", "scroll-pt", "scroll-pr", "scroll-pb", "scroll-pl"],
2504
+ "scroll-px": ["scroll-pr", "scroll-pl"],
2505
+ "scroll-py": ["scroll-pt", "scroll-pb"],
2506
+ touch: ["touch-x", "touch-y", "touch-pz"],
2507
+ "touch-x": ["touch"],
2508
+ "touch-y": ["touch"],
2509
+ "touch-pz": ["touch"]
2510
+ },
2511
+ conflictingClassGroupModifiers: {
2512
+ "font-size": ["leading"]
2513
+ }
2514
+ };
2515
+ };
2516
+ var twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
2517
+
2518
+ // src/lib/utils.ts
2519
+ function cn(...inputs) {
2520
+ return twMerge((0, import_clsx.clsx)(inputs));
2521
+ }
2522
+
2523
+ // src/components/ui/button.tsx
2524
+ var import_jsx_runtime = require("react/jsx-runtime");
2525
+ var Button = React.forwardRef(
2526
+ ({ className, variant = "default", size = "default", ...props }, ref) => {
2527
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2528
+ "button",
2529
+ {
2530
+ className: cn(
2531
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
2532
+ {
2533
+ "bg-primary text-primary-foreground hover:bg-primary/90": variant === "default",
2534
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90": variant === "destructive",
2535
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground": variant === "outline",
2536
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80": variant === "secondary",
2537
+ "hover:bg-accent hover:text-accent-foreground": variant === "ghost",
2538
+ "text-primary underline-offset-4 hover:underline": variant === "link"
2539
+ },
2540
+ {
2541
+ "h-10 px-4 py-2": size === "default",
2542
+ "h-9 rounded-md px-3": size === "sm",
2543
+ "h-11 rounded-md px-8": size === "lg",
2544
+ "h-10 w-10": size === "icon"
2545
+ },
2546
+ className
2547
+ ),
2548
+ ref,
2549
+ ...props
2550
+ }
2551
+ );
2552
+ }
2553
+ );
2554
+ Button.displayName = "Button";
2555
+
2556
+ // src/components/ui/card.tsx
2557
+ var React2 = __toESM(require("react"));
2558
+ var import_jsx_runtime2 = require("react/jsx-runtime");
2559
+ var Card = React2.forwardRef(
2560
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2561
+ "div",
2562
+ {
2563
+ ref,
2564
+ className: cn(
2565
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
2566
+ className
2567
+ ),
2568
+ ...props
2569
+ }
2570
+ )
2571
+ );
2572
+ Card.displayName = "Card";
2573
+ var CardHeader = React2.forwardRef(
2574
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })
2575
+ );
2576
+ CardHeader.displayName = "CardHeader";
2577
+ var CardTitle = React2.forwardRef(
2578
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
2579
+ );
2580
+ CardTitle.displayName = "CardTitle";
2581
+ var CardDescription = React2.forwardRef(
2582
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props })
2583
+ );
2584
+ CardDescription.displayName = "CardDescription";
2585
+ var CardContent = React2.forwardRef(
2586
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, className: cn("p-6 pt-0", className), ...props })
2587
+ );
2588
+ CardContent.displayName = "CardContent";
2589
+ var CardFooter = React2.forwardRef(
2590
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, className: cn("flex items-center p-6 pt-0", className), ...props })
2591
+ );
2592
+ CardFooter.displayName = "CardFooter";
2593
+
2594
+ // src/components/seo/SEORecordForm.tsx
2595
+ var import_react2 = require("react");
2596
+ var import_react_hook_form = require("react-hook-form");
2597
+ var import_zod2 = require("@hookform/resolvers/zod");
2598
+
2599
+ // src/lib/validation/seo-schema.ts
2600
+ var import_zod = require("zod");
2601
+ var ogTypeSchema = import_zod.z.enum([
2602
+ "website",
2603
+ "article",
2604
+ "product",
2605
+ "book",
2606
+ "profile",
2607
+ "music",
2608
+ "video"
2609
+ ]);
2610
+ var twitterCardSchema = import_zod.z.enum([
2611
+ "summary",
2612
+ "summary_large_image",
2613
+ "app",
2614
+ "player"
2615
+ ]);
2616
+ var validationStatusSchema = import_zod.z.enum([
2617
+ "pending",
2618
+ "valid",
2619
+ "invalid",
2620
+ "warning"
2621
+ ]);
2622
+ var seoMetadataSchema = import_zod.z.object({
2623
+ // Basic metadata
2624
+ title: import_zod.z.string().max(60, "Title must be 60 characters or less").optional(),
2625
+ description: import_zod.z.string().max(160, "Description must be 160 characters or less").optional(),
2626
+ keywords: import_zod.z.array(import_zod.z.string()).optional(),
2627
+ // Open Graph
2628
+ ogTitle: import_zod.z.string().max(60).optional(),
2629
+ ogDescription: import_zod.z.string().max(200).optional(),
2630
+ ogImageUrl: import_zod.z.string().url("Must be a valid URL").optional(),
2631
+ ogImageWidth: import_zod.z.number().int().positive().max(1200).optional(),
2632
+ ogImageHeight: import_zod.z.number().int().positive().max(1200).optional(),
2633
+ ogType: ogTypeSchema.optional(),
2634
+ ogUrl: import_zod.z.string().url("Must be a valid URL").optional(),
2635
+ ogSiteName: import_zod.z.string().optional(),
2636
+ // Twitter Card
2637
+ twitterCard: twitterCardSchema.optional(),
2638
+ twitterTitle: import_zod.z.string().max(70).optional(),
2639
+ twitterDescription: import_zod.z.string().max(200).optional(),
2640
+ twitterImageUrl: import_zod.z.string().url("Must be a valid URL").optional(),
2641
+ twitterSite: import_zod.z.string().optional(),
2642
+ twitterCreator: import_zod.z.string().optional(),
2643
+ // Additional metadata
2644
+ canonicalUrl: import_zod.z.string().url("Must be a valid URL").optional(),
2645
+ robots: import_zod.z.string().optional(),
2646
+ author: import_zod.z.string().optional(),
2647
+ publishedTime: import_zod.z.coerce.date().optional(),
2648
+ modifiedTime: import_zod.z.coerce.date().optional(),
2649
+ // Structured data
2650
+ structuredData: import_zod.z.record(import_zod.z.unknown()).optional()
2651
+ });
2652
+ var seoRecordSchema = seoMetadataSchema.extend({
2653
+ id: import_zod.z.string().uuid().optional(),
2654
+ userId: import_zod.z.string().uuid(),
2655
+ routePath: import_zod.z.string().min(1, "Route path is required").regex(/^\/.*/, "Route path must start with /"),
2656
+ validationStatus: validationStatusSchema.optional(),
2657
+ lastValidatedAt: import_zod.z.coerce.date().optional(),
2658
+ validationErrors: import_zod.z.record(import_zod.z.unknown()).optional(),
2659
+ createdAt: import_zod.z.coerce.date().optional(),
2660
+ updatedAt: import_zod.z.coerce.date().optional()
2661
+ });
2662
+ var createSEORecordSchema = seoRecordSchema.omit({
2663
+ id: true,
2664
+ validationStatus: true,
2665
+ lastValidatedAt: true,
2666
+ validationErrors: true,
2667
+ createdAt: true,
2668
+ updatedAt: true
2669
+ });
2670
+ var updateSEORecordSchema = seoRecordSchema.partial().required({ id: true }).omit({
2671
+ userId: true,
2672
+ createdAt: true
2673
+ });
2674
+
2675
+ // src/components/ui/input.tsx
2676
+ var React3 = __toESM(require("react"));
2677
+ var import_jsx_runtime3 = require("react/jsx-runtime");
2678
+ var Input = React3.forwardRef(
2679
+ ({ className, type, ...props }, ref) => {
2680
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2681
+ "input",
2682
+ {
2683
+ type,
2684
+ className: cn(
2685
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
2686
+ className
2687
+ ),
2688
+ ref,
2689
+ ...props
2690
+ }
2691
+ );
2692
+ }
2693
+ );
2694
+ Input.displayName = "Input";
2695
+
2696
+ // src/components/seo/OGImagePreview.tsx
2697
+ var import_react = require("react");
2698
+
2699
+ // src/components/ui/spinner.tsx
2700
+ var import_jsx_runtime4 = require("react/jsx-runtime");
2701
+ function Spinner({ className, size = "default" }) {
2702
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2703
+ "div",
2704
+ {
2705
+ className: cn(
2706
+ "animate-spin rounded-full border-2 border-current border-t-transparent",
2707
+ {
2708
+ "h-4 w-4": size === "sm",
2709
+ "h-6 w-6": size === "default",
2710
+ "h-8 w-8": size === "lg"
2711
+ },
2712
+ className
2713
+ )
2714
+ }
2715
+ );
2716
+ }
2717
+
2718
+ // src/components/seo/OGImagePreview.tsx
2719
+ var import_jsx_runtime5 = require("react/jsx-runtime");
2720
+ function OGImagePreview({
2721
+ imageUrl,
2722
+ expectedWidth,
2723
+ expectedHeight,
2724
+ title,
2725
+ description
2726
+ }) {
2727
+ const [selectedPlatform, setSelectedPlatform] = (0, import_react.useState)("facebook");
2728
+ const [imageError, setImageError] = (0, import_react.useState)(false);
2729
+ const [validationResult, setValidationResult] = (0, import_react.useState)(null);
2730
+ const [validating, setValidating] = (0, import_react.useState)(false);
2731
+ (0, import_react.useEffect)(() => {
2732
+ if (imageUrl) {
2733
+ validateImage();
2734
+ }
2735
+ }, [imageUrl, expectedWidth, expectedHeight]);
2736
+ const validateImage = async () => {
2737
+ if (!imageUrl) return;
2738
+ setValidating(true);
2739
+ try {
2740
+ const response = await fetch("/api/validate-image", {
2741
+ method: "POST",
2742
+ headers: { "Content-Type": "application/json" },
2743
+ body: JSON.stringify({
2744
+ imageUrl,
2745
+ expectedWidth,
2746
+ expectedHeight
2747
+ })
2748
+ });
2749
+ if (response.ok) {
2750
+ const result = await response.json();
2751
+ setValidationResult(result);
2752
+ }
2753
+ } catch {
2754
+ } finally {
2755
+ setValidating(false);
2756
+ }
2757
+ };
2758
+ const platformSpecs = {
2759
+ facebook: {
2760
+ name: "Facebook",
2761
+ width: 1200,
2762
+ height: 630,
2763
+ aspectRatio: "1.91:1",
2764
+ color: "bg-blue-50 border-blue-200"
2765
+ },
2766
+ twitter: {
2767
+ name: "Twitter",
2768
+ width: 1200,
2769
+ height: 675,
2770
+ aspectRatio: "16:9",
2771
+ color: "bg-sky-50 border-sky-200"
2772
+ },
2773
+ linkedin: {
2774
+ name: "LinkedIn",
2775
+ width: 1200,
2776
+ height: 627,
2777
+ aspectRatio: "1.91:1",
2778
+ color: "bg-blue-50 border-blue-200"
2779
+ }
2780
+ };
2781
+ const spec = platformSpecs[selectedPlatform];
2782
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Card, { children: [
2783
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(CardHeader, { children: [
2784
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CardTitle, { children: "OG Image Preview" }),
2785
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CardDescription, { children: "Preview how your image appears on social platforms" })
2786
+ ] }),
2787
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(CardContent, { className: "space-y-4", children: [
2788
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex gap-2", children: ["facebook", "twitter", "linkedin"].map((platform) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2789
+ "button",
2790
+ {
2791
+ onClick: () => setSelectedPlatform(platform),
2792
+ className: `px-4 py-2 rounded-lg text-sm font-medium transition-colors ${selectedPlatform === platform ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
2793
+ "aria-pressed": selectedPlatform === platform,
2794
+ children: platformSpecs[platform].name
2795
+ },
2796
+ platform
2797
+ )) }),
2798
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `p-3 rounded-lg border ${spec.color}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-sm", children: [
2799
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "font-medium mb-1", children: [
2800
+ spec.name,
2801
+ " Recommended Size"
2802
+ ] }),
2803
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-gray-600", children: [
2804
+ spec.width,
2805
+ " \xD7 ",
2806
+ spec.height,
2807
+ "px (",
2808
+ spec.aspectRatio,
2809
+ ")"
2810
+ ] })
2811
+ ] }) }),
2812
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "relative", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2813
+ "div",
2814
+ {
2815
+ className: "relative border-2 border-gray-300 rounded-lg overflow-hidden bg-gray-100",
2816
+ style: {
2817
+ aspectRatio: `${spec.width} / ${spec.height}`,
2818
+ maxWidth: "100%"
2819
+ },
2820
+ children: [
2821
+ imageUrl && !imageError ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2822
+ "img",
2823
+ {
2824
+ src: imageUrl,
2825
+ alt: title || "OG Image",
2826
+ className: "w-full h-full object-cover",
2827
+ onError: () => setImageError(true),
2828
+ onLoad: () => setImageError(false)
2829
+ }
2830
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center justify-center h-full text-gray-400", children: imageError ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-center p-4", children: [
2831
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-sm font-medium", children: "Image failed to load" }),
2832
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-xs mt-1", children: "Check the URL is accessible" })
2833
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, {}) }),
2834
+ title && !imageError && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4 text-white", children: [
2835
+ title && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "font-semibold text-sm mb-1 line-clamp-2", children: title }),
2836
+ description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-xs opacity-90 line-clamp-2", children: description })
2837
+ ] })
2838
+ ]
2839
+ }
2840
+ ) }),
2841
+ validating && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: [
2842
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { size: "sm" }),
2843
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Validating image..." })
2844
+ ] }),
2845
+ validationResult && !validating && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-2", children: [
2846
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
2847
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-sm font-medium", children: "Validation Status" }),
2848
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2849
+ "span",
2850
+ {
2851
+ className: `text-xs px-2 py-1 rounded ${validationResult.isValid ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`,
2852
+ children: validationResult.isValid ? "Valid" : "Issues Found"
2853
+ }
2854
+ )
2855
+ ] }),
2856
+ validationResult.metadata && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-xs text-gray-600 space-y-1", children: [
2857
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2858
+ "Dimensions: ",
2859
+ validationResult.metadata.width,
2860
+ " \xD7 ",
2861
+ validationResult.metadata.height,
2862
+ "px"
2863
+ ] }),
2864
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2865
+ "Format: ",
2866
+ validationResult.metadata.format.toUpperCase()
2867
+ ] }),
2868
+ validationResult.metadata.size && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2869
+ "Size: ",
2870
+ (validationResult.metadata.size / 1024).toFixed(1),
2871
+ " KB"
2872
+ ] })
2873
+ ] }),
2874
+ validationResult.issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "space-y-2 mt-3", children: validationResult.issues.map((issue, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2875
+ "div",
2876
+ {
2877
+ className: `p-2 rounded text-xs border ${issue.severity === "critical" ? "bg-red-50 border-red-200 text-red-800" : issue.severity === "warning" ? "bg-yellow-50 border-yellow-200 text-yellow-800" : "bg-blue-50 border-blue-200 text-blue-800"}`,
2878
+ children: [
2879
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "font-medium", children: issue.field }),
2880
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mt-1", children: issue.message }),
2881
+ issue.expected && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-1", children: [
2882
+ "Expected: ",
2883
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "bg-white/50 px-1 rounded", children: issue.expected })
2884
+ ] }),
2885
+ issue.actual && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-1", children: [
2886
+ "Actual: ",
2887
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "bg-white/50 px-1 rounded", children: issue.actual })
2888
+ ] })
2889
+ ]
2890
+ },
2891
+ idx
2892
+ )) })
2893
+ ] }),
2894
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "pt-2 border-t", children: [
2895
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Image URL" }),
2896
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-xs font-mono text-gray-700 break-all", children: imageUrl || "No image URL provided" })
2897
+ ] })
2898
+ ] })
2899
+ ] });
2900
+ }
2901
+
2902
+ // src/components/seo/SEORecordForm.tsx
2903
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2904
+ function SEORecordForm({ record, onSuccess, onCancel }) {
2905
+ const [loading, setLoading] = (0, import_react2.useState)(false);
2906
+ const [error, setError] = (0, import_react2.useState)(null);
2907
+ const isEditing = !!record;
2908
+ const schema = isEditing ? updateSEORecordSchema : createSEORecordSchema;
2909
+ const {
2910
+ register,
2911
+ handleSubmit,
2912
+ formState: { errors },
2913
+ reset,
2914
+ watch
2915
+ } = (0, import_react_hook_form.useForm)({
2916
+ resolver: (0, import_zod2.zodResolver)(schema),
2917
+ defaultValues: record ? {
2918
+ id: record.id,
2919
+ routePath: record.routePath,
2920
+ title: record.title,
2921
+ description: record.description,
2922
+ keywords: record.keywords,
2923
+ ogTitle: record.ogTitle,
2924
+ ogDescription: record.ogDescription,
2925
+ ogImageUrl: record.ogImageUrl,
2926
+ ogImageWidth: record.ogImageWidth,
2927
+ ogImageHeight: record.ogImageHeight,
2928
+ ogType: record.ogType,
2929
+ ogUrl: record.ogUrl,
2930
+ ogSiteName: record.ogSiteName,
2931
+ twitterCard: record.twitterCard,
2932
+ twitterTitle: record.twitterTitle,
2933
+ twitterDescription: record.twitterDescription,
2934
+ twitterImageUrl: record.twitterImageUrl,
2935
+ twitterSite: record.twitterSite,
2936
+ twitterCreator: record.twitterCreator,
2937
+ canonicalUrl: record.canonicalUrl,
2938
+ robots: record.robots,
2939
+ author: record.author,
2940
+ publishedTime: record.publishedTime,
2941
+ modifiedTime: record.modifiedTime
2942
+ } : {
2943
+ routePath: ""
2944
+ }
2945
+ });
2946
+ const titleValue = watch("title");
2947
+ const descriptionValue = watch("description");
2948
+ const ogTitleValue = watch("ogTitle");
2949
+ const ogDescriptionValue = watch("ogDescription");
2950
+ const onSubmit = async (data) => {
2951
+ setLoading(true);
2952
+ setError(null);
2953
+ try {
2954
+ const url = isEditing ? `/api/seo-records/${data.id}` : "/api/seo-records";
2955
+ const method = isEditing ? "PATCH" : "POST";
2956
+ const response = await fetch(url, {
2957
+ method,
2958
+ headers: { "Content-Type": "application/json" },
2959
+ body: JSON.stringify(data)
2960
+ });
2961
+ if (!response.ok) {
2962
+ const errorData = await response.json();
2963
+ throw new Error(errorData.error || "Failed to save SEO record");
2964
+ }
2965
+ onSuccess?.();
2966
+ if (!isEditing) {
2967
+ reset();
2968
+ }
2969
+ } catch (err) {
2970
+ setError(err instanceof Error ? err.message : "An error occurred");
2971
+ } finally {
2972
+ setLoading(false);
2973
+ }
2974
+ };
2975
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit(onSubmit), className: "space-y-6", children: [
2976
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2977
+ "div",
2978
+ {
2979
+ className: "bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg text-sm",
2980
+ role: "alert",
2981
+ "aria-live": "polite",
2982
+ children: error
2983
+ }
2984
+ ),
2985
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Card, { children: [
2986
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardHeader, { children: [
2987
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardTitle, { children: "Basic Information" }),
2988
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardDescription, { children: "Core SEO metadata" })
2989
+ ] }),
2990
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "space-y-4", children: [
2991
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
2992
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { htmlFor: "routePath", className: "block text-sm font-medium text-gray-700 mb-1", children: [
2993
+ "Route Path ",
2994
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-red-500", children: "*" })
2995
+ ] }),
2996
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2997
+ Input,
2998
+ {
2999
+ id: "routePath",
3000
+ ...register("routePath"),
3001
+ placeholder: "/about",
3002
+ "aria-describedby": errors.routePath ? "routePath-error" : void 0,
3003
+ "aria-invalid": errors.routePath ? "true" : "false"
3004
+ }
3005
+ ),
3006
+ errors.routePath && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { id: "routePath-error", className: "mt-1 text-sm text-red-600", role: "alert", children: errors.routePath.message })
3007
+ ] }),
3008
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3009
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "title", className: "block text-sm font-medium text-gray-700 mb-1", children: "Title" }),
3010
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3011
+ Input,
3012
+ {
3013
+ id: "title",
3014
+ ...register("title"),
3015
+ placeholder: "Page Title",
3016
+ maxLength: 60,
3017
+ "aria-describedby": "title-help title-count"
3018
+ }
3019
+ ),
3020
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex justify-between mt-1", children: [
3021
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { id: "title-help", className: "text-xs text-gray-500", children: "Recommended: 50-60 characters" }),
3022
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { id: "title-count", className: "text-xs text-gray-500", "aria-live": "polite", children: [
3023
+ titleValue?.length || 0,
3024
+ "/60"
3025
+ ] })
3026
+ ] }),
3027
+ errors.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-sm text-red-600", role: "alert", children: errors.title.message })
3028
+ ] }),
3029
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3030
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "description", className: "block text-sm font-medium text-gray-700 mb-1", children: "Description" }),
3031
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3032
+ "textarea",
3033
+ {
3034
+ id: "description",
3035
+ ...register("description"),
3036
+ rows: 3,
3037
+ placeholder: "Page description",
3038
+ maxLength: 160,
3039
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500",
3040
+ "aria-describedby": "description-help description-count"
3041
+ }
3042
+ ),
3043
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex justify-between mt-1", children: [
3044
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { id: "description-help", className: "text-xs text-gray-500", children: "Recommended: 150-160 characters" }),
3045
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { id: "description-count", className: "text-xs text-gray-500", "aria-live": "polite", children: [
3046
+ descriptionValue?.length || 0,
3047
+ "/160"
3048
+ ] })
3049
+ ] }),
3050
+ errors.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-sm text-red-600", role: "alert", children: errors.description.message })
3051
+ ] }),
3052
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3053
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "canonicalUrl", className: "block text-sm font-medium text-gray-700 mb-1", children: "Canonical URL" }),
3054
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3055
+ Input,
3056
+ {
3057
+ id: "canonicalUrl",
3058
+ type: "url",
3059
+ ...register("canonicalUrl"),
3060
+ placeholder: "https://example.com/page"
3061
+ }
3062
+ ),
3063
+ errors.canonicalUrl && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-sm text-red-600", role: "alert", children: errors.canonicalUrl.message })
3064
+ ] })
3065
+ ] })
3066
+ ] }),
3067
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Card, { children: [
3068
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardHeader, { children: [
3069
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardTitle, { children: "Open Graph" }),
3070
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardDescription, { children: "Social media sharing metadata" })
3071
+ ] }),
3072
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "space-y-4", children: [
3073
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3074
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "ogType", className: "block text-sm font-medium text-gray-700 mb-1", children: "OG Type" }),
3075
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3076
+ "select",
3077
+ {
3078
+ id: "ogType",
3079
+ ...register("ogType"),
3080
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500",
3081
+ children: [
3082
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Select type" }),
3083
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "website", children: "Website" }),
3084
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "article", children: "Article" }),
3085
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "product", children: "Product" }),
3086
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "book", children: "Book" }),
3087
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "profile", children: "Profile" }),
3088
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "music", children: "Music" }),
3089
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "video", children: "Video" })
3090
+ ]
3091
+ }
3092
+ )
3093
+ ] }),
3094
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3095
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "ogTitle", className: "block text-sm font-medium text-gray-700 mb-1", children: "OG Title" }),
3096
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3097
+ Input,
3098
+ {
3099
+ id: "ogTitle",
3100
+ ...register("ogTitle"),
3101
+ placeholder: "Open Graph title",
3102
+ maxLength: 60,
3103
+ "aria-describedby": "ogTitle-count"
3104
+ }
3105
+ ),
3106
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { id: "ogTitle-count", className: "text-xs text-gray-500 mt-1 block", "aria-live": "polite", children: [
3107
+ ogTitleValue?.length || 0,
3108
+ "/60"
3109
+ ] })
3110
+ ] }),
3111
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3112
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "ogDescription", className: "block text-sm font-medium text-gray-700 mb-1", children: "OG Description" }),
3113
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3114
+ "textarea",
3115
+ {
3116
+ id: "ogDescription",
3117
+ ...register("ogDescription"),
3118
+ rows: 3,
3119
+ placeholder: "Open Graph description",
3120
+ maxLength: 200,
3121
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500",
3122
+ "aria-describedby": "ogDescription-count"
3123
+ }
3124
+ ),
3125
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { id: "ogDescription-count", className: "text-xs text-gray-500 mt-1 block", "aria-live": "polite", children: [
3126
+ ogDescriptionValue?.length || 0,
3127
+ "/200"
3128
+ ] })
3129
+ ] }),
3130
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3131
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "ogImageUrl", className: "block text-sm font-medium text-gray-700 mb-1", children: "OG Image URL" }),
3132
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3133
+ Input,
3134
+ {
3135
+ id: "ogImageUrl",
3136
+ type: "url",
3137
+ ...register("ogImageUrl"),
3138
+ placeholder: "https://example.com/image.jpg"
3139
+ }
3140
+ ),
3141
+ errors.ogImageUrl && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-sm text-red-600", role: "alert", children: errors.ogImageUrl.message }),
3142
+ watch("ogImageUrl") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mt-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3143
+ OGImagePreview,
3144
+ {
3145
+ imageUrl: watch("ogImageUrl") || "",
3146
+ expectedWidth: watch("ogImageWidth"),
3147
+ expectedHeight: watch("ogImageHeight"),
3148
+ title: watch("ogTitle") || watch("title"),
3149
+ description: watch("ogDescription") || watch("description")
3150
+ }
3151
+ ) })
3152
+ ] }),
3153
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
3154
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3155
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "ogImageWidth", className: "block text-sm font-medium text-gray-700 mb-1", children: "OG Image Width" }),
3156
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3157
+ Input,
3158
+ {
3159
+ id: "ogImageWidth",
3160
+ type: "number",
3161
+ ...register("ogImageWidth", { valueAsNumber: true }),
3162
+ placeholder: "1200"
3163
+ }
3164
+ )
3165
+ ] }),
3166
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3167
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "ogImageHeight", className: "block text-sm font-medium text-gray-700 mb-1", children: "OG Image Height" }),
3168
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3169
+ Input,
3170
+ {
3171
+ id: "ogImageHeight",
3172
+ type: "number",
3173
+ ...register("ogImageHeight", { valueAsNumber: true }),
3174
+ placeholder: "630"
3175
+ }
3176
+ )
3177
+ ] })
3178
+ ] }),
3179
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3180
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "ogSiteName", className: "block text-sm font-medium text-gray-700 mb-1", children: "OG Site Name" }),
3181
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Input, { id: "ogSiteName", ...register("ogSiteName"), placeholder: "Site Name" })
3182
+ ] })
3183
+ ] })
3184
+ ] }),
3185
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Card, { children: [
3186
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardHeader, { children: [
3187
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardTitle, { children: "Twitter Card" }),
3188
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardDescription, { children: "Twitter sharing metadata" })
3189
+ ] }),
3190
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "space-y-4", children: [
3191
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3192
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "twitterCard", className: "block text-sm font-medium text-gray-700 mb-1", children: "Twitter Card Type" }),
3193
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3194
+ "select",
3195
+ {
3196
+ id: "twitterCard",
3197
+ ...register("twitterCard"),
3198
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500",
3199
+ children: [
3200
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Select type" }),
3201
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "summary", children: "Summary" }),
3202
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "summary_large_image", children: "Summary Large Image" }),
3203
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "app", children: "App" }),
3204
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "player", children: "Player" })
3205
+ ]
3206
+ }
3207
+ )
3208
+ ] }),
3209
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3210
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "twitterTitle", className: "block text-sm font-medium text-gray-700 mb-1", children: "Twitter Title" }),
3211
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3212
+ Input,
3213
+ {
3214
+ id: "twitterTitle",
3215
+ ...register("twitterTitle"),
3216
+ placeholder: "Twitter title",
3217
+ maxLength: 70
3218
+ }
3219
+ )
3220
+ ] }),
3221
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3222
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "twitterDescription", className: "block text-sm font-medium text-gray-700 mb-1", children: "Twitter Description" }),
3223
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3224
+ "textarea",
3225
+ {
3226
+ id: "twitterDescription",
3227
+ ...register("twitterDescription"),
3228
+ rows: 3,
3229
+ placeholder: "Twitter description",
3230
+ maxLength: 200,
3231
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
3232
+ }
3233
+ )
3234
+ ] }),
3235
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
3236
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "twitterImageUrl", className: "block text-sm font-medium text-gray-700 mb-1", children: "Twitter Image URL" }),
3237
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3238
+ Input,
3239
+ {
3240
+ id: "twitterImageUrl",
3241
+ type: "url",
3242
+ ...register("twitterImageUrl"),
3243
+ placeholder: "https://example.com/twitter-image.jpg"
3244
+ }
3245
+ )
3246
+ ] })
3247
+ ] })
3248
+ ] }),
3249
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-4 justify-end", children: [
3250
+ onCancel && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { type: "button", variant: "outline", onClick: onCancel, children: "Cancel" }),
3251
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Button, { type: "submit", disabled: loading, children: [
3252
+ loading ? "Saving..." : isEditing ? "Update" : "Create",
3253
+ " SEO Record"
3254
+ ] })
3255
+ ] })
3256
+ ] });
3257
+ }
3258
+
3259
+ // src/components/seo/SEORecordList.tsx
3260
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3261
+ function SEORecordList() {
3262
+ const [records, setRecords] = (0, import_react3.useState)([]);
3263
+ const [loading, setLoading] = (0, import_react3.useState)(true);
3264
+ const [error, setError] = (0, import_react3.useState)(null);
3265
+ const [editingId, setEditingId] = (0, import_react3.useState)(null);
3266
+ const [showForm, setShowForm] = (0, import_react3.useState)(false);
3267
+ const fetchRecords = async () => {
3268
+ setLoading(true);
3269
+ setError(null);
3270
+ try {
3271
+ const response = await fetch("/api/seo-records");
3272
+ if (!response.ok) {
3273
+ throw new Error("Failed to fetch SEO records");
3274
+ }
3275
+ const data = await response.json();
3276
+ setRecords(data.data || []);
3277
+ } catch (err) {
3278
+ setError(err instanceof Error ? err.message : "An error occurred");
3279
+ } finally {
3280
+ setLoading(false);
3281
+ }
3282
+ };
3283
+ (0, import_react3.useEffect)(() => {
3284
+ fetchRecords();
3285
+ }, []);
3286
+ const handleDelete = async (id) => {
3287
+ if (!confirm("Are you sure you want to delete this SEO record?")) {
3288
+ return;
3289
+ }
3290
+ try {
3291
+ const response = await fetch(`/api/seo-records/${id}`, {
3292
+ method: "DELETE"
3293
+ });
3294
+ if (!response.ok) {
3295
+ throw new Error("Failed to delete SEO record");
3296
+ }
3297
+ await fetchRecords();
3298
+ } catch (err) {
3299
+ alert(err instanceof Error ? err.message : "Failed to delete record");
3300
+ }
3301
+ };
3302
+ const handleEdit = (id) => {
3303
+ setEditingId(id);
3304
+ setShowForm(true);
3305
+ };
3306
+ const handleFormSuccess = () => {
3307
+ setShowForm(false);
3308
+ setEditingId(null);
3309
+ fetchRecords();
3310
+ };
3311
+ const handleCancel = () => {
3312
+ setShowForm(false);
3313
+ setEditingId(null);
3314
+ };
3315
+ const editingRecord = editingId ? records.find((r) => r.id === editingId) || void 0 : void 0;
3316
+ if (showForm) {
3317
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
3318
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3319
+ Button,
3320
+ {
3321
+ variant: "outline",
3322
+ onClick: handleCancel,
3323
+ className: "mb-4",
3324
+ children: "\u2190 Back to List"
3325
+ }
3326
+ ),
3327
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3328
+ SEORecordForm,
3329
+ {
3330
+ record: editingRecord,
3331
+ onSuccess: handleFormSuccess,
3332
+ onCancel: handleCancel
3333
+ }
3334
+ )
3335
+ ] });
3336
+ }
3337
+ if (loading) {
3338
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-center py-8", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-gray-600", children: "Loading SEO records..." }) });
3339
+ }
3340
+ if (error) {
3341
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg", children: [
3342
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { children: [
3343
+ "Error: ",
3344
+ error
3345
+ ] }),
3346
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { onClick: fetchRecords, variant: "outline", className: "mt-2", children: "Retry" })
3347
+ ] });
3348
+ }
3349
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-4", children: [
3350
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between items-center", children: [
3351
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { className: "text-2xl font-bold text-gray-900", children: "SEO Records" }),
3352
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { onClick: () => setShowForm(true), children: "Create New Record" })
3353
+ ] }),
3354
+ records.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(CardContent, { className: "py-8 text-center", children: [
3355
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-gray-600 mb-4", children: "No SEO records yet." }),
3356
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { onClick: () => setShowForm(true), children: "Create Your First Record" })
3357
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "grid gap-4", children: records.map((record) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Card, { children: [
3358
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between items-start", children: [
3359
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
3360
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardTitle, { className: "text-lg", children: record.routePath }),
3361
+ record.title && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm text-gray-600 mt-1", children: record.title })
3362
+ ] }),
3363
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-2", children: [
3364
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3365
+ Button,
3366
+ {
3367
+ variant: "outline",
3368
+ size: "sm",
3369
+ onClick: () => record.id && handleEdit(record.id),
3370
+ children: "Edit"
3371
+ }
3372
+ ),
3373
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3374
+ Button,
3375
+ {
3376
+ variant: "outline",
3377
+ size: "sm",
3378
+ onClick: () => record.id && handleDelete(record.id),
3379
+ children: "Delete"
3380
+ }
3381
+ )
3382
+ ] })
3383
+ ] }) }),
3384
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardContent, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dl", { className: "grid grid-cols-2 gap-4 text-sm", children: [
3385
+ record.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3386
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { className: "text-gray-500", children: "Description" }),
3387
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { className: "text-gray-900", children: record.description })
3388
+ ] }),
3389
+ record.ogType && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3390
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { className: "text-gray-500", children: "OG Type" }),
3391
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { className: "text-gray-900", children: record.ogType })
3392
+ ] }),
3393
+ record.twitterCard && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3394
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { className: "text-gray-500", children: "Twitter Card" }),
3395
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { className: "text-gray-900", children: record.twitterCard })
3396
+ ] }),
3397
+ record.validationStatus && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3398
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { className: "text-gray-500", children: "Status" }),
3399
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { className: "text-gray-900", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3400
+ "span",
3401
+ {
3402
+ className: `inline-flex px-2 py-1 rounded text-xs font-medium ${record.validationStatus === "valid" ? "bg-green-100 text-green-800" : record.validationStatus === "invalid" ? "bg-red-100 text-red-800" : record.validationStatus === "warning" ? "bg-yellow-100 text-yellow-800" : "bg-gray-100 text-gray-800"}`,
3403
+ children: record.validationStatus
3404
+ }
3405
+ ) })
3406
+ ] })
3407
+ ] }) })
3408
+ ] }, record.id)) })
3409
+ ] });
3410
+ }
3411
+
3412
+ // src/components/seo/ValidationDashboard.tsx
3413
+ var import_react4 = require("react");
3414
+ var import_jsx_runtime8 = require("react/jsx-runtime");
3415
+ var Link = ({ href, children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("a", { href, ...props, children });
3416
+ function ValidationDashboard() {
3417
+ const [records, setRecords] = (0, import_react4.useState)([]);
3418
+ const [loading, setLoading] = (0, import_react4.useState)(true);
3419
+ const [validating, setValidating] = (0, import_react4.useState)({});
3420
+ const [validationResults, setValidationResults] = (0, import_react4.useState)({});
3421
+ (0, import_react4.useEffect)(() => {
3422
+ fetchRecords();
3423
+ }, []);
3424
+ const fetchRecords = async () => {
3425
+ setLoading(true);
3426
+ try {
3427
+ const response = await fetch("/api/seo-records");
3428
+ if (response.ok) {
3429
+ const data = await response.json();
3430
+ setRecords(data.data || []);
3431
+ }
3432
+ } catch {
3433
+ } finally {
3434
+ setLoading(false);
3435
+ }
3436
+ };
3437
+ const validateRecord = async (record) => {
3438
+ if (!record.id) return;
3439
+ setValidating((prev) => ({ ...prev, [record.id]: true }));
3440
+ try {
3441
+ const url = record.canonicalUrl || `https://example.com${record.routePath}`;
3442
+ const response = await fetch(`/api/seo-records/${record.id}/validate`, {
3443
+ method: "POST",
3444
+ headers: { "Content-Type": "application/json" },
3445
+ body: JSON.stringify({ url })
3446
+ });
3447
+ if (response.ok) {
3448
+ const data = await response.json();
3449
+ setValidationResults((prev) => ({
3450
+ ...prev,
3451
+ [record.id]: data
3452
+ }));
3453
+ const allIssues2 = data.validations.flatMap((v) => v.result.issues);
3454
+ const hasCritical = allIssues2.some((i) => i.severity === "critical");
3455
+ const hasWarnings = allIssues2.some((i) => i.severity === "warning");
3456
+ const validationStatus = hasCritical ? "invalid" : hasWarnings ? "warning" : "valid";
3457
+ await fetch(`/api/seo-records/${record.id}/update-validation`, {
3458
+ method: "PATCH",
3459
+ headers: { "Content-Type": "application/json" },
3460
+ body: JSON.stringify({
3461
+ validationStatus,
3462
+ validationErrors: allIssues2.reduce((acc, issue) => {
3463
+ acc[issue.field] = issue;
3464
+ return acc;
3465
+ }, {}),
3466
+ lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString()
3467
+ })
3468
+ });
3469
+ fetchRecords();
3470
+ }
3471
+ } catch {
3472
+ } finally {
3473
+ setValidating((prev) => ({ ...prev, [record.id]: false }));
3474
+ }
3475
+ };
3476
+ const getSeverityColor = (severity) => {
3477
+ switch (severity) {
3478
+ case "critical":
3479
+ return "bg-red-100 text-red-800 border-red-200";
3480
+ case "warning":
3481
+ return "bg-yellow-100 text-yellow-800 border-yellow-200";
3482
+ case "info":
3483
+ return "bg-blue-100 text-blue-800 border-blue-200";
3484
+ default:
3485
+ return "bg-gray-100 text-gray-800 border-gray-200";
3486
+ }
3487
+ };
3488
+ const getSeverityIcon = (severity) => {
3489
+ switch (severity) {
3490
+ case "critical":
3491
+ return "\u274C";
3492
+ case "warning":
3493
+ return "\u26A0\uFE0F";
3494
+ case "info":
3495
+ return "\u2139\uFE0F";
3496
+ default:
3497
+ return "\u2022";
3498
+ }
3499
+ };
3500
+ if (loading) {
3501
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-center py-8", children: [
3502
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner, {}),
3503
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-gray-600 mt-4", children: "Loading records..." })
3504
+ ] });
3505
+ }
3506
+ const allIssues = [];
3507
+ Object.entries(validationResults).forEach(([recordId, validation]) => {
3508
+ const record = records.find((r) => r.id === recordId);
3509
+ if (record) {
3510
+ validation.validations.forEach((val) => {
3511
+ val.result.issues.forEach((issue) => {
3512
+ allIssues.push({
3513
+ record,
3514
+ issue,
3515
+ validationType: val.type
3516
+ });
3517
+ });
3518
+ });
3519
+ }
3520
+ });
3521
+ const criticalIssues = allIssues.filter((i) => i.issue.severity === "critical");
3522
+ const warningIssues = allIssues.filter((i) => i.issue.severity === "warning");
3523
+ const infoIssues = allIssues.filter((i) => i.issue.severity === "info");
3524
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-6", children: [
3525
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex justify-between items-center", children: [
3526
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
3527
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-2xl font-bold text-gray-900", children: "Validation Dashboard" }),
3528
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-gray-600 mt-1", children: "Validate your SEO records against live pages" })
3529
+ ] }),
3530
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { onClick: fetchRecords, variant: "outline", children: "Refresh Records" })
3531
+ ] }),
3532
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "grid gap-4 md:grid-cols-4", children: [
3533
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-center", children: [
3534
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-3xl font-bold text-gray-900", children: records.length }),
3535
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm text-gray-600 mt-1", children: "Total Records" })
3536
+ ] }) }) }),
3537
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-center", children: [
3538
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-3xl font-bold text-red-600", children: criticalIssues.length }),
3539
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm text-gray-600 mt-1", children: "Critical Issues" })
3540
+ ] }) }) }),
3541
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-center", children: [
3542
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-3xl font-bold text-yellow-600", children: warningIssues.length }),
3543
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm text-gray-600 mt-1", children: "Warnings" })
3544
+ ] }) }) }),
3545
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-center", children: [
3546
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-3xl font-bold text-blue-600", children: infoIssues.length }),
3547
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm text-gray-600 mt-1", children: "Info" })
3548
+ ] }) }) })
3549
+ ] }),
3550
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-4", children: [
3551
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "text-lg font-semibold text-gray-900", children: "SEO Records" }),
3552
+ records.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "py-8 text-center", children: [
3553
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-gray-600", children: "No SEO records found." }),
3554
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Link, { href: "/dashboard", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { className: "mt-4", children: "Create Your First Record" }) })
3555
+ ] }) }) : records.map((record) => {
3556
+ const result = record.id ? validationResults[record.id] : void 0;
3557
+ const hasIssues = result?.validations.some((v) => v.result.issues.length > 0);
3558
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { children: [
3559
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex justify-between items-start", children: [
3560
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
3561
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardTitle, { className: "text-lg", children: record.routePath }),
3562
+ record.title && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardDescription, { className: "mt-1", children: record.title })
3563
+ ] }),
3564
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3565
+ Button,
3566
+ {
3567
+ onClick: () => validateRecord(record),
3568
+ disabled: validating[record.id],
3569
+ size: "sm",
3570
+ children: validating[record.id] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3571
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner, { className: "mr-2" }),
3572
+ "Validating..."
3573
+ ] }) : "Validate"
3574
+ }
3575
+ )
3576
+ ] }) }),
3577
+ result && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { children: [
3578
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "space-y-4", children: result.validations.map((validation, idx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "border-t pt-4", children: [
3579
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
3580
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "font-medium text-sm text-gray-700", children: [
3581
+ validation.type.toUpperCase(),
3582
+ " Validation"
3583
+ ] }),
3584
+ validation.result.isValid ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs bg-green-100 text-green-800 px-2 py-1 rounded", children: "Valid" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs bg-red-100 text-red-800 px-2 py-1 rounded", children: "Issues Found" })
3585
+ ] }),
3586
+ validation.result.issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "space-y-2", children: validation.result.issues.map((issue, issueIdx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3587
+ "div",
3588
+ {
3589
+ className: `p-3 rounded border ${getSeverityColor(issue.severity)}`,
3590
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-start gap-2", children: [
3591
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: getSeverityIcon(issue.severity) }),
3592
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex-1", children: [
3593
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "font-medium text-sm", children: issue.field }),
3594
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm mt-1", children: issue.message }),
3595
+ issue.expected && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs mt-1", children: [
3596
+ "Expected: ",
3597
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { className: "bg-white/50 px-1 rounded", children: issue.expected })
3598
+ ] }),
3599
+ issue.actual && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs mt-1", children: [
3600
+ "Actual: ",
3601
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { className: "bg-white/50 px-1 rounded", children: issue.actual })
3602
+ ] })
3603
+ ] })
3604
+ ] })
3605
+ },
3606
+ issueIdx
3607
+ )) }),
3608
+ validation.result.metadata && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mt-2 text-xs text-gray-600", children: [
3609
+ "Image: ",
3610
+ validation.result.metadata.width,
3611
+ "x",
3612
+ validation.result.metadata.height,
3613
+ " (",
3614
+ validation.result.metadata.format,
3615
+ validation.result.metadata.size ? `, ${(validation.result.metadata.size / 1024).toFixed(1)}KB` : "",
3616
+ ")"
3617
+ ] })
3618
+ ] }, idx)) }),
3619
+ hasIssues && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mt-4", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Link, { href: `/dashboard?edit=${record.id}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { variant: "outline", size: "sm", children: "Edit Record" }) }) })
3620
+ ] })
3621
+ ] }, record.id);
3622
+ })
3623
+ ] })
3624
+ ] });
3625
+ }
3626
+ // Annotate the CommonJS export names for ESM import in node:
3627
+ 0 && (module.exports = {
3628
+ Button,
3629
+ Card,
3630
+ CardContent,
3631
+ CardDescription,
3632
+ CardFooter,
3633
+ CardHeader,
3634
+ CardTitle,
3635
+ Input,
3636
+ OGImagePreview,
3637
+ SEORecordForm,
3638
+ SEORecordList,
3639
+ Spinner,
3640
+ ValidationDashboard
3641
+ });
3642
+ //# sourceMappingURL=index.js.map