@olonjs/cli 3.0.124 → 3.0.126

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.113",
1646
+ "@olonjs/core": "^1.0.115",
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,47 +5840,188 @@ 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.menu ?? [];
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
5345
- href={data.designSystemHref}
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
6024
+ href={data.designSystemHref}
5346
6025
  className="text-[12px] text-muted-foreground hover:text-foreground transition-colors"
5347
6026
  >
5348
6027
  Design System
@@ -5374,7 +6053,7 @@ import { z } from 'zod';
5374
6053
  export const FooterSchema = z.object({
5375
6054
  brandText: z.string().describe('ui:text'),
5376
6055
  copyright: z.string().describe('ui:text'),
5377
- menu: z.array(z.object({
6056
+ links: z.array(z.object({
5378
6057
  label: z.string().describe('ui:text'),
5379
6058
  href: z.string().describe('ui:text'),
5380
6059
  })).optional().describe('ui:list'),
@@ -5564,7 +6243,7 @@ export function Header({ data, settings, menu }: HeaderViewProps) {
5564
6243
  const isSticky = settings?.sticky ?? true;
5565
6244
  const navRef = useRef<HTMLElement>(null);
5566
6245
 
5567
- const linksField = data.menu as unknown;
6246
+ const linksField = data.links as unknown;
5568
6247
  const rawLinks = Array.isArray(linksField) ? linksField : [];
5569
6248
  const menuItems = Array.isArray(menu) ? (menu as unknown[]) : [];
5570
6249
  // If tenant explicitly uses a JSON ref for links, resolve from menu config.
@@ -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';
@@ -6007,7 +6684,7 @@ export const HeaderSchema = z.object({
6007
6684
  signinHref: z.string().optional().describe('ui:text'),
6008
6685
  ctaHref: z.string().optional().describe('ui:text'),
6009
6686
  ctaLabel: z.string().optional().describe('ui:text'),
6010
- menu: z.array(z.object({
6687
+ links: z.array(z.object({
6011
6688
  label: z.string().describe('ui:text'),
6012
6689
  href: z.string().describe('ui:text'),
6013
6690
  isCta: z.boolean().default(false).describe('ui:checkbox'),
@@ -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..."
@@ -7172,8 +8073,8 @@ function hasRenderableCta(cta: { label?: string; href?: string } | undefined): c
7172
8073
  }
7173
8074
 
7174
8075
  export function OlonHeroView({ data }: Props) {
7175
- const { tenantId = 'default' } = useConfig();
7176
- const imageUrl = data.image?.url ? resolveAssetUrl(data.image.url, tenantId) : heroPlugImage;
8076
+ const { tenantId = 'default', basePath = '/' } = useConfig();
8077
+ const imageUrl = data.image?.url ? resolveAssetUrl(data.image.url, tenantId, basePath) : heroPlugImage;
7177
8078
 
7178
8079
  return (
7179
8080
  <section
@@ -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,509 +11639,1870 @@ 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}
10197
- 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',
10202
- className
10203
- )}
10204
- ref={ref}
10205
- {...props}
10206
- />
10207
- )
10208
- }
10209
- )
10210
- Input.displayName = 'Input'
10211
-
10212
- export { Input }
10213
-
10214
- 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'
10219
-
10220
- const Label = React.forwardRef<
10221
- HTMLLabelElement,
10222
- React.LabelHTMLAttributes<HTMLLabelElement>
10223
- >(({ className, ...props }, ref) => (
10224
- <label
10225
- ref={ref}
10226
- className={cn('block text-xs font-medium text-foreground mb-1.5 cursor-default', className)}
10227
- {...props}
10228
- />
10229
- ))
10230
- Label.displayName = 'Label'
10231
-
10232
- export { Label }
10233
-
10234
- END_OF_FILE_CONTENT
10235
- echo "Creating src/components/ui/select.tsx..."
10236
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.tsx"
10237
- import * as React from "react"
10238
- import { Select as SelectPrimitive } from "radix-ui"
10239
-
10240
- import { cn } from "@/lib/utils"
10241
- import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
11651
+ function Dialog({
11652
+ ...props
11653
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
11654
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
11655
+ }
10242
11656
 
10243
- function Select({
11657
+ function DialogTrigger({
10244
11658
  ...props
10245
- }: React.ComponentProps<typeof SelectPrimitive.Root>) {
10246
- return <SelectPrimitive.Root data-slot="select" {...props} />
11659
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
11660
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
10247
11661
  }
10248
11662
 
10249
- function SelectGroup({
10250
- className,
11663
+ function DialogPortal({
10251
11664
  ...props
10252
- }: React.ComponentProps<typeof SelectPrimitive.Group>) {
10253
- return (
10254
- <SelectPrimitive.Group
10255
- data-slot="select-group"
10256
- className={cn("scroll-my-1 p-1", className)}
10257
- {...props}
10258
- />
10259
- )
11665
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
11666
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
10260
11667
  }
10261
11668
 
10262
- function SelectValue({
11669
+ function DialogClose({
10263
11670
  ...props
10264
- }: React.ComponentProps<typeof SelectPrimitive.Value>) {
10265
- return <SelectPrimitive.Value data-slot="select-value" {...props} />
11671
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
11672
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
10266
11673
  }
10267
11674
 
10268
- function SelectTrigger({
11675
+ function DialogOverlay({
10269
11676
  className,
10270
- size = "default",
10271
- children,
10272
11677
  ...props
10273
- }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
10274
- size?: "sm" | "default"
10275
- }) {
11678
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
10276
11679
  return (
10277
- <SelectPrimitive.Trigger
10278
- data-slot="select-trigger"
10279
- data-size={size}
11680
+ <DialogPrimitive.Overlay
11681
+ data-slot="dialog-overlay"
10280
11682
  className={cn(
10281
- "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",
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",
10282
11684
  className
10283
11685
  )}
10284
11686
  {...props}
10285
- >
10286
- {children}
10287
- <SelectPrimitive.Icon asChild>
10288
- <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
10289
- </SelectPrimitive.Icon>
10290
- </SelectPrimitive.Trigger>
11687
+ />
10291
11688
  )
10292
11689
  }
10293
11690
 
10294
- function SelectContent({
11691
+ function DialogContent({
10295
11692
  className,
10296
11693
  children,
10297
- position = "popper",
10298
- align = "center",
11694
+ showCloseButton = true,
10299
11695
  ...props
10300
- }: React.ComponentProps<typeof SelectPrimitive.Content>) {
11696
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
11697
+ showCloseButton?: boolean
11698
+ }) {
10301
11699
  return (
10302
- <SelectPrimitive.Portal>
10303
- <SelectPrimitive.Content
10304
- data-slot="select-content"
10305
- data-align-trigger={position === "item-aligned"}
11700
+ <DialogPortal>
11701
+ <DialogOverlay />
11702
+ <DialogPrimitive.Content
11703
+ data-slot="dialog-content"
10306
11704
  className={cn(
10307
- "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",
10308
- 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",
10309
- className
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",
11706
+ className
10310
11707
  )}
10311
- position={position}
10312
- align={align}
10313
11708
  {...props}
10314
11709
  >
10315
- <SelectScrollUpButton />
10316
- <SelectPrimitive.Viewport
10317
- data-position={position}
10318
- className={cn(
10319
- "p-1",
10320
- position === "popper" && "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
10321
- )}
10322
- >
10323
- {children}
10324
- </SelectPrimitive.Viewport>
10325
- <SelectScrollDownButton />
10326
- </SelectPrimitive.Content>
10327
- </SelectPrimitive.Portal>
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>
10328
11726
  )
10329
11727
  }
10330
11728
 
10331
- function SelectLabel({
10332
- className,
10333
- ...props
10334
- }: React.ComponentProps<typeof SelectPrimitive.Label>) {
11729
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
10335
11730
  return (
10336
- <SelectPrimitive.Label
10337
- data-slot="select-label"
10338
- className={cn("text-muted-foreground px-1.5 py-1 text-xs", className)}
11731
+ <div
11732
+ data-slot="dialog-header"
11733
+ className={cn("flex flex-col gap-2", className)}
10339
11734
  {...props}
10340
11735
  />
10341
11736
  )
10342
11737
  }
10343
11738
 
10344
- function SelectItem({
11739
+ function DialogFooter({
10345
11740
  className,
11741
+ showCloseButton = false,
10346
11742
  children,
10347
11743
  ...props
10348
- }: React.ComponentProps<typeof SelectPrimitive.Item>) {
11744
+ }: React.ComponentProps<"div"> & {
11745
+ showCloseButton?: boolean
11746
+ }) {
10349
11747
  return (
10350
- <SelectPrimitive.Item
10351
- data-slot="select-item"
11748
+ <div
11749
+ data-slot="dialog-footer"
10352
11750
  className={cn(
10353
- "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",
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",
10354
11752
  className
10355
11753
  )}
10356
11754
  {...props}
10357
11755
  >
10358
- <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
10359
- <SelectPrimitive.ItemIndicator>
10360
- <CheckIcon className="pointer-events-none" />
10361
- </SelectPrimitive.ItemIndicator>
10362
- </span>
10363
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
10364
- </SelectPrimitive.Item>
11756
+ {children}
11757
+ {showCloseButton && (
11758
+ <DialogPrimitive.Close asChild>
11759
+ <Button variant="outline">Close</Button>
11760
+ </DialogPrimitive.Close>
11761
+ )}
11762
+ </div>
10365
11763
  )
10366
11764
  }
10367
11765
 
10368
- function SelectSeparator({
11766
+ function DialogTitle({
10369
11767
  className,
10370
11768
  ...props
10371
- }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
11769
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
10372
11770
  return (
10373
- <SelectPrimitive.Separator
10374
- data-slot="select-separator"
10375
- className={cn("bg-border -mx-1 my-1 h-px pointer-events-none", className)}
11771
+ <DialogPrimitive.Title
11772
+ data-slot="dialog-title"
11773
+ className={cn(
11774
+ "text-base leading-none font-medium",
11775
+ className
11776
+ )}
10376
11777
  {...props}
10377
11778
  />
10378
11779
  )
10379
11780
  }
10380
11781
 
10381
- function SelectScrollUpButton({
10382
- className,
10383
- ...props
10384
- }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
10385
- return (
10386
- <SelectPrimitive.ScrollUpButton
10387
- data-slot="select-scroll-up-button"
10388
- className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
10389
- {...props}
10390
- >
10391
- <ChevronUpIcon />
10392
- </SelectPrimitive.ScrollUpButton>
10393
- )
10394
- }
10395
-
10396
- function SelectScrollDownButton({
11782
+ function DialogDescription({
10397
11783
  className,
10398
11784
  ...props
10399
- }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
11785
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
10400
11786
  return (
10401
- <SelectPrimitive.ScrollDownButton
10402
- data-slot="select-scroll-down-button"
10403
- className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
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
+ )}
10404
11793
  {...props}
10405
- >
10406
- <ChevronDownIcon />
10407
- </SelectPrimitive.ScrollDownButton>
11794
+ />
10408
11795
  )
10409
11796
  }
10410
11797
 
10411
11798
  export {
10412
- Select,
10413
- SelectContent,
10414
- SelectGroup,
10415
- SelectItem,
10416
- SelectLabel,
10417
- SelectScrollDownButton,
10418
- SelectScrollUpButton,
10419
- SelectSeparator,
10420
- SelectTrigger,
10421
- SelectValue,
11799
+ Dialog,
11800
+ DialogClose,
11801
+ DialogContent,
11802
+ DialogDescription,
11803
+ DialogFooter,
11804
+ DialogHeader,
11805
+ DialogOverlay,
11806
+ DialogPortal,
11807
+ DialogTitle,
11808
+ DialogTrigger,
10422
11809
  }
10423
11810
 
10424
11811
 
10425
-
10426
-
10427
11812
  END_OF_FILE_CONTENT
10428
- echo "Creating src/components/ui/select.txt..."
10429
- cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.txt"
11813
+ echo "Creating src/components/ui/dropdown-menu.tsx..."
11814
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/dropdown-menu.tsx"
10430
11815
  import * as React from "react"
10431
- import { Select as SelectPrimitive } from "radix-ui"
11816
+ import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
10432
11817
 
10433
11818
  import { cn } from "@/lib/utils"
10434
- import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
11819
+ import { CheckIcon, ChevronRightIcon } from "lucide-react"
10435
11820
 
10436
- function Select({
11821
+ function DropdownMenu({
10437
11822
  ...props
10438
- }: React.ComponentProps<typeof SelectPrimitive.Root>) {
10439
- return <SelectPrimitive.Root data-slot="select" {...props} />
11823
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
11824
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
10440
11825
  }
10441
11826
 
10442
- function SelectGroup({
10443
- className,
11827
+ function DropdownMenuPortal({
10444
11828
  ...props
10445
- }: React.ComponentProps<typeof SelectPrimitive.Group>) {
11829
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
10446
11830
  return (
10447
- <SelectPrimitive.Group
10448
- data-slot="select-group"
10449
- className={cn("scroll-my-1 p-1", className)}
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"
10450
11841
  {...props}
10451
11842
  />
10452
11843
  )
10453
11844
  }
10454
11845
 
10455
- function SelectValue({
11846
+ function DropdownMenuContent({
11847
+ className,
11848
+ align = "start",
11849
+ sideOffset = 4,
10456
11850
  ...props
10457
- }: React.ComponentProps<typeof SelectPrimitive.Value>) {
10458
- return <SelectPrimitive.Value data-slot="select-value" {...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 )}
11859
+ {...props}
11860
+ />
11861
+ </DropdownMenuPrimitive.Portal>
11862
+ )
10459
11863
  }
10460
11864
 
10461
- function SelectTrigger({
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({
10462
11897
  className,
10463
- size = "default",
10464
11898
  children,
11899
+ checked,
11900
+ inset,
10465
11901
  ...props
10466
- }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
10467
- size?: "sm" | "default"
11902
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & {
11903
+ inset?: boolean
10468
11904
  }) {
10469
11905
  return (
10470
- <SelectPrimitive.Trigger
10471
- data-slot="select-trigger"
10472
- data-size={size}
11906
+ <DropdownMenuPrimitive.CheckboxItem
11907
+ data-slot="dropdown-menu-checkbox-item"
11908
+ data-inset={inset}
10473
11909
  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",
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",
10475
11911
  className
10476
11912
  )}
11913
+ checked={checked}
10477
11914
  {...props}
10478
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>
10479
11925
  {children}
10480
- <SelectPrimitive.Icon asChild>
10481
- <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
10482
- </SelectPrimitive.Icon>
10483
- </SelectPrimitive.Trigger>
11926
+ </DropdownMenuPrimitive.CheckboxItem>
10484
11927
  )
10485
11928
  }
10486
11929
 
10487
- function SelectContent({
10488
- className,
10489
- children,
10490
- position = "popper",
10491
- align = "center",
11930
+ function DropdownMenuRadioGroup({
10492
11931
  ...props
10493
- }: React.ComponentProps<typeof SelectPrimitive.Content>) {
11932
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
10494
11933
  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}
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"
10507
11962
  >
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>
11963
+ <DropdownMenuPrimitive.ItemIndicator>
11964
+ <CheckIcon
11965
+ />
11966
+ </DropdownMenuPrimitive.ItemIndicator>
11967
+ </span>
11968
+ {children}
11969
+ </DropdownMenuPrimitive.RadioItem>
10521
11970
  )
10522
11971
  }
10523
11972
 
10524
- function SelectLabel({
11973
+ function DropdownMenuLabel({
10525
11974
  className,
11975
+ inset,
10526
11976
  ...props
10527
- }: React.ComponentProps<typeof SelectPrimitive.Label>) {
11977
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
11978
+ inset?: boolean
11979
+ }) {
10528
11980
  return (
10529
- <SelectPrimitive.Label
10530
- data-slot="select-label"
10531
- className={cn("text-muted-foreground px-1.5 py-1 text-xs", className)}
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
+ )}
10532
11988
  {...props}
10533
11989
  />
10534
11990
  )
10535
11991
  }
10536
11992
 
10537
- function SelectItem({
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({
10538
12029
  className,
12030
+ inset,
10539
12031
  children,
10540
12032
  ...props
10541
- }: React.ComponentProps<typeof SelectPrimitive.Item>) {
12033
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
12034
+ inset?: boolean
12035
+ }) {
10542
12036
  return (
10543
- <SelectPrimitive.Item
10544
- data-slot="select-item"
12037
+ <DropdownMenuPrimitive.SubTrigger
12038
+ data-slot="dropdown-menu-sub-trigger"
12039
+ data-inset={inset}
10545
12040
  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",
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",
10547
12042
  className
10548
12043
  )}
10549
12044
  {...props}
10550
12045
  >
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>
12046
+ {children}
12047
+ <ChevronRightIcon className="ml-auto" />
12048
+ </DropdownMenuPrimitive.SubTrigger>
10558
12049
  )
10559
12050
  }
10560
12051
 
10561
- function SelectSeparator({
12052
+ function DropdownMenuSubContent({
10562
12053
  className,
10563
12054
  ...props
10564
- }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
12055
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
10565
12056
  return (
10566
- <SelectPrimitive.Separator
10567
- data-slot="select-separator"
10568
- className={cn("bg-border -mx-1 my-1 h-px pointer-events-none", className)}
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
+ }
12082
+
12083
+
12084
+ END_OF_FILE_CONTENT
12085
+ echo "Creating src/components/ui/hover-card.tsx..."
12086
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/hover-card.tsx"
12087
+ "use client"
12088
+
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>
12172
+ >(({ className, ...props }, ref) => (
12173
+ <label
12174
+ ref={ref}
12175
+ className={cn('block text-xs font-medium text-foreground mb-1.5 cursor-default', className)}
12176
+ {...props}
12177
+ />
12178
+ ))
12179
+ Label.displayName = 'Label'
12180
+
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
+ )}
10569
12278
  {...props}
10570
12279
  />
10571
12280
  )
10572
12281
  }
10573
12282
 
10574
- function SelectScrollUpButton({
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
+
12636
+
12637
+ END_OF_FILE_CONTENT
12638
+ echo "Creating src/components/ui/select.txt..."
12639
+ cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.txt"
12640
+ import * as React from "react"
12641
+ import { Select as SelectPrimitive } from "radix-ui"
12642
+
12643
+ import { cn } from "@/lib/utils"
12644
+ import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
12645
+
12646
+ function Select({
12647
+ ...props
12648
+ }: React.ComponentProps<typeof SelectPrimitive.Root>) {
12649
+ return <SelectPrimitive.Root data-slot="select" {...props} />
12650
+ }
12651
+
12652
+ function SelectGroup({
12653
+ className,
12654
+ ...props
12655
+ }: React.ComponentProps<typeof SelectPrimitive.Group>) {
12656
+ return (
12657
+ <SelectPrimitive.Group
12658
+ data-slot="select-group"
12659
+ className={cn("scroll-my-1 p-1", className)}
12660
+ {...props}
12661
+ />
12662
+ )
12663
+ }
12664
+
12665
+ function SelectValue({
12666
+ ...props
12667
+ }: React.ComponentProps<typeof SelectPrimitive.Value>) {
12668
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />
12669
+ }
12670
+
12671
+ function SelectTrigger({
12672
+ className,
12673
+ size = "default",
12674
+ children,
12675
+ ...props
12676
+ }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
12677
+ size?: "sm" | "default"
12678
+ }) {
12679
+ return (
12680
+ <SelectPrimitive.Trigger
12681
+ data-slot="select-trigger"
12682
+ data-size={size}
12683
+ className={cn(
12684
+ "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",
12685
+ className
12686
+ )}
12687
+ {...props}
12688
+ >
12689
+ {children}
12690
+ <SelectPrimitive.Icon asChild>
12691
+ <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
12692
+ </SelectPrimitive.Icon>
12693
+ </SelectPrimitive.Trigger>
12694
+ )
12695
+ }
12696
+
12697
+ function SelectContent({
12698
+ className,
12699
+ children,
12700
+ position = "popper",
12701
+ align = "center",
12702
+ ...props
12703
+ }: React.ComponentProps<typeof SelectPrimitive.Content>) {
12704
+ return (
12705
+ <SelectPrimitive.Portal>
12706
+ <SelectPrimitive.Content
12707
+ data-slot="select-content"
12708
+ data-align-trigger={position === "item-aligned"}
12709
+ className={cn(
12710
+ "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",
12711
+ 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",
12712
+ className
12713
+ )}
12714
+ position={position}
12715
+ align={align}
12716
+ {...props}
12717
+ >
12718
+ <SelectScrollUpButton />
12719
+ <SelectPrimitive.Viewport
12720
+ data-position={position}
12721
+ className={cn(
12722
+ "p-1",
12723
+ position === "popper" && "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
12724
+ )}
12725
+ >
12726
+ {children}
12727
+ </SelectPrimitive.Viewport>
12728
+ <SelectScrollDownButton />
12729
+ </SelectPrimitive.Content>
12730
+ </SelectPrimitive.Portal>
12731
+ )
12732
+ }
12733
+
12734
+ function SelectLabel({
12735
+ className,
12736
+ ...props
12737
+ }: React.ComponentProps<typeof SelectPrimitive.Label>) {
12738
+ return (
12739
+ <SelectPrimitive.Label
12740
+ data-slot="select-label"
12741
+ className={cn("text-muted-foreground px-1.5 py-1 text-xs", className)}
12742
+ {...props}
12743
+ />
12744
+ )
12745
+ }
12746
+
12747
+ function SelectItem({
12748
+ className,
12749
+ children,
12750
+ ...props
12751
+ }: React.ComponentProps<typeof SelectPrimitive.Item>) {
12752
+ return (
12753
+ <SelectPrimitive.Item
12754
+ data-slot="select-item"
12755
+ className={cn(
12756
+ "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",
12757
+ className
12758
+ )}
12759
+ {...props}
12760
+ >
12761
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
12762
+ <SelectPrimitive.ItemIndicator>
12763
+ <CheckIcon className="pointer-events-none" />
12764
+ </SelectPrimitive.ItemIndicator>
12765
+ </span>
12766
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
12767
+ </SelectPrimitive.Item>
12768
+ )
12769
+ }
12770
+
12771
+ function SelectSeparator({
12772
+ className,
12773
+ ...props
12774
+ }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
12775
+ return (
12776
+ <SelectPrimitive.Separator
12777
+ data-slot="select-separator"
12778
+ className={cn("bg-border -mx-1 my-1 h-px pointer-events-none", className)}
12779
+ {...props}
12780
+ />
12781
+ )
12782
+ }
12783
+
12784
+ function SelectScrollUpButton({
12785
+ className,
12786
+ ...props
12787
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
12788
+ return (
12789
+ <SelectPrimitive.ScrollUpButton
12790
+ data-slot="select-scroll-up-button"
12791
+ className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
12792
+ {...props}
12793
+ >
12794
+ <ChevronUpIcon />
12795
+ </SelectPrimitive.ScrollUpButton>
12796
+ )
12797
+ }
12798
+
12799
+ function SelectScrollDownButton({
12800
+ className,
12801
+ ...props
12802
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
12803
+ return (
12804
+ <SelectPrimitive.ScrollDownButton
12805
+ data-slot="select-scroll-down-button"
12806
+ className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className)}
12807
+ {...props}
12808
+ >
12809
+ <ChevronDownIcon />
12810
+ </SelectPrimitive.ScrollDownButton>
12811
+ )
12812
+ }
12813
+
12814
+ export {
12815
+ Select,
12816
+ SelectContent,
12817
+ SelectGroup,
12818
+ SelectItem,
12819
+ SelectLabel,
12820
+ SelectScrollDownButton,
12821
+ SelectScrollUpButton,
12822
+ SelectSeparator,
12823
+ SelectTrigger,
12824
+ SelectValue,
12825
+ }
12826
+
12827
+
12828
+
12829
+
12830
+ END_OF_FILE_CONTENT
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
+
12859
+ import * as React from "react"
12860
+ import { Dialog as SheetPrimitive } from "radix-ui"
12861
+
12862
+ import { cn } from "@/lib/utils"
12863
+ import { Button } from "@/components/ui/button"
12864
+ import { XIcon } from "lucide-react"
12865
+
12866
+ function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
12867
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />
12868
+ }
12869
+
12870
+ function SheetTrigger({
12871
+ ...props
12872
+ }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
12873
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
12874
+ }
12875
+
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({
12889
+ className,
12890
+ ...props
12891
+ }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
12892
+ return (
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
+ )}
12899
+ {...props}
12900
+ />
12901
+ )
12902
+ }
12903
+
12904
+ function SheetContent({
12905
+ className,
12906
+ children,
12907
+ side = "right",
12908
+ showCloseButton = true,
12909
+ ...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
+ )
12943
+ }
12944
+
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({
13035
+ className,
13036
+ size = "default",
13037
+ ...props
13038
+ }: React.ComponentProps<typeof SwitchPrimitive.Root> & {
13039
+ size?: "sm" | "default"
13040
+ }) {
13041
+ return (
13042
+ <SwitchPrimitive.Root
13043
+ data-slot="switch"
13044
+ data-size={size}
13045
+ className={cn(
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",
13047
+ className
13048
+ )}
13049
+ {...props}
13050
+ >
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>
13056
+ )
13057
+ }
13058
+
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({
13157
+ className,
13158
+ ...props
13159
+ }: React.ComponentProps<"caption">) {
13160
+ return (
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
+ />
13207
+ )
13208
+ }
13209
+
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({
13226
+ className,
13227
+ variant = "default",
13228
+ ...props
13229
+ }: React.ComponentProps<typeof TabsPrimitive.List> &
13230
+ VariantProps<typeof tabsListVariants>) {
13231
+ return (
13232
+ <TabsPrimitive.List
13233
+ data-slot="tabs-list"
13234
+ data-variant={variant}
13235
+ className={cn(tabsListVariants({ variant }), className)}
13236
+ {...props}
13237
+ />
13238
+ )
13239
+ }
13240
+
13241
+ function TabsTrigger({
13242
+ className,
13243
+ ...props
13244
+ }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
13245
+ return (
13246
+ <TabsPrimitive.Trigger
13247
+ data-slot="tabs-trigger"
13248
+ className={cn(
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",
13253
+ className
13254
+ )}
13255
+ {...props}
13256
+ />
13257
+ )
13258
+ }
13259
+
13260
+ function TabsContent({
13261
+ className,
13262
+ ...props
13263
+ }: React.ComponentProps<typeof TabsPrimitive.Content>) {
13264
+ return (
13265
+ <TabsPrimitive.Content
13266
+ data-slot="tabs-content"
13267
+ className={cn("flex-1 text-sm outline-none", className)}
13268
+ {...props}
13269
+ />
13270
+ )
13271
+ }
13272
+
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
@@ -10695,12 +13513,12 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/menu.json"
10695
13513
  {
10696
13514
  "main": [
10697
13515
  {
10698
- "label": "Architecture",
10699
- "href": "#Architecture"
13516
+ "label": "Why",
13517
+ "href": "#Why"
10700
13518
  },
10701
13519
  {
10702
- "label": "Whys",
10703
- "href": "#Why"
13520
+ "label": "Architecture",
13521
+ "href": "#Architecture"
10704
13522
  },
10705
13523
  {
10706
13524
  "label": "Example",
@@ -10714,52 +13532,6 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/menu.json"
10714
13532
  "label": "GitHub",
10715
13533
  "href": "https://github.com/olonjs/core"
10716
13534
  }
10717
- ],
10718
- "footer": [
10719
- {
10720
- "label": "GitHubs",
10721
- "href": "https://github.com/olonjs/core"
10722
- }
10723
- ],
10724
- "nested": [
10725
- {
10726
- "label": "Why",
10727
- "href": "/why",
10728
- "children": [
10729
- {
10730
- "label": "Overview",
10731
- "href": "/platform/overview"
10732
- },
10733
- {
10734
- "label": "Architecture",
10735
- "href": "/platform/architecture"
10736
- },
10737
- {
10738
- "label": "Security",
10739
- "href": "/platform/security"
10740
- },
10741
- {
10742
- "label": "Integrations",
10743
- "href": "/platform/integrations"
10744
- },
10745
- {
10746
- "label": "Roadmap",
10747
- "href": "/platform/roadmap"
10748
- }
10749
- ]
10750
- },
10751
- {
10752
- "label": "Solutions",
10753
- "href": "/solutions"
10754
- },
10755
- {
10756
- "label": "Pricing",
10757
- "href": "/pricing"
10758
- },
10759
- {
10760
- "label": "Resources",
10761
- "href": "/resources"
10762
- }
10763
13535
  ]
10764
13536
  }
10765
13537
  END_OF_FILE_CONTENT
@@ -10811,15 +13583,51 @@ END_OF_FILE_CONTENT
10811
13583
  echo "Creating src/data/config/site.json..."
10812
13584
  cat << 'END_OF_FILE_CONTENT' > "src/data/config/site.json"
10813
13585
  {
13586
+ "identity": {
13587
+ "title": "OlonJS",
13588
+ "logoUrl": "/brand/mark/olon-mark-dark.svg"
13589
+ },
13590
+ "pages": [
13591
+ {
13592
+ "slug": "home",
13593
+ "label": "Home"
13594
+ },
13595
+ {
13596
+ "slug": "design-system",
13597
+ "label": "Design System"
13598
+ }
13599
+ ],
10814
13600
  "header": {
10815
13601
  "id": "global-header",
10816
13602
  "type": "header",
10817
13603
  "data": {
10818
13604
  "logoText": "Olon",
10819
13605
  "badge": "JS",
10820
- "menu": {
10821
- "$ref": "../config/menu.json#/main"
10822
- }
13606
+ "links": [
13607
+ {
13608
+ "label": "Why",
13609
+ "href": "#Why"
13610
+ },
13611
+ {
13612
+ "label": "Architecture",
13613
+ "href": "#Architecture"
13614
+ },
13615
+ {
13616
+ "label": "Example",
13617
+ "href": "#Example"
13618
+ },
13619
+ {
13620
+ "label": "Get started",
13621
+ "href": "#Getstarted"
13622
+ },
13623
+ {
13624
+ "label": "GitHub",
13625
+ "href": "https://github.com/olonjs/core"
13626
+ }
13627
+ ],
13628
+ "ctaLabel": "",
13629
+ "ctaHref": "",
13630
+ "signinHref": ""
10823
13631
  }
10824
13632
  },
10825
13633
  "footer": {
@@ -10828,15 +13636,18 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/site.json"
10828
13636
  "data": {
10829
13637
  "brandText": "OlonJS",
10830
13638
  "copyright": "© 2026 OlonJS · v1.5 · Guido Serio",
10831
- "menu": {
10832
- "$ref": "../config/menu.json#/footer"
10833
- }
13639
+ "links": [
13640
+ {
13641
+ "label": "GitHub",
13642
+ "href": "https://github.com/olonjs/core"
13643
+ }
13644
+ ],
13645
+ "designSystemHref": ""
13646
+ },
13647
+ "settings": {
13648
+ "showLogo": true
10834
13649
  }
10835
- },
10836
- "identity": {
10837
- "title": "Site"
10838
- },
10839
- "pages": []
13650
+ }
10840
13651
  }
10841
13652
  END_OF_FILE_CONTENT
10842
13653
  echo "Creating src/data/config/theme.json..."
@@ -11066,9 +13877,9 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/pages/home.json"
11066
13877
  "data": {
11067
13878
  "id": "hero-main",
11068
13879
  "eyebrow": "CONTRACT LAYER · V1.5 · OPEN SOURCE",
11069
- "headline": "Build Websites ",
11070
- "subline": "AI Agents Can Operate",
11071
- "body": "Aaa agents are becoming operational actors in commerce, marketing, and support. They need more than content — they need a contract. OlonJS is the deterministic machine contract for websites: every site typed, structured, and addressable by design. No custom glue. No fragile integrations. Just a contract any agent can read and operate.",
13880
+ "headline": "Contract Layer",
13881
+ "subline": "for the agentic web.",
13882
+ "body": "AI agents are becoming operational actors in commerce, marketing, and support. They need more than content — they need a contract. OlonJS is the deterministic machine contract for websites: every site typed, structured, and addressable by design. No custom glue. No fragile integrations. Just a contract any agent can read and operate.",
11072
13883
  "primaryCta": {
11073
13884
  "label": "Get started",
11074
13885
  "href": "#getstarted"
@@ -11842,26 +14653,13 @@ cat << 'END_OF_FILE_CONTENT' > "src/entry-ssg.tsx"
11842
14653
  import { renderToString } from 'react-dom/server';
11843
14654
  import { StaticRouter } from 'react-router-dom/server';
11844
14655
  import { ConfigProvider, PageRenderer, StudioProvider, resolveRuntimeConfig } from '@olonjs/core';
11845
- import type { JsonPagesConfig, MenuConfig, PageConfig, SiteConfig, ThemeConfig } from '@/types';
14656
+ import type { JsonPagesConfig, PageConfig, SiteConfig, ThemeConfig } from '@/types';
11846
14657
  import { ThemeProvider } from '@/components/ThemeProvider';
11847
14658
  import { ComponentRegistry } from '@/lib/ComponentRegistry';
11848
14659
  import { SECTION_SCHEMAS } from '@/lib/schemas';
11849
- import { getFilePages } from '@/lib/getFilePages';
11850
- import siteData from '@/data/config/site.json';
11851
- import menuData from '@/data/config/menu.json';
11852
- import themeData from '@/data/config/theme.json';
14660
+ import { menuConfig, pages, refDocuments, siteConfig, themeConfig } from '@/runtime';
11853
14661
  import tenantCss from '@/index.css?inline';
11854
14662
 
11855
- const siteConfig = siteData as unknown as SiteConfig;
11856
- const menuConfig: MenuConfig = { main: [] };
11857
- const themeConfig = themeData as unknown as ThemeConfig;
11858
- const pages = getFilePages();
11859
- const refDocuments = {
11860
- 'menu.json': menuData,
11861
- 'config/menu.json': menuData,
11862
- 'src/data/config/menu.json': menuData,
11863
- } satisfies NonNullable<JsonPagesConfig['refDocuments']>;
11864
-
11865
14663
  function isRecord(value: unknown): value is Record<string, unknown> {
11866
14664
  return typeof value === 'object' && value !== null && !Array.isArray(value);
11867
14665
  }
@@ -13542,6 +16340,38 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
13542
16340
 
13543
16341
 
13544
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
+
13545
16375
  END_OF_FILE_CONTENT
13546
16376
  mkdir -p "src/types"
13547
16377
  echo "Creating src/types.ts..."
@@ -13739,7 +16569,7 @@ import react from '@vitejs/plugin-react';
13739
16569
  import tailwindcss from '@tailwindcss/vite';
13740
16570
  import path from 'path';
13741
16571
  import fs from 'fs';
13742
- import { fileURLToPath, pathToFileURL } from 'url';
16572
+ import { fileURLToPath } from 'url';
13743
16573
 
13744
16574
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13745
16575
 
@@ -13796,8 +16626,7 @@ function normalizeManifestSlug(raw) {
13796
16626
  }
13797
16627
 
13798
16628
  async function loadWebMcpBuilders() {
13799
- const corePkgPath = path.dirname(fileURLToPath(import.meta.resolve('@olonjs/core/package.json')));
13800
- const moduleUrl = pathToFileURL(path.resolve(corePkgPath, 'src', 'lib', 'webmcp-contracts.mjs')).href;
16629
+ const moduleUrl = import.meta.resolve('@olonjs/core');
13801
16630
  return import(moduleUrl);
13802
16631
  }
13803
16632
  export default defineConfig({
@@ -13812,9 +16641,10 @@ export default defineConfig({
13812
16641
  const isPageJsonRequest = isTenantPageJsonRequest(req, pathname);
13813
16642
 
13814
16643
  const handleManifestRequest = async () => {
13815
- const { buildPageContract, buildPageManifest, buildSiteManifest, buildLlmsTxt } = await loadWebMcpBuilders();
13816
- const ssrEntry = await server.ssrLoadModule('/src/entry-ssg.tsx');
13817
- 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();
13818
16648
 
13819
16649
  if (req.method === 'GET' && pathname === '/llms.txt') {
13820
16650
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
@@ -13984,6 +16814,4 @@ export default defineConfig({
13984
16814
  });
13985
16815
 
13986
16816
 
13987
-
13988
-
13989
16817
  END_OF_FILE_CONTENT