@brightsideoy/kama 0.1.0-alpha.1 ā 0.1.0-alpha.100
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/cli.js +343 -0
- package/defaults/definitions/block-layouts/three-column.yaml +20 -0
- package/defaults/definitions/block-layouts/two-column.yaml +29 -0
- package/defaults/definitions/blocks/button.yaml +15 -0
- package/defaults/definitions/blocks/header.yaml +15 -0
- package/defaults/definitions/blocks/header_image.yaml +22 -0
- package/defaults/definitions/blocks/hero.yaml +27 -0
- package/defaults/definitions/blocks/image_card.yaml +57 -0
- package/defaults/definitions/blocks/single_image.yaml +28 -0
- package/defaults/definitions/blocks/text.yaml +16 -0
- package/defaults/definitions/channels/ecommerce.yaml +24 -0
- package/defaults/definitions/channels/website.yaml +32 -0
- package/defaults/definitions/field-sets/access.yaml +29 -0
- package/defaults/definitions/field-sets/channel-base.yaml +31 -0
- package/defaults/definitions/field-sets/comments.yaml +11 -0
- package/defaults/definitions/field-sets/content-block.yaml +46 -0
- package/defaults/definitions/field-sets/page-base.yaml +49 -0
- package/defaults/definitions/field-sets/page-meta.yaml +29 -0
- package/defaults/definitions/field-sets/page-social.yaml +80 -0
- package/defaults/definitions/field-sets/tree-base.yaml +31 -0
- package/defaults/definitions/field-sets/visibility.yaml +8 -0
- package/defaults/definitions/node-layouts/landing-page.yml +22 -0
- package/defaults/definitions/node-layouts/single-column.yml +38 -0
- package/defaults/definitions/node-layouts/three-column.yml +41 -0
- package/defaults/definitions/node-layouts/two-column.yml +40 -0
- package/defaults/definitions/nodes/address.yaml +32 -0
- package/defaults/definitions/nodes/article.yaml +43 -0
- package/defaults/definitions/nodes/event.yaml +49 -0
- package/defaults/definitions/nodes/external-link.yaml +26 -0
- package/defaults/definitions/nodes/page.yaml +65 -0
- package/defaults/definitions/nodes/person.yaml +37 -0
- package/defaults/definitions/nodes/product.yaml +86 -0
- package/defaults/definitions/trees/navigation.yaml +29 -0
- package/defaults/definitions/trees/social-links.yaml +9 -0
- package/definitions/_page.yaml +52 -0
- package/definitions/editor/button.ts +11 -0
- package/definitions/editor/default.ts +21 -0
- package/definitions/editor/header.ts +36 -0
- package/definitions/editor/minimal.ts +15 -0
- package/definitions/validators/iban.ts +12 -0
- package/definitions/validators/magic-word.ts +7 -0
- package/dist/assets/index-10GEw0KY.css +1 -0
- package/dist/assets/index-u699KJW2.js +1392 -0
- package/dist/index.html +13 -0
- package/dist/index.js +7922 -1022
- package/dist/media/kama/2026/08/17a098c92bc54ea4b4441e531a34575b.jpg +0 -0
- package/dist/media/kama/2026/08/3e9ae457dd23461680cc699f42d07cf0.jpg +0 -0
- package/dist/media/kama/2026/08/8e5b3a2fd194459f9b962f8e12928bf0.jpg +0 -0
- package/dist/media/kama/2026/08/8ea5c21026cd4944bdf78c99fe2544a9.jpg +0 -0
- package/package.json +25 -11
- package/schema.sql +221 -0
- package/scripts/bundle-definitions.js +176 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import * as readline from 'node:readline/promises';
|
|
6
|
+
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
console.log('\nš„ --- KAMA SITE GENERATOR --- š„\n');
|
|
14
|
+
|
|
15
|
+
console.log('Select mode:');
|
|
16
|
+
console.log(' 1) Kickstart new Kama Monorepo (CMS + Initial Site)');
|
|
17
|
+
console.log(' 2) Add a new Astro Site to current workspace (sites/)');
|
|
18
|
+
|
|
19
|
+
const choice = await rl.question('\nChoice [1/2]: ');
|
|
20
|
+
|
|
21
|
+
if (choice.trim() === '2') {
|
|
22
|
+
await createNewSiteOnly();
|
|
23
|
+
} else {
|
|
24
|
+
await createFullWorkspace();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
rl.close();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* MODE 1: Kickstart full Monorepo Workspace
|
|
32
|
+
*/
|
|
33
|
+
async function createFullWorkspace() {
|
|
34
|
+
const projName = (await rl.question('Project folder name [kama-workspace]: ')) || 'kama-workspace';
|
|
35
|
+
const cmsDomain = (await rl.question('CMS Admin Domain (e.g., admin.t3cloud.eu): ')) || 'admin.t3cloud.eu';
|
|
36
|
+
const siteDomain = (await rl.question('First Website Domain (e.g., kama.t3cloud.eu): ')) || 'kama.t3cloud.eu';
|
|
37
|
+
|
|
38
|
+
const rootDir = join(process.cwd(), projName);
|
|
39
|
+
|
|
40
|
+
// šÆ Derive dynamic worker, database, and bucket names from project folder name
|
|
41
|
+
const workerName = `${projName}-worker`.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
42
|
+
let dbName = `${projName}-db`.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
43
|
+
const bucketName = `${projName}-media-prod`.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
44
|
+
|
|
45
|
+
console.log(`\nš Creating workspace folder at ./${projName}...`);
|
|
46
|
+
mkdirSync(rootDir, { recursive: true });
|
|
47
|
+
|
|
48
|
+
// šÆ Scaffold schemas directory for project-level custom YAML definitions
|
|
49
|
+
mkdirSync(join(rootDir, 'schemas', 'nodes'), { recursive: true });
|
|
50
|
+
mkdirSync(join(rootDir, 'schemas', 'blocks'), { recursive: true });
|
|
51
|
+
|
|
52
|
+
// --- AUTO D1 CREATION MAGIC ---
|
|
53
|
+
let d1DatabaseId = 'REPLACE_WITH_YOUR_D1_ID';
|
|
54
|
+
let isAuthenticated = false;
|
|
55
|
+
|
|
56
|
+
console.log(`\nš Checking Cloudflare authentication...`);
|
|
57
|
+
try {
|
|
58
|
+
execSync('npx wrangler whoami', { stdio: 'pipe' });
|
|
59
|
+
isAuthenticated = true;
|
|
60
|
+
console.log(`ā
Logged in to Cloudflare.`);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.log(`ā ļø You are not logged in to Cloudflare.`);
|
|
63
|
+
console.log(`š Launching browser for Cloudflare login...`);
|
|
64
|
+
try {
|
|
65
|
+
execSync('npx wrangler login', { stdio: 'inherit' });
|
|
66
|
+
isAuthenticated = true;
|
|
67
|
+
console.log(`ā
Successfully logged in!`);
|
|
68
|
+
} catch (loginError) {
|
|
69
|
+
console.log(`ā Login failed or was cancelled.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isAuthenticated) {
|
|
74
|
+
try {
|
|
75
|
+
console.log(`\nšļø Checking existing Cloudflare D1 databases...`);
|
|
76
|
+
const listOutput = execSync(`npx wrangler d1 list --json`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
77
|
+
|
|
78
|
+
const jsonStart = listOutput.indexOf('[');
|
|
79
|
+
const jsonEnd = listOutput.lastIndexOf(']') + 1;
|
|
80
|
+
let databases = [];
|
|
81
|
+
|
|
82
|
+
if (jsonStart !== -1 && jsonEnd !== -1) {
|
|
83
|
+
databases = JSON.parse(listOutput.substring(jsonStart, jsonEnd));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const existingDb = databases.find(db => db.name === dbName);
|
|
87
|
+
|
|
88
|
+
if (existingDb) {
|
|
89
|
+
console.log(`ā ļø Database '${dbName}' already exists on your account.`);
|
|
90
|
+
const useExisting = await rl.question(`Do you want to link this existing database? (y/n) [y]: `);
|
|
91
|
+
|
|
92
|
+
if (useExisting.toLowerCase() !== 'n') {
|
|
93
|
+
d1DatabaseId = existingDb.uuid;
|
|
94
|
+
console.log(`ā
Linked existing database ID: ${d1DatabaseId}`);
|
|
95
|
+
} else {
|
|
96
|
+
dbName = (await rl.question(`Enter a new database name: `)).trim();
|
|
97
|
+
console.log(`\nšļø Creating new database '${dbName}'...`);
|
|
98
|
+
const d1Output = execSync(`npx wrangler d1 create ${dbName}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
99
|
+
const match = d1Output.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/);
|
|
100
|
+
if (match) {
|
|
101
|
+
d1DatabaseId = match[0];
|
|
102
|
+
console.log(`ā
Success! Database ID bound: ${d1DatabaseId}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
console.log(`\nšļø Attempting to auto-create Cloudflare D1 Database '${dbName}'...`);
|
|
107
|
+
const d1Output = execSync(`npx wrangler d1 create ${dbName}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
108
|
+
const match = d1Output.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/);
|
|
109
|
+
if (match) {
|
|
110
|
+
d1DatabaseId = match[0];
|
|
111
|
+
console.log(`ā
Success! Database ID bound: ${d1DatabaseId}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.log(`ā ļø Database operation failed. We'll leave a placeholder.`);
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
console.log(`ā ļø Skipping database creation. We'll leave a placeholder in wrangler.json.`);
|
|
119
|
+
}
|
|
120
|
+
// ------------------------------
|
|
121
|
+
|
|
122
|
+
// šÆ 1. Root package.json
|
|
123
|
+
writeFileSync(
|
|
124
|
+
join(rootDir, 'package.json'),
|
|
125
|
+
JSON.stringify({
|
|
126
|
+
name: projName,
|
|
127
|
+
private: true,
|
|
128
|
+
type: 'module',
|
|
129
|
+
workspaces: ['cms-worker', 'sites/*'],
|
|
130
|
+
scripts: {
|
|
131
|
+
'bundle:schemas': 'node node_modules/@brightsideoy/kama/scripts/bundle-definitions.js',
|
|
132
|
+
'dev:cms': 'npm run bundle:schemas && npm run dev -w cms-worker',
|
|
133
|
+
'deploy:cms': 'npm run bundle:schemas && npm run deploy -w cms-worker',
|
|
134
|
+
},
|
|
135
|
+
dependencies: {
|
|
136
|
+
'@brightsideoy/kama': 'alpha',
|
|
137
|
+
}
|
|
138
|
+
}, null, 2)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// šÆ 2. Setup CMS Worker with Dynamic Names
|
|
142
|
+
console.log(`\nāļø Configuring CMS Worker '${workerName}' (${cmsDomain})...`);
|
|
143
|
+
const cmsDir = join(rootDir, 'cms-worker');
|
|
144
|
+
mkdirSync(cmsDir, { recursive: true });
|
|
145
|
+
|
|
146
|
+
writeFileSync(
|
|
147
|
+
join(cmsDir, 'index.js'),
|
|
148
|
+
`export { default } from '@brightsideoy/kama';\n`
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
writeFileSync(
|
|
152
|
+
join(cmsDir, 'package.json'),
|
|
153
|
+
JSON.stringify({
|
|
154
|
+
name: workerName,
|
|
155
|
+
type: 'module',
|
|
156
|
+
version: '1.0.0',
|
|
157
|
+
scripts: {
|
|
158
|
+
"bundle:schemas": "node ../node_modules/@brightsideoy/kama/scripts/bundle-definitions.js",
|
|
159
|
+
"schema:local": `npm run bundle:schemas && wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql`,
|
|
160
|
+
"schema:remote": `npm run bundle:schemas && wrangler d1 execute ${dbName} --remote --file=../node_modules/@brightsideoy/kama/schema.sql`,
|
|
161
|
+
"dev": "npm run schema:local && wrangler dev",
|
|
162
|
+
"deploy": "npm run schema:remote && wrangler deploy"
|
|
163
|
+
},
|
|
164
|
+
dependencies: {
|
|
165
|
+
'@brightsideoy/kama': 'alpha',
|
|
166
|
+
},
|
|
167
|
+
devDependencies: {
|
|
168
|
+
wrangler: '^4.127.1',
|
|
169
|
+
tsx: '^4.19.2',
|
|
170
|
+
},
|
|
171
|
+
}, null, 2)
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// šÆ Fixed CMS wrangler.json (Added PUBLIC_FRONTEND_URL & CLOUDFLARE_ZONE_ID to public vars)
|
|
175
|
+
writeFileSync(
|
|
176
|
+
join(cmsDir, 'wrangler.json'),
|
|
177
|
+
JSON.stringify({
|
|
178
|
+
$schema: '../node_modules/wrangler/config-schema.json',
|
|
179
|
+
name: workerName,
|
|
180
|
+
main: 'index.js',
|
|
181
|
+
compatibility_date: '2026-08-01',
|
|
182
|
+
compatibility_flags: ['nodejs_compat'],
|
|
183
|
+
assets: {
|
|
184
|
+
directory: '../node_modules/@brightsideoy/kama/dist',
|
|
185
|
+
binding: 'ASSETS'
|
|
186
|
+
},
|
|
187
|
+
images: {
|
|
188
|
+
binding: 'IMAGES'
|
|
189
|
+
},
|
|
190
|
+
routes: [{ pattern: cmsDomain, custom_domain: true }],
|
|
191
|
+
vars: {
|
|
192
|
+
CDN_DOMAIN: cmsDomain,
|
|
193
|
+
PUBLIC_FRONTEND_URL: `https://${siteDomain}`,
|
|
194
|
+
CLOUDFLARE_ZONE_ID: "REPLACE_WITH_YOUR_32_CHAR_ZONE_ID"
|
|
195
|
+
},
|
|
196
|
+
d1_databases: [{
|
|
197
|
+
binding: 'DB',
|
|
198
|
+
database_name: dbName,
|
|
199
|
+
database_id: d1DatabaseId,
|
|
200
|
+
}],
|
|
201
|
+
r2_buckets: [{
|
|
202
|
+
binding: 'MEDIA_BUCKET',
|
|
203
|
+
bucket_name: bucketName,
|
|
204
|
+
}],
|
|
205
|
+
}, null, 2)
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// šÆ Fixed .dev.vars (Only contains API token for local dev, not public Zone ID)
|
|
209
|
+
writeFileSync(
|
|
210
|
+
join(cmsDir, '.dev.vars'),
|
|
211
|
+
`# Cloudflare Edge Cache Purging Secrets\nCLOUDFLARE_API_TOKEN=cfut_YOUR_CUSTOM_PURGE_TOKEN\n`
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// šÆ 3. Setup First Astro Site tied to target workerName
|
|
215
|
+
const siteFolderName = siteDomain.split('.')[0];
|
|
216
|
+
await generateAstroSite(rootDir, siteFolderName, siteDomain, cmsDomain, workerName);
|
|
217
|
+
|
|
218
|
+
console.log(`\n⨠Workspace ready! Next steps:`);
|
|
219
|
+
console.log(` 1. cd ${projName}`);
|
|
220
|
+
console.log(` 2. npm install`);
|
|
221
|
+
if (d1DatabaseId === 'REPLACE_WITH_YOUR_D1_ID') {
|
|
222
|
+
console.log(` 3. Run 'npx wrangler login' and create a D1 database.`);
|
|
223
|
+
}
|
|
224
|
+
console.log(` 4. Update CLOUDFLARE_ZONE_ID in cms-worker/wrangler.json with your actual Zone ID`);
|
|
225
|
+
console.log(` 5. Securely store your purge token: cd cms-worker && npx wrangler secret put CLOUDFLARE_API_TOKEN`);
|
|
226
|
+
console.log(` 6. Run 'npm run dev:cms' from the workspace root to start the backend.`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* MODE 2: Add Site to existing workspace
|
|
231
|
+
*/
|
|
232
|
+
async function createNewSiteOnly() {
|
|
233
|
+
const rootDir = process.cwd();
|
|
234
|
+
|
|
235
|
+
if (!existsSync(join(rootDir, 'package.json'))) {
|
|
236
|
+
console.error('ā Error: Must run this command inside the root of your workspace.');
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let targetWorkerName = 'kama-worker';
|
|
241
|
+
const cmsWranglerPath = join(rootDir, 'cms-worker', 'wrangler.json');
|
|
242
|
+
if (existsSync(cmsWranglerPath)) {
|
|
243
|
+
try {
|
|
244
|
+
const cmsWrangler = JSON.parse(readFileSync(cmsWranglerPath, 'utf-8'));
|
|
245
|
+
if (cmsWrangler.name) targetWorkerName = cmsWrangler.name;
|
|
246
|
+
} catch (e) { }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const siteFolder = await rl.question('Site folder name (e.g., my-new-site): ');
|
|
250
|
+
const siteDomain = await rl.question('Website Domain (e.g., brand.com): ');
|
|
251
|
+
const cmsDomain = (await rl.question('CMS API Domain (e.g., admin.t3cloud.eu): ')) || 'admin.t3cloud.eu';
|
|
252
|
+
|
|
253
|
+
await generateAstroSite(rootDir, siteFolder, siteDomain, cmsDomain.replace(/^https?:\/\//, ''), targetWorkerName);
|
|
254
|
+
|
|
255
|
+
console.log(`\n⨠Site created in sites/${siteFolder}! Run 'npm install' at workspace root.`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Helper: Scaffolds the Astro site with middleware, wrangler.json, & slug page
|
|
260
|
+
*/
|
|
261
|
+
async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsDomain, workerName = 'kama-worker') {
|
|
262
|
+
console.log(`\nš Scaffolding Astro site (${siteFolder}) mapped to ${siteDomain} (Service: ${workerName})...`);
|
|
263
|
+
|
|
264
|
+
const siteDir = join(rootDir, 'sites', siteFolder);
|
|
265
|
+
mkdirSync(join(siteDir, 'src/pages'), { recursive: true });
|
|
266
|
+
mkdirSync(join(siteDir, 'src/layouts'), { recursive: true });
|
|
267
|
+
mkdirSync(join(siteDir, 'src/components/blocks'), { recursive: true });
|
|
268
|
+
mkdirSync(join(siteDir, 'src/lib'), { recursive: true });
|
|
269
|
+
|
|
270
|
+
writeFileSync(
|
|
271
|
+
join(siteDir, 'package.json'),
|
|
272
|
+
JSON.stringify({
|
|
273
|
+
name: siteFolder,
|
|
274
|
+
type: 'module',
|
|
275
|
+
version: '0.0.1',
|
|
276
|
+
engines: { node: '>=22.12.0' },
|
|
277
|
+
scripts: {
|
|
278
|
+
dev: 'astro dev',
|
|
279
|
+
build: 'astro build',
|
|
280
|
+
preview: 'astro preview',
|
|
281
|
+
deploy: 'astro build && wrangler deploy',
|
|
282
|
+
},
|
|
283
|
+
dependencies: {
|
|
284
|
+
'@astrojs/cloudflare': '^14.2.5',
|
|
285
|
+
'@brightsideoy/kama': 'alpha',
|
|
286
|
+
astro: '^7.2.2',
|
|
287
|
+
wrangler: '^4.127.1',
|
|
288
|
+
},
|
|
289
|
+
}, null, 2)
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// šÆ Fixed Astro wrangler.json: Added `run_worker_first: true` to prevent media routing 404s on Edge
|
|
293
|
+
writeFileSync(
|
|
294
|
+
join(siteDir, 'wrangler.json'),
|
|
295
|
+
JSON.stringify({
|
|
296
|
+
$schema: 'node_modules/wrangler/config-schema.json',
|
|
297
|
+
name: siteFolder,
|
|
298
|
+
compatibility_date: '2026-08-01',
|
|
299
|
+
compatibility_flags: ['nodejs_compat'],
|
|
300
|
+
assets: {
|
|
301
|
+
directory: './dist/client',
|
|
302
|
+
binding: 'ASSETS',
|
|
303
|
+
html_handling: 'drop-trailing-slash',
|
|
304
|
+
not_found_handling: '404-page',
|
|
305
|
+
run_worker_first: true
|
|
306
|
+
},
|
|
307
|
+
services: [{ binding: 'CMS_WORKER', service: workerName }],
|
|
308
|
+
routes: [{ pattern: siteDomain, custom_domain: true }],
|
|
309
|
+
vars: {
|
|
310
|
+
PUBLIC_CMS_URL: `https://${cmsDomain}`,
|
|
311
|
+
PUBLIC_API_URL: `https://${cmsDomain}`,
|
|
312
|
+
},
|
|
313
|
+
}, null, 2)
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const astroConfig = `import { defineConfig } from 'astro/config';
|
|
317
|
+
import cloudflare from '@astrojs/cloudflare';
|
|
318
|
+
|
|
319
|
+
export default defineConfig({
|
|
320
|
+
output: 'server',
|
|
321
|
+
trailingSlash: 'ignore',
|
|
322
|
+
image: {
|
|
323
|
+
remotePatterns: [
|
|
324
|
+
{ protocol: 'http', hostname: 'localhost', port: '3000' },
|
|
325
|
+
{ protocol: 'https', hostname: '${cmsDomain}' }
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
build: {
|
|
329
|
+
inlineStylesheets: 'always',
|
|
330
|
+
},
|
|
331
|
+
adapter: cloudflare({
|
|
332
|
+
imageService: 'cloudflare',
|
|
333
|
+
})
|
|
334
|
+
});`;
|
|
335
|
+
writeFileSync(join(siteDir, 'astro.config.mjs'), astroConfig);
|
|
336
|
+
|
|
337
|
+
writeFileSync(
|
|
338
|
+
join(siteDir, '.env'),
|
|
339
|
+
`PUBLIC_CMS_URL="https://${cmsDomain}"\nPUBLIC_API_URL="https://${cmsDomain}"\n`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
id: three-column
|
|
2
|
+
label: Three Columns (33/33/33)
|
|
3
|
+
showLabel: false
|
|
4
|
+
group: Sub-Layouts
|
|
5
|
+
|
|
6
|
+
layout:
|
|
7
|
+
columns: "1fr 1fr 1fr"
|
|
8
|
+
areas:
|
|
9
|
+
- ["left", "middle", "right"]
|
|
10
|
+
|
|
11
|
+
regions:
|
|
12
|
+
left:
|
|
13
|
+
label: Left Column
|
|
14
|
+
showLabel: false # š Hides "LEFT COLUMN" header on this region cell
|
|
15
|
+
middle:
|
|
16
|
+
label: Middle Column
|
|
17
|
+
showLabel: false # š Hides "LEFT COLUMN" header on this region cell
|
|
18
|
+
right:
|
|
19
|
+
label: Right Column
|
|
20
|
+
showLabel: false # š Keeps "RIGHT COLUMN" visible
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
id: two-column
|
|
2
|
+
label: Two Columns (50/50)
|
|
3
|
+
showLabel: false # š Hides the main "Two Columns" badge on the canvas
|
|
4
|
+
group: Sub-Layouts
|
|
5
|
+
|
|
6
|
+
layout:
|
|
7
|
+
columns: "1fr 1fr"
|
|
8
|
+
areas:
|
|
9
|
+
- ["left", "right"]
|
|
10
|
+
|
|
11
|
+
regions:
|
|
12
|
+
left:
|
|
13
|
+
label: Left Column
|
|
14
|
+
showLabel: false # š Hides "LEFT COLUMN" header on this region cell
|
|
15
|
+
right:
|
|
16
|
+
label: Right Column
|
|
17
|
+
showLabel: false # š Keeps "RIGHT COLUMN" visible
|
|
18
|
+
|
|
19
|
+
# Optional: We can give the layout block its own settings!
|
|
20
|
+
fields:
|
|
21
|
+
gap:
|
|
22
|
+
label: Column Gap
|
|
23
|
+
type: select
|
|
24
|
+
default: "2rem"
|
|
25
|
+
options:
|
|
26
|
+
- { label: "None", value: "0" }
|
|
27
|
+
- { label: "Small", value: "1rem" }
|
|
28
|
+
- { label: "Medium", value: "2rem" }
|
|
29
|
+
- { label: "Large", value: "4rem" }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: header_image
|
|
2
|
+
label: Header Image
|
|
3
|
+
group: Media
|
|
4
|
+
description: Image for page header.
|
|
5
|
+
|
|
6
|
+
fields:
|
|
7
|
+
- image:
|
|
8
|
+
label: Image
|
|
9
|
+
type: asset
|
|
10
|
+
required: true
|
|
11
|
+
multiple: false
|
|
12
|
+
maxSizeMB: 15
|
|
13
|
+
allowedTypes: ["image/*"]
|
|
14
|
+
crops:
|
|
15
|
+
standard:
|
|
16
|
+
label: "Page header (21:9)"
|
|
17
|
+
aspectRatio: 2.333
|
|
18
|
+
public: true
|
|
19
|
+
|
|
20
|
+
- fieldSet: access
|
|
21
|
+
- hidden:
|
|
22
|
+
default: false
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
id: hero
|
|
2
|
+
label: Hero
|
|
3
|
+
group: Content
|
|
4
|
+
|
|
5
|
+
fields:
|
|
6
|
+
- heroBadge:
|
|
7
|
+
type: text
|
|
8
|
+
label: Badge
|
|
9
|
+
public: true
|
|
10
|
+
- heroTitle:
|
|
11
|
+
type: text
|
|
12
|
+
label: Title
|
|
13
|
+
public: true
|
|
14
|
+
- heroSubTitle:
|
|
15
|
+
type: text
|
|
16
|
+
label: Sub-Title
|
|
17
|
+
public: true
|
|
18
|
+
- headline:
|
|
19
|
+
type: richtext
|
|
20
|
+
editor: header
|
|
21
|
+
label: Headline
|
|
22
|
+
public: true
|
|
23
|
+
default: "Header here..."
|
|
24
|
+
|
|
25
|
+
- fieldSet: access
|
|
26
|
+
- hidden:
|
|
27
|
+
default: false
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
id: image_card
|
|
2
|
+
label: Image Card
|
|
3
|
+
Group: Content
|
|
4
|
+
description: A versatile card with an image, text, and a CTA button.
|
|
5
|
+
|
|
6
|
+
fields:
|
|
7
|
+
- image:
|
|
8
|
+
label: Card Image
|
|
9
|
+
showLabel: false
|
|
10
|
+
type: asset
|
|
11
|
+
required: true
|
|
12
|
+
multiple: false
|
|
13
|
+
maxSizeMB: 15
|
|
14
|
+
maxPreviewWidth: 300px
|
|
15
|
+
allowedTypes: ["image/*"]
|
|
16
|
+
crops:
|
|
17
|
+
standard:
|
|
18
|
+
label: "Card Header (16:9)"
|
|
19
|
+
aspectRatio: 3.0
|
|
20
|
+
public: true
|
|
21
|
+
|
|
22
|
+
- headline:
|
|
23
|
+
label: Headline
|
|
24
|
+
type: text
|
|
25
|
+
required: true
|
|
26
|
+
public: true
|
|
27
|
+
default: "Headline..."
|
|
28
|
+
|
|
29
|
+
- summary:
|
|
30
|
+
label: Text
|
|
31
|
+
description: Keep it brief, 2-3 sentences max.
|
|
32
|
+
type: richtext
|
|
33
|
+
editor: minimal
|
|
34
|
+
public: true
|
|
35
|
+
default: "Card text content..."
|
|
36
|
+
|
|
37
|
+
- link:
|
|
38
|
+
label: Link
|
|
39
|
+
type: link
|
|
40
|
+
allowedLinkTypes:
|
|
41
|
+
- tree
|
|
42
|
+
- node
|
|
43
|
+
- url
|
|
44
|
+
- file
|
|
45
|
+
- email
|
|
46
|
+
- telephone
|
|
47
|
+
|
|
48
|
+
- linkText:
|
|
49
|
+
label: Button Text
|
|
50
|
+
type: text
|
|
51
|
+
description: "e.g., 'Read More' or 'Buy Now'"
|
|
52
|
+
default: "Read More"
|
|
53
|
+
public: true
|
|
54
|
+
|
|
55
|
+
- fieldSet: access
|
|
56
|
+
- hidden:
|
|
57
|
+
default: false
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: single_image
|
|
2
|
+
label: Single Image
|
|
3
|
+
group: Media
|
|
4
|
+
description: Just a single image block.
|
|
5
|
+
|
|
6
|
+
fields:
|
|
7
|
+
- image:
|
|
8
|
+
label: Image
|
|
9
|
+
type: asset
|
|
10
|
+
required: true
|
|
11
|
+
multiple: false
|
|
12
|
+
maxSizeMB: 15
|
|
13
|
+
allowedTypes: ["image/*"]
|
|
14
|
+
public: true
|
|
15
|
+
|
|
16
|
+
- link:
|
|
17
|
+
label: Link
|
|
18
|
+
type: link
|
|
19
|
+
position: sidebar
|
|
20
|
+
allowedLinkTypes:
|
|
21
|
+
- tree
|
|
22
|
+
- node
|
|
23
|
+
- url
|
|
24
|
+
- file
|
|
25
|
+
|
|
26
|
+
- fieldSet: access
|
|
27
|
+
- hidden:
|
|
28
|
+
default: false
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: text
|
|
2
|
+
label: Text
|
|
3
|
+
group: Content
|
|
4
|
+
description: Paragraph body
|
|
5
|
+
|
|
6
|
+
fields:
|
|
7
|
+
- body:
|
|
8
|
+
label: Text
|
|
9
|
+
type: richtext
|
|
10
|
+
editor: default
|
|
11
|
+
default: "Write content here..."
|
|
12
|
+
public: true
|
|
13
|
+
|
|
14
|
+
- fieldSet: access
|
|
15
|
+
- hidden:
|
|
16
|
+
default: false
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
id: ecommerce
|
|
2
|
+
label: "E-Commerce"
|
|
3
|
+
|
|
4
|
+
tabs:
|
|
5
|
+
settings:
|
|
6
|
+
label: "Channel"
|
|
7
|
+
fields:
|
|
8
|
+
- fieldSet: channel-base
|
|
9
|
+
|
|
10
|
+
- currency:
|
|
11
|
+
label: Default Currency
|
|
12
|
+
type: select
|
|
13
|
+
default: "EUR"
|
|
14
|
+
options:
|
|
15
|
+
- "EUR"
|
|
16
|
+
- "USD"
|
|
17
|
+
- "GBP"
|
|
18
|
+
|
|
19
|
+
- checkoutUrl:
|
|
20
|
+
label: Checkout Endpoint URL
|
|
21
|
+
type: text
|
|
22
|
+
public: true
|
|
23
|
+
|
|
24
|
+
- fieldSet: access
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
id: website
|
|
2
|
+
label: "Website"
|
|
3
|
+
|
|
4
|
+
tabs:
|
|
5
|
+
settings:
|
|
6
|
+
label: "Channel"
|
|
7
|
+
fields:
|
|
8
|
+
- fieldSet: channel-base
|
|
9
|
+
|
|
10
|
+
- domain:
|
|
11
|
+
label: Hostname
|
|
12
|
+
type: text
|
|
13
|
+
dbType: text
|
|
14
|
+
index: true
|
|
15
|
+
public: true
|
|
16
|
+
|
|
17
|
+
- baseUrl:
|
|
18
|
+
label: Base URL
|
|
19
|
+
type: text
|
|
20
|
+
dbType: text
|
|
21
|
+
public: true
|
|
22
|
+
|
|
23
|
+
- defaultLanguage:
|
|
24
|
+
label: Channel Language / Locale
|
|
25
|
+
type: language-select
|
|
26
|
+
dbType: text
|
|
27
|
+
public: true
|
|
28
|
+
|
|
29
|
+
access:
|
|
30
|
+
label: "Access"
|
|
31
|
+
fields:
|
|
32
|
+
- fieldSet: access
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
tab:
|
|
2
|
+
id: access
|
|
3
|
+
label: "Access"
|
|
4
|
+
|
|
5
|
+
fields:
|
|
6
|
+
isHidden: # šÆ Renamed from 'hidden' to match the DB column
|
|
7
|
+
type: boolean
|
|
8
|
+
label: Hidden
|
|
9
|
+
default: false
|
|
10
|
+
public: false
|
|
11
|
+
variant: danger
|
|
12
|
+
dbType: integer
|
|
13
|
+
index: true
|
|
14
|
+
position: sidebar
|
|
15
|
+
liveSync: true
|
|
16
|
+
|
|
17
|
+
startShowingAt:
|
|
18
|
+
type: datetime
|
|
19
|
+
label: Start Showing At
|
|
20
|
+
position: sidebar
|
|
21
|
+
public: false
|
|
22
|
+
index: true
|
|
23
|
+
|
|
24
|
+
stopShowingAt:
|
|
25
|
+
type: datetime
|
|
26
|
+
label: Stop Showing At
|
|
27
|
+
position: sidebar
|
|
28
|
+
public: false
|
|
29
|
+
index: true
|