@databricks/appkit-ui 0.47.1 → 0.48.0

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.
@@ -26,6 +26,12 @@ const rules = [
26
26
  id: "no-parse-float-without-validation",
27
27
  pattern: "parseFloat($X).toFixed($Y)",
28
28
  message: "parseFloat can return NaN. Validate input or use toNumber() helper from shared/types.ts."
29
+ },
30
+ {
31
+ id: "no-variants-in-prod",
32
+ pattern: "<Variants $$$P>$$$C</Variants>",
33
+ message: "<Variants> is a development-only variant picker and must not be shipped. Finalize the chosen <Variant> before deploying.",
34
+ includeTests: false
29
35
  }
30
36
  ];
31
37
  function isTestFile(filePath, rootDir) {
@@ -1 +1 @@
1
- {"version":3,"file":"lint.js","names":[],"sources":["../../../src/cli/commands/lint.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Lang, parse } from \"@ast-grep/napi\";\nimport { Command } from \"commander\";\n\ninterface Rule {\n id: string;\n pattern: string;\n message: string;\n includeTests?: boolean;\n filter?: (code: string) => boolean;\n}\n\nconst rules: Rule[] = [\n {\n id: \"no-double-type-assertion\",\n pattern: \"$X as unknown as $Y\",\n message:\n \"Avoid double type assertion (as unknown as). Use proper type guards or fix the source type.\",\n },\n {\n id: \"no-as-any\",\n pattern: \"$X as any\",\n message:\n 'Avoid \"as any\" type assertion. Use proper typing or unknown with type guards.',\n includeTests: false, // acceptable in test mocks\n },\n {\n id: \"no-array-index-key\",\n pattern: \"key={$IDX}\",\n message:\n \"Avoid using array index as React key. Use a stable unique identifier.\",\n filter: (code) => /key=\\{(idx|index|i)\\}/.test(code),\n },\n {\n id: \"no-parse-float-without-validation\",\n pattern: \"parseFloat($X).toFixed($Y)\",\n message:\n \"parseFloat can return NaN. Validate input or use toNumber() helper from shared/types.ts.\",\n },\n];\n\nfunction isTestFile(filePath: string, rootDir: string): boolean {\n // Relative to scan root: an ancestor `tests` dir must not mark the whole project as tests.\n const rel = path.relative(rootDir, filePath);\n return (\n /\\.(test|spec)\\.(ts|tsx)$/.test(rel) || /(^|[/\\\\])tests[/\\\\]/.test(rel)\n );\n}\n\nfunction findTsFiles(dir: string, files: string[] = []): string[] {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if ([\"node_modules\", \"dist\", \"build\", \".git\"].includes(entry.name))\n continue;\n findTsFiles(fullPath, files);\n } else if (entry.isFile() && /\\.(ts|tsx)$/.test(entry.name)) {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\ninterface Violation {\n file: string;\n line: number;\n column: number;\n rule: string;\n message: string;\n code: string;\n}\n\nfunction lintFile(\n filePath: string,\n rules: Rule[],\n rootDir: string,\n): Violation[] {\n const violations: Violation[] = [];\n const content = fs.readFileSync(filePath, \"utf-8\");\n const lang = filePath.endsWith(\".tsx\") ? Lang.Tsx : Lang.TypeScript;\n const testFile = isTestFile(filePath, rootDir);\n\n const ast = parse(lang, content);\n const root = ast.root();\n\n for (const rule of rules) {\n // skip rules that don't apply to test files\n if (testFile && rule.includeTests === false) continue;\n\n const matches = root.findAll(rule.pattern);\n\n for (const match of matches) {\n const code = match.text();\n\n if (rule.filter && !rule.filter(code)) continue;\n\n const range = match.range();\n violations.push({\n file: filePath,\n line: range.start.line + 1,\n column: range.start.column + 1,\n rule: rule.id,\n message: rule.message,\n code: code.length > 80 ? `${code.slice(0, 77)}...` : code,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Lint command implementation\n */\nfunction runLint() {\n const rootDir = process.cwd();\n const files = findTsFiles(rootDir);\n\n console.log(`Scanning ${files.length} TypeScript files...\\n`);\n\n const allViolations: Violation[] = [];\n\n for (const file of files) {\n const violations = lintFile(file, rules, rootDir);\n allViolations.push(...violations);\n }\n\n if (allViolations.length === 0) {\n console.log(\"No ast-grep lint violations found.\");\n process.exit(0);\n }\n\n console.log(`Found ${allViolations.length} violation(s):\\n`);\n\n for (const v of allViolations) {\n const relPath = path.relative(rootDir, v.file);\n console.log(`${relPath}:${v.line}:${v.column}`);\n console.log(` ${v.rule}: ${v.message}`);\n console.log(` > ${v.code}\\n`);\n }\n\n process.exit(1);\n}\n\nexport const lintCommand = new Command(\"lint\")\n .description(\"Run AST-based linting on TypeScript files\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ appkit lint`,\n )\n .action(runLint);\n"],"mappings":";;;;;;AAaA,MAAM,QAAgB;CACpB;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACH;CACD;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACF,cAAc;EACf;CACD;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACF,SAAS,SAAS,wBAAwB,KAAK,KAAK;EACrD;CACD;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACH;CACF;AAED,SAAS,WAAW,UAAkB,SAA0B;CAE9D,MAAM,MAAM,KAAK,SAAS,SAAS,SAAS;AAC5C,QACE,2BAA2B,KAAK,IAAI,IAAI,sBAAsB,KAAK,IAAI;;AAI3E,SAAS,YAAY,KAAa,QAAkB,EAAE,EAAY;CAChE,MAAM,UAAU,GAAG,YAAY,KAAK,EAAE,eAAe,MAAM,CAAC;AAE5D,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;AAE3C,MAAI,MAAM,aAAa,EAAE;AACvB,OAAI;IAAC;IAAgB;IAAQ;IAAS;IAAO,CAAC,SAAS,MAAM,KAAK,CAChE;AACF,eAAY,UAAU,MAAM;aACnB,MAAM,QAAQ,IAAI,cAAc,KAAK,MAAM,KAAK,CACzD,OAAM,KAAK,SAAS;;AAIxB,QAAO;;AAYT,SAAS,SACP,UACA,OACA,SACa;CACb,MAAM,aAA0B,EAAE;CAClC,MAAM,UAAU,GAAG,aAAa,UAAU,QAAQ;CAClD,MAAM,OAAO,SAAS,SAAS,OAAO,GAAG,KAAK,MAAM,KAAK;CACzD,MAAM,WAAW,WAAW,UAAU,QAAQ;CAG9C,MAAM,OADM,MAAM,MAAM,QAAQ,CACf,MAAM;AAEvB,MAAK,MAAM,QAAQ,OAAO;AAExB,MAAI,YAAY,KAAK,iBAAiB,MAAO;EAE7C,MAAM,UAAU,KAAK,QAAQ,KAAK,QAAQ;AAE1C,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,OAAO,MAAM,MAAM;AAEzB,OAAI,KAAK,UAAU,CAAC,KAAK,OAAO,KAAK,CAAE;GAEvC,MAAM,QAAQ,MAAM,OAAO;AAC3B,cAAW,KAAK;IACd,MAAM;IACN,MAAM,MAAM,MAAM,OAAO;IACzB,QAAQ,MAAM,MAAM,SAAS;IAC7B,MAAM,KAAK;IACX,SAAS,KAAK;IACd,MAAM,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO;IACtD,CAAC;;;AAIN,QAAO;;;;;AAMT,SAAS,UAAU;CACjB,MAAM,UAAU,QAAQ,KAAK;CAC7B,MAAM,QAAQ,YAAY,QAAQ;AAElC,SAAQ,IAAI,YAAY,MAAM,OAAO,wBAAwB;CAE7D,MAAM,gBAA6B,EAAE;AAErC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,SAAS,MAAM,OAAO,QAAQ;AACjD,gBAAc,KAAK,GAAG,WAAW;;AAGnC,KAAI,cAAc,WAAW,GAAG;AAC9B,UAAQ,IAAI,qCAAqC;AACjD,UAAQ,KAAK,EAAE;;AAGjB,SAAQ,IAAI,SAAS,cAAc,OAAO,kBAAkB;AAE5D,MAAK,MAAM,KAAK,eAAe;EAC7B,MAAM,UAAU,KAAK,SAAS,SAAS,EAAE,KAAK;AAC9C,UAAQ,IAAI,GAAG,QAAQ,GAAG,EAAE,KAAK,GAAG,EAAE,SAAS;AAC/C,UAAQ,IAAI,KAAK,EAAE,KAAK,IAAI,EAAE,UAAU;AACxC,UAAQ,IAAI,OAAO,EAAE,KAAK,IAAI;;AAGhC,SAAQ,KAAK,EAAE;;AAGjB,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YAAY,4CAA4C,CACxD,YACC,SACA;;iBAGD,CACA,OAAO,QAAQ"}
1
+ {"version":3,"file":"lint.js","names":[],"sources":["../../../src/cli/commands/lint.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Lang, parse } from \"@ast-grep/napi\";\nimport { Command } from \"commander\";\n\ninterface Rule {\n id: string;\n pattern: string;\n message: string;\n includeTests?: boolean;\n filter?: (code: string) => boolean;\n}\n\nconst rules: Rule[] = [\n {\n id: \"no-double-type-assertion\",\n pattern: \"$X as unknown as $Y\",\n message:\n \"Avoid double type assertion (as unknown as). Use proper type guards or fix the source type.\",\n },\n {\n id: \"no-as-any\",\n pattern: \"$X as any\",\n message:\n 'Avoid \"as any\" type assertion. Use proper typing or unknown with type guards.',\n includeTests: false, // acceptable in test mocks\n },\n {\n id: \"no-array-index-key\",\n pattern: \"key={$IDX}\",\n message:\n \"Avoid using array index as React key. Use a stable unique identifier.\",\n filter: (code) => /key=\\{(idx|index|i)\\}/.test(code),\n },\n {\n id: \"no-parse-float-without-validation\",\n pattern: \"parseFloat($X).toFixed($Y)\",\n message:\n \"parseFloat can return NaN. Validate input or use toNumber() helper from shared/types.ts.\",\n },\n {\n // <Variants> is the dev-only variant picker from @databricks/appkit-ui,\n // meant to drive the local edit loop. Finalize the chosen variant before\n // deploying so the picker chrome never reaches production.\n id: \"no-variants-in-prod\",\n pattern: \"<Variants $$$P>$$$C</Variants>\",\n message:\n \"<Variants> is a development-only variant picker and must not be shipped. Finalize the chosen <Variant> before deploying.\",\n includeTests: false,\n },\n];\n\nfunction isTestFile(filePath: string, rootDir: string): boolean {\n // Relative to scan root: an ancestor `tests` dir must not mark the whole project as tests.\n const rel = path.relative(rootDir, filePath);\n return (\n /\\.(test|spec)\\.(ts|tsx)$/.test(rel) || /(^|[/\\\\])tests[/\\\\]/.test(rel)\n );\n}\n\nfunction findTsFiles(dir: string, files: string[] = []): string[] {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if ([\"node_modules\", \"dist\", \"build\", \".git\"].includes(entry.name))\n continue;\n findTsFiles(fullPath, files);\n } else if (entry.isFile() && /\\.(ts|tsx)$/.test(entry.name)) {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\ninterface Violation {\n file: string;\n line: number;\n column: number;\n rule: string;\n message: string;\n code: string;\n}\n\nfunction lintFile(\n filePath: string,\n rules: Rule[],\n rootDir: string,\n): Violation[] {\n const violations: Violation[] = [];\n const content = fs.readFileSync(filePath, \"utf-8\");\n const lang = filePath.endsWith(\".tsx\") ? Lang.Tsx : Lang.TypeScript;\n const testFile = isTestFile(filePath, rootDir);\n\n const ast = parse(lang, content);\n const root = ast.root();\n\n for (const rule of rules) {\n // skip rules that don't apply to test files\n if (testFile && rule.includeTests === false) continue;\n\n const matches = root.findAll(rule.pattern);\n\n for (const match of matches) {\n const code = match.text();\n\n if (rule.filter && !rule.filter(code)) continue;\n\n const range = match.range();\n violations.push({\n file: filePath,\n line: range.start.line + 1,\n column: range.start.column + 1,\n rule: rule.id,\n message: rule.message,\n code: code.length > 80 ? `${code.slice(0, 77)}...` : code,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Lint command implementation\n */\nfunction runLint() {\n const rootDir = process.cwd();\n const files = findTsFiles(rootDir);\n\n console.log(`Scanning ${files.length} TypeScript files...\\n`);\n\n const allViolations: Violation[] = [];\n\n for (const file of files) {\n const violations = lintFile(file, rules, rootDir);\n allViolations.push(...violations);\n }\n\n if (allViolations.length === 0) {\n console.log(\"No ast-grep lint violations found.\");\n process.exit(0);\n }\n\n console.log(`Found ${allViolations.length} violation(s):\\n`);\n\n for (const v of allViolations) {\n const relPath = path.relative(rootDir, v.file);\n console.log(`${relPath}:${v.line}:${v.column}`);\n console.log(` ${v.rule}: ${v.message}`);\n console.log(` > ${v.code}\\n`);\n }\n\n process.exit(1);\n}\n\nexport const lintCommand = new Command(\"lint\")\n .description(\"Run AST-based linting on TypeScript files\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ appkit lint`,\n )\n .action(runLint);\n"],"mappings":";;;;;;AAaA,MAAM,QAAgB;CACpB;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACH;CACD;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACF,cAAc;EACf;CACD;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACF,SAAS,SAAS,wBAAwB,KAAK,KAAK;EACrD;CACD;EACE,IAAI;EACJ,SAAS;EACT,SACE;EACH;CACD;EAIE,IAAI;EACJ,SAAS;EACT,SACE;EACF,cAAc;EACf;CACF;AAED,SAAS,WAAW,UAAkB,SAA0B;CAE9D,MAAM,MAAM,KAAK,SAAS,SAAS,SAAS;AAC5C,QACE,2BAA2B,KAAK,IAAI,IAAI,sBAAsB,KAAK,IAAI;;AAI3E,SAAS,YAAY,KAAa,QAAkB,EAAE,EAAY;CAChE,MAAM,UAAU,GAAG,YAAY,KAAK,EAAE,eAAe,MAAM,CAAC;AAE5D,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;AAE3C,MAAI,MAAM,aAAa,EAAE;AACvB,OAAI;IAAC;IAAgB;IAAQ;IAAS;IAAO,CAAC,SAAS,MAAM,KAAK,CAChE;AACF,eAAY,UAAU,MAAM;aACnB,MAAM,QAAQ,IAAI,cAAc,KAAK,MAAM,KAAK,CACzD,OAAM,KAAK,SAAS;;AAIxB,QAAO;;AAYT,SAAS,SACP,UACA,OACA,SACa;CACb,MAAM,aAA0B,EAAE;CAClC,MAAM,UAAU,GAAG,aAAa,UAAU,QAAQ;CAClD,MAAM,OAAO,SAAS,SAAS,OAAO,GAAG,KAAK,MAAM,KAAK;CACzD,MAAM,WAAW,WAAW,UAAU,QAAQ;CAG9C,MAAM,OADM,MAAM,MAAM,QAAQ,CACf,MAAM;AAEvB,MAAK,MAAM,QAAQ,OAAO;AAExB,MAAI,YAAY,KAAK,iBAAiB,MAAO;EAE7C,MAAM,UAAU,KAAK,QAAQ,KAAK,QAAQ;AAE1C,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,OAAO,MAAM,MAAM;AAEzB,OAAI,KAAK,UAAU,CAAC,KAAK,OAAO,KAAK,CAAE;GAEvC,MAAM,QAAQ,MAAM,OAAO;AAC3B,cAAW,KAAK;IACd,MAAM;IACN,MAAM,MAAM,MAAM,OAAO;IACzB,QAAQ,MAAM,MAAM,SAAS;IAC7B,MAAM,KAAK;IACX,SAAS,KAAK;IACd,MAAM,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO;IACtD,CAAC;;;AAIN,QAAO;;;;;AAMT,SAAS,UAAU;CACjB,MAAM,UAAU,QAAQ,KAAK;CAC7B,MAAM,QAAQ,YAAY,QAAQ;AAElC,SAAQ,IAAI,YAAY,MAAM,OAAO,wBAAwB;CAE7D,MAAM,gBAA6B,EAAE;AAErC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,SAAS,MAAM,OAAO,QAAQ;AACjD,gBAAc,KAAK,GAAG,WAAW;;AAGnC,KAAI,cAAc,WAAW,GAAG;AAC9B,UAAQ,IAAI,qCAAqC;AACjD,UAAQ,KAAK,EAAE;;AAGjB,SAAQ,IAAI,SAAS,cAAc,OAAO,kBAAkB;AAE5D,MAAK,MAAM,KAAK,eAAe;EAC7B,MAAM,UAAU,KAAK,SAAS,SAAS,EAAE,KAAK;AAC9C,UAAQ,IAAI,GAAG,QAAQ,GAAG,EAAE,KAAK,GAAG,EAAE,SAAS;AAC/C,UAAQ,IAAI,KAAK,EAAE,KAAK,IAAI,EAAE,UAAU;AACxC,UAAQ,IAAI,OAAO,EAAE,KAAK,IAAI;;AAGhC,SAAQ,KAAK,EAAE;;AAGjB,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YAAY,4CAA4C,CACxD,YACC,SACA;;iBAGD,CACA,OAAO,QAAQ"}
@@ -104,4 +104,7 @@ import { Textarea } from "./ui/textarea.js";
104
104
  import { Toggle, toggleVariants } from "./ui/toggle.js";
105
105
  import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group.js";
106
106
  import "./ui/index.js";
107
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AgentChatEvent, AggregatedResourceStatus, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnalyticsFormat, AreaChart, AreaChartProps, AreaChartSpecificProps, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BarChartProps, BarChartSpecificProps, BaseChart, BaseChartProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, CartesianContext, ChartBaseProps, ChartColorPalette, ChartData, ChartInference, ChartType, ChartUITokens, ChartWrapper, ChartWrapperProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnCategory, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataFormat, DataProps, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryEntry, DirectoryList, DirectoryListProps, DonutChart, DonutChartProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileBreadcrumbProps, FileBrowserLabels, FileEntry, FileEntryProps, FilePreview, FilePreviewPanel, FilePreviewPanelProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieAttachmentResponse, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieChatProps, GenieChatStatus, GenieColumnMeta, GenieMessageItem, GenieMessageResponse, GenieQueryVisualization, GenieStatementResponse, GenieStreamEvent, HeatmapChart, HeatmapChartProps, HeatmapChartSpecificProps, HeatmapContext, HoverCard, HoverCardContent, HoverCardTrigger, InferResultByFormat, InferRowType, InferServingChunk, InferServingRequest, InferServingResponse, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LineChartProps, LineChartSpecificProps, LoadingSkeleton, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, NewFolderInputProps, NormalizedChartData, NormalizedChartDataBase, NormalizedHeatmapData, OptionBuilderContext, Orientation, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, PieChartProps, PieChartSpecificProps, PluginRegistry, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, QueryProps, QueryRegistry, RadarChart, RadarChartProps, RadarChartSpecificProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ResourceKindRenderer, ResourceSeverity, ResourceStatus, ResourceStatusFilter, ResourceStatusIndicator, ResourceStatusIndicatorProps, ResourceStatusProvider, ResourceStatusProviderProps, ResourceStatusToasterOptions, ResourceWaitingPlaceholder, ScatterChart, ScatterChartProps, ScatterChartSpecificProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, ServingAlias, ServingEndpointRegistry, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, TERMINAL_STATUSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransformedGenieData, TypedArrowTable, UnifiedChartProps, UseAgentChatOptions, UseAgentChatResult, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, UseChartDataOptions, UseChartDataResult, UseGenieChatOptions, UseGenieChatReturn, UseServingInvokeOptions, UseServingInvokeResult, UseServingStreamOptions, UseServingStreamResult, WarehouseState, WarehouseStatus, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAgentChat, useAllThemeColors, useAnalyticsQuery, useChartData, useChartUITokens, useFormField, useGenieChat, useIsMobile, usePluginClientConfig, usePortalContainer, useResolvedPortalContainer, useResourceStatus, useResourceStatusPublisher, useResourceStatusToaster, useServingInvoke, useServingStream, useSidebar, useThemeColors };
107
+ import { Variant, VariantProps } from "./ui-variants/variant.js";
108
+ import { Variants, VariantsProps } from "./ui-variants/variants.js";
109
+ import "./ui-variants/index.js";
110
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AgentChatEvent, AggregatedResourceStatus, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnalyticsFormat, AreaChart, AreaChartProps, AreaChartSpecificProps, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BarChartProps, BarChartSpecificProps, BaseChart, BaseChartProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, CartesianContext, ChartBaseProps, ChartColorPalette, ChartData, ChartInference, ChartType, ChartUITokens, ChartWrapper, ChartWrapperProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnCategory, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataFormat, DataProps, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryEntry, DirectoryList, DirectoryListProps, DonutChart, DonutChartProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileBreadcrumbProps, FileBrowserLabels, FileEntry, FileEntryProps, FilePreview, FilePreviewPanel, FilePreviewPanelProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieAttachmentResponse, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieChatProps, GenieChatStatus, GenieColumnMeta, GenieMessageItem, GenieMessageResponse, GenieQueryVisualization, GenieStatementResponse, GenieStreamEvent, HeatmapChart, HeatmapChartProps, HeatmapChartSpecificProps, HeatmapContext, HoverCard, HoverCardContent, HoverCardTrigger, InferResultByFormat, InferRowType, InferServingChunk, InferServingRequest, InferServingResponse, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LineChartProps, LineChartSpecificProps, LoadingSkeleton, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, NewFolderInputProps, NormalizedChartData, NormalizedChartDataBase, NormalizedHeatmapData, OptionBuilderContext, Orientation, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, PieChartProps, PieChartSpecificProps, PluginRegistry, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, QueryProps, QueryRegistry, RadarChart, RadarChartProps, RadarChartSpecificProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ResourceKindRenderer, ResourceSeverity, ResourceStatus, ResourceStatusFilter, ResourceStatusIndicator, ResourceStatusIndicatorProps, ResourceStatusProvider, ResourceStatusProviderProps, ResourceStatusToasterOptions, ResourceWaitingPlaceholder, ScatterChart, ScatterChartProps, ScatterChartSpecificProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, ServingAlias, ServingEndpointRegistry, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, TERMINAL_STATUSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransformedGenieData, TypedArrowTable, UnifiedChartProps, UseAgentChatOptions, UseAgentChatResult, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, UseChartDataOptions, UseChartDataResult, UseGenieChatOptions, UseGenieChatReturn, UseServingInvokeOptions, UseServingInvokeResult, UseServingStreamOptions, UseServingStreamResult, Variant, VariantProps, Variants, VariantsProps, WarehouseState, WarehouseStatus, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAgentChat, useAllThemeColors, useAnalyticsQuery, useChartData, useChartUITokens, useFormField, useGenieChat, useIsMobile, usePluginClientConfig, usePortalContainer, useResolvedPortalContainer, useResourceStatus, useResourceStatusPublisher, useResourceStatusToaster, useServingInvoke, useServingStream, useSidebar, useThemeColors };
@@ -94,5 +94,7 @@ import { Slider } from "./ui/slider.js";
94
94
  import { Switch } from "./ui/switch.js";
95
95
  import { Toggle, toggleVariants } from "./ui/toggle.js";
96
96
  import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group.js";
97
+ import { Variant } from "./ui-variants/variant.js";
98
+ import { Variants } from "./ui-variants/variants.js";
97
99
 
98
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AreaChart, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BaseChart, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartWrapper, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryList, DonutChart, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileEntry, FilePreviewPanel, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieQueryVisualization, HeatmapChart, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LoadingSkeleton, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, RadarChart, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ResourceStatusIndicator, ResourceStatusProvider, ResourceWaitingPlaceholder, ScatterChart, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAgentChat, useAllThemeColors, useAnalyticsQuery, useChartData, useChartUITokens, useFormField, useGenieChat, useIsMobile, usePluginClientConfig, usePortalContainer, useResolvedPortalContainer, useResourceStatus, useResourceStatusPublisher, useResourceStatusToaster, useServingInvoke, useServingStream, useSidebar, useThemeColors };
100
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AreaChart, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BaseChart, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartWrapper, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryList, DonutChart, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileEntry, FilePreviewPanel, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieQueryVisualization, HeatmapChart, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LoadingSkeleton, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, RadarChart, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ResourceStatusIndicator, ResourceStatusProvider, ResourceWaitingPlaceholder, ScatterChart, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Variant, Variants, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAgentChat, useAllThemeColors, useAnalyticsQuery, useChartData, useChartUITokens, useFormField, useGenieChat, useIsMobile, usePluginClientConfig, usePortalContainer, useResolvedPortalContainer, useResourceStatus, useResourceStatusPublisher, useResourceStatusToaster, useServingInvoke, useServingStream, useSidebar, useThemeColors };
@@ -0,0 +1,2 @@
1
+ import { Variant, VariantProps } from "./variant.js";
2
+ import { Variants, VariantsProps } from "./variants.js";
@@ -0,0 +1,21 @@
1
+ import * as React$1 from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/react/ui-variants/variant.d.ts
5
+ /** Props for a single {@link Variant} inside a {@link Variants} block. */
6
+ interface VariantProps {
7
+ /** Human-readable label shown in the switcher and recorded on confirm. */
8
+ label?: string;
9
+ children: React$1.ReactNode;
10
+ }
11
+ /**
12
+ * One candidate inside a {@link Variants} block. Purely a declarative marker:
13
+ * `Variants` reads its `label` and renders its `children` for the active
14
+ * index. It is not meant to be rendered on its own.
15
+ */
16
+ declare function Variant({
17
+ children
18
+ }: VariantProps): react_jsx_runtime0.JSX.Element;
19
+ //#endregion
20
+ export { Variant, VariantProps };
21
+ //# sourceMappingURL=variant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variant.d.ts","names":[],"sources":["../../../src/react/ui-variants/variant.tsx"],"mappings":";;;;;UAGiB,YAAA;;EAEf,KAAA;EACA,QAAA,EAAU,OAAA,CAAM,SAAA;AAAA;;;;;;iBAQF,OAAA,CAAA;EAAU;AAAA,GAAY,YAAA,GAAY,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -0,0 +1,15 @@
1
+ import { Fragment, jsx } from "react/jsx-runtime";
2
+
3
+ //#region src/react/ui-variants/variant.tsx
4
+ /**
5
+ * One candidate inside a {@link Variants} block. Purely a declarative marker:
6
+ * `Variants` reads its `label` and renders its `children` for the active
7
+ * index. It is not meant to be rendered on its own.
8
+ */
9
+ function Variant({ children }) {
10
+ return /* @__PURE__ */ jsx(Fragment, { children });
11
+ }
12
+
13
+ //#endregion
14
+ export { Variant };
15
+ //# sourceMappingURL=variant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variant.js","names":[],"sources":["../../../src/react/ui-variants/variant.tsx"],"sourcesContent":["import type * as React from \"react\";\n\n/** Props for a single {@link Variant} inside a {@link Variants} block. */\nexport interface VariantProps {\n /** Human-readable label shown in the switcher and recorded on confirm. */\n label?: string;\n children: React.ReactNode;\n}\n\n/**\n * One candidate inside a {@link Variants} block. Purely a declarative marker:\n * `Variants` reads its `label` and renders its `children` for the active\n * index. It is not meant to be rendered on its own.\n */\nexport function Variant({ children }: VariantProps) {\n return <>{children}</>;\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,QAAQ,EAAE,YAA0B;AAClD,QAAO,gCAAG,WAAY"}
@@ -0,0 +1,87 @@
1
+ import { ReactNode } from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/react/ui-variants/variants.d.ts
5
+ /** Props for {@link Variants}. */
6
+ interface VariantsProps {
7
+ /**
8
+ * Stable, unique identifier for this block. The variant-picking agent matches
9
+ * on it to finalize the chosen variant into source, so it must be unique per
10
+ * file. (Named `blockId`, not `id`, because it is a semantic key — not a DOM
11
+ * element id.)
12
+ */
13
+ blockId: string;
14
+ /** `<Variant>` children — one candidate UI each. */
15
+ children: ReactNode;
16
+ /**
17
+ * How the wrapper flows in the layout. Defaults to `"block"` (full width,
18
+ * variants stack vertically) — the right choice for page sections, heroes,
19
+ * and other block-level regions, which is the common case. Use `"inline"`
20
+ * when wrapping a small inline element such as a single button, so the
21
+ * wrapper hugs its content instead of spanning the row.
22
+ */
23
+ layout?: "block" | "inline";
24
+ /** Class name applied to the block wrapper (merged after `layout`). */
25
+ className?: string;
26
+ /**
27
+ * Any CSS color for the switcher chrome (dot, ring, control cluster, count
28
+ * pill, confirm tick). Every accent element derives from this one value.
29
+ * Defaults to {@link DEFAULT_ACCENT}. The error state stays red regardless.
30
+ *
31
+ * @example accent="var(--primary)" // match the app theme
32
+ * @example accent="#f43f5e" // rose
33
+ */
34
+ accent?: string;
35
+ }
36
+ /**
37
+ * Dev-time UI picker: renders several `<Variant>` candidates one at a time.
38
+ *
39
+ * At rest the block shows a small, slowly-pulsing corner dot so it's clear
40
+ * it's "in variant state" without hovering — the dot pulses while a choice is
41
+ * still pending and goes calm once recorded. On hover (or focus) an accent
42
+ * ring outlines the block and the dot gives way to a compact control cluster
43
+ * (‹ index/total › ✓) that scales out of the corner. Confirming POSTs the
44
+ * choice to the `uiVariants()` recorder plugin; a coding agent then finalizes
45
+ * the chosen variant into source, removing this wrapper.
46
+ *
47
+ * All switcher chrome uses the `accent` color (default {@link DEFAULT_ACCENT},
48
+ * a violet distinct from most apps' `primary`) so it doesn't blend into the
49
+ * content it wraps.
50
+ *
51
+ * Degrades gracefully. In a production build the picker chrome is dropped
52
+ * entirely and only the first variant renders as plain content — a safety net
53
+ * for a block that shipped without being finalized. In dev, if the recorder
54
+ * endpoint is absent (feature off) the switcher still works as a viewer and
55
+ * Confirm reports that recording is unavailable.
56
+ *
57
+ * @example Page section (default layout — full-width, stacks vertically)
58
+ * ```tsx
59
+ * <Variants blockId="hero">
60
+ * <Variant label="Split"><section>…</section></Variant>
61
+ * <Variant label="Centered"><section>…</section></Variant>
62
+ * </Variants>
63
+ * ```
64
+ *
65
+ * @example Small inline element (hug the content)
66
+ * ```tsx
67
+ * <Variants blockId="hero-cta" layout="inline">
68
+ * <Variant label="Ghost"><Button variant="ghost">Get started</Button></Variant>
69
+ * <Variant label="Solid"><Button>Get started</Button></Variant>
70
+ * </Variants>
71
+ * ```
72
+ *
73
+ * @example Custom accent
74
+ * ```tsx
75
+ * <Variants blockId="hero" accent="#f43f5e">…</Variants>
76
+ * ```
77
+ */
78
+ declare function Variants({
79
+ blockId,
80
+ children,
81
+ layout,
82
+ className,
83
+ accent
84
+ }: VariantsProps): react_jsx_runtime0.JSX.Element | null;
85
+ //#endregion
86
+ export { Variants, VariantsProps };
87
+ //# sourceMappingURL=variants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variants.d.ts","names":[],"sources":["../../../src/react/ui-variants/variants.tsx"],"mappings":";;;;;UAoCiB,aAAA;;AAAjB;;;;;EAOE,OAAA;EAEU;EAAV,QAAA,EAAU,SAAA;EAUV;;;;AA0DF;;;EA5DE,MAAA;EA8DA;EA5DA,SAAA;EA8DA;;;;;;;;EArDA,MAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiDc,QAAA,CAAA;EACd,OAAA;EACA,QAAA;EACA,MAAA;EACA,SAAA;EACA;AAAA,GACC,aAAA,GAAa,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -0,0 +1,199 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { Variant } from "./variant.js";
3
+ import { Children, isValidElement, useMemo, useState } from "react";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { Check, CheckCheck, ChevronLeft, ChevronRight, Loader2 } from "lucide-react";
6
+
7
+ //#region src/react/ui-variants/variants.tsx
8
+ /** Endpoint the recorder plugin (`uiVariants()`) mounts in development. */
9
+ const CONFIRM_ENDPOINT = "/api/ui-variants/confirm";
10
+ /** Abort the confirm request after this long so the button can't hang. */
11
+ const CONFIRM_TIMEOUT_MS = 5e3;
12
+ /**
13
+ * Default switcher accent — a violet chosen to stand apart from most apps'
14
+ * `primary`, so the dev-only picker chrome doesn't blend into the content it
15
+ * wraps. Override per-block via the `accent` prop.
16
+ */
17
+ const DEFAULT_ACCENT = "#8b5cf6";
18
+ function isVariantElement(node) {
19
+ return isValidElement(node) && node.type === Variant;
20
+ }
21
+ /**
22
+ * Dev-time UI picker: renders several `<Variant>` candidates one at a time.
23
+ *
24
+ * At rest the block shows a small, slowly-pulsing corner dot so it's clear
25
+ * it's "in variant state" without hovering — the dot pulses while a choice is
26
+ * still pending and goes calm once recorded. On hover (or focus) an accent
27
+ * ring outlines the block and the dot gives way to a compact control cluster
28
+ * (‹ index/total › ✓) that scales out of the corner. Confirming POSTs the
29
+ * choice to the `uiVariants()` recorder plugin; a coding agent then finalizes
30
+ * the chosen variant into source, removing this wrapper.
31
+ *
32
+ * All switcher chrome uses the `accent` color (default {@link DEFAULT_ACCENT},
33
+ * a violet distinct from most apps' `primary`) so it doesn't blend into the
34
+ * content it wraps.
35
+ *
36
+ * Degrades gracefully. In a production build the picker chrome is dropped
37
+ * entirely and only the first variant renders as plain content — a safety net
38
+ * for a block that shipped without being finalized. In dev, if the recorder
39
+ * endpoint is absent (feature off) the switcher still works as a viewer and
40
+ * Confirm reports that recording is unavailable.
41
+ *
42
+ * @example Page section (default layout — full-width, stacks vertically)
43
+ * ```tsx
44
+ * <Variants blockId="hero">
45
+ * <Variant label="Split"><section>…</section></Variant>
46
+ * <Variant label="Centered"><section>…</section></Variant>
47
+ * </Variants>
48
+ * ```
49
+ *
50
+ * @example Small inline element (hug the content)
51
+ * ```tsx
52
+ * <Variants blockId="hero-cta" layout="inline">
53
+ * <Variant label="Ghost"><Button variant="ghost">Get started</Button></Variant>
54
+ * <Variant label="Solid"><Button>Get started</Button></Variant>
55
+ * </Variants>
56
+ * ```
57
+ *
58
+ * @example Custom accent
59
+ * ```tsx
60
+ * <Variants blockId="hero" accent="#f43f5e">…</Variants>
61
+ * ```
62
+ */
63
+ function Variants({ blockId, children, layout = "block", className, accent = DEFAULT_ACCENT }) {
64
+ const variants = useMemo(() => Children.toArray(children).filter(isVariantElement), [children]);
65
+ const [active, setActive] = useState(0);
66
+ const [confirmState, setConfirmState] = useState("idle");
67
+ if (variants.length === 0) return null;
68
+ if (!import.meta.env.DEV) return /* @__PURE__ */ jsx(Fragment, { children: variants[0] });
69
+ const clampedActive = Math.min(active, variants.length - 1);
70
+ const current = variants[clampedActive];
71
+ const label = current.props.label ?? `Variant ${clampedActive + 1}`;
72
+ const atStart = clampedActive === 0;
73
+ const atEnd = clampedActive === variants.length - 1;
74
+ const go = (delta) => {
75
+ setActive((i) => Math.max(0, Math.min(variants.length - 1, i + delta)));
76
+ setConfirmState("idle");
77
+ };
78
+ const confirm = async () => {
79
+ if (confirmState === "saving") return;
80
+ setConfirmState("saving");
81
+ const controller = new AbortController();
82
+ const timeout = setTimeout(() => controller.abort(), CONFIRM_TIMEOUT_MS);
83
+ try {
84
+ const res = await fetch(CONFIRM_ENDPOINT, {
85
+ method: "POST",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify({
88
+ blockId,
89
+ chosenIndex: clampedActive,
90
+ label
91
+ }),
92
+ signal: controller.signal
93
+ });
94
+ if (res.status === 404 || res.status === 403) {
95
+ setConfirmState("unavailable");
96
+ return;
97
+ }
98
+ setConfirmState(res.ok ? "recorded" : "error");
99
+ } catch {
100
+ setConfirmState("error");
101
+ } finally {
102
+ clearTimeout(timeout);
103
+ }
104
+ };
105
+ return /* @__PURE__ */ jsxs("div", {
106
+ "data-slot": "variants",
107
+ style: { "--variants-accent": accent },
108
+ className: cn("group relative rounded-md ring-[color:var(--variants-accent)] transition-[box-shadow] duration-200", layout === "inline" ? "inline-block" : "block w-full", "hover:ring-1 focus-within:ring-1", className),
109
+ children: [
110
+ current,
111
+ /* @__PURE__ */ jsx(StateDot, {
112
+ state: confirmState,
113
+ className: "group-hover:pointer-events-none group-hover:opacity-0 group-focus-within:pointer-events-none group-focus-within:opacity-0"
114
+ }),
115
+ /* @__PURE__ */ jsxs("div", {
116
+ "data-slot": "variants-switcher",
117
+ className: cn("absolute -top-3 right-0 z-20 flex items-center gap-0.5 rounded-full border bg-background/95 px-1 py-0.5 shadow-md backdrop-blur", "border-[color-mix(in_srgb,var(--variants-accent)_40%,transparent)]", "pointer-events-none origin-top-right scale-90 opacity-0 transition-all duration-150", "group-hover:pointer-events-auto group-hover:scale-100 group-hover:opacity-100", "group-focus-within:pointer-events-auto group-focus-within:scale-100 group-focus-within:opacity-100"),
118
+ children: [
119
+ /* @__PURE__ */ jsx(RoundButton, {
120
+ "aria-label": "Previous variant",
121
+ disabled: atStart,
122
+ onClick: () => go(-1),
123
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5" })
124
+ }),
125
+ /* @__PURE__ */ jsxs("span", {
126
+ className: "select-none rounded-full bg-[color-mix(in_srgb,var(--variants-accent)_15%,transparent)] px-1 text-[10px] font-semibold text-[color:var(--variants-accent)] tabular-nums",
127
+ children: [
128
+ clampedActive + 1,
129
+ "/",
130
+ variants.length
131
+ ]
132
+ }),
133
+ /* @__PURE__ */ jsx(RoundButton, {
134
+ "aria-label": "Next variant",
135
+ disabled: atEnd,
136
+ onClick: () => go(1),
137
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5" })
138
+ }),
139
+ /* @__PURE__ */ jsx(ConfirmButton, {
140
+ state: confirmState,
141
+ label,
142
+ onClick: confirm
143
+ })
144
+ ]
145
+ })
146
+ ]
147
+ });
148
+ }
149
+ function dotColorFor(state) {
150
+ switch (state) {
151
+ case "error": return "bg-destructive";
152
+ case "unavailable": return "bg-muted-foreground/40";
153
+ default: return "bg-[color:var(--variants-accent)]";
154
+ }
155
+ }
156
+ /**
157
+ * Resting corner indicator: pulses while a choice is pending, calm once
158
+ * recorded — so a block's picked/unpicked state reads at a glance without
159
+ * hovering.
160
+ */
161
+ function StateDot({ state, className }) {
162
+ const pulsing = state === "idle" || state === "saving";
163
+ const color = dotColorFor(state);
164
+ return /* @__PURE__ */ jsxs("span", {
165
+ "aria-hidden": true,
166
+ "data-slot": "variants-dot",
167
+ className: cn("absolute -top-1 -right-1 z-10 flex size-3 items-center justify-center transition-opacity duration-150", className),
168
+ children: [pulsing && /* @__PURE__ */ jsx("span", { className: cn("absolute inline-flex size-full animate-ping rounded-full opacity-50 [animation-duration:2.5s]", color) }), /* @__PURE__ */ jsx("span", { className: cn("relative inline-flex size-2.5 rounded-full ring-2 ring-background", color) })]
169
+ });
170
+ }
171
+ function RoundButton({ children, disabled, className, ...props }) {
172
+ return /* @__PURE__ */ jsx("button", {
173
+ type: "button",
174
+ disabled,
175
+ className: cn("inline-flex size-6 items-center justify-center rounded-full text-muted-foreground transition-colors", "hover:bg-accent hover:text-accent-foreground", "disabled:pointer-events-none disabled:opacity-30", className),
176
+ ...props,
177
+ children
178
+ });
179
+ }
180
+ /**
181
+ * Round tick that confirms the active variant. State is conveyed by icon +
182
+ * color only (no text). The chosen `label` rides along in the title/aria for
183
+ * accessibility and hover context.
184
+ */
185
+ function ConfirmButton({ state, label, onClick }) {
186
+ const title = state === "recorded" ? `Recorded "${label}" — ask the agent to finalize` : state === "unavailable" ? "Recorder unavailable" : state === "error" ? "Failed — click to retry" : `Confirm "${label}"`;
187
+ return /* @__PURE__ */ jsx(RoundButton, {
188
+ "aria-label": title,
189
+ title,
190
+ disabled: state === "saving" || state === "unavailable",
191
+ onClick,
192
+ className: cn(state === "recorded" && "text-[color:var(--variants-accent)] hover:bg-[color-mix(in_srgb,var(--variants-accent)_12%,transparent)] hover:text-[color:var(--variants-accent)]", state === "error" && "text-destructive hover:bg-destructive/10 hover:text-destructive", (state === "idle" || state === "saving") && "hover:bg-[color-mix(in_srgb,var(--variants-accent)_12%,transparent)] hover:text-[color:var(--variants-accent)]"),
193
+ children: state === "saving" ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : state === "recorded" ? /* @__PURE__ */ jsx(CheckCheck, { className: "size-3.5" }) : /* @__PURE__ */ jsx(Check, { className: "size-3.5" })
194
+ });
195
+ }
196
+
197
+ //#endregion
198
+ export { Variants };
199
+ //# sourceMappingURL=variants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variants.js","names":[],"sources":["../../../src/react/ui-variants/variants.tsx"],"sourcesContent":["import {\n Check,\n CheckCheck,\n ChevronLeft,\n ChevronRight,\n Loader2,\n} from \"lucide-react\";\nimport {\n Children,\n type ComponentProps,\n type CSSProperties,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useMemo,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Variant, type VariantProps } from \"./variant\";\n\n/** Endpoint the recorder plugin (`uiVariants()`) mounts in development. */\nconst CONFIRM_ENDPOINT = \"/api/ui-variants/confirm\";\n\n/** Abort the confirm request after this long so the button can't hang. */\nconst CONFIRM_TIMEOUT_MS = 5000;\n\n/**\n * Default switcher accent — a violet chosen to stand apart from most apps'\n * `primary`, so the dev-only picker chrome doesn't blend into the content it\n * wraps. Override per-block via the `accent` prop.\n */\nconst DEFAULT_ACCENT = \"#8b5cf6\";\n\ntype ConfirmState = \"idle\" | \"saving\" | \"recorded\" | \"unavailable\" | \"error\";\n\n/** Props for {@link Variants}. */\nexport interface VariantsProps {\n /**\n * Stable, unique identifier for this block. The variant-picking agent matches\n * on it to finalize the chosen variant into source, so it must be unique per\n * file. (Named `blockId`, not `id`, because it is a semantic key — not a DOM\n * element id.)\n */\n blockId: string;\n /** `<Variant>` children — one candidate UI each. */\n children: ReactNode;\n /**\n * How the wrapper flows in the layout. Defaults to `\"block\"` (full width,\n * variants stack vertically) — the right choice for page sections, heroes,\n * and other block-level regions, which is the common case. Use `\"inline\"`\n * when wrapping a small inline element such as a single button, so the\n * wrapper hugs its content instead of spanning the row.\n */\n layout?: \"block\" | \"inline\";\n /** Class name applied to the block wrapper (merged after `layout`). */\n className?: string;\n /**\n * Any CSS color for the switcher chrome (dot, ring, control cluster, count\n * pill, confirm tick). Every accent element derives from this one value.\n * Defaults to {@link DEFAULT_ACCENT}. The error state stays red regardless.\n *\n * @example accent=\"var(--primary)\" // match the app theme\n * @example accent=\"#f43f5e\" // rose\n */\n accent?: string;\n}\n\nfunction isVariantElement(node: ReactNode): node is ReactElement<VariantProps> {\n return isValidElement(node) && node.type === Variant;\n}\n\n/**\n * Dev-time UI picker: renders several `<Variant>` candidates one at a time.\n *\n * At rest the block shows a small, slowly-pulsing corner dot so it's clear\n * it's \"in variant state\" without hovering — the dot pulses while a choice is\n * still pending and goes calm once recorded. On hover (or focus) an accent\n * ring outlines the block and the dot gives way to a compact control cluster\n * (‹ index/total › ✓) that scales out of the corner. Confirming POSTs the\n * choice to the `uiVariants()` recorder plugin; a coding agent then finalizes\n * the chosen variant into source, removing this wrapper.\n *\n * All switcher chrome uses the `accent` color (default {@link DEFAULT_ACCENT},\n * a violet distinct from most apps' `primary`) so it doesn't blend into the\n * content it wraps.\n *\n * Degrades gracefully. In a production build the picker chrome is dropped\n * entirely and only the first variant renders as plain content — a safety net\n * for a block that shipped without being finalized. In dev, if the recorder\n * endpoint is absent (feature off) the switcher still works as a viewer and\n * Confirm reports that recording is unavailable.\n *\n * @example Page section (default layout — full-width, stacks vertically)\n * ```tsx\n * <Variants blockId=\"hero\">\n * <Variant label=\"Split\"><section>…</section></Variant>\n * <Variant label=\"Centered\"><section>…</section></Variant>\n * </Variants>\n * ```\n *\n * @example Small inline element (hug the content)\n * ```tsx\n * <Variants blockId=\"hero-cta\" layout=\"inline\">\n * <Variant label=\"Ghost\"><Button variant=\"ghost\">Get started</Button></Variant>\n * <Variant label=\"Solid\"><Button>Get started</Button></Variant>\n * </Variants>\n * ```\n *\n * @example Custom accent\n * ```tsx\n * <Variants blockId=\"hero\" accent=\"#f43f5e\">…</Variants>\n * ```\n */\nexport function Variants({\n blockId,\n children,\n layout = \"block\",\n className,\n accent = DEFAULT_ACCENT,\n}: VariantsProps) {\n const variants = useMemo(\n () => Children.toArray(children).filter(isVariantElement),\n [children],\n );\n const [active, setActive] = useState(0);\n const [confirmState, setConfirmState] = useState<ConfirmState>(\"idle\");\n\n if (variants.length === 0) return null;\n if (!import.meta.env.DEV) return <>{variants[0]}</>;\n\n const clampedActive = Math.min(active, variants.length - 1);\n const current = variants[clampedActive];\n const label = current.props.label ?? `Variant ${clampedActive + 1}`;\n const atStart = clampedActive === 0;\n const atEnd = clampedActive === variants.length - 1;\n\n const go = (delta: number) => {\n // No wrap-around: clamp within bounds.\n setActive((i) => Math.max(0, Math.min(variants.length - 1, i + delta)));\n setConfirmState(\"idle\");\n };\n\n const confirm = async () => {\n if (confirmState === \"saving\") return;\n setConfirmState(\"saving\");\n\n // Bound the request so a hung recorder can't leave the button stuck in\n // \"saving\" forever.\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), CONFIRM_TIMEOUT_MS);\n try {\n const res = await fetch(CONFIRM_ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ blockId, chosenIndex: clampedActive, label }),\n signal: controller.signal,\n });\n // 404/403: the recorder isn't mounted (feature off or a production\n // build). Degrade to a plain viewer.\n if (res.status === 404 || res.status === 403) {\n setConfirmState(\"unavailable\");\n return;\n }\n // Any other non-OK status (e.g. a 500) is a failure the user can retry.\n setConfirmState(res.ok ? \"recorded\" : \"error\");\n } catch {\n // Network failure or timeout abort — the request never completed.\n setConfirmState(\"error\");\n } finally {\n clearTimeout(timeout);\n }\n };\n\n return (\n <div\n data-slot=\"variants\"\n // All accent chrome derives from this one variable; children read it via\n // the cascade, so the `accent` prop flows everywhere without prop-drilling.\n style={{ \"--variants-accent\": accent } as CSSProperties}\n className={cn(\n // Ring appears only on hover/focus so blocks stay calm at rest; the\n // corner dot is the resting \"in variant state\" indicator.\n \"group relative rounded-md ring-[color:var(--variants-accent)] transition-[box-shadow] duration-200\",\n layout === \"inline\" ? \"inline-block\" : \"block w-full\",\n \"hover:ring-1 focus-within:ring-1\",\n className,\n )}\n >\n {current}\n\n <StateDot\n state={confirmState}\n className=\"group-hover:pointer-events-none group-hover:opacity-0 group-focus-within:pointer-events-none group-focus-within:opacity-0\"\n />\n\n <div\n data-slot=\"variants-switcher\"\n className={cn(\n \"absolute -top-3 right-0 z-20 flex items-center gap-0.5 rounded-full border bg-background/95 px-1 py-0.5 shadow-md backdrop-blur\",\n \"border-[color-mix(in_srgb,var(--variants-accent)_40%,transparent)]\",\n \"pointer-events-none origin-top-right scale-90 opacity-0 transition-all duration-150\",\n \"group-hover:pointer-events-auto group-hover:scale-100 group-hover:opacity-100\",\n \"group-focus-within:pointer-events-auto group-focus-within:scale-100 group-focus-within:opacity-100\",\n )}\n >\n <RoundButton\n aria-label=\"Previous variant\"\n disabled={atStart}\n onClick={() => go(-1)}\n >\n <ChevronLeft className=\"size-3.5\" />\n </RoundButton>\n\n <span className=\"select-none rounded-full bg-[color-mix(in_srgb,var(--variants-accent)_15%,transparent)] px-1 text-[10px] font-semibold text-[color:var(--variants-accent)] tabular-nums\">\n {clampedActive + 1}/{variants.length}\n </span>\n\n <RoundButton\n aria-label=\"Next variant\"\n disabled={atEnd}\n onClick={() => go(1)}\n >\n <ChevronRight className=\"size-3.5\" />\n </RoundButton>\n\n <ConfirmButton state={confirmState} label={label} onClick={confirm} />\n </div>\n </div>\n );\n}\n\nfunction dotColorFor(state: ConfirmState): string {\n switch (state) {\n case \"error\":\n return \"bg-destructive\";\n case \"unavailable\":\n return \"bg-muted-foreground/40\";\n default:\n return \"bg-[color:var(--variants-accent)]\";\n }\n}\n\n/**\n * Resting corner indicator: pulses while a choice is pending, calm once\n * recorded — so a block's picked/unpicked state reads at a glance without\n * hovering.\n */\nfunction StateDot({\n state,\n className,\n}: {\n state: ConfirmState;\n className?: string;\n}) {\n const pulsing = state === \"idle\" || state === \"saving\";\n const color = dotColorFor(state);\n\n return (\n <span\n aria-hidden\n data-slot=\"variants-dot\"\n className={cn(\n \"absolute -top-1 -right-1 z-10 flex size-3 items-center justify-center transition-opacity duration-150\",\n className,\n )}\n >\n {pulsing && (\n <span\n className={cn(\n // Slow, gentle pulse (~2.5s) so it reads as an ambient hint,\n // not an alarm. Overrides tailwind's default 1s ping cadence.\n \"absolute inline-flex size-full animate-ping rounded-full opacity-50 [animation-duration:2.5s]\",\n color,\n )}\n />\n )}\n <span\n className={cn(\n \"relative inline-flex size-2.5 rounded-full ring-2 ring-background\",\n color,\n )}\n />\n </span>\n );\n}\n\nfunction RoundButton({\n children,\n disabled,\n className,\n ...props\n}: ComponentProps<\"button\">) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n className={cn(\n \"inline-flex size-6 items-center justify-center rounded-full text-muted-foreground transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground\",\n \"disabled:pointer-events-none disabled:opacity-30\",\n className,\n )}\n {...props}\n >\n {children}\n </button>\n );\n}\n\n/**\n * Round tick that confirms the active variant. State is conveyed by icon +\n * color only (no text). The chosen `label` rides along in the title/aria for\n * accessibility and hover context.\n */\nfunction ConfirmButton({\n state,\n label,\n onClick,\n}: {\n state: ConfirmState;\n label: string;\n onClick: () => void;\n}) {\n const title =\n state === \"recorded\"\n ? `Recorded \"${label}\" — ask the agent to finalize`\n : state === \"unavailable\"\n ? \"Recorder unavailable\"\n : state === \"error\"\n ? \"Failed — click to retry\"\n : `Confirm \"${label}\"`;\n\n return (\n <RoundButton\n aria-label={title}\n title={title}\n disabled={state === \"saving\" || state === \"unavailable\"}\n onClick={onClick}\n className={cn(\n state === \"recorded\" &&\n \"text-[color:var(--variants-accent)] hover:bg-[color-mix(in_srgb,var(--variants-accent)_12%,transparent)] hover:text-[color:var(--variants-accent)]\",\n state === \"error\" &&\n \"text-destructive hover:bg-destructive/10 hover:text-destructive\",\n (state === \"idle\" || state === \"saving\") &&\n \"hover:bg-[color-mix(in_srgb,var(--variants-accent)_12%,transparent)] hover:text-[color:var(--variants-accent)]\",\n )}\n >\n {state === \"saving\" ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : state === \"recorded\" ? (\n <CheckCheck className=\"size-3.5\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n </RoundButton>\n );\n}\n"],"mappings":";;;;;;;;AAqBA,MAAM,mBAAmB;;AAGzB,MAAM,qBAAqB;;;;;;AAO3B,MAAM,iBAAiB;AAoCvB,SAAS,iBAAiB,MAAqD;AAC7E,QAAO,eAAe,KAAK,IAAI,KAAK,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6C/C,SAAgB,SAAS,EACvB,SACA,UACA,SAAS,SACT,WACA,SAAS,kBACO;CAChB,MAAM,WAAW,cACT,SAAS,QAAQ,SAAS,CAAC,OAAO,iBAAiB,EACzD,CAAC,SAAS,CACX;CACD,MAAM,CAAC,QAAQ,aAAa,SAAS,EAAE;CACvC,MAAM,CAAC,cAAc,mBAAmB,SAAuB,OAAO;AAEtE,KAAI,SAAS,WAAW,EAAG,QAAO;AAClC,KAAI,CAAC,OAAO,KAAK,IAAI,IAAK,QAAO,0CAAG,SAAS,KAAM;CAEnD,MAAM,gBAAgB,KAAK,IAAI,QAAQ,SAAS,SAAS,EAAE;CAC3D,MAAM,UAAU,SAAS;CACzB,MAAM,QAAQ,QAAQ,MAAM,SAAS,WAAW,gBAAgB;CAChE,MAAM,UAAU,kBAAkB;CAClC,MAAM,QAAQ,kBAAkB,SAAS,SAAS;CAElD,MAAM,MAAM,UAAkB;AAE5B,aAAW,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,SAAS,GAAG,IAAI,MAAM,CAAC,CAAC;AACvE,kBAAgB,OAAO;;CAGzB,MAAM,UAAU,YAAY;AAC1B,MAAI,iBAAiB,SAAU;AAC/B,kBAAgB,SAAS;EAIzB,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,UAAU,iBAAiB,WAAW,OAAO,EAAE,mBAAmB;AACxE,MAAI;GACF,MAAM,MAAM,MAAM,MAAM,kBAAkB;IACxC,QAAQ;IACR,SAAS,EAAE,gBAAgB,oBAAoB;IAC/C,MAAM,KAAK,UAAU;KAAE;KAAS,aAAa;KAAe;KAAO,CAAC;IACpE,QAAQ,WAAW;IACpB,CAAC;AAGF,OAAI,IAAI,WAAW,OAAO,IAAI,WAAW,KAAK;AAC5C,oBAAgB,cAAc;AAC9B;;AAGF,mBAAgB,IAAI,KAAK,aAAa,QAAQ;UACxC;AAEN,mBAAgB,QAAQ;YAChB;AACR,gBAAa,QAAQ;;;AAIzB,QACE,qBAAC;EACC,aAAU;EAGV,OAAO,EAAE,qBAAqB,QAAQ;EACtC,WAAW,GAGT,sGACA,WAAW,WAAW,iBAAiB,gBACvC,oCACA,UACD;;GAEA;GAED,oBAAC;IACC,OAAO;IACP,WAAU;KACV;GAEF,qBAAC;IACC,aAAU;IACV,WAAW,GACT,mIACA,sEACA,uFACA,iFACA,qGACD;;KAED,oBAAC;MACC,cAAW;MACX,UAAU;MACV,eAAe,GAAG,GAAG;gBAErB,oBAAC,eAAY,WAAU,aAAa;OACxB;KAEd,qBAAC;MAAK,WAAU;;OACb,gBAAgB;OAAE;OAAE,SAAS;;OACzB;KAEP,oBAAC;MACC,cAAW;MACX,UAAU;MACV,eAAe,GAAG,EAAE;gBAEpB,oBAAC,gBAAa,WAAU,aAAa;OACzB;KAEd,oBAAC;MAAc,OAAO;MAAqB;MAAO,SAAS;OAAW;;KAClE;;GACF;;AAIV,SAAS,YAAY,OAA6B;AAChD,SAAQ,OAAR;EACE,KAAK,QACH,QAAO;EACT,KAAK,cACH,QAAO;EACT,QACE,QAAO;;;;;;;;AASb,SAAS,SAAS,EAChB,OACA,aAIC;CACD,MAAM,UAAU,UAAU,UAAU,UAAU;CAC9C,MAAM,QAAQ,YAAY,MAAM;AAEhC,QACE,qBAAC;EACC;EACA,aAAU;EACV,WAAW,GACT,yGACA,UACD;aAEA,WACC,oBAAC,UACC,WAAW,GAGT,iGACA,MACD,GACD,EAEJ,oBAAC,UACC,WAAW,GACT,qEACA,MACD,GACD;GACG;;AAIX,SAAS,YAAY,EACnB,UACA,UACA,WACA,GAAG,SACwB;AAC3B,QACE,oBAAC;EACC,MAAK;EACK;EACV,WAAW,GACT,uGACA,gDACA,oDACA,UACD;EACD,GAAI;EAEH;GACM;;;;;;;AASb,SAAS,cAAc,EACrB,OACA,OACA,WAKC;CACD,MAAM,QACJ,UAAU,aACN,aAAa,MAAM,iCACnB,UAAU,gBACR,yBACA,UAAU,UACR,4BACA,YAAY,MAAM;AAE5B,QACE,oBAAC;EACC,cAAY;EACL;EACP,UAAU,UAAU,YAAY,UAAU;EACjC;EACT,WAAW,GACT,UAAU,cACR,sJACF,UAAU,WACR,oEACD,UAAU,UAAU,UAAU,aAC7B,iHACH;YAEA,UAAU,WACT,oBAAC,WAAQ,WAAU,0BAA0B,GAC3C,UAAU,aACZ,oBAAC,cAAW,WAAU,aAAa,GAEnC,oBAAC,SAAM,WAAU,aAAa;GAEpB"}
@@ -1118,6 +1118,7 @@ declare const pluginManifestSchema: z.ZodObject<{
1118
1118
  license: z.ZodOptional<z.ZodString>;
1119
1119
  onSetupMessage: z.ZodOptional<z.ZodString>;
1120
1120
  hidden: z.ZodOptional<z.ZodBoolean>;
1121
+ devOnly: z.ZodOptional<z.ZodBoolean>;
1121
1122
  stability: z.ZodOptional<z.ZodEnum<{
1122
1123
  beta: "beta";
1123
1124
  ga: "ga";
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","names":[],"sources":["../../src/schemas/manifest.ts"],"mappings":";;;cAsPa,yBAAA,EAAyB,CAAA,CAAA,qBAAA,EAAA,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;cA8FzB,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAkQxB,4BAAA,EAA4B,CAAA,CAAA,SAAA;;;;;cAoE5B,oBAAA,EAAoB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+frB,mBAAA,GAAsB,CAAA,CAAE,KAAA,QAAa,yBAAA;AAAA,KACrC,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;AAAA,KAIpC,sBAAA,GAAyB,CAAA,CAAE,KAAA,QAC9B,4BAAA;AAAA,KAEG,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,oBAAA"}
1
+ {"version":3,"file":"manifest.d.ts","names":[],"sources":["../../src/schemas/manifest.ts"],"mappings":";;;cAsPa,yBAAA,EAAyB,CAAA,CAAA,qBAAA,EAAA,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;cA8FzB,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAkQxB,4BAAA,EAA4B,CAAA,CAAA,SAAA;;;;;cAoE5B,oBAAA,EAAoB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqgBrB,mBAAA,GAAsB,CAAA,CAAE,KAAA,QAAa,yBAAA;AAAA,KACrC,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;AAAA,KAIpC,sBAAA,GAAyB,CAAA,CAAE,KAAA,QAC9B,4BAAA;AAAA,KAEG,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,oBAAA"}
@@ -334,6 +334,7 @@ const pluginManifestSchema = z.object({
334
334
  license: z.string().optional().describe("SPDX license identifier"),
335
335
  onSetupMessage: z.string().optional().describe("Message displayed to the user after project initialization. Use this to inform about manual setup steps (e.g. environment variables, resource provisioning)."),
336
336
  hidden: z.boolean().optional().describe("When true, this plugin is excluded from the template plugins manifest (appkit.plugins.json) during sync."),
337
+ devOnly: z.boolean().optional().describe("When true, this plugin is only registered when NODE_ENV === 'development'. In any other environment createApp skips it entirely (not constructed, no routes, resources not validated). Use for dev-only tooling that must never run in a deployed app."),
337
338
  stability: z.enum(["beta", "ga"]).optional().describe("Plugin stability level. Beta plugins may have breaking API changes between minor releases but are on a path to GA. GA (general availability) plugins follow semver strictly."),
338
339
  scaffolding: z.object({ rules: pluginScaffoldingRulesSchema.optional().describe("Structured rules for scaffolding agents declared at the plugin level.") }).strict().optional().describe("Plugin-level scaffolding metadata consumed by scaffolding agents. Symmetric with template-level `scaffolding`.")
339
340
  }).strict().describe("Schema for Databricks AppKit plugin manifest files. Defines plugin metadata, resource requirements, and configuration options.");