sapient-ai 0.1.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.
Files changed (55) hide show
  1. package/README.md +48 -0
  2. package/bin/sapient-ai.js +623 -0
  3. package/local-registry/README.md +59 -0
  4. package/local-registry/r/accordion.json +65 -0
  5. package/local-registry/r/alert.json +64 -0
  6. package/local-registry/r/badge.json +64 -0
  7. package/local-registry/r/button.json +66 -0
  8. package/local-registry/r/checkbox.json +65 -0
  9. package/local-registry/r/customer-satisfaction.json +61 -0
  10. package/local-registry/r/input.json +61 -0
  11. package/local-registry/r/label.json +64 -0
  12. package/local-registry/r/multiple-choice-card.json +66 -0
  13. package/local-registry/r/multiple-choice-grid.json +64 -0
  14. package/local-registry/r/multiple-choice-list.json +64 -0
  15. package/local-registry/r/news-card.json +61 -0
  16. package/local-registry/r/privacy-consent.json +61 -0
  17. package/local-registry/r/product-card.json +64 -0
  18. package/local-registry/r/profile-card.json +64 -0
  19. package/local-registry/r/progress.json +64 -0
  20. package/local-registry/r/promo-card.json +64 -0
  21. package/local-registry/r/radio-group.json +65 -0
  22. package/local-registry/r/separator.json +64 -0
  23. package/local-registry/r/switch.json +64 -0
  24. package/local-registry/r/tabs.json +64 -0
  25. package/local-registry/r/textarea.json +61 -0
  26. package/local-registry/r/video-card.json +69 -0
  27. package/local-registry/scripts/build-registry.mjs +283 -0
  28. package/local-registry/scripts/sync-to-design-system-public.mjs +43 -0
  29. package/local-registry/src/components/ui/sapient-accordion.tsx +89 -0
  30. package/local-registry/src/components/ui/sapient-alert.tsx +68 -0
  31. package/local-registry/src/components/ui/sapient-badge.tsx +28 -0
  32. package/local-registry/src/components/ui/sapient-button.tsx +31 -0
  33. package/local-registry/src/components/ui/sapient-checkbox.tsx +35 -0
  34. package/local-registry/src/components/ui/sapient-customer-satisfaction.tsx +189 -0
  35. package/local-registry/src/components/ui/sapient-icon.tsx +40 -0
  36. package/local-registry/src/components/ui/sapient-input.tsx +23 -0
  37. package/local-registry/src/components/ui/sapient-label.tsx +25 -0
  38. package/local-registry/src/components/ui/sapient-multiple-choice-card.tsx +172 -0
  39. package/local-registry/src/components/ui/sapient-multiple-choice-grid.tsx +94 -0
  40. package/local-registry/src/components/ui/sapient-multiple-choice-list.tsx +74 -0
  41. package/local-registry/src/components/ui/sapient-news-card.tsx +227 -0
  42. package/local-registry/src/components/ui/sapient-privacy-consent.tsx +197 -0
  43. package/local-registry/src/components/ui/sapient-product-card.tsx +468 -0
  44. package/local-registry/src/components/ui/sapient-profile-card.tsx +193 -0
  45. package/local-registry/src/components/ui/sapient-progress.tsx +32 -0
  46. package/local-registry/src/components/ui/sapient-promo-card.tsx +247 -0
  47. package/local-registry/src/components/ui/sapient-radio-button.tsx +82 -0
  48. package/local-registry/src/components/ui/sapient-radio-group.tsx +54 -0
  49. package/local-registry/src/components/ui/sapient-separator.tsx +28 -0
  50. package/local-registry/src/components/ui/sapient-switch.tsx +36 -0
  51. package/local-registry/src/components/ui/sapient-tabs.tsx +82 -0
  52. package/local-registry/src/components/ui/sapient-textarea.tsx +23 -0
  53. package/local-registry/src/components/ui/sapient-video-card.tsx +159 -0
  54. package/local-registry/src/components/ui/sapient-video-controller.tsx +214 -0
  55. package/package.json +25 -0
@@ -0,0 +1,59 @@
1
+ # Local Registry Test Kit
2
+
3
+ This folder lets you test `sapient-ai` end-to-end without a public registry URL.
4
+
5
+ ## What it generates
6
+
7
+ - `r/button.json`: includes theme CSS variables, icon helper, and `SapientButton`.
8
+ - `r/badge.json`: includes `SapientBadge` and depends on `@sapient/button`.
9
+ - `r/input.json`, `r/textarea.json`, `r/label.json`
10
+ - `r/checkbox.json`, `r/radio-group.json`, `r/switch.json`
11
+ - `r/progress.json`, `r/separator.json`
12
+ - `r/tabs.json`, `r/accordion.json`, `r/alert.json`
13
+
14
+ ## Build registry JSON files
15
+
16
+ ```bash
17
+ cd /Users/mooshah/Desktop/Sapient-Playground/conversational-playground
18
+ npm --workspace sapient-ai run build:local-registry
19
+ ```
20
+
21
+ ## Serve files locally
22
+
23
+ ```bash
24
+ cd /Users/mooshah/Desktop/Sapient-Playground/conversational-playground/packages/cli/local-registry
25
+ npx --yes serve . -p 4310
26
+ ```
27
+
28
+ This exposes:
29
+
30
+ - `http://localhost:4310/r/button.json`
31
+ - `http://localhost:4310/r/badge.json`
32
+ - `http://localhost:4310/r/input.json`
33
+ - `http://localhost:4310/r/textarea.json`
34
+ - `http://localhost:4310/r/label.json`
35
+ - `http://localhost:4310/r/checkbox.json`
36
+ - `http://localhost:4310/r/radio-group.json`
37
+ - `http://localhost:4310/r/switch.json`
38
+ - `http://localhost:4310/r/progress.json`
39
+ - `http://localhost:4310/r/separator.json`
40
+ - `http://localhost:4310/r/tabs.json`
41
+ - `http://localhost:4310/r/accordion.json`
42
+ - `http://localhost:4310/r/alert.json`
43
+
44
+ ## Test from a mock project
45
+
46
+ ```bash
47
+ mkdir -p /tmp/sapient-mock
48
+ cd /tmp/sapient-mock
49
+ npx create-next-app@latest . --ts --tailwind --eslint --app --src-dir --import-alias "@/*"
50
+
51
+ SAPIENT_REGISTRY_URL="http://localhost:4310/r/{name}.json" \
52
+ npx --yes /Users/mooshah/Desktop/Sapient-Playground/conversational-playground/sapient-ai-0.0.1.tgz init -y
53
+
54
+ SAPIENT_REGISTRY_URL="http://localhost:4310/r/{name}.json" \
55
+ npx --yes /Users/mooshah/Desktop/Sapient-Playground/conversational-playground/sapient-ai-0.0.1.tgz add button
56
+
57
+ SAPIENT_REGISTRY_URL="http://localhost:4310/r/{name}.json" \
58
+ npx --yes /Users/mooshah/Desktop/Sapient-Playground/conversational-playground/sapient-ai-0.0.1.tgz add badge
59
+ ```
@@ -0,0 +1,65 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "accordion",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Accordion",
6
+ "description": "Accordion primitives for collapsible content sections.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-accordion.tsx",
57
+ "type": "registry:ui",
58
+ "content": "import * as React from \"react\";\nimport * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport { ChevronDown } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type SapientAccordionProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Root\n>;\n\nexport const SapientAccordion = React.forwardRef<\n React.ElementRef<typeof AccordionPrimitive.Root>,\n SapientAccordionProps\n>(({ className, ...props }, ref) => {\n return (\n <AccordionPrimitive.Root ref={ref} className={cn(className)} {...props} />\n );\n});\n\nSapientAccordion.displayName = \"SapientAccordion\";\n\nexport type SapientAccordionItemProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Item\n>;\n\nexport const SapientAccordionItem = React.forwardRef<\n React.ElementRef<typeof AccordionPrimitive.Item>,\n SapientAccordionItemProps\n>(({ className, ...props }, ref) => {\n return (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn(\"border-b\", className)}\n {...props}\n />\n );\n});\n\nSapientAccordionItem.displayName = \"SapientAccordionItem\";\n\nexport type SapientAccordionTriggerProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Trigger\n>;\n\nexport const SapientAccordionTrigger = React.forwardRef<\n React.ElementRef<typeof AccordionPrimitive.Trigger>,\n SapientAccordionTriggerProps\n>(({ className, children, ...props }, ref) => {\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronDown className=\"h-4 w-4 shrink-0 transition-transform duration-200\" />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n );\n});\n\nSapientAccordionTrigger.displayName = \"SapientAccordionTrigger\";\n\nexport type SapientAccordionContentProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Content\n>;\n\nexport const SapientAccordionContent = React.forwardRef<\n React.ElementRef<typeof AccordionPrimitive.Content>,\n SapientAccordionContentProps\n>(({ className, children, ...props }, ref) => {\n return (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n \"overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down\",\n className\n )}\n {...props}\n >\n <div className=\"pb-4 pt-0\">{children}</div>\n </AccordionPrimitive.Content>\n );\n});\n\nSapientAccordionContent.displayName = \"SapientAccordionContent\";\n"
59
+ }
60
+ ],
61
+ "dependencies": [
62
+ "@radix-ui/react-accordion",
63
+ "lucide-react"
64
+ ]
65
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "alert",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Alert",
6
+ "description": "Status and feedback alert component with semantic variants.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-alert.tsx",
57
+ "type": "registry:ui",
58
+ "content": "import * as React from \"react\";\nimport {\n AlertCircle,\n AlertTriangle,\n CheckCircle,\n Info,\n} from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SapientAlertProps {\n variant?: \"default\" | \"destructive\" | \"success\" | \"warning\";\n title?: string;\n description?: string;\n className?: string;\n children?: React.ReactNode;\n}\n\nexport function SapientAlert({\n variant = \"default\",\n title,\n description,\n className,\n children,\n}: SapientAlertProps) {\n const variants = {\n default: \"bg-background text-foreground border-border\",\n destructive:\n \"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive\",\n success:\n \"border-green-500/50 text-green-700 dark:text-green-400 [&>svg]:text-green-600\",\n warning:\n \"border-yellow-500/50 text-yellow-700 dark:text-yellow-400 [&>svg]:text-yellow-600\",\n };\n\n const icons = {\n default: Info,\n destructive: AlertCircle,\n success: CheckCircle,\n warning: AlertTriangle,\n };\n\n const Icon = icons[variant] ?? Info;\n\n return (\n <div\n className={cn(\n \"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground\",\n variants[variant],\n className\n )}\n >\n <Icon className=\"h-4 w-4\" />\n <div className=\"flex flex-col gap-1\">\n {title ? (\n <h5 className=\"mb-1 font-medium leading-none tracking-tight\">\n {title}\n </h5>\n ) : null}\n {description || children ? (\n <div className=\"text-sm opacity-90 [&_p]:leading-relaxed\">\n {description}\n {children}\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n"
59
+ }
60
+ ],
61
+ "dependencies": [
62
+ "lucide-react"
63
+ ]
64
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "badge",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Badge",
6
+ "description": "Sapient badge that reuses the iconography helper.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-badge.tsx",
57
+ "type": "registry:ui",
58
+ "content": "import * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { SapientIcon } from \"@/components/ui/sapient-icon\";\n\nexport interface SapientBadgeProps\n extends React.HTMLAttributes<HTMLSpanElement> {\n icon?: string;\n}\n\nexport function SapientBadge({\n icon,\n className,\n children,\n ...props\n}: SapientBadgeProps) {\n return (\n <span\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border border-border bg-card px-2.5 py-1 text-xs font-medium text-card-foreground\",\n className\n )}\n {...props}\n >\n {icon ? <SapientIcon name={icon} size={14} /> : null}\n <span>{children}</span>\n </span>\n );\n}\n"
59
+ }
60
+ ],
61
+ "registryDependencies": [
62
+ "@sapient/button"
63
+ ]
64
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "button",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Button",
6
+ "description": "Sapient button with semantic theme tokens and iconography support.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-icon.tsx",
57
+ "type": "registry:ui",
58
+ "content": "import * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nconst ICON_BASE_URL = \"https://pub-3f6483ae20764a8fbcfcf8b0b7dc076a.r2.dev\";\nconst FLAGS_BASE_URL = \"https://pub-40d153c17ab64cc8abb11157721f0d1d.r2.dev\";\n\nexport type SapientIconVariant = \"general\" | \"flags\";\n\nexport interface SapientIconProps\n extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, \"src\" | \"alt\"> {\n name: string;\n label?: string;\n size?: number;\n variant?: SapientIconVariant;\n}\n\nexport function SapientIcon({\n name,\n label,\n size = 20,\n variant = \"general\",\n className,\n ...props\n}: SapientIconProps) {\n const baseUrl = variant === \"flags\" ? FLAGS_BASE_URL : ICON_BASE_URL;\n const src = `${baseUrl}/${name}.svg`;\n\n return (\n <img\n src={src}\n alt={label ?? name}\n width={size}\n height={size}\n className={cn(\"shrink-0\", className)}\n loading=\"lazy\"\n decoding=\"async\"\n {...props}\n />\n );\n}\n"
59
+ },
60
+ {
61
+ "path": "components/ui/sapient-button.tsx",
62
+ "type": "registry:ui",
63
+ "content": "import * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { SapientIcon } from \"@/components/ui/sapient-icon\";\n\nexport interface SapientButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n icon?: string;\n iconPosition?: \"left\" | \"right\";\n}\n\nexport function SapientButton({\n icon,\n iconPosition = \"left\",\n className,\n children,\n ...props\n}: SapientButtonProps) {\n return (\n <button\n className={cn(\n \"inline-flex h-10 items-center justify-center gap-2 rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n className\n )}\n {...props}\n >\n {icon && iconPosition === \"left\" ? <SapientIcon name={icon} size={18} /> : null}\n <span>{children}</span>\n {icon && iconPosition === \"right\" ? <SapientIcon name={icon} size={18} /> : null}\n </button>\n );\n}\n"
64
+ }
65
+ ]
66
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "checkbox",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Checkbox",
6
+ "description": "Checkbox control styled with Sapient semantic tokens.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-checkbox.tsx",
57
+ "type": "registry:ui",
58
+ "content": "import * as React from \"react\";\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport { Check } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SapientCheckboxProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>,\n \"checked\"\n > {\n checked?: boolean;\n}\n\nexport const SapientCheckbox = React.forwardRef<\n React.ElementRef<typeof CheckboxPrimitive.Root>,\n SapientCheckboxProps\n>(({ className, checked = false, ...props }, ref) => {\n return (\n <CheckboxPrimitive.Root\n ref={ref}\n checked={checked}\n className={cn(\n \"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator className=\"flex items-center justify-center text-current\">\n <Check className=\"h-3 w-3\" strokeWidth={3} />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n});\n\nSapientCheckbox.displayName = \"SapientCheckbox\";\n"
59
+ }
60
+ ],
61
+ "dependencies": [
62
+ "@radix-ui/react-checkbox",
63
+ "lucide-react"
64
+ ]
65
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "customer-satisfaction",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Customer Satisfaction",
6
+ "description": "Satisfaction survey card with animated face feedback slider.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-customer-satisfaction.tsx",
57
+ "type": "registry:ui",
58
+ "content": "'use client';\n\nimport React, { useState, useId } from 'react';\nimport { cn } from '@/lib/utils';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface CustomerSatisfactionProps {\n className?: string;\n /** Question text shown as the card heading */\n question?: string;\n /** Min slider label (left) */\n minLabel?: string;\n /** Max slider label (right) */\n maxLabel?: string;\n /** Initial value 0–100 */\n defaultValue?: number;\n /** Controlled value 0–100 */\n value?: number;\n onChange?: (value: number) => void;\n /** Left-side label (two lines, e.g. [\"PER NIENTE\", \"SODDISFATTO\"]) */\n minLabelLines?: [string, string];\n /** Right-side label (two lines) */\n maxLabelLines?: [string, string];\n}\n\n// ─── Face illustration (pure SVG) ────────────────────────────────────────────\n// Exact coordinates extracted from Figma SVG exports.\n// Viewbox: 0 0 162 131 (matches both exported SVGs)\n//\n// Frame 4254 = Sad (v = 0)\n// Frame 4255 = Happy (v = 100)\n//\n// All numeric path coordinates are linearly interpolated so the face\n// morphs smoothly as the slider moves.\n\n// Helper: lerp between sad (a) and happy (b) values based on 0–100 slider\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * (t / 100);\n}\n\n// Round to 3 dp to keep path strings compact\nfunction r(n: number): string {\n return n.toFixed(3);\n}\n\nfunction SatisfactionFace({ value }: { value: number }) {\n const v = Math.min(100, Math.max(0, value));\n const l = (a: number, b: number) => lerp(a, b, v);\n\n // ── Main mouth arc (cubic bezier) ────────────────────────────────────────\n // Sad: M27.04,105.66 C46.23,58.95 104.46,53.19 135.18,94.15\n // Happy: M27.04,71.83 C46.23,118.54 104.46,124.30 135.18,83.35\n const arc_My = l(105.664, 71.8327);\n const arc_C1y = l(58.9536, 118.543);\n const arc_C2y = l(53.1949, 124.301);\n const arc_Ey = l(94.1461, 83.3503);\n const arcPath = `M27.0389,${r(arc_My)} C46.2348,${r(arc_C1y)} 104.462,${r(arc_C2y)} 135.176,${r(arc_Ey)}`;\n\n // ── Left curl ────────────────────────────────────────────────────────────\n // Sad: M34.10,107.98 C29.94,110.06 22.98,106.60 21.14,101.61\n // Happy: M34.10,69.51 C29.94,67.43 22.98,70.90 21.14,75.89\n const lc_My = l(107.983, 69.5134);\n const lc_C1y = l(110.062, 67.4345);\n const lc_C2y = l(106.596, 70.9);\n const lc_Ey = l(101.606, 75.8902);\n const leftCurl = `M34.0956,${r(lc_My)} C29.9417,${r(lc_C1y)} 22.9833,${r(lc_C2y)} 21.1386,${r(lc_Ey)}`;\n\n // ── Right curl ───────────────────────────────────────────────────────────\n // Sad: M138.97,88.07 C139.90,92.62 134.75,98.44 129.45,98.93\n // Happy: M138.97,89.43 C139.90,84.88 134.75,79.05 129.45,78.56\n const rc_My = l(88.0696, 89.4267);\n const rc_C1y = l(92.62, 84.8763);\n const rc_C2y = l(98.4444, 79.0519);\n const rc_Ey = l(98.9347, 78.5616);\n const rightCurl = `M138.966,${r(rc_My)} C139.899,${r(rc_C1y)} 134.75,${r(rc_C2y)} 129.453,${r(rc_Ey)}`;\n\n const strokeColor = 'hsl(var(--secondary-600))';\n const strokeW = '3.83917';\n\n return (\n <svg\n viewBox=\"0 0 162 131\"\n width={162}\n height={131}\n fill=\"none\"\n aria-hidden=\"true\"\n >\n {/* Left eye */}\n <ellipse cx=\"63.7274\" cy=\"35.3567\" rx=\"6.39862\" ry=\"15.3567\" fill={strokeColor} />\n {/* Right eye */}\n <ellipse cx=\"97.5246\" cy=\"35.3567\" rx=\"6.39862\" ry=\"15.3567\" fill={strokeColor} />\n {/* Main mouth arc */}\n <path d={arcPath} stroke={strokeColor} strokeWidth={strokeW} />\n {/* Left curl */}\n <path d={leftCurl} stroke={strokeColor} strokeWidth={strokeW} strokeLinecap=\"round\" />\n {/* Right curl */}\n <path d={rightCurl} stroke={strokeColor} strokeWidth={strokeW} strokeLinecap=\"round\" />\n </svg>\n );\n}\n\n// ─── CustomerSatisfaction ─────────────────────────────────────────────────────\n\nexport function CustomerSatisfaction({\n className,\n question = 'Quanto sei soddisfatto della tua esperienza?',\n minLabelLines = ['PER NIENTE', 'SODDISFATTO'],\n maxLabelLines = ['MOLTO', 'SODDISFATTO'],\n defaultValue = 25,\n value: controlledValue,\n onChange,\n}: CustomerSatisfactionProps) {\n const sliderId = useId();\n const [internalValue, setInternalValue] = useState(defaultValue);\n\n const value = controlledValue ?? internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const v = Number(e.target.value);\n setInternalValue(v);\n onChange?.(v);\n };\n\n // Thumb position as a percentage for custom styling\n const pct = value;\n\n return (\n <div\n className={cn(\n 'flex flex-col gap-[var(--spacing-l,24px)] items-center overflow-hidden rounded-[var(--radius-token-lg,32px)] p-[var(--spacing-l,24px)] w-[353px] bg-white',\n className\n )}\n >\n {/* ── Question ── */}\n <p className=\"text-[length:var(--text-body,16px)] leading-[var(--leading-body,24px)] font-[var(--font-weight-medium,500)] text-[hsl(var(--neutral-950))] w-full whitespace-pre-wrap\">\n {question}\n </p>\n\n {/* ── Face illustration ── */}\n <div className=\"flex flex-col gap-[16px] items-center p-[20px] shrink-0\">\n <SatisfactionFace value={value} />\n </div>\n\n {/* ── Slider ── */}\n <div className=\"flex flex-col gap-[var(--spacing-m,16px)] items-center w-full shrink-0\">\n {/* Track + thumb */}\n <div className=\"relative w-full flex items-center h-[30px]\">\n {/* Track background */}\n <div className=\"absolute inset-x-0 h-[4px] rounded-full bg-[hsl(var(--neutral-200))]\" />\n\n {/* Native range input — fully transparent, sits on top */}\n <label htmlFor={sliderId} className=\"sr-only\">\n {question}\n </label>\n <input\n id={sliderId}\n type=\"range\"\n min={0}\n max={100}\n step={1}\n value={value}\n onChange={handleChange}\n className=\"absolute inset-x-0 w-full h-full opacity-0 cursor-grab active:cursor-grabbing z-10\"\n style={{ margin: 0 }}\n />\n\n {/* Custom thumb */}\n <div\n className=\"absolute size-[30px] rounded-full bg-[hsl(var(--neutral-950))] shadow-md pointer-events-none -translate-x-1/2\"\n style={{ left: `${pct}%` }}\n />\n </div>\n\n {/* Labels */}\n <div className=\"flex items-start justify-between w-full\">\n <p className=\"text-[10px] leading-[1.4] font-normal uppercase tracking-[0.04em] text-[hsl(var(--neutral-500))] whitespace-pre-wrap\">\n {minLabelLines[0]}{'\\n'}{minLabelLines[1]}\n </p>\n <p className=\"text-[10px] leading-[1.4] font-normal uppercase tracking-[0.04em] text-[hsl(var(--neutral-500))] text-right whitespace-pre-wrap\">\n {maxLabelLines[0]}{'\\n'}{maxLabelLines[1]}\n </p>\n </div>\n </div>\n </div>\n );\n}\n\nexport default CustomerSatisfaction;\n"
59
+ }
60
+ ]
61
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "input",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Input",
6
+ "description": "Text input aligned to Sapient semantic tokens.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-input.tsx",
57
+ "type": "registry:ui",
58
+ "content": "import * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SapientInputProps\n extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nexport const SapientInput = React.forwardRef<HTMLInputElement, SapientInputProps>(\n ({ className, type = \"text\", ...props }, ref) => {\n return (\n <input\n ref={ref}\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n />\n );\n }\n);\n\nSapientInput.displayName = \"SapientInput\";\n"
59
+ }
60
+ ]
61
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "label",
4
+ "type": "registry:ui",
5
+ "title": "Sapient Label",
6
+ "description": "Accessible form label built on Radix Label.",
7
+ "cssVars": {
8
+ "theme": {
9
+ "radius": "0.5rem"
10
+ },
11
+ "light": {
12
+ "background": "0 0% 98%",
13
+ "foreground": "0 0% 12%",
14
+ "card": "0 0% 98%",
15
+ "card-foreground": "0 0% 12%",
16
+ "popover": "0 0% 100%",
17
+ "popover-foreground": "0 0% 12%",
18
+ "primary": "161 89% 53%",
19
+ "primary-foreground": "0 0% 0%",
20
+ "secondary": "249 89% 63%",
21
+ "secondary-foreground": "0 0% 100%",
22
+ "muted": "0 0% 88%",
23
+ "muted-foreground": "0 0% 44%",
24
+ "accent": "161 89% 53%",
25
+ "accent-foreground": "0 0% 0%",
26
+ "destructive": "349 81% 50%",
27
+ "destructive-foreground": "0 0% 100%",
28
+ "border": "0 0% 78%",
29
+ "input": "0 0% 78%",
30
+ "ring": "161 89% 53%"
31
+ },
32
+ "dark": {
33
+ "background": "0 0% 0%",
34
+ "foreground": "0 0% 98%",
35
+ "card": "0 0% 12%",
36
+ "card-foreground": "0 0% 98%",
37
+ "popover": "0 0% 12%",
38
+ "popover-foreground": "0 0% 98%",
39
+ "primary": "161 89% 67%",
40
+ "primary-foreground": "0 0% 0%",
41
+ "secondary": "249 100% 73%",
42
+ "secondary-foreground": "0 0% 100%",
43
+ "muted": "0 0% 32%",
44
+ "muted-foreground": "0 0% 78%",
45
+ "accent": "161 89% 67%",
46
+ "accent-foreground": "0 0% 0%",
47
+ "destructive": "350 90% 57%",
48
+ "destructive-foreground": "0 0% 100%",
49
+ "border": "0 0% 44%",
50
+ "input": "0 0% 44%",
51
+ "ring": "161 89% 67%"
52
+ }
53
+ },
54
+ "files": [
55
+ {
56
+ "path": "components/ui/sapient-label.tsx",
57
+ "type": "registry:ui",
58
+ "content": "import * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cn } from \"@/lib/utils\";\n\nexport type SapientLabelProps = React.ComponentPropsWithoutRef<\n typeof LabelPrimitive.Root\n>;\n\nexport const SapientLabel = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n SapientLabelProps\n>(({ className, ...props }, ref) => {\n return (\n <LabelPrimitive.Root\n ref={ref}\n className={cn(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n className\n )}\n {...props}\n />\n );\n});\n\nSapientLabel.displayName = \"SapientLabel\";\n"
59
+ }
60
+ ],
61
+ "dependencies": [
62
+ "@radix-ui/react-label"
63
+ ]
64
+ }