@deneb-ui/create-template 2.0.70 → 2.0.72
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/index.js +191 -7
- package/package.json +1 -1
- package/template/fivora-template.json +13 -24
- package/template/package.json +2 -2
- package/template/scripts/merge-platform-contract.js +82 -5
- package/template/src/app/about_us/page.tsx +105 -19
- package/template/src/app/contact/page.tsx +124 -39
- package/template/src/app/globals.css +13 -83
- package/template/src/app/layout.tsx +7 -1
- package/template/src/app/page.tsx +118 -33
- package/template/src/app/products/detail/page.tsx +13 -1
- package/template/src/app/services/page.tsx +67 -9
- package/template/src/components/SiteChrome.tsx +4 -0
- package/template/src/components/ThemeToggle.tsx +89 -0
- package/template/src/data/site-data.json +99 -74
- package/template/src/lib/siteDataContext.tsx +2 -0
package/bin/index.js
CHANGED
|
@@ -54,11 +54,121 @@ function copyFolderSync(src, dest) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
const THEME_PRESETS = {
|
|
58
|
+
dual: {
|
|
59
|
+
key: 'dual',
|
|
60
|
+
name: 'Dual-Mode (Light + Dark Switcher) [Recommended]',
|
|
61
|
+
theme: {
|
|
62
|
+
primaryColor: '#016a7e',
|
|
63
|
+
secondaryColor: '#0a1931',
|
|
64
|
+
accentColor: '#00adb5',
|
|
65
|
+
backgroundColor: '#ffffff',
|
|
66
|
+
textColor: '#0f172a',
|
|
67
|
+
headingFont: 'Inter',
|
|
68
|
+
bodyFont: 'Inter',
|
|
69
|
+
baseSize: '16px',
|
|
70
|
+
heroMinHeight: '72vh',
|
|
71
|
+
sectionPadding: '5rem',
|
|
72
|
+
dark: {
|
|
73
|
+
backgroundColor: '#090d16',
|
|
74
|
+
cardBackgroundColor: '#131b2e',
|
|
75
|
+
textColor: '#f8fafc',
|
|
76
|
+
borderColor: 'rgba(255, 255, 255, 0.12)',
|
|
77
|
+
primaryColor: '#00adb5',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
light: {
|
|
82
|
+
key: 'light',
|
|
83
|
+
name: 'Modern Clean (Pure Light Commerce)',
|
|
84
|
+
theme: {
|
|
85
|
+
primaryColor: '#2563eb',
|
|
86
|
+
secondaryColor: '#1e293b',
|
|
87
|
+
accentColor: '#3b82f6',
|
|
88
|
+
backgroundColor: '#ffffff',
|
|
89
|
+
cardBackgroundColor: '#ffffff',
|
|
90
|
+
textColor: '#0f172a',
|
|
91
|
+
borderColor: '#e2e8f0',
|
|
92
|
+
headingFont: 'Inter',
|
|
93
|
+
bodyFont: 'Inter',
|
|
94
|
+
baseSize: '16px',
|
|
95
|
+
heroMinHeight: '72vh',
|
|
96
|
+
sectionPadding: '5rem',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
dark: {
|
|
100
|
+
key: 'dark',
|
|
101
|
+
name: 'Luxury Obsidian (Pure Dark Theme)',
|
|
102
|
+
theme: {
|
|
103
|
+
primaryColor: '#6366f1',
|
|
104
|
+
secondaryColor: '#0f172a',
|
|
105
|
+
accentColor: '#818cf8',
|
|
106
|
+
backgroundColor: '#090d16',
|
|
107
|
+
cardBackgroundColor: '#131b2e',
|
|
108
|
+
textColor: '#f8fafc',
|
|
109
|
+
borderColor: 'rgba(255, 255, 255, 0.12)',
|
|
110
|
+
headingFont: 'Inter',
|
|
111
|
+
bodyFont: 'Inter',
|
|
112
|
+
baseSize: '16px',
|
|
113
|
+
heroMinHeight: '72vh',
|
|
114
|
+
sectionPadding: '5rem',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
emerald: {
|
|
118
|
+
key: 'emerald',
|
|
119
|
+
name: 'Emerald Luxury (Deep Green & Gold)',
|
|
120
|
+
theme: {
|
|
121
|
+
primaryColor: '#10b981',
|
|
122
|
+
secondaryColor: '#064e3b',
|
|
123
|
+
accentColor: '#d97706',
|
|
124
|
+
backgroundColor: '#061a14',
|
|
125
|
+
cardBackgroundColor: '#0c2e24',
|
|
126
|
+
textColor: '#f0fdf4',
|
|
127
|
+
borderColor: 'rgba(16, 185, 129, 0.2)',
|
|
128
|
+
headingFont: 'Inter',
|
|
129
|
+
bodyFont: 'Inter',
|
|
130
|
+
baseSize: '16px',
|
|
131
|
+
heroMinHeight: '72vh',
|
|
132
|
+
sectionPadding: '5rem',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
cyberpunk: {
|
|
136
|
+
key: 'cyberpunk',
|
|
137
|
+
name: 'Cyberpunk Neon (Futuristic Dark)',
|
|
138
|
+
theme: {
|
|
139
|
+
primaryColor: '#ec4899',
|
|
140
|
+
secondaryColor: '#18181b',
|
|
141
|
+
accentColor: '#06b6d4',
|
|
142
|
+
backgroundColor: '#09090b',
|
|
143
|
+
cardBackgroundColor: '#18181b',
|
|
144
|
+
textColor: '#fafafa',
|
|
145
|
+
borderColor: 'rgba(236, 72, 153, 0.3)',
|
|
146
|
+
headingFont: 'Inter',
|
|
147
|
+
bodyFont: 'Inter',
|
|
148
|
+
baseSize: '16px',
|
|
149
|
+
heroMinHeight: '72vh',
|
|
150
|
+
sectionPadding: '5rem',
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
function resolveThemePreset(input) {
|
|
156
|
+
if (!input) return THEME_PRESETS.dual;
|
|
157
|
+
const clean = input.trim().toLowerCase();
|
|
158
|
+
if (clean === '1' || clean === 'dual') return THEME_PRESETS.dual;
|
|
159
|
+
if (clean === '2' || clean === 'light') return THEME_PRESETS.light;
|
|
160
|
+
if (clean === '3' || clean === 'dark') return THEME_PRESETS.dark;
|
|
161
|
+
if (clean === '4' || clean === 'emerald') return THEME_PRESETS.emerald;
|
|
162
|
+
if (clean === '5' || clean === 'cyberpunk') return THEME_PRESETS.cyberpunk;
|
|
163
|
+
return THEME_PRESETS[clean] || THEME_PRESETS.dual;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function runScaffolding(targetInput, chosenThemeKey = 'dual') {
|
|
58
167
|
const targetPath = path.resolve(process.cwd(), targetInput.trim());
|
|
59
168
|
const folderName = path.basename(targetPath);
|
|
60
169
|
const sanitizedPkgName = folderName.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
61
170
|
const templateDir = path.resolve(__dirname, '..', 'template');
|
|
171
|
+
const selectedPreset = resolveThemePreset(chosenThemeKey);
|
|
62
172
|
|
|
63
173
|
if (!fs.existsSync(templateDir)) {
|
|
64
174
|
console.error(`\n\x1b[31mError:\x1b[0m Template directory not found at ${templateDir}`);
|
|
@@ -71,6 +181,7 @@ function runScaffolding(targetInput) {
|
|
|
71
181
|
}
|
|
72
182
|
|
|
73
183
|
console.log(`\n\x1b[32mCreating DENEB template in:\x1b[0m ${targetPath}...\n`);
|
|
184
|
+
console.log(` \x1b[36m🎨 Starter Theme Style:\x1b[0m ${selectedPreset.name}`);
|
|
74
185
|
copyFolderSync(templateDir, targetPath);
|
|
75
186
|
|
|
76
187
|
// Restore .gitignore if preserved as _gitignore
|
|
@@ -91,8 +202,29 @@ function runScaffolding(targetInput) {
|
|
|
91
202
|
}
|
|
92
203
|
}
|
|
93
204
|
|
|
205
|
+
// Inject selected theme into site-data.json
|
|
206
|
+
const siteDataPath = path.join(targetPath, 'src', 'data', 'site-data.json');
|
|
207
|
+
if (fs.existsSync(siteDataPath)) {
|
|
208
|
+
try {
|
|
209
|
+
const siteData = JSON.parse(fs.readFileSync(siteDataPath, 'utf8'));
|
|
210
|
+
if (siteData.template && siteData.template.structure) {
|
|
211
|
+
siteData.template.structure.theme = {
|
|
212
|
+
...(siteData.template.structure.theme || {}),
|
|
213
|
+
...selectedPreset.theme,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
siteData.theme = {
|
|
217
|
+
...(siteData.theme || {}),
|
|
218
|
+
...selectedPreset.theme,
|
|
219
|
+
};
|
|
220
|
+
fs.writeFileSync(siteDataPath, JSON.stringify(siteData, null, 2) + '\n');
|
|
221
|
+
} catch {
|
|
222
|
+
// ignore
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
94
226
|
console.log(`\x1b[32m✔ Template structure created!\x1b[0m`);
|
|
95
|
-
console.log(` \x1b[90m- Pre-configured @deneb-ui/ui (
|
|
227
|
+
console.log(` \x1b[90m- Pre-configured @deneb-ui/ui (ThemeStyles, ThemeToggle & 28+ Editable Components)\x1b[0m`);
|
|
96
228
|
console.log(` \x1b[90m- Pre-configured @deneb-ui/cli (lab, validate, zip tools)\x1b[0m\n`);
|
|
97
229
|
|
|
98
230
|
const skipInstall = process.argv.includes('--skip-install');
|
|
@@ -135,7 +267,7 @@ function runScaffolding(targetInput) {
|
|
|
135
267
|
console.log(`\x1b[32m══════════════════════════════════════════════════════════════════════════════\x1b[0m\n`);
|
|
136
268
|
console.log(`Your storefront template is pre-configured with:`);
|
|
137
269
|
console.log(` \x1b[36m⚡ Next.js 15 App Router\x1b[0m (React 19 + Static Export support)`);
|
|
138
|
-
console.log(` \x1b[36m🎨
|
|
270
|
+
console.log(` \x1b[36m🎨 Dynamic Theming Engine\x1b[0m (ThemeStyles + ThemeToggle + Zero-FOUC)`);
|
|
139
271
|
console.log(` \x1b[36m💎 @deneb-ui/ui\x1b[0m (28+ Visual-First Editable Components)`);
|
|
140
272
|
console.log(` \x1b[36m🛠️ @deneb-ui/cli\x1b[0m (Visual Lab, Preflight Validator & Packager)`);
|
|
141
273
|
console.log(` \x1b[36m📋 Fivora Manifest\x1b[0m (Version 2 Contract Compliant)\n`);
|
|
@@ -163,8 +295,44 @@ function runScaffolding(targetInput) {
|
|
|
163
295
|
|
|
164
296
|
printBanner();
|
|
165
297
|
|
|
166
|
-
|
|
167
|
-
|
|
298
|
+
// Extract CLI flags like --theme=dark or --theme=dual
|
|
299
|
+
let cliTheme = null;
|
|
300
|
+
const themeArg = args.find((a) => a.startsWith('--theme='));
|
|
301
|
+
if (themeArg) {
|
|
302
|
+
cliTheme = themeArg.split('=')[1];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const positionalTarget = args.find((a) => !a.startsWith('-'));
|
|
306
|
+
const isNonInteractive =
|
|
307
|
+
!process.stdin.isTTY ||
|
|
308
|
+
Boolean(process.env.CI) ||
|
|
309
|
+
args.includes('--yes') ||
|
|
310
|
+
args.includes('-y') ||
|
|
311
|
+
args.includes('--non-interactive') ||
|
|
312
|
+
args.includes('--skip-install');
|
|
313
|
+
|
|
314
|
+
if (positionalTarget && positionalTarget.trim()) {
|
|
315
|
+
if (cliTheme || isNonInteractive) {
|
|
316
|
+
runScaffolding(positionalTarget, cliTheme || 'dual');
|
|
317
|
+
} else {
|
|
318
|
+
const rl = readline.createInterface({
|
|
319
|
+
input: process.stdin,
|
|
320
|
+
output: process.stdout,
|
|
321
|
+
});
|
|
322
|
+
console.log('⚡ Template Theme Selection:\n');
|
|
323
|
+
console.log(' \x1b[36m1)\x1b[0m Dual-Mode \x1b[90m(Light + Dark Switcher pre-installed)\x1b[0m \x1b[32m[Recommended]\x1b[0m');
|
|
324
|
+
console.log(' \x1b[36m2)\x1b[0m Modern Clean \x1b[90m(Pure Light commerce)\x1b[0m');
|
|
325
|
+
console.log(' \x1b[36m3)\x1b[0m Luxury Obsidian \x1b[90m(Pure Dark theme)\x1b[0m');
|
|
326
|
+
console.log(' \x1b[36m4)\x1b[0m Emerald Luxury \x1b[90m(Deep Green & Gold)\x1b[0m');
|
|
327
|
+
console.log(' \x1b[36m5)\x1b[0m Cyberpunk Neon \x1b[90m(Futuristic Dark)\x1b[0m\n');
|
|
328
|
+
|
|
329
|
+
rl.question(' \x1b[36m?\x1b[0m \x1b[1mSelect starter theme (1-5, default 1):\x1b[0m ', (themeAns) => {
|
|
330
|
+
rl.close();
|
|
331
|
+
runScaffolding(positionalTarget, themeAns.trim() || 'dual');
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
} else if (isNonInteractive) {
|
|
335
|
+
runScaffolding('my-deneb-store', cliTheme || 'dual');
|
|
168
336
|
} else {
|
|
169
337
|
const rl = readline.createInterface({
|
|
170
338
|
input: process.stdin,
|
|
@@ -173,8 +341,24 @@ if (targetDirInput && targetDirInput.trim() && !targetDirInput.startsWith('-'))
|
|
|
173
341
|
|
|
174
342
|
console.log('⚡ Welcome to DENEB UI Template Creator!\n');
|
|
175
343
|
rl.question(' \x1b[36m?\x1b[0m \x1b[1mWhat is your template name?\x1b[0m \x1b[90m(e.g. my-store)\x1b[0m: ', (answer) => {
|
|
176
|
-
rl.close();
|
|
177
344
|
const name = answer.trim() || 'my-deneb-store';
|
|
178
|
-
|
|
345
|
+
|
|
346
|
+
if (cliTheme) {
|
|
347
|
+
rl.close();
|
|
348
|
+
runScaffolding(name, cliTheme);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
console.log('\n \x1b[36m?\x1b[0m \x1b[1mSelect starter theme style:\x1b[0m');
|
|
353
|
+
console.log(' \x1b[36m1)\x1b[0m Dual-Mode \x1b[90m(Light + Dark Switcher pre-installed)\x1b[0m \x1b[32m[Recommended]\x1b[0m');
|
|
354
|
+
console.log(' \x1b[36m2)\x1b[0m Modern Clean \x1b[90m(Pure Light commerce)\x1b[0m');
|
|
355
|
+
console.log(' \x1b[36m3)\x1b[0m Luxury Obsidian \x1b[90m(Pure Dark theme)\x1b[0m');
|
|
356
|
+
console.log(' \x1b[36m4)\x1b[0m Emerald Luxury \x1b[90m(Deep Green & Gold)\x1b[0m');
|
|
357
|
+
console.log(' \x1b[36m5)\x1b[0m Cyberpunk Neon \x1b[90m(Futuristic Dark)\x1b[0m\n');
|
|
358
|
+
|
|
359
|
+
rl.question(' \x1b[1mEnter choice (1-5, default 1):\x1b[0m ', (themeAns) => {
|
|
360
|
+
rl.close();
|
|
361
|
+
runScaffolding(name, themeAns.trim() || 'dual');
|
|
362
|
+
});
|
|
179
363
|
});
|
|
180
364
|
}
|
package/package.json
CHANGED
|
@@ -5,34 +5,12 @@
|
|
|
5
5
|
"contractVersion": 1,
|
|
6
6
|
"mode": "strict",
|
|
7
7
|
"controlOnlyPaths": [
|
|
8
|
-
"__fivoraIntake.tone",
|
|
9
|
-
"__fivoraIntake.outputLanguage",
|
|
10
|
-
"__fivoraIntake.businessSummary",
|
|
11
|
-
"__fivoraIntake.additionalBusinessDetails",
|
|
12
|
-
"__fivoraIntake.referenceWebsiteUrl",
|
|
13
|
-
"additionalPages[*].id",
|
|
14
|
-
"additionalPages[*].route",
|
|
15
|
-
"common.currency",
|
|
16
|
-
"common.whatsappNumber",
|
|
17
|
-
"common.whatsapp",
|
|
18
|
-
"common.email",
|
|
19
|
-
"common.address",
|
|
20
|
-
"common.contactNumber",
|
|
21
|
-
"common.openingHours",
|
|
22
|
-
"common.newsletterTitle",
|
|
23
|
-
"common.newsletterDescription",
|
|
24
|
-
"contact.contactNumber",
|
|
25
|
-
"contact.googleMapLink",
|
|
26
|
-
"contact.whatsapp",
|
|
27
8
|
"products[*].id",
|
|
28
9
|
"products[*].isAvailable",
|
|
29
|
-
"products[*].measurement",
|
|
30
|
-
"products[*].unit",
|
|
31
|
-
"testimonials[*].id",
|
|
32
|
-
"categories[*].slug",
|
|
33
10
|
"services[*].id",
|
|
34
11
|
"products[*].currency",
|
|
35
|
-
"about.collageImages[*].id"
|
|
12
|
+
"about.collageImages[*].id",
|
|
13
|
+
"contact.whatsapp"
|
|
36
14
|
]
|
|
37
15
|
},
|
|
38
16
|
"siteDataFile": "src/data/site-data.json",
|
|
@@ -424,12 +402,23 @@
|
|
|
424
402
|
"key": "submitLabel",
|
|
425
403
|
"type": "text",
|
|
426
404
|
"label": "Submit button"
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
"key": "directMessageText",
|
|
408
|
+
"type": "text",
|
|
409
|
+
"label": "Direct message label"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"key": "whatsapp",
|
|
413
|
+
"type": "tel",
|
|
414
|
+
"label": "WhatsApp"
|
|
427
415
|
}
|
|
428
416
|
]
|
|
429
417
|
},
|
|
430
418
|
{
|
|
431
419
|
"id": "products",
|
|
432
420
|
"path": "products",
|
|
421
|
+
"pageKey": "home",
|
|
433
422
|
"type": "list",
|
|
434
423
|
"label": "Products",
|
|
435
424
|
"minItems": 0,
|
package/template/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"next": "15.5.23",
|
|
19
19
|
"react": "19.2.8",
|
|
20
20
|
"react-dom": "19.2.8",
|
|
21
|
-
"@deneb-ui/ui": "^2.0.
|
|
21
|
+
"@deneb-ui/ui": "^2.0.72"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@eslint/eslintrc": "3.3.6",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"eslint": "9.39.5",
|
|
29
29
|
"eslint-config-next": "15.5.23",
|
|
30
30
|
"typescript": "5.8.3",
|
|
31
|
-
"@deneb-ui/cli": "^2.0.
|
|
31
|
+
"@deneb-ui/cli": "^2.0.72",
|
|
32
32
|
"tailwindcss": "^3.4.17",
|
|
33
33
|
"postcss": "8.5.26",
|
|
34
34
|
"autoprefixer": "^10.4.20"
|
|
@@ -56,7 +56,6 @@ const PLATFORM_PATHS = [
|
|
|
56
56
|
// Contact fields used as action links (href attributes)
|
|
57
57
|
'contact.contactNumber',
|
|
58
58
|
'contact.googleMapLink',
|
|
59
|
-
'contact.whatsapp',
|
|
60
59
|
// Product system fields
|
|
61
60
|
'products[*].id',
|
|
62
61
|
'products[*].isAvailable',
|
|
@@ -65,6 +64,9 @@ const PLATFORM_PATHS = [
|
|
|
65
64
|
// List item system keys
|
|
66
65
|
'testimonials[*].id',
|
|
67
66
|
'categories[*].slug',
|
|
67
|
+
'services[*].id',
|
|
68
|
+
'products[*].currency',
|
|
69
|
+
'about.collageImages[*].id',
|
|
68
70
|
];
|
|
69
71
|
|
|
70
72
|
const manifestPath = path.resolve(__dirname, '..', 'fivora-template.json');
|
|
@@ -76,12 +78,87 @@ if (!fs.existsSync(manifestPath)) {
|
|
|
76
78
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
77
79
|
if (!manifest.visualEditing) manifest.visualEditing = {};
|
|
78
80
|
|
|
81
|
+
const siteDataPath = path.resolve(__dirname, '..', manifest.siteDataFile || 'src/data/site-data.json');
|
|
82
|
+
let siteData = {};
|
|
83
|
+
if (fs.existsSync(siteDataPath)) {
|
|
84
|
+
try {
|
|
85
|
+
siteData = JSON.parse(fs.readFileSync(siteDataPath, 'utf8'));
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function pathExistsInContent(content, p) {
|
|
90
|
+
if (!content || typeof content !== 'object') return false;
|
|
91
|
+
const segments = p.split('.');
|
|
92
|
+
let current = [content];
|
|
93
|
+
for (const seg of segments) {
|
|
94
|
+
const isList = seg.endsWith('[*]');
|
|
95
|
+
const key = isList ? seg.slice(0, -3) : seg;
|
|
96
|
+
const next = [];
|
|
97
|
+
for (const item of current) {
|
|
98
|
+
if (!item || typeof item !== 'object') continue;
|
|
99
|
+
const val = item[key];
|
|
100
|
+
if (val === undefined) continue;
|
|
101
|
+
if (isList) {
|
|
102
|
+
if (Array.isArray(val)) {
|
|
103
|
+
next.push(...val);
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
next.push(val);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (next.length === 0) return false;
|
|
110
|
+
current = next;
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function pathExistsInSchema(schema, p) {
|
|
116
|
+
if (!schema || !Array.isArray(schema.sections)) return false;
|
|
117
|
+
const canonical = p.replace(/\[\d+\]/g, '[*]');
|
|
118
|
+
|
|
119
|
+
function checkNode(node, currentPath) {
|
|
120
|
+
if (currentPath === canonical) return true;
|
|
121
|
+
if (node.type === 'object' && Array.isArray(node.fields)) {
|
|
122
|
+
for (const field of node.fields) {
|
|
123
|
+
if (checkNode(field, `${currentPath}.${field.key}`)) return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (node.type === 'list') {
|
|
127
|
+
const itemPath = `${currentPath}[*]`;
|
|
128
|
+
if (itemPath === canonical) return true;
|
|
129
|
+
if (node.itemField && itemPath === canonical) return true;
|
|
130
|
+
if (Array.isArray(node.fields)) {
|
|
131
|
+
for (const field of node.fields) {
|
|
132
|
+
if (checkNode(field, `${itemPath}.${field.key}`)) return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const section of schema.sections) {
|
|
140
|
+
if (checkNode(section, section.path)) return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isKnownPath(p) {
|
|
146
|
+
return (
|
|
147
|
+
pathExistsInContent(siteData.content, p) ||
|
|
148
|
+
pathExistsInContent(siteData, p) ||
|
|
149
|
+
pathExistsInSchema(manifest.editorSchema, p)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
79
153
|
const existing = Array.isArray(manifest.visualEditing.controlOnlyPaths)
|
|
80
|
-
? manifest.visualEditing.controlOnlyPaths
|
|
154
|
+
? manifest.visualEditing.controlOnlyPaths.filter(isKnownPath)
|
|
155
|
+
: [];
|
|
81
156
|
|
|
82
|
-
const
|
|
83
|
-
const
|
|
157
|
+
const relevantPlatformPaths = PLATFORM_PATHS.filter(isKnownPath);
|
|
158
|
+
const merged = [...new Set([...relevantPlatformPaths, ...existing])];
|
|
84
159
|
manifest.visualEditing.controlOnlyPaths = merged;
|
|
85
160
|
|
|
86
161
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
87
|
-
console.log(`merge-platform-contract:
|
|
162
|
+
console.log(`merge-platform-contract: ${merged.length} platform paths retained`);
|
|
163
|
+
|
|
164
|
+
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { EditableText, EditableImage } from '@deneb-ui/ui';
|
|
4
4
|
import { contentObject, contentText, contentList, useSiteData } from '@/lib/siteDataContext';
|
|
5
|
-
import { withBasePath } from '@/lib/utils';
|
|
6
5
|
|
|
7
6
|
export default function AboutPage() {
|
|
8
7
|
const siteData = useSiteData();
|
|
@@ -13,30 +12,117 @@ export default function AboutPage() {
|
|
|
13
12
|
<div data-preview-page-key="about_us">
|
|
14
13
|
<section className="page-section split" data-design-section="about">
|
|
15
14
|
<div className="split-copy">
|
|
16
|
-
<
|
|
17
|
-
|
|
15
|
+
<EditableText
|
|
16
|
+
variant="h1"
|
|
17
|
+
size="4xl"
|
|
18
|
+
weight="bold"
|
|
19
|
+
color="heading"
|
|
20
|
+
className="section-title"
|
|
21
|
+
data-preview-field-path="about.heading"
|
|
22
|
+
defaultValue={contentText(about.heading)}
|
|
23
|
+
/>
|
|
24
|
+
<EditableText
|
|
25
|
+
variant="lead"
|
|
26
|
+
color="muted"
|
|
27
|
+
data-preview-field-path="about.body"
|
|
28
|
+
defaultValue={contentText(about.body)}
|
|
29
|
+
/>
|
|
18
30
|
</div>
|
|
19
|
-
<
|
|
31
|
+
<EditableImage
|
|
20
32
|
className="split-image"
|
|
21
|
-
src={
|
|
22
|
-
alt=""
|
|
33
|
+
src={contentText(about.imageUrl) || '/placeholder.svg'}
|
|
34
|
+
alt="About DENEB-UI"
|
|
23
35
|
data-preview-field-path="about.imageUrl"
|
|
36
|
+
radius="xl"
|
|
37
|
+
fit="cover"
|
|
24
38
|
/>
|
|
25
39
|
</section>
|
|
26
40
|
|
|
27
41
|
<section className="page-section" data-design-section="about-heritage">
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '2rem', alignItems: 'start' }}>
|
|
43
|
+
<div>
|
|
44
|
+
<EditableText
|
|
45
|
+
variant="h2"
|
|
46
|
+
size="2xl"
|
|
47
|
+
weight="bold"
|
|
48
|
+
color="heading"
|
|
49
|
+
data-preview-field-path="about.collageHeading"
|
|
50
|
+
defaultValue={contentText(about.collageHeading)}
|
|
51
|
+
/>
|
|
52
|
+
<EditableText
|
|
53
|
+
variant="p"
|
|
54
|
+
color="muted"
|
|
55
|
+
data-preview-field-path="about.collageDescription"
|
|
56
|
+
defaultValue={contentText(about.collageDescription)}
|
|
57
|
+
style={{ marginTop: '0.5rem', marginBottom: '1.5rem' }}
|
|
58
|
+
/>
|
|
59
|
+
<EditableText
|
|
60
|
+
variant="h3"
|
|
61
|
+
size="xl"
|
|
62
|
+
weight="bold"
|
|
63
|
+
color="heading"
|
|
64
|
+
data-preview-field-path="about.historyHeading"
|
|
65
|
+
defaultValue={contentText(about.historyHeading)}
|
|
66
|
+
/>
|
|
67
|
+
<EditableText
|
|
68
|
+
variant="p"
|
|
69
|
+
color="muted"
|
|
70
|
+
data-preview-field-path="about.history"
|
|
71
|
+
defaultValue={contentText(about.history)}
|
|
72
|
+
style={{ marginTop: '0.5rem' }}
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div
|
|
77
|
+
data-preview-list-path="about.collageImages"
|
|
78
|
+
style={{
|
|
79
|
+
display: 'grid',
|
|
80
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
81
|
+
gap: '0.75rem',
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
{collageImages.map((item, index) => (
|
|
85
|
+
<figure
|
|
86
|
+
key={item.id || index}
|
|
87
|
+
data-preview-item-path={`about.collageImages[${index}]`}
|
|
88
|
+
style={{
|
|
89
|
+
position: 'relative',
|
|
90
|
+
overflow: 'hidden',
|
|
91
|
+
borderRadius: '1rem',
|
|
92
|
+
border: '1px solid var(--color-border, #e2e8f0)',
|
|
93
|
+
margin: 0,
|
|
94
|
+
minHeight: '140px',
|
|
95
|
+
background: 'var(--color-secondary, #f1f5f9)',
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
<img
|
|
99
|
+
src={item.image || '/placeholder.svg'}
|
|
100
|
+
alt={item.caption || ''}
|
|
101
|
+
data-preview-field-path={`about.collageImages[${index}].image`}
|
|
102
|
+
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
|
|
103
|
+
/>
|
|
104
|
+
{item.caption && (
|
|
105
|
+
<figcaption
|
|
106
|
+
style={{
|
|
107
|
+
position: 'absolute',
|
|
108
|
+
insetInline: 0,
|
|
109
|
+
bottom: 0,
|
|
110
|
+
padding: '0.75rem 1rem',
|
|
111
|
+
background: 'linear-gradient(transparent, rgba(15,23,42,0.85))',
|
|
112
|
+
color: '#fff',
|
|
113
|
+
fontSize: '0.8125rem',
|
|
114
|
+
fontWeight: 600,
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
<span data-preview-field-path={`about.collageImages[${index}].caption`}>
|
|
118
|
+
{item.caption}
|
|
119
|
+
</span>
|
|
120
|
+
</figcaption>
|
|
121
|
+
)}
|
|
122
|
+
</figure>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
40
126
|
</section>
|
|
41
127
|
</div>
|
|
42
128
|
);
|