@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.
Files changed (52) hide show
  1. package/bin/cli.js +343 -0
  2. package/defaults/definitions/block-layouts/three-column.yaml +20 -0
  3. package/defaults/definitions/block-layouts/two-column.yaml +29 -0
  4. package/defaults/definitions/blocks/button.yaml +15 -0
  5. package/defaults/definitions/blocks/header.yaml +15 -0
  6. package/defaults/definitions/blocks/header_image.yaml +22 -0
  7. package/defaults/definitions/blocks/hero.yaml +27 -0
  8. package/defaults/definitions/blocks/image_card.yaml +57 -0
  9. package/defaults/definitions/blocks/single_image.yaml +28 -0
  10. package/defaults/definitions/blocks/text.yaml +16 -0
  11. package/defaults/definitions/channels/ecommerce.yaml +24 -0
  12. package/defaults/definitions/channels/website.yaml +32 -0
  13. package/defaults/definitions/field-sets/access.yaml +29 -0
  14. package/defaults/definitions/field-sets/channel-base.yaml +31 -0
  15. package/defaults/definitions/field-sets/comments.yaml +11 -0
  16. package/defaults/definitions/field-sets/content-block.yaml +46 -0
  17. package/defaults/definitions/field-sets/page-base.yaml +49 -0
  18. package/defaults/definitions/field-sets/page-meta.yaml +29 -0
  19. package/defaults/definitions/field-sets/page-social.yaml +80 -0
  20. package/defaults/definitions/field-sets/tree-base.yaml +31 -0
  21. package/defaults/definitions/field-sets/visibility.yaml +8 -0
  22. package/defaults/definitions/node-layouts/landing-page.yml +22 -0
  23. package/defaults/definitions/node-layouts/single-column.yml +38 -0
  24. package/defaults/definitions/node-layouts/three-column.yml +41 -0
  25. package/defaults/definitions/node-layouts/two-column.yml +40 -0
  26. package/defaults/definitions/nodes/address.yaml +32 -0
  27. package/defaults/definitions/nodes/article.yaml +43 -0
  28. package/defaults/definitions/nodes/event.yaml +49 -0
  29. package/defaults/definitions/nodes/external-link.yaml +26 -0
  30. package/defaults/definitions/nodes/page.yaml +65 -0
  31. package/defaults/definitions/nodes/person.yaml +37 -0
  32. package/defaults/definitions/nodes/product.yaml +86 -0
  33. package/defaults/definitions/trees/navigation.yaml +29 -0
  34. package/defaults/definitions/trees/social-links.yaml +9 -0
  35. package/definitions/_page.yaml +52 -0
  36. package/definitions/editor/button.ts +11 -0
  37. package/definitions/editor/default.ts +21 -0
  38. package/definitions/editor/header.ts +36 -0
  39. package/definitions/editor/minimal.ts +15 -0
  40. package/definitions/validators/iban.ts +12 -0
  41. package/definitions/validators/magic-word.ts +7 -0
  42. package/dist/assets/index-10GEw0KY.css +1 -0
  43. package/dist/assets/index-u699KJW2.js +1392 -0
  44. package/dist/index.html +13 -0
  45. package/dist/index.js +7922 -1022
  46. package/dist/media/kama/2026/08/17a098c92bc54ea4b4441e531a34575b.jpg +0 -0
  47. package/dist/media/kama/2026/08/3e9ae457dd23461680cc699f42d07cf0.jpg +0 -0
  48. package/dist/media/kama/2026/08/8e5b3a2fd194459f9b962f8e12928bf0.jpg +0 -0
  49. package/dist/media/kama/2026/08/8ea5c21026cd4944bdf78c99fe2544a9.jpg +0 -0
  50. package/package.json +25 -11
  51. package/schema.sql +221 -0
  52. 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,15 @@
1
+ id: button
2
+ label: Button
3
+ group: Content
4
+ description: Button text
5
+
6
+ fields:
7
+ - text:
8
+ label: Text
9
+ type: richtext
10
+ editor: button
11
+ public: true
12
+
13
+ - fieldSet: access
14
+ - hidden:
15
+ default: false
@@ -0,0 +1,15 @@
1
+ id: header
2
+ label: Header
3
+ group: Content
4
+
5
+ fields:
6
+ - headline:
7
+ type: richtext
8
+ editor: header
9
+ label: Headline
10
+ public: true
11
+ default: "Header here..."
12
+
13
+ - fieldSet: access
14
+ - hidden:
15
+ default: false
@@ -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