@olonjs/cli 3.0.123 → 3.0.125

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.
@@ -1445,7 +1445,6 @@ When you receive a OlonJS tenant task:
1445
1445
  5. State assumptions when inferring intended branded output from examples.
1446
1446
 
1447
1447
  END_OF_FILE_CONTENT
1448
- # SKIP: .cursor/skills-cursor/olonjs/SKILL.md:Zone.Identifier is binary and cannot be embedded as text.
1449
1448
  mkdir -p ".cursor/skills-cursor/shell"
1450
1449
  echo "Creating .cursor/skills-cursor/shell/SKILL.md..."
1451
1450
  cat << 'END_OF_FILE_CONTENT' > ".cursor/skills-cursor/shell/SKILL.md"
@@ -1644,7 +1643,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
1644
1643
  "@tiptap/extension-link": "^2.11.5",
1645
1644
  "@tiptap/react": "^2.11.5",
1646
1645
  "@tiptap/starter-kit": "^2.11.5",
1647
- "@olonjs/core": "^1.0.112",
1646
+ "@olonjs/core": "^1.0.114",
1648
1647
  "class-variance-authority": "^0.7.1",
1649
1648
  "clsx": "^2.1.1",
1650
1649
  "lucide-react": "^0.474.0",
@@ -2553,6 +2552,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/App.tsx"
2553
2552
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2554
2553
  import { JsonPagesEngine } from '@olonjs/core';
2555
2554
  import type { JsonPagesConfig, LibraryImageEntry, ProjectState } from '@olonjs/core';
2555
+ import { normalizeBasePath, withBasePath } from '@olonjs/core';
2556
2556
  import { ComponentRegistry } from '@/lib/ComponentRegistry';
2557
2557
  import { SECTION_SCHEMAS } from '@/lib/schemas';
2558
2558
  import { addSectionConfig } from '@/lib/addSectionConfig';
@@ -2577,14 +2577,16 @@ const CLOUD_API_URL =
2577
2577
  const CLOUD_API_KEY =
2578
2578
  import.meta.env.VITE_OLONJS_API_KEY ?? import.meta.env.VITE_JSONPAGES_API_KEY;
2579
2579
  const SAVE2REPO_ENABLED = import.meta.env.VITE_SAVE2REPO === 'true';
2580
+ const APP_BASE_PATH = normalizeBasePath(import.meta.env.BASE_URL || '/');
2580
2581
 
2581
2582
  const themeConfig = themeData as unknown as ThemeConfig;
2582
- const menuConfig: MenuConfig = { main: [] };
2583
+ const menuConfig = menuData as unknown as MenuConfig;
2583
2584
  const refDocuments = {
2584
- 'menu.json': menuData,
2585
- 'config/menu.json': menuData,
2586
- 'src/data/config/menu.json': menuData,
2585
+ 'menu.json': menuConfig,
2586
+ 'config/menu.json': menuConfig,
2587
+ 'src/data/config/menu.json': menuConfig,
2587
2588
  } satisfies NonNullable<JsonPagesConfig['refDocuments']>;
2589
+
2588
2590
  const TENANT_ID = 'alpha';
2589
2591
 
2590
2592
  const filePages = getFilePages();
@@ -2844,13 +2846,13 @@ function normalizeSlugForCache(slug: string): string {
2844
2846
  }
2845
2847
 
2846
2848
  function buildPublishedPageHref(slug: string): string {
2847
- return `/pages/${normalizeSlugForCache(slug)}.json`;
2849
+ return withBasePath(`/pages/${normalizeSlugForCache(slug)}.json`, APP_BASE_PATH);
2848
2850
  }
2849
2851
 
2850
2852
  async function loadPublishedStaticContent(
2851
2853
  knownSlugs: string[]
2852
2854
  ): Promise<{ pages: Record<string, PageConfig>; siteConfig: SiteConfig }> {
2853
- const siteResponse = await fetch('/config/site.json', { cache: 'no-store' });
2855
+ const siteResponse = await fetch(withBasePath('/config/site.json', APP_BASE_PATH), { cache: 'no-store' });
2854
2856
  if (!siteResponse.ok) {
2855
2857
  throw new Error(`Static site config unavailable: ${siteResponse.status}`);
2856
2858
  }
@@ -3330,6 +3332,7 @@ function App() {
3330
3332
 
3331
3333
  const config: JsonPagesConfig = {
3332
3334
  tenantId: TENANT_ID,
3335
+ basePath: APP_BASE_PATH,
3333
3336
  registry: ComponentRegistry as JsonPagesConfig['registry'],
3334
3337
  schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
3335
3338
  pages,
@@ -3398,7 +3401,7 @@ function App() {
3398
3401
  showColdSave: isSave2RepoMode,
3399
3402
  },
3400
3403
  assets: {
3401
- assetsBaseUrl: '/assets',
3404
+ assetsBaseUrl: withBasePath('/assets', APP_BASE_PATH),
3402
3405
  assetsManifest,
3403
3406
  async onAssetUpload(file: File): Promise<string> {
3404
3407
  if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
@@ -3814,7 +3817,6 @@ export function useTheme() {
3814
3817
  }
3815
3818
 
3816
3819
  END_OF_FILE_CONTENT
3817
- # SKIP: src/components/ThemeProvider.tsx:Zone.Identifier is binary and cannot be embedded as text.
3818
3820
  echo "Creating src/components/ThemeToggle.tsx..."
3819
3821
  cat << 'END_OF_FILE_CONTENT' > "src/components/ThemeToggle.tsx"
3820
3822
  import { Sun, Moon } from 'lucide-react'
@@ -3845,7 +3847,243 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
3845
3847
  }
3846
3848
 
3847
3849
  END_OF_FILE_CONTENT
3848
- # SKIP: src/components/ThemeToggle.tsx:Zone.Identifier is binary and cannot be embedded as text.
3850
+ mkdir -p "src/components/accommodations-section"
3851
+ echo "Creating src/components/accommodations-section/View.tsx..."
3852
+ cat << 'END_OF_FILE_CONTENT' > "src/components/accommodations-section/View.tsx"
3853
+ // Layout: Hero=N/A, Features=HORIZONTAL SCROLL (cards in scrollable row)
3854
+ import React from 'react';
3855
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
3856
+ import { ScrollArea } from '@/components/ui/scroll-area';
3857
+ import { Check } from 'lucide-react';
3858
+ import type { AccommodationsSectionData, AccommodationsSectionSettings } from './types';
3859
+
3860
+ export const AccommodationsSection: React.FC<{ data: AccommodationsSectionData; settings: AccommodationsSectionSettings }> = ({ data }) => {
3861
+ return (
3862
+ <section
3863
+ style={{
3864
+ '--local-bg': 'var(--elevated)',
3865
+ '--local-text': 'var(--foreground)',
3866
+ '--local-text-muted': 'var(--muted-foreground)',
3867
+ '--local-primary': 'var(--primary)',
3868
+ '--local-accent': 'var(--accent)',
3869
+ '--local-border': 'var(--border)',
3870
+ '--local-surface': 'var(--card)',
3871
+ '--local-radius-lg': 'var(--theme-radius-lg)',
3872
+ '--local-radius-md': 'var(--theme-radius-md)',
3873
+ } as React.CSSProperties}
3874
+ className="relative z-0 py-28 bg-[var(--local-bg)]"
3875
+ >
3876
+ <div className="max-w-[var(--theme-spacing-container-max)] mx-auto px-6">
3877
+ {/* Header */}
3878
+ <div className="text-center mb-12">
3879
+ {data.label && (
3880
+ <div className="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">
3881
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
3882
+ {data.label}
3883
+ </div>
3884
+ )}
3885
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)] mb-4" data-jp-field="title">
3886
+ {data.title}
3887
+ </h2>
3888
+ {data.subtitle && (
3889
+ <p className="text-lg text-[var(--local-text-muted)] max-w-2xl mx-auto" data-jp-field="subtitle">
3890
+ {data.subtitle}
3891
+ </p>
3892
+ )}
3893
+ </div>
3894
+
3895
+ {/* Horizontal Scrolling Cards */}
3896
+ <ScrollArea className="w-full">
3897
+ <div className="flex gap-6 pb-4" style={{ width: 'max-content' }}>
3898
+ {data.accommodations.map((accommodation, idx) => (
3899
+ <Card
3900
+ key={accommodation.id || `legacy-${idx}`}
3901
+ className="w-80 flex-shrink-0 bg-[var(--local-surface)] border-[var(--local-border)] rounded-[var(--local-radius-lg)] overflow-hidden group hover:shadow-lg transition-all duration-300"
3902
+ data-jp-item-id={accommodation.id || `legacy-${idx}`}
3903
+ data-jp-item-field="accommodations"
3904
+ >
3905
+ {accommodation.image?.url && (
3906
+ <div className="relative h-48 overflow-hidden">
3907
+ <img
3908
+ src={accommodation.image.url}
3909
+ alt={accommodation.image.alt}
3910
+ className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
3911
+ />
3912
+ <div className="absolute inset-0 bg-gradient-to-t from-black/50 via-transparent to-transparent" />
3913
+ </div>
3914
+ )}
3915
+
3916
+ <CardHeader className="pb-3">
3917
+ <CardTitle className="font-display text-xl text-[var(--local-text)]">
3918
+ {accommodation.title}
3919
+ </CardTitle>
3920
+ </CardHeader>
3921
+
3922
+ <CardContent className="space-y-4">
3923
+ <p className="text-[var(--local-text-muted)] leading-relaxed">
3924
+ {accommodation.description}
3925
+ </p>
3926
+
3927
+ {accommodation.features.length > 0 && (
3928
+ <div className="space-y-2">
3929
+ <h4 className="font-semibold text-sm text-[var(--local-text)]">Caratteristiche:</h4>
3930
+ <div className="space-y-1">
3931
+ {accommodation.features.map((feature, featureIdx) => (
3932
+ <div key={featureIdx} className="flex items-center gap-2">
3933
+ <Check className="w-4 h-4 text-[var(--local-primary)] flex-shrink-0" />
3934
+ <span className="text-sm text-[var(--local-text-muted)]">{feature}</span>
3935
+ </div>
3936
+ ))}
3937
+ </div>
3938
+ </div>
3939
+ )}
3940
+ </CardContent>
3941
+ </Card>
3942
+ ))}
3943
+ </div>
3944
+ </ScrollArea>
3945
+
3946
+ {data.accommodations.length > 3 && (
3947
+ <div className="text-center mt-6">
3948
+ <p className="text-sm text-[var(--local-text-muted)]">
3949
+ Scorri orizzontalmente per vedere tutte le sistemazioni
3950
+ </p>
3951
+ </div>
3952
+ )}
3953
+ </div>
3954
+ </section>
3955
+ );
3956
+ };
3957
+
3958
+ END_OF_FILE_CONTENT
3959
+ echo "Creating src/components/accommodations-section/index.ts..."
3960
+ cat << 'END_OF_FILE_CONTENT' > "src/components/accommodations-section/index.ts"
3961
+ export { AccommodationsSection } from './View';
3962
+ export { AccommodationsSectionSchema } from './schema';
3963
+ export type { AccommodationsSectionData, AccommodationsSectionSettings } from './types';
3964
+
3965
+
3966
+ END_OF_FILE_CONTENT
3967
+ echo "Creating src/components/accommodations-section/schema.ts..."
3968
+ cat << 'END_OF_FILE_CONTENT' > "src/components/accommodations-section/schema.ts"
3969
+ import { z } from 'zod';
3970
+ import { BaseSectionData, BaseArrayItem, ImageSelectionSchema } from '@/lib/base-schemas';
3971
+
3972
+ const AccommodationItem = BaseArrayItem.extend({
3973
+ title: z.string().describe('ui:text'),
3974
+ description: z.string().describe('ui:textarea'),
3975
+ features: z.array(z.string()).describe('ui:list'),
3976
+ image: ImageSelectionSchema,
3977
+ });
3978
+
3979
+ export const AccommodationsSectionSchema = BaseSectionData.extend({
3980
+ label: z.string().optional().describe('ui:text'),
3981
+ title: z.string().describe('ui:text'),
3982
+ subtitle: z.string().optional().describe('ui:textarea'),
3983
+ accommodations: z.array(AccommodationItem).describe('ui:list'),
3984
+ });
3985
+
3986
+
3987
+ END_OF_FILE_CONTENT
3988
+ echo "Creating src/components/accommodations-section/types.ts..."
3989
+ cat << 'END_OF_FILE_CONTENT' > "src/components/accommodations-section/types.ts"
3990
+ import { z } from 'zod';
3991
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
3992
+ import { AccommodationsSectionSchema } from './schema';
3993
+
3994
+ export type AccommodationsSectionData = z.infer<typeof AccommodationsSectionSchema>;
3995
+ export type AccommodationsSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
3996
+
3997
+
3998
+ END_OF_FILE_CONTENT
3999
+ mkdir -p "src/components/activities-section"
4000
+ echo "Creating src/components/activities-section/View.tsx..."
4001
+ cat << 'END_OF_FILE_CONTENT' > "src/components/activities-section/View.tsx"
4002
+ // Layout: Hero=F (MINIMAL HERO), Features=D (ACCORDION)
4003
+ import React from 'react';
4004
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
4005
+ import type { ActivitiesSectionData, ActivitiesSectionSettings } from './types';
4006
+
4007
+ export const ActivitiesSectionComponent: React.FC<{ data: ActivitiesSectionData; settings: ActivitiesSectionSettings }> = ({ data }) => (
4008
+ <section
4009
+ style={{
4010
+ '--local-bg': 'var(--background)',
4011
+ '--local-text': 'var(--foreground)',
4012
+ '--local-text-muted': 'var(--muted-foreground)',
4013
+ '--local-primary': 'var(--primary)',
4014
+ '--local-accent': 'var(--accent)',
4015
+ '--local-border': 'var(--border)',
4016
+ '--local-surface': 'var(--card)',
4017
+ '--local-radius-lg': 'var(--theme-radius-lg)',
4018
+ } as React.CSSProperties}
4019
+ className="relative z-0 py-24"
4020
+ >
4021
+ <div className="max-w-[900px] mx-auto px-8">
4022
+ {data.label && (
4023
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase text-[var(--local-accent)] mb-4" data-jp-field="label">
4024
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
4025
+ {data.label}
4026
+ </div>
4027
+ )}
4028
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)]" data-jp-field="title">{data.title}</h2>
4029
+ <p className="mt-5 text-[var(--local-text-muted)] max-w-2xl" data-jp-field="description">{data.description}</p>
4030
+ <Accordion type="single" collapsible className="mt-8 rounded-[var(--local-radius-lg)] border border-[var(--local-border)] bg-[var(--local-surface)] px-6">
4031
+ {data.items.map((item, idx) => (
4032
+ <AccordionItem
4033
+ key={item.id || 'legacy-' + idx}
4034
+ value={item.id || 'legacy-' + idx}
4035
+ data-jp-item-id={item.id || 'legacy-' + idx}
4036
+ data-jp-item-field="items"
4037
+ >
4038
+ <AccordionTrigger className="font-display text-left text-[var(--local-text)]">{item.title}</AccordionTrigger>
4039
+ <AccordionContent className="text-[var(--local-text-muted)]">{item.body}</AccordionContent>
4040
+ </AccordionItem>
4041
+ ))}
4042
+ </Accordion>
4043
+ </div>
4044
+ </section>
4045
+ );
4046
+
4047
+
4048
+ END_OF_FILE_CONTENT
4049
+ echo "Creating src/components/activities-section/index.ts..."
4050
+ cat << 'END_OF_FILE_CONTENT' > "src/components/activities-section/index.ts"
4051
+ export { ActivitiesSectionComponent } from './View';
4052
+ export { ActivitiesSectionSchema } from './schema';
4053
+ export type { ActivitiesSectionData, ActivitiesSectionSettings } from './types';
4054
+
4055
+
4056
+ END_OF_FILE_CONTENT
4057
+ echo "Creating src/components/activities-section/schema.ts..."
4058
+ cat << 'END_OF_FILE_CONTENT' > "src/components/activities-section/schema.ts"
4059
+ import { z } from 'zod';
4060
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
4061
+
4062
+ const ActivitySchema = BaseArrayItem.extend({
4063
+ title: z.string().describe('ui:text'),
4064
+ body: z.string().describe('ui:textarea'),
4065
+ });
4066
+
4067
+ export const ActivitiesSectionSchema = BaseSectionData.extend({
4068
+ label: z.string().optional().describe('ui:text'),
4069
+ title: z.string().describe('ui:text'),
4070
+ description: z.string().describe('ui:textarea'),
4071
+ items: z.array(ActivitySchema).describe('ui:list'),
4072
+ });
4073
+
4074
+
4075
+ END_OF_FILE_CONTENT
4076
+ echo "Creating src/components/activities-section/types.ts..."
4077
+ cat << 'END_OF_FILE_CONTENT' > "src/components/activities-section/types.ts"
4078
+ import { z } from 'zod';
4079
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
4080
+ import { ActivitiesSectionSchema } from './schema';
4081
+
4082
+ export type ActivitiesSectionData = z.infer<typeof ActivitiesSectionSchema>;
4083
+ export type ActivitiesSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
4084
+
4085
+
4086
+ END_OF_FILE_CONTENT
3849
4087
  mkdir -p "src/components/cloud-ai-native-grid"
3850
4088
  echo "Creating src/components/cloud-ai-native-grid/View.tsx..."
3851
4089
  cat << 'END_OF_FILE_CONTENT' > "src/components/cloud-ai-native-grid/View.tsx"
@@ -3944,6 +4182,104 @@ export type CloudAiNativeGridSettings = z.infer<typeof CloudAiNativeGridSettings
3944
4182
 
3945
4183
  END_OF_FILE_CONTENT
3946
4184
  mkdir -p "src/components/contact"
4185
+ mkdir -p "src/components/contact-section"
4186
+ echo "Creating src/components/contact-section/View.tsx..."
4187
+ cat << 'END_OF_FILE_CONTENT' > "src/components/contact-section/View.tsx"
4188
+ // Layout: Hero=E (MAGAZINE), Features=C (TIMELINE)
4189
+ import React from 'react';
4190
+ import { ArrowRight, MapPin } from 'lucide-react';
4191
+ import { Button } from '@/components/ui/button';
4192
+ import { Card, CardContent } from '@/components/ui/card';
4193
+ import type { ContactSectionData, ContactSectionSettings } from './types';
4194
+
4195
+ export const ContactSectionComponent: React.FC<{ data: ContactSectionData; settings: ContactSectionSettings }> = ({ data }) => (
4196
+ <section
4197
+ style={{
4198
+ '--local-bg': 'var(--background)',
4199
+ '--local-text': 'var(--foreground)',
4200
+ '--local-text-muted': 'var(--muted-foreground)',
4201
+ '--local-primary': 'var(--primary)',
4202
+ '--local-primary-foreground': 'var(--primary-foreground)',
4203
+ '--local-accent': 'var(--accent)',
4204
+ '--local-border': 'var(--border)',
4205
+ '--local-surface': 'var(--card)',
4206
+ '--local-radius-md': 'var(--theme-radius-md)',
4207
+ '--local-radius-lg': 'var(--theme-radius-lg)',
4208
+ } as React.CSSProperties}
4209
+ className="relative z-0 py-24"
4210
+ >
4211
+ <div className="max-w-[1200px] mx-auto px-8 grid gap-8 lg:grid-cols-[0.9fr_1.1fr]">
4212
+ <div>
4213
+ {data.label && (
4214
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase text-[var(--local-accent)] mb-4" data-jp-field="label">
4215
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
4216
+ {data.label}
4217
+ </div>
4218
+ )}
4219
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)]" data-jp-field="title">{data.title}</h2>
4220
+ <p className="mt-4 max-w-lg text-[var(--local-text-muted)]" data-jp-field="description">{data.description}</p>
4221
+ <Button asChild variant="default" className="mt-6 bg-[var(--local-primary)] text-[var(--local-primary-foreground)] hover:opacity-90 rounded-[var(--local-radius-md)]">
4222
+ <a href={data.primaryCta.href}>{data.primaryCta.label} <ArrowRight className="h-4 w-4" /></a>
4223
+ </Button>
4224
+ </div>
4225
+ <div className="grid gap-4">
4226
+ {data.details.map((item, idx) => (
4227
+ <Card key={item.id || 'legacy-' + idx} className="border-[var(--local-border)] bg-[var(--local-surface)] rounded-[var(--local-radius-lg)]" data-jp-item-id={item.id || 'legacy-' + idx} data-jp-item-field="details">
4228
+ <CardContent className="p-6 flex gap-4">
4229
+ <MapPin className="h-5 w-5 mt-1 text-[var(--local-accent)]" />
4230
+ <div>
4231
+ <h3 className="font-display font-bold text-[1.2rem] text-[var(--local-text)]">{item.title}</h3>
4232
+ <p className="mt-2 text-sm text-[var(--local-text-muted)] whitespace-pre-line">{item.body}</p>
4233
+ </div>
4234
+ </CardContent>
4235
+ </Card>
4236
+ ))}
4237
+ </div>
4238
+ </div>
4239
+ </section>
4240
+ );
4241
+
4242
+
4243
+ END_OF_FILE_CONTENT
4244
+ echo "Creating src/components/contact-section/index.ts..."
4245
+ cat << 'END_OF_FILE_CONTENT' > "src/components/contact-section/index.ts"
4246
+ export { ContactSectionComponent } from './View';
4247
+ export { ContactSectionSchema } from './schema';
4248
+ export type { ContactSectionData, ContactSectionSettings } from './types';
4249
+
4250
+
4251
+ END_OF_FILE_CONTENT
4252
+ echo "Creating src/components/contact-section/schema.ts..."
4253
+ cat << 'END_OF_FILE_CONTENT' > "src/components/contact-section/schema.ts"
4254
+ import { z } from 'zod';
4255
+ import { BaseSectionData, BaseArrayItem, CtaSchema } from '@/lib/base-schemas';
4256
+
4257
+ const DetailSchema = BaseArrayItem.extend({
4258
+ title: z.string().describe('ui:text'),
4259
+ body: z.string().describe('ui:textarea'),
4260
+ });
4261
+
4262
+ export const ContactSectionSchema = BaseSectionData.extend({
4263
+ label: z.string().optional().describe('ui:text'),
4264
+ title: z.string().describe('ui:text'),
4265
+ description: z.string().describe('ui:textarea'),
4266
+ details: z.array(DetailSchema).describe('ui:list'),
4267
+ primaryCta: CtaSchema,
4268
+ });
4269
+
4270
+
4271
+ END_OF_FILE_CONTENT
4272
+ echo "Creating src/components/contact-section/types.ts..."
4273
+ cat << 'END_OF_FILE_CONTENT' > "src/components/contact-section/types.ts"
4274
+ import { z } from 'zod';
4275
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
4276
+ import { ContactSectionSchema } from './schema';
4277
+
4278
+ export type ContactSectionData = z.infer<typeof ContactSectionSchema>;
4279
+ export type ContactSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
4280
+
4281
+
4282
+ END_OF_FILE_CONTENT
3947
4283
  echo "Creating src/components/contact/View.tsx..."
3948
4284
  cat << 'END_OF_FILE_CONTENT' > "src/components/contact/View.tsx"
3949
4285
  import { useState, type CSSProperties } from 'react';
@@ -4270,6 +4606,103 @@ import { CtaBannerSchema } from './schema';
4270
4606
  export type CtaBannerData = z.infer<typeof CtaBannerSchema>;
4271
4607
  export type CtaBannerSettings = z.infer<typeof BaseSectionSettingsSchema>;
4272
4608
 
4609
+ END_OF_FILE_CONTENT
4610
+ mkdir -p "src/components/cuisine-section"
4611
+ echo "Creating src/components/cuisine-section/View.tsx..."
4612
+ cat << 'END_OF_FILE_CONTENT' > "src/components/cuisine-section/View.tsx"
4613
+ // Layout: Hero=B (BENTO GRID), Features=A (BENTO)
4614
+ import React from 'react';
4615
+ import { Card, CardContent } from '@/components/ui/card';
4616
+ import type { CuisineSectionData, CuisineSectionSettings } from './types';
4617
+
4618
+ export const CuisineSectionComponent: React.FC<{ data: CuisineSectionData; settings: CuisineSectionSettings }> = ({ data }) => (
4619
+ <section
4620
+ style={{
4621
+ '--local-bg': 'var(--card)',
4622
+ '--local-text': 'var(--foreground)',
4623
+ '--local-text-muted': 'var(--muted-foreground)',
4624
+ '--local-primary': 'var(--primary)',
4625
+ '--local-accent': 'var(--accent)',
4626
+ '--local-border': 'var(--border)',
4627
+ '--local-surface': 'var(--background)',
4628
+ '--local-radius-md': 'var(--theme-radius-md)',
4629
+ '--local-radius-lg': 'var(--theme-radius-lg)',
4630
+ } as React.CSSProperties}
4631
+ className="relative z-0 py-24 bg-[var(--local-bg)]"
4632
+ >
4633
+ <div className="max-w-[1200px] mx-auto px-8">
4634
+ {data.label && (
4635
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase text-[var(--local-accent)] mb-4" data-jp-field="label">
4636
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
4637
+ {data.label}
4638
+ </div>
4639
+ )}
4640
+ <div className="grid gap-8 lg:grid-cols-[1fr_1.1fr] items-start">
4641
+ <div>
4642
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)]" data-jp-field="title">{data.title}</h2>
4643
+ <p className="mt-5 text-[var(--local-text-muted)] max-w-lg" data-jp-field="description">{data.description}</p>
4644
+ <img src={data.image?.url} alt={data.image?.alt || ''} className="mt-8 w-full h-[360px] object-cover rounded-[var(--local-radius-lg)] border border-[var(--local-border)]" />
4645
+ </div>
4646
+ <div className="grid gap-4 md:grid-cols-2">
4647
+ {data.products.map((item, idx) => (
4648
+ <Card
4649
+ key={item.id || 'legacy-' + idx}
4650
+ className={'border-[var(--local-border)] bg-[var(--local-surface)] rounded-[var(--local-radius-lg)] ' + (idx === 0 ? 'md:col-span-2' : '')}
4651
+ data-jp-item-id={item.id || 'legacy-' + idx}
4652
+ data-jp-item-field="products"
4653
+ >
4654
+ <CardContent className="p-6">
4655
+ <h3 className="font-display font-bold text-[1.2rem] text-[var(--local-text)]">{item.title}</h3>
4656
+ <p className="mt-2 text-sm text-[var(--local-text-muted)]">{item.body}</p>
4657
+ </CardContent>
4658
+ </Card>
4659
+ ))}
4660
+ </div>
4661
+ </div>
4662
+ </div>
4663
+ </section>
4664
+ );
4665
+
4666
+
4667
+ END_OF_FILE_CONTENT
4668
+ echo "Creating src/components/cuisine-section/index.ts..."
4669
+ cat << 'END_OF_FILE_CONTENT' > "src/components/cuisine-section/index.ts"
4670
+ export { CuisineSectionComponent } from './View';
4671
+ export { CuisineSectionSchema } from './schema';
4672
+ export type { CuisineSectionData, CuisineSectionSettings } from './types';
4673
+
4674
+
4675
+ END_OF_FILE_CONTENT
4676
+ echo "Creating src/components/cuisine-section/schema.ts..."
4677
+ cat << 'END_OF_FILE_CONTENT' > "src/components/cuisine-section/schema.ts"
4678
+ import { z } from 'zod';
4679
+ import { BaseSectionData, BaseArrayItem, ImageSelectionSchema } from '@/lib/base-schemas';
4680
+
4681
+ const ProductSchema = BaseArrayItem.extend({
4682
+ title: z.string().describe('ui:text'),
4683
+ body: z.string().describe('ui:textarea'),
4684
+ });
4685
+
4686
+ export const CuisineSectionSchema = BaseSectionData.extend({
4687
+ label: z.string().optional().describe('ui:text'),
4688
+ title: z.string().describe('ui:text'),
4689
+ description: z.string().describe('ui:textarea'),
4690
+ image: ImageSelectionSchema.optional().default({ url: '', alt: '' }),
4691
+ products: z.array(ProductSchema).describe('ui:list'),
4692
+ });
4693
+
4694
+
4695
+ END_OF_FILE_CONTENT
4696
+ echo "Creating src/components/cuisine-section/types.ts..."
4697
+ cat << 'END_OF_FILE_CONTENT' > "src/components/cuisine-section/types.ts"
4698
+ import { z } from 'zod';
4699
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
4700
+ import { CuisineSectionSchema } from './schema';
4701
+
4702
+ export type CuisineSectionData = z.infer<typeof CuisineSectionSchema>;
4703
+ export type CuisineSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
4704
+
4705
+
4273
4706
  END_OF_FILE_CONTENT
4274
4707
  mkdir -p "src/components/design-system"
4275
4708
  echo "Creating src/components/design-system/View.tsx..."
@@ -5128,6 +5561,111 @@ import { DevexSchema } from './schema';
5128
5561
  export type DevexData = z.infer<typeof DevexSchema>;
5129
5562
  export type DevexSettings = z.infer<typeof BaseSectionSettingsSchema>;
5130
5563
 
5564
+ END_OF_FILE_CONTENT
5565
+ mkdir -p "src/components/docs-layout"
5566
+ mkdir -p "src/components/experiences-section"
5567
+ echo "Creating src/components/experiences-section/View.tsx..."
5568
+ cat << 'END_OF_FILE_CONTENT' > "src/components/experiences-section/View.tsx"
5569
+ // Layout: Hero=B (BENTO GRID), Features=A (BENTO)
5570
+ import React from 'react';
5571
+ import { ArrowRight } from 'lucide-react';
5572
+ import { Button } from '@/components/ui/button';
5573
+ import { Card, CardContent } from '@/components/ui/card';
5574
+ import type { ExperiencesSectionData, ExperiencesSectionSettings } from './types';
5575
+
5576
+ export const ExperiencesSectionComponent: React.FC<{ data: ExperiencesSectionData; settings: ExperiencesSectionSettings }> = ({ data }) => (
5577
+ <section
5578
+ style={{
5579
+ '--local-bg': 'var(--card)',
5580
+ '--local-text': 'var(--foreground)',
5581
+ '--local-text-muted': 'var(--muted-foreground)',
5582
+ '--local-primary': 'var(--primary)',
5583
+ '--local-primary-foreground': 'var(--primary-foreground)',
5584
+ '--local-accent': 'var(--accent)',
5585
+ '--local-border': 'var(--border)',
5586
+ '--local-surface': 'var(--background)',
5587
+ '--local-radius-md': 'var(--theme-radius-md)',
5588
+ '--local-radius-lg': 'var(--theme-radius-lg)',
5589
+ } as React.CSSProperties}
5590
+ className="relative z-0 py-24 bg-[var(--local-bg)]"
5591
+ >
5592
+ <div className="max-w-[1200px] mx-auto px-8">
5593
+ {data.label && (
5594
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase text-[var(--local-accent)] mb-4" data-jp-field="label">
5595
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
5596
+ {data.label}
5597
+ </div>
5598
+ )}
5599
+ <div className="flex flex-col lg:flex-row lg:items-end lg:justify-between gap-6">
5600
+ <div>
5601
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)]" data-jp-field="title">{data.title}</h2>
5602
+ <p className="mt-4 max-w-2xl text-[var(--local-text-muted)]" data-jp-field="description">{data.description}</p>
5603
+ </div>
5604
+ <Button asChild variant="default" className="bg-[var(--local-primary)] text-[var(--local-primary-foreground)] hover:opacity-90 rounded-[var(--local-radius-md)]">
5605
+ <a href={data.primaryCta.href}>{data.primaryCta.label} <ArrowRight className="h-4 w-4" /></a>
5606
+ </Button>
5607
+ </div>
5608
+ <div className="mt-10 grid gap-5 md:grid-cols-2 xl:grid-cols-3">
5609
+ {data.items.map((item, idx) => (
5610
+ <Card
5611
+ key={item.id || 'legacy-' + idx}
5612
+ className={'border-[var(--local-border)] bg-[var(--local-surface)] rounded-[var(--local-radius-lg)] ' + (idx === 0 ? 'xl:col-span-2' : '')}
5613
+ data-jp-item-id={item.id || 'legacy-' + idx}
5614
+ data-jp-item-field="items"
5615
+ >
5616
+ <CardContent className="p-6">
5617
+ <h3 className="font-display font-bold text-[1.2rem] text-[var(--local-text)]">{item.title}</h3>
5618
+ <p className="mt-2 text-sm text-[var(--local-text-muted)]">{item.body}</p>
5619
+ <p className="mt-4 text-sm text-[var(--local-text)]">{item.details}</p>
5620
+ </CardContent>
5621
+ </Card>
5622
+ ))}
5623
+ </div>
5624
+ </div>
5625
+ </section>
5626
+ );
5627
+
5628
+
5629
+ END_OF_FILE_CONTENT
5630
+ echo "Creating src/components/experiences-section/index.ts..."
5631
+ cat << 'END_OF_FILE_CONTENT' > "src/components/experiences-section/index.ts"
5632
+ export { ExperiencesSectionComponent } from './View';
5633
+ export { ExperiencesSectionSchema } from './schema';
5634
+ export type { ExperiencesSectionData, ExperiencesSectionSettings } from './types';
5635
+
5636
+
5637
+ END_OF_FILE_CONTENT
5638
+ echo "Creating src/components/experiences-section/schema.ts..."
5639
+ cat << 'END_OF_FILE_CONTENT' > "src/components/experiences-section/schema.ts"
5640
+ import { z } from 'zod';
5641
+ import { BaseSectionData, BaseArrayItem, CtaSchema } from '@/lib/base-schemas';
5642
+
5643
+ const ExperienceSchema = BaseArrayItem.extend({
5644
+ title: z.string().describe('ui:text'),
5645
+ body: z.string().describe('ui:textarea'),
5646
+ details: z.string().describe('ui:textarea'),
5647
+ });
5648
+
5649
+ export const ExperiencesSectionSchema = BaseSectionData.extend({
5650
+ label: z.string().optional().describe('ui:text'),
5651
+ title: z.string().describe('ui:text'),
5652
+ description: z.string().describe('ui:textarea'),
5653
+ items: z.array(ExperienceSchema).describe('ui:list'),
5654
+ primaryCta: CtaSchema,
5655
+ });
5656
+
5657
+
5658
+ END_OF_FILE_CONTENT
5659
+ echo "Creating src/components/experiences-section/types.ts..."
5660
+ cat << 'END_OF_FILE_CONTENT' > "src/components/experiences-section/types.ts"
5661
+ import { z } from 'zod';
5662
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
5663
+ import { ExperiencesSectionSchema } from './schema';
5664
+
5665
+ export type ExperiencesSectionData = z.infer<typeof ExperiencesSectionSchema>;
5666
+ export type ExperiencesSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
5667
+
5668
+
5131
5669
  END_OF_FILE_CONTENT
5132
5670
  mkdir -p "src/components/feature-grid"
5133
5671
  echo "Creating src/components/feature-grid/View.tsx..."
@@ -5302,46 +5840,187 @@ export type FeatureGridData = z.infer<typeof FeatureGridSchema>;
5302
5840
  export type FeatureGridSettings = z.infer<typeof FeatureGridSettingsSchema>;
5303
5841
 
5304
5842
  END_OF_FILE_CONTENT
5305
- mkdir -p "src/components/footer"
5306
- echo "Creating src/components/footer/View.tsx..."
5307
- cat << 'END_OF_FILE_CONTENT' > "src/components/footer/View.tsx"
5308
- import { OlonMark } from '@/components/OlonWordmark';
5309
- import type { FooterData, FooterSettings } from './types';
5310
-
5311
- interface FooterViewProps {
5312
- data: FooterData;
5313
- settings?: FooterSettings;
5314
- }
5315
-
5316
- export function Footer({ data, settings }: FooterViewProps) {
5317
- const showLogo = settings?.showLogo ?? true;
5318
- const links = data.links ?? [];
5319
-
5320
- return (
5321
- <footer className="border-t border-border px-6 py-8">
5322
- <div className="max-w-6xl px-6 mx-auto flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
5323
- <div className="flex items-center gap-6">
5324
- {showLogo && (
5325
- <div className="flex items-center gap-2.5">
5326
- <OlonMark size={18} />
5327
- <span className="text-base font-display text-foreground tracking-[-0.02em]">
5328
- {data.brandText}
5329
- </span>
5330
- </div>
5331
- )}
5332
- {links.length > 0 && (
5333
- <div className="flex items-center gap-4">
5334
- {links.map((link) => (
5335
- <a
5336
- key={link.label}
5337
- href={link.href}
5338
- className="text-[12px] text-muted-foreground hover:text-foreground transition-colors"
5339
- >
5340
- {link.label}
5341
- </a>
5342
- ))}
5343
- {data.designSystemHref && (
5344
- <a
5843
+ mkdir -p "src/components/features-overview"
5844
+ echo "Creating src/components/features-overview/View.tsx..."
5845
+ cat << 'END_OF_FILE_CONTENT' > "src/components/features-overview/View.tsx"
5846
+ // Layout: Hero=N/A, Features=BENTO (irregular grid)
5847
+ import React from 'react';
5848
+ import { Card, CardContent } from '@/components/ui/card';
5849
+ import { Leaf, Mountain, Utensils } from 'lucide-react';
5850
+ import type { FeaturesOverviewData, FeaturesOverviewSettings } from './types';
5851
+
5852
+ export const FeaturesOverview: React.FC<{ data: FeaturesOverviewData; settings: FeaturesOverviewSettings }> = ({ data }) => {
5853
+ const getIcon = (iconName: string) => {
5854
+ switch (iconName) {
5855
+ case 'leaf': return <Leaf className="w-6 h-6" />;
5856
+ case 'mountain': return <Mountain className="w-6 h-6" />;
5857
+ case 'utensils': return <Utensils className="w-6 h-6" />;
5858
+ default: return <Leaf className="w-6 h-6" />;
5859
+ }
5860
+ };
5861
+
5862
+ return (
5863
+ <section
5864
+ style={{
5865
+ '--local-bg': 'var(--background)',
5866
+ '--local-text': 'var(--foreground)',
5867
+ '--local-text-muted': 'var(--muted-foreground)',
5868
+ '--local-primary': 'var(--primary)',
5869
+ '--local-accent': 'var(--accent)',
5870
+ '--local-border': 'var(--border)',
5871
+ '--local-surface': 'var(--card)',
5872
+ '--local-radius-lg': 'var(--theme-radius-lg)',
5873
+ '--local-radius-md': 'var(--theme-radius-md)',
5874
+ } as React.CSSProperties}
5875
+ className="relative z-0 py-28 bg-[var(--local-bg)]"
5876
+ >
5877
+ <div className="max-w-[var(--theme-spacing-container-max)] mx-auto px-6">
5878
+ {/* Header */}
5879
+ <div className="text-center mb-16">
5880
+ {data.label && (
5881
+ <div className="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">
5882
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
5883
+ {data.label}
5884
+ </div>
5885
+ )}
5886
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)] mb-4" data-jp-field="title">
5887
+ {data.title}
5888
+ </h2>
5889
+ {data.description && (
5890
+ <p className="text-lg text-[var(--local-text-muted)] max-w-2xl mx-auto" data-jp-field="description">
5891
+ {data.description}
5892
+ </p>
5893
+ )}
5894
+ </div>
5895
+
5896
+ {/* Bento Grid */}
5897
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 auto-rows-fr">
5898
+ {data.features.map((feature, idx) => (
5899
+ <Card
5900
+ key={feature.id || `legacy-${idx}`}
5901
+ className={`
5902
+ bg-[var(--local-surface)] border-[var(--local-border)] rounded-[var(--local-radius-lg)]
5903
+ overflow-hidden group hover:shadow-lg transition-all duration-300
5904
+ ${idx === 0 ? 'lg:col-span-2' : ''}
5905
+ ${idx === 1 ? 'lg:row-span-2' : ''}
5906
+ `}
5907
+ data-jp-item-id={feature.id || `legacy-${idx}`}
5908
+ data-jp-item-field="features"
5909
+ >
5910
+ {feature.image?.url && (
5911
+ <div className="relative h-48 overflow-hidden">
5912
+ <img
5913
+ src={feature.image.url}
5914
+ alt={feature.image.alt}
5915
+ className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
5916
+ />
5917
+ <div className="absolute inset-0 bg-gradient-to-t from-black/50 via-transparent to-transparent" />
5918
+ {feature.icon && (
5919
+ <div className="absolute top-4 left-4 p-3 bg-[var(--local-primary)] rounded-[var(--local-radius-md)] text-white">
5920
+ {getIcon(feature.icon)}
5921
+ </div>
5922
+ )}
5923
+ </div>
5924
+ )}
5925
+
5926
+ <CardContent className="p-6">
5927
+ <h3 className="font-display font-bold text-xl text-[var(--local-text)] mb-3">
5928
+ {feature.title}
5929
+ </h3>
5930
+ <p className="text-[var(--local-text-muted)] leading-relaxed">
5931
+ {feature.description}
5932
+ </p>
5933
+ </CardContent>
5934
+ </Card>
5935
+ ))}
5936
+ </div>
5937
+ </div>
5938
+ </section>
5939
+ );
5940
+ };
5941
+
5942
+
5943
+ END_OF_FILE_CONTENT
5944
+ echo "Creating src/components/features-overview/index.ts..."
5945
+ cat << 'END_OF_FILE_CONTENT' > "src/components/features-overview/index.ts"
5946
+ export { FeaturesOverview } from './View';
5947
+ export { FeaturesOverviewSchema } from './schema';
5948
+ export type { FeaturesOverviewData, FeaturesOverviewSettings } from './types';
5949
+
5950
+
5951
+ END_OF_FILE_CONTENT
5952
+ echo "Creating src/components/features-overview/schema.ts..."
5953
+ cat << 'END_OF_FILE_CONTENT' > "src/components/features-overview/schema.ts"
5954
+ import { z } from 'zod';
5955
+ import { BaseSectionData, BaseArrayItem, ImageSelectionSchema } from '@/lib/base-schemas';
5956
+
5957
+ const FeatureItem = BaseArrayItem.extend({
5958
+ title: z.string().describe('ui:text'),
5959
+ description: z.string().describe('ui:textarea'),
5960
+ icon: z.string().optional().describe('ui:text'),
5961
+ image: ImageSelectionSchema,
5962
+ });
5963
+
5964
+ export const FeaturesOverviewSchema = BaseSectionData.extend({
5965
+ label: z.string().optional().describe('ui:text'),
5966
+ title: z.string().describe('ui:text'),
5967
+ description: z.string().optional().describe('ui:textarea'),
5968
+ features: z.array(FeatureItem).describe('ui:list'),
5969
+ });
5970
+
5971
+
5972
+ END_OF_FILE_CONTENT
5973
+ echo "Creating src/components/features-overview/types.ts..."
5974
+ cat << 'END_OF_FILE_CONTENT' > "src/components/features-overview/types.ts"
5975
+ import { z } from 'zod';
5976
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
5977
+ import { FeaturesOverviewSchema } from './schema';
5978
+
5979
+ export type FeaturesOverviewData = z.infer<typeof FeaturesOverviewSchema>;
5980
+ export type FeaturesOverviewSettings = z.infer<typeof BaseSectionSettingsSchema>;
5981
+
5982
+
5983
+ END_OF_FILE_CONTENT
5984
+ mkdir -p "src/components/footer"
5985
+ echo "Creating src/components/footer/View.tsx..."
5986
+ cat << 'END_OF_FILE_CONTENT' > "src/components/footer/View.tsx"
5987
+ import { OlonMark } from '@/components/OlonWordmark';
5988
+ import type { FooterData, FooterSettings } from './types';
5989
+
5990
+ interface FooterViewProps {
5991
+ data: FooterData;
5992
+ settings?: FooterSettings;
5993
+ }
5994
+
5995
+ export function Footer({ data, settings }: FooterViewProps) {
5996
+ const showLogo = settings?.showLogo ?? true;
5997
+ const links = data.links ?? [];
5998
+
5999
+ return (
6000
+ <footer className="border-t border-border px-6 py-8">
6001
+ <div className="max-w-6xl px-6 mx-auto flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
6002
+ <div className="flex items-center gap-6">
6003
+ {showLogo && (
6004
+ <div className="flex items-center gap-2.5">
6005
+ <OlonMark size={18} />
6006
+ <span className="text-base font-display text-foreground tracking-[-0.02em]">
6007
+ {data.brandText}
6008
+ </span>
6009
+ </div>
6010
+ )}
6011
+ {links.length > 0 && (
6012
+ <div className="flex items-center gap-4">
6013
+ {links.map((link) => (
6014
+ <a
6015
+ key={link.label}
6016
+ href={link.href}
6017
+ className="text-[12px] text-muted-foreground hover:text-foreground transition-colors"
6018
+ >
6019
+ {link.label}
6020
+ </a>
6021
+ ))}
6022
+ {data.designSystemHref && (
6023
+ <a
5345
6024
  href={data.designSystemHref}
5346
6025
  className="text-[12px] text-muted-foreground hover:text-foreground transition-colors"
5347
6026
  >
@@ -5846,7 +6525,6 @@ export function Header({ data, settings, menu }: HeaderViewProps) {
5846
6525
  }
5847
6526
 
5848
6527
  END_OF_FILE_CONTENT
5849
- # SKIP: src/components/header/View.tsx:Zone.Identifier is binary and cannot be embedded as text.
5850
6528
  echo "Creating src/components/header/View_.tsx..."
5851
6529
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/View_.tsx"
5852
6530
  import React, { useState } from 'react';
@@ -5990,7 +6668,6 @@ export * from './View';
5990
6668
  export * from './schema';
5991
6669
  export * from './types';
5992
6670
  END_OF_FILE_CONTENT
5993
- # SKIP: src/components/header/index.ts:Zone.Identifier is binary and cannot be embedded as text.
5994
6671
  echo "Creating src/components/header/schema.ts..."
5995
6672
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/schema.ts"
5996
6673
  import { z } from 'zod';
@@ -6023,7 +6700,6 @@ export const HeaderSettingsSchema = z.object({
6023
6700
  sticky: z.boolean().default(true).describe('ui:checkbox'),
6024
6701
  });
6025
6702
  END_OF_FILE_CONTENT
6026
- # SKIP: src/components/header/schema.ts:Zone.Identifier is binary and cannot be embedded as text.
6027
6703
  echo "Creating src/components/header/types.ts..."
6028
6704
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/types.ts"
6029
6705
  import { z } from 'zod';
@@ -6043,9 +6719,139 @@ export type HeaderData = z.infer<typeof HeaderSchema>;
6043
6719
  */
6044
6720
  export type HeaderSettings = z.infer<typeof HeaderSettingsSchema>;
6045
6721
  END_OF_FILE_CONTENT
6046
- # SKIP: src/components/header/types.ts:Zone.Identifier is binary and cannot be embedded as text.
6047
6722
  mkdir -p "src/components/hero"
6048
- # SKIP: src/components/hero/RadialBackground - Copy.tsx:Zone.Identifier is binary and cannot be embedded as text.
6723
+ mkdir -p "src/components/hero-section"
6724
+ echo "Creating src/components/hero-section/View.tsx..."
6725
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hero-section/View.tsx"
6726
+ // Layout: Hero=FULLSCREEN CINEMATIC, Features=N/A
6727
+ import React from 'react';
6728
+ import { Button } from '@/components/ui/button';
6729
+ import type { HeroSectionData, HeroSectionSettings } from './types';
6730
+
6731
+ export const HeroSection: React.FC<{ data: HeroSectionData; settings: HeroSectionSettings }> = ({ data }) => {
6732
+ return (
6733
+ <section
6734
+ style={{
6735
+ '--local-bg': 'var(--background)',
6736
+ '--local-text': 'var(--foreground)',
6737
+ '--local-text-muted': 'var(--muted-foreground)',
6738
+ '--local-primary': 'var(--primary)',
6739
+ '--local-primary-foreground': 'var(--primary-foreground)',
6740
+ '--local-accent': 'var(--accent)',
6741
+ '--local-border': 'var(--border)',
6742
+ '--local-radius-md': 'var(--theme-radius-md)',
6743
+ } as React.CSSProperties}
6744
+ className="relative z-0 min-h-screen flex items-center justify-center bg-[var(--local-bg)] overflow-hidden"
6745
+ >
6746
+ {/* Background Image */}
6747
+ {data.heroImage?.url && (
6748
+ <div
6749
+ className="absolute inset-0 bg-cover bg-center bg-no-repeat"
6750
+ style={{ backgroundImage: `url(${data.heroImage.url})` }}
6751
+ >
6752
+ <div className="absolute inset-0 bg-gradient-to-b from-black/40 via-black/20 to-black/60" />
6753
+ </div>
6754
+ )}
6755
+
6756
+ {/* Decorative Background */}
6757
+ <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_50%_0%,color-mix(in_oklch,var(--local-accent)_15%,transparent)_35%,transparent_70%)] pointer-events-none" />
6758
+
6759
+ {/* Content */}
6760
+ <div className="relative z-10 max-w-4xl mx-auto px-6 text-center">
6761
+ {data.label && (
6762
+ <div className="inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.12em] text-white/90 mb-6 jp-animate-in" data-jp-field="label">
6763
+ <span className="w-8 h-px bg-white/60" />
6764
+ {data.label}
6765
+ <span className="w-8 h-px bg-white/60" />
6766
+ </div>
6767
+ )}
6768
+
6769
+ <h1 className="font-display font-black text-[clamp(3rem,6vw,5.5rem)] leading-[1.0] tracking-tight text-white mb-6 jp-animate-in jp-d1" data-jp-field="title">
6770
+ {data.title}
6771
+ {data.titleHighlight && (
6772
+ <>
6773
+ {' '}
6774
+ <em className="not-italic bg-gradient-to-br from-[var(--local-accent)] to-orange-300 bg-clip-text text-transparent" data-jp-field="titleHighlight">
6775
+ {data.titleHighlight}
6776
+ </em>
6777
+ </>
6778
+ )}
6779
+ </h1>
6780
+
6781
+ <p className="text-xl leading-relaxed text-white/90 max-w-2xl mx-auto mb-8 jp-animate-in jp-d2" data-jp-field="description">
6782
+ {data.description}
6783
+ </p>
6784
+
6785
+ <div className="flex flex-col sm:flex-row gap-4 justify-center jp-animate-in jp-d3">
6786
+ <Button
6787
+ asChild
6788
+ variant="default"
6789
+ size="lg"
6790
+ className="bg-[var(--local-primary)] text-[var(--local-primary-foreground)] hover:opacity-90 px-8 py-4 text-base font-semibold rounded-[var(--local-radius-md)]"
6791
+ >
6792
+ <a href={data.primaryCta.href}>{data.primaryCta.label}</a>
6793
+ </Button>
6794
+
6795
+ {data.secondaryCta && (
6796
+ <Button
6797
+ asChild
6798
+ variant="outline"
6799
+ size="lg"
6800
+ className="border-white/30 text-white hover:bg-white/10 px-8 py-4 text-base font-semibold rounded-[var(--local-radius-md)]"
6801
+ >
6802
+ <a href={data.secondaryCta.href}>{data.secondaryCta.label}</a>
6803
+ </Button>
6804
+ )}
6805
+ </div>
6806
+ </div>
6807
+
6808
+ {/* Scroll Indicator */}
6809
+ <div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 text-white/70">
6810
+ <span className="text-xs uppercase tracking-widest">Scopri di più</span>
6811
+ <div className="w-px h-12 bg-gradient-to-b from-white/40 to-transparent" />
6812
+ </div>
6813
+ </section>
6814
+ );
6815
+ };
6816
+
6817
+
6818
+ END_OF_FILE_CONTENT
6819
+ echo "Creating src/components/hero-section/index.ts..."
6820
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hero-section/index.ts"
6821
+ export { HeroSection } from './View';
6822
+ export { HeroSectionSchema } from './schema';
6823
+ export type { HeroSectionData, HeroSectionSettings } from './types';
6824
+
6825
+
6826
+ END_OF_FILE_CONTENT
6827
+ echo "Creating src/components/hero-section/schema.ts..."
6828
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hero-section/schema.ts"
6829
+ import { z } from 'zod';
6830
+ import { BaseSectionData, CtaSchema, ImageSelectionSchema } from '@/lib/base-schemas';
6831
+
6832
+ export const HeroSectionSchema = BaseSectionData.extend({
6833
+ label: z.string().optional().describe('ui:text'),
6834
+ title: z.string().describe('ui:text'),
6835
+ titleHighlight: z.string().optional().describe('ui:text'),
6836
+ description: z.string().describe('ui:textarea'),
6837
+ primaryCta: CtaSchema,
6838
+ secondaryCta: CtaSchema.optional(),
6839
+ heroImage: ImageSelectionSchema,
6840
+ });
6841
+
6842
+
6843
+ END_OF_FILE_CONTENT
6844
+ echo "Creating src/components/hero-section/types.ts..."
6845
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hero-section/types.ts"
6846
+ import { z } from 'zod';
6847
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
6848
+ import { HeroSectionSchema } from './schema';
6849
+
6850
+ export type HeroSectionData = z.infer<typeof HeroSectionSchema>;
6851
+ export type HeroSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
6852
+
6853
+
6854
+ END_OF_FILE_CONTENT
6049
6855
  echo "Creating src/components/hero/RadialBackground.tsx..."
6050
6856
  cat << 'END_OF_FILE_CONTENT' > "src/components/hero/RadialBackground.tsx"
6051
6857
  import { useEffect, useRef, useState } from 'react';
@@ -6413,6 +7219,101 @@ import { HeroSchema, HeroSettingsSchema } from './schema';
6413
7219
  export type HeroData = z.infer<typeof HeroSchema>;
6414
7220
  export type HeroSettings = z.infer<typeof HeroSettingsSchema>;
6415
7221
 
7222
+ END_OF_FILE_CONTENT
7223
+ mkdir -p "src/components/hospitality-section"
7224
+ echo "Creating src/components/hospitality-section/View.tsx..."
7225
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hospitality-section/View.tsx"
7226
+ // Layout: Hero=A (SPLIT 60/40), Features=A (BENTO)
7227
+ import React from 'react';
7228
+ import { Card, CardContent } from '@/components/ui/card';
7229
+ import type { HospitalitySectionData, HospitalitySectionSettings } from './types';
7230
+
7231
+ export const HospitalitySectionComponent: React.FC<{ data: HospitalitySectionData; settings: HospitalitySectionSettings }> = ({ data }) => (
7232
+ <section
7233
+ style={{
7234
+ '--local-bg': 'var(--background)',
7235
+ '--local-text': 'var(--foreground)',
7236
+ '--local-text-muted': 'var(--muted-foreground)',
7237
+ '--local-primary': 'var(--primary)',
7238
+ '--local-accent': 'var(--accent)',
7239
+ '--local-border': 'var(--border)',
7240
+ '--local-surface': 'var(--card)',
7241
+ '--local-radius-md': 'var(--theme-radius-md)',
7242
+ '--local-radius-lg': 'var(--theme-radius-lg)',
7243
+ } as React.CSSProperties}
7244
+ className="relative z-0 py-24"
7245
+ >
7246
+ <div className="max-w-[1200px] mx-auto px-8 grid gap-10 lg:grid-cols-[0.9fr_1.1fr] items-center">
7247
+ <img src={data.image?.url} alt={data.image?.alt || ''} className="w-full h-[520px] object-cover rounded-[var(--local-radius-lg)] border border-[var(--local-border)]" />
7248
+ <div>
7249
+ {data.label && (
7250
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase text-[var(--local-accent)] mb-4" data-jp-field="label">
7251
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
7252
+ {data.label}
7253
+ </div>
7254
+ )}
7255
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)]" data-jp-field="title">{data.title}</h2>
7256
+ <p className="mt-5 max-w-xl text-[var(--local-text-muted)]" data-jp-field="description">{data.description}</p>
7257
+ <div className="grid md:grid-cols-2 gap-4 mt-8">
7258
+ {data.items.map((item, idx) => (
7259
+ <Card
7260
+ key={item.id || 'legacy-' + idx}
7261
+ className="rounded-[var(--local-radius-md)] border-[var(--local-border)] bg-[var(--local-surface)]"
7262
+ data-jp-item-id={item.id || 'legacy-' + idx}
7263
+ data-jp-item-field="items"
7264
+ >
7265
+ <CardContent className="p-5">
7266
+ <h3 className="font-display font-bold text-[1.2rem] text-[var(--local-text)]">{item.title}</h3>
7267
+ <p className="mt-2 text-sm text-[var(--local-text-muted)]">{item.body}</p>
7268
+ </CardContent>
7269
+ </Card>
7270
+ ))}
7271
+ </div>
7272
+ </div>
7273
+ </div>
7274
+ </section>
7275
+ );
7276
+
7277
+
7278
+ END_OF_FILE_CONTENT
7279
+ echo "Creating src/components/hospitality-section/index.ts..."
7280
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hospitality-section/index.ts"
7281
+ export { HospitalitySectionComponent } from './View';
7282
+ export { HospitalitySectionSchema } from './schema';
7283
+ export type { HospitalitySectionData, HospitalitySectionSettings } from './types';
7284
+
7285
+
7286
+ END_OF_FILE_CONTENT
7287
+ echo "Creating src/components/hospitality-section/schema.ts..."
7288
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hospitality-section/schema.ts"
7289
+ import { z } from 'zod';
7290
+ import { BaseSectionData, BaseArrayItem, ImageSelectionSchema } from '@/lib/base-schemas';
7291
+
7292
+ const ItemSchema = BaseArrayItem.extend({
7293
+ title: z.string().describe('ui:text'),
7294
+ body: z.string().describe('ui:textarea'),
7295
+ });
7296
+
7297
+ export const HospitalitySectionSchema = BaseSectionData.extend({
7298
+ label: z.string().optional().describe('ui:text'),
7299
+ title: z.string().describe('ui:text'),
7300
+ description: z.string().describe('ui:textarea'),
7301
+ image: ImageSelectionSchema.optional().default({ url: '', alt: '' }),
7302
+ items: z.array(ItemSchema).describe('ui:list'),
7303
+ });
7304
+
7305
+
7306
+ END_OF_FILE_CONTENT
7307
+ echo "Creating src/components/hospitality-section/types.ts..."
7308
+ cat << 'END_OF_FILE_CONTENT' > "src/components/hospitality-section/types.ts"
7309
+ import { z } from 'zod';
7310
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
7311
+ import { HospitalitySectionSchema } from './schema';
7312
+
7313
+ export type HospitalitySectionData = z.infer<typeof HospitalitySectionSchema>;
7314
+ export type HospitalitySectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
7315
+
7316
+
6416
7317
  END_OF_FILE_CONTENT
6417
7318
  mkdir -p "src/components/login"
6418
7319
  echo "Creating src/components/login/View.tsx..."
@@ -7574,6 +8475,124 @@ import { PageHeroSchema } from './schema';
7574
8475
  export type PageHeroData = z.infer<typeof PageHeroSchema>;
7575
8476
  export type PageHeroSettings = z.infer<typeof BaseSectionSettingsSchema>;
7576
8477
 
8478
+ END_OF_FILE_CONTENT
8479
+ mkdir -p "src/components/pricing-section"
8480
+ echo "Creating src/components/pricing-section/View.tsx..."
8481
+ cat << 'END_OF_FILE_CONTENT' > "src/components/pricing-section/View.tsx"
8482
+ // Layout: Hero=E (MAGAZINE), Features=C (TIMELINE)
8483
+ import React from 'react';
8484
+ import { Card, CardContent } from '@/components/ui/card';
8485
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
8486
+ import type { PricingSectionData, PricingSectionSettings } from './types';
8487
+
8488
+ export const PricingSectionComponent: React.FC<{ data: PricingSectionData; settings: PricingSectionSettings }> = ({ data }) => (
8489
+ <section
8490
+ style={{
8491
+ '--local-bg': 'var(--background)',
8492
+ '--local-text': 'var(--foreground)',
8493
+ '--local-text-muted': 'var(--muted-foreground)',
8494
+ '--local-primary': 'var(--primary)',
8495
+ '--local-accent': 'var(--accent)',
8496
+ '--local-border': 'var(--border)',
8497
+ '--local-surface': 'var(--card)',
8498
+ '--local-radius-md': 'var(--theme-radius-md)',
8499
+ '--local-radius-lg': 'var(--theme-radius-lg)',
8500
+ } as React.CSSProperties}
8501
+ className="relative z-0 py-24"
8502
+ >
8503
+ <div className="max-w-[1200px] mx-auto px-8">
8504
+ {data.label && (
8505
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase text-[var(--local-accent)] mb-4" data-jp-field="label">
8506
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
8507
+ {data.label}
8508
+ </div>
8509
+ )}
8510
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)]" data-jp-field="title">{data.title}</h2>
8511
+ <p className="mt-4 max-w-2xl text-[var(--local-text-muted)]" data-jp-field="description">{data.description}</p>
8512
+
8513
+ <Card className="mt-8 border-[var(--local-border)] bg-[var(--local-surface)] rounded-[var(--local-radius-lg)]">
8514
+ <CardContent className="p-0">
8515
+ <Table>
8516
+ <TableHeader>
8517
+ <TableRow>
8518
+ <TableHead>Treatment</TableHead>
8519
+ <TableHead>Rate</TableHead>
8520
+ <TableHead>Details</TableHead>
8521
+ </TableRow>
8522
+ </TableHeader>
8523
+ <TableBody>
8524
+ {data.rows.map((item, idx) => (
8525
+ <TableRow key={item.id || 'legacy-' + idx} data-jp-item-id={item.id || 'legacy-' + idx} data-jp-item-field="rows">
8526
+ <TableCell className="font-medium">{item.treatment}</TableCell>
8527
+ <TableCell>{item.price}</TableCell>
8528
+ <TableCell>{item.details}</TableCell>
8529
+ </TableRow>
8530
+ ))}
8531
+ </TableBody>
8532
+ </Table>
8533
+ </CardContent>
8534
+ </Card>
8535
+
8536
+ <div className="grid gap-4 md:grid-cols-3 mt-6">
8537
+ {data.notes.map((item, idx) => (
8538
+ <Card key={item.id || 'legacy-' + idx} className="border-[var(--local-border)] bg-[var(--local-surface)] rounded-[var(--local-radius-md)]" data-jp-item-id={item.id || 'legacy-' + idx} data-jp-item-field="notes">
8539
+ <CardContent className="p-5">
8540
+ <h3 className="font-display font-bold text-[1.1rem] text-[var(--local-text)]">{item.title}</h3>
8541
+ <p className="mt-2 text-sm text-[var(--local-text-muted)]">{item.body}</p>
8542
+ </CardContent>
8543
+ </Card>
8544
+ ))}
8545
+ </div>
8546
+ </div>
8547
+ </section>
8548
+ );
8549
+
8550
+
8551
+ END_OF_FILE_CONTENT
8552
+ echo "Creating src/components/pricing-section/index.ts..."
8553
+ cat << 'END_OF_FILE_CONTENT' > "src/components/pricing-section/index.ts"
8554
+ export { PricingSectionComponent } from './View';
8555
+ export { PricingSectionSchema } from './schema';
8556
+ export type { PricingSectionData, PricingSectionSettings } from './types';
8557
+
8558
+
8559
+ END_OF_FILE_CONTENT
8560
+ echo "Creating src/components/pricing-section/schema.ts..."
8561
+ cat << 'END_OF_FILE_CONTENT' > "src/components/pricing-section/schema.ts"
8562
+ import { z } from 'zod';
8563
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
8564
+
8565
+ const RowSchema = BaseArrayItem.extend({
8566
+ treatment: z.string().describe('ui:text'),
8567
+ price: z.string().describe('ui:text'),
8568
+ details: z.string().describe('ui:textarea'),
8569
+ });
8570
+
8571
+ const NoteSchema = BaseArrayItem.extend({
8572
+ title: z.string().describe('ui:text'),
8573
+ body: z.string().describe('ui:textarea'),
8574
+ });
8575
+
8576
+ export const PricingSectionSchema = BaseSectionData.extend({
8577
+ label: z.string().optional().describe('ui:text'),
8578
+ title: z.string().describe('ui:text'),
8579
+ description: z.string().describe('ui:textarea'),
8580
+ rows: z.array(RowSchema).describe('ui:list'),
8581
+ notes: z.array(NoteSchema).describe('ui:list'),
8582
+ });
8583
+
8584
+
8585
+ END_OF_FILE_CONTENT
8586
+ echo "Creating src/components/pricing-section/types.ts..."
8587
+ cat << 'END_OF_FILE_CONTENT' > "src/components/pricing-section/types.ts"
8588
+ import { z } from 'zod';
8589
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
8590
+ import { PricingSectionSchema } from './schema';
8591
+
8592
+ export type PricingSectionData = z.infer<typeof PricingSectionSchema>;
8593
+ export type PricingSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
8594
+
8595
+
7577
8596
  END_OF_FILE_CONTENT
7578
8597
  mkdir -p "src/components/problem-statement"
7579
8598
  echo "Creating src/components/problem-statement/View.tsx..."
@@ -8786,13 +9805,107 @@ cat << 'END_OF_FILE_CONTENT' > "src/components/save-drawer/saverStyle.css"
8786
9805
 
8787
9806
 
8788
9807
  END_OF_FILE_CONTENT
8789
- mkdir -p "src/components/tiptap"
8790
- echo "Creating src/components/tiptap/INTEGRATION.md..."
8791
- cat << 'END_OF_FILE_CONTENT' > "src/components/tiptap/INTEGRATION.md"
8792
- # Tiptap Editorial — Integration Guide
8793
-
8794
- How to add the `tiptap` section to a new tenant.
8795
-
9808
+ mkdir -p "src/components/testimonials-section"
9809
+ echo "Creating src/components/testimonials-section/View.tsx..."
9810
+ cat << 'END_OF_FILE_CONTENT' > "src/components/testimonials-section/View.tsx"
9811
+ // Layout: Hero=B (BENTO GRID), Features=B (HORIZONTAL SCROLL)
9812
+ import React from 'react';
9813
+ import { ScrollArea } from '@/components/ui/scroll-area';
9814
+ import { Card, CardContent } from '@/components/ui/card';
9815
+ import type { TestimonialsSectionData, TestimonialsSectionSettings } from './types';
9816
+
9817
+ export const TestimonialsSectionComponent: React.FC<{ data: TestimonialsSectionData; settings: TestimonialsSectionSettings }> = ({ data }) => (
9818
+ <section
9819
+ style={{
9820
+ '--local-bg': 'var(--card)',
9821
+ '--local-text': 'var(--foreground)',
9822
+ '--local-text-muted': 'var(--muted-foreground)',
9823
+ '--local-primary': 'var(--primary)',
9824
+ '--local-accent': 'var(--accent)',
9825
+ '--local-border': 'var(--border)',
9826
+ '--local-surface': 'var(--background)',
9827
+ '--local-radius-md': 'var(--theme-radius-md)',
9828
+ '--local-radius-lg': 'var(--theme-radius-lg)',
9829
+ } as React.CSSProperties}
9830
+ className="relative z-0 py-24 bg-[var(--local-bg)]"
9831
+ >
9832
+ <div className="max-w-[1200px] mx-auto px-8">
9833
+ {data.label && (
9834
+ <div className="jp-section-label inline-flex items-center gap-2 text-[0.72rem] font-bold uppercase text-[var(--local-accent)] mb-4" data-jp-field="label">
9835
+ <span className="w-5 h-px bg-[var(--local-primary)]" />
9836
+ {data.label}
9837
+ </div>
9838
+ )}
9839
+ <h2 className="font-display font-black text-[clamp(2rem,4.5vw,3.8rem)] leading-[1.05] tracking-tight text-[var(--local-text)]" data-jp-field="title">{data.title}</h2>
9840
+ <ScrollArea className="w-full mt-8">
9841
+ <div className="flex gap-5 pb-4">
9842
+ {data.items.map((item, idx) => (
9843
+ <Card
9844
+ key={item.id || 'legacy-' + idx}
9845
+ className="min-w-[320px] max-w-[360px] border-[var(--local-border)] bg-[var(--local-surface)] rounded-[var(--local-radius-lg)]"
9846
+ data-jp-item-id={item.id || 'legacy-' + idx}
9847
+ data-jp-item-field="items"
9848
+ >
9849
+ <CardContent className="p-6">
9850
+ <p className="text-[var(--local-text)] leading-relaxed">“{item.quote}”</p>
9851
+ <div className="mt-5 font-display text-xl text-[var(--local-text)]">{item.name}</div>
9852
+ <div className="text-sm text-[var(--local-text-muted)]">{item.meta}</div>
9853
+ </CardContent>
9854
+ </Card>
9855
+ ))}
9856
+ </div>
9857
+ </ScrollArea>
9858
+ </div>
9859
+ </section>
9860
+ );
9861
+
9862
+
9863
+ END_OF_FILE_CONTENT
9864
+ echo "Creating src/components/testimonials-section/index.ts..."
9865
+ cat << 'END_OF_FILE_CONTENT' > "src/components/testimonials-section/index.ts"
9866
+ export { TestimonialsSectionComponent } from './View';
9867
+ export { TestimonialsSectionSchema } from './schema';
9868
+ export type { TestimonialsSectionData, TestimonialsSectionSettings } from './types';
9869
+
9870
+
9871
+ END_OF_FILE_CONTENT
9872
+ echo "Creating src/components/testimonials-section/schema.ts..."
9873
+ cat << 'END_OF_FILE_CONTENT' > "src/components/testimonials-section/schema.ts"
9874
+ import { z } from 'zod';
9875
+ import { BaseSectionData, BaseArrayItem } from '@/lib/base-schemas';
9876
+
9877
+ const TestimonialSchema = BaseArrayItem.extend({
9878
+ name: z.string().describe('ui:text'),
9879
+ quote: z.string().describe('ui:textarea'),
9880
+ meta: z.string().describe('ui:text'),
9881
+ });
9882
+
9883
+ export const TestimonialsSectionSchema = BaseSectionData.extend({
9884
+ label: z.string().optional().describe('ui:text'),
9885
+ title: z.string().describe('ui:text'),
9886
+ items: z.array(TestimonialSchema).describe('ui:list'),
9887
+ });
9888
+
9889
+
9890
+ END_OF_FILE_CONTENT
9891
+ echo "Creating src/components/testimonials-section/types.ts..."
9892
+ cat << 'END_OF_FILE_CONTENT' > "src/components/testimonials-section/types.ts"
9893
+ import { z } from 'zod';
9894
+ import { BaseSectionSettingsSchema } from '@/lib/base-schemas';
9895
+ import { TestimonialsSectionSchema } from './schema';
9896
+
9897
+ export type TestimonialsSectionData = z.infer<typeof TestimonialsSectionSchema>;
9898
+ export type TestimonialsSectionSettings = z.infer<typeof BaseSectionSettingsSchema>;
9899
+
9900
+
9901
+ END_OF_FILE_CONTENT
9902
+ mkdir -p "src/components/tiptap"
9903
+ echo "Creating src/components/tiptap/INTEGRATION.md..."
9904
+ cat << 'END_OF_FILE_CONTENT' > "src/components/tiptap/INTEGRATION.md"
9905
+ # Tiptap Editorial — Integration Guide
9906
+
9907
+ How to add the `tiptap` section to a new tenant.
9908
+
8796
9909
  ---
8797
9910
 
8798
9911
  ## 1. Copy the component
@@ -9992,6 +11105,223 @@ export function OlonLogo({
9992
11105
  )
9993
11106
  }
9994
11107
 
11108
+ END_OF_FILE_CONTENT
11109
+ echo "Creating src/components/ui/accordion.tsx..."
11110
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/accordion.tsx"
11111
+ import * as React from "react"
11112
+ import { Accordion as AccordionPrimitive } from "radix-ui"
11113
+
11114
+ import { cn } from "@/lib/utils"
11115
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
11116
+
11117
+ function Accordion({
11118
+ className,
11119
+ ...props
11120
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
11121
+ return (
11122
+ <AccordionPrimitive.Root
11123
+ data-slot="accordion"
11124
+ className={cn("flex w-full flex-col", className)}
11125
+ {...props}
11126
+ />
11127
+ )
11128
+ }
11129
+
11130
+ function AccordionItem({
11131
+ className,
11132
+ ...props
11133
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
11134
+ return (
11135
+ <AccordionPrimitive.Item
11136
+ data-slot="accordion-item"
11137
+ className={cn("not-last:border-b", className)}
11138
+ {...props}
11139
+ />
11140
+ )
11141
+ }
11142
+
11143
+ function AccordionTrigger({
11144
+ className,
11145
+ children,
11146
+ ...props
11147
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
11148
+ return (
11149
+ <AccordionPrimitive.Header className="flex">
11150
+ <AccordionPrimitive.Trigger
11151
+ data-slot="accordion-trigger"
11152
+ className={cn(
11153
+ "group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
11154
+ className
11155
+ )}
11156
+ {...props}
11157
+ >
11158
+ {children}
11159
+ <ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
11160
+ <ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
11161
+ </AccordionPrimitive.Trigger>
11162
+ </AccordionPrimitive.Header>
11163
+ )
11164
+ }
11165
+
11166
+ function AccordionContent({
11167
+ className,
11168
+ children,
11169
+ ...props
11170
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
11171
+ return (
11172
+ <AccordionPrimitive.Content
11173
+ data-slot="accordion-content"
11174
+ className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
11175
+ {...props}
11176
+ >
11177
+ <div
11178
+ className={cn(
11179
+ "h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
11180
+ className
11181
+ )}
11182
+ >
11183
+ {children}
11184
+ </div>
11185
+ </AccordionPrimitive.Content>
11186
+ )
11187
+ }
11188
+
11189
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
11190
+
11191
+
11192
+ END_OF_FILE_CONTENT
11193
+ echo "Creating src/components/ui/aspect-ratio.tsx..."
11194
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/aspect-ratio.tsx"
11195
+ "use client"
11196
+
11197
+ import { AspectRatio as AspectRatioPrimitive } from "radix-ui"
11198
+
11199
+ function AspectRatio({
11200
+ ...props
11201
+ }: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
11202
+ return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />
11203
+ }
11204
+
11205
+ export { AspectRatio }
11206
+
11207
+
11208
+ END_OF_FILE_CONTENT
11209
+ echo "Creating src/components/ui/avatar.tsx..."
11210
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/avatar.tsx"
11211
+ "use client"
11212
+
11213
+ import * as React from "react"
11214
+ import { Avatar as AvatarPrimitive } from "radix-ui"
11215
+
11216
+ import { cn } from "@/lib/utils"
11217
+
11218
+ function Avatar({
11219
+ className,
11220
+ size = "default",
11221
+ ...props
11222
+ }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
11223
+ size?: "default" | "sm" | "lg"
11224
+ }) {
11225
+ return (
11226
+ <AvatarPrimitive.Root
11227
+ data-slot="avatar"
11228
+ data-size={size}
11229
+ className={cn(
11230
+ "group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
11231
+ className
11232
+ )}
11233
+ {...props}
11234
+ />
11235
+ )
11236
+ }
11237
+
11238
+ function AvatarImage({
11239
+ className,
11240
+ ...props
11241
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
11242
+ return (
11243
+ <AvatarPrimitive.Image
11244
+ data-slot="avatar-image"
11245
+ className={cn(
11246
+ "aspect-square size-full rounded-full object-cover",
11247
+ className
11248
+ )}
11249
+ {...props}
11250
+ />
11251
+ )
11252
+ }
11253
+
11254
+ function AvatarFallback({
11255
+ className,
11256
+ ...props
11257
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
11258
+ return (
11259
+ <AvatarPrimitive.Fallback
11260
+ data-slot="avatar-fallback"
11261
+ className={cn(
11262
+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
11263
+ className
11264
+ )}
11265
+ {...props}
11266
+ />
11267
+ )
11268
+ }
11269
+
11270
+ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
11271
+ return (
11272
+ <span
11273
+ data-slot="avatar-badge"
11274
+ className={cn(
11275
+ "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
11276
+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
11277
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
11278
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
11279
+ className
11280
+ )}
11281
+ {...props}
11282
+ />
11283
+ )
11284
+ }
11285
+
11286
+ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
11287
+ return (
11288
+ <div
11289
+ data-slot="avatar-group"
11290
+ className={cn(
11291
+ "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
11292
+ className
11293
+ )}
11294
+ {...props}
11295
+ />
11296
+ )
11297
+ }
11298
+
11299
+ function AvatarGroupCount({
11300
+ className,
11301
+ ...props
11302
+ }: React.ComponentProps<"div">) {
11303
+ return (
11304
+ <div
11305
+ data-slot="avatar-group-count"
11306
+ className={cn(
11307
+ "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
11308
+ className
11309
+ )}
11310
+ {...props}
11311
+ />
11312
+ )
11313
+ }
11314
+
11315
+ export {
11316
+ Avatar,
11317
+ AvatarImage,
11318
+ AvatarFallback,
11319
+ AvatarGroup,
11320
+ AvatarGroupCount,
11321
+ AvatarBadge,
11322
+ }
11323
+
11324
+
9995
11325
  END_OF_FILE_CONTENT
9996
11326
  echo "Creating src/components/ui/badge.tsx..."
9997
11327
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/badge.tsx"
@@ -10025,6 +11355,133 @@ function Badge({ className, variant, ...props }: BadgeProps) {
10025
11355
 
10026
11356
  export { Badge, badgeVariants }
10027
11357
 
11358
+ END_OF_FILE_CONTENT
11359
+ echo "Creating src/components/ui/breadcrumb.tsx..."
11360
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/breadcrumb.tsx"
11361
+ import * as React from "react"
11362
+ import { Slot } from "radix-ui"
11363
+
11364
+ import { cn } from "@/lib/utils"
11365
+ import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
11366
+
11367
+ function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
11368
+ return (
11369
+ <nav
11370
+ aria-label="breadcrumb"
11371
+ data-slot="breadcrumb"
11372
+ className={cn(className)}
11373
+ {...props}
11374
+ />
11375
+ )
11376
+ }
11377
+
11378
+ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
11379
+ return (
11380
+ <ol
11381
+ data-slot="breadcrumb-list"
11382
+ className={cn(
11383
+ "flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground",
11384
+ className
11385
+ )}
11386
+ {...props}
11387
+ />
11388
+ )
11389
+ }
11390
+
11391
+ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
11392
+ return (
11393
+ <li
11394
+ data-slot="breadcrumb-item"
11395
+ className={cn("inline-flex items-center gap-1", className)}
11396
+ {...props}
11397
+ />
11398
+ )
11399
+ }
11400
+
11401
+ function BreadcrumbLink({
11402
+ asChild,
11403
+ className,
11404
+ ...props
11405
+ }: React.ComponentProps<"a"> & {
11406
+ asChild?: boolean
11407
+ }) {
11408
+ const Comp = asChild ? Slot.Root : "a"
11409
+
11410
+ return (
11411
+ <Comp
11412
+ data-slot="breadcrumb-link"
11413
+ className={cn("transition-colors hover:text-foreground", className)}
11414
+ {...props}
11415
+ />
11416
+ )
11417
+ }
11418
+
11419
+ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
11420
+ return (
11421
+ <span
11422
+ data-slot="breadcrumb-page"
11423
+ role="link"
11424
+ aria-disabled="true"
11425
+ aria-current="page"
11426
+ className={cn("font-normal text-foreground", className)}
11427
+ {...props}
11428
+ />
11429
+ )
11430
+ }
11431
+
11432
+ function BreadcrumbSeparator({
11433
+ children,
11434
+ className,
11435
+ ...props
11436
+ }: React.ComponentProps<"li">) {
11437
+ return (
11438
+ <li
11439
+ data-slot="breadcrumb-separator"
11440
+ role="presentation"
11441
+ aria-hidden="true"
11442
+ className={cn("[&>svg]:size-3.5", className)}
11443
+ {...props}
11444
+ >
11445
+ {children ?? (
11446
+ <ChevronRightIcon />
11447
+ )}
11448
+ </li>
11449
+ )
11450
+ }
11451
+
11452
+ function BreadcrumbEllipsis({
11453
+ className,
11454
+ ...props
11455
+ }: React.ComponentProps<"span">) {
11456
+ return (
11457
+ <span
11458
+ data-slot="breadcrumb-ellipsis"
11459
+ role="presentation"
11460
+ aria-hidden="true"
11461
+ className={cn(
11462
+ "flex size-5 items-center justify-center [&>svg]:size-4",
11463
+ className
11464
+ )}
11465
+ {...props}
11466
+ >
11467
+ <MoreHorizontalIcon
11468
+ />
11469
+ <span className="sr-only">More</span>
11470
+ </span>
11471
+ )
11472
+ }
11473
+
11474
+ export {
11475
+ Breadcrumb,
11476
+ BreadcrumbList,
11477
+ BreadcrumbItem,
11478
+ BreadcrumbLink,
11479
+ BreadcrumbPage,
11480
+ BreadcrumbSeparator,
11481
+ BreadcrumbEllipsis,
11482
+ }
11483
+
11484
+
10028
11485
  END_OF_FILE_CONTENT
10029
11486
  echo "Creating src/components/ui/button.tsx..."
10030
11487
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/button.tsx"
@@ -10182,44 +11639,536 @@ export { Checkbox }
10182
11639
 
10183
11640
 
10184
11641
  END_OF_FILE_CONTENT
10185
- echo "Creating src/components/ui/input.tsx..."
10186
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/input.tsx"
10187
- import * as React from 'react'
10188
- import { cn } from '@/lib/utils'
11642
+ echo "Creating src/components/ui/dialog.tsx..."
11643
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/dialog.tsx"
11644
+ import * as React from "react"
11645
+ import { Dialog as DialogPrimitive } from "radix-ui"
10189
11646
 
10190
- export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
11647
+ import { cn } from "@/lib/utils"
11648
+ import { Button } from "@/components/ui/button"
11649
+ import { XIcon } from "lucide-react"
10191
11650
 
10192
- const Input = React.forwardRef<HTMLInputElement, InputProps>(
10193
- ({ className, type, ...props }, ref) => {
10194
- return (
10195
- <input
10196
- type={type}
11651
+ function Dialog({
11652
+ ...props
11653
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
11654
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
11655
+ }
11656
+
11657
+ function DialogTrigger({
11658
+ ...props
11659
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
11660
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
11661
+ }
11662
+
11663
+ function DialogPortal({
11664
+ ...props
11665
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
11666
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
11667
+ }
11668
+
11669
+ function DialogClose({
11670
+ ...props
11671
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
11672
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
11673
+ }
11674
+
11675
+ function DialogOverlay({
11676
+ className,
11677
+ ...props
11678
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
11679
+ return (
11680
+ <DialogPrimitive.Overlay
11681
+ data-slot="dialog-overlay"
11682
+ className={cn(
11683
+ "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
11684
+ className
11685
+ )}
11686
+ {...props}
11687
+ />
11688
+ )
11689
+ }
11690
+
11691
+ function DialogContent({
11692
+ className,
11693
+ children,
11694
+ showCloseButton = true,
11695
+ ...props
11696
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
11697
+ showCloseButton?: boolean
11698
+ }) {
11699
+ return (
11700
+ <DialogPortal>
11701
+ <DialogOverlay />
11702
+ <DialogPrimitive.Content
11703
+ data-slot="dialog-content"
10197
11704
  className={cn(
10198
- 'flex h-9 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground',
10199
- 'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary',
10200
- 'disabled:cursor-not-allowed disabled:opacity-50',
10201
- 'transition-colors duration-150',
11705
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
10202
11706
  className
10203
11707
  )}
10204
- ref={ref}
11708
+ {...props}
11709
+ >
11710
+ {children}
11711
+ {showCloseButton && (
11712
+ <DialogPrimitive.Close data-slot="dialog-close" asChild>
11713
+ <Button
11714
+ variant="ghost"
11715
+ className="absolute top-2 right-2"
11716
+ size="sm"
11717
+ >
11718
+ <XIcon
11719
+ />
11720
+ <span className="sr-only">Close</span>
11721
+ </Button>
11722
+ </DialogPrimitive.Close>
11723
+ )}
11724
+ </DialogPrimitive.Content>
11725
+ </DialogPortal>
11726
+ )
11727
+ }
11728
+
11729
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
11730
+ return (
11731
+ <div
11732
+ data-slot="dialog-header"
11733
+ className={cn("flex flex-col gap-2", className)}
11734
+ {...props}
11735
+ />
11736
+ )
11737
+ }
11738
+
11739
+ function DialogFooter({
11740
+ className,
11741
+ showCloseButton = false,
11742
+ children,
11743
+ ...props
11744
+ }: React.ComponentProps<"div"> & {
11745
+ showCloseButton?: boolean
11746
+ }) {
11747
+ return (
11748
+ <div
11749
+ data-slot="dialog-footer"
11750
+ className={cn(
11751
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
11752
+ className
11753
+ )}
11754
+ {...props}
11755
+ >
11756
+ {children}
11757
+ {showCloseButton && (
11758
+ <DialogPrimitive.Close asChild>
11759
+ <Button variant="outline">Close</Button>
11760
+ </DialogPrimitive.Close>
11761
+ )}
11762
+ </div>
11763
+ )
11764
+ }
11765
+
11766
+ function DialogTitle({
11767
+ className,
11768
+ ...props
11769
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
11770
+ return (
11771
+ <DialogPrimitive.Title
11772
+ data-slot="dialog-title"
11773
+ className={cn(
11774
+ "text-base leading-none font-medium",
11775
+ className
11776
+ )}
11777
+ {...props}
11778
+ />
11779
+ )
11780
+ }
11781
+
11782
+ function DialogDescription({
11783
+ className,
11784
+ ...props
11785
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
11786
+ return (
11787
+ <DialogPrimitive.Description
11788
+ data-slot="dialog-description"
11789
+ className={cn(
11790
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
11791
+ className
11792
+ )}
11793
+ {...props}
11794
+ />
11795
+ )
11796
+ }
11797
+
11798
+ export {
11799
+ Dialog,
11800
+ DialogClose,
11801
+ DialogContent,
11802
+ DialogDescription,
11803
+ DialogFooter,
11804
+ DialogHeader,
11805
+ DialogOverlay,
11806
+ DialogPortal,
11807
+ DialogTitle,
11808
+ DialogTrigger,
11809
+ }
11810
+
11811
+
11812
+ END_OF_FILE_CONTENT
11813
+ echo "Creating src/components/ui/dropdown-menu.tsx..."
11814
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/dropdown-menu.tsx"
11815
+ import * as React from "react"
11816
+ import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
11817
+
11818
+ import { cn } from "@/lib/utils"
11819
+ import { CheckIcon, ChevronRightIcon } from "lucide-react"
11820
+
11821
+ function DropdownMenu({
11822
+ ...props
11823
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
11824
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
11825
+ }
11826
+
11827
+ function DropdownMenuPortal({
11828
+ ...props
11829
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
11830
+ return (
11831
+ <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
11832
+ )
11833
+ }
11834
+
11835
+ function DropdownMenuTrigger({
11836
+ ...props
11837
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
11838
+ return (
11839
+ <DropdownMenuPrimitive.Trigger
11840
+ data-slot="dropdown-menu-trigger"
11841
+ {...props}
11842
+ />
11843
+ )
11844
+ }
11845
+
11846
+ function DropdownMenuContent({
11847
+ className,
11848
+ align = "start",
11849
+ sideOffset = 4,
11850
+ ...props
11851
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
11852
+ return (
11853
+ <DropdownMenuPrimitive.Portal>
11854
+ <DropdownMenuPrimitive.Content
11855
+ data-slot="dropdown-menu-content"
11856
+ sideOffset={sideOffset}
11857
+ align={align}
11858
+ className={cn("z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:overflow-hidden data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
10205
11859
  {...props}
10206
11860
  />
10207
- )
10208
- }
10209
- )
10210
- Input.displayName = 'Input'
11861
+ </DropdownMenuPrimitive.Portal>
11862
+ )
11863
+ }
11864
+
11865
+ function DropdownMenuGroup({
11866
+ ...props
11867
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
11868
+ return (
11869
+ <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
11870
+ )
11871
+ }
11872
+
11873
+ function DropdownMenuItem({
11874
+ className,
11875
+ inset,
11876
+ variant = "default",
11877
+ ...props
11878
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
11879
+ inset?: boolean
11880
+ variant?: "default" | "destructive"
11881
+ }) {
11882
+ return (
11883
+ <DropdownMenuPrimitive.Item
11884
+ data-slot="dropdown-menu-item"
11885
+ data-inset={inset}
11886
+ data-variant={variant}
11887
+ className={cn(
11888
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
11889
+ className
11890
+ )}
11891
+ {...props}
11892
+ />
11893
+ )
11894
+ }
11895
+
11896
+ function DropdownMenuCheckboxItem({
11897
+ className,
11898
+ children,
11899
+ checked,
11900
+ inset,
11901
+ ...props
11902
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & {
11903
+ inset?: boolean
11904
+ }) {
11905
+ return (
11906
+ <DropdownMenuPrimitive.CheckboxItem
11907
+ data-slot="dropdown-menu-checkbox-item"
11908
+ data-inset={inset}
11909
+ className={cn(
11910
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
11911
+ className
11912
+ )}
11913
+ checked={checked}
11914
+ {...props}
11915
+ >
11916
+ <span
11917
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
11918
+ data-slot="dropdown-menu-checkbox-item-indicator"
11919
+ >
11920
+ <DropdownMenuPrimitive.ItemIndicator>
11921
+ <CheckIcon
11922
+ />
11923
+ </DropdownMenuPrimitive.ItemIndicator>
11924
+ </span>
11925
+ {children}
11926
+ </DropdownMenuPrimitive.CheckboxItem>
11927
+ )
11928
+ }
11929
+
11930
+ function DropdownMenuRadioGroup({
11931
+ ...props
11932
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
11933
+ return (
11934
+ <DropdownMenuPrimitive.RadioGroup
11935
+ data-slot="dropdown-menu-radio-group"
11936
+ {...props}
11937
+ />
11938
+ )
11939
+ }
11940
+
11941
+ function DropdownMenuRadioItem({
11942
+ className,
11943
+ children,
11944
+ inset,
11945
+ ...props
11946
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & {
11947
+ inset?: boolean
11948
+ }) {
11949
+ return (
11950
+ <DropdownMenuPrimitive.RadioItem
11951
+ data-slot="dropdown-menu-radio-item"
11952
+ data-inset={inset}
11953
+ className={cn(
11954
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
11955
+ className
11956
+ )}
11957
+ {...props}
11958
+ >
11959
+ <span
11960
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
11961
+ data-slot="dropdown-menu-radio-item-indicator"
11962
+ >
11963
+ <DropdownMenuPrimitive.ItemIndicator>
11964
+ <CheckIcon
11965
+ />
11966
+ </DropdownMenuPrimitive.ItemIndicator>
11967
+ </span>
11968
+ {children}
11969
+ </DropdownMenuPrimitive.RadioItem>
11970
+ )
11971
+ }
11972
+
11973
+ function DropdownMenuLabel({
11974
+ className,
11975
+ inset,
11976
+ ...props
11977
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
11978
+ inset?: boolean
11979
+ }) {
11980
+ return (
11981
+ <DropdownMenuPrimitive.Label
11982
+ data-slot="dropdown-menu-label"
11983
+ data-inset={inset}
11984
+ className={cn(
11985
+ "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
11986
+ className
11987
+ )}
11988
+ {...props}
11989
+ />
11990
+ )
11991
+ }
11992
+
11993
+ function DropdownMenuSeparator({
11994
+ className,
11995
+ ...props
11996
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
11997
+ return (
11998
+ <DropdownMenuPrimitive.Separator
11999
+ data-slot="dropdown-menu-separator"
12000
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
12001
+ {...props}
12002
+ />
12003
+ )
12004
+ }
12005
+
12006
+ function DropdownMenuShortcut({
12007
+ className,
12008
+ ...props
12009
+ }: React.ComponentProps<"span">) {
12010
+ return (
12011
+ <span
12012
+ data-slot="dropdown-menu-shortcut"
12013
+ className={cn(
12014
+ "ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
12015
+ className
12016
+ )}
12017
+ {...props}
12018
+ />
12019
+ )
12020
+ }
12021
+
12022
+ function DropdownMenuSub({
12023
+ ...props
12024
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
12025
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
12026
+ }
12027
+
12028
+ function DropdownMenuSubTrigger({
12029
+ className,
12030
+ inset,
12031
+ children,
12032
+ ...props
12033
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
12034
+ inset?: boolean
12035
+ }) {
12036
+ return (
12037
+ <DropdownMenuPrimitive.SubTrigger
12038
+ data-slot="dropdown-menu-sub-trigger"
12039
+ data-inset={inset}
12040
+ className={cn(
12041
+ "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
12042
+ className
12043
+ )}
12044
+ {...props}
12045
+ >
12046
+ {children}
12047
+ <ChevronRightIcon className="ml-auto" />
12048
+ </DropdownMenuPrimitive.SubTrigger>
12049
+ )
12050
+ }
12051
+
12052
+ function DropdownMenuSubContent({
12053
+ className,
12054
+ ...props
12055
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
12056
+ return (
12057
+ <DropdownMenuPrimitive.SubContent
12058
+ data-slot="dropdown-menu-sub-content"
12059
+ className={cn("z-50 min-w-[96px] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
12060
+ {...props}
12061
+ />
12062
+ )
12063
+ }
12064
+
12065
+ export {
12066
+ DropdownMenu,
12067
+ DropdownMenuPortal,
12068
+ DropdownMenuTrigger,
12069
+ DropdownMenuContent,
12070
+ DropdownMenuGroup,
12071
+ DropdownMenuLabel,
12072
+ DropdownMenuItem,
12073
+ DropdownMenuCheckboxItem,
12074
+ DropdownMenuRadioGroup,
12075
+ DropdownMenuRadioItem,
12076
+ DropdownMenuSeparator,
12077
+ DropdownMenuShortcut,
12078
+ DropdownMenuSub,
12079
+ DropdownMenuSubTrigger,
12080
+ DropdownMenuSubContent,
12081
+ }
10211
12082
 
10212
- export { Input }
10213
12083
 
10214
12084
  END_OF_FILE_CONTENT
10215
- echo "Creating src/components/ui/label.tsx..."
10216
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/label.tsx"
10217
- import * as React from 'react'
10218
- import { cn } from '@/lib/utils'
12085
+ echo "Creating src/components/ui/hover-card.tsx..."
12086
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/hover-card.tsx"
12087
+ "use client"
10219
12088
 
10220
- const Label = React.forwardRef<
10221
- HTMLLabelElement,
10222
- React.LabelHTMLAttributes<HTMLLabelElement>
12089
+ import * as React from "react"
12090
+ import { HoverCard as HoverCardPrimitive } from "radix-ui"
12091
+
12092
+ import { cn } from "@/lib/utils"
12093
+
12094
+ function HoverCard({
12095
+ ...props
12096
+ }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
12097
+ return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
12098
+ }
12099
+
12100
+ function HoverCardTrigger({
12101
+ ...props
12102
+ }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
12103
+ return (
12104
+ <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
12105
+ )
12106
+ }
12107
+
12108
+ function HoverCardContent({
12109
+ className,
12110
+ align = "center",
12111
+ sideOffset = 4,
12112
+ ...props
12113
+ }: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
12114
+ return (
12115
+ <HoverCardPrimitive.Portal data-slot="hover-card-portal">
12116
+ <HoverCardPrimitive.Content
12117
+ data-slot="hover-card-content"
12118
+ align={align}
12119
+ sideOffset={sideOffset}
12120
+ className={cn(
12121
+ "z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
12122
+ className
12123
+ )}
12124
+ {...props}
12125
+ />
12126
+ </HoverCardPrimitive.Portal>
12127
+ )
12128
+ }
12129
+
12130
+ export { HoverCard, HoverCardTrigger, HoverCardContent }
12131
+
12132
+
12133
+ END_OF_FILE_CONTENT
12134
+ echo "Creating src/components/ui/input.tsx..."
12135
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/input.tsx"
12136
+ import * as React from 'react'
12137
+ import { cn } from '@/lib/utils'
12138
+
12139
+ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
12140
+
12141
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
12142
+ ({ className, type, ...props }, ref) => {
12143
+ return (
12144
+ <input
12145
+ type={type}
12146
+ className={cn(
12147
+ 'flex h-9 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground',
12148
+ 'focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary',
12149
+ 'disabled:cursor-not-allowed disabled:opacity-50',
12150
+ 'transition-colors duration-150',
12151
+ className
12152
+ )}
12153
+ ref={ref}
12154
+ {...props}
12155
+ />
12156
+ )
12157
+ }
12158
+ )
12159
+ Input.displayName = 'Input'
12160
+
12161
+ export { Input }
12162
+
12163
+ END_OF_FILE_CONTENT
12164
+ echo "Creating src/components/ui/label.tsx..."
12165
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/label.tsx"
12166
+ import * as React from 'react'
12167
+ import { cn } from '@/lib/utils'
12168
+
12169
+ const Label = React.forwardRef<
12170
+ HTMLLabelElement,
12171
+ React.LabelHTMLAttributes<HTMLLabelElement>
10223
12172
  >(({ className, ...props }, ref) => (
10224
12173
  <label
10225
12174
  ref={ref}
@@ -10229,11 +12178,465 @@ const Label = React.forwardRef<
10229
12178
  ))
10230
12179
  Label.displayName = 'Label'
10231
12180
 
10232
- export { Label }
12181
+ export { Label }
12182
+
12183
+ END_OF_FILE_CONTENT
12184
+ echo "Creating src/components/ui/navigation-menu.tsx..."
12185
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/navigation-menu.tsx"
12186
+ import * as React from "react"
12187
+ import { cva } from "class-variance-authority"
12188
+ import { NavigationMenu as NavigationMenuPrimitive } from "radix-ui"
12189
+
12190
+ import { cn } from "@/lib/utils"
12191
+ import { ChevronDownIcon } from "lucide-react"
12192
+
12193
+ function NavigationMenu({
12194
+ className,
12195
+ children,
12196
+ viewport = true,
12197
+ ...props
12198
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
12199
+ viewport?: boolean
12200
+ }) {
12201
+ return (
12202
+ <NavigationMenuPrimitive.Root
12203
+ data-slot="navigation-menu"
12204
+ data-viewport={viewport}
12205
+ className={cn(
12206
+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
12207
+ className
12208
+ )}
12209
+ {...props}
12210
+ >
12211
+ {children}
12212
+ {viewport && <NavigationMenuViewport />}
12213
+ </NavigationMenuPrimitive.Root>
12214
+ )
12215
+ }
12216
+
12217
+ function NavigationMenuList({
12218
+ className,
12219
+ ...props
12220
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
12221
+ return (
12222
+ <NavigationMenuPrimitive.List
12223
+ data-slot="navigation-menu-list"
12224
+ className={cn(
12225
+ "group flex flex-1 list-none items-center justify-center gap-0",
12226
+ className
12227
+ )}
12228
+ {...props}
12229
+ />
12230
+ )
12231
+ }
12232
+
12233
+ function NavigationMenuItem({
12234
+ className,
12235
+ ...props
12236
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
12237
+ return (
12238
+ <NavigationMenuPrimitive.Item
12239
+ data-slot="navigation-menu-item"
12240
+ className={cn("relative", className)}
12241
+ {...props}
12242
+ />
12243
+ )
12244
+ }
12245
+
12246
+ const navigationMenuTriggerStyle = cva(
12247
+ "group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center rounded-lg px-2.5 py-1.5 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted data-open:bg-muted/50 data-open:hover:bg-muted data-open:focus:bg-muted"
12248
+ )
12249
+
12250
+ function NavigationMenuTrigger({
12251
+ className,
12252
+ children,
12253
+ ...props
12254
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
12255
+ return (
12256
+ <NavigationMenuPrimitive.Trigger
12257
+ data-slot="navigation-menu-trigger"
12258
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
12259
+ {...props}
12260
+ >
12261
+ {children}{" "}
12262
+ <ChevronDownIcon className="relative top-px ml-1 size-3 transition duration-300 group-data-popup-open/navigation-menu-trigger:rotate-180 group-data-open/navigation-menu-trigger:rotate-180" aria-hidden="true" />
12263
+ </NavigationMenuPrimitive.Trigger>
12264
+ )
12265
+ }
12266
+
12267
+ function NavigationMenuContent({
12268
+ className,
12269
+ ...props
12270
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
12271
+ return (
12272
+ <NavigationMenuPrimitive.Content
12273
+ data-slot="navigation-menu-content"
12274
+ className={cn(
12275
+ "top-0 left-0 w-full p-1 ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none md:absolute md:w-auto group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95",
12276
+ className
12277
+ )}
12278
+ {...props}
12279
+ />
12280
+ )
12281
+ }
12282
+
12283
+ function NavigationMenuViewport({
12284
+ className,
12285
+ ...props
12286
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
12287
+ return (
12288
+ <div
12289
+ className={cn(
12290
+ "absolute top-full left-0 isolate z-50 flex justify-center"
12291
+ )}
12292
+ >
12293
+ <NavigationMenuPrimitive.Viewport
12294
+ data-slot="navigation-menu-viewport"
12295
+ className={cn(
12296
+ "origin-top-center relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-lg bg-popover text-popover-foreground shadow ring-1 ring-foreground/10 duration-100 md:w-(--radix-navigation-menu-viewport-width) data-open:animate-in data-open:zoom-in-90 data-closed:animate-out data-closed:zoom-out-90",
12297
+ className
12298
+ )}
12299
+ {...props}
12300
+ />
12301
+ </div>
12302
+ )
12303
+ }
12304
+
12305
+ function NavigationMenuLink({
12306
+ className,
12307
+ ...props
12308
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
12309
+ return (
12310
+ <NavigationMenuPrimitive.Link
12311
+ data-slot="navigation-menu-link"
12312
+ className={cn(
12313
+ "flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md data-active:bg-muted/50 data-active:hover:bg-muted data-active:focus:bg-muted [&_svg:not([class*='size-'])]:size-4",
12314
+ className
12315
+ )}
12316
+ {...props}
12317
+ />
12318
+ )
12319
+ }
12320
+
12321
+ function NavigationMenuIndicator({
12322
+ className,
12323
+ ...props
12324
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
12325
+ return (
12326
+ <NavigationMenuPrimitive.Indicator
12327
+ data-slot="navigation-menu-indicator"
12328
+ className={cn(
12329
+ "top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in",
12330
+ className
12331
+ )}
12332
+ {...props}
12333
+ >
12334
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
12335
+ </NavigationMenuPrimitive.Indicator>
12336
+ )
12337
+ }
12338
+
12339
+ export {
12340
+ NavigationMenu,
12341
+ NavigationMenuList,
12342
+ NavigationMenuItem,
12343
+ NavigationMenuContent,
12344
+ NavigationMenuTrigger,
12345
+ NavigationMenuLink,
12346
+ NavigationMenuIndicator,
12347
+ NavigationMenuViewport,
12348
+ navigationMenuTriggerStyle,
12349
+ }
12350
+
12351
+
12352
+ END_OF_FILE_CONTENT
12353
+ echo "Creating src/components/ui/progress.tsx..."
12354
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/progress.tsx"
12355
+ import * as React from "react"
12356
+ import { Progress as ProgressPrimitive } from "radix-ui"
12357
+
12358
+ import { cn } from "@/lib/utils"
12359
+
12360
+ function Progress({
12361
+ className,
12362
+ value,
12363
+ ...props
12364
+ }: React.ComponentProps<typeof ProgressPrimitive.Root>) {
12365
+ return (
12366
+ <ProgressPrimitive.Root
12367
+ data-slot="progress"
12368
+ className={cn(
12369
+ "relative flex h-1 w-full items-center overflow-x-hidden rounded-full bg-muted",
12370
+ className
12371
+ )}
12372
+ {...props}
12373
+ >
12374
+ <ProgressPrimitive.Indicator
12375
+ data-slot="progress-indicator"
12376
+ className="size-full flex-1 bg-primary transition-all"
12377
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
12378
+ />
12379
+ </ProgressPrimitive.Root>
12380
+ )
12381
+ }
12382
+
12383
+ export { Progress }
12384
+
12385
+
12386
+ END_OF_FILE_CONTENT
12387
+ echo "Creating src/components/ui/scroll-area.tsx..."
12388
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/scroll-area.tsx"
12389
+ import * as React from "react"
12390
+ import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"
12391
+
12392
+ import { cn } from "@/lib/utils"
12393
+
12394
+ function ScrollArea({
12395
+ className,
12396
+ children,
12397
+ ...props
12398
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
12399
+ return (
12400
+ <ScrollAreaPrimitive.Root
12401
+ data-slot="scroll-area"
12402
+ className={cn("relative", className)}
12403
+ {...props}
12404
+ >
12405
+ <ScrollAreaPrimitive.Viewport
12406
+ data-slot="scroll-area-viewport"
12407
+ className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
12408
+ >
12409
+ {children}
12410
+ </ScrollAreaPrimitive.Viewport>
12411
+ <ScrollBar />
12412
+ <ScrollAreaPrimitive.Corner />
12413
+ </ScrollAreaPrimitive.Root>
12414
+ )
12415
+ }
12416
+
12417
+ function ScrollBar({
12418
+ className,
12419
+ orientation = "vertical",
12420
+ ...props
12421
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
12422
+ return (
12423
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
12424
+ data-slot="scroll-area-scrollbar"
12425
+ data-orientation={orientation}
12426
+ orientation={orientation}
12427
+ className={cn(
12428
+ "flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
12429
+ className
12430
+ )}
12431
+ {...props}
12432
+ >
12433
+ <ScrollAreaPrimitive.ScrollAreaThumb
12434
+ data-slot="scroll-area-thumb"
12435
+ className="relative flex-1 rounded-full bg-border"
12436
+ />
12437
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
12438
+ )
12439
+ }
12440
+
12441
+ export { ScrollArea, ScrollBar }
12442
+
12443
+
12444
+ END_OF_FILE_CONTENT
12445
+ echo "Creating src/components/ui/select.tsx..."
12446
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.tsx"
12447
+ import * as React from "react"
12448
+ import { Select as SelectPrimitive } from "radix-ui"
12449
+
12450
+ import { cn } from "@/lib/utils"
12451
+ import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
12452
+
12453
+ function Select({
12454
+ ...props
12455
+ }: React.ComponentProps<typeof SelectPrimitive.Root>) {
12456
+ return <SelectPrimitive.Root data-slot="select" {...props} />
12457
+ }
12458
+
12459
+ function SelectGroup({
12460
+ className,
12461
+ ...props
12462
+ }: React.ComponentProps<typeof SelectPrimitive.Group>) {
12463
+ return (
12464
+ <SelectPrimitive.Group
12465
+ data-slot="select-group"
12466
+ className={cn("scroll-my-1 p-1", className)}
12467
+ {...props}
12468
+ />
12469
+ )
12470
+ }
12471
+
12472
+ function SelectValue({
12473
+ ...props
12474
+ }: React.ComponentProps<typeof SelectPrimitive.Value>) {
12475
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />
12476
+ }
12477
+
12478
+ function SelectTrigger({
12479
+ className,
12480
+ size = "default",
12481
+ children,
12482
+ ...props
12483
+ }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
12484
+ size?: "sm" | "default"
12485
+ }) {
12486
+ return (
12487
+ <SelectPrimitive.Trigger
12488
+ data-slot="select-trigger"
12489
+ data-size={size}
12490
+ className={cn(
12491
+ "border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-full items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
12492
+ className
12493
+ )}
12494
+ {...props}
12495
+ >
12496
+ {children}
12497
+ <SelectPrimitive.Icon asChild>
12498
+ <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
12499
+ </SelectPrimitive.Icon>
12500
+ </SelectPrimitive.Trigger>
12501
+ )
12502
+ }
12503
+
12504
+ function SelectContent({
12505
+ className,
12506
+ children,
12507
+ position = "popper",
12508
+ align = "center",
12509
+ ...props
12510
+ }: React.ComponentProps<typeof SelectPrimitive.Content>) {
12511
+ return (
12512
+ <SelectPrimitive.Portal>
12513
+ <SelectPrimitive.Content
12514
+ data-slot="select-content"
12515
+ data-align-trigger={position === "item-aligned"}
12516
+ className={cn(
12517
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-lg shadow-md ring-1 duration-100 relative z-[110] max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none",
12518
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
12519
+ className
12520
+ )}
12521
+ position={position}
12522
+ align={align}
12523
+ {...props}
12524
+ >
12525
+ <SelectScrollUpButton />
12526
+ <SelectPrimitive.Viewport
12527
+ data-position={position}
12528
+ className={cn(
12529
+ "p-1",
12530
+ position === "popper" && "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
12531
+ )}
12532
+ >
12533
+ {children}
12534
+ </SelectPrimitive.Viewport>
12535
+ <SelectScrollDownButton />
12536
+ </SelectPrimitive.Content>
12537
+ </SelectPrimitive.Portal>
12538
+ )
12539
+ }
12540
+
12541
+ function SelectLabel({
12542
+ className,
12543
+ ...props
12544
+ }: React.ComponentProps<typeof SelectPrimitive.Label>) {
12545
+ return (
12546
+ <SelectPrimitive.Label
12547
+ data-slot="select-label"
12548
+ className={cn("text-muted-foreground px-1.5 py-1 text-xs", className)}
12549
+ {...props}
12550
+ />
12551
+ )
12552
+ }
12553
+
12554
+ function SelectItem({
12555
+ className,
12556
+ children,
12557
+ ...props
12558
+ }: React.ComponentProps<typeof SelectPrimitive.Item>) {
12559
+ return (
12560
+ <SelectPrimitive.Item
12561
+ data-slot="select-item"
12562
+ className={cn(
12563
+ "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
12564
+ className
12565
+ )}
12566
+ {...props}
12567
+ >
12568
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
12569
+ <SelectPrimitive.ItemIndicator>
12570
+ <CheckIcon className="pointer-events-none" />
12571
+ </SelectPrimitive.ItemIndicator>
12572
+ </span>
12573
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
12574
+ </SelectPrimitive.Item>
12575
+ )
12576
+ }
12577
+
12578
+ function SelectSeparator({
12579
+ className,
12580
+ ...props
12581
+ }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
12582
+ return (
12583
+ <SelectPrimitive.Separator
12584
+ data-slot="select-separator"
12585
+ className={cn("bg-border -mx-1 my-1 h-px pointer-events-none", className)}
12586
+ {...props}
12587
+ />
12588
+ )
12589
+ }
12590
+
12591
+ function SelectScrollUpButton({
12592
+ className,
12593
+ ...props
12594
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
12595
+ return (
12596
+ <SelectPrimitive.ScrollUpButton
12597
+ data-slot="select-scroll-up-button"
12598
+ className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
12599
+ {...props}
12600
+ >
12601
+ <ChevronUpIcon />
12602
+ </SelectPrimitive.ScrollUpButton>
12603
+ )
12604
+ }
12605
+
12606
+ function SelectScrollDownButton({
12607
+ className,
12608
+ ...props
12609
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
12610
+ return (
12611
+ <SelectPrimitive.ScrollDownButton
12612
+ data-slot="select-scroll-down-button"
12613
+ className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
12614
+ {...props}
12615
+ >
12616
+ <ChevronDownIcon />
12617
+ </SelectPrimitive.ScrollDownButton>
12618
+ )
12619
+ }
12620
+
12621
+ export {
12622
+ Select,
12623
+ SelectContent,
12624
+ SelectGroup,
12625
+ SelectItem,
12626
+ SelectLabel,
12627
+ SelectScrollDownButton,
12628
+ SelectScrollUpButton,
12629
+ SelectSeparator,
12630
+ SelectTrigger,
12631
+ SelectValue,
12632
+ }
12633
+
12634
+
12635
+
10233
12636
 
10234
12637
  END_OF_FILE_CONTENT
10235
- echo "Creating src/components/ui/select.tsx..."
10236
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.tsx"
12638
+ echo "Creating src/components/ui/select.txt..."
12639
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.txt"
10237
12640
  import * as React from "react"
10238
12641
  import { Select as SelectPrimitive } from "radix-ui"
10239
12642
 
@@ -10425,266 +12828,681 @@ export {
10425
12828
 
10426
12829
 
10427
12830
  END_OF_FILE_CONTENT
10428
- echo "Creating src/components/ui/select.txt..."
10429
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.txt"
12831
+ echo "Creating src/components/ui/separator.tsx..."
12832
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/separator.tsx"
12833
+ import * as React from 'react'
12834
+ import { cn } from '@/lib/utils'
12835
+
12836
+ const Separator = React.forwardRef<
12837
+ HTMLDivElement,
12838
+ React.HTMLAttributes<HTMLDivElement> & { orientation?: 'horizontal' | 'vertical' }
12839
+ >(({ className, orientation = 'horizontal', ...props }, ref) => (
12840
+ <div
12841
+ ref={ref}
12842
+ className={cn(
12843
+ 'shrink-0 bg-border',
12844
+ orientation === 'horizontal' ? 'h-[0.5px] w-full' : 'h-full w-[0.5px]',
12845
+ className
12846
+ )}
12847
+ {...props}
12848
+ />
12849
+ ))
12850
+ Separator.displayName = 'Separator'
12851
+
12852
+ export { Separator }
12853
+
12854
+ END_OF_FILE_CONTENT
12855
+ echo "Creating src/components/ui/sheet.tsx..."
12856
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/sheet.tsx"
12857
+ "use client"
12858
+
10430
12859
  import * as React from "react"
10431
- import { Select as SelectPrimitive } from "radix-ui"
12860
+ import { Dialog as SheetPrimitive } from "radix-ui"
10432
12861
 
10433
12862
  import { cn } from "@/lib/utils"
10434
- import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
12863
+ import { Button } from "@/components/ui/button"
12864
+ import { XIcon } from "lucide-react"
10435
12865
 
10436
- function Select({
12866
+ function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
12867
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />
12868
+ }
12869
+
12870
+ function SheetTrigger({
10437
12871
  ...props
10438
- }: React.ComponentProps<typeof SelectPrimitive.Root>) {
10439
- return <SelectPrimitive.Root data-slot="select" {...props} />
12872
+ }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
12873
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
10440
12874
  }
10441
12875
 
10442
- function SelectGroup({
12876
+ function SheetClose({
12877
+ ...props
12878
+ }: React.ComponentProps<typeof SheetPrimitive.Close>) {
12879
+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
12880
+ }
12881
+
12882
+ function SheetPortal({
12883
+ ...props
12884
+ }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
12885
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
12886
+ }
12887
+
12888
+ function SheetOverlay({
10443
12889
  className,
10444
12890
  ...props
10445
- }: React.ComponentProps<typeof SelectPrimitive.Group>) {
12891
+ }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
10446
12892
  return (
10447
- <SelectPrimitive.Group
10448
- data-slot="select-group"
10449
- className={cn("scroll-my-1 p-1", className)}
12893
+ <SheetPrimitive.Overlay
12894
+ data-slot="sheet-overlay"
12895
+ className={cn(
12896
+ "fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
12897
+ className
12898
+ )}
10450
12899
  {...props}
10451
12900
  />
10452
12901
  )
10453
12902
  }
10454
12903
 
10455
- function SelectValue({
12904
+ function SheetContent({
12905
+ className,
12906
+ children,
12907
+ side = "right",
12908
+ showCloseButton = true,
10456
12909
  ...props
10457
- }: React.ComponentProps<typeof SelectPrimitive.Value>) {
10458
- return <SelectPrimitive.Value data-slot="select-value" {...props} />
12910
+ }: React.ComponentProps<typeof SheetPrimitive.Content> & {
12911
+ side?: "top" | "right" | "bottom" | "left"
12912
+ showCloseButton?: boolean
12913
+ }) {
12914
+ return (
12915
+ <SheetPortal>
12916
+ <SheetOverlay />
12917
+ <SheetPrimitive.Content
12918
+ data-slot="sheet-content"
12919
+ data-side={side}
12920
+ className={cn(
12921
+ "fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:animate-out data-closed:fade-out-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=right]:data-closed:slide-out-to-right-10 data-[side=top]:data-closed:slide-out-to-top-10",
12922
+ className
12923
+ )}
12924
+ {...props}
12925
+ >
12926
+ {children}
12927
+ {showCloseButton && (
12928
+ <SheetPrimitive.Close data-slot="sheet-close" asChild>
12929
+ <Button
12930
+ variant="ghost"
12931
+ className="absolute top-3 right-3"
12932
+ size="sm"
12933
+ >
12934
+ <XIcon
12935
+ />
12936
+ <span className="sr-only">Close</span>
12937
+ </Button>
12938
+ </SheetPrimitive.Close>
12939
+ )}
12940
+ </SheetPrimitive.Content>
12941
+ </SheetPortal>
12942
+ )
10459
12943
  }
10460
12944
 
10461
- function SelectTrigger({
12945
+ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
12946
+ return (
12947
+ <div
12948
+ data-slot="sheet-header"
12949
+ className={cn("flex flex-col gap-0.5 p-4", className)}
12950
+ {...props}
12951
+ />
12952
+ )
12953
+ }
12954
+
12955
+ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
12956
+ return (
12957
+ <div
12958
+ data-slot="sheet-footer"
12959
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
12960
+ {...props}
12961
+ />
12962
+ )
12963
+ }
12964
+
12965
+ function SheetTitle({
12966
+ className,
12967
+ ...props
12968
+ }: React.ComponentProps<typeof SheetPrimitive.Title>) {
12969
+ return (
12970
+ <SheetPrimitive.Title
12971
+ data-slot="sheet-title"
12972
+ className={cn(
12973
+ "text-base font-medium text-foreground",
12974
+ className
12975
+ )}
12976
+ {...props}
12977
+ />
12978
+ )
12979
+ }
12980
+
12981
+ function SheetDescription({
12982
+ className,
12983
+ ...props
12984
+ }: React.ComponentProps<typeof SheetPrimitive.Description>) {
12985
+ return (
12986
+ <SheetPrimitive.Description
12987
+ data-slot="sheet-description"
12988
+ className={cn("text-sm text-muted-foreground", className)}
12989
+ {...props}
12990
+ />
12991
+ )
12992
+ }
12993
+
12994
+ export {
12995
+ Sheet,
12996
+ SheetTrigger,
12997
+ SheetClose,
12998
+ SheetContent,
12999
+ SheetHeader,
13000
+ SheetFooter,
13001
+ SheetTitle,
13002
+ SheetDescription,
13003
+ }
13004
+
13005
+
13006
+ END_OF_FILE_CONTENT
13007
+ echo "Creating src/components/ui/skeleton.tsx..."
13008
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/skeleton.tsx"
13009
+ import { cn } from '@/lib/utils';
13010
+ import type { HTMLAttributes } from 'react';
13011
+
13012
+ function Skeleton({
13013
+ className,
13014
+ ...props
13015
+ }: HTMLAttributes<HTMLDivElement>) {
13016
+ return (
13017
+ <div
13018
+ className={cn('animate-pulse rounded-md bg-muted', className)}
13019
+ {...props}
13020
+ />
13021
+ );
13022
+ }
13023
+
13024
+ export { Skeleton };
13025
+
13026
+ END_OF_FILE_CONTENT
13027
+ echo "Creating src/components/ui/switch.tsx..."
13028
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/switch.tsx"
13029
+ import * as React from "react"
13030
+ import { Switch as SwitchPrimitive } from "radix-ui"
13031
+
13032
+ import { cn } from "@/lib/utils"
13033
+
13034
+ function Switch({
10462
13035
  className,
10463
13036
  size = "default",
10464
- children,
10465
13037
  ...props
10466
- }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
13038
+ }: React.ComponentProps<typeof SwitchPrimitive.Root> & {
10467
13039
  size?: "sm" | "default"
10468
13040
  }) {
10469
13041
  return (
10470
- <SelectPrimitive.Trigger
10471
- data-slot="select-trigger"
13042
+ <SwitchPrimitive.Root
13043
+ data-slot="switch"
10472
13044
  data-size={size}
10473
13045
  className={cn(
10474
- "border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-full items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
13046
+ "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
10475
13047
  className
10476
13048
  )}
10477
13049
  {...props}
10478
13050
  >
10479
- {children}
10480
- <SelectPrimitive.Icon asChild>
10481
- <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
10482
- </SelectPrimitive.Icon>
10483
- </SelectPrimitive.Trigger>
13051
+ <SwitchPrimitive.Thumb
13052
+ data-slot="switch-thumb"
13053
+ className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
13054
+ />
13055
+ </SwitchPrimitive.Root>
10484
13056
  )
10485
13057
  }
10486
13058
 
10487
- function SelectContent({
13059
+ export { Switch }
13060
+
13061
+
13062
+ END_OF_FILE_CONTENT
13063
+ echo "Creating src/components/ui/table.tsx..."
13064
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/table.tsx"
13065
+ import * as React from "react"
13066
+
13067
+ import { cn } from "@/lib/utils"
13068
+
13069
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
13070
+ return (
13071
+ <div
13072
+ data-slot="table-container"
13073
+ className="relative w-full overflow-x-auto"
13074
+ >
13075
+ <table
13076
+ data-slot="table"
13077
+ className={cn("w-full caption-bottom text-sm", className)}
13078
+ {...props}
13079
+ />
13080
+ </div>
13081
+ )
13082
+ }
13083
+
13084
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
13085
+ return (
13086
+ <thead
13087
+ data-slot="table-header"
13088
+ className={cn("[&_tr]:border-b", className)}
13089
+ {...props}
13090
+ />
13091
+ )
13092
+ }
13093
+
13094
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
13095
+ return (
13096
+ <tbody
13097
+ data-slot="table-body"
13098
+ className={cn("[&_tr:last-child]:border-0", className)}
13099
+ {...props}
13100
+ />
13101
+ )
13102
+ }
13103
+
13104
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
13105
+ return (
13106
+ <tfoot
13107
+ data-slot="table-footer"
13108
+ className={cn(
13109
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
13110
+ className
13111
+ )}
13112
+ {...props}
13113
+ />
13114
+ )
13115
+ }
13116
+
13117
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
13118
+ return (
13119
+ <tr
13120
+ data-slot="table-row"
13121
+ className={cn(
13122
+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
13123
+ className
13124
+ )}
13125
+ {...props}
13126
+ />
13127
+ )
13128
+ }
13129
+
13130
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
13131
+ return (
13132
+ <th
13133
+ data-slot="table-head"
13134
+ className={cn(
13135
+ "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
13136
+ className
13137
+ )}
13138
+ {...props}
13139
+ />
13140
+ )
13141
+ }
13142
+
13143
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
13144
+ return (
13145
+ <td
13146
+ data-slot="table-cell"
13147
+ className={cn(
13148
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
13149
+ className
13150
+ )}
13151
+ {...props}
13152
+ />
13153
+ )
13154
+ }
13155
+
13156
+ function TableCaption({
10488
13157
  className,
10489
- children,
10490
- position = "popper",
10491
- align = "center",
10492
13158
  ...props
10493
- }: React.ComponentProps<typeof SelectPrimitive.Content>) {
13159
+ }: React.ComponentProps<"caption">) {
10494
13160
  return (
10495
- <SelectPrimitive.Portal>
10496
- <SelectPrimitive.Content
10497
- data-slot="select-content"
10498
- data-align-trigger={position === "item-aligned"}
10499
- className={cn(
10500
- "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-lg shadow-md ring-1 duration-100 relative z-[110] max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none",
10501
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
10502
- className
10503
- )}
10504
- position={position}
10505
- align={align}
10506
- {...props}
10507
- >
10508
- <SelectScrollUpButton />
10509
- <SelectPrimitive.Viewport
10510
- data-position={position}
10511
- className={cn(
10512
- "p-1",
10513
- position === "popper" && "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
10514
- )}
10515
- >
10516
- {children}
10517
- </SelectPrimitive.Viewport>
10518
- <SelectScrollDownButton />
10519
- </SelectPrimitive.Content>
10520
- </SelectPrimitive.Portal>
13161
+ <caption
13162
+ data-slot="table-caption"
13163
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
13164
+ {...props}
13165
+ />
13166
+ )
13167
+ }
13168
+
13169
+ export {
13170
+ Table,
13171
+ TableHeader,
13172
+ TableBody,
13173
+ TableFooter,
13174
+ TableHead,
13175
+ TableRow,
13176
+ TableCell,
13177
+ TableCaption,
13178
+ }
13179
+
13180
+
13181
+ END_OF_FILE_CONTENT
13182
+ echo "Creating src/components/ui/tabs.tsx..."
13183
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/tabs.tsx"
13184
+ "use client"
13185
+
13186
+ import * as React from "react"
13187
+ import { cva, type VariantProps } from "class-variance-authority"
13188
+ import { Tabs as TabsPrimitive } from "radix-ui"
13189
+
13190
+ import { cn } from "@/lib/utils"
13191
+
13192
+ function Tabs({
13193
+ className,
13194
+ orientation = "horizontal",
13195
+ ...props
13196
+ }: React.ComponentProps<typeof TabsPrimitive.Root>) {
13197
+ return (
13198
+ <TabsPrimitive.Root
13199
+ data-slot="tabs"
13200
+ data-orientation={orientation}
13201
+ className={cn(
13202
+ "group/tabs flex gap-2 data-horizontal:flex-col",
13203
+ className
13204
+ )}
13205
+ {...props}
13206
+ />
10521
13207
  )
10522
13208
  }
10523
13209
 
10524
- function SelectLabel({
13210
+ const tabsListVariants = cva(
13211
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
13212
+ {
13213
+ variants: {
13214
+ variant: {
13215
+ default: "bg-muted",
13216
+ line: "gap-1 bg-transparent",
13217
+ },
13218
+ },
13219
+ defaultVariants: {
13220
+ variant: "default",
13221
+ },
13222
+ }
13223
+ )
13224
+
13225
+ function TabsList({
10525
13226
  className,
13227
+ variant = "default",
10526
13228
  ...props
10527
- }: React.ComponentProps<typeof SelectPrimitive.Label>) {
13229
+ }: React.ComponentProps<typeof TabsPrimitive.List> &
13230
+ VariantProps<typeof tabsListVariants>) {
10528
13231
  return (
10529
- <SelectPrimitive.Label
10530
- data-slot="select-label"
10531
- className={cn("text-muted-foreground px-1.5 py-1 text-xs", className)}
13232
+ <TabsPrimitive.List
13233
+ data-slot="tabs-list"
13234
+ data-variant={variant}
13235
+ className={cn(tabsListVariants({ variant }), className)}
10532
13236
  {...props}
10533
13237
  />
10534
13238
  )
10535
13239
  }
10536
13240
 
10537
- function SelectItem({
13241
+ function TabsTrigger({
10538
13242
  className,
10539
- children,
10540
13243
  ...props
10541
- }: React.ComponentProps<typeof SelectPrimitive.Item>) {
13244
+ }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
10542
13245
  return (
10543
- <SelectPrimitive.Item
10544
- data-slot="select-item"
13246
+ <TabsPrimitive.Trigger
13247
+ data-slot="tabs-trigger"
10545
13248
  className={cn(
10546
- "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
13249
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
13250
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
13251
+ "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
13252
+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
10547
13253
  className
10548
13254
  )}
10549
13255
  {...props}
10550
- >
10551
- <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
10552
- <SelectPrimitive.ItemIndicator>
10553
- <CheckIcon className="pointer-events-none" />
10554
- </SelectPrimitive.ItemIndicator>
10555
- </span>
10556
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
10557
- </SelectPrimitive.Item>
13256
+ />
10558
13257
  )
10559
13258
  }
10560
13259
 
10561
- function SelectSeparator({
13260
+ function TabsContent({
10562
13261
  className,
10563
13262
  ...props
10564
- }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
13263
+ }: React.ComponentProps<typeof TabsPrimitive.Content>) {
10565
13264
  return (
10566
- <SelectPrimitive.Separator
10567
- data-slot="select-separator"
10568
- className={cn("bg-border -mx-1 my-1 h-px pointer-events-none", className)}
13265
+ <TabsPrimitive.Content
13266
+ data-slot="tabs-content"
13267
+ className={cn("flex-1 text-sm outline-none", className)}
10569
13268
  {...props}
10570
13269
  />
10571
13270
  )
10572
13271
  }
10573
13272
 
10574
- function SelectScrollUpButton({
13273
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
13274
+
13275
+
13276
+ END_OF_FILE_CONTENT
13277
+ echo "Creating src/components/ui/textarea.tsx..."
13278
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/textarea.tsx"
13279
+ import * as React from "react"
13280
+
13281
+ import { cn } from "@/lib/utils"
13282
+
13283
+ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
13284
+ return (
13285
+ <textarea
13286
+ data-slot="textarea"
13287
+ className={cn(
13288
+ "border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors focus-visible:ring-3 aria-invalid:ring-3 md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
13289
+ className
13290
+ )}
13291
+ {...props}
13292
+ />
13293
+ )
13294
+ }
13295
+
13296
+ export { Textarea }
13297
+
13298
+
13299
+
13300
+
13301
+
13302
+ END_OF_FILE_CONTENT
13303
+ echo "Creating src/components/ui/toggle-group.tsx..."
13304
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/toggle-group.tsx"
13305
+ import * as React from "react"
13306
+ import { type VariantProps } from "class-variance-authority"
13307
+ import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
13308
+
13309
+ import { cn } from "@/lib/utils"
13310
+ import { toggleVariants } from "@/components/ui/toggle"
13311
+
13312
+ const ToggleGroupContext = React.createContext<
13313
+ VariantProps<typeof toggleVariants> & {
13314
+ spacing?: number
13315
+ orientation?: "horizontal" | "vertical"
13316
+ }
13317
+ >({
13318
+ size: "default",
13319
+ variant: "default",
13320
+ spacing: 0,
13321
+ orientation: "horizontal",
13322
+ })
13323
+
13324
+ function ToggleGroup({
10575
13325
  className,
13326
+ variant,
13327
+ size,
13328
+ spacing = 0,
13329
+ orientation = "horizontal",
13330
+ children,
10576
13331
  ...props
10577
- }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
13332
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
13333
+ VariantProps<typeof toggleVariants> & {
13334
+ spacing?: number
13335
+ orientation?: "horizontal" | "vertical"
13336
+ }) {
10578
13337
  return (
10579
- <SelectPrimitive.ScrollUpButton
10580
- data-slot="select-scroll-up-button"
10581
- className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
13338
+ <ToggleGroupPrimitive.Root
13339
+ data-slot="toggle-group"
13340
+ data-variant={variant}
13341
+ data-size={size}
13342
+ data-spacing={spacing}
13343
+ data-orientation={orientation}
13344
+ style={{ "--gap": spacing } as React.CSSProperties}
13345
+ className={cn(
13346
+ "group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch",
13347
+ className
13348
+ )}
10582
13349
  {...props}
10583
13350
  >
10584
- <ChevronUpIcon />
10585
- </SelectPrimitive.ScrollUpButton>
13351
+ <ToggleGroupContext.Provider
13352
+ value={{ variant, size, spacing, orientation }}
13353
+ >
13354
+ {children}
13355
+ </ToggleGroupContext.Provider>
13356
+ </ToggleGroupPrimitive.Root>
10586
13357
  )
10587
13358
  }
10588
13359
 
10589
- function SelectScrollDownButton({
13360
+ function ToggleGroupItem({
10590
13361
  className,
13362
+ children,
13363
+ variant = "default",
13364
+ size = "default",
10591
13365
  ...props
10592
- }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
13366
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
13367
+ VariantProps<typeof toggleVariants>) {
13368
+ const context = React.useContext(ToggleGroupContext)
13369
+
10593
13370
  return (
10594
- <SelectPrimitive.ScrollDownButton
10595
- data-slot="select-scroll-down-button"
10596
- className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
13371
+ <ToggleGroupPrimitive.Item
13372
+ data-slot="toggle-group-item"
13373
+ data-variant={context.variant || variant}
13374
+ data-size={context.size || size}
13375
+ data-spacing={context.spacing}
13376
+ className={cn(
13377
+ "shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
13378
+ toggleVariants({
13379
+ variant: context.variant || variant,
13380
+ size: context.size || size,
13381
+ }),
13382
+ className
13383
+ )}
10597
13384
  {...props}
10598
13385
  >
10599
- <ChevronDownIcon />
10600
- </SelectPrimitive.ScrollDownButton>
13386
+ {children}
13387
+ </ToggleGroupPrimitive.Item>
10601
13388
  )
10602
13389
  }
10603
13390
 
10604
- export {
10605
- Select,
10606
- SelectContent,
10607
- SelectGroup,
10608
- SelectItem,
10609
- SelectLabel,
10610
- SelectScrollDownButton,
10611
- SelectScrollUpButton,
10612
- SelectSeparator,
10613
- SelectTrigger,
10614
- SelectValue,
10615
- }
13391
+ export { ToggleGroup, ToggleGroupItem }
10616
13392
 
10617
13393
 
13394
+ END_OF_FILE_CONTENT
13395
+ echo "Creating src/components/ui/toggle.tsx..."
13396
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/toggle.tsx"
13397
+ "use client"
10618
13398
 
13399
+ import * as React from "react"
13400
+ import { cva, type VariantProps } from "class-variance-authority"
13401
+ import { Toggle as TogglePrimitive } from "radix-ui"
10619
13402
 
10620
- END_OF_FILE_CONTENT
10621
- echo "Creating src/components/ui/separator.tsx..."
10622
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/separator.tsx"
10623
- import * as React from 'react'
10624
- import { cn } from '@/lib/utils'
13403
+ import { cn } from "@/lib/utils"
10625
13404
 
10626
- const Separator = React.forwardRef<
10627
- HTMLDivElement,
10628
- React.HTMLAttributes<HTMLDivElement> & { orientation?: 'horizontal' | 'vertical' }
10629
- >(({ className, orientation = 'horizontal', ...props }, ref) => (
10630
- <div
10631
- ref={ref}
10632
- className={cn(
10633
- 'shrink-0 bg-border',
10634
- orientation === 'horizontal' ? 'h-[0.5px] w-full' : 'h-full w-[0.5px]',
10635
- className
10636
- )}
10637
- {...props}
10638
- />
10639
- ))
10640
- Separator.displayName = 'Separator'
13405
+ const toggleVariants = cva(
13406
+ "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
13407
+ {
13408
+ variants: {
13409
+ variant: {
13410
+ default: "bg-transparent",
13411
+ outline: "border border-input bg-transparent hover:bg-muted",
13412
+ },
13413
+ size: {
13414
+ default:
13415
+ "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
13416
+ sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
13417
+ lg: "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
13418
+ },
13419
+ },
13420
+ defaultVariants: {
13421
+ variant: "default",
13422
+ size: "default",
13423
+ },
13424
+ }
13425
+ )
10641
13426
 
10642
- export { Separator }
13427
+ function Toggle({
13428
+ className,
13429
+ variant = "default",
13430
+ size = "default",
13431
+ ...props
13432
+ }: React.ComponentProps<typeof TogglePrimitive.Root> &
13433
+ VariantProps<typeof toggleVariants>) {
13434
+ return (
13435
+ <TogglePrimitive.Root
13436
+ data-slot="toggle"
13437
+ className={cn(toggleVariants({ variant, size, className }))}
13438
+ {...props}
13439
+ />
13440
+ )
13441
+ }
13442
+
13443
+ export { Toggle, toggleVariants }
10643
13444
 
10644
- END_OF_FILE_CONTENT
10645
- echo "Creating src/components/ui/skeleton.tsx..."
10646
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/skeleton.tsx"
10647
- import { cn } from '@/lib/utils';
10648
- import type { HTMLAttributes } from 'react';
10649
-
10650
- function Skeleton({
10651
- className,
10652
- ...props
10653
- }: HTMLAttributes<HTMLDivElement>) {
10654
- return (
10655
- <div
10656
- className={cn('animate-pulse rounded-md bg-muted', className)}
10657
- {...props}
10658
- />
10659
- );
10660
- }
10661
-
10662
- export { Skeleton };
10663
13445
 
10664
13446
  END_OF_FILE_CONTENT
10665
- echo "Creating src/components/ui/textarea.tsx..."
10666
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/textarea.tsx"
13447
+ echo "Creating src/components/ui/tooltip.tsx..."
13448
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/tooltip.tsx"
13449
+ "use client"
13450
+
10667
13451
  import * as React from "react"
13452
+ import { Tooltip as TooltipPrimitive } from "radix-ui"
10668
13453
 
10669
13454
  import { cn } from "@/lib/utils"
10670
13455
 
10671
- function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
13456
+ function TooltipProvider({
13457
+ delayDuration = 0,
13458
+ ...props
13459
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
10672
13460
  return (
10673
- <textarea
10674
- data-slot="textarea"
10675
- className={cn(
10676
- "border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors focus-visible:ring-3 aria-invalid:ring-3 md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
10677
- className
10678
- )}
13461
+ <TooltipPrimitive.Provider
13462
+ data-slot="tooltip-provider"
13463
+ delayDuration={delayDuration}
10679
13464
  {...props}
10680
13465
  />
10681
13466
  )
10682
13467
  }
10683
13468
 
10684
- export { Textarea }
13469
+ function Tooltip({
13470
+ ...props
13471
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
13472
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
13473
+ }
10685
13474
 
13475
+ function TooltipTrigger({
13476
+ ...props
13477
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
13478
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
13479
+ }
10686
13480
 
13481
+ function TooltipContent({
13482
+ className,
13483
+ sideOffset = 0,
13484
+ children,
13485
+ ...props
13486
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
13487
+ return (
13488
+ <TooltipPrimitive.Portal>
13489
+ <TooltipPrimitive.Content
13490
+ data-slot="tooltip-content"
13491
+ sideOffset={sideOffset}
13492
+ className={cn(
13493
+ "z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
13494
+ className
13495
+ )}
13496
+ {...props}
13497
+ >
13498
+ {children}
13499
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
13500
+ </TooltipPrimitive.Content>
13501
+ </TooltipPrimitive.Portal>
13502
+ )
13503
+ }
10687
13504
 
13505
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
10688
13506
 
10689
13507
 
10690
13508
  END_OF_FILE_CONTENT
@@ -10694,10 +13512,9 @@ echo "Creating src/data/config/menu.json..."
10694
13512
  cat << 'END_OF_FILE_CONTENT' > "src/data/config/menu.json"
10695
13513
  {
10696
13514
  "main": [
10697
- {
13515
+ {
10698
13516
  "label": "Why",
10699
13517
  "href": "#Why"
10700
-
10701
13518
  },
10702
13519
  {
10703
13520
  "label": "Architecture",
@@ -10711,14 +13528,13 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/menu.json"
10711
13528
  "label": "Get started",
10712
13529
  "href": "#Getstarted"
10713
13530
  },
10714
- {
13531
+ {
10715
13532
  "label": "GitHub",
10716
13533
  "href": "https://github.com/olonjs/core"
10717
13534
  }
10718
13535
  ]
10719
13536
  }
10720
13537
  END_OF_FILE_CONTENT
10721
- # SKIP: src/data/config/menu.json:Zone.Identifier is binary and cannot be embedded as text.
10722
13538
  echo "Creating src/data/config/menu_example_for_schema.json..."
10723
13539
  cat << 'END_OF_FILE_CONTENT' > "src/data/config/menu_example_for_schema.json"
10724
13540
  {
@@ -10834,7 +13650,6 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/site.json"
10834
13650
  }
10835
13651
  }
10836
13652
  END_OF_FILE_CONTENT
10837
- # SKIP: src/data/config/site.json:Zone.Identifier is binary and cannot be embedded as text.
10838
13653
  echo "Creating src/data/config/theme.json..."
10839
13654
  cat << 'END_OF_FILE_CONTENT' > "src/data/config/theme.json"
10840
13655
  {
@@ -11002,7 +13817,6 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/theme.json"
11002
13817
  }
11003
13818
  }
11004
13819
  END_OF_FILE_CONTENT
11005
- # SKIP: src/data/config/theme.json:Zone.Identifier is binary and cannot be embedded as text.
11006
13820
  mkdir -p "src/data/pages"
11007
13821
  echo "Creating src/data/pages/design-system.json..."
11008
13822
  cat << 'END_OF_FILE_CONTENT' > "src/data/pages/design-system.json"
@@ -11839,26 +14653,13 @@ cat << 'END_OF_FILE_CONTENT' > "src/entry-ssg.tsx"
11839
14653
  import { renderToString } from 'react-dom/server';
11840
14654
  import { StaticRouter } from 'react-router-dom/server';
11841
14655
  import { ConfigProvider, PageRenderer, StudioProvider, resolveRuntimeConfig } from '@olonjs/core';
11842
- import type { JsonPagesConfig, MenuConfig, PageConfig, SiteConfig, ThemeConfig } from '@/types';
14656
+ import type { JsonPagesConfig, PageConfig, SiteConfig, ThemeConfig } from '@/types';
11843
14657
  import { ThemeProvider } from '@/components/ThemeProvider';
11844
14658
  import { ComponentRegistry } from '@/lib/ComponentRegistry';
11845
14659
  import { SECTION_SCHEMAS } from '@/lib/schemas';
11846
- import { getFilePages } from '@/lib/getFilePages';
11847
- import siteData from '@/data/config/site.json';
11848
- import menuData from '@/data/config/menu.json';
11849
- import themeData from '@/data/config/theme.json';
14660
+ import { menuConfig, pages, refDocuments, siteConfig, themeConfig } from '@/runtime';
11850
14661
  import tenantCss from '@/index.css?inline';
11851
14662
 
11852
- const siteConfig = siteData as unknown as SiteConfig;
11853
- const menuConfig: MenuConfig = { main: [] };
11854
- const themeConfig = themeData as unknown as ThemeConfig;
11855
- const pages = getFilePages();
11856
- const refDocuments = {
11857
- 'menu.json': menuData,
11858
- 'config/menu.json': menuData,
11859
- 'src/data/config/menu.json': menuData,
11860
- } satisfies NonNullable<JsonPagesConfig['refDocuments']>;
11861
-
11862
14663
  function isRecord(value: unknown): value is Record<string, unknown> {
11863
14664
  return typeof value === 'object' && value !== null && !Array.isArray(value);
11864
14665
  }
@@ -13539,6 +16340,38 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
13539
16340
 
13540
16341
 
13541
16342
 
16343
+ END_OF_FILE_CONTENT
16344
+ echo "Creating src/runtime.ts..."
16345
+ cat << 'END_OF_FILE_CONTENT' > "src/runtime.ts"
16346
+ import type { JsonPagesConfig, MenuConfig, PageConfig, SiteConfig, ThemeConfig } from '@/types';
16347
+ import { SECTION_SCHEMAS } from '@/lib/schemas';
16348
+ import { getFilePages } from '@/lib/getFilePages';
16349
+ import siteData from '@/data/config/site.json';
16350
+ import menuData from '@/data/config/menu.json';
16351
+ import themeData from '@/data/config/theme.json';
16352
+
16353
+ export const siteConfig = siteData as unknown as SiteConfig;
16354
+ export const themeConfig = themeData as unknown as ThemeConfig;
16355
+ export const menuConfig = menuData as unknown as MenuConfig;
16356
+ export const pages = getFilePages();
16357
+ export const refDocuments = {
16358
+ 'menu.json': menuConfig,
16359
+ 'config/menu.json': menuConfig,
16360
+ 'src/data/config/menu.json': menuConfig,
16361
+ } satisfies NonNullable<JsonPagesConfig['refDocuments']>;
16362
+
16363
+ export function getWebMcpBuildState(): {
16364
+ pages: Record<string, PageConfig>;
16365
+ schemas: JsonPagesConfig['schemas'];
16366
+ siteConfig: SiteConfig;
16367
+ } {
16368
+ return {
16369
+ pages,
16370
+ schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
16371
+ siteConfig,
16372
+ };
16373
+ }
16374
+
13542
16375
  END_OF_FILE_CONTENT
13543
16376
  mkdir -p "src/types"
13544
16377
  echo "Creating src/types.ts..."
@@ -13736,7 +16569,7 @@ import react from '@vitejs/plugin-react';
13736
16569
  import tailwindcss from '@tailwindcss/vite';
13737
16570
  import path from 'path';
13738
16571
  import fs from 'fs';
13739
- import { fileURLToPath, pathToFileURL } from 'url';
16572
+ import { fileURLToPath } from 'url';
13740
16573
 
13741
16574
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13742
16575
 
@@ -13793,8 +16626,7 @@ function normalizeManifestSlug(raw) {
13793
16626
  }
13794
16627
 
13795
16628
  async function loadWebMcpBuilders() {
13796
- const corePkgPath = path.dirname(fileURLToPath(import.meta.resolve('@olonjs/core/package.json')));
13797
- const moduleUrl = pathToFileURL(path.resolve(corePkgPath, 'src', 'lib', 'webmcp-contracts.mjs')).href;
16629
+ const moduleUrl = import.meta.resolve('@olonjs/core');
13798
16630
  return import(moduleUrl);
13799
16631
  }
13800
16632
  export default defineConfig({
@@ -13809,9 +16641,10 @@ export default defineConfig({
13809
16641
  const isPageJsonRequest = isTenantPageJsonRequest(req, pathname);
13810
16642
 
13811
16643
  const handleManifestRequest = async () => {
13812
- const { buildPageContract, buildPageManifest, buildSiteManifest, buildLlmsTxt } = await loadWebMcpBuilders();
13813
- const ssrEntry = await server.ssrLoadModule('/src/entry-ssg.tsx');
13814
- const buildState = ssrEntry.getWebMcpBuildState();
16644
+ const core = await loadWebMcpBuilders();
16645
+ const { buildPageContract, buildPageManifest, buildSiteManifest, buildLlmsTxt } = core.webmcp;
16646
+ const runtime = await server.ssrLoadModule('/src/runtime.ts');
16647
+ const buildState = runtime.getWebMcpBuildState();
13815
16648
 
13816
16649
  if (req.method === 'GET' && pathname === '/llms.txt') {
13817
16650
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
@@ -13927,6 +16760,7 @@ export default defineConfig({
13927
16760
  if (!fs.existsSync(DATA_CONFIG_DIR)) fs.mkdirSync(DATA_CONFIG_DIR, { recursive: true });
13928
16761
  if (!fs.existsSync(DATA_PAGES_DIR)) fs.mkdirSync(DATA_PAGES_DIR, { recursive: true });
13929
16762
  if (projectState.site != null) fs.writeFileSync(path.join(DATA_CONFIG_DIR, 'site.json'), JSON.stringify(projectState.site, null, 2), 'utf8');
16763
+ if (projectState.menu != null) fs.writeFileSync(path.join(DATA_CONFIG_DIR, 'menu.json'), JSON.stringify(projectState.menu, null, 2), 'utf8');
13930
16764
  if (projectState.theme != null) fs.writeFileSync(path.join(DATA_CONFIG_DIR, 'theme.json'), JSON.stringify(projectState.theme, null, 2), 'utf8');
13931
16765
  if (projectState.page != null) {
13932
16766
  const safeSlug = (slug.replace(/[^a-zA-Z0-9-_]/g, '_') || 'page');
@@ -13980,7 +16814,4 @@ export default defineConfig({
13980
16814
  });
13981
16815
 
13982
16816
 
13983
-
13984
-
13985
-
13986
16817
  END_OF_FILE_CONTENT