@vibecms/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vibe.js +497 -0
- package/package.json +39 -0
- package/templates/components/cms/Analytics.tsx +42 -0
- package/templates/components/cms/BlockRenderer.tsx +37 -0
- package/templates/components/cms/CmsStage.tsx +116 -0
- package/templates/components/cms/EditProvider.tsx +121 -0
- package/templates/components/cms/Editor.tsx +954 -0
- package/templates/components/cms/FormGenerator.tsx +611 -0
- package/templates/components/cms/SEO.tsx +35 -0
- package/templates/components/cms/SiteFooter.tsx +39 -0
- package/templates/components/cms/SiteHeader.tsx +43 -0
- package/templates/components/cms/VisualWrapper.tsx +128 -0
- package/templates/components/cms/fields/ColorPicker.tsx +71 -0
- package/templates/components/cms/fields/IconPicker.tsx +67 -0
- package/templates/components/cms/fields/ImageUpload.tsx +120 -0
- package/templates/components/cms/fields/MediaGallery.tsx +176 -0
- package/templates/components/cms/fields/MultiReferencePicker.tsx +83 -0
- package/templates/components/cms/fields/ReferencePicker.tsx +75 -0
- package/templates/components/cms/fields/RichText.tsx +121 -0
- package/templates/lib/cms/auditor.ts +307 -0
- package/templates/lib/cms/auth-nextauth.ts +26 -0
- package/templates/lib/cms/engine.ts +3 -0
- package/templates/lib/cms/registry.ts +12 -0
- package/templates/lib/cms/sanitize.ts +18 -0
- package/templates/lib/cms/schema.ts +51 -0
- package/templates/lib/cms/store.ts +59 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/lib/cms/schema.ts
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export type VibeUIType = 'text' | 'rich-text' | 'gallery' | 'image' | 'boolean' | 'number' | 'object' | 'array' | 'color-picker' | 'icon-picker' | 'reference' | 'reference-array';
|
|
5
|
+
|
|
6
|
+
export function vibeField<T extends z.ZodTypeAny>(schema: T, uiType: VibeUIType, options?: { collection?: string }) {
|
|
7
|
+
if ((uiType === 'reference' || uiType === 'reference-array') && options?.collection) {
|
|
8
|
+
return schema.describe(`ui:${uiType}:${options.collection}`);
|
|
9
|
+
}
|
|
10
|
+
return schema.describe(`ui:${uiType}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function vibeConditional<T extends z.ZodTypeAny>(schema: T, dependsOnField: string, expectedValue: string | boolean | number) {
|
|
14
|
+
const currentDesc = schema.description || '';
|
|
15
|
+
return schema.describe(`${currentDesc}|dependsOn:${dependsOnField}=${expectedValue}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getVibeUIType(schema: z.ZodTypeAny): VibeUIType | undefined {
|
|
19
|
+
if (!schema) return undefined;
|
|
20
|
+
const desc = schema.description;
|
|
21
|
+
if (desc && desc.startsWith('ui:')) {
|
|
22
|
+
return desc.split(':')[1] as VibeUIType;
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getReferenceCollection(schema: z.ZodTypeAny): string | undefined {
|
|
28
|
+
if (!schema) return undefined;
|
|
29
|
+
const desc = schema.description;
|
|
30
|
+
if (desc && desc.startsWith('ui:reference:')) {
|
|
31
|
+
return desc.split(':')[2];
|
|
32
|
+
}
|
|
33
|
+
if (desc && desc.startsWith('ui:reference-array:')) {
|
|
34
|
+
return desc.split(':')[2];
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getConditionalLogic(schema: z.ZodTypeAny): { field: string, value: string } | undefined {
|
|
40
|
+
if (!schema) return undefined;
|
|
41
|
+
const desc = schema.description;
|
|
42
|
+
if (desc && desc.includes('|dependsOn:')) {
|
|
43
|
+
const section = desc.split('|').find(s => s.startsWith('dependsOn:'));
|
|
44
|
+
if (section) {
|
|
45
|
+
const condition = section.replace('dependsOn:', '');
|
|
46
|
+
const [field, value] = condition.split('=');
|
|
47
|
+
return { field, value };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
|
|
2
|
+
// src/lib/cms/store.ts
|
|
3
|
+
import { map, atom } from 'nanostores';
|
|
4
|
+
|
|
5
|
+
export const isEditMode = atom<boolean>(false);
|
|
6
|
+
export const contentDraft = map<Record<string, any>>({});
|
|
7
|
+
export const focusedField = atom<string | null>(null); // Changed to allow null
|
|
8
|
+
export const focusTrigger = atom<number>(0);
|
|
9
|
+
export const previewDevice = atom<'desktop' | 'tablet' | 'mobile'>('desktop');
|
|
10
|
+
export const currentDocument = atom<{ collection: string; slug: string } | null>(null);
|
|
11
|
+
|
|
12
|
+
// Global map tracking which UI paths are currently reporting DOM layout overflows
|
|
13
|
+
export const layoutWarnings = map<Record<string, boolean>>({});
|
|
14
|
+
|
|
15
|
+
export interface ClipboardPayload {
|
|
16
|
+
type: string;
|
|
17
|
+
data: any;
|
|
18
|
+
}
|
|
19
|
+
let initialClipboard: ClipboardPayload | null = null;
|
|
20
|
+
if (typeof window !== 'undefined') {
|
|
21
|
+
try {
|
|
22
|
+
const saved = localStorage.getItem('vibe-clipboard');
|
|
23
|
+
if (saved) initialClipboard = JSON.parse(saved);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.warn('Failed to parse clipboard from local storage', e);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const clipboardBuffer = atom<ClipboardPayload | null>(initialClipboard);
|
|
29
|
+
|
|
30
|
+
export function updateDraftPath(path: string, value: any) {
|
|
31
|
+
const keys = path.split('.');
|
|
32
|
+
|
|
33
|
+
if (keys.length === 1) {
|
|
34
|
+
contentDraft.setKey(keys[0] as any, value);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const state = contentDraft.get();
|
|
39
|
+
const newState = JSON.parse(JSON.stringify(state)); // Deep clone
|
|
40
|
+
|
|
41
|
+
let current: any = newState;
|
|
42
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
43
|
+
const key = keys[i];
|
|
44
|
+
if (!current[key]) {
|
|
45
|
+
current[key] = isNaN(Number(keys[i + 1])) ? {} : [];
|
|
46
|
+
}
|
|
47
|
+
current = current[key];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
current[keys[keys.length - 1]] = value;
|
|
51
|
+
contentDraft.set(newState);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function setFocusedField(path: string, triggerSidebarFocus = true) {
|
|
55
|
+
focusedField.set(path);
|
|
56
|
+
if (triggerSidebarFocus) {
|
|
57
|
+
focusTrigger.set(Date.now());
|
|
58
|
+
}
|
|
59
|
+
}
|