vue-wswg-editor 0.0.10 → 0.0.12

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.
@@ -26,6 +26,8 @@ export type EditorFieldType =
26
26
  | "repeater" // ✅
27
27
  | "margin" // ✅
28
28
  | "info" // ✅
29
+ | "image" // ✅
30
+ | "object" // ✅
29
31
  | "custom"; // 🔌 (image, json, video, richtext, etc)
30
32
 
31
33
  /**
@@ -57,6 +59,8 @@ export interface EditorFieldConfig {
57
59
  // Repeater-specific properties
58
60
  repeaterFields?: Record<string, EditorFieldConfig>;
59
61
  repeaterFieldLabel?: string; // attribute key for the repeater field label
62
+ // Object-specific properties
63
+ objectFields?: Record<string, EditorFieldConfig>; // nested fields for object type
60
64
  // String length validation
61
65
  minLength?: number;
62
66
  maxLength?: number;
@@ -69,6 +73,9 @@ export interface EditorFieldConfig {
69
73
  // Image specific
70
74
  // Whether the image is responsive eg: xs, sm, md, lg, xl, primary
71
75
  responsive?: boolean;
76
+ // Range specific
77
+ // Suffix to display after the range value (e.g., "px", "%", "rem")
78
+ valueSuffix?: string;
72
79
  }
73
80
 
74
81
  /**
@@ -156,6 +163,11 @@ export const createField = {
156
163
  ...config,
157
164
  }),
158
165
 
166
+ image: (config: Partial<EditorFieldConfig> = {}): EditorFieldConfig => ({
167
+ type: "image",
168
+ ...config,
169
+ }),
170
+
159
171
  margin: (config: Partial<EditorFieldConfig> = {}): EditorFieldConfig => ({
160
172
  type: "margin",
161
173
  options: [
@@ -177,4 +189,14 @@ export const createField = {
177
189
  type: "info",
178
190
  ...config,
179
191
  }),
192
+
193
+ // Object field for nested properties (e.g., meta.description)
194
+ object: (
195
+ objectFields: Record<string, EditorFieldConfig>,
196
+ config: Partial<EditorFieldConfig> = {}
197
+ ): EditorFieldConfig => ({
198
+ type: "object",
199
+ objectFields,
200
+ ...config,
201
+ }),
180
202
  };
@@ -148,7 +148,7 @@ export function toCamelCase(input: string): string {
148
148
  if (parts.length === 0) return "";
149
149
 
150
150
  return (
151
- parts?.[0]?.toLowerCase() +
151
+ parts[0].toLowerCase() +
152
152
  parts
153
153
  .slice(1)
154
154
  .map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase())
@@ -3,14 +3,6 @@ import type { EditorFieldConfig } from "./fieldConfig";
3
3
  import type { Block } from "../types/Block";
4
4
  import type { Layout } from "../types/Layout";
5
5
  import { generateNameVariations, toCamelCase } from "./helpers";
6
- // Dynamic imports for all page builder blocks and layouts
7
- // IMPORTANT: These globs use the @page-builder alias which must be configured in the CONSUMING APP's vite.config.ts
8
- // The globs are evaluated by the consuming app's Vite build, so they resolve relative to the consuming app's project root
9
- // The consuming app should have: "@page-builder": fileURLToPath(new URL("../page-builder", import.meta.url))
10
- import { modules as layoutModules } from "vue-wswg-editor:layouts";
11
- import { modules as blockModules } from "vue-wswg-editor:blocks";
12
- import { modules as fieldModules } from "vue-wswg-editor:fields";
13
- import { modules as thumbnailModules } from "vue-wswg-editor:thumbnails";
14
6
 
15
7
  // Load all thumbnail images - Vite will process these as assets and provide URLs
16
8
  // For images, Vite returns the URL as the default export when using eager: true
@@ -22,6 +14,8 @@ import { modules as thumbnailModules } from "vue-wswg-editor:thumbnails";
22
14
  export const pageBuilderBlocks: Ref<Record<string, Block>> = shallowRef({});
23
15
  export const pageBuilderLayouts: Ref<Record<string, Layout>> = shallowRef({});
24
16
  const pageBuilderBlockFields: Ref<Record<string, any>> = ref({});
17
+ // Store thumbnail modules after lazy loading
18
+ let thumbnailModulesCache: Record<string, any> | null = null;
25
19
 
26
20
  ////////////////////////////////////////////////////////////
27
21
  // Helper Functions
@@ -55,7 +49,7 @@ function markComponentsAsRaw(obj: any): any {
55
49
  * Extract the default export from a module, handling different module formats
56
50
  * Works with both eager and lazy-loaded modules from import.meta.glob
57
51
  */
58
- function getModuleDefault(module: any): any {
52
+ export function getModuleDefault(module: any): any {
59
53
  if (!module) return undefined;
60
54
 
61
55
  // If module is a function (lazy-loaded), we'd need to await it, but with eager: true it should be resolved
@@ -98,11 +92,11 @@ export function getLayouts(): Record<string, Layout> {
98
92
  * @returns Thumbnail URL or undefined if not found
99
93
  */
100
94
  export function getBlockThumbnailUrl(directory: string | undefined): string | undefined {
101
- if (!directory) return undefined;
95
+ if (!directory || !thumbnailModulesCache) return undefined;
102
96
  // Construct the thumbnail path from the directory
103
97
  const thumbnailPath = `${directory}/thumbnail.png`;
104
98
  // Look up the thumbnail in the preloaded modules
105
- const thumbnailModule = thumbnailModules[thumbnailPath];
99
+ const thumbnailModule = thumbnailModulesCache[thumbnailPath];
106
100
  if (!thumbnailModule) return undefined;
107
101
  // For images, Vite returns an object with a default property containing the URL
108
102
  // When using eager: true, the module is already loaded
@@ -152,6 +146,9 @@ async function initialiseBlockFieldsRegistry(): Promise<void> {
152
146
  delete pageBuilderBlockFields.value[key];
153
147
  });
154
148
 
149
+ // Lazy load virtual modules to prevent initialization order issues
150
+ const { modules: fieldModules } = await import("vue-wswg-editor:fields");
151
+
155
152
  for (const [path, module] of Object.entries(fieldModules)) {
156
153
  let resolvedModule = module;
157
154
  // If module is a function (lazy-loaded), call it to get the actual module
@@ -169,19 +166,21 @@ async function initialiseLayoutRegistry(): Promise<void> {
169
166
  delete pageBuilderLayouts.value[key];
170
167
  });
171
168
 
172
- // Use layoutModules if available (from vite plugin), otherwise fall back to layoutModules
173
- const modulesToUse = layoutModules;
169
+ // Lazy load virtual modules to prevent initialization order issues
170
+ const { modules: layoutModules } = await import("vue-wswg-editor:layouts");
174
171
 
175
172
  // Handle both eager-loaded modules and lazy-loaded modules (functions)
176
- for (const [, module] of Object.entries(modulesToUse)) {
173
+ for (const [, module] of Object.entries(layoutModules)) {
177
174
  let resolvedModule = module;
178
175
  // If module is a function (lazy-loaded), call it to get the actual module
179
176
  if (typeof module === "function") {
180
177
  resolvedModule = await module();
181
178
  }
182
179
  const layout = getModuleDefault(resolvedModule);
183
- // exclude modules without name
184
- if (!layout || !layout.label) return;
180
+ // exclude modules without name or label - use continue instead of return to skip this iteration
181
+ if (!layout || !layout.label) {
182
+ continue;
183
+ }
185
184
  // Mark layout component as raw to prevent Vue from making it reactive
186
185
  pageBuilderLayouts.value[layout.__name] = markRaw(layout);
187
186
  }
@@ -193,6 +192,9 @@ async function initialiseBlockRegistry(): Promise<void> {
193
192
  delete pageBuilderBlocks.value[key];
194
193
  });
195
194
 
195
+ // Lazy load virtual modules to prevent initialization order issues
196
+ const { modules: blockModules } = await import("vue-wswg-editor:blocks");
197
+
196
198
  // Load all blocks from the glob pattern
197
199
  // With eager: true, module is the actual module object, not a loader function
198
200
  for (const [path, module] of Object.entries(blockModules)) {
@@ -220,11 +222,16 @@ async function initialiseBlockRegistry(): Promise<void> {
220
222
  }
221
223
 
222
224
  export async function initialiseRegistry(): Promise<void> {
223
- await initialiseLayoutRegistry();
224
- await initialiseBlockFieldsRegistry();
225
- await initialiseBlockRegistry();
226
- }
225
+ try {
226
+ // Lazy load thumbnail modules for getBlockThumbnailUrl
227
+ const { modules: thumbnailModules } = await import("vue-wswg-editor:thumbnails");
228
+ thumbnailModulesCache = thumbnailModules;
227
229
 
228
- // Initialise the registry when the module is loaded
229
- // Use void to handle the promise without blocking
230
- void initialiseRegistry();
230
+ await initialiseLayoutRegistry();
231
+ await initialiseBlockFieldsRegistry();
232
+ await initialiseBlockRegistry();
233
+ } catch (error) {
234
+ console.error("[vue-wswg-editor:registry] Error during registry initialization:", error);
235
+ throw error;
236
+ }
237
+ }
@@ -1,9 +1,19 @@
1
- import type { ValidatorFunction } from "./fieldConfig";
1
+ import type { EditorFieldConfig, ValidatorFunction } from "./fieldConfig";
2
2
  import { getBlockComponent, getLayoutFields } from "./registry";
3
3
  import { toNiceName } from "./helpers";
4
4
 
5
- export function validateField(value: any, validator: ValidatorFunction) {
6
- return validator(value);
5
+ export function validateField(value: any, fieldConfig: EditorFieldConfig) {
6
+ // Create generic validator from field config properties (minLength, maxLength, etc.)
7
+ const genericValidator = createGenericValidator(fieldConfig);
8
+
9
+ // Combine generic validator with custom validator if provided
10
+ const combinedValidator = combineValidators(genericValidator, fieldConfig.validator);
11
+
12
+ // If no validator exists, clear any error message
13
+ if (!combinedValidator) return true;
14
+
15
+ // Validate the value
16
+ return combinedValidator(value);
7
17
  }
8
18
 
9
19
  export interface ValidationResult {
@@ -48,18 +58,15 @@ async function validateSettings(value: any, settingsKey: string = "settings"): P
48
58
 
49
59
  if (!value[settingsKey]) return validationResult;
50
60
  const layoutOptions = getLayoutFields(value[settingsKey].layout);
51
-
52
61
  // Loop each field in the settings
53
62
  for (const field in value[settingsKey]) {
54
63
  const fieldConfig = layoutOptions[field];
55
- // If the field has a validator, validate it
56
- if (fieldConfig?.validator) {
57
- const result = await validateField(value[settingsKey][field], fieldConfig.validator);
58
- // If validation fails (returns false or a string), add to validation results
59
- if (result !== true) {
60
- validationResult.errors[field] = result;
61
- validationResult.isValid = false;
62
- }
64
+ if (!fieldConfig) continue;
65
+ const result = await validateField(value[settingsKey][field], fieldConfig);
66
+ // If validation fails (returns false or a string), add to validation results
67
+ if (result !== true) {
68
+ validationResult.errors[fieldConfig.label || field] = result;
69
+ validationResult.isValid = false;
63
70
  }
64
71
  }
65
72
 
@@ -81,7 +88,7 @@ async function validateBlocks(value: any, blocksKey: string = "blocks"): Promise
81
88
 
82
89
  // Add validation results entry for the section
83
90
  validationResults[blockType] = {
84
- title: toNiceName(blockType),
91
+ title: blockComponent.label || toNiceName(blockType),
85
92
  isValid: true,
86
93
  errors: {},
87
94
  };
@@ -94,17 +101,165 @@ async function validateBlocks(value: any, blocksKey: string = "blocks"): Promise
94
101
  // Loop each field in the block
95
102
  for (const field in blockComponent?.fields || {}) {
96
103
  const fieldConfig = blockComponent?.fields?.[field];
97
- // If the field has a validator, validate it
98
- if (fieldConfig?.validator) {
99
- const result = await validateField(block[field], fieldConfig.validator);
100
- // If validation fails (returns false or a string), add to validation results
101
- if (result !== true) {
102
- validationResults[blockType].errors[field] = result;
103
- validationResults[blockType].isValid = false;
104
- }
104
+ if (!fieldConfig) continue;
105
+ // Validate
106
+ const result = await validateField(block[field], fieldConfig);
107
+ // If validation fails (returns false or a string), add to validation results
108
+ if (result !== true) {
109
+ validationResults[blockType].errors[fieldConfig.label || field] = result;
110
+ validationResults[blockType].isValid = false;
105
111
  }
106
112
  }
107
113
  }
108
114
  // Return validation results if there are any errors, otherwise return true
109
115
  return validationResults;
110
116
  }
117
+
118
+ /**
119
+ * Creates a generic validator function based on field config properties
120
+ * Handles: required, minLength, maxLength, min, max, minItems, maxItems
121
+ * Returns a ValidatorFunction that can be combined with custom validators
122
+ */
123
+ export function createGenericValidator(fieldConfig: EditorFieldConfig): ValidatorFunction | null {
124
+ const validations: Array<(value: any) => Promise<boolean | string>> = [];
125
+
126
+ // Required validation
127
+ if (fieldConfig.required) {
128
+ validations.push(async (value: any) => {
129
+ if (value === null || value === undefined || value === "") {
130
+ return "This field is required";
131
+ }
132
+ if (Array.isArray(value) && value.length === 0) {
133
+ return "This field is required";
134
+ }
135
+ return true;
136
+ });
137
+ }
138
+
139
+ // String length validations (for text, textarea, email, url)
140
+ if (
141
+ fieldConfig.type === "text" ||
142
+ fieldConfig.type === "textarea" ||
143
+ fieldConfig.type === "email" ||
144
+ fieldConfig.type === "url"
145
+ ) {
146
+ if (fieldConfig.minLength !== undefined) {
147
+ validations.push(async (value: any) => {
148
+ if (value === null || value === undefined || value === "") return true; // Skip if empty (required handles this)
149
+ const strValue = String(value);
150
+ if (strValue.length < fieldConfig.minLength!) {
151
+ return `Must be at least ${fieldConfig.minLength} characters`;
152
+ }
153
+ return true;
154
+ });
155
+ }
156
+
157
+ if (fieldConfig.maxLength !== undefined) {
158
+ validations.push(async (value: any) => {
159
+ if (value === null || value === undefined || value === "") return true; // Skip if empty
160
+ const strValue = String(value);
161
+ if (strValue.length >= fieldConfig.maxLength!) {
162
+ return `Must be no more than ${fieldConfig.maxLength} characters`;
163
+ }
164
+ return true;
165
+ });
166
+ }
167
+ }
168
+
169
+ // Number validations
170
+ if (fieldConfig.type === "number" || fieldConfig.type === "range") {
171
+ if (fieldConfig.min !== undefined) {
172
+ validations.push(async (value: any) => {
173
+ if (value === null || value === undefined || value === "") return true; // Skip if empty
174
+ const numValue = Number(value);
175
+ if (isNaN(numValue) || numValue < fieldConfig.min!) {
176
+ return `Must be at least ${fieldConfig.min}`;
177
+ }
178
+ return true;
179
+ });
180
+ }
181
+
182
+ if (fieldConfig.max !== undefined) {
183
+ validations.push(async (value: any) => {
184
+ if (value === null || value === undefined || value === "") return true; // Skip if empty
185
+ const numValue = Number(value);
186
+ if (isNaN(numValue) || numValue > fieldConfig.max!) {
187
+ return `Must be no more than ${fieldConfig.max}`;
188
+ }
189
+ return true;
190
+ });
191
+ }
192
+ }
193
+
194
+ // Repeater validations
195
+ if (fieldConfig.type === "repeater") {
196
+ if (fieldConfig.minItems !== undefined) {
197
+ validations.push(async (value: any) => {
198
+ if (!Array.isArray(value)) return true; // Skip if not array
199
+ if (value.length < fieldConfig.minItems!) {
200
+ return `Must have at least ${fieldConfig.minItems} item${fieldConfig.minItems! > 1 ? "s" : ""}`;
201
+ }
202
+ return true;
203
+ });
204
+ }
205
+
206
+ if (fieldConfig.maxItems !== undefined) {
207
+ validations.push(async (value: any) => {
208
+ if (!Array.isArray(value)) return true; // Skip if not array
209
+ if (value.length > fieldConfig.maxItems!) {
210
+ return `Must have no more than ${fieldConfig.maxItems} item${fieldConfig.maxItems! > 1 ? "s" : ""}`;
211
+ }
212
+ return true;
213
+ });
214
+ }
215
+ }
216
+
217
+ // If no built-in validations, return null
218
+ if (validations.length === 0) {
219
+ return null;
220
+ }
221
+
222
+ // Return a combined validator function
223
+ return async (value: any): Promise<boolean | string> => {
224
+ for (const validation of validations) {
225
+ const result = await validation(value);
226
+ if (result !== true) {
227
+ return result;
228
+ }
229
+ }
230
+ return true;
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Combines a generic validator (built-in validations) with a custom validator
236
+ * Runs built-in validations first, then custom validator if provided
237
+ */
238
+ export function combineValidators(
239
+ genericValidator: ValidatorFunction | null,
240
+ customValidator?: ValidatorFunction
241
+ ): ValidatorFunction | undefined {
242
+ if (!genericValidator && !customValidator) {
243
+ return undefined;
244
+ }
245
+
246
+ if (!genericValidator) {
247
+ return customValidator;
248
+ }
249
+
250
+ if (!customValidator) {
251
+ return genericValidator;
252
+ }
253
+
254
+ // Return combined validator that runs both
255
+ return async (value: any): Promise<boolean | string> => {
256
+ // Run generic validator first
257
+ const genericResult = await genericValidator(value);
258
+ if (genericResult !== true) {
259
+ return genericResult;
260
+ }
261
+
262
+ // Then run custom validator
263
+ return await customValidator(value);
264
+ };
265
+ }
@@ -0,0 +1,161 @@
1
+ // Type declarations for vue-wswg-editor library dependencies
2
+ // These declarations allow the library to use internal dependencies without requiring
3
+ // consumers to install their type definitions
4
+
5
+ // SortableJS type declaration - self-contained for library consumers
6
+ // This allows the library to use sortablejs without requiring consumers to install @types/sortablejs
7
+ declare module "sortablejs" {
8
+ interface SortableOptions {
9
+ group?:
10
+ | string
11
+ | {
12
+ name?: string;
13
+ pull?: boolean | string | ((to: any, from: any, dragEl: HTMLElement) => boolean | string);
14
+ put?: boolean | string | ((to: any) => boolean | string);
15
+ };
16
+ sort?: boolean;
17
+ delay?: number;
18
+ delayOnTouchStart?: boolean;
19
+ touchStartThreshold?: number;
20
+ disabled?: boolean;
21
+ store?: { get: (sortable: any) => any[]; set: (sortable: any) => void };
22
+ animation?: number;
23
+ easing?: string;
24
+ handle?: string;
25
+ filter?: string | ((event: Event | TouchEvent) => boolean);
26
+ preventOnFilter?: boolean;
27
+ draggable?: string;
28
+ dataIdAttr?: string;
29
+ ghostClass?: string;
30
+ chosenClass?: string;
31
+ dragClass?: string;
32
+ swapThreshold?: number;
33
+ invertSwap?: boolean;
34
+ invertedSwapThreshold?: number;
35
+ direction?: "vertical" | "horizontal";
36
+ forceFallback?: boolean;
37
+ fallbackClass?: string;
38
+ fallbackOnBody?: boolean;
39
+ fallbackTolerance?: number;
40
+ fallbackOffset?: { x: number; y: number };
41
+ supportPointer?: boolean;
42
+ emptyInsertThreshold?: number;
43
+ setData?: (dataTransfer: DataTransfer, dragEl: HTMLElement) => void;
44
+ onStart?: (evt: any) => void;
45
+ onEnd?: (evt: any) => void;
46
+ onAdd?: (evt: any) => void;
47
+ onUpdate?: (evt: any) => void;
48
+ onSort?: (evt: any) => void;
49
+ onRemove?: (evt: any) => void;
50
+ onFilter?: (evt: any) => void;
51
+ onMove?: (evt: any, originalEvent: Event) => boolean | -1 | 1 | void;
52
+ onClone?: (evt: any) => void;
53
+ [key: string]: any;
54
+ }
55
+
56
+ class Sortable {
57
+ constructor(element: HTMLElement, options?: SortableOptions);
58
+ option(name: string, value?: any): any;
59
+ destroy(): void;
60
+ el: HTMLElement;
61
+ options: SortableOptions;
62
+ static active: Sortable | null;
63
+ static utils: {
64
+ on(el: HTMLElement, event: string, fn: (evt: Event) => void): void;
65
+ off(el: HTMLElement, event: string, fn: (evt: Event) => void): void;
66
+ get(el: HTMLElement, path: string): any;
67
+ set(el: HTMLElement, path: string, value: any): void;
68
+ closest(el: HTMLElement, selector: string): HTMLElement | null;
69
+ matches(el: HTMLElement, selector: string): boolean;
70
+ find(el: HTMLElement, selector: string): HTMLElement[];
71
+ create(tag: string, classes?: string): HTMLElement;
72
+ is(el: HTMLElement, selector: string): boolean;
73
+ extend(dest: any, ...src: any[]): any;
74
+ throttle(fn: (...args: any[]) => any, wait: number): (...args: any[]) => any;
75
+ debounce(fn: (...args: any[]) => any, wait: number): (...args: any[]) => any;
76
+ clone(el: HTMLElement): HTMLElement;
77
+ toggleClass(el: HTMLElement, name: string, state?: boolean): void;
78
+ index(el: HTMLElement, selector?: string): number;
79
+ nextTick(fn: () => void): void;
80
+ cancelNextTick(id: number): void;
81
+ detectDirection(el: HTMLElement): "vertical" | "horizontal";
82
+ getChild(el: HTMLElement, childNum: number): HTMLElement | null;
83
+ };
84
+ static mount(...plugins: any[]): void;
85
+ static create(element: HTMLElement, options?: SortableOptions): Sortable;
86
+ static get(element: HTMLElement): Sortable | undefined;
87
+ static readonly version: string;
88
+ }
89
+
90
+ export = Sortable;
91
+ export default Sortable;
92
+ }
93
+
94
+ // Yup type declaration - self-contained for library consumers
95
+ // This allows the library to use yup without requiring consumers to install yup types
96
+ declare module "yup" {
97
+ export interface Schema<T = any> {
98
+ validate(value: any, options?: any): Promise<T>;
99
+ isValid(value: any, options?: any): Promise<boolean>;
100
+ validateSync(value: any, options?: any): T;
101
+ isValidSync(value: any, options?: any): boolean;
102
+ cast(value: any, options?: any): T;
103
+ nullable(isNullable?: boolean): Schema<T | null>;
104
+ required(message?: string): Schema<T>;
105
+ notRequired(): Schema<T | undefined>;
106
+ default(value: T | (() => T)): Schema<T>;
107
+ typeError(message?: string): Schema<T>;
108
+ oneOf(values: any[], message?: string): Schema<T>;
109
+ notOneOf(values: any[], message?: string): Schema<T>;
110
+ when(keys: string | string[], builder: any): Schema<T>;
111
+ test(
112
+ name: string,
113
+ message: string | ((value: any) => string),
114
+ test: (value: any) => boolean | Promise<boolean>
115
+ ): Schema<T>;
116
+ transform(transform: (value: any) => any): Schema<T>;
117
+ }
118
+
119
+ export function string(): Schema<string>;
120
+ export function number(): Schema<number>;
121
+ export function boolean(): Schema<boolean>;
122
+ export function date(): Schema<Date>;
123
+ export function object(shape?: Record<string, Schema>): Schema<Record<string, any>>;
124
+ export function array(of?: Schema): Schema<any[]>;
125
+ export function mixed(): Schema<any>;
126
+ export function lazy(fn: () => Schema): Schema;
127
+ export function reach(schema: Schema, path: string, value?: any, context?: any): Schema;
128
+ export function ref(path: string, options?: { contextPrefix?: string }): any;
129
+ export function setLocale(customLocale: Record<string, any>): void;
130
+ export function getLocale(): Record<string, any>;
131
+ export function addMethod(schemaType: string, name: string, method: (...args: any[]) => any): void;
132
+ export class ValidationError extends Error {
133
+ value: any;
134
+ path: string;
135
+ type?: string;
136
+ errors: string[];
137
+ inner: ValidationError[];
138
+ params?: Record<string, any>;
139
+ constructor(errors: string | string[], value: any, path?: string, type?: string);
140
+ }
141
+ export const mixed: {
142
+ (): Schema<any>;
143
+ };
144
+ }
145
+
146
+ // Virtual modules created by vue-wswg-editor vite plugin
147
+ declare module "vue-wswg-editor:layouts" {
148
+ export const modules: Record<string, () => Promise<any>>;
149
+ }
150
+
151
+ declare module "vue-wswg-editor:blocks" {
152
+ export const modules: Record<string, () => Promise<any>>;
153
+ }
154
+
155
+ declare module "vue-wswg-editor:fields" {
156
+ export const modules: Record<string, () => Promise<any>>;
157
+ }
158
+
159
+ declare module "vue-wswg-editor:thumbnails" {
160
+ export const modules: Record<string, () => Promise<any>>;
161
+ }