@jsonpages/cli 3.0.8 → 3.0.11

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.
@@ -111,665 +111,665 @@ END_OF_FILE_CONTENT
111
111
  mkdir -p "src/components/arch-layers"
112
112
  echo "Creating src/components/arch-layers/View.tsx..."
113
113
  cat << 'END_OF_FILE_CONTENT' > "src/components/arch-layers/View.tsx"
114
- import React from 'react';
115
- import { cn } from '@/lib/utils';
116
- import type { ArchLayersData, ArchLayersSettings, ArchLayerLevel, SyntaxTokenType } from './types';
117
-
118
- const layerBgStyles: Record<ArchLayerLevel, string> = {
119
- l0: 'bg-[#3b82f6]',
120
- l1: 'bg-[rgba(59,130,246,0.6)]',
121
- l2: 'bg-[rgba(59,130,246,0.35)]',
122
- };
123
-
124
- const tokenStyles: Record<SyntaxTokenType, string> = {
125
- plain: 'text-[#cbd5e1]',
126
- keyword: 'text-[#60a5fa]',
127
- type: 'text-[#22d3ee]',
128
- string: 'text-[#4ade80]',
129
- comment: 'text-[#64748b] italic',
130
- operator: 'text-[#f472b6]',
131
- };
132
-
133
- export const ArchLayers: React.FC<{ data: ArchLayersData; settings?: ArchLayersSettings }> = ({ data }) => {
134
- return (
135
- <section
136
- style={{
137
- '--local-bg': 'var(--card)',
138
- '--local-text': 'var(--foreground)',
139
- '--local-text-muted': 'var(--muted-foreground)',
140
- '--local-primary': 'var(--primary)',
141
- '--local-accent': 'var(--color-accent, #60a5fa)',
142
- '--local-border': 'var(--border)',
143
- '--local-deep': 'var(--background)',
144
- } as React.CSSProperties}
145
- className="relative z-0 py-28 bg-[var(--local-bg)]"
146
- >
147
- <div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
148
- <div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
149
- <div className="max-w-[1200px] mx-auto px-8">
150
- <div className="text-center">
151
- {data.label && (
152
- <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
153
- <span className="w-5 h-px bg-[var(--local-primary)]" />
154
- {data.label}
155
- </div>
156
- )}
157
- <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
158
- {data.title}
159
- </h2>
160
- {data.description && (
161
- <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] mx-auto leading-relaxed" data-jp-field="description">
162
- {data.description}
163
- </p>
164
- )}
165
- </div>
166
- <div className="mt-14 max-w-[740px] mx-auto">
167
- {data.layers.map((layer, idx) => (
168
- <div
169
- key={layer.id ?? idx}
170
- className="group border border-[rgba(255,255,255,0.06)] rounded-[7px] p-8 mb-4 bg-[rgba(255,255,255,0.015)] flex items-start gap-6 transition-all duration-300 hover:border-[rgba(59,130,246,0.2)] hover:translate-x-1.5"
171
- data-jp-item-id={layer.id ?? `legacy-${idx}`}
172
- data-jp-item-field="layers"
173
- >
174
- <div className={cn(
175
- 'shrink-0 w-9 h-9 rounded-lg flex items-center justify-center font-mono text-[0.85rem] font-bold text-white',
176
- layerBgStyles[layer.layerLevel]
177
- )}>
178
- {layer.number}
179
- </div>
180
- <div>
181
- <h4 className="text-[1.05rem] font-bold text-[var(--local-text)] mb-1.5">
182
- {layer.title}
183
- </h4>
184
- <p className="text-[0.92rem] text-[var(--local-text-muted)] leading-relaxed">
185
- {layer.description}
186
- </p>
187
- </div>
188
- </div>
189
- ))}
190
- </div>
191
- {data.codeLines && data.codeLines.length > 0 && (
192
- <div className="mt-12 max-w-[740px] mx-auto">
193
- <div className="border border-[rgba(255,255,255,0.08)] rounded-[7px] overflow-hidden bg-[var(--local-deep)]">
194
- <div className="flex items-center gap-2 px-5 py-3 bg-[rgba(255,255,255,0.03)] border-b border-[rgba(255,255,255,0.06)]">
195
- <span className="w-2.5 h-2.5 rounded-full bg-[#ef4444]" />
196
- <span className="w-2.5 h-2.5 rounded-full bg-[#f59e0b]" />
197
- <span className="w-2.5 h-2.5 rounded-full bg-[#22c55e]" />
198
- {data.codeFilename && (
199
- <span className="ml-3 font-mono text-[0.75rem] text-[var(--local-text-muted)] opacity-60" data-jp-field="codeFilename">
200
- {data.codeFilename}
201
- </span>
202
- )}
203
- </div>
204
- <div className="p-6 font-mono text-[0.82rem] leading-[1.7] overflow-x-auto">
205
- {data.codeLines.map((line, idx) => (
206
- <div key={idx} data-jp-item-id={(line as { id?: string }).id ?? `legacy-${idx}`} data-jp-item-field="codeLines">
207
- <span className={tokenStyles[line.tokenType]}>
208
- {line.content}
209
- </span>
210
- </div>
211
- ))}
212
- </div>
213
- </div>
214
- </div>
215
- )}
216
- </div>
217
- </section>
218
- );
219
- };
114
+ import React from 'react';
115
+ import { cn } from '@/lib/utils';
116
+ import type { ArchLayersData, ArchLayersSettings, ArchLayerLevel, SyntaxTokenType } from './types';
117
+
118
+ const layerBgStyles: Record<ArchLayerLevel, string> = {
119
+ l0: 'bg-[#3b82f6]',
120
+ l1: 'bg-[rgba(59,130,246,0.6)]',
121
+ l2: 'bg-[rgba(59,130,246,0.35)]',
122
+ };
123
+
124
+ const tokenStyles: Record<SyntaxTokenType, string> = {
125
+ plain: 'text-[#cbd5e1]',
126
+ keyword: 'text-[#60a5fa]',
127
+ type: 'text-[#22d3ee]',
128
+ string: 'text-[#4ade80]',
129
+ comment: 'text-[#64748b] italic',
130
+ operator: 'text-[#f472b6]',
131
+ };
132
+
133
+ export const ArchLayers: React.FC<{ data: ArchLayersData; settings?: ArchLayersSettings }> = ({ data }) => {
134
+ return (
135
+ <section
136
+ style={{
137
+ '--local-bg': 'var(--card)',
138
+ '--local-text': 'var(--foreground)',
139
+ '--local-text-muted': 'var(--muted-foreground)',
140
+ '--local-primary': 'var(--primary)',
141
+ '--local-accent': 'var(--color-accent, #60a5fa)',
142
+ '--local-border': 'var(--border)',
143
+ '--local-deep': 'var(--background)',
144
+ } as React.CSSProperties}
145
+ className="relative z-0 py-28 bg-[var(--local-bg)]"
146
+ >
147
+ <div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
148
+ <div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
149
+ <div className="max-w-[1200px] mx-auto px-8">
150
+ <div className="text-center">
151
+ {data.label && (
152
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
153
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
154
+ {data.label}
155
+ </div>
156
+ )}
157
+ <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
158
+ {data.title}
159
+ </h2>
160
+ {data.description && (
161
+ <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] mx-auto leading-relaxed" data-jp-field="description">
162
+ {data.description}
163
+ </p>
164
+ )}
165
+ </div>
166
+ <div className="mt-14 max-w-[740px] mx-auto">
167
+ {data.layers.map((layer, idx) => (
168
+ <div
169
+ key={layer.id ?? idx}
170
+ className="group border border-[rgba(255,255,255,0.06)] rounded-[7px] p-8 mb-4 bg-[rgba(255,255,255,0.015)] flex items-start gap-6 transition-all duration-300 hover:border-[rgba(59,130,246,0.2)] hover:translate-x-1.5"
171
+ data-jp-item-id={layer.id ?? `legacy-${idx}`}
172
+ data-jp-item-field="layers"
173
+ >
174
+ <div className={cn(
175
+ 'shrink-0 w-9 h-9 rounded-lg flex items-center justify-center font-mono text-[0.85rem] font-bold text-white',
176
+ layerBgStyles[layer.layerLevel]
177
+ )}>
178
+ {layer.number}
179
+ </div>
180
+ <div>
181
+ <h4 className="text-[1.05rem] font-bold text-[var(--local-text)] mb-1.5">
182
+ {layer.title}
183
+ </h4>
184
+ <p className="text-[0.92rem] text-[var(--local-text-muted)] leading-relaxed">
185
+ {layer.description}
186
+ </p>
187
+ </div>
188
+ </div>
189
+ ))}
190
+ </div>
191
+ {data.codeLines && data.codeLines.length > 0 && (
192
+ <div className="mt-12 max-w-[740px] mx-auto">
193
+ <div className="border border-[rgba(255,255,255,0.08)] rounded-[7px] overflow-hidden bg-[var(--local-deep)]">
194
+ <div className="flex items-center gap-2 px-5 py-3 bg-[rgba(255,255,255,0.03)] border-b border-[rgba(255,255,255,0.06)]">
195
+ <span className="w-2.5 h-2.5 rounded-full bg-[#ef4444]" />
196
+ <span className="w-2.5 h-2.5 rounded-full bg-[#f59e0b]" />
197
+ <span className="w-2.5 h-2.5 rounded-full bg-[#22c55e]" />
198
+ {data.codeFilename && (
199
+ <span className="ml-3 font-mono text-[0.75rem] text-[var(--local-text-muted)] opacity-60" data-jp-field="codeFilename">
200
+ {data.codeFilename}
201
+ </span>
202
+ )}
203
+ </div>
204
+ <div className="p-6 font-mono text-[0.82rem] leading-[1.7] overflow-x-auto">
205
+ {data.codeLines.map((line, idx) => (
206
+ <div key={idx} data-jp-item-id={(line as { id?: string }).id ?? `legacy-${idx}`} data-jp-item-field="codeLines">
207
+ <span className={tokenStyles[line.tokenType]}>
208
+ {line.content}
209
+ </span>
210
+ </div>
211
+ ))}
212
+ </div>
213
+ </div>
214
+ </div>
215
+ )}
216
+ </div>
217
+ </section>
218
+ );
219
+ };
220
220
 
221
221
  END_OF_FILE_CONTENT
222
222
  echo "Creating src/components/arch-layers/index.ts..."
223
223
  cat << 'END_OF_FILE_CONTENT' > "src/components/arch-layers/index.ts"
224
- export * from './View';
225
- export * from './schema';
226
- export * from './types';
224
+ export * from './View';
225
+ export * from './schema';
226
+ export * from './types';
227
227
 
228
228
  END_OF_FILE_CONTENT
229
229
  echo "Creating src/components/arch-layers/schema.ts..."
230
230
  cat << 'END_OF_FILE_CONTENT' > "src/components/arch-layers/schema.ts"
231
- import { z } from 'zod';
232
- import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
233
-
234
- export const ArchLayerLevelSchema = z.enum(['l0', 'l1', 'l2']);
235
- export const SyntaxTokenTypeSchema = z.enum(['plain', 'keyword', 'type', 'string', 'comment', 'operator']);
236
-
237
- const ArchLayerItemSchema = BaseArrayItem.extend({
238
- number: z.string().describe('ui:text'),
239
- layerLevel: ArchLayerLevelSchema.describe('ui:select'),
240
- title: z.string().describe('ui:text'),
241
- description: z.string().describe('ui:textarea'),
242
- });
243
-
244
- const SyntaxLineSchema = z.object({
245
- content: z.string().describe('ui:text'),
246
- tokenType: SyntaxTokenTypeSchema.default('plain').describe('ui:select'),
247
- });
248
-
249
- export const ArchLayersSchema = BaseSectionData.extend({
250
- label: z.string().optional().describe('ui:text'),
251
- title: z.string().describe('ui:text'),
252
- description: z.string().optional().describe('ui:textarea'),
253
- layers: z.array(ArchLayerItemSchema).describe('ui:list'),
254
- codeFilename: z.string().optional().describe('ui:text'),
255
- codeLines: z.array(SyntaxLineSchema).optional().describe('ui:list'),
256
- });
231
+ import { z } from 'zod';
232
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
233
+
234
+ export const ArchLayerLevelSchema = z.enum(['l0', 'l1', 'l2']);
235
+ export const SyntaxTokenTypeSchema = z.enum(['plain', 'keyword', 'type', 'string', 'comment', 'operator']);
236
+
237
+ const ArchLayerItemSchema = BaseArrayItem.extend({
238
+ number: z.string().describe('ui:text'),
239
+ layerLevel: ArchLayerLevelSchema.describe('ui:select'),
240
+ title: z.string().describe('ui:text'),
241
+ description: z.string().describe('ui:textarea'),
242
+ });
243
+
244
+ const SyntaxLineSchema = z.object({
245
+ content: z.string().describe('ui:text'),
246
+ tokenType: SyntaxTokenTypeSchema.default('plain').describe('ui:select'),
247
+ });
248
+
249
+ export const ArchLayersSchema = BaseSectionData.extend({
250
+ label: z.string().optional().describe('ui:text'),
251
+ title: z.string().describe('ui:text'),
252
+ description: z.string().optional().describe('ui:textarea'),
253
+ layers: z.array(ArchLayerItemSchema).describe('ui:list'),
254
+ codeFilename: z.string().optional().describe('ui:text'),
255
+ codeLines: z.array(SyntaxLineSchema).optional().describe('ui:list'),
256
+ });
257
257
 
258
258
  END_OF_FILE_CONTENT
259
259
  echo "Creating src/components/arch-layers/types.ts..."
260
260
  cat << 'END_OF_FILE_CONTENT' > "src/components/arch-layers/types.ts"
261
- import { z } from 'zod';
262
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
263
- import { ArchLayersSchema, ArchLayerLevelSchema, SyntaxTokenTypeSchema } from './schema';
264
-
265
- export type ArchLayersData = z.infer<typeof ArchLayersSchema>;
266
- export type ArchLayersSettings = z.infer<typeof BaseSectionSettingsSchema>;
267
- export type ArchLayerLevel = z.infer<typeof ArchLayerLevelSchema>;
268
- export type SyntaxTokenType = z.infer<typeof SyntaxTokenTypeSchema>;
261
+ import { z } from 'zod';
262
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
263
+ import { ArchLayersSchema, ArchLayerLevelSchema, SyntaxTokenTypeSchema } from './schema';
264
+
265
+ export type ArchLayersData = z.infer<typeof ArchLayersSchema>;
266
+ export type ArchLayersSettings = z.infer<typeof BaseSectionSettingsSchema>;
267
+ export type ArchLayerLevel = z.infer<typeof ArchLayerLevelSchema>;
268
+ export type SyntaxTokenType = z.infer<typeof SyntaxTokenTypeSchema>;
269
269
 
270
270
  END_OF_FILE_CONTENT
271
271
  mkdir -p "src/components/code-block"
272
272
  echo "Creating src/components/code-block/View.tsx..."
273
273
  cat << 'END_OF_FILE_CONTENT' > "src/components/code-block/View.tsx"
274
- import React from 'react';
275
- import { cn } from '@/lib/utils';
276
- import { Icon } from '@/lib/IconResolver';
277
- import type { CodeBlockData, CodeBlockSettings } from './types';
278
-
279
- export const CodeBlock: React.FC<{ data: CodeBlockData; settings?: CodeBlockSettings }> = ({ data, settings }) => {
280
- const showLineNumbers = settings?.showLineNumbers ?? true;
281
-
282
- return (
283
- <section
284
- style={{
285
- '--local-surface': 'var(--card)',
286
- '--local-text-muted': 'var(--muted-foreground)',
287
- '--local-bg': 'var(--background)',
288
- '--local-border': 'var(--border)',
289
- '--local-text': 'var(--foreground)',
290
- '--local-accent': 'var(--primary)',
291
- '--local-radius-lg': 'var(--radius)',
292
- } as React.CSSProperties}
293
- className="py-16 bg-[var(--local-surface)]"
294
- >
295
- <div className="container mx-auto px-6 max-w-4xl">
296
- {data.label && (
297
- <div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-wider text-[var(--local-text-muted)] mb-4" data-jp-field="label">
298
- <Icon name="terminal" size={14} />
299
- <span>{data.label}</span>
300
- </div>
301
- )}
302
- <div className="rounded-[var(--local-radius-lg)] bg-[var(--local-bg)] border border-[var(--local-border)] overflow-hidden">
303
- <div className="p-6 font-mono text-sm overflow-x-auto">
304
- {data.lines.map((line, idx) => (
305
- <div key={idx} className="flex items-start gap-4 py-1" data-jp-item-id={(line as { id?: string }).id ?? `legacy-${idx}`} data-jp-item-field="lines">
306
- {showLineNumbers && (
307
- <span className="select-none w-6 text-right text-[var(--local-text-muted)]/50">
308
- {idx + 1}
309
- </span>
310
- )}
311
- <span
312
- className={cn(
313
- line.isComment
314
- ? 'text-[var(--local-text-muted)]/60'
315
- : 'text-[var(--local-text)]'
316
- )}
317
- >
318
- {!line.isComment && (
319
- <span className="text-[var(--local-accent)] mr-2">$</span>
320
- )}
321
- {line.content}
322
- </span>
323
- </div>
324
- ))}
325
- </div>
326
- </div>
327
- </div>
328
- </section>
329
- );
330
- };
274
+ import React from 'react';
275
+ import { cn } from '@/lib/utils';
276
+ import { Icon } from '@/lib/IconResolver';
277
+ import type { CodeBlockData, CodeBlockSettings } from './types';
278
+
279
+ export const CodeBlock: React.FC<{ data: CodeBlockData; settings?: CodeBlockSettings }> = ({ data, settings }) => {
280
+ const showLineNumbers = settings?.showLineNumbers ?? true;
281
+
282
+ return (
283
+ <section
284
+ style={{
285
+ '--local-surface': 'var(--card)',
286
+ '--local-text-muted': 'var(--muted-foreground)',
287
+ '--local-bg': 'var(--background)',
288
+ '--local-border': 'var(--border)',
289
+ '--local-text': 'var(--foreground)',
290
+ '--local-accent': 'var(--primary)',
291
+ '--local-radius-lg': 'var(--radius)',
292
+ } as React.CSSProperties}
293
+ className="py-16 bg-[var(--local-surface)]"
294
+ >
295
+ <div className="container mx-auto px-6 max-w-4xl">
296
+ {data.label && (
297
+ <div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-wider text-[var(--local-text-muted)] mb-4" data-jp-field="label">
298
+ <Icon name="terminal" size={14} />
299
+ <span>{data.label}</span>
300
+ </div>
301
+ )}
302
+ <div className="rounded-[var(--local-radius-lg)] bg-[var(--local-bg)] border border-[var(--local-border)] overflow-hidden">
303
+ <div className="p-6 font-mono text-sm overflow-x-auto">
304
+ {data.lines.map((line, idx) => (
305
+ <div key={idx} className="flex items-start gap-4 py-1" data-jp-item-id={(line as { id?: string }).id ?? `legacy-${idx}`} data-jp-item-field="lines">
306
+ {showLineNumbers && (
307
+ <span className="select-none w-6 text-right text-[var(--local-text-muted)]/50">
308
+ {idx + 1}
309
+ </span>
310
+ )}
311
+ <span
312
+ className={cn(
313
+ line.isComment
314
+ ? 'text-[var(--local-text-muted)]/60'
315
+ : 'text-[var(--local-text)]'
316
+ )}
317
+ >
318
+ {!line.isComment && (
319
+ <span className="text-[var(--local-accent)] mr-2">$</span>
320
+ )}
321
+ {line.content}
322
+ </span>
323
+ </div>
324
+ ))}
325
+ </div>
326
+ </div>
327
+ </div>
328
+ </section>
329
+ );
330
+ };
331
331
 
332
332
  END_OF_FILE_CONTENT
333
333
  echo "Creating src/components/code-block/index.ts..."
334
334
  cat << 'END_OF_FILE_CONTENT' > "src/components/code-block/index.ts"
335
- export * from './View';
336
- export * from './schema';
337
- export * from './types';
335
+ export * from './View';
336
+ export * from './schema';
337
+ export * from './types';
338
338
 
339
339
  END_OF_FILE_CONTENT
340
340
  echo "Creating src/components/code-block/schema.ts..."
341
341
  cat << 'END_OF_FILE_CONTENT' > "src/components/code-block/schema.ts"
342
- import { z } from 'zod';
343
- import { BaseSectionData } from '@/lib/base-schemas';
344
-
345
- export const LegacyCodeLineSchema = z.object({
346
- content: z.string().describe('ui:text'),
347
- isComment: z.boolean().default(false).describe('ui:checkbox'),
348
- });
349
-
350
- export const CodeBlockSchema = BaseSectionData.extend({
351
- label: z.string().optional().describe('ui:text'),
352
- lines: z.array(LegacyCodeLineSchema).describe('ui:list'),
353
- });
354
-
355
- export const CodeBlockSettingsSchema = z.object({
356
- showLineNumbers: z.boolean().optional().describe('ui:checkbox'),
357
- });
342
+ import { z } from 'zod';
343
+ import { BaseSectionData } from '@/lib/base-schemas';
344
+
345
+ export const LegacyCodeLineSchema = z.object({
346
+ content: z.string().describe('ui:text'),
347
+ isComment: z.boolean().default(false).describe('ui:checkbox'),
348
+ });
349
+
350
+ export const CodeBlockSchema = BaseSectionData.extend({
351
+ label: z.string().optional().describe('ui:text'),
352
+ lines: z.array(LegacyCodeLineSchema).describe('ui:list'),
353
+ });
354
+
355
+ export const CodeBlockSettingsSchema = z.object({
356
+ showLineNumbers: z.boolean().optional().describe('ui:checkbox'),
357
+ });
358
358
 
359
359
  END_OF_FILE_CONTENT
360
360
  echo "Creating src/components/code-block/types.ts..."
361
361
  cat << 'END_OF_FILE_CONTENT' > "src/components/code-block/types.ts"
362
- import { z } from 'zod';
363
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
364
- import { CodeBlockSchema, CodeBlockSettingsSchema } from './schema';
365
-
366
- export type CodeBlockData = z.infer<typeof CodeBlockSchema>;
367
- export type CodeBlockSettings = z.infer<typeof BaseSectionSettingsSchema> & z.infer<typeof CodeBlockSettingsSchema>;
362
+ import { z } from 'zod';
363
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
364
+ import { CodeBlockSchema, CodeBlockSettingsSchema } from './schema';
365
+
366
+ export type CodeBlockData = z.infer<typeof CodeBlockSchema>;
367
+ export type CodeBlockSettings = z.infer<typeof BaseSectionSettingsSchema> & z.infer<typeof CodeBlockSettingsSchema>;
368
368
 
369
369
  END_OF_FILE_CONTENT
370
370
  mkdir -p "src/components/cta-banner"
371
371
  echo "Creating src/components/cta-banner/View.tsx..."
372
372
  cat << 'END_OF_FILE_CONTENT' > "src/components/cta-banner/View.tsx"
373
- import React from 'react';
374
- import { cn } from '@/lib/utils';
375
- import type { CtaBannerData, CtaBannerSettings } from './types';
376
-
377
- export const CtaBanner: React.FC<{ data: CtaBannerData; settings?: CtaBannerSettings }> = ({ data }) => {
378
- return (
379
- <section
380
- style={{
381
- '--local-bg': 'var(--background)',
382
- '--local-text': 'var(--foreground)',
383
- '--local-text-muted': 'var(--muted-foreground)',
384
- '--local-primary': 'var(--primary)',
385
- '--local-accent': 'var(--color-accent, #60a5fa)',
386
- } as React.CSSProperties}
387
- className="relative py-28 bg-[var(--local-bg)] overflow-hidden text-center"
388
- >
389
- <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[60vw] h-[60vw] bg-[radial-gradient(circle,rgba(59,130,246,0.08)_0%,transparent_60%)] pointer-events-none" />
390
- <div className="relative max-w-[1200px] mx-auto px-8">
391
- {data.label && (
392
- <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
393
- <span className="w-5 h-px bg-[var(--local-primary)]" />
394
- {data.label}
395
- </div>
396
- )}
397
- <h2
398
- className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-6"
399
- data-jp-field="title"
400
- >
401
- {data.title}
402
- </h2>
403
- {data.description && (
404
- <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] mx-auto leading-relaxed mb-10" data-jp-field="description">
405
- {data.description}
406
- </p>
407
- )}
408
- {data.ctas && data.ctas.length > 0 && (
409
- <div className="flex gap-4 justify-center flex-wrap">
410
- {data.ctas.map((cta, idx) => (
411
- <a
412
- key={cta.id ?? idx}
413
- href={cta.href}
414
- data-jp-item-id={cta.id ?? `legacy-${idx}`}
415
- data-jp-item-field="ctas"
416
- className={cn(
417
- 'inline-flex items-center gap-2 px-8 py-3.5 rounded-[5px] font-semibold text-base transition-all duration-200 no-underline',
418
- cta.variant === 'primary'
419
- ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-0.5 hover:shadow-[0_8px_30px_rgba(59,130,246,0.3)]'
420
- : 'bg-transparent text-[var(--local-text)] border border-[rgba(255,255,255,0.12)] hover:border-[rgba(255,255,255,0.3)] hover:bg-[rgba(255,255,255,0.04)]'
421
- )}
422
- >
423
- {cta.label}
424
- </a>
425
- ))}
426
- </div>
427
- )}
428
- </div>
429
- </section>
430
- );
431
- };
373
+ import React from 'react';
374
+ import { cn } from '@/lib/utils';
375
+ import type { CtaBannerData, CtaBannerSettings } from './types';
376
+
377
+ export const CtaBanner: React.FC<{ data: CtaBannerData; settings?: CtaBannerSettings }> = ({ data }) => {
378
+ return (
379
+ <section
380
+ style={{
381
+ '--local-bg': 'var(--background)',
382
+ '--local-text': 'var(--foreground)',
383
+ '--local-text-muted': 'var(--muted-foreground)',
384
+ '--local-primary': 'var(--primary)',
385
+ '--local-accent': 'var(--color-accent, #60a5fa)',
386
+ } as React.CSSProperties}
387
+ className="relative py-28 bg-[var(--local-bg)] overflow-hidden text-center"
388
+ >
389
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[60vw] h-[60vw] bg-[radial-gradient(circle,rgba(59,130,246,0.08)_0%,transparent_60%)] pointer-events-none" />
390
+ <div className="relative max-w-[1200px] mx-auto px-8">
391
+ {data.label && (
392
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
393
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
394
+ {data.label}
395
+ </div>
396
+ )}
397
+ <h2
398
+ className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-6"
399
+ data-jp-field="title"
400
+ >
401
+ {data.title}
402
+ </h2>
403
+ {data.description && (
404
+ <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] mx-auto leading-relaxed mb-10" data-jp-field="description">
405
+ {data.description}
406
+ </p>
407
+ )}
408
+ {data.ctas && data.ctas.length > 0 && (
409
+ <div className="flex gap-4 justify-center flex-wrap">
410
+ {data.ctas.map((cta, idx) => (
411
+ <a
412
+ key={cta.id ?? idx}
413
+ href={cta.href}
414
+ data-jp-item-id={cta.id ?? `legacy-${idx}`}
415
+ data-jp-item-field="ctas"
416
+ className={cn(
417
+ 'inline-flex items-center gap-2 px-8 py-3.5 rounded-[5px] font-semibold text-base transition-all duration-200 no-underline',
418
+ cta.variant === 'primary'
419
+ ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-0.5 hover:shadow-[0_8px_30px_rgba(59,130,246,0.3)]'
420
+ : 'bg-transparent text-[var(--local-text)] border border-[rgba(255,255,255,0.12)] hover:border-[rgba(255,255,255,0.3)] hover:bg-[rgba(255,255,255,0.04)]'
421
+ )}
422
+ >
423
+ {cta.label}
424
+ </a>
425
+ ))}
426
+ </div>
427
+ )}
428
+ </div>
429
+ </section>
430
+ );
431
+ };
432
432
 
433
433
  END_OF_FILE_CONTENT
434
434
  echo "Creating src/components/cta-banner/index.ts..."
435
435
  cat << 'END_OF_FILE_CONTENT' > "src/components/cta-banner/index.ts"
436
- export * from './View';
437
- export * from './schema';
438
- export * from './types';
436
+ export * from './View';
437
+ export * from './schema';
438
+ export * from './types';
439
439
 
440
440
  END_OF_FILE_CONTENT
441
441
  echo "Creating src/components/cta-banner/schema.ts..."
442
442
  cat << 'END_OF_FILE_CONTENT' > "src/components/cta-banner/schema.ts"
443
- import { z } from 'zod';
444
- import { BaseSectionData, CtaSchema } from '@/lib/base-schemas';
445
-
446
- export const CtaBannerSchema = BaseSectionData.extend({
447
- label: z.string().optional().describe('ui:text'),
448
- title: z.string().describe('ui:text'),
449
- description: z.string().optional().describe('ui:textarea'),
450
- ctas: z.array(CtaSchema).optional().describe('ui:list'),
451
- });
443
+ import { z } from 'zod';
444
+ import { BaseSectionData, CtaSchema } from '@/lib/base-schemas';
445
+
446
+ export const CtaBannerSchema = BaseSectionData.extend({
447
+ label: z.string().optional().describe('ui:text'),
448
+ title: z.string().describe('ui:text'),
449
+ description: z.string().optional().describe('ui:textarea'),
450
+ ctas: z.array(CtaSchema).optional().describe('ui:list'),
451
+ });
452
452
 
453
453
  END_OF_FILE_CONTENT
454
454
  echo "Creating src/components/cta-banner/types.ts..."
455
455
  cat << 'END_OF_FILE_CONTENT' > "src/components/cta-banner/types.ts"
456
- import { z } from 'zod';
457
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
458
- import { CtaBannerSchema } from './schema';
459
-
460
- export type CtaBannerData = z.infer<typeof CtaBannerSchema>;
461
- export type CtaBannerSettings = z.infer<typeof BaseSectionSettingsSchema>;
456
+ import { z } from 'zod';
457
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
458
+ import { CtaBannerSchema } from './schema';
459
+
460
+ export type CtaBannerData = z.infer<typeof CtaBannerSchema>;
461
+ export type CtaBannerSettings = z.infer<typeof BaseSectionSettingsSchema>;
462
462
 
463
463
  END_OF_FILE_CONTENT
464
464
  mkdir -p "src/components/feature-grid"
465
465
  echo "Creating src/components/feature-grid/View.tsx..."
466
466
  cat << 'END_OF_FILE_CONTENT' > "src/components/feature-grid/View.tsx"
467
- import React from 'react';
468
- import { cn } from '@/lib/utils';
469
- import { Icon } from '@/lib/IconResolver';
470
- import type { FeatureGridData, FeatureGridSettings } from './types';
471
-
472
- const columnsMap: Record<2 | 3 | 4, string> = {
473
- 2: 'md:grid-cols-2',
474
- 3: 'md:grid-cols-3',
475
- 4: 'md:grid-cols-4',
476
- };
477
-
478
- export const FeatureGrid: React.FC<{ data: FeatureGridData; settings?: FeatureGridSettings }> = ({ data, settings }) => {
479
- const colKey = settings?.columns ?? 3;
480
- const cols = (colKey === 2 || colKey === 3 || colKey === 4) ? columnsMap[colKey] : columnsMap[3];
481
- const isBordered = settings?.cardStyle === 'bordered';
482
-
483
- const localStyles = {
484
- '--local-bg': 'var(--background)',
485
- '--local-text': 'var(--foreground)',
486
- '--local-text-muted': 'var(--muted-foreground)',
487
- '--local-surface': 'var(--card)',
488
- '--local-surface-alt': 'var(--muted)',
489
- '--local-border': 'var(--border)',
490
- '--local-radius-lg': 'var(--radius)',
491
- '--local-radius-md': 'calc(var(--radius) - 2px)',
492
- } as React.CSSProperties;
493
-
494
- return (
495
- <section style={localStyles} className="py-20 bg-[var(--local-bg)] relative z-0">
496
- <div className="container mx-auto px-6">
497
- <h2 className="text-3xl md:text-4xl font-bold text-center text-[var(--local-text)] mb-16" data-jp-field="sectionTitle">
498
- {data.sectionTitle}
499
- </h2>
500
- <div className={cn('grid grid-cols-1 gap-6', cols)}>
501
- {data.cards.map((card, idx) => (
502
- <div
503
- key={card.id ?? idx}
504
- className={cn(
505
- 'p-6 rounded-[var(--local-radius-lg)] bg-[var(--local-surface)]',
506
- isBordered && 'border border-[var(--local-border)]'
507
- )}
508
- data-jp-item-id={card.id ?? `legacy-${idx}`}
509
- data-jp-item-field="cards"
510
- >
511
- {card.icon && (
512
- <div className="w-10 h-10 rounded-[var(--local-radius-md)] bg-[var(--local-surface-alt)] flex items-center justify-center mb-4">
513
- <Icon name={card.icon} size={20} className="text-[var(--local-text-muted)]" />
514
- </div>
515
- )}
516
- <h3 className="text-lg font-semibold text-[var(--local-text)] mb-2">
517
- {card.emoji && <span className="mr-2">{card.emoji}</span>}
518
- {card.title}
519
- </h3>
520
- <p className="text-sm text-[var(--local-text-muted)] leading-relaxed">
521
- {card.description}
522
- </p>
523
- </div>
524
- ))}
525
- </div>
526
- </div>
527
- </section>
528
- );
529
- };
467
+ import React from 'react';
468
+ import { cn } from '@/lib/utils';
469
+ import { Icon } from '@/lib/IconResolver';
470
+ import type { FeatureGridData, FeatureGridSettings } from './types';
471
+
472
+ const columnsMap: Record<2 | 3 | 4, string> = {
473
+ 2: 'md:grid-cols-2',
474
+ 3: 'md:grid-cols-3',
475
+ 4: 'md:grid-cols-4',
476
+ };
477
+
478
+ export const FeatureGrid: React.FC<{ data: FeatureGridData; settings?: FeatureGridSettings }> = ({ data, settings }) => {
479
+ const colKey = settings?.columns ?? 3;
480
+ const cols = (colKey === 2 || colKey === 3 || colKey === 4) ? columnsMap[colKey] : columnsMap[3];
481
+ const isBordered = settings?.cardStyle === 'bordered';
482
+
483
+ const localStyles = {
484
+ '--local-bg': 'var(--background)',
485
+ '--local-text': 'var(--foreground)',
486
+ '--local-text-muted': 'var(--muted-foreground)',
487
+ '--local-surface': 'var(--card)',
488
+ '--local-surface-alt': 'var(--muted)',
489
+ '--local-border': 'var(--border)',
490
+ '--local-radius-lg': 'var(--radius)',
491
+ '--local-radius-md': 'calc(var(--radius) - 2px)',
492
+ } as React.CSSProperties;
493
+
494
+ return (
495
+ <section style={localStyles} className="py-20 bg-[var(--local-bg)] relative z-0">
496
+ <div className="container mx-auto px-6">
497
+ <h2 className="text-3xl md:text-4xl font-bold text-center text-[var(--local-text)] mb-16" data-jp-field="sectionTitle">
498
+ {data.sectionTitle}
499
+ </h2>
500
+ <div className={cn('grid grid-cols-1 gap-6', cols)}>
501
+ {data.cards.map((card, idx) => (
502
+ <div
503
+ key={card.id ?? idx}
504
+ className={cn(
505
+ 'p-6 rounded-[var(--local-radius-lg)] bg-[var(--local-surface)]',
506
+ isBordered && 'border border-[var(--local-border)]'
507
+ )}
508
+ data-jp-item-id={card.id ?? `legacy-${idx}`}
509
+ data-jp-item-field="cards"
510
+ >
511
+ {card.icon && (
512
+ <div className="w-10 h-10 rounded-[var(--local-radius-md)] bg-[var(--local-surface-alt)] flex items-center justify-center mb-4">
513
+ <Icon name={card.icon} size={20} className="text-[var(--local-text-muted)]" />
514
+ </div>
515
+ )}
516
+ <h3 className="text-lg font-semibold text-[var(--local-text)] mb-2">
517
+ {card.emoji && <span className="mr-2">{card.emoji}</span>}
518
+ {card.title}
519
+ </h3>
520
+ <p className="text-sm text-[var(--local-text-muted)] leading-relaxed">
521
+ {card.description}
522
+ </p>
523
+ </div>
524
+ ))}
525
+ </div>
526
+ </div>
527
+ </section>
528
+ );
529
+ };
530
530
 
531
531
  END_OF_FILE_CONTENT
532
532
  echo "Creating src/components/feature-grid/index.ts..."
533
533
  cat << 'END_OF_FILE_CONTENT' > "src/components/feature-grid/index.ts"
534
- export * from './View';
535
- export * from './schema';
536
- export * from './types';
534
+ export * from './View';
535
+ export * from './schema';
536
+ export * from './types';
537
537
 
538
538
  END_OF_FILE_CONTENT
539
539
  echo "Creating src/components/feature-grid/schema.ts..."
540
540
  cat << 'END_OF_FILE_CONTENT' > "src/components/feature-grid/schema.ts"
541
- import { z } from 'zod';
542
- import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
543
-
544
- export const FeatureCardSchema = BaseArrayItem.extend({
545
- icon: z.string().optional().describe('ui:icon-picker'),
546
- emoji: z.string().optional().describe('ui:text'),
547
- title: z.string().describe('ui:text'),
548
- description: z.string().describe('ui:textarea'),
549
- });
550
-
551
- export const FeatureGridSchema = BaseSectionData.extend({
552
- sectionTitle: z.string().describe('ui:text'),
553
- cards: z.array(FeatureCardSchema).describe('ui:list'),
554
- });
555
-
556
- export const FeatureGridSettingsSchema = z.object({
557
- columns: z.union([z.literal(2), z.literal(3), z.literal(4)]).optional().describe('ui:number'),
558
- cardStyle: z.enum(['plain', 'bordered']).optional().describe('ui:select'),
559
- });
541
+ import { z } from 'zod';
542
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
543
+
544
+ export const FeatureCardSchema = BaseArrayItem.extend({
545
+ icon: z.string().optional().describe('ui:icon-picker'),
546
+ emoji: z.string().optional().describe('ui:text'),
547
+ title: z.string().describe('ui:text'),
548
+ description: z.string().describe('ui:textarea'),
549
+ });
550
+
551
+ export const FeatureGridSchema = BaseSectionData.extend({
552
+ sectionTitle: z.string().describe('ui:text'),
553
+ cards: z.array(FeatureCardSchema).describe('ui:list'),
554
+ });
555
+
556
+ export const FeatureGridSettingsSchema = z.object({
557
+ columns: z.union([z.literal(2), z.literal(3), z.literal(4)]).optional().describe('ui:number'),
558
+ cardStyle: z.enum(['plain', 'bordered']).optional().describe('ui:select'),
559
+ });
560
560
 
561
561
  END_OF_FILE_CONTENT
562
562
  echo "Creating src/components/feature-grid/types.ts..."
563
563
  cat << 'END_OF_FILE_CONTENT' > "src/components/feature-grid/types.ts"
564
- import { z } from 'zod';
565
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
566
- import { FeatureGridSchema, FeatureGridSettingsSchema } from './schema';
567
-
568
- export type FeatureGridData = z.infer<typeof FeatureGridSchema>;
569
- export type FeatureGridSettings = z.infer<typeof BaseSectionSettingsSchema> & z.infer<typeof FeatureGridSettingsSchema>;
564
+ import { z } from 'zod';
565
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
566
+ import { FeatureGridSchema, FeatureGridSettingsSchema } from './schema';
567
+
568
+ export type FeatureGridData = z.infer<typeof FeatureGridSchema>;
569
+ export type FeatureGridSettings = z.infer<typeof BaseSectionSettingsSchema> & z.infer<typeof FeatureGridSettingsSchema>;
570
570
 
571
571
  END_OF_FILE_CONTENT
572
572
  mkdir -p "src/components/footer"
573
573
  echo "Creating src/components/footer/View.tsx..."
574
574
  cat << 'END_OF_FILE_CONTENT' > "src/components/footer/View.tsx"
575
- import React from 'react';
576
- import type { FooterData, FooterSettings } from './types';
577
-
578
- export const Footer: React.FC<{ data: FooterData; settings?: FooterSettings }> = ({ data }) => {
579
- return (
580
- <footer
581
- style={{
582
- '--local-bg': 'var(--background)',
583
- '--local-text': 'var(--foreground)',
584
- '--local-text-muted': 'var(--muted-foreground)',
585
- '--local-accent': 'var(--color-accent, #60a5fa)',
586
- '--local-border': 'rgba(255,255,255,0.05)',
587
- } as React.CSSProperties}
588
- className="py-12 border-t border-[var(--local-border)] bg-[var(--local-bg)] relative z-0"
589
- >
590
- <div className="max-w-[1200px] mx-auto px-8">
591
- <div className="flex flex-col md:flex-row items-center justify-between gap-4">
592
- <div className="flex items-center gap-2 font-bold text-[0.9rem] text-[var(--local-text-muted)]" data-jp-field="brandText">
593
- {data.brandText}
594
- {data.brandHighlight && (
595
- <span className="text-[var(--local-accent)]" data-jp-field="brandHighlight">{data.brandHighlight}</span>
596
- )}
597
- </div>
598
- {data.links && data.links.length > 0 && (
599
- <nav className="flex gap-6">
600
- {data.links.map((link, idx) => (
601
- <a
602
- key={idx}
603
- href={link.href}
604
- className="text-[0.82rem] text-[var(--local-text-muted)] hover:text-[var(--local-accent)] transition-colors no-underline"
605
- data-jp-item-id={(link as { id?: string }).id ?? `legacy-${idx}`}
606
- data-jp-item-field="links"
607
- >
608
- {link.label}
609
- </a>
610
- ))}
611
- </nav>
612
- )}
613
- <div className="text-[0.8rem] text-[var(--local-text-muted)] opacity-60" data-jp-field="copyright">
614
- {data.copyright}
615
- </div>
616
- </div>
617
- </div>
618
- </footer>
619
- );
620
- };
575
+ import React from 'react';
576
+ import type { FooterData, FooterSettings } from './types';
577
+
578
+ export const Footer: React.FC<{ data: FooterData; settings?: FooterSettings }> = ({ data }) => {
579
+ return (
580
+ <footer
581
+ style={{
582
+ '--local-bg': 'var(--background)',
583
+ '--local-text': 'var(--foreground)',
584
+ '--local-text-muted': 'var(--muted-foreground)',
585
+ '--local-accent': 'var(--color-accent, #60a5fa)',
586
+ '--local-border': 'rgba(255,255,255,0.05)',
587
+ } as React.CSSProperties}
588
+ className="py-12 border-t border-[var(--local-border)] bg-[var(--local-bg)] relative z-0"
589
+ >
590
+ <div className="max-w-[1200px] mx-auto px-8">
591
+ <div className="flex flex-col md:flex-row items-center justify-between gap-4">
592
+ <div className="flex items-center gap-2 font-bold text-[0.9rem] text-[var(--local-text-muted)]" data-jp-field="brandText">
593
+ {data.brandText}
594
+ {data.brandHighlight && (
595
+ <span className="text-[var(--local-accent)]" data-jp-field="brandHighlight">{data.brandHighlight}</span>
596
+ )}
597
+ </div>
598
+ {data.links && data.links.length > 0 && (
599
+ <nav className="flex gap-6">
600
+ {data.links.map((link, idx) => (
601
+ <a
602
+ key={idx}
603
+ href={link.href}
604
+ className="text-[0.82rem] text-[var(--local-text-muted)] hover:text-[var(--local-accent)] transition-colors no-underline"
605
+ data-jp-item-id={(link as { id?: string }).id ?? `legacy-${idx}`}
606
+ data-jp-item-field="links"
607
+ >
608
+ {link.label}
609
+ </a>
610
+ ))}
611
+ </nav>
612
+ )}
613
+ <div className="text-[0.8rem] text-[var(--local-text-muted)] opacity-60" data-jp-field="copyright">
614
+ {data.copyright}
615
+ </div>
616
+ </div>
617
+ </div>
618
+ </footer>
619
+ );
620
+ };
621
621
 
622
622
  END_OF_FILE_CONTENT
623
623
  echo "Creating src/components/footer/index.ts..."
624
624
  cat << 'END_OF_FILE_CONTENT' > "src/components/footer/index.ts"
625
- export * from './View';
626
- export * from './schema';
627
- export * from './types';
625
+ export * from './View';
626
+ export * from './schema';
627
+ export * from './types';
628
628
 
629
629
  END_OF_FILE_CONTENT
630
630
  echo "Creating src/components/footer/schema.ts..."
631
631
  cat << 'END_OF_FILE_CONTENT' > "src/components/footer/schema.ts"
632
- import { z } from 'zod';
633
-
634
- export const FooterSchema = z.object({
635
- brandText: z.string().describe('ui:text'),
636
- brandHighlight: z.string().optional().describe('ui:text'),
637
- copyright: z.string().describe('ui:text'),
638
- links: z.array(z.object({
639
- label: z.string().describe('ui:text'),
640
- href: z.string().describe('ui:text'),
641
- })).optional().describe('ui:list'),
642
- });
643
-
644
- export const FooterSettingsSchema = z.object({
645
- showLogo: z.boolean().optional().describe('ui:checkbox'),
646
- });
632
+ import { z } from 'zod';
633
+
634
+ export const FooterSchema = z.object({
635
+ brandText: z.string().describe('ui:text'),
636
+ brandHighlight: z.string().optional().describe('ui:text'),
637
+ copyright: z.string().describe('ui:text'),
638
+ links: z.array(z.object({
639
+ label: z.string().describe('ui:text'),
640
+ href: z.string().describe('ui:text'),
641
+ })).optional().describe('ui:list'),
642
+ });
643
+
644
+ export const FooterSettingsSchema = z.object({
645
+ showLogo: z.boolean().optional().describe('ui:checkbox'),
646
+ });
647
647
 
648
648
  END_OF_FILE_CONTENT
649
649
  echo "Creating src/components/footer/types.ts..."
650
650
  cat << 'END_OF_FILE_CONTENT' > "src/components/footer/types.ts"
651
- import { z } from 'zod';
652
- import { FooterSchema, FooterSettingsSchema } from './schema';
653
-
654
- export type FooterData = z.infer<typeof FooterSchema>;
655
- export type FooterSettings = z.infer<typeof FooterSettingsSchema>;
651
+ import { z } from 'zod';
652
+ import { FooterSchema, FooterSettingsSchema } from './schema';
653
+
654
+ export type FooterData = z.infer<typeof FooterSchema>;
655
+ export type FooterSettings = z.infer<typeof FooterSettingsSchema>;
656
656
 
657
657
  END_OF_FILE_CONTENT
658
658
  mkdir -p "src/components/header"
659
659
  echo "Creating src/components/header/View.tsx..."
660
660
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/View.tsx"
661
- import React, { useState, useEffect } from 'react';
662
- import { cn } from '@/lib/utils';
663
- import type { MenuItem } from '@jsonpages/core';
664
- import type { HeaderData, HeaderSettings } from './types';
665
-
666
- export const Header: React.FC<{
667
- data: HeaderData;
668
- settings?: HeaderSettings;
669
- menu: MenuItem[];
670
- }> = ({ data, menu }) => {
671
- const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
672
- const [scrolled, setScrolled] = useState(false);
673
-
674
- useEffect(() => {
675
- const handleScroll = () => setScrolled(window.scrollY > 40);
676
- window.addEventListener('scroll', handleScroll, { passive: true });
677
- return () => window.removeEventListener('scroll', handleScroll);
678
- }, []);
679
-
680
- return (
681
- <header
682
- style={{
683
- '--local-bg': 'rgba(6,13,27,0.92)',
684
- '--local-text': 'var(--foreground)',
685
- '--local-text-muted': 'var(--muted-foreground)',
686
- '--local-primary': 'var(--primary)',
687
- '--local-accent': 'var(--color-accent, #60a5fa)',
688
- '--local-border': 'rgba(59,130,246,0.08)',
689
- } as React.CSSProperties}
690
- className={cn(
691
- 'w-full py-4 transition-all duration-300 z-0',
692
- scrolled
693
- ? 'bg-[var(--local-bg)] backdrop-blur-[20px] border-b border-[var(--local-border)]'
694
- : 'bg-transparent border-b border-transparent'
695
- )}
696
- >
697
- <div className="max-w-[1200px] mx-auto px-8 flex justify-between items-center">
698
- <a
699
- href="/"
700
- className="flex items-center gap-2.5 no-underline font-bold text-xl tracking-tight text-[var(--local-text)]"
701
- >
702
- {data.logoIconText && (
703
- <div className="w-8 h-8 rounded-md bg-gradient-to-br from-[var(--local-primary)] to-[var(--local-accent)] flex items-center justify-center font-mono text-[0.8rem] font-bold text-[var(--background)]" data-jp-field="logoIconText">
704
- {data.logoIconText}
705
- </div>
706
- )}
707
- <span data-jp-field="logoText">
708
- {data.logoText}
709
- {data.logoHighlight && (
710
- <span className="text-[var(--local-accent)]" data-jp-field="logoHighlight">{data.logoHighlight}</span>
711
- )}
712
- </span>
713
- </a>
714
-
715
- <nav className="hidden md:flex items-center gap-10">
716
- {menu.map((item, idx) => (
717
- <a
718
- key={(item as { id?: string }).id ?? idx}
719
- href={item.href}
720
- data-jp-item-id={(item as { id?: string }).id ?? `legacy-${idx}`}
721
- data-jp-item-field="links"
722
- target={item.external ? '_blank' : undefined}
723
- rel={item.external ? 'noopener noreferrer' : undefined}
724
- className={cn(
725
- 'no-underline text-sm font-medium transition-colors',
726
- item.isCta
727
- ? 'bg-[var(--local-primary)] text-white px-5 py-2 rounded-lg font-semibold hover:brightness-110 hover:-translate-y-px'
728
- : 'text-[var(--local-text-muted)] hover:text-[var(--local-text)]'
729
- )}
730
- >
731
- {item.label}
732
- </a>
733
- ))}
734
- </nav>
735
-
736
- <button
737
- type="button"
738
- className="md:hidden p-2 text-[var(--local-text-muted)] hover:text-[var(--local-text)]"
739
- onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
740
- aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
741
- >
742
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
743
- {mobileMenuOpen ? (
744
- <><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></>
745
- ) : (
746
- <><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="18" x2="21" y2="18" /></>
747
- )}
748
- </svg>
749
- </button>
750
- </div>
751
-
752
- {mobileMenuOpen && (
753
- <nav className="md:hidden border-t border-[var(--local-border)] bg-[var(--local-bg)] backdrop-blur-[20px]">
754
- <div className="max-w-[1200px] mx-auto px-8 py-4 flex flex-col gap-4">
755
- {menu.map((item, idx) => (
756
- <a
757
- key={(item as { id?: string }).id ?? idx}
758
- href={item.href}
759
- className="text-base font-medium text-[var(--local-text-muted)] hover:text-[var(--local-text)] transition-colors py-2 no-underline"
760
- onClick={() => setMobileMenuOpen(false)}
761
- data-jp-item-id={(item as { id?: string }).id ?? `legacy-${idx}`}
762
- data-jp-item-field="links"
763
- >
764
- {item.label}
765
- </a>
766
- ))}
767
- </div>
768
- </nav>
769
- )}
770
- </header>
771
- );
772
- };
661
+ import React, { useState, useEffect } from 'react';
662
+ import { cn } from '@/lib/utils';
663
+ import type { MenuItem } from '@jsonpages/core';
664
+ import type { HeaderData, HeaderSettings } from './types';
665
+
666
+ export const Header: React.FC<{
667
+ data: HeaderData;
668
+ settings?: HeaderSettings;
669
+ menu: MenuItem[];
670
+ }> = ({ data, menu }) => {
671
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
672
+ const [scrolled, setScrolled] = useState(false);
673
+
674
+ useEffect(() => {
675
+ const handleScroll = () => setScrolled(window.scrollY > 40);
676
+ window.addEventListener('scroll', handleScroll, { passive: true });
677
+ return () => window.removeEventListener('scroll', handleScroll);
678
+ }, []);
679
+
680
+ return (
681
+ <header
682
+ style={{
683
+ '--local-bg': 'rgba(6,13,27,0.92)',
684
+ '--local-text': 'var(--foreground)',
685
+ '--local-text-muted': 'var(--muted-foreground)',
686
+ '--local-primary': 'var(--primary)',
687
+ '--local-accent': 'var(--color-accent, #60a5fa)',
688
+ '--local-border': 'rgba(59,130,246,0.08)',
689
+ } as React.CSSProperties}
690
+ className={cn(
691
+ 'w-full py-4 transition-all duration-300 z-0',
692
+ scrolled
693
+ ? 'bg-[var(--local-bg)] backdrop-blur-[20px] border-b border-[var(--local-border)]'
694
+ : 'bg-transparent border-b border-transparent'
695
+ )}
696
+ >
697
+ <div className="max-w-[1200px] mx-auto px-8 flex justify-between items-center">
698
+ <a
699
+ href="/"
700
+ className="flex items-center gap-2.5 no-underline font-bold text-xl tracking-tight text-[var(--local-text)]"
701
+ >
702
+ {data.logoIconText && (
703
+ <div className="w-8 h-8 rounded-md bg-gradient-to-br from-[var(--local-primary)] to-[var(--local-accent)] flex items-center justify-center font-mono text-[0.8rem] font-bold text-[var(--background)]" data-jp-field="logoIconText">
704
+ {data.logoIconText}
705
+ </div>
706
+ )}
707
+ <span data-jp-field="logoText">
708
+ {data.logoText}
709
+ {data.logoHighlight && (
710
+ <span className="text-[var(--local-accent)]" data-jp-field="logoHighlight">{data.logoHighlight}</span>
711
+ )}
712
+ </span>
713
+ </a>
714
+
715
+ <nav className="hidden md:flex items-center gap-10">
716
+ {menu.map((item, idx) => (
717
+ <a
718
+ key={(item as { id?: string }).id ?? idx}
719
+ href={item.href}
720
+ data-jp-item-id={(item as { id?: string }).id ?? `legacy-${idx}`}
721
+ data-jp-item-field="links"
722
+ target={item.external ? '_blank' : undefined}
723
+ rel={item.external ? 'noopener noreferrer' : undefined}
724
+ className={cn(
725
+ 'no-underline text-sm font-medium transition-colors',
726
+ item.isCta
727
+ ? 'bg-[var(--local-primary)] text-white px-5 py-2 rounded-lg font-semibold hover:brightness-110 hover:-translate-y-px'
728
+ : 'text-[var(--local-text-muted)] hover:text-[var(--local-text)]'
729
+ )}
730
+ >
731
+ {item.label}
732
+ </a>
733
+ ))}
734
+ </nav>
735
+
736
+ <button
737
+ type="button"
738
+ className="md:hidden p-2 text-[var(--local-text-muted)] hover:text-[var(--local-text)]"
739
+ onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
740
+ aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
741
+ >
742
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
743
+ {mobileMenuOpen ? (
744
+ <><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></>
745
+ ) : (
746
+ <><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="18" x2="21" y2="18" /></>
747
+ )}
748
+ </svg>
749
+ </button>
750
+ </div>
751
+
752
+ {mobileMenuOpen && (
753
+ <nav className="md:hidden border-t border-[var(--local-border)] bg-[var(--local-bg)] backdrop-blur-[20px]">
754
+ <div className="max-w-[1200px] mx-auto px-8 py-4 flex flex-col gap-4">
755
+ {menu.map((item, idx) => (
756
+ <a
757
+ key={(item as { id?: string }).id ?? idx}
758
+ href={item.href}
759
+ className="text-base font-medium text-[var(--local-text-muted)] hover:text-[var(--local-text)] transition-colors py-2 no-underline"
760
+ onClick={() => setMobileMenuOpen(false)}
761
+ data-jp-item-id={(item as { id?: string }).id ?? `legacy-${idx}`}
762
+ data-jp-item-field="links"
763
+ >
764
+ {item.label}
765
+ </a>
766
+ ))}
767
+ </div>
768
+ </nav>
769
+ )}
770
+ </header>
771
+ );
772
+ };
773
773
 
774
774
  END_OF_FILE_CONTENT
775
775
  echo "Creating src/components/header/index.ts..."
@@ -827,780 +827,780 @@ END_OF_FILE_CONTENT
827
827
  mkdir -p "src/components/hero"
828
828
  echo "Creating src/components/hero/View.tsx..."
829
829
  cat << 'END_OF_FILE_CONTENT' > "src/components/hero/View.tsx"
830
- import React from 'react';
831
- import { cn } from '@/lib/utils';
832
- import type { HeroData, HeroSettings } from './types';
833
-
834
- export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ data }) => {
835
- return (
836
- <section
837
- style={{
838
- '--local-bg': 'var(--background)',
839
- '--local-text': 'var(--foreground)',
840
- '--local-text-muted': 'var(--muted-foreground)',
841
- '--local-primary': 'var(--primary)',
842
- '--local-accent': 'var(--color-accent, #60a5fa)',
843
- '--local-cyan': 'var(--color-secondary, #22d3ee)',
844
- '--local-border': 'var(--border)',
845
- } as React.CSSProperties}
846
- className="jp-hero relative min-h-screen flex items-center overflow-hidden pt-24 bg-[var(--local-bg)]"
847
- >
848
- <div className="absolute -top-[40%] -right-[20%] w-[70vw] h-[70vw] rounded-full bg-[radial-gradient(circle,rgba(59,130,246,0.06)_0%,transparent_70%)] pointer-events-none" />
849
- <div className="absolute -bottom-[10%] -left-[10%] w-[50vw] h-[50vw] rounded-full bg-[radial-gradient(circle,rgba(34,211,238,0.03)_0%,transparent_60%)] pointer-events-none" />
850
-
851
- <div className="relative max-w-[1200px] mx-auto px-8 w-full">
852
- <div className="max-w-[820px]">
853
- {data.badge && (
854
- <div className="inline-flex items-center gap-2 bg-[rgba(59,130,246,0.08)] border border-[rgba(59,130,246,0.2)] px-4 py-1.5 rounded-full text-[0.78rem] font-semibold text-[var(--local-accent)] mb-8 tracking-wide jp-animate-in" data-jp-field="badge">
855
- <span className="w-1.5 h-1.5 rounded-full bg-[var(--local-accent)] jp-pulse-dot" />
856
- {data.badge}
857
- </div>
858
- )}
859
- <h1
860
- className="font-display text-[clamp(2.8rem,6vw,4.8rem)] font-black text-[var(--local-text)] leading-[1.08] tracking-tight mb-6 jp-animate-in jp-d1"
861
- data-jp-field="title"
862
- >
863
- {data.title}
864
- {data.titleHighlight && (
865
- <>
866
- <br />
867
- <em className="not-italic bg-gradient-to-br from-[var(--local-accent)] to-[var(--local-cyan)] bg-clip-text text-transparent" data-jp-field="titleHighlight">
868
- {data.titleHighlight}
869
- </em>
870
- </>
871
- )}
872
- </h1>
873
- {data.description && (
874
- <p className="text-xl text-[var(--local-text-muted)] max-w-[600px] leading-relaxed mb-10 jp-animate-in jp-d2" data-jp-field="description">
875
- {data.description}
876
- </p>
877
- )}
878
- {data.ctas && data.ctas.length > 0 && (
879
- <div className="flex gap-4 flex-wrap jp-animate-in jp-d3">
880
- {data.ctas.map((cta, idx) => (
881
- <a
882
- key={cta.id ?? idx}
883
- href={cta.href}
884
- data-jp-item-id={cta.id ?? `legacy-${idx}`}
885
- data-jp-item-field="ctas"
886
- className={cn(
887
- 'inline-flex items-center gap-2 px-8 py-3.5 rounded-[5px] font-semibold text-base transition-all duration-200 no-underline',
888
- cta.variant === 'primary'
889
- ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-0.5 hover:shadow-[0_8px_30px_rgba(59,130,246,0.3)]'
890
- : 'bg-transparent text-[var(--local-text)] border border-[rgba(255,255,255,0.12)] hover:border-[rgba(255,255,255,0.3)] hover:bg-[rgba(255,255,255,0.04)]'
891
- )}
892
- >
893
- {cta.label}
894
- </a>
895
- ))}
896
- </div>
897
- )}
898
- {data.metrics && data.metrics.length > 0 && (
899
- <div className="flex gap-12 mt-16 pt-12 border-t border-[rgba(255,255,255,0.06)] flex-wrap jp-animate-in jp-d4">
900
- {data.metrics.map((metric, idx) => (
901
- <div
902
- key={(metric as { id?: string }).id ?? idx}
903
- data-jp-item-id={(metric as { id?: string }).id ?? `legacy-${idx}`}
904
- data-jp-item-field="metrics"
905
- >
906
- <div className="text-[2rem] font-bold text-[var(--local-text)] font-display">
907
- {metric.val}
908
- </div>
909
- <div className="text-[0.82rem] text-[var(--muted-foreground)] mt-0.5 opacity-70">
910
- {metric.label}
911
- </div>
912
- </div>
913
- ))}
914
- </div>
915
- )}
916
- </div>
917
- </div>
918
- </section>
919
- );
920
- };
830
+ import React from 'react';
831
+ import { cn } from '@/lib/utils';
832
+ import type { HeroData, HeroSettings } from './types';
833
+
834
+ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ data }) => {
835
+ return (
836
+ <section
837
+ style={{
838
+ '--local-bg': 'var(--background)',
839
+ '--local-text': 'var(--foreground)',
840
+ '--local-text-muted': 'var(--muted-foreground)',
841
+ '--local-primary': 'var(--primary)',
842
+ '--local-accent': 'var(--color-accent, #60a5fa)',
843
+ '--local-cyan': 'var(--color-secondary, #22d3ee)',
844
+ '--local-border': 'var(--border)',
845
+ } as React.CSSProperties}
846
+ className="jp-hero relative min-h-screen flex items-center overflow-hidden pt-24 bg-[var(--local-bg)]"
847
+ >
848
+ <div className="absolute -top-[40%] -right-[20%] w-[70vw] h-[70vw] rounded-full bg-[radial-gradient(circle,rgba(59,130,246,0.06)_0%,transparent_70%)] pointer-events-none" />
849
+ <div className="absolute -bottom-[10%] -left-[10%] w-[50vw] h-[50vw] rounded-full bg-[radial-gradient(circle,rgba(34,211,238,0.03)_0%,transparent_60%)] pointer-events-none" />
850
+
851
+ <div className="relative max-w-[1200px] mx-auto px-8 w-full">
852
+ <div className="max-w-[820px]">
853
+ {data.badge && (
854
+ <div className="inline-flex items-center gap-2 bg-[rgba(59,130,246,0.08)] border border-[rgba(59,130,246,0.2)] px-4 py-1.5 rounded-full text-[0.78rem] font-semibold text-[var(--local-accent)] mb-8 tracking-wide jp-animate-in" data-jp-field="badge">
855
+ <span className="w-1.5 h-1.5 rounded-full bg-[var(--local-accent)] jp-pulse-dot" />
856
+ {data.badge}
857
+ </div>
858
+ )}
859
+ <h1
860
+ className="font-display text-[clamp(2.8rem,6vw,4.8rem)] font-black text-[var(--local-text)] leading-[1.08] tracking-tight mb-6 jp-animate-in jp-d1"
861
+ data-jp-field="title"
862
+ >
863
+ {data.title}
864
+ {data.titleHighlight && (
865
+ <>
866
+ <br />
867
+ <em className="not-italic bg-gradient-to-br from-[var(--local-accent)] to-[var(--local-cyan)] bg-clip-text text-transparent" data-jp-field="titleHighlight">
868
+ {data.titleHighlight}
869
+ </em>
870
+ </>
871
+ )}
872
+ </h1>
873
+ {data.description && (
874
+ <p className="text-xl text-[var(--local-text-muted)] max-w-[600px] leading-relaxed mb-10 jp-animate-in jp-d2" data-jp-field="description">
875
+ {data.description}
876
+ </p>
877
+ )}
878
+ {data.ctas && data.ctas.length > 0 && (
879
+ <div className="flex gap-4 flex-wrap jp-animate-in jp-d3">
880
+ {data.ctas.map((cta, idx) => (
881
+ <a
882
+ key={cta.id ?? idx}
883
+ href={cta.href}
884
+ data-jp-item-id={cta.id ?? `legacy-${idx}`}
885
+ data-jp-item-field="ctas"
886
+ className={cn(
887
+ 'inline-flex items-center gap-2 px-8 py-3.5 rounded-[5px] font-semibold text-base transition-all duration-200 no-underline',
888
+ cta.variant === 'primary'
889
+ ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-0.5 hover:shadow-[0_8px_30px_rgba(59,130,246,0.3)]'
890
+ : 'bg-transparent text-[var(--local-text)] border border-[rgba(255,255,255,0.12)] hover:border-[rgba(255,255,255,0.3)] hover:bg-[rgba(255,255,255,0.04)]'
891
+ )}
892
+ >
893
+ {cta.label}
894
+ </a>
895
+ ))}
896
+ </div>
897
+ )}
898
+ {data.metrics && data.metrics.length > 0 && (
899
+ <div className="flex gap-12 mt-16 pt-12 border-t border-[rgba(255,255,255,0.06)] flex-wrap jp-animate-in jp-d4">
900
+ {data.metrics.map((metric, idx) => (
901
+ <div
902
+ key={(metric as { id?: string }).id ?? idx}
903
+ data-jp-item-id={(metric as { id?: string }).id ?? `legacy-${idx}`}
904
+ data-jp-item-field="metrics"
905
+ >
906
+ <div className="text-[2rem] font-bold text-[var(--local-text)] font-display">
907
+ {metric.val}
908
+ </div>
909
+ <div className="text-[0.82rem] text-[var(--muted-foreground)] mt-0.5 opacity-70">
910
+ {metric.label}
911
+ </div>
912
+ </div>
913
+ ))}
914
+ </div>
915
+ )}
916
+ </div>
917
+ </div>
918
+ </section>
919
+ );
920
+ };
921
921
 
922
922
  END_OF_FILE_CONTENT
923
923
  echo "Creating src/components/hero/index.ts..."
924
924
  cat << 'END_OF_FILE_CONTENT' > "src/components/hero/index.ts"
925
- export * from './View';
926
- export * from './schema';
927
- export * from './types';
925
+ export * from './View';
926
+ export * from './schema';
927
+ export * from './types';
928
928
 
929
929
  END_OF_FILE_CONTENT
930
930
  echo "Creating src/components/hero/schema.ts..."
931
931
  cat << 'END_OF_FILE_CONTENT' > "src/components/hero/schema.ts"
932
- import { z } from 'zod';
933
- import { BaseSectionData, CtaSchema } from '@/lib/base-schemas';
934
-
935
- const HeroMetricSchema = z.object({
936
- val: z.string().describe('ui:text'),
937
- label: z.string().describe('ui:text'),
938
- });
939
-
940
- export const HeroSchema = BaseSectionData.extend({
941
- badge: z.string().optional().describe('ui:text'),
942
- title: z.string().describe('ui:text'),
943
- titleHighlight: z.string().optional().describe('ui:text'),
944
- description: z.string().optional().describe('ui:textarea'),
945
- ctas: z.array(CtaSchema).optional().describe('ui:list'),
946
- metrics: z.array(HeroMetricSchema).optional().describe('ui:list'),
947
- });
932
+ import { z } from 'zod';
933
+ import { BaseSectionData, CtaSchema } from '@/lib/base-schemas';
934
+
935
+ const HeroMetricSchema = z.object({
936
+ val: z.string().describe('ui:text'),
937
+ label: z.string().describe('ui:text'),
938
+ });
939
+
940
+ export const HeroSchema = BaseSectionData.extend({
941
+ badge: z.string().optional().describe('ui:text'),
942
+ title: z.string().describe('ui:text'),
943
+ titleHighlight: z.string().optional().describe('ui:text'),
944
+ description: z.string().optional().describe('ui:textarea'),
945
+ ctas: z.array(CtaSchema).optional().describe('ui:list'),
946
+ metrics: z.array(HeroMetricSchema).optional().describe('ui:list'),
947
+ });
948
948
 
949
949
  END_OF_FILE_CONTENT
950
950
  echo "Creating src/components/hero/types.ts..."
951
951
  cat << 'END_OF_FILE_CONTENT' > "src/components/hero/types.ts"
952
- import { z } from 'zod';
953
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
954
- import { HeroSchema } from './schema';
955
-
956
- export type HeroData = z.infer<typeof HeroSchema>;
957
- export type HeroSettings = z.infer<typeof BaseSectionSettingsSchema>;
952
+ import { z } from 'zod';
953
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
954
+ import { HeroSchema } from './schema';
955
+
956
+ export type HeroData = z.infer<typeof HeroSchema>;
957
+ export type HeroSettings = z.infer<typeof BaseSectionSettingsSchema>;
958
958
 
959
959
  END_OF_FILE_CONTENT
960
960
  mkdir -p "src/components/image-test"
961
961
  mkdir -p "src/components/pa-section"
962
962
  echo "Creating src/components/pa-section/View.tsx..."
963
963
  cat << 'END_OF_FILE_CONTENT' > "src/components/pa-section/View.tsx"
964
- import React from 'react';
965
- import type { PaSectionData, PaSectionSettings } from './types';
966
-
967
- export const PaSection: React.FC<{ data: PaSectionData; settings?: PaSectionSettings }> = ({ data }) => {
968
- return (
969
- <section
970
- style={{
971
- '--local-bg': 'var(--card)',
972
- '--local-text': 'var(--foreground)',
973
- '--local-text-muted': 'var(--muted-foreground)',
974
- '--local-primary': 'var(--primary)',
975
- '--local-accent': 'var(--color-accent, #60a5fa)',
976
- '--local-deep': 'var(--background)',
977
- } as React.CSSProperties}
978
- className="relative z-0 py-28 bg-[var(--local-bg)]"
979
- >
980
- <div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
981
- <div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
982
- <div className="max-w-[1200px] mx-auto px-8">
983
- {data.label && (
984
- <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
985
- <span className="w-5 h-px bg-[var(--local-primary)]" />
986
- {data.label}
987
- </div>
988
- )}
989
- <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
990
- {data.title}
991
- </h2>
992
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center mt-12">
993
- <div>
994
- <h3 className="text-2xl font-bold text-[var(--local-text)] mb-4" data-jp-field="subtitle">
995
- {data.subtitle}
996
- </h3>
997
- {data.paragraphs.map((p, idx) => (
998
- <p key={idx} className="text-[var(--local-text-muted)] mb-5 text-[1.05rem] leading-relaxed" data-jp-item-id={(p as { id?: string }).id ?? `legacy-${idx}`} data-jp-item-field="paragraphs">
999
- {p.text}
1000
- </p>
1001
- ))}
1002
- {data.badges && data.badges.length > 0 && (
1003
- <div className="flex gap-2.5 flex-wrap mt-4">
1004
- {data.badges.map((badge, idx) => (
1005
- <span
1006
- key={idx}
1007
- className="inline-flex items-center gap-1.5 bg-[rgba(34,197,94,0.08)] border border-[rgba(34,197,94,0.2)] text-[#4ade80] px-3 py-1.5 rounded-md text-[0.78rem] font-semibold"
1008
- data-jp-item-id={(badge as { id?: string }).id ?? `legacy-${idx}`}
1009
- data-jp-item-field="badges"
1010
- >
1011
- {badge.label}
1012
- </span>
1013
- ))}
1014
- </div>
1015
- )}
1016
- </div>
1017
- <div className="border border-[rgba(255,255,255,0.06)] rounded-lg p-12 bg-[rgba(255,255,255,0.02)] text-center">
1018
- {data.engines && data.engines.length >= 2 && (
1019
- <div className="flex items-center justify-center gap-6 mb-8">
1020
- {data.engines.map((engine, idx) => (
1021
- <React.Fragment key={idx}>
1022
- {idx > 0 && (
1023
- <span className="text-[var(--local-text-muted)] text-2xl opacity-50">⇄</span>
1024
- )}
1025
- <div
1026
- className={
1027
- engine.variant === 'tailwind'
1028
- ? 'px-6 py-4 rounded-xl font-bold text-[0.95rem] border bg-[rgba(59,130,246,0.08)] border-[rgba(59,130,246,0.2)] text-[#60a5fa]'
1029
- : 'px-6 py-4 rounded-xl font-bold text-[0.95rem] border bg-[rgba(34,197,94,0.08)] border-[rgba(34,197,94,0.2)] text-[#4ade80]'
1030
- }
1031
- data-jp-item-id={(engine as { id?: string }).id ?? `legacy-${idx}`}
1032
- data-jp-item-field="engines"
1033
- >
1034
- {engine.label}
1035
- </div>
1036
- </React.Fragment>
1037
- ))}
1038
- </div>
1039
- )}
1040
- {data.codeSnippet && (
1041
- <div className="font-mono text-[0.85rem] text-[var(--local-text-muted)] bg-[var(--local-deep)] p-4 rounded-lg text-left border border-[rgba(255,255,255,0.04)]" data-jp-field="codeSnippet">
1042
- <pre className="whitespace-pre-wrap m-0">{data.codeSnippet}</pre>
1043
- <div className="mt-4 text-[0.75rem] text-center opacity-50">
1044
- Same JSON. Different Render Engine.
1045
- </div>
1046
- </div>
1047
- )}
1048
- </div>
1049
- </div>
1050
- </div>
1051
- </section>
1052
- );
1053
- };
964
+ import React from 'react';
965
+ import type { PaSectionData, PaSectionSettings } from './types';
966
+
967
+ export const PaSection: React.FC<{ data: PaSectionData; settings?: PaSectionSettings }> = ({ data }) => {
968
+ return (
969
+ <section
970
+ style={{
971
+ '--local-bg': 'var(--card)',
972
+ '--local-text': 'var(--foreground)',
973
+ '--local-text-muted': 'var(--muted-foreground)',
974
+ '--local-primary': 'var(--primary)',
975
+ '--local-accent': 'var(--color-accent, #60a5fa)',
976
+ '--local-deep': 'var(--background)',
977
+ } as React.CSSProperties}
978
+ className="relative z-0 py-28 bg-[var(--local-bg)]"
979
+ >
980
+ <div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
981
+ <div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.1)] to-transparent" />
982
+ <div className="max-w-[1200px] mx-auto px-8">
983
+ {data.label && (
984
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
985
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
986
+ {data.label}
987
+ </div>
988
+ )}
989
+ <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
990
+ {data.title}
991
+ </h2>
992
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center mt-12">
993
+ <div>
994
+ <h3 className="text-2xl font-bold text-[var(--local-text)] mb-4" data-jp-field="subtitle">
995
+ {data.subtitle}
996
+ </h3>
997
+ {data.paragraphs.map((p, idx) => (
998
+ <p key={idx} className="text-[var(--local-text-muted)] mb-5 text-[1.05rem] leading-relaxed" data-jp-item-id={(p as { id?: string }).id ?? `legacy-${idx}`} data-jp-item-field="paragraphs">
999
+ {p.text}
1000
+ </p>
1001
+ ))}
1002
+ {data.badges && data.badges.length > 0 && (
1003
+ <div className="flex gap-2.5 flex-wrap mt-4">
1004
+ {data.badges.map((badge, idx) => (
1005
+ <span
1006
+ key={idx}
1007
+ className="inline-flex items-center gap-1.5 bg-[rgba(34,197,94,0.08)] border border-[rgba(34,197,94,0.2)] text-[#4ade80] px-3 py-1.5 rounded-md text-[0.78rem] font-semibold"
1008
+ data-jp-item-id={(badge as { id?: string }).id ?? `legacy-${idx}`}
1009
+ data-jp-item-field="badges"
1010
+ >
1011
+ {badge.label}
1012
+ </span>
1013
+ ))}
1014
+ </div>
1015
+ )}
1016
+ </div>
1017
+ <div className="border border-[rgba(255,255,255,0.06)] rounded-lg p-12 bg-[rgba(255,255,255,0.02)] text-center">
1018
+ {data.engines && data.engines.length >= 2 && (
1019
+ <div className="flex items-center justify-center gap-6 mb-8">
1020
+ {data.engines.map((engine, idx) => (
1021
+ <React.Fragment key={idx}>
1022
+ {idx > 0 && (
1023
+ <span className="text-[var(--local-text-muted)] text-2xl opacity-50">⇄</span>
1024
+ )}
1025
+ <div
1026
+ className={
1027
+ engine.variant === 'tailwind'
1028
+ ? 'px-6 py-4 rounded-xl font-bold text-[0.95rem] border bg-[rgba(59,130,246,0.08)] border-[rgba(59,130,246,0.2)] text-[#60a5fa]'
1029
+ : 'px-6 py-4 rounded-xl font-bold text-[0.95rem] border bg-[rgba(34,197,94,0.08)] border-[rgba(34,197,94,0.2)] text-[#4ade80]'
1030
+ }
1031
+ data-jp-item-id={(engine as { id?: string }).id ?? `legacy-${idx}`}
1032
+ data-jp-item-field="engines"
1033
+ >
1034
+ {engine.label}
1035
+ </div>
1036
+ </React.Fragment>
1037
+ ))}
1038
+ </div>
1039
+ )}
1040
+ {data.codeSnippet && (
1041
+ <div className="font-mono text-[0.85rem] text-[var(--local-text-muted)] bg-[var(--local-deep)] p-4 rounded-lg text-left border border-[rgba(255,255,255,0.04)]" data-jp-field="codeSnippet">
1042
+ <pre className="whitespace-pre-wrap m-0">{data.codeSnippet}</pre>
1043
+ <div className="mt-4 text-[0.75rem] text-center opacity-50">
1044
+ Same JSON. Different Render Engine.
1045
+ </div>
1046
+ </div>
1047
+ )}
1048
+ </div>
1049
+ </div>
1050
+ </div>
1051
+ </section>
1052
+ );
1053
+ };
1054
1054
 
1055
1055
  END_OF_FILE_CONTENT
1056
1056
  echo "Creating src/components/pa-section/index.ts..."
1057
1057
  cat << 'END_OF_FILE_CONTENT' > "src/components/pa-section/index.ts"
1058
- export * from './View';
1059
- export * from './schema';
1060
- export * from './types';
1058
+ export * from './View';
1059
+ export * from './schema';
1060
+ export * from './types';
1061
1061
 
1062
1062
  END_OF_FILE_CONTENT
1063
1063
  echo "Creating src/components/pa-section/schema.ts..."
1064
1064
  cat << 'END_OF_FILE_CONTENT' > "src/components/pa-section/schema.ts"
1065
- import { z } from 'zod';
1066
- import { BaseSectionData } from '@/lib/base-schemas';
1067
-
1068
- const PaBadgeSchema = z.object({
1069
- label: z.string().describe('ui:text'),
1070
- });
1071
-
1072
- const PaEngineSchema = z.object({
1073
- label: z.string().describe('ui:text'),
1074
- variant: z.enum(['tailwind', 'bootstrap']).describe('ui:select'),
1075
- });
1076
-
1077
- export const PaSectionSchema = BaseSectionData.extend({
1078
- label: z.string().optional().describe('ui:text'),
1079
- title: z.string().describe('ui:text'),
1080
- subtitle: z.string().describe('ui:text'),
1081
- paragraphs: z.array(z.object({ text: z.string().describe('ui:textarea') })).describe('ui:list'),
1082
- badges: z.array(PaBadgeSchema).optional().describe('ui:list'),
1083
- engines: z.array(PaEngineSchema).optional().describe('ui:list'),
1084
- codeSnippet: z.string().optional().describe('ui:textarea'),
1085
- });
1065
+ import { z } from 'zod';
1066
+ import { BaseSectionData } from '@/lib/base-schemas';
1067
+
1068
+ const PaBadgeSchema = z.object({
1069
+ label: z.string().describe('ui:text'),
1070
+ });
1071
+
1072
+ const PaEngineSchema = z.object({
1073
+ label: z.string().describe('ui:text'),
1074
+ variant: z.enum(['tailwind', 'bootstrap']).describe('ui:select'),
1075
+ });
1076
+
1077
+ export const PaSectionSchema = BaseSectionData.extend({
1078
+ label: z.string().optional().describe('ui:text'),
1079
+ title: z.string().describe('ui:text'),
1080
+ subtitle: z.string().describe('ui:text'),
1081
+ paragraphs: z.array(z.object({ text: z.string().describe('ui:textarea') })).describe('ui:list'),
1082
+ badges: z.array(PaBadgeSchema).optional().describe('ui:list'),
1083
+ engines: z.array(PaEngineSchema).optional().describe('ui:list'),
1084
+ codeSnippet: z.string().optional().describe('ui:textarea'),
1085
+ });
1086
1086
 
1087
1087
  END_OF_FILE_CONTENT
1088
1088
  echo "Creating src/components/pa-section/types.ts..."
1089
1089
  cat << 'END_OF_FILE_CONTENT' > "src/components/pa-section/types.ts"
1090
- import { z } from 'zod';
1091
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1092
- import { PaSectionSchema } from './schema';
1093
-
1094
- export type PaSectionData = z.infer<typeof PaSectionSchema>;
1095
- export type PaSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
1090
+ import { z } from 'zod';
1091
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1092
+ import { PaSectionSchema } from './schema';
1093
+
1094
+ export type PaSectionData = z.infer<typeof PaSectionSchema>;
1095
+ export type PaSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
1096
1096
 
1097
1097
  END_OF_FILE_CONTENT
1098
1098
  mkdir -p "src/components/philosophy"
1099
1099
  echo "Creating src/components/philosophy/View.tsx..."
1100
1100
  cat << 'END_OF_FILE_CONTENT' > "src/components/philosophy/View.tsx"
1101
- import React from 'react';
1102
- import type { PhilosophyData, PhilosophySettings } from './types';
1103
-
1104
- export const Philosophy: React.FC<{ data: PhilosophyData; settings?: PhilosophySettings }> = ({ data }) => {
1105
- const renderQuote = () => {
1106
- if (!data.quoteHighlightWord) {
1107
- return <>{data.quote}</>;
1108
- }
1109
- const parts = data.quote.split(data.quoteHighlightWord);
1110
- return (
1111
- <>
1112
- {parts.map((part, idx) => (
1113
- <React.Fragment key={idx}>
1114
- {part}
1115
- {idx < parts.length - 1 && (
1116
- <em className="not-italic text-[var(--local-accent)]">
1117
- {data.quoteHighlightWord}
1118
- </em>
1119
- )}
1120
- </React.Fragment>
1121
- ))}
1122
- </>
1123
- );
1124
- };
1125
-
1126
- return (
1127
- <section
1128
- style={{
1129
- '--local-bg': 'var(--background)',
1130
- '--local-text': 'var(--foreground)',
1131
- '--local-text-muted': 'var(--muted-foreground)',
1132
- '--local-accent': 'var(--color-accent, #60a5fa)',
1133
- '--local-primary': 'var(--primary)',
1134
- } as React.CSSProperties}
1135
- className="relative z-0 py-28 bg-[var(--local-bg)]"
1136
- >
1137
- <div className="max-w-[1200px] mx-auto px-8">
1138
- <div className="max-w-[760px] mx-auto text-center">
1139
- {data.label && (
1140
- <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
1141
- <span className="w-5 h-px bg-[var(--local-primary)]" />
1142
- {data.label}
1143
- </div>
1144
- )}
1145
- <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
1146
- {data.title}
1147
- </h2>
1148
- <blockquote className="font-display text-[clamp(1.6rem,3vw,2.4rem)] text-[var(--local-text)] font-bold leading-[1.35] my-8" data-jp-field="quote">
1149
- &ldquo;{renderQuote()}&rdquo;
1150
- </blockquote>
1151
- {data.description && (
1152
- <p className="text-[1.05rem] text-[var(--local-text-muted)] max-w-[560px] mx-auto leading-relaxed" data-jp-field="description">
1153
- {data.description}
1154
- </p>
1155
- )}
1156
- </div>
1157
- </div>
1158
- </section>
1159
- );
1160
- };
1101
+ import React from 'react';
1102
+ import type { PhilosophyData, PhilosophySettings } from './types';
1103
+
1104
+ export const Philosophy: React.FC<{ data: PhilosophyData; settings?: PhilosophySettings }> = ({ data }) => {
1105
+ const renderQuote = () => {
1106
+ if (!data.quoteHighlightWord) {
1107
+ return <>{data.quote}</>;
1108
+ }
1109
+ const parts = data.quote.split(data.quoteHighlightWord);
1110
+ return (
1111
+ <>
1112
+ {parts.map((part, idx) => (
1113
+ <React.Fragment key={idx}>
1114
+ {part}
1115
+ {idx < parts.length - 1 && (
1116
+ <em className="not-italic text-[var(--local-accent)]">
1117
+ {data.quoteHighlightWord}
1118
+ </em>
1119
+ )}
1120
+ </React.Fragment>
1121
+ ))}
1122
+ </>
1123
+ );
1124
+ };
1125
+
1126
+ return (
1127
+ <section
1128
+ style={{
1129
+ '--local-bg': 'var(--background)',
1130
+ '--local-text': 'var(--foreground)',
1131
+ '--local-text-muted': 'var(--muted-foreground)',
1132
+ '--local-accent': 'var(--color-accent, #60a5fa)',
1133
+ '--local-primary': 'var(--primary)',
1134
+ } as React.CSSProperties}
1135
+ className="relative z-0 py-28 bg-[var(--local-bg)]"
1136
+ >
1137
+ <div className="max-w-[1200px] mx-auto px-8">
1138
+ <div className="max-w-[760px] mx-auto text-center">
1139
+ {data.label && (
1140
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
1141
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
1142
+ {data.label}
1143
+ </div>
1144
+ )}
1145
+ <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
1146
+ {data.title}
1147
+ </h2>
1148
+ <blockquote className="font-display text-[clamp(1.6rem,3vw,2.4rem)] text-[var(--local-text)] font-bold leading-[1.35] my-8" data-jp-field="quote">
1149
+ &ldquo;{renderQuote()}&rdquo;
1150
+ </blockquote>
1151
+ {data.description && (
1152
+ <p className="text-[1.05rem] text-[var(--local-text-muted)] max-w-[560px] mx-auto leading-relaxed" data-jp-field="description">
1153
+ {data.description}
1154
+ </p>
1155
+ )}
1156
+ </div>
1157
+ </div>
1158
+ </section>
1159
+ );
1160
+ };
1161
1161
 
1162
1162
  END_OF_FILE_CONTENT
1163
1163
  echo "Creating src/components/philosophy/index.ts..."
1164
1164
  cat << 'END_OF_FILE_CONTENT' > "src/components/philosophy/index.ts"
1165
- export * from './View';
1166
- export * from './schema';
1167
- export * from './types';
1165
+ export * from './View';
1166
+ export * from './schema';
1167
+ export * from './types';
1168
1168
 
1169
1169
  END_OF_FILE_CONTENT
1170
1170
  echo "Creating src/components/philosophy/schema.ts..."
1171
1171
  cat << 'END_OF_FILE_CONTENT' > "src/components/philosophy/schema.ts"
1172
- import { z } from 'zod';
1173
- import { BaseSectionData } from '@/lib/base-schemas';
1174
-
1175
- export const PhilosophySchema = BaseSectionData.extend({
1176
- label: z.string().optional().describe('ui:text'),
1177
- title: z.string().describe('ui:text'),
1178
- quote: z.string().describe('ui:textarea'),
1179
- quoteHighlightWord: z.string().optional().describe('ui:text'),
1180
- description: z.string().optional().describe('ui:textarea'),
1181
- });
1172
+ import { z } from 'zod';
1173
+ import { BaseSectionData } from '@/lib/base-schemas';
1174
+
1175
+ export const PhilosophySchema = BaseSectionData.extend({
1176
+ label: z.string().optional().describe('ui:text'),
1177
+ title: z.string().describe('ui:text'),
1178
+ quote: z.string().describe('ui:textarea'),
1179
+ quoteHighlightWord: z.string().optional().describe('ui:text'),
1180
+ description: z.string().optional().describe('ui:textarea'),
1181
+ });
1182
1182
 
1183
1183
  END_OF_FILE_CONTENT
1184
1184
  echo "Creating src/components/philosophy/types.ts..."
1185
1185
  cat << 'END_OF_FILE_CONTENT' > "src/components/philosophy/types.ts"
1186
- import { z } from 'zod';
1187
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1188
- import { PhilosophySchema } from './schema';
1189
-
1190
- export type PhilosophyData = z.infer<typeof PhilosophySchema>;
1191
- export type PhilosophySettings = z.infer<typeof BaseSectionSettingsSchema>;
1186
+ import { z } from 'zod';
1187
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1188
+ import { PhilosophySchema } from './schema';
1189
+
1190
+ export type PhilosophyData = z.infer<typeof PhilosophySchema>;
1191
+ export type PhilosophySettings = z.infer<typeof BaseSectionSettingsSchema>;
1192
1192
 
1193
1193
  END_OF_FILE_CONTENT
1194
1194
  mkdir -p "src/components/pillars-grid"
1195
1195
  echo "Creating src/components/pillars-grid/View.tsx..."
1196
1196
  cat << 'END_OF_FILE_CONTENT' > "src/components/pillars-grid/View.tsx"
1197
- import React from 'react';
1198
- import { cn } from '@/lib/utils';
1199
- import { Icon, isIconName } from '@/lib/IconResolver';
1200
- import type { PillarsGridData, PillarsGridSettings, PillarIconVariant, PillarTagVariant } from './types';
1201
-
1202
- const iconVariantStyles: Record<PillarIconVariant, string> = {
1203
- split: 'bg-[rgba(59,130,246,0.1)] text-[#60a5fa]',
1204
- registry: 'bg-[rgba(34,211,238,0.1)] text-[#22d3ee]',
1205
- federation: 'bg-[rgba(168,85,247,0.1)] text-[#c084fc]',
1206
- };
1207
-
1208
- const tagVariantStyles: Record<PillarTagVariant, string> = {
1209
- core: 'bg-[rgba(59,130,246,0.1)] text-[#60a5fa]',
1210
- pattern: 'bg-[rgba(34,211,238,0.1)] text-[#22d3ee]',
1211
- enterprise: 'bg-[rgba(168,85,247,0.1)] text-[#c084fc]',
1212
- };
1213
-
1214
- export const PillarsGrid: React.FC<{ data: PillarsGridData; settings?: PillarsGridSettings }> = ({ data }) => {
1215
- return (
1216
- <section
1217
- style={{
1218
- '--local-bg': 'var(--background)',
1219
- '--local-text': 'var(--foreground)',
1220
- '--local-text-muted': 'var(--muted-foreground)',
1221
- '--local-primary': 'var(--primary)',
1222
- '--local-accent': 'var(--color-accent, #60a5fa)',
1223
- '--local-border': 'var(--border)',
1224
- } as React.CSSProperties}
1225
- className="relative z-0 py-28 bg-[var(--local-bg)]"
1226
- >
1227
- <div className="absolute top-0 left-1/2 -translate-x-1/2 w-[80%] h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.15)] to-transparent" />
1228
- <div className="max-w-[1200px] mx-auto px-8">
1229
- {data.label && (
1230
- <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
1231
- <span className="w-5 h-px bg-[var(--local-primary)]" />
1232
- {data.label}
1233
- </div>
1234
- )}
1235
- <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
1236
- {data.title}
1237
- </h2>
1238
- {data.description && (
1239
- <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] leading-relaxed" data-jp-field="description">
1240
- {data.description}
1241
- </p>
1242
- )}
1243
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-14">
1244
- {data.pillars.map((pillar, idx) => (
1245
- <div
1246
- key={pillar.id ?? idx}
1247
- className="jp-pillar-card group relative border border-[rgba(255,255,255,0.06)] rounded-lg p-10 bg-[rgba(255,255,255,0.015)] transition-all duration-300 overflow-hidden hover:border-[rgba(59,130,246,0.2)] hover:-translate-y-1 hover:bg-[rgba(59,130,246,0.03)]"
1248
- data-jp-item-id={pillar.id ?? `legacy-${idx}`}
1249
- data-jp-item-field="pillars"
1250
- >
1251
- <div className="absolute top-0 left-0 right-0 h-[3px] bg-gradient-to-r from-[var(--local-primary)] to-[#22d3ee] opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
1252
- <div className={cn(
1253
- 'w-12 h-12 rounded-xl flex items-center justify-center mb-6 text-xl font-bold',
1254
- iconVariantStyles[pillar.iconVariant]
1255
- )}>
1256
- {pillar.icon && isIconName(pillar.icon) ? (
1257
- <Icon name={pillar.icon} size={24} className="shrink-0" />
1258
- ) : pillar.icon ? (
1259
- <span>{pillar.icon}</span>
1260
- ) : null}
1261
- </div>
1262
- <h3 className="text-xl font-bold text-[var(--local-text)] mb-3">
1263
- {pillar.title}
1264
- </h3>
1265
- <p className="text-[0.95rem] text-[var(--local-text-muted)] leading-relaxed">
1266
- {pillar.description}
1267
- </p>
1268
- <span className={cn(
1269
- 'inline-block text-[0.7rem] font-semibold uppercase tracking-wide px-3 py-1 rounded mt-4',
1270
- tagVariantStyles[pillar.tagVariant]
1271
- )}>
1272
- {pillar.tag}
1273
- </span>
1274
- </div>
1275
- ))}
1276
- </div>
1277
- </div>
1278
- </section>
1279
- );
1280
- };
1197
+ import React from 'react';
1198
+ import { cn } from '@/lib/utils';
1199
+ import { Icon, isIconName } from '@/lib/IconResolver';
1200
+ import type { PillarsGridData, PillarsGridSettings, PillarIconVariant, PillarTagVariant } from './types';
1201
+
1202
+ const iconVariantStyles: Record<PillarIconVariant, string> = {
1203
+ split: 'bg-[rgba(59,130,246,0.1)] text-[#60a5fa]',
1204
+ registry: 'bg-[rgba(34,211,238,0.1)] text-[#22d3ee]',
1205
+ federation: 'bg-[rgba(168,85,247,0.1)] text-[#c084fc]',
1206
+ };
1207
+
1208
+ const tagVariantStyles: Record<PillarTagVariant, string> = {
1209
+ core: 'bg-[rgba(59,130,246,0.1)] text-[#60a5fa]',
1210
+ pattern: 'bg-[rgba(34,211,238,0.1)] text-[#22d3ee]',
1211
+ enterprise: 'bg-[rgba(168,85,247,0.1)] text-[#c084fc]',
1212
+ };
1213
+
1214
+ export const PillarsGrid: React.FC<{ data: PillarsGridData; settings?: PillarsGridSettings }> = ({ data }) => {
1215
+ return (
1216
+ <section
1217
+ style={{
1218
+ '--local-bg': 'var(--background)',
1219
+ '--local-text': 'var(--foreground)',
1220
+ '--local-text-muted': 'var(--muted-foreground)',
1221
+ '--local-primary': 'var(--primary)',
1222
+ '--local-accent': 'var(--color-accent, #60a5fa)',
1223
+ '--local-border': 'var(--border)',
1224
+ } as React.CSSProperties}
1225
+ className="relative z-0 py-28 bg-[var(--local-bg)]"
1226
+ >
1227
+ <div className="absolute top-0 left-1/2 -translate-x-1/2 w-[80%] h-px bg-gradient-to-r from-transparent via-[rgba(59,130,246,0.15)] to-transparent" />
1228
+ <div className="max-w-[1200px] mx-auto px-8">
1229
+ {data.label && (
1230
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
1231
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
1232
+ {data.label}
1233
+ </div>
1234
+ )}
1235
+ <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
1236
+ {data.title}
1237
+ </h2>
1238
+ {data.description && (
1239
+ <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] leading-relaxed" data-jp-field="description">
1240
+ {data.description}
1241
+ </p>
1242
+ )}
1243
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-14">
1244
+ {data.pillars.map((pillar, idx) => (
1245
+ <div
1246
+ key={pillar.id ?? idx}
1247
+ className="jp-pillar-card group relative border border-[rgba(255,255,255,0.06)] rounded-lg p-10 bg-[rgba(255,255,255,0.015)] transition-all duration-300 overflow-hidden hover:border-[rgba(59,130,246,0.2)] hover:-translate-y-1 hover:bg-[rgba(59,130,246,0.03)]"
1248
+ data-jp-item-id={pillar.id ?? `legacy-${idx}`}
1249
+ data-jp-item-field="pillars"
1250
+ >
1251
+ <div className="absolute top-0 left-0 right-0 h-[3px] bg-gradient-to-r from-[var(--local-primary)] to-[#22d3ee] opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
1252
+ <div className={cn(
1253
+ 'w-12 h-12 rounded-xl flex items-center justify-center mb-6 text-xl font-bold',
1254
+ iconVariantStyles[pillar.iconVariant]
1255
+ )}>
1256
+ {pillar.icon && isIconName(pillar.icon) ? (
1257
+ <Icon name={pillar.icon} size={24} className="shrink-0" />
1258
+ ) : pillar.icon ? (
1259
+ <span>{pillar.icon}</span>
1260
+ ) : null}
1261
+ </div>
1262
+ <h3 className="text-xl font-bold text-[var(--local-text)] mb-3">
1263
+ {pillar.title}
1264
+ </h3>
1265
+ <p className="text-[0.95rem] text-[var(--local-text-muted)] leading-relaxed">
1266
+ {pillar.description}
1267
+ </p>
1268
+ <span className={cn(
1269
+ 'inline-block text-[0.7rem] font-semibold uppercase tracking-wide px-3 py-1 rounded mt-4',
1270
+ tagVariantStyles[pillar.tagVariant]
1271
+ )}>
1272
+ {pillar.tag}
1273
+ </span>
1274
+ </div>
1275
+ ))}
1276
+ </div>
1277
+ </div>
1278
+ </section>
1279
+ );
1280
+ };
1281
1281
 
1282
1282
  END_OF_FILE_CONTENT
1283
1283
  echo "Creating src/components/pillars-grid/index.ts..."
1284
1284
  cat << 'END_OF_FILE_CONTENT' > "src/components/pillars-grid/index.ts"
1285
- export * from './View';
1286
- export * from './schema';
1287
- export * from './types';
1285
+ export * from './View';
1286
+ export * from './schema';
1287
+ export * from './types';
1288
1288
 
1289
1289
  END_OF_FILE_CONTENT
1290
1290
  echo "Creating src/components/pillars-grid/schema.ts..."
1291
1291
  cat << 'END_OF_FILE_CONTENT' > "src/components/pillars-grid/schema.ts"
1292
- import { z } from 'zod';
1293
- import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
1294
-
1295
- export const PillarIconVariantSchema = z.enum(['split', 'registry', 'federation']);
1296
- export const PillarTagVariantSchema = z.enum(['core', 'pattern', 'enterprise']);
1297
-
1298
- const PillarCardSchema = BaseArrayItem.extend({
1299
- icon: z.string().describe('ui:icon-picker'),
1300
- iconVariant: PillarIconVariantSchema.describe('ui:select'),
1301
- title: z.string().describe('ui:text'),
1302
- description: z.string().describe('ui:textarea'),
1303
- tag: z.string().describe('ui:text'),
1304
- tagVariant: PillarTagVariantSchema.describe('ui:select'),
1305
- });
1306
-
1307
- export const PillarsGridSchema = BaseSectionData.extend({
1308
- label: z.string().optional().describe('ui:text'),
1309
- title: z.string().describe('ui:text'),
1310
- description: z.string().optional().describe('ui:textarea'),
1311
- pillars: z.array(PillarCardSchema).describe('ui:list'),
1312
- });
1292
+ import { z } from 'zod';
1293
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
1294
+
1295
+ export const PillarIconVariantSchema = z.enum(['split', 'registry', 'federation']);
1296
+ export const PillarTagVariantSchema = z.enum(['core', 'pattern', 'enterprise']);
1297
+
1298
+ const PillarCardSchema = BaseArrayItem.extend({
1299
+ icon: z.string().describe('ui:icon-picker'),
1300
+ iconVariant: PillarIconVariantSchema.describe('ui:select'),
1301
+ title: z.string().describe('ui:text'),
1302
+ description: z.string().describe('ui:textarea'),
1303
+ tag: z.string().describe('ui:text'),
1304
+ tagVariant: PillarTagVariantSchema.describe('ui:select'),
1305
+ });
1306
+
1307
+ export const PillarsGridSchema = BaseSectionData.extend({
1308
+ label: z.string().optional().describe('ui:text'),
1309
+ title: z.string().describe('ui:text'),
1310
+ description: z.string().optional().describe('ui:textarea'),
1311
+ pillars: z.array(PillarCardSchema).describe('ui:list'),
1312
+ });
1313
1313
 
1314
1314
  END_OF_FILE_CONTENT
1315
1315
  echo "Creating src/components/pillars-grid/types.ts..."
1316
1316
  cat << 'END_OF_FILE_CONTENT' > "src/components/pillars-grid/types.ts"
1317
- import { z } from 'zod';
1318
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1319
- import { PillarsGridSchema, PillarIconVariantSchema, PillarTagVariantSchema } from './schema';
1320
-
1321
- export type PillarsGridData = z.infer<typeof PillarsGridSchema>;
1322
- export type PillarsGridSettings = z.infer<typeof BaseSectionSettingsSchema>;
1323
- export type PillarIconVariant = z.infer<typeof PillarIconVariantSchema>;
1324
- export type PillarTagVariant = z.infer<typeof PillarTagVariantSchema>;
1317
+ import { z } from 'zod';
1318
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1319
+ import { PillarsGridSchema, PillarIconVariantSchema, PillarTagVariantSchema } from './schema';
1320
+
1321
+ export type PillarsGridData = z.infer<typeof PillarsGridSchema>;
1322
+ export type PillarsGridSettings = z.infer<typeof BaseSectionSettingsSchema>;
1323
+ export type PillarIconVariant = z.infer<typeof PillarIconVariantSchema>;
1324
+ export type PillarTagVariant = z.infer<typeof PillarTagVariantSchema>;
1325
1325
 
1326
1326
  END_OF_FILE_CONTENT
1327
1327
  mkdir -p "src/components/problem-statement"
1328
1328
  echo "Creating src/components/problem-statement/View.tsx..."
1329
1329
  cat << 'END_OF_FILE_CONTENT' > "src/components/problem-statement/View.tsx"
1330
- import React from 'react';
1331
- import { cn } from '@/lib/utils';
1332
- import type { ProblemStatementData, ProblemStatementSettings, SiloBlockVariant } from './types';
1333
-
1334
- const variantStyles: Record<SiloBlockVariant, string> = {
1335
- red: 'bg-[rgba(239,68,68,0.08)] border-[rgba(239,68,68,0.3)] text-[#f87171]',
1336
- amber: 'bg-[rgba(245,158,11,0.08)] border-[rgba(245,158,11,0.3)] text-[#fbbf24]',
1337
- green: 'bg-[rgba(34,197,94,0.08)] border-[rgba(34,197,94,0.3)] text-[#4ade80]',
1338
- blue: 'bg-[rgba(59,130,246,0.08)] border-[rgba(59,130,246,0.3)] text-[#60a5fa]',
1339
- };
1340
-
1341
- export const ProblemStatement: React.FC<{ data: ProblemStatementData; settings?: ProblemStatementSettings }> = ({ data }) => {
1342
- return (
1343
- <section
1344
- style={{
1345
- '--local-bg': 'var(--background)',
1346
- '--local-surface': 'var(--card)',
1347
- '--local-text': 'var(--foreground)',
1348
- '--local-text-muted': 'var(--muted-foreground)',
1349
- '--local-border': 'var(--border)',
1350
- } as React.CSSProperties}
1351
- className="jp-problem relative z-0 py-28 bg-gradient-to-b from-[var(--local-bg)] to-[var(--local-surface)]"
1352
- >
1353
- <div className="max-w-[1200px] mx-auto px-8">
1354
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
1355
- <div className="relative h-[360px] border border-[rgba(255,255,255,0.06)] rounded-lg bg-[rgba(255,255,255,0.02)] overflow-hidden flex items-center justify-center">
1356
- <div className="text-center p-8">
1357
- {data.siloGroups.map((group, gIdx) => (
1358
- <div
1359
- key={gIdx}
1360
- className="mb-4"
1361
- data-jp-item-id={(group as { id?: string }).id ?? `legacy-${gIdx}`}
1362
- data-jp-item-field="siloGroups"
1363
- >
1364
- <div className="flex flex-wrap justify-center gap-1.5">
1365
- {group.blocks.map((block, bIdx) => (
1366
- <span
1367
- key={(block as { id?: string }).id ?? bIdx}
1368
- className={cn(
1369
- 'inline-block px-4 py-2 rounded-lg text-[0.8rem] font-semibold border',
1370
- variantStyles[block.variant]
1371
- )}
1372
- data-jp-item-id={(block as { id?: string }).id ?? `legacy-${bIdx}`}
1373
- data-jp-item-field="blocks"
1374
- >
1375
- {block.label}
1376
- </span>
1377
- ))}
1378
- </div>
1379
- <span className="text-[0.7rem] text-[var(--local-text-muted)] uppercase tracking-widest mt-2 block opacity-60">
1380
- {group.label}
1381
- </span>
1382
- </div>
1383
- ))}
1384
- </div>
1385
- </div>
1386
- <div>
1387
- <h3 className="text-2xl font-bold text-[var(--local-text)] mb-4" data-jp-field="title">
1388
- {data.title}
1389
- </h3>
1390
- {data.paragraphs.map((p, idx) => (
1391
- <p
1392
- key={idx}
1393
- className="text-[var(--local-text-muted)] mb-5 text-[1.05rem] leading-relaxed"
1394
- data-jp-item-id={(p as { id?: string }).id ?? `legacy-${idx}`}
1395
- data-jp-item-field="paragraphs"
1396
- >
1397
- {p.isBold ? <strong className="text-[var(--local-text)]">{p.text}</strong> : p.text}
1398
- </p>
1399
- ))}
1400
- </div>
1401
- </div>
1402
- </div>
1403
- </section>
1404
- );
1405
- };
1330
+ import React from 'react';
1331
+ import { cn } from '@/lib/utils';
1332
+ import type { ProblemStatementData, ProblemStatementSettings, SiloBlockVariant } from './types';
1333
+
1334
+ const variantStyles: Record<SiloBlockVariant, string> = {
1335
+ red: 'bg-[rgba(239,68,68,0.08)] border-[rgba(239,68,68,0.3)] text-[#f87171]',
1336
+ amber: 'bg-[rgba(245,158,11,0.08)] border-[rgba(245,158,11,0.3)] text-[#fbbf24]',
1337
+ green: 'bg-[rgba(34,197,94,0.08)] border-[rgba(34,197,94,0.3)] text-[#4ade80]',
1338
+ blue: 'bg-[rgba(59,130,246,0.08)] border-[rgba(59,130,246,0.3)] text-[#60a5fa]',
1339
+ };
1340
+
1341
+ export const ProblemStatement: React.FC<{ data: ProblemStatementData; settings?: ProblemStatementSettings }> = ({ data }) => {
1342
+ return (
1343
+ <section
1344
+ style={{
1345
+ '--local-bg': 'var(--background)',
1346
+ '--local-surface': 'var(--card)',
1347
+ '--local-text': 'var(--foreground)',
1348
+ '--local-text-muted': 'var(--muted-foreground)',
1349
+ '--local-border': 'var(--border)',
1350
+ } as React.CSSProperties}
1351
+ className="jp-problem relative z-0 py-28 bg-gradient-to-b from-[var(--local-bg)] to-[var(--local-surface)]"
1352
+ >
1353
+ <div className="max-w-[1200px] mx-auto px-8">
1354
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
1355
+ <div className="relative h-[360px] border border-[rgba(255,255,255,0.06)] rounded-lg bg-[rgba(255,255,255,0.02)] overflow-hidden flex items-center justify-center">
1356
+ <div className="text-center p-8">
1357
+ {data.siloGroups.map((group, gIdx) => (
1358
+ <div
1359
+ key={gIdx}
1360
+ className="mb-4"
1361
+ data-jp-item-id={(group as { id?: string }).id ?? `legacy-${gIdx}`}
1362
+ data-jp-item-field="siloGroups"
1363
+ >
1364
+ <div className="flex flex-wrap justify-center gap-1.5">
1365
+ {group.blocks.map((block, bIdx) => (
1366
+ <span
1367
+ key={(block as { id?: string }).id ?? bIdx}
1368
+ className={cn(
1369
+ 'inline-block px-4 py-2 rounded-lg text-[0.8rem] font-semibold border',
1370
+ variantStyles[block.variant]
1371
+ )}
1372
+ data-jp-item-id={(block as { id?: string }).id ?? `legacy-${bIdx}`}
1373
+ data-jp-item-field="blocks"
1374
+ >
1375
+ {block.label}
1376
+ </span>
1377
+ ))}
1378
+ </div>
1379
+ <span className="text-[0.7rem] text-[var(--local-text-muted)] uppercase tracking-widest mt-2 block opacity-60">
1380
+ {group.label}
1381
+ </span>
1382
+ </div>
1383
+ ))}
1384
+ </div>
1385
+ </div>
1386
+ <div>
1387
+ <h3 className="text-2xl font-bold text-[var(--local-text)] mb-4" data-jp-field="title">
1388
+ {data.title}
1389
+ </h3>
1390
+ {data.paragraphs.map((p, idx) => (
1391
+ <p
1392
+ key={idx}
1393
+ className="text-[var(--local-text-muted)] mb-5 text-[1.05rem] leading-relaxed"
1394
+ data-jp-item-id={(p as { id?: string }).id ?? `legacy-${idx}`}
1395
+ data-jp-item-field="paragraphs"
1396
+ >
1397
+ {p.isBold ? <strong className="text-[var(--local-text)]">{p.text}</strong> : p.text}
1398
+ </p>
1399
+ ))}
1400
+ </div>
1401
+ </div>
1402
+ </div>
1403
+ </section>
1404
+ );
1405
+ };
1406
1406
 
1407
1407
  END_OF_FILE_CONTENT
1408
1408
  echo "Creating src/components/problem-statement/index.ts..."
1409
1409
  cat << 'END_OF_FILE_CONTENT' > "src/components/problem-statement/index.ts"
1410
- export * from './View';
1411
- export * from './schema';
1412
- export * from './types';
1410
+ export * from './View';
1411
+ export * from './schema';
1412
+ export * from './types';
1413
1413
 
1414
1414
  END_OF_FILE_CONTENT
1415
1415
  echo "Creating src/components/problem-statement/schema.ts..."
1416
1416
  cat << 'END_OF_FILE_CONTENT' > "src/components/problem-statement/schema.ts"
1417
- import { z } from 'zod';
1418
- import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
1419
-
1420
- export const SiloBlockVariantSchema = z.enum(['red', 'amber', 'green', 'blue']);
1421
- const SiloBlockSchema = BaseArrayItem.extend({
1422
- label: z.string().describe('ui:text'),
1423
- variant: SiloBlockVariantSchema.describe('ui:select'),
1424
- });
1425
-
1426
- const SiloGroupSchema = BaseArrayItem.extend({
1427
- blocks: z.array(SiloBlockSchema).describe('ui:list'),
1428
- label: z.string().describe('ui:text'),
1429
- });
1430
-
1431
- const ProblemParagraphSchema = BaseArrayItem.extend({
1432
- text: z.string().describe('ui:textarea'),
1433
- isBold: z.boolean().default(false).describe('ui:checkbox'),
1434
- });
1435
-
1436
- export const ProblemStatementSchema = BaseSectionData.extend({
1437
- siloGroups: z.array(SiloGroupSchema).describe('ui:list'),
1438
- title: z.string().describe('ui:text'),
1439
- paragraphs: z.array(ProblemParagraphSchema).describe('ui:list'),
1440
- highlight: z.string().optional().describe('ui:text'),
1441
- });
1417
+ import { z } from 'zod';
1418
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
1419
+
1420
+ export const SiloBlockVariantSchema = z.enum(['red', 'amber', 'green', 'blue']);
1421
+ const SiloBlockSchema = BaseArrayItem.extend({
1422
+ label: z.string().describe('ui:text'),
1423
+ variant: SiloBlockVariantSchema.describe('ui:select'),
1424
+ });
1425
+
1426
+ const SiloGroupSchema = BaseArrayItem.extend({
1427
+ blocks: z.array(SiloBlockSchema).describe('ui:list'),
1428
+ label: z.string().describe('ui:text'),
1429
+ });
1430
+
1431
+ const ProblemParagraphSchema = BaseArrayItem.extend({
1432
+ text: z.string().describe('ui:textarea'),
1433
+ isBold: z.boolean().default(false).describe('ui:checkbox'),
1434
+ });
1435
+
1436
+ export const ProblemStatementSchema = BaseSectionData.extend({
1437
+ siloGroups: z.array(SiloGroupSchema).describe('ui:list'),
1438
+ title: z.string().describe('ui:text'),
1439
+ paragraphs: z.array(ProblemParagraphSchema).describe('ui:list'),
1440
+ highlight: z.string().optional().describe('ui:text'),
1441
+ });
1442
1442
 
1443
1443
  END_OF_FILE_CONTENT
1444
1444
  echo "Creating src/components/problem-statement/types.ts..."
1445
1445
  cat << 'END_OF_FILE_CONTENT' > "src/components/problem-statement/types.ts"
1446
- import { z } from 'zod';
1447
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1448
- import { ProblemStatementSchema, SiloBlockVariantSchema } from './schema';
1449
-
1450
- export type ProblemStatementData = z.infer<typeof ProblemStatementSchema>;
1451
- export type ProblemStatementSettings = z.infer<typeof BaseSectionSettingsSchema>;
1452
- export type SiloBlockVariant = z.infer<typeof SiloBlockVariantSchema>;
1446
+ import { z } from 'zod';
1447
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1448
+ import { ProblemStatementSchema, SiloBlockVariantSchema } from './schema';
1449
+
1450
+ export type ProblemStatementData = z.infer<typeof ProblemStatementSchema>;
1451
+ export type ProblemStatementSettings = z.infer<typeof BaseSectionSettingsSchema>;
1452
+ export type SiloBlockVariant = z.infer<typeof SiloBlockVariantSchema>;
1453
1453
 
1454
1454
  END_OF_FILE_CONTENT
1455
1455
  mkdir -p "src/components/product-triad"
1456
1456
  echo "Creating src/components/product-triad/View.tsx..."
1457
1457
  cat << 'END_OF_FILE_CONTENT' > "src/components/product-triad/View.tsx"
1458
- import React from 'react';
1459
- import { cn } from '@/lib/utils';
1460
- import type { ProductTriadData, ProductTriadSettings } from './types';
1461
-
1462
- export const ProductTriad: React.FC<{ data: ProductTriadData; settings?: ProductTriadSettings }> = ({ data }) => {
1463
- return (
1464
- <section
1465
- style={{
1466
- '--local-bg': 'var(--background)',
1467
- '--local-text': 'var(--foreground)',
1468
- '--local-text-muted': 'var(--muted-foreground)',
1469
- '--local-primary': 'var(--primary)',
1470
- '--local-accent': 'var(--color-accent, #60a5fa)',
1471
- '--local-border': 'var(--border)',
1472
- } as React.CSSProperties}
1473
- className="relative z-0 py-28 bg-[var(--local-bg)]"
1474
- >
1475
- <div className="max-w-[1200px] mx-auto px-8">
1476
- <div className="text-center">
1477
- {data.label && (
1478
- <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
1479
- <span className="w-5 h-px bg-[var(--local-primary)]" />
1480
- {data.label}
1481
- </div>
1482
- )}
1483
- <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
1484
- {data.title}
1485
- </h2>
1486
- {data.description && (
1487
- <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] mx-auto leading-relaxed" data-jp-field="description">
1488
- {data.description}
1489
- </p>
1490
- )}
1491
- </div>
1492
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-14">
1493
- {data.products.map((product, idx) => (
1494
- <div
1495
- key={product.id ?? idx}
1496
- className={cn(
1497
- 'relative border rounded-lg p-10 transition-all duration-300 hover:-translate-y-1',
1498
- product.featured
1499
- ? 'border-[rgba(59,130,246,0.3)] bg-gradient-to-b from-[rgba(59,130,246,0.06)] to-[rgba(59,130,246,0.01)] hover:border-[rgba(59,130,246,0.4)]'
1500
- : 'border-[rgba(255,255,255,0.06)] bg-[rgba(255,255,255,0.015)] hover:border-[rgba(59,130,246,0.2)]'
1501
- )}
1502
- data-jp-item-id={product.id ?? `legacy-${idx}`}
1503
- data-jp-item-field="products"
1504
- >
1505
- {product.featured && (
1506
- <div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-[var(--local-primary)] text-white text-[0.7rem] font-bold px-4 py-1 rounded-full uppercase tracking-wide">
1507
- Most Popular
1508
- </div>
1509
- )}
1510
- <div className="text-[0.75rem] font-bold uppercase tracking-[0.1em] text-[var(--local-accent)] mb-2">
1511
- {product.tier}
1512
- </div>
1513
- <div className="text-2xl font-extrabold text-[var(--local-text)] mb-2">
1514
- {product.name}
1515
- </div>
1516
- <div className="font-display text-[2.2rem] font-extrabold text-[var(--local-text)] mb-1">
1517
- {product.price}
1518
- {product.priceSuffix && (
1519
- <span className="text-[0.9rem] font-normal text-[var(--local-text-muted)]">
1520
- {product.priceSuffix}
1521
- </span>
1522
- )}
1523
- </div>
1524
- <div className="text-[0.85rem] text-[var(--local-text-muted)] mb-6 pb-6 border-b border-[rgba(255,255,255,0.06)]">
1525
- {product.delivery}
1526
- </div>
1527
- <ul className="mb-8 space-y-0">
1528
- {product.features.map((feature, fIdx) => (
1529
- <li
1530
- key={fIdx}
1531
- className="text-[0.9rem] text-[#cbd5e1] py-1.5 pl-6 relative before:content-['✓'] before:absolute before:left-0 before:text-[var(--local-accent)] before:font-bold before:text-[0.8rem]"
1532
- >
1533
- {feature.text}
1534
- </li>
1535
- ))}
1536
- </ul>
1537
- {product.ctaLabel && product.ctaHref && (
1538
- <a
1539
- href={product.ctaHref}
1540
- className={cn(
1541
- 'block text-center py-3 rounded-[5px] no-underline font-semibold text-[0.95rem] transition-all duration-200',
1542
- product.ctaVariant === 'primary'
1543
- ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-px'
1544
- : 'bg-[rgba(255,255,255,0.05)] text-[#e2e8f0] border border-[rgba(255,255,255,0.1)] hover:bg-[rgba(255,255,255,0.08)] hover:border-[rgba(255,255,255,0.2)]'
1545
- )}
1546
- >
1547
- {product.ctaLabel}
1548
- </a>
1549
- )}
1550
- </div>
1551
- ))}
1552
- </div>
1553
- </div>
1554
- </section>
1555
- );
1556
- };
1458
+ import React from 'react';
1459
+ import { cn } from '@/lib/utils';
1460
+ import type { ProductTriadData, ProductTriadSettings } from './types';
1461
+
1462
+ export const ProductTriad: React.FC<{ data: ProductTriadData; settings?: ProductTriadSettings }> = ({ data }) => {
1463
+ return (
1464
+ <section
1465
+ style={{
1466
+ '--local-bg': 'var(--background)',
1467
+ '--local-text': 'var(--foreground)',
1468
+ '--local-text-muted': 'var(--muted-foreground)',
1469
+ '--local-primary': 'var(--primary)',
1470
+ '--local-accent': 'var(--color-accent, #60a5fa)',
1471
+ '--local-border': 'var(--border)',
1472
+ } as React.CSSProperties}
1473
+ className="relative z-0 py-28 bg-[var(--local-bg)]"
1474
+ >
1475
+ <div className="max-w-[1200px] mx-auto px-8">
1476
+ <div className="text-center">
1477
+ {data.label && (
1478
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-[var(--local-accent)] mb-4" data-jp-field="label">
1479
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
1480
+ {data.label}
1481
+ </div>
1482
+ )}
1483
+ <h2 className="font-display text-[clamp(2rem,4vw,3.2rem)] font-extrabold text-[var(--local-text)] leading-[1.15] tracking-tight mb-4" data-jp-field="title">
1484
+ {data.title}
1485
+ </h2>
1486
+ {data.description && (
1487
+ <p className="text-lg text-[var(--local-text-muted)] max-w-[600px] mx-auto leading-relaxed" data-jp-field="description">
1488
+ {data.description}
1489
+ </p>
1490
+ )}
1491
+ </div>
1492
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-14">
1493
+ {data.products.map((product, idx) => (
1494
+ <div
1495
+ key={product.id ?? idx}
1496
+ className={cn(
1497
+ 'relative border rounded-lg p-10 transition-all duration-300 hover:-translate-y-1',
1498
+ product.featured
1499
+ ? 'border-[rgba(59,130,246,0.3)] bg-gradient-to-b from-[rgba(59,130,246,0.06)] to-[rgba(59,130,246,0.01)] hover:border-[rgba(59,130,246,0.4)]'
1500
+ : 'border-[rgba(255,255,255,0.06)] bg-[rgba(255,255,255,0.015)] hover:border-[rgba(59,130,246,0.2)]'
1501
+ )}
1502
+ data-jp-item-id={product.id ?? `legacy-${idx}`}
1503
+ data-jp-item-field="products"
1504
+ >
1505
+ {product.featured && (
1506
+ <div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-[var(--local-primary)] text-white text-[0.7rem] font-bold px-4 py-1 rounded-full uppercase tracking-wide">
1507
+ Most Popular
1508
+ </div>
1509
+ )}
1510
+ <div className="text-[0.75rem] font-bold uppercase tracking-[0.1em] text-[var(--local-accent)] mb-2">
1511
+ {product.tier}
1512
+ </div>
1513
+ <div className="text-2xl font-extrabold text-[var(--local-text)] mb-2">
1514
+ {product.name}
1515
+ </div>
1516
+ <div className="font-display text-[2.2rem] font-extrabold text-[var(--local-text)] mb-1">
1517
+ {product.price}
1518
+ {product.priceSuffix && (
1519
+ <span className="text-[0.9rem] font-normal text-[var(--local-text-muted)]">
1520
+ {product.priceSuffix}
1521
+ </span>
1522
+ )}
1523
+ </div>
1524
+ <div className="text-[0.85rem] text-[var(--local-text-muted)] mb-6 pb-6 border-b border-[rgba(255,255,255,0.06)]">
1525
+ {product.delivery}
1526
+ </div>
1527
+ <ul className="mb-8 space-y-0">
1528
+ {product.features.map((feature, fIdx) => (
1529
+ <li
1530
+ key={fIdx}
1531
+ className="text-[0.9rem] text-[#cbd5e1] py-1.5 pl-6 relative before:content-['✓'] before:absolute before:left-0 before:text-[var(--local-accent)] before:font-bold before:text-[0.8rem]"
1532
+ >
1533
+ {feature.text}
1534
+ </li>
1535
+ ))}
1536
+ </ul>
1537
+ {product.ctaLabel && product.ctaHref && (
1538
+ <a
1539
+ href={product.ctaHref}
1540
+ className={cn(
1541
+ 'block text-center py-3 rounded-[5px] no-underline font-semibold text-[0.95rem] transition-all duration-200',
1542
+ product.ctaVariant === 'primary'
1543
+ ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-px'
1544
+ : 'bg-[rgba(255,255,255,0.05)] text-[#e2e8f0] border border-[rgba(255,255,255,0.1)] hover:bg-[rgba(255,255,255,0.08)] hover:border-[rgba(255,255,255,0.2)]'
1545
+ )}
1546
+ >
1547
+ {product.ctaLabel}
1548
+ </a>
1549
+ )}
1550
+ </div>
1551
+ ))}
1552
+ </div>
1553
+ </div>
1554
+ </section>
1555
+ );
1556
+ };
1557
1557
 
1558
1558
  END_OF_FILE_CONTENT
1559
1559
  echo "Creating src/components/product-triad/index.ts..."
1560
1560
  cat << 'END_OF_FILE_CONTENT' > "src/components/product-triad/index.ts"
1561
- export * from './View';
1562
- export * from './schema';
1563
- export * from './types';
1561
+ export * from './View';
1562
+ export * from './schema';
1563
+ export * from './types';
1564
1564
 
1565
1565
  END_OF_FILE_CONTENT
1566
1566
  echo "Creating src/components/product-triad/schema.ts..."
1567
1567
  cat << 'END_OF_FILE_CONTENT' > "src/components/product-triad/schema.ts"
1568
- import { z } from 'zod';
1569
- import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
1570
-
1571
- const ProductFeatureSchema = z.object({
1572
- text: z.string().describe('ui:text'),
1573
- });
1574
-
1575
- const ProductCardSchema = BaseArrayItem.extend({
1576
- tier: z.string().describe('ui:text'),
1577
- name: z.string().describe('ui:text'),
1578
- price: z.string().describe('ui:text'),
1579
- priceSuffix: z.string().optional().describe('ui:text'),
1580
- delivery: z.string().describe('ui:text'),
1581
- features: z.array(ProductFeatureSchema).describe('ui:list'),
1582
- featured: z.boolean().default(false).describe('ui:checkbox'),
1583
- ctaLabel: z.string().optional().describe('ui:text'),
1584
- ctaHref: z.string().optional().describe('ui:text'),
1585
- ctaVariant: z.enum(['primary', 'secondary']).default('secondary').describe('ui:select'),
1586
- });
1587
-
1588
- export const ProductTriadSchema = BaseSectionData.extend({
1589
- label: z.string().optional().describe('ui:text'),
1590
- title: z.string().describe('ui:text'),
1591
- description: z.string().optional().describe('ui:textarea'),
1592
- products: z.array(ProductCardSchema).describe('ui:list'),
1593
- });
1568
+ import { z } from 'zod';
1569
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
1570
+
1571
+ const ProductFeatureSchema = z.object({
1572
+ text: z.string().describe('ui:text'),
1573
+ });
1574
+
1575
+ const ProductCardSchema = BaseArrayItem.extend({
1576
+ tier: z.string().describe('ui:text'),
1577
+ name: z.string().describe('ui:text'),
1578
+ price: z.string().describe('ui:text'),
1579
+ priceSuffix: z.string().optional().describe('ui:text'),
1580
+ delivery: z.string().describe('ui:text'),
1581
+ features: z.array(ProductFeatureSchema).describe('ui:list'),
1582
+ featured: z.boolean().default(false).describe('ui:checkbox'),
1583
+ ctaLabel: z.string().optional().describe('ui:text'),
1584
+ ctaHref: z.string().optional().describe('ui:text'),
1585
+ ctaVariant: z.enum(['primary', 'secondary']).default('secondary').describe('ui:select'),
1586
+ });
1587
+
1588
+ export const ProductTriadSchema = BaseSectionData.extend({
1589
+ label: z.string().optional().describe('ui:text'),
1590
+ title: z.string().describe('ui:text'),
1591
+ description: z.string().optional().describe('ui:textarea'),
1592
+ products: z.array(ProductCardSchema).describe('ui:list'),
1593
+ });
1594
1594
 
1595
1595
  END_OF_FILE_CONTENT
1596
1596
  echo "Creating src/components/product-triad/types.ts..."
1597
1597
  cat << 'END_OF_FILE_CONTENT' > "src/components/product-triad/types.ts"
1598
- import { z } from 'zod';
1599
- import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1600
- import { ProductTriadSchema } from './schema';
1601
-
1602
- export type ProductTriadData = z.infer<typeof ProductTriadSchema>;
1603
- export type ProductTriadSettings = z.infer<typeof BaseSectionSettingsSchema>;
1598
+ import { z } from 'zod';
1599
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
1600
+ import { ProductTriadSchema } from './schema';
1601
+
1602
+ export type ProductTriadData = z.infer<typeof ProductTriadSchema>;
1603
+ export type ProductTriadSettings = z.infer<typeof BaseSectionSettingsSchema>;
1604
1604
 
1605
1605
  END_OF_FILE_CONTENT
1606
1606
  mkdir -p "src/components/ui"
@@ -2178,85 +2178,85 @@ END_OF_FILE_CONTENT
2178
2178
  # SKIP: src/data/config/menu.json:Zone.Identifier è un file binario e non può essere convertito in testo.
2179
2179
  echo "Creating src/data/config/site.json..."
2180
2180
  cat << 'END_OF_FILE_CONTENT' > "src/data/config/site.json"
2181
- {
2182
- "identity": {
2183
- "title": "JsonPages",
2184
- "logoUrl": "/logo.svg"
2185
- },
2186
- "pages": [
2187
- {
2188
- "slug": "home",
2189
- "label": "Home"
2190
- },
2191
- {
2192
- "slug": "architecture",
2193
- "label": "Architecture"
2194
- },
2195
- {
2196
- "slug": "usage",
2197
- "label": "Usage"
2198
- }
2199
- ],
2200
- "header": {
2201
- "id": "global-header",
2202
- "type": "header",
2203
- "data": {
2204
- "logoText": "Json",
2205
- "logoHighlight": "Pages",
2206
- "logoIconText": "{ }",
2207
- "links": [
2208
- {
2209
- "label": "Architecture",
2210
- "href": "#architecture"
2211
- },
2212
- {
2213
- "label": "Products",
2214
- "href": "#products"
2215
- },
2216
- {
2217
- "label": "PA Ready",
2218
- "href": "#pa-ready"
2219
- },
2220
- {
2221
- "label": "Philosophy",
2222
- "href": "#philosophy"
2223
- }
2224
- ]
2225
- },
2226
- "settings": {
2227
- "sticky": true
2228
- }
2229
- },
2230
- "footer": {
2231
- "id": "global-footer",
2232
- "type": "footer",
2233
- "data": {
2234
- "brandText": "Json",
2235
- "brandHighlight": "Pages",
2236
- "copyright": "© 2026 JsonPages Ecosystem. Architected by Guido Filippo Serio.",
2237
- "links": [
2238
- {
2239
- "label": "Documentation",
2240
- "href": "#"
2241
- },
2242
- {
2243
- "label": "API Reference",
2244
- "href": "#"
2245
- },
2246
- {
2247
- "label": "Changelog",
2248
- "href": "#"
2249
- },
2250
- {
2251
- "label": "Privacy",
2252
- "href": "#"
2253
- }
2254
- ]
2255
- },
2256
- "settings": {
2257
- "showLogo": true
2258
- }
2259
- }
2181
+ {
2182
+ "identity": {
2183
+ "title": "JsonPages",
2184
+ "logoUrl": "/logo.svg"
2185
+ },
2186
+ "pages": [
2187
+ {
2188
+ "slug": "home",
2189
+ "label": "Home"
2190
+ },
2191
+ {
2192
+ "slug": "architecture",
2193
+ "label": "Architecture"
2194
+ },
2195
+ {
2196
+ "slug": "usage",
2197
+ "label": "Usage"
2198
+ }
2199
+ ],
2200
+ "header": {
2201
+ "id": "global-header",
2202
+ "type": "header",
2203
+ "data": {
2204
+ "logoText": "Json",
2205
+ "logoHighlight": "Pages",
2206
+ "logoIconText": "{ }",
2207
+ "links": [
2208
+ {
2209
+ "label": "Architecture",
2210
+ "href": "#architecture"
2211
+ },
2212
+ {
2213
+ "label": "Products",
2214
+ "href": "#products"
2215
+ },
2216
+ {
2217
+ "label": "PA Ready",
2218
+ "href": "#pa-ready"
2219
+ },
2220
+ {
2221
+ "label": "Philosophy",
2222
+ "href": "#philosophy"
2223
+ }
2224
+ ]
2225
+ },
2226
+ "settings": {
2227
+ "sticky": true
2228
+ }
2229
+ },
2230
+ "footer": {
2231
+ "id": "global-footer",
2232
+ "type": "footer",
2233
+ "data": {
2234
+ "brandText": "Json",
2235
+ "brandHighlight": "Pages",
2236
+ "copyright": "© 2026 JsonPages Ecosystem. Architected by Guido Filippo Serio.",
2237
+ "links": [
2238
+ {
2239
+ "label": "Documentation",
2240
+ "href": "#"
2241
+ },
2242
+ {
2243
+ "label": "API Reference",
2244
+ "href": "#"
2245
+ },
2246
+ {
2247
+ "label": "Changelog",
2248
+ "href": "#"
2249
+ },
2250
+ {
2251
+ "label": "Privacy",
2252
+ "href": "#"
2253
+ }
2254
+ ]
2255
+ },
2256
+ "settings": {
2257
+ "showLogo": true
2258
+ }
2259
+ }
2260
2260
  }
2261
2261
  END_OF_FILE_CONTENT
2262
2262
  # SKIP: src/data/config/site.json:Zone.Identifier è un file binario e non può essere convertito in testo.
@@ -2921,39 +2921,39 @@ END_OF_FILE_CONTENT
2921
2921
  mkdir -p "src/lib"
2922
2922
  echo "Creating src/lib/ComponentRegistry.tsx..."
2923
2923
  cat << 'END_OF_FILE_CONTENT' > "src/lib/ComponentRegistry.tsx"
2924
- import React from 'react';
2925
- import { Header } from '@/components/header';
2926
- import { Footer } from '@/components/footer';
2927
- import { Hero } from '@/components/hero';
2928
- import { FeatureGrid } from '@/components/feature-grid';
2929
- import { CodeBlock } from '@/components/code-block';
2930
- import { ProblemStatement } from '@/components/problem-statement';
2931
- import { PillarsGrid } from '@/components/pillars-grid';
2932
- import { ArchLayers } from '@/components/arch-layers';
2933
- import { ProductTriad } from '@/components/product-triad';
2934
- import { PaSection } from '@/components/pa-section';
2935
- import { Philosophy } from '@/components/philosophy';
2936
- import { CtaBanner } from '@/components/cta-banner';
2937
-
2938
- import type { SectionType } from '@jsonpages/core';
2939
- import type { SectionComponentPropsMap } from '@/types';
2940
-
2941
- export const ComponentRegistry: {
2942
- [K in SectionType]: React.FC<SectionComponentPropsMap[K]>;
2943
- } = {
2944
- 'header': Header,
2945
- 'footer': Footer,
2946
- 'hero': Hero,
2947
- 'feature-grid': FeatureGrid,
2948
- 'code-block': CodeBlock,
2949
- 'problem-statement': ProblemStatement,
2950
- 'pillars-grid': PillarsGrid,
2951
- 'arch-layers': ArchLayers,
2952
- 'product-triad': ProductTriad,
2953
- 'pa-section': PaSection,
2954
- 'philosophy': Philosophy,
2955
- 'cta-banner': CtaBanner,
2956
- };
2924
+ import React from 'react';
2925
+ import { Header } from '@/components/header';
2926
+ import { Footer } from '@/components/footer';
2927
+ import { Hero } from '@/components/hero';
2928
+ import { FeatureGrid } from '@/components/feature-grid';
2929
+ import { CodeBlock } from '@/components/code-block';
2930
+ import { ProblemStatement } from '@/components/problem-statement';
2931
+ import { PillarsGrid } from '@/components/pillars-grid';
2932
+ import { ArchLayers } from '@/components/arch-layers';
2933
+ import { ProductTriad } from '@/components/product-triad';
2934
+ import { PaSection } from '@/components/pa-section';
2935
+ import { Philosophy } from '@/components/philosophy';
2936
+ import { CtaBanner } from '@/components/cta-banner';
2937
+
2938
+ import type { SectionType } from '@jsonpages/core';
2939
+ import type { SectionComponentPropsMap } from '@/types';
2940
+
2941
+ export const ComponentRegistry: {
2942
+ [K in SectionType]: React.FC<SectionComponentPropsMap[K]>;
2943
+ } = {
2944
+ 'header': Header,
2945
+ 'footer': Footer,
2946
+ 'hero': Hero,
2947
+ 'feature-grid': FeatureGrid,
2948
+ 'code-block': CodeBlock,
2949
+ 'problem-statement': ProblemStatement,
2950
+ 'pillars-grid': PillarsGrid,
2951
+ 'arch-layers': ArchLayers,
2952
+ 'product-triad': ProductTriad,
2953
+ 'pa-section': PaSection,
2954
+ 'philosophy': Philosophy,
2955
+ 'cta-banner': CtaBanner,
2956
+ };
2957
2957
 
2958
2958
  END_OF_FILE_CONTENT
2959
2959
  echo "Creating src/lib/IconResolver.tsx..."
@@ -3016,146 +3016,146 @@ export const Icon: React.FC<IconProps> = ({ name, size = 20, className }) => {
3016
3016
  END_OF_FILE_CONTENT
3017
3017
  echo "Creating src/lib/addSectionConfig.ts..."
3018
3018
  cat << 'END_OF_FILE_CONTENT' > "src/lib/addSectionConfig.ts"
3019
- /**
3020
- * Add-section config for the ICE admin (tenant-alpha).
3021
- * Provides default data and labels for each addable section type.
3022
- * Core remains agnostic; this file is tenant-specific.
3023
- */
3024
- import type { AddSectionConfig } from '@jsonpages/core';
3025
-
3026
- const addableSectionTypes = [
3027
- 'hero',
3028
- 'feature-grid',
3029
- 'code-block',
3030
- 'problem-statement',
3031
- 'pillars-grid',
3032
- 'arch-layers',
3033
- 'product-triad',
3034
- 'pa-section',
3035
- 'philosophy',
3036
- 'cta-banner',
3037
- ] as const;
3038
-
3039
- const sectionTypeLabels: Record<string, string> = {
3040
- 'hero': 'Hero',
3041
- 'feature-grid': 'Feature Grid',
3042
- 'code-block': 'Code Block',
3043
- 'problem-statement': 'Problem Statement',
3044
- 'pillars-grid': 'Pillars Grid',
3045
- 'arch-layers': 'Architecture Layers',
3046
- 'product-triad': 'Product Triad',
3047
- 'pa-section': 'PA Section',
3048
- 'philosophy': 'Philosophy',
3049
- 'cta-banner': 'CTA Banner',
3050
- };
3051
-
3052
- function getDefaultSectionData(sectionType: string): Record<string, unknown> {
3053
- switch (sectionType) {
3054
- case 'hero':
3055
- return { title: 'New Hero', description: '' };
3056
- case 'feature-grid':
3057
- return { sectionTitle: 'Features', cards: [] };
3058
- case 'code-block':
3059
- return { lines: [] };
3060
- case 'problem-statement':
3061
- return { title: 'Problem Statement', siloGroups: [], paragraphs: [] };
3062
- case 'pillars-grid':
3063
- return { title: 'Pillars', pillars: [] };
3064
- case 'arch-layers':
3065
- return { title: 'Architecture', layers: [] };
3066
- case 'product-triad':
3067
- return { title: 'Products', products: [] };
3068
- case 'pa-section':
3069
- return { title: 'Section', subtitle: 'Subtitle', paragraphs: [{ text: '' }] };
3070
- case 'philosophy':
3071
- return { title: 'Philosophy', quote: 'Your quote here.' };
3072
- case 'cta-banner':
3073
- return { title: 'Call to Action', description: '' };
3074
- default:
3075
- return {};
3076
- }
3077
- }
3078
-
3079
- export const addSectionConfig: AddSectionConfig = {
3080
- addableSectionTypes: [...addableSectionTypes],
3081
- sectionTypeLabels,
3082
- getDefaultSectionData,
3083
- };
3019
+ /**
3020
+ * Add-section config for the ICE admin (tenant-alpha).
3021
+ * Provides default data and labels for each addable section type.
3022
+ * Core remains agnostic; this file is tenant-specific.
3023
+ */
3024
+ import type { AddSectionConfig } from '@jsonpages/core';
3025
+
3026
+ const addableSectionTypes = [
3027
+ 'hero',
3028
+ 'feature-grid',
3029
+ 'code-block',
3030
+ 'problem-statement',
3031
+ 'pillars-grid',
3032
+ 'arch-layers',
3033
+ 'product-triad',
3034
+ 'pa-section',
3035
+ 'philosophy',
3036
+ 'cta-banner',
3037
+ ] as const;
3038
+
3039
+ const sectionTypeLabels: Record<string, string> = {
3040
+ 'hero': 'Hero',
3041
+ 'feature-grid': 'Feature Grid',
3042
+ 'code-block': 'Code Block',
3043
+ 'problem-statement': 'Problem Statement',
3044
+ 'pillars-grid': 'Pillars Grid',
3045
+ 'arch-layers': 'Architecture Layers',
3046
+ 'product-triad': 'Product Triad',
3047
+ 'pa-section': 'PA Section',
3048
+ 'philosophy': 'Philosophy',
3049
+ 'cta-banner': 'CTA Banner',
3050
+ };
3051
+
3052
+ function getDefaultSectionData(sectionType: string): Record<string, unknown> {
3053
+ switch (sectionType) {
3054
+ case 'hero':
3055
+ return { title: 'New Hero', description: '' };
3056
+ case 'feature-grid':
3057
+ return { sectionTitle: 'Features', cards: [] };
3058
+ case 'code-block':
3059
+ return { lines: [] };
3060
+ case 'problem-statement':
3061
+ return { title: 'Problem Statement', siloGroups: [], paragraphs: [] };
3062
+ case 'pillars-grid':
3063
+ return { title: 'Pillars', pillars: [] };
3064
+ case 'arch-layers':
3065
+ return { title: 'Architecture', layers: [] };
3066
+ case 'product-triad':
3067
+ return { title: 'Products', products: [] };
3068
+ case 'pa-section':
3069
+ return { title: 'Section', subtitle: 'Subtitle', paragraphs: [{ text: '' }] };
3070
+ case 'philosophy':
3071
+ return { title: 'Philosophy', quote: 'Your quote here.' };
3072
+ case 'cta-banner':
3073
+ return { title: 'Call to Action', description: '' };
3074
+ default:
3075
+ return {};
3076
+ }
3077
+ }
3078
+
3079
+ export const addSectionConfig: AddSectionConfig = {
3080
+ addableSectionTypes: [...addableSectionTypes],
3081
+ sectionTypeLabels,
3082
+ getDefaultSectionData,
3083
+ };
3084
3084
 
3085
3085
  END_OF_FILE_CONTENT
3086
3086
  echo "Creating src/lib/base-schemas.ts..."
3087
3087
  cat << 'END_OF_FILE_CONTENT' > "src/lib/base-schemas.ts"
3088
- import { z } from 'zod';
3089
-
3090
- /**
3091
- * Base schemas shared by section capsules (CIP governance).
3092
- * Capsules extend these for consistent anchorId, array items, and settings.
3093
- */
3094
- export const BaseSectionData = z.object({
3095
- anchorId: z.string().optional().describe('ui:text'),
3096
- });
3097
-
3098
- export const BaseArrayItem = z.object({
3099
- id: z.string().optional(),
3100
- });
3101
-
3102
- export const BaseSectionSettingsSchema = z.object({
3103
- paddingTop: z.enum(['none', 'sm', 'md', 'lg', 'xl', '2xl']).default('md').describe('ui:select'),
3104
- paddingBottom: z.enum(['none', 'sm', 'md', 'lg', 'xl', '2xl']).default('md').describe('ui:select'),
3105
- theme: z.enum(['dark', 'light', 'accent']).default('dark').describe('ui:select'),
3106
- container: z.enum(['boxed', 'fluid']).default('boxed').describe('ui:select'),
3107
- });
3108
-
3109
- export const CtaSchema = z.object({
3110
- id: z.string().optional(),
3111
- label: z.string().describe('ui:text'),
3112
- href: z.string().describe('ui:text'),
3113
- variant: z.enum(['primary', 'secondary']).default('primary').describe('ui:select'),
3114
- });
3088
+ import { z } from 'zod';
3089
+
3090
+ /**
3091
+ * Base schemas shared by section capsules (CIP governance).
3092
+ * Capsules extend these for consistent anchorId, array items, and settings.
3093
+ */
3094
+ export const BaseSectionData = z.object({
3095
+ anchorId: z.string().optional().describe('ui:text'),
3096
+ });
3097
+
3098
+ export const BaseArrayItem = z.object({
3099
+ id: z.string().optional(),
3100
+ });
3101
+
3102
+ export const BaseSectionSettingsSchema = z.object({
3103
+ paddingTop: z.enum(['none', 'sm', 'md', 'lg', 'xl', '2xl']).default('md').describe('ui:select'),
3104
+ paddingBottom: z.enum(['none', 'sm', 'md', 'lg', 'xl', '2xl']).default('md').describe('ui:select'),
3105
+ theme: z.enum(['dark', 'light', 'accent']).default('dark').describe('ui:select'),
3106
+ container: z.enum(['boxed', 'fluid']).default('boxed').describe('ui:select'),
3107
+ });
3108
+
3109
+ export const CtaSchema = z.object({
3110
+ id: z.string().optional(),
3111
+ label: z.string().describe('ui:text'),
3112
+ href: z.string().describe('ui:text'),
3113
+ variant: z.enum(['primary', 'secondary']).default('primary').describe('ui:select'),
3114
+ });
3115
3115
 
3116
3116
  END_OF_FILE_CONTENT
3117
3117
  echo "Creating src/lib/schemas.ts..."
3118
3118
  cat << 'END_OF_FILE_CONTENT' > "src/lib/schemas.ts"
3119
- /**
3120
- * SECTION_SCHEMAS registry — SSOT for FormFactory.
3121
- * Re-exports base schemas and aggregates all section schemas from TBP capsules.
3122
- */
3123
- export {
3124
- BaseSectionData,
3125
- BaseArrayItem,
3126
- BaseSectionSettingsSchema,
3127
- CtaSchema,
3128
- } from './base-schemas';
3129
-
3130
- import { HeaderSchema } from '@/components/header';
3131
- import { FooterSchema } from '@/components/footer';
3132
- import { HeroSchema } from '@/components/hero';
3133
- import { FeatureGridSchema } from '@/components/feature-grid';
3134
- import { CodeBlockSchema } from '@/components/code-block';
3135
- import { ProblemStatementSchema } from '@/components/problem-statement';
3136
- import { PillarsGridSchema } from '@/components/pillars-grid';
3137
- import { ArchLayersSchema } from '@/components/arch-layers';
3138
- import { ProductTriadSchema } from '@/components/product-triad';
3139
- import { PaSectionSchema } from '@/components/pa-section';
3140
- import { PhilosophySchema } from '@/components/philosophy';
3141
- import { CtaBannerSchema } from '@/components/cta-banner';
3142
-
3143
- export const SECTION_SCHEMAS = {
3144
- 'header': HeaderSchema,
3145
- 'footer': FooterSchema,
3146
- 'hero': HeroSchema,
3147
- 'feature-grid': FeatureGridSchema,
3148
- 'code-block': CodeBlockSchema,
3149
- 'problem-statement': ProblemStatementSchema,
3150
- 'pillars-grid': PillarsGridSchema,
3151
- 'arch-layers': ArchLayersSchema,
3152
- 'product-triad': ProductTriadSchema,
3153
- 'pa-section': PaSectionSchema,
3154
- 'philosophy': PhilosophySchema,
3155
- 'cta-banner': CtaBannerSchema,
3156
- } as const;
3157
-
3158
- export type SectionType = keyof typeof SECTION_SCHEMAS;
3119
+ /**
3120
+ * SECTION_SCHEMAS registry — SSOT for FormFactory.
3121
+ * Re-exports base schemas and aggregates all section schemas from TBP capsules.
3122
+ */
3123
+ export {
3124
+ BaseSectionData,
3125
+ BaseArrayItem,
3126
+ BaseSectionSettingsSchema,
3127
+ CtaSchema,
3128
+ } from './base-schemas';
3129
+
3130
+ import { HeaderSchema } from '@/components/header';
3131
+ import { FooterSchema } from '@/components/footer';
3132
+ import { HeroSchema } from '@/components/hero';
3133
+ import { FeatureGridSchema } from '@/components/feature-grid';
3134
+ import { CodeBlockSchema } from '@/components/code-block';
3135
+ import { ProblemStatementSchema } from '@/components/problem-statement';
3136
+ import { PillarsGridSchema } from '@/components/pillars-grid';
3137
+ import { ArchLayersSchema } from '@/components/arch-layers';
3138
+ import { ProductTriadSchema } from '@/components/product-triad';
3139
+ import { PaSectionSchema } from '@/components/pa-section';
3140
+ import { PhilosophySchema } from '@/components/philosophy';
3141
+ import { CtaBannerSchema } from '@/components/cta-banner';
3142
+
3143
+ export const SECTION_SCHEMAS = {
3144
+ 'header': HeaderSchema,
3145
+ 'footer': FooterSchema,
3146
+ 'hero': HeroSchema,
3147
+ 'feature-grid': FeatureGridSchema,
3148
+ 'code-block': CodeBlockSchema,
3149
+ 'problem-statement': ProblemStatementSchema,
3150
+ 'pillars-grid': PillarsGridSchema,
3151
+ 'arch-layers': ArchLayersSchema,
3152
+ 'product-triad': ProductTriadSchema,
3153
+ 'pa-section': PaSectionSchema,
3154
+ 'philosophy': PhilosophySchema,
3155
+ 'cta-banner': CtaBannerSchema,
3156
+ } as const;
3157
+
3158
+ export type SectionType = keyof typeof SECTION_SCHEMAS;
3159
3159
 
3160
3160
  END_OF_FILE_CONTENT
3161
3161
  echo "Creating src/lib/utils.ts..."
@@ -3189,68 +3189,68 @@ END_OF_FILE_CONTENT
3189
3189
  # SKIP: src/registry-types.ts è un file binario e non può essere convertito in testo.
3190
3190
  echo "Creating src/types.ts..."
3191
3191
  cat << 'END_OF_FILE_CONTENT' > "src/types.ts"
3192
- import type { MenuItem } from '@jsonpages/core';
3193
- import type { HeaderData, HeaderSettings } from '@/components/header';
3194
- import type { FooterData, FooterSettings } from '@/components/footer';
3195
- import type { HeroData, HeroSettings } from '@/components/hero';
3196
- import type { FeatureGridData, FeatureGridSettings } from '@/components/feature-grid';
3197
- import type { CodeBlockData, CodeBlockSettings } from '@/components/code-block';
3198
- import type { ProblemStatementData, ProblemStatementSettings } from '@/components/problem-statement';
3199
- import type { PillarsGridData, PillarsGridSettings } from '@/components/pillars-grid';
3200
- import type { ArchLayersData, ArchLayersSettings } from '@/components/arch-layers';
3201
- import type { ProductTriadData, ProductTriadSettings } from '@/components/product-triad';
3202
- import type { PaSectionData, PaSectionSettings } from '@/components/pa-section';
3203
- import type { PhilosophyData, PhilosophySettings } from '@/components/philosophy';
3204
- import type { CtaBannerData, CtaBannerSettings } from '@/components/cta-banner';
3205
-
3206
- export type SectionComponentPropsMap = {
3207
- 'header': { data: HeaderData; settings?: HeaderSettings; menu: MenuItem[] };
3208
- 'footer': { data: FooterData; settings?: FooterSettings };
3209
- 'hero': { data: HeroData; settings?: HeroSettings };
3210
- 'feature-grid': { data: FeatureGridData; settings?: FeatureGridSettings };
3211
- 'code-block': { data: CodeBlockData; settings?: CodeBlockSettings };
3212
- 'problem-statement': { data: ProblemStatementData; settings?: ProblemStatementSettings };
3213
- 'pillars-grid': { data: PillarsGridData; settings?: PillarsGridSettings };
3214
- 'arch-layers': { data: ArchLayersData; settings?: ArchLayersSettings };
3215
- 'product-triad': { data: ProductTriadData; settings?: ProductTriadSettings };
3216
- 'pa-section': { data: PaSectionData; settings?: PaSectionSettings };
3217
- 'philosophy': { data: PhilosophyData; settings?: PhilosophySettings };
3218
- 'cta-banner': { data: CtaBannerData; settings?: CtaBannerSettings };
3219
- };
3220
-
3221
- declare module '@jsonpages/core' {
3222
- export interface SectionDataRegistry {
3223
- 'header': HeaderData;
3224
- 'footer': FooterData;
3225
- 'hero': HeroData;
3226
- 'feature-grid': FeatureGridData;
3227
- 'code-block': CodeBlockData;
3228
- 'problem-statement': ProblemStatementData;
3229
- 'pillars-grid': PillarsGridData;
3230
- 'arch-layers': ArchLayersData;
3231
- 'product-triad': ProductTriadData;
3232
- 'pa-section': PaSectionData;
3233
- 'philosophy': PhilosophyData;
3234
- 'cta-banner': CtaBannerData;
3235
- }
3236
-
3237
- export interface SectionSettingsRegistry {
3238
- 'header': HeaderSettings;
3239
- 'footer': FooterSettings;
3240
- 'hero': HeroSettings;
3241
- 'feature-grid': FeatureGridSettings;
3242
- 'code-block': CodeBlockSettings;
3243
- 'problem-statement': ProblemStatementSettings;
3244
- 'pillars-grid': PillarsGridSettings;
3245
- 'arch-layers': ArchLayersSettings;
3246
- 'product-triad': ProductTriadSettings;
3247
- 'pa-section': PaSectionSettings;
3248
- 'philosophy': PhilosophySettings;
3249
- 'cta-banner': CtaBannerSettings;
3250
- }
3251
- }
3252
-
3253
- export * from '@jsonpages/core';
3192
+ import type { MenuItem } from '@jsonpages/core';
3193
+ import type { HeaderData, HeaderSettings } from '@/components/header';
3194
+ import type { FooterData, FooterSettings } from '@/components/footer';
3195
+ import type { HeroData, HeroSettings } from '@/components/hero';
3196
+ import type { FeatureGridData, FeatureGridSettings } from '@/components/feature-grid';
3197
+ import type { CodeBlockData, CodeBlockSettings } from '@/components/code-block';
3198
+ import type { ProblemStatementData, ProblemStatementSettings } from '@/components/problem-statement';
3199
+ import type { PillarsGridData, PillarsGridSettings } from '@/components/pillars-grid';
3200
+ import type { ArchLayersData, ArchLayersSettings } from '@/components/arch-layers';
3201
+ import type { ProductTriadData, ProductTriadSettings } from '@/components/product-triad';
3202
+ import type { PaSectionData, PaSectionSettings } from '@/components/pa-section';
3203
+ import type { PhilosophyData, PhilosophySettings } from '@/components/philosophy';
3204
+ import type { CtaBannerData, CtaBannerSettings } from '@/components/cta-banner';
3205
+
3206
+ export type SectionComponentPropsMap = {
3207
+ 'header': { data: HeaderData; settings?: HeaderSettings; menu: MenuItem[] };
3208
+ 'footer': { data: FooterData; settings?: FooterSettings };
3209
+ 'hero': { data: HeroData; settings?: HeroSettings };
3210
+ 'feature-grid': { data: FeatureGridData; settings?: FeatureGridSettings };
3211
+ 'code-block': { data: CodeBlockData; settings?: CodeBlockSettings };
3212
+ 'problem-statement': { data: ProblemStatementData; settings?: ProblemStatementSettings };
3213
+ 'pillars-grid': { data: PillarsGridData; settings?: PillarsGridSettings };
3214
+ 'arch-layers': { data: ArchLayersData; settings?: ArchLayersSettings };
3215
+ 'product-triad': { data: ProductTriadData; settings?: ProductTriadSettings };
3216
+ 'pa-section': { data: PaSectionData; settings?: PaSectionSettings };
3217
+ 'philosophy': { data: PhilosophyData; settings?: PhilosophySettings };
3218
+ 'cta-banner': { data: CtaBannerData; settings?: CtaBannerSettings };
3219
+ };
3220
+
3221
+ declare module '@jsonpages/core' {
3222
+ export interface SectionDataRegistry {
3223
+ 'header': HeaderData;
3224
+ 'footer': FooterData;
3225
+ 'hero': HeroData;
3226
+ 'feature-grid': FeatureGridData;
3227
+ 'code-block': CodeBlockData;
3228
+ 'problem-statement': ProblemStatementData;
3229
+ 'pillars-grid': PillarsGridData;
3230
+ 'arch-layers': ArchLayersData;
3231
+ 'product-triad': ProductTriadData;
3232
+ 'pa-section': PaSectionData;
3233
+ 'philosophy': PhilosophyData;
3234
+ 'cta-banner': CtaBannerData;
3235
+ }
3236
+
3237
+ export interface SectionSettingsRegistry {
3238
+ 'header': HeaderSettings;
3239
+ 'footer': FooterSettings;
3240
+ 'hero': HeroSettings;
3241
+ 'feature-grid': FeatureGridSettings;
3242
+ 'code-block': CodeBlockSettings;
3243
+ 'problem-statement': ProblemStatementSettings;
3244
+ 'pillars-grid': PillarsGridSettings;
3245
+ 'arch-layers': ArchLayersSettings;
3246
+ 'product-triad': ProductTriadSettings;
3247
+ 'pa-section': PaSectionSettings;
3248
+ 'philosophy': PhilosophySettings;
3249
+ 'cta-banner': CtaBannerSettings;
3250
+ }
3251
+ }
3252
+
3253
+ export * from '@jsonpages/core';
3254
3254
 
3255
3255
  END_OF_FILE_CONTENT
3256
3256
  echo "Creating src/vite-env.d.ts..."