@kilivi/payloadcms-theme-management 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +48 -31
  2. package/dist/components/ThemeLivePreviewSync.d.ts +11 -0
  3. package/dist/components/ThemeLivePreviewSync.d.ts.map +1 -0
  4. package/dist/components/ThemeLivePreviewSync.js +40 -0
  5. package/dist/components/ThemeLivePreviewSync.js.map +1 -0
  6. package/dist/fields/ThemeColorPickerField.d.ts.map +1 -1
  7. package/dist/fields/ThemeColorPickerField.js +26 -9
  8. package/dist/fields/ThemeColorPickerField.js.map +1 -1
  9. package/dist/fields/ThemePreviewField.d.ts.map +1 -1
  10. package/dist/fields/ThemePreviewField.js +2 -1
  11. package/dist/fields/ThemePreviewField.js.map +1 -1
  12. package/dist/fields/themeConfigurationField.d.ts +1 -0
  13. package/dist/fields/themeConfigurationField.d.ts.map +1 -1
  14. package/dist/fields/themeConfigurationField.js +3 -2
  15. package/dist/fields/themeConfigurationField.js.map +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +337 -16
  19. package/dist/index.js.map +1 -1
  20. package/dist/server.d.ts +30 -0
  21. package/dist/server.d.ts.map +1 -1
  22. package/dist/server.js +42 -0
  23. package/dist/server.js.map +1 -1
  24. package/dist/types/color.d.js +2 -0
  25. package/dist/types/color.d.js.map +1 -0
  26. package/dist/types.d.ts +69 -0
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/types.js.map +1 -1
  29. package/dist/utils/livePreviewRefresh.d.ts +17 -0
  30. package/dist/utils/livePreviewRefresh.d.ts.map +1 -0
  31. package/dist/utils/livePreviewRefresh.js +20 -0
  32. package/dist/utils/livePreviewRefresh.js.map +1 -0
  33. package/dist/utils/livePreviewThemeRuntime.d.ts +7 -0
  34. package/dist/utils/livePreviewThemeRuntime.d.ts.map +1 -0
  35. package/dist/utils/livePreviewThemeRuntime.js +58 -0
  36. package/dist/utils/livePreviewThemeRuntime.js.map +1 -0
  37. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fields/themeConfigurationField.ts"],"sourcesContent":["import type { Field, TabsField } from 'payload'\nimport {\n BASE_FONT_SIZE_OPTIONS,\n BODY_FONT_OPTIONS,\n HEADING_FONT_OPTIONS,\n LINE_HEIGHT_OPTIONS,\n} from '../constants/themeFonts.js'\nimport type { ThemePreset } from '../index.js'\nimport { darkModeField, lightModeField } from './colorModeFields.js'\n\ninterface ThemeConfigurationFieldOptions {\n themePresets: ThemePreset[]\n defaultTheme: string\n includeColorModeToggle: boolean\n includeCustomCSS: boolean\n includeBrandIdentity: boolean\n enableAdvancedFeatures?: boolean\n}\n\n// Type for admin.custom property to avoid ts-expect-error\ninterface ThemeAdminCustom {\n themePresets?: ThemePreset[]\n}\n\n// Type for a single tab (not the full TabsField)\nexport type ThemeTab = NonNullable<TabsField['tabs']>[number]\n\n/**\n * Theme Configuration Field with Theme Selection as default value setter\n * Theme Selection populates lightMode/darkMode colors automatically\n */\nexport function createThemeConfigurationField(options: ThemeConfigurationFieldOptions): ThemeTab {\n const {\n themePresets,\n defaultTheme,\n includeColorModeToggle,\n includeCustomCSS,\n enableAdvancedFeatures = true,\n } = options\n\n const fields: Field[] = [\n {\n name: 'theme',\n type: 'text',\n required: true,\n defaultValue: defaultTheme,\n label: {\n en: '🎨 Theme Selection',\n cs: '🎨 Výběr tématu',\n },\n validate: (value: unknown) => {\n if (typeof value === 'string' && value.trim().length > 0) {\n return true\n }\n return 'Select a theme'\n },\n admin: {\n description: {\n en: 'Select a theme to auto-populate color values. You can customize colors after selection.',\n cs: 'Vyberte téma pro automatické předvyplnění barev. Po výběru můžete barvy upravit.',\n },\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/ThemePreviewField',\n },\n custom: {\n themePresets,\n } as ThemeAdminCustom,\n },\n },\n ]\n\n // Border Radius Configuration - Always visible\n fields.push({\n type: 'row',\n fields: [\n {\n name: 'borderRadius',\n type: 'select',\n label: {\n en: '📐 Border Radius',\n cs: '📐 Zaoblení rohů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None (0px)', cs: 'Žádné (0px)' }, value: 'none' },\n { label: { en: 'Small (0.25rem)', cs: 'Malé (0.25rem)' }, value: 'small' },\n { label: { en: 'Medium (0.5rem)', cs: 'Střední (0.5rem)' }, value: 'medium' },\n { label: { en: 'Large (0.75rem)', cs: 'Velké (0.75rem)' }, value: 'large' },\n { label: { en: 'XL (1rem)', cs: 'XL (1rem)' }, value: 'xl' },\n ],\n admin: {\n description: {\n en: 'Default corner rounding for UI elements',\n cs: 'Výchozí zaoblení rohů pro UI prvky',\n },\n width: '50%',\n },\n dbName: 'border_radius',\n },\n {\n name: 'spacing',\n type: 'select',\n label: {\n en: '📏 Spacing Scale',\n cs: '📏 Škála rozestupů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'Compact', cs: 'Kompaktní' }, value: 'compact' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'Comfortable', cs: 'Pohodlné' }, value: 'comfortable' },\n ],\n admin: {\n description: {\n en: 'Global spacing between elements',\n cs: 'Globální rozestupy mezi prvky',\n },\n width: '50%',\n },\n dbName: 'spacing',\n },\n ],\n })\n\n // Color Mode Settings\n if (includeColorModeToggle) {\n fields.push({\n type: 'collapsible',\n label: {\n en: '🌗 Color Mode Settings',\n cs: '🌗 Nastavení barevného režimu',\n },\n admin: {\n initCollapsed: false,\n description: {\n en: 'Configure light and dark mode colors. Changes here override theme selection.',\n cs: 'Nakonfigurujte barvy světlého a tmavého režimu. Změny zde přepíší výběr tématu.',\n },\n },\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'colorMode',\n type: 'select',\n label: {\n en: 'Default Color Mode',\n cs: 'Výchozí barevný režim',\n },\n defaultValue: 'auto',\n options: [\n {\n label: {\n en: 'Auto (System)',\n cs: 'Automaticky (Systém)',\n },\n value: 'auto',\n },\n {\n label: { en: 'Light', cs: 'Světlý' },\n value: 'light',\n },\n {\n label: { en: 'Dark', cs: 'Tmavý' },\n value: 'dark',\n },\n ],\n admin: {\n description: {\n en: 'Default color mode for visitors',\n cs: 'Výchozí barevný režim pro návštěvníky',\n },\n width: '50%',\n },\n dbName: 'color_mode',\n },\n {\n name: 'allowColorModeToggle',\n type: 'checkbox',\n label: {\n en: 'Enable Mode Toggle',\n cs: 'Povolit přepínač režimu',\n },\n defaultValue: true,\n admin: {\n description: {\n en: 'Let visitors switch modes',\n cs: 'Umožnit návštěvníkům přepínat režimy',\n },\n width: '50%',\n },\n },\n ],\n },\n lightModeField,\n darkModeField,\n ],\n })\n }\n\n // Typography - Always visible\n fields.push({\n type: 'collapsible',\n label: {\n en: '🅰️ Typography',\n cs: '🅰️ Typografie',\n },\n admin: {\n initCollapsed: true,\n description: {\n en: 'Choose font families and base typography settings. Leave fields on \"Use preset\" to inherit the theme defaults.',\n cs: 'Zvolte rodiny písem a základní typografické nastavení. Hodnota \"Použít výchozí\" zachová nastavení motivu.',\n },\n },\n fields: [\n {\n name: 'typography',\n type: 'group',\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFont',\n type: 'select',\n label: {\n en: 'Body font',\n cs: 'Písmo pro text',\n },\n defaultValue: 'preset',\n options: [...BODY_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'body_font',\n },\n {\n name: 'headingFont',\n type: 'select',\n label: {\n en: 'Heading font',\n cs: 'Písmo pro nadpisy',\n },\n defaultValue: 'preset',\n options: [...HEADING_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'heading_font',\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFontCustom',\n type: 'text',\n label: {\n en: 'Custom body font stack',\n cs: 'Vlastní písmo pro text',\n },\n admin: {\n width: '50%',\n placeholder: '\"IBM Plex Sans\", sans-serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom font.',\n cs: 'Při volbě vlastního písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.bodyFont === 'custom',\n },\n },\n {\n name: 'headingFontCustom',\n type: 'text',\n label: {\n en: 'Custom heading font stack',\n cs: 'Vlastní písmo pro nadpisy',\n },\n admin: {\n width: '50%',\n placeholder: '\"Cormorant Garamond\", serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom heading font.',\n cs: 'Při volbě vlastního nadpisového písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.headingFont === 'custom',\n },\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'baseFontSize',\n type: 'select',\n label: {\n en: 'Base font size',\n cs: 'Základní velikost písma',\n },\n defaultValue: 'preset',\n options: BASE_FONT_SIZE_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n dbName: 'base_font_size',\n },\n {\n name: 'lineHeight',\n type: 'select',\n label: {\n en: 'Line height',\n cs: 'Řádkování',\n },\n defaultValue: 'preset',\n options: LINE_HEIGHT_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n dbName: 'line_height',\n },\n ],\n },\n ],\n },\n ],\n })\n\n const customPresetsField: Field = {\n type: 'collapsible',\n label: {\n en: '🧩 Custom Theme Presets',\n cs: '🧩 Vlastní motivy',\n },\n admin: {\n initCollapsed: true,\n description: {\n en: 'Import additional theme presets via JSON to extend or override the defaults. These presets become available instantly in the Theme Selection above.',\n cs: 'Importujte další motivy pomocí JSONu a rozšiřte nebo přepište výchozí nabídku. Motivy budou ihned k dispozici ve výběru výše.',\n },\n },\n fields: [\n {\n name: 'customThemePresets',\n type: 'json',\n label: {\n en: 'Custom Preset JSON',\n cs: 'JSON s vlastními motivy',\n },\n admin: {\n description: {\n en: 'Paste or import an array (or object map) of theme presets. Each entry should include at least a unique \"name\" and \"label\" with optional lightMode/darkMode colors.',\n cs: 'Vložte nebo importujte pole (či objekt) motivů. Každý motiv musí mít unikátní „name“ a „label“ a volitelně barvy pro světly/tmavý režim.',\n },\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/ThemePresetImportField',\n },\n },\n },\n ],\n }\n\n fields.push(customPresetsField)\n\n // Advanced Settings (when using extended theme configuration)\n if (enableAdvancedFeatures) {\n const advancedFields: Field[] = [\n {\n name: 'animationLevel',\n type: 'select',\n label: {\n en: 'Animation Level',\n cs: 'Úroveň animací',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None', cs: 'Žádné' }, value: 'none' },\n { label: { en: 'Reduced', cs: 'Snížené' }, value: 'reduced' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'High', cs: 'Vysoké' }, value: 'high' },\n ],\n admin: {\n description: {\n en: 'Control website animations and transitions',\n cs: 'Kontrola animací a přechodů na webu',\n },\n width: '50%',\n },\n dbName: 'animation_level',\n },\n ]\n\n if (includeCustomCSS) {\n advancedFields.push({\n name: 'customCSS',\n type: 'code',\n label: {\n en: 'Custom CSS',\n cs: 'Vlastní CSS',\n },\n admin: {\n language: 'css',\n description: {\n en: 'Add custom CSS to override default styles',\n cs: 'Přidejte vlastní CSS pro přepsání výchozích stylů',\n },\n // placeholder:\n // ':root {\\n --primary: #your-color;\\n --border-radius: 8px;\\n --font-family: \"Your Font\";\\n}\\n\\n.custom-class {\\n /* Your custom styles */\\n}',\n // rows: 8,\n },\n })\n }\n\n fields.push({\n type: 'collapsible',\n label: {\n en: '⚙️ Advanced Settings',\n cs: '⚙️ Pokročilé nastavení',\n },\n admin: {\n initCollapsed: true,\n },\n fields: advancedFields,\n })\n }\n\n // Return as tab configuration to be injected into tabs structure\n return {\n label: {\n en: '🎨 Appearance Settings',\n cs: '🎨 Nastavení vzhledu',\n },\n fields: [\n {\n name: 'themeConfiguration',\n type: 'group',\n admin: {\n description: {\n en: 'Configure website appearance and styling',\n cs: 'Nakonfigurujte vzhled a stylování webu',\n },\n },\n fields,\n },\n ],\n }\n}\n"],"names":["BASE_FONT_SIZE_OPTIONS","BODY_FONT_OPTIONS","HEADING_FONT_OPTIONS","LINE_HEIGHT_OPTIONS","darkModeField","lightModeField","createThemeConfigurationField","options","themePresets","defaultTheme","includeColorModeToggle","includeCustomCSS","enableAdvancedFeatures","fields","name","type","required","defaultValue","label","en","cs","validate","value","trim","length","admin","description","components","Field","custom","push","width","dbName","initCollapsed","placeholder","condition","_","siblingData","bodyFont","headingFont","map","option","customPresetsField","advancedFields","language"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/fields/themeConfigurationField.ts"],"sourcesContent":["import type { Field, TabsField } from 'payload'\nimport {\n BASE_FONT_SIZE_OPTIONS,\n BODY_FONT_OPTIONS,\n HEADING_FONT_OPTIONS,\n LINE_HEIGHT_OPTIONS,\n} from '../constants/themeFonts.js'\nimport type { ThemePreset } from '../index.js'\nimport { darkModeField, lightModeField } from './colorModeFields.js'\n\ninterface ThemeConfigurationFieldOptions {\n themePresets: ThemePreset[]\n defaultTheme: string\n includeColorModeToggle: boolean\n includeCustomCSS: boolean\n includeBrandIdentity: boolean\n enableAdvancedFeatures?: boolean\n useThemePreviewField?: boolean\n}\n\n// Type for admin.custom property to avoid ts-expect-error\ninterface ThemeAdminCustom {\n themePresets?: ThemePreset[]\n useThemePreviewField?: boolean\n}\n\n// Type for a single tab (not the full TabsField)\nexport type ThemeTab = NonNullable<TabsField['tabs']>[number]\n\n/**\n * Theme Configuration Field with Theme Selection as default value setter\n * Theme Selection populates lightMode/darkMode colors automatically\n */\nexport function createThemeConfigurationField(options: ThemeConfigurationFieldOptions): ThemeTab {\n const {\n themePresets,\n defaultTheme,\n includeColorModeToggle,\n includeCustomCSS,\n enableAdvancedFeatures = true,\n useThemePreviewField = true,\n } = options\n\n const fields: Field[] = [\n {\n name: 'theme',\n type: 'text',\n required: true,\n defaultValue: defaultTheme,\n label: {\n en: '🎨 Theme Selection',\n cs: '🎨 Výběr tématu',\n },\n validate: (value: unknown) => {\n if (typeof value === 'string' && value.trim().length > 0) {\n return true\n }\n return 'Select a theme'\n },\n admin: {\n description: {\n en: 'Select a theme to auto-populate color values. You can customize colors after selection.',\n cs: 'Vyberte téma pro automatické předvyplnění barev. Po výběru můžete barvy upravit.',\n },\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/ThemePreviewField',\n },\n custom: {\n themePresets,\n useThemePreviewField,\n } as ThemeAdminCustom,\n },\n },\n ]\n\n // Border Radius Configuration - Always visible\n fields.push({\n type: 'row',\n fields: [\n {\n name: 'borderRadius',\n type: 'select',\n label: {\n en: '📐 Border Radius',\n cs: '📐 Zaoblení rohů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None (0px)', cs: 'Žádné (0px)' }, value: 'none' },\n { label: { en: 'Small (0.25rem)', cs: 'Malé (0.25rem)' }, value: 'small' },\n { label: { en: 'Medium (0.5rem)', cs: 'Střední (0.5rem)' }, value: 'medium' },\n { label: { en: 'Large (0.75rem)', cs: 'Velké (0.75rem)' }, value: 'large' },\n { label: { en: 'XL (1rem)', cs: 'XL (1rem)' }, value: 'xl' },\n ],\n admin: {\n description: {\n en: 'Default corner rounding for UI elements',\n cs: 'Výchozí zaoblení rohů pro UI prvky',\n },\n width: '50%',\n },\n dbName: 'border_radius',\n },\n {\n name: 'spacing',\n type: 'select',\n label: {\n en: '📏 Spacing Scale',\n cs: '📏 Škála rozestupů',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'Compact', cs: 'Kompaktní' }, value: 'compact' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'Comfortable', cs: 'Pohodlné' }, value: 'comfortable' },\n ],\n admin: {\n description: {\n en: 'Global spacing between elements',\n cs: 'Globální rozestupy mezi prvky',\n },\n width: '50%',\n },\n dbName: 'spacing',\n },\n ],\n })\n\n // Color Mode Settings\n if (includeColorModeToggle) {\n fields.push({\n type: 'collapsible',\n label: {\n en: '🌗 Color Mode Settings',\n cs: '🌗 Nastavení barevného režimu',\n },\n admin: {\n initCollapsed: false,\n description: {\n en: 'Configure light and dark mode colors. Changes here override theme selection.',\n cs: 'Nakonfigurujte barvy světlého a tmavého režimu. Změny zde přepíší výběr tématu.',\n },\n },\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'colorMode',\n type: 'select',\n label: {\n en: 'Default Color Mode',\n cs: 'Výchozí barevný režim',\n },\n defaultValue: 'auto',\n options: [\n {\n label: {\n en: 'Auto (System)',\n cs: 'Automaticky (Systém)',\n },\n value: 'auto',\n },\n {\n label: { en: 'Light', cs: 'Světlý' },\n value: 'light',\n },\n {\n label: { en: 'Dark', cs: 'Tmavý' },\n value: 'dark',\n },\n ],\n admin: {\n description: {\n en: 'Default color mode for visitors',\n cs: 'Výchozí barevný režim pro návštěvníky',\n },\n width: '50%',\n },\n dbName: 'color_mode',\n },\n {\n name: 'allowColorModeToggle',\n type: 'checkbox',\n label: {\n en: 'Enable Mode Toggle',\n cs: 'Povolit přepínač režimu',\n },\n defaultValue: true,\n admin: {\n description: {\n en: 'Let visitors switch modes',\n cs: 'Umožnit návštěvníkům přepínat režimy',\n },\n width: '50%',\n },\n },\n ],\n },\n lightModeField,\n darkModeField,\n ],\n })\n }\n\n // Typography - Always visible\n fields.push({\n type: 'collapsible',\n label: {\n en: '🅰️ Typography',\n cs: '🅰️ Typografie',\n },\n admin: {\n initCollapsed: true,\n description: {\n en: 'Choose font families and base typography settings. Leave fields on \"Use preset\" to inherit the theme defaults.',\n cs: 'Zvolte rodiny písem a základní typografické nastavení. Hodnota \"Použít výchozí\" zachová nastavení motivu.',\n },\n },\n fields: [\n {\n name: 'typography',\n type: 'group',\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFont',\n type: 'select',\n label: {\n en: 'Body font',\n cs: 'Písmo pro text',\n },\n defaultValue: 'preset',\n options: [...BODY_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'body_font',\n },\n {\n name: 'headingFont',\n type: 'select',\n label: {\n en: 'Heading font',\n cs: 'Písmo pro nadpisy',\n },\n defaultValue: 'preset',\n options: [...HEADING_FONT_OPTIONS],\n admin: {\n width: '50%',\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/FontSelectField',\n },\n },\n dbName: 'heading_font',\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'bodyFontCustom',\n type: 'text',\n label: {\n en: 'Custom body font stack',\n cs: 'Vlastní písmo pro text',\n },\n admin: {\n width: '50%',\n placeholder: '\"IBM Plex Sans\", sans-serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom font.',\n cs: 'Při volbě vlastního písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.bodyFont === 'custom',\n },\n },\n {\n name: 'headingFontCustom',\n type: 'text',\n label: {\n en: 'Custom heading font stack',\n cs: 'Vlastní písmo pro nadpisy',\n },\n admin: {\n width: '50%',\n placeholder: '\"Cormorant Garamond\", serif',\n description: {\n en: 'Provide full CSS font-family stack when using a custom heading font.',\n cs: 'Při volbě vlastního nadpisového písma zadejte celý CSS zápis font-family.',\n },\n condition: (_, siblingData) => siblingData?.headingFont === 'custom',\n },\n },\n ],\n },\n {\n type: 'row',\n fields: [\n {\n name: 'baseFontSize',\n type: 'select',\n label: {\n en: 'Base font size',\n cs: 'Základní velikost písma',\n },\n defaultValue: 'preset',\n options: BASE_FONT_SIZE_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n dbName: 'base_font_size',\n },\n {\n name: 'lineHeight',\n type: 'select',\n label: {\n en: 'Line height',\n cs: 'Řádkování',\n },\n defaultValue: 'preset',\n options: LINE_HEIGHT_OPTIONS.map((option) => ({\n label: option.label,\n value: option.value,\n })),\n admin: {\n width: '50%',\n },\n dbName: 'line_height',\n },\n ],\n },\n ],\n },\n ],\n })\n\n const customPresetsField: Field = {\n type: 'collapsible',\n label: {\n en: '🧩 Custom Theme Presets',\n cs: '🧩 Vlastní motivy',\n },\n admin: {\n initCollapsed: true,\n description: {\n en: 'Import additional theme presets via JSON to extend or override the defaults. These presets become available instantly in the Theme Selection above.',\n cs: 'Importujte další motivy pomocí JSONu a rozšiřte nebo přepište výchozí nabídku. Motivy budou ihned k dispozici ve výběru výše.',\n },\n },\n fields: [\n {\n name: 'customThemePresets',\n type: 'json',\n label: {\n en: 'Custom Preset JSON',\n cs: 'JSON s vlastními motivy',\n },\n admin: {\n description: {\n en: 'Paste or import an array (or object map) of theme presets. Each entry should include at least a unique \"name\" and \"label\" with optional lightMode/darkMode colors.',\n cs: 'Vložte nebo importujte pole (či objekt) motivů. Každý motiv musí mít unikátní „name“ a „label“ a volitelně barvy pro světly/tmavý režim.',\n },\n components: {\n Field: '@kilivi/payloadcms-theme-management/fields/ThemePresetImportField',\n },\n },\n },\n ],\n }\n\n fields.push(customPresetsField)\n\n // Advanced Settings (when using extended theme configuration)\n if (enableAdvancedFeatures) {\n const advancedFields: Field[] = [\n {\n name: 'animationLevel',\n type: 'select',\n label: {\n en: 'Animation Level',\n cs: 'Úroveň animací',\n },\n defaultValue: 'medium',\n options: [\n { label: { en: 'None', cs: 'Žádné' }, value: 'none' },\n { label: { en: 'Reduced', cs: 'Snížené' }, value: 'reduced' },\n { label: { en: 'Medium', cs: 'Střední' }, value: 'medium' },\n { label: { en: 'High', cs: 'Vysoké' }, value: 'high' },\n ],\n admin: {\n description: {\n en: 'Control website animations and transitions',\n cs: 'Kontrola animací a přechodů na webu',\n },\n width: '50%',\n },\n dbName: 'animation_level',\n },\n ]\n\n if (includeCustomCSS) {\n advancedFields.push({\n name: 'customCSS',\n type: 'code',\n label: {\n en: 'Custom CSS',\n cs: 'Vlastní CSS',\n },\n admin: {\n language: 'css',\n description: {\n en: 'Add custom CSS to override default styles',\n cs: 'Přidejte vlastní CSS pro přepsání výchozích stylů',\n },\n // placeholder:\n // ':root {\\n --primary: #your-color;\\n --border-radius: 8px;\\n --font-family: \"Your Font\";\\n}\\n\\n.custom-class {\\n /* Your custom styles */\\n}',\n // rows: 8,\n },\n })\n }\n\n fields.push({\n type: 'collapsible',\n label: {\n en: '⚙️ Advanced Settings',\n cs: '⚙️ Pokročilé nastavení',\n },\n admin: {\n initCollapsed: true,\n },\n fields: advancedFields,\n })\n }\n\n // Return as tab configuration to be injected into tabs structure\n return {\n label: {\n en: '🎨 Appearance Settings',\n cs: '🎨 Nastavení vzhledu',\n },\n fields: [\n {\n name: 'themeConfiguration',\n type: 'group',\n admin: {\n description: {\n en: 'Configure website appearance and styling',\n cs: 'Nakonfigurujte vzhled a stylování webu',\n },\n },\n fields,\n },\n ],\n }\n}\n"],"names":["BASE_FONT_SIZE_OPTIONS","BODY_FONT_OPTIONS","HEADING_FONT_OPTIONS","LINE_HEIGHT_OPTIONS","darkModeField","lightModeField","createThemeConfigurationField","options","themePresets","defaultTheme","includeColorModeToggle","includeCustomCSS","enableAdvancedFeatures","useThemePreviewField","fields","name","type","required","defaultValue","label","en","cs","validate","value","trim","length","admin","description","components","Field","custom","push","width","dbName","initCollapsed","placeholder","condition","_","siblingData","bodyFont","headingFont","map","option","customPresetsField","advancedFields","language"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export type { ThemeTab } from './fields/themeConfigurationField.js';
13
13
  export { extendedThemePresets, allExtendedThemePresets, extendedThemeToCSSVariables, } from './extended-presets.js';
14
14
  export type { ExtendedThemePreset, ShadcnColorTokens } from './extended-presets.js';
15
15
  export { applyExtendedTheme, generateExtendedThemeCSS, resetExtendedTheme, getExtendedThemeTokens, createUseExtendedTheme, getTailwindVarReferences, } from './utils/extendedThemeHelpers.js';
16
- export type { ThemeManagementPluginOptions, FetchThemeConfigurationOptions } from './types.js';
16
+ export type { ThemeManagementPluginOptions, ThemeManagementCacheRevalidationOptions, FetchThemeConfigurationOptions, ThemeManagementLivePreviewOptions, ThemeManagementLivePreviewUrlArgs, } from './types.js';
17
17
  export { DEFAULT_THEME_CONFIGURATION, resolveThemeConfiguration, } from './utils/resolveThemeConfiguration.js';
18
18
  export type { ResolvedThemeConfiguration } from './utils/resolveThemeConfiguration.js';
19
19
  export { generateThemeColorsCss, hexToHsl } from './utils/themeColors.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiD,MAAM,EAAa,MAAM,SAAS,CAAA;AAG/F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,KAAK,EAAE,8BAA8B,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAA;AAqG9F,eAAO,MAAM,qBAAqB,GAAI,UAAS,4BAAiC,KAAG,MAsMlF,CAAA;AAED,KAAK,iBAAiB,GAAG,MAAM,GAAG,8BAA8B,CAAA;AAEhE,eAAO,MAAM,uBAAuB,GAClC,UAAU,iBAAiB,KAC1B,OAAO,CAAC,sBAAsB,GAAG,IAAI,CA6EvC,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,KAAG,WAAW,GAAG,IAEhE,CAAA;AAED,eAAO,MAAM,wBAAwB,QAAO,WAAW,EAEtD,CAAA;AAED,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnE,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACtE,YAAY,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACnF,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EAAE,4BAA4B,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAA;AAC9F,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,sCAAsC,CAAA;AAC7C,YAAY,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAA;AACtF,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACzE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAGvE,OAAO,EACL,gBAAgB,IAAI,iBAAiB,EACrC,oBAAoB,GACrB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAEhF,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG1D,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,4BAA4B,CAAA;AAMnC,eAAO,MAAM,qBAAqB,aArVa,4BAA4B,KAAQ,MAqVzB,CAAA;AAC1D,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,aAvVU,4BAA4B,KAAQ,MAuVtB,CAAA;AAG7D,OAAO,EACL,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAE1B,eAAe,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiD,MAAM,EAAa,MAAM,SAAS,CAAA;AAG/F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/C,OAAO,KAAK,EACV,8BAA8B,EAG9B,4BAA4B,EAC7B,MAAM,YAAY,CAAA;AA2hBnB,eAAO,MAAM,qBAAqB,GAAI,UAAS,4BAAiC,KAAG,MA+NlF,CAAA;AAED,KAAK,iBAAiB,GAAG,MAAM,GAAG,8BAA8B,CAAA;AAEhE,eAAO,MAAM,uBAAuB,GAClC,UAAU,iBAAiB,KAC1B,OAAO,CAAC,sBAAsB,GAAG,IAAI,CA6EvC,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,KAAG,WAAW,GAAG,IAEhE,CAAA;AAED,eAAO,MAAM,wBAAwB,QAAO,WAAW,EAEtD,CAAA;AAED,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnE,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACtE,YAAY,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACnF,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,4BAA4B,EAC5B,uCAAuC,EACvC,8BAA8B,EAC9B,iCAAiC,EACjC,iCAAiC,GAClC,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,sCAAsC,CAAA;AAC7C,YAAY,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAA;AACtF,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACzE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAGvE,OAAO,EACL,gBAAgB,IAAI,iBAAiB,EACrC,oBAAoB,GACrB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAEhF,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG1D,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,4BAA4B,CAAA;AAMnC,eAAO,MAAM,qBAAqB,aApXa,4BAA4B,KAAQ,MAoXzB,CAAA;AAC1D,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,aAtXU,4BAA4B,KAAQ,MAsXtB,CAAA;AAG7D,OAAO,EACL,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAE1B,eAAe,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -79,10 +79,313 @@ const THEME_FIELD_NAME = 'themeConfiguration';
79
79
  return sanitizedFields.map((field)=>field.type === 'tabs' ? updatedTabsField : field);
80
80
  };
81
81
  const ensureCollectionsArray = (collections)=>Array.isArray(collections) ? collections : [];
82
+ const ensureEndpointsArray = (endpoints)=>Array.isArray(endpoints) ? endpoints : [];
83
+ const normalizeLivePreviewOptions = (livePreview)=>{
84
+ if (livePreview === false) {
85
+ return {
86
+ enabled: false,
87
+ pageCollection: 'pages',
88
+ pageSlug: 'home',
89
+ fallbackToFirstPage: true,
90
+ tenantField: 'tenant',
91
+ tenantQueryParam: 'tenant',
92
+ breakpoints: undefined
93
+ };
94
+ }
95
+ const raw = typeof livePreview === 'object' && livePreview ? livePreview : {};
96
+ return {
97
+ enabled: raw.enabled ?? true,
98
+ pageCollection: raw.pageCollection ?? 'pages',
99
+ pageSlug: raw.pageSlug ?? 'home',
100
+ fallbackToFirstPage: raw.fallbackToFirstPage ?? true,
101
+ tenantField: raw.tenantField ?? 'tenant',
102
+ tenantQueryParam: raw.tenantQueryParam ?? 'tenant',
103
+ breakpoints: raw.breakpoints,
104
+ url: raw.url
105
+ };
106
+ };
107
+ const dedupeStrings = (values)=>[
108
+ ...new Set(values.filter((value)=>typeof value === 'string' && value.trim().length > 0))
109
+ ];
110
+ const normalizeRoutePath = (path)=>{
111
+ if (!path || !path.trim()) {
112
+ return '/theme/revalidate';
113
+ }
114
+ return path.startsWith('/') ? path : `/${path}`;
115
+ };
116
+ const normalizeCacheRevalidationOptions = (args)=>{
117
+ const { cacheRevalidation, standaloneCollectionSlug, useStandaloneCollection } = args;
118
+ if (cacheRevalidation === false) {
119
+ return {
120
+ enabled: false,
121
+ injectRoute: false,
122
+ routePath: '/theme/revalidate',
123
+ tags: [],
124
+ paths: []
125
+ };
126
+ }
127
+ const raw = typeof cacheRevalidation === 'object' && cacheRevalidation ? cacheRevalidation : {};
128
+ const defaultTag = `global_${standaloneCollectionSlug}`;
129
+ return {
130
+ enabled: raw.enabled ?? useStandaloneCollection,
131
+ injectRoute: raw.injectRoute ?? true,
132
+ routePath: normalizeRoutePath(raw.routePath),
133
+ secret: raw.secret,
134
+ tags: dedupeStrings([
135
+ defaultTag,
136
+ ...raw.tags ?? []
137
+ ]),
138
+ paths: dedupeStrings(raw.paths ?? [])
139
+ };
140
+ };
141
+ const asRecord = (value)=>{
142
+ if (value && typeof value === 'object') {
143
+ return value;
144
+ }
145
+ return undefined;
146
+ };
147
+ const pickString = (value)=>{
148
+ if (typeof value === 'string' && value.trim()) return value;
149
+ return undefined;
150
+ };
151
+ const extractTenantValue = (value)=>{
152
+ if (!value) return undefined;
153
+ if (typeof value === 'string') return pickString(value);
154
+ const tenantRecord = asRecord(value);
155
+ if (!tenantRecord) return undefined;
156
+ return pickString(tenantRecord.id) ?? pickString(tenantRecord.slug) ?? pickString(tenantRecord.value) ?? pickString(tenantRecord.tenant);
157
+ };
158
+ const resolveTenantSlug = (req, tenantField, data)=>{
159
+ const fromData = extractTenantValue(data?.[tenantField]);
160
+ if (fromData) return fromData;
161
+ const query = req.query;
162
+ if (query && typeof URLSearchParams !== 'undefined' && query instanceof URLSearchParams) {
163
+ return pickString(query.get('tenant')) ?? pickString(query.get('tenantSlug')) ?? pickString(query.get('tenantId'));
164
+ }
165
+ const queryRecord = asRecord(query);
166
+ const fromQuery = extractTenantValue(queryRecord?.tenant) ?? extractTenantValue(queryRecord?.tenantSlug) ?? extractTenantValue(queryRecord?.tenantId);
167
+ if (fromQuery) return fromQuery;
168
+ const userRecord = asRecord(req.user);
169
+ const fromUser = extractTenantValue(userRecord?.[tenantField]) ?? extractTenantValue(userRecord?.tenant) ?? extractTenantValue(userRecord?.tenantSlug);
170
+ if (fromUser) return fromUser;
171
+ const headers = req.headers;
172
+ if (headers && typeof Headers !== 'undefined' && headers instanceof Headers) {
173
+ return pickString(headers.get('x-tenant-id')) ?? pickString(headers.get('x-tenant')) ?? pickString(headers.get('payload-tenant'));
174
+ }
175
+ const headerRecord = asRecord(headers);
176
+ return extractTenantValue(headerRecord?.['x-tenant-id']) ?? extractTenantValue(headerRecord?.['x-tenant']) ?? extractTenantValue(headerRecord?.['payload-tenant']);
177
+ };
178
+ const buildDefaultPreviewPath = (pageSlug, tenantSlug, tenantQueryParam)=>{
179
+ const normalizedSlug = pageSlug === 'home' ? '' : pageSlug;
180
+ const basePath = normalizedSlug ? `/${normalizedSlug}` : '/';
181
+ if (!tenantSlug) {
182
+ return basePath;
183
+ }
184
+ const separator = basePath.includes('?') ? '&' : '?';
185
+ return `${basePath}${separator}${tenantQueryParam}=${encodeURIComponent(tenantSlug)}`;
186
+ };
187
+ const resolveLivePreviewUrl = async (args)=>{
188
+ const { req, data, livePreview, enableLogging } = args;
189
+ const tenantSlug = resolveTenantSlug(req, livePreview.tenantField, data);
190
+ const tenantWhere = tenantSlug ? {
191
+ [livePreview.tenantField]: {
192
+ equals: tenantSlug
193
+ }
194
+ } : undefined;
195
+ const homeWhere = {
196
+ and: [
197
+ {
198
+ slug: {
199
+ equals: livePreview.pageSlug
200
+ }
201
+ },
202
+ ...tenantWhere ? [
203
+ tenantWhere
204
+ ] : []
205
+ ]
206
+ };
207
+ try {
208
+ const preferredPageResponse = await req.payload.find({
209
+ collection: livePreview.pageCollection,
210
+ where: homeWhere,
211
+ depth: 0,
212
+ draft: true,
213
+ limit: 1,
214
+ overrideAccess: true,
215
+ pagination: false
216
+ });
217
+ let page = preferredPageResponse?.docs?.[0];
218
+ if (!page && livePreview.fallbackToFirstPage) {
219
+ const fallbackResponse = await req.payload.find({
220
+ collection: livePreview.pageCollection,
221
+ where: tenantWhere,
222
+ depth: 0,
223
+ draft: true,
224
+ limit: 1,
225
+ overrideAccess: true,
226
+ pagination: false
227
+ });
228
+ page = fallbackResponse?.docs?.[0];
229
+ }
230
+ const pageRecord = asRecord(page);
231
+ const resolvedPageSlug = pickString(pageRecord?.slug) ?? livePreview.pageSlug;
232
+ if (livePreview.url) {
233
+ return await livePreview.url({
234
+ page: pageRecord,
235
+ pageSlug: resolvedPageSlug,
236
+ tenantSlug,
237
+ req
238
+ });
239
+ }
240
+ return buildDefaultPreviewPath(resolvedPageSlug, tenantSlug, livePreview.tenantQueryParam);
241
+ } catch (error) {
242
+ if (enableLogging) {
243
+ req.payload.logger?.warn?.('🎨 Theme Management Plugin: live preview URL fallback used', error);
244
+ }
245
+ return buildDefaultPreviewPath(livePreview.pageSlug, tenantSlug, livePreview.tenantQueryParam);
246
+ }
247
+ };
248
+ const createPreviewAdminConfig = (normalizedLivePreview, enableLogging)=>{
249
+ if (!normalizedLivePreview.enabled) {
250
+ return {};
251
+ }
252
+ return {
253
+ livePreview: {
254
+ breakpoints: normalizedLivePreview.breakpoints,
255
+ url: ({ data, req })=>resolveLivePreviewUrl({
256
+ req: req,
257
+ data: asRecord(data),
258
+ livePreview: normalizedLivePreview,
259
+ enableLogging
260
+ })
261
+ },
262
+ preview: (data, options)=>{
263
+ const req = options?.req;
264
+ if (!req) return null;
265
+ return resolveLivePreviewUrl({
266
+ req: req,
267
+ data,
268
+ livePreview: normalizedLivePreview,
269
+ enableLogging
270
+ });
271
+ }
272
+ };
273
+ };
274
+ const readSecretFromRequest = async (req)=>{
275
+ const requestRecord = asRecord(req);
276
+ const headersValue = requestRecord?.headers;
277
+ if (headersValue && typeof Headers !== 'undefined' && headersValue instanceof Headers) {
278
+ return pickString(headersValue.get('x-theme-revalidate-secret')) ?? pickString(headersValue.get('x-revalidate-secret'));
279
+ }
280
+ const headerRecord = asRecord(headersValue);
281
+ const fromHeaders = pickString(headerRecord?.['x-theme-revalidate-secret']) ?? pickString(headerRecord?.['x-revalidate-secret']);
282
+ if (fromHeaders) {
283
+ return fromHeaders;
284
+ }
285
+ const requestWithJson = req;
286
+ const body = typeof requestWithJson?.json === 'function' ? await requestWithJson.json() : undefined;
287
+ const bodyRecord = asRecord(body);
288
+ const fromBody = pickString(bodyRecord?.secret);
289
+ if (fromBody) {
290
+ return fromBody;
291
+ }
292
+ const queryRecord = asRecord(requestRecord?.query);
293
+ return pickString(queryRecord?.secret);
294
+ };
295
+ const executeThemeRevalidation = async (options)=>{
296
+ const { tags, paths, enableLogging, logger } = options;
297
+ try {
298
+ const { revalidatePath, revalidateTag } = await import('next/cache');
299
+ tags.forEach((tag)=>revalidateTag(tag));
300
+ paths.forEach((path)=>revalidatePath(path));
301
+ if (enableLogging) {
302
+ logger?.info?.(`🎨 Theme Management Plugin: revalidated cache tags=[${tags.join(', ')}] paths=[${paths.join(', ')}]`);
303
+ }
304
+ } catch (error) {
305
+ if (enableLogging) {
306
+ logger?.error?.('🎨 Theme Management Plugin: cache invalidation failed:', error);
307
+ }
308
+ throw error;
309
+ }
310
+ return {
311
+ tags,
312
+ paths
313
+ };
314
+ };
315
+ const createCacheRevalidationEndpoint = (options)=>{
316
+ const { cacheRevalidation, enableLogging } = options;
317
+ if (!cacheRevalidation.enabled || !cacheRevalidation.injectRoute) {
318
+ return null;
319
+ }
320
+ return {
321
+ path: cacheRevalidation.routePath,
322
+ method: 'post',
323
+ handler: async (req)=>{
324
+ const reqRecord = asRecord(req);
325
+ const payload = asRecord(reqRecord?.payload);
326
+ const logger = payload?.logger;
327
+ if (cacheRevalidation.secret) {
328
+ const providedSecret = await readSecretFromRequest(req);
329
+ if (providedSecret !== cacheRevalidation.secret) {
330
+ return jsonResponse({
331
+ error: 'Unauthorized'
332
+ }, 401);
333
+ }
334
+ }
335
+ try {
336
+ const result = await executeThemeRevalidation({
337
+ tags: cacheRevalidation.tags,
338
+ paths: cacheRevalidation.paths,
339
+ enableLogging,
340
+ logger
341
+ });
342
+ return jsonResponse({
343
+ ok: true,
344
+ revalidated: result
345
+ });
346
+ } catch {
347
+ return jsonResponse({
348
+ ok: false,
349
+ error: 'Revalidation failed'
350
+ }, 500);
351
+ }
352
+ }
353
+ };
354
+ };
355
+ const mergeEndpoint = (endpoints, endpoint)=>{
356
+ if (!endpoint) {
357
+ return endpoints;
358
+ }
359
+ const withoutExisting = endpoints.filter((candidate)=>{
360
+ const record = candidate;
361
+ return !(record?.path === endpoint.path && typeof record.method === 'string' && record.method.toLowerCase() === endpoint.method);
362
+ });
363
+ return [
364
+ ...withoutExisting,
365
+ endpoint
366
+ ];
367
+ };
368
+ const jsonResponse = (body, status = 200)=>{
369
+ if (typeof Response !== 'undefined' && typeof Response.json === 'function') {
370
+ return Response.json(body, {
371
+ status
372
+ });
373
+ }
374
+ return {
375
+ status,
376
+ json: async ()=>body
377
+ };
378
+ };
82
379
  export const themeManagementPlugin = (options = {})=>{
83
380
  return (config)=>{
84
381
  const { enabled = true, targetCollection = 'site-settings', useStandaloneCollection = false, standaloneCollectionSlug = 'appearance-settings', // Allow either string or localized object; default comes from plugin translations
85
- standaloneCollectionLabel = getTranslations().standaloneCollectionLabel, themePresets = allThemePresets, defaultTheme = 'cool', includeColorModeToggle = true, includeCustomCSS = true, includeBrandIdentity = false, enableAdvancedFeatures = true, enableLogging = false } = options;
382
+ standaloneCollectionLabel = getTranslations().standaloneCollectionLabel, themePresets = allThemePresets, defaultTheme = 'cool', includeColorModeToggle = true, includeCustomCSS = true, includeBrandIdentity = false, enableAdvancedFeatures = true, enableLogging = false, livePreview = true, cacheRevalidation } = options;
383
+ const normalizedLivePreview = normalizeLivePreviewOptions(livePreview);
384
+ const normalizedCacheRevalidation = normalizeCacheRevalidationOptions({
385
+ cacheRevalidation,
386
+ standaloneCollectionSlug,
387
+ useStandaloneCollection
388
+ });
86
389
  if (!enabled) {
87
390
  if (enableLogging) {
88
391
  console.log('🎨 Theme Management Plugin: disabled via options, skipping.');
@@ -95,7 +398,8 @@ export const themeManagementPlugin = (options = {})=>{
95
398
  includeColorModeToggle,
96
399
  includeCustomCSS,
97
400
  includeBrandIdentity: includeBrandIdentity ?? false,
98
- enableAdvancedFeatures
401
+ enableAdvancedFeatures,
402
+ useThemePreviewField: !normalizedLivePreview.enabled
99
403
  });
100
404
  // If using standalone collection, create a new global
101
405
  if (useStandaloneCollection) {
@@ -139,7 +443,8 @@ export const themeManagementPlugin = (options = {})=>{
139
443
  slug: standaloneCollectionSlug,
140
444
  label: standaloneCollectionLabel,
141
445
  admin: {
142
- group: 'Settings'
446
+ group: 'Settings',
447
+ ...createPreviewAdminConfig(normalizedLivePreview, enableLogging)
143
448
  },
144
449
  // Provide a broad access object but cast to GlobalConfig['access'] to satisfy types
145
450
  access: (()=>{
@@ -159,19 +464,16 @@ export const themeManagementPlugin = (options = {})=>{
159
464
  afterChange: [
160
465
  async ({ doc, req })=>{
161
466
  // Invalidate cache after appearance settings change
162
- if (!req?.context?.disableRevalidate) {
467
+ if (!req?.context?.disableRevalidate && normalizedCacheRevalidation.enabled) {
163
468
  try {
164
- const cacheTag = `global_${standaloneCollectionSlug}`;
165
- // Import revalidateTag dynamically to avoid issues in non-Next.js environments
166
- const { revalidateTag } = await import('next/cache');
167
- revalidateTag(cacheTag);
168
- if (enableLogging) {
169
- req.payload.logger.info(`🎨 Theme Management Plugin: cache invalidated for tag "${cacheTag}"`);
170
- }
171
- } catch (error) {
172
- if (enableLogging) {
173
- req.payload.logger.error('🎨 Theme Management Plugin: cache invalidation failed:', error);
174
- }
469
+ await executeThemeRevalidation({
470
+ tags: normalizedCacheRevalidation.tags,
471
+ paths: normalizedCacheRevalidation.paths,
472
+ enableLogging,
473
+ logger: req?.payload?.logger
474
+ });
475
+ } catch {
476
+ // no-op: logging is handled in executeThemeRevalidation
175
477
  }
176
478
  }
177
479
  return doc;
@@ -188,8 +490,15 @@ export const themeManagementPlugin = (options = {})=>{
188
490
  }
189
491
  return config;
190
492
  }
493
+ const currentEndpoints = ensureEndpointsArray(config.endpoints);
494
+ const cacheEndpoint = createCacheRevalidationEndpoint({
495
+ cacheRevalidation: normalizedCacheRevalidation,
496
+ enableLogging
497
+ });
498
+ const endpoints = mergeEndpoint(currentEndpoints, cacheEndpoint);
191
499
  return {
192
500
  ...config,
501
+ endpoints,
193
502
  globals: [
194
503
  ...globals,
195
504
  standaloneGlobal
@@ -208,9 +517,14 @@ export const themeManagementPlugin = (options = {})=>{
208
517
  }
209
518
  const existingFields = Array.isArray(collection.fields) ? collection.fields : [];
210
519
  const fields = injectThemeTab(existingFields, themeTab, enableLogging);
520
+ const admin = {
521
+ ...collection.admin,
522
+ ...createPreviewAdminConfig(normalizedLivePreview, enableLogging)
523
+ };
211
524
  return {
212
525
  ...collection,
213
- fields
526
+ fields,
527
+ admin
214
528
  };
215
529
  });
216
530
  if (!collectionTouched) {
@@ -219,8 +533,15 @@ export const themeManagementPlugin = (options = {})=>{
219
533
  }
220
534
  return config;
221
535
  }
536
+ const currentEndpoints = ensureEndpointsArray(config.endpoints);
537
+ const cacheEndpoint = createCacheRevalidationEndpoint({
538
+ cacheRevalidation: normalizedCacheRevalidation,
539
+ enableLogging
540
+ });
541
+ const endpoints = mergeEndpoint(currentEndpoints, cacheEndpoint);
222
542
  return {
223
543
  ...config,
544
+ endpoints,
224
545
  collections
225
546
  };
226
547
  };