From 939c947f3f861dd869d3f5217d5a82198e947b07 Mon Sep 17 00:00:00 2001 From: Nahiyan Khan Date: Mon, 6 Apr 2026 13:08:34 -0400 Subject: [PATCH 1/6] =?UTF-8?q?Add=20ghost-ui=20design=20language=20regist?= =?UTF-8?q?ry=20=E2=80=94=20port=20boss-ui=20components,=20tokens,=20and?= =?UTF-8?q?=20theme=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings 97 shadcn-compatible components (49 UI primitives + 48 AI elements), design tokens, theming system, and registry into the ghost monorepo as packages/ghost-ui. Excludes icons (not OSS) and proprietary fonts (swapped CashSans → Geist). Replaces next-themes with a custom framework-agnostic ThemeProvider to eliminate the Next.js dependency. Co-Authored-By: Claude Opus 4.6 (1M context) --- biome.json | 16 +- package.json | 1 + packages/ghost-ui/components.json | 21 + packages/ghost-ui/package.json | 92 + packages/ghost-ui/registry.json | 1444 +++ .../src/components/ai-elements/agent.tsx | 141 + .../src/components/ai-elements/artifact.tsx | 148 + .../components/ai-elements/attachments.tsx | 426 + .../components/ai-elements/audio-player.tsx | 229 + .../src/components/ai-elements/canvas.tsx | 26 + .../ai-elements/chain-of-thought.tsx | 222 + .../src/components/ai-elements/checkpoint.tsx | 71 + .../src/components/ai-elements/code-block.tsx | 563 + .../src/components/ai-elements/commit.tsx | 458 + .../components/ai-elements/confirmation.tsx | 174 + .../src/components/ai-elements/connection.tsx | 28 + .../src/components/ai-elements/context.tsx | 409 + .../src/components/ai-elements/controls.tsx | 18 + .../components/ai-elements/conversation.tsx | 168 + .../src/components/ai-elements/edge.tsx | 143 + .../ai-elements/environment-variables.tsx | 324 + .../src/components/ai-elements/file-tree.tsx | 304 + .../src/components/ai-elements/image.tsx | 24 + .../ai-elements/inline-citation.tsx | 296 + .../components/ai-elements/jsx-preview.tsx | 310 + .../src/components/ai-elements/message.tsx | 357 + .../components/ai-elements/mic-selector.tsx | 375 + .../components/ai-elements/model-selector.tsx | 213 + .../src/components/ai-elements/node.tsx | 80 + .../components/ai-elements/open-in-chat.tsx | 370 + .../components/ai-elements/package-info.tsx | 239 + .../src/components/ai-elements/panel.tsx | 15 + .../src/components/ai-elements/persona.tsx | 306 + .../src/components/ai-elements/plan.tsx | 147 + .../components/ai-elements/prompt-input.tsx | 1463 +++ .../src/components/ai-elements/queue.tsx | 274 + .../src/components/ai-elements/reasoning.tsx | 226 + .../src/components/ai-elements/sandbox.tsx | 127 + .../components/ai-elements/schema-display.tsx | 473 + .../src/components/ai-elements/shimmer.tsx | 77 + .../src/components/ai-elements/snippet.tsx | 145 + .../src/components/ai-elements/sources.tsx | 77 + .../components/ai-elements/speech-input.tsx | 323 + .../components/ai-elements/stack-trace.tsx | 528 + .../src/components/ai-elements/suggestion.tsx | 54 + .../src/components/ai-elements/task.tsx | 87 + .../src/components/ai-elements/terminal.tsx | 273 + .../components/ai-elements/test-results.tsx | 496 + .../src/components/ai-elements/tool.tsx | 173 + .../src/components/ai-elements/toolbar.tsx | 16 + .../components/ai-elements/transcription.tsx | 125 + .../components/ai-elements/voice-selector.tsx | 524 + .../components/ai-elements/web-preview.tsx | 281 + .../components/theme-panel/ColorControls.tsx | 157 + .../components/theme-panel/ColorSwatch.tsx | 29 + .../components/theme-panel/ExportReset.tsx | 56 + .../components/theme-panel/PresetSelector.tsx | 61 + .../components/theme-panel/RadiusControls.tsx | 64 + .../components/theme-panel/ShadowControls.tsx | 64 + .../src/components/theme-panel/ThemePanel.tsx | 100 + .../theme-panel/ThemePanelTrigger.tsx | 20 + .../theme-panel/TypographyControls.tsx | 194 + .../src/components/theme/ThemeControls.tsx | 18 + .../src/components/theme/ThemeProvider.tsx | 6 + .../src/components/theme/ThemeToggle.tsx | 50 + .../ghost-ui/src/components/ui/accordion.tsx | 66 + .../src/components/ui/alert-dialog.tsx | 159 + packages/ghost-ui/src/components/ui/alert.tsx | 66 + .../src/components/ui/aspect-ratio.tsx | 11 + .../ghost-ui/src/components/ui/avatar.tsx | 53 + packages/ghost-ui/src/components/ui/badge.tsx | 46 + .../ghost-ui/src/components/ui/breadcrumb.tsx | 109 + .../src/components/ui/button-group.tsx | 82 + .../ghost-ui/src/components/ui/button.tsx | 95 + .../ghost-ui/src/components/ui/calendar.tsx | 76 + packages/ghost-ui/src/components/ui/card.tsx | 95 + .../ghost-ui/src/components/ui/carousel.tsx | 238 + packages/ghost-ui/src/components/ui/chart.tsx | 384 + .../ghost-ui/src/components/ui/checkbox.tsx | 32 + .../src/components/ui/collapsible.tsx | 33 + .../ghost-ui/src/components/ui/command.tsx | 176 + .../src/components/ui/context-menu.tsx | 252 + .../ghost-ui/src/components/ui/dialog.tsx | 138 + .../ghost-ui/src/components/ui/drawer.tsx | 135 + .../src/components/ui/dropdown-menu.tsx | 254 + packages/ghost-ui/src/components/ui/form.tsx | 167 + .../ghost-ui/src/components/ui/hover-card.tsx | 44 + .../src/components/ui/input-group.tsx | 169 + .../ghost-ui/src/components/ui/input-otp.tsx | 77 + packages/ghost-ui/src/components/ui/input.tsx | 21 + packages/ghost-ui/src/components/ui/label.tsx | 24 + .../ghost-ui/src/components/ui/menubar.tsx | 273 + .../src/components/ui/navigation-menu.tsx | 168 + .../ghost-ui/src/components/ui/pagination.tsx | 125 + .../ghost-ui/src/components/ui/popover.tsx | 48 + .../ghost-ui/src/components/ui/progress.tsx | 31 + .../src/components/ui/radio-group.tsx | 45 + .../ghost-ui/src/components/ui/resizable.tsx | 56 + .../src/components/ui/scroll-area.tsx | 58 + .../ghost-ui/src/components/ui/select.tsx | 185 + .../ghost-ui/src/components/ui/separator.tsx | 28 + packages/ghost-ui/src/components/ui/sheet.tsx | 142 + .../ghost-ui/src/components/ui/sidebar.tsx | 725 ++ .../ghost-ui/src/components/ui/skeleton.tsx | 13 + .../ghost-ui/src/components/ui/slider.tsx | 63 + .../ghost-ui/src/components/ui/sonner.tsx | 25 + .../ghost-ui/src/components/ui/spinner.tsx | 16 + .../ghost-ui/src/components/ui/switch.tsx | 31 + packages/ghost-ui/src/components/ui/table.tsx | 113 + packages/ghost-ui/src/components/ui/tabs.tsx | 66 + .../ghost-ui/src/components/ui/textarea.tsx | 18 + .../src/components/ui/toggle-group.tsx | 72 + .../ghost-ui/src/components/ui/toggle.tsx | 47 + .../ghost-ui/src/components/ui/tooltip.tsx | 61 + .../ghost-ui/src/contexts/ThemeContext.tsx | 65 + .../src/contexts/ThemePanelContext.tsx | 249 + .../src/hooks/use-copy-to-clipboard.ts | 35 + .../src/hooks/use-intersection-observer.ts | 48 + packages/ghost-ui/src/hooks/use-mobile.ts | 21 + .../ghost-ui/src/hooks/use-scroll-reveal.ts | 109 + .../ghost-ui/src/hooks/use-text-animator.tsx | 262 + .../ghost-ui/src/lib/component-registry.ts | 158 + packages/ghost-ui/src/lib/component-source.ts | 162 + packages/ghost-ui/src/lib/theme-defaults.ts | 159 + packages/ghost-ui/src/lib/theme-presets.ts | 402 + packages/ghost-ui/src/lib/theme-provider.tsx | 135 + packages/ghost-ui/src/lib/theme-utils.ts | 79 + packages/ghost-ui/src/lib/utils.ts | 20 + .../ghost-ui/src/store/preferences-store.ts | 110 + packages/ghost-ui/src/styles/main.css | 646 ++ packages/ghost-ui/src/types/split-type.d.ts | 18 + packages/ghost-ui/src/types/theme.ts | 22 + packages/ghost-ui/tsconfig.json | 22 + pnpm-lock.yaml | 9261 +++++++++++++++-- 134 files changed, 31443 insertions(+), 670 deletions(-) create mode 100644 packages/ghost-ui/components.json create mode 100644 packages/ghost-ui/package.json create mode 100644 packages/ghost-ui/registry.json create mode 100644 packages/ghost-ui/src/components/ai-elements/agent.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/artifact.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/attachments.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/audio-player.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/canvas.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/chain-of-thought.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/checkpoint.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/code-block.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/commit.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/confirmation.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/connection.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/context.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/controls.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/conversation.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/edge.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/environment-variables.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/file-tree.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/image.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/inline-citation.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/jsx-preview.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/message.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/mic-selector.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/model-selector.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/node.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/open-in-chat.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/package-info.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/panel.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/persona.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/plan.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/prompt-input.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/queue.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/reasoning.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/sandbox.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/schema-display.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/shimmer.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/snippet.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/sources.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/speech-input.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/stack-trace.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/suggestion.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/task.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/terminal.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/test-results.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/tool.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/toolbar.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/transcription.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/voice-selector.tsx create mode 100644 packages/ghost-ui/src/components/ai-elements/web-preview.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/ColorControls.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/ColorSwatch.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/ExportReset.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/PresetSelector.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/RadiusControls.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/ShadowControls.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/ThemePanel.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/ThemePanelTrigger.tsx create mode 100644 packages/ghost-ui/src/components/theme-panel/TypographyControls.tsx create mode 100644 packages/ghost-ui/src/components/theme/ThemeControls.tsx create mode 100644 packages/ghost-ui/src/components/theme/ThemeProvider.tsx create mode 100644 packages/ghost-ui/src/components/theme/ThemeToggle.tsx create mode 100644 packages/ghost-ui/src/components/ui/accordion.tsx create mode 100644 packages/ghost-ui/src/components/ui/alert-dialog.tsx create mode 100644 packages/ghost-ui/src/components/ui/alert.tsx create mode 100644 packages/ghost-ui/src/components/ui/aspect-ratio.tsx create mode 100644 packages/ghost-ui/src/components/ui/avatar.tsx create mode 100644 packages/ghost-ui/src/components/ui/badge.tsx create mode 100644 packages/ghost-ui/src/components/ui/breadcrumb.tsx create mode 100644 packages/ghost-ui/src/components/ui/button-group.tsx create mode 100644 packages/ghost-ui/src/components/ui/button.tsx create mode 100644 packages/ghost-ui/src/components/ui/calendar.tsx create mode 100644 packages/ghost-ui/src/components/ui/card.tsx create mode 100644 packages/ghost-ui/src/components/ui/carousel.tsx create mode 100644 packages/ghost-ui/src/components/ui/chart.tsx create mode 100644 packages/ghost-ui/src/components/ui/checkbox.tsx create mode 100644 packages/ghost-ui/src/components/ui/collapsible.tsx create mode 100644 packages/ghost-ui/src/components/ui/command.tsx create mode 100644 packages/ghost-ui/src/components/ui/context-menu.tsx create mode 100644 packages/ghost-ui/src/components/ui/dialog.tsx create mode 100644 packages/ghost-ui/src/components/ui/drawer.tsx create mode 100644 packages/ghost-ui/src/components/ui/dropdown-menu.tsx create mode 100644 packages/ghost-ui/src/components/ui/form.tsx create mode 100644 packages/ghost-ui/src/components/ui/hover-card.tsx create mode 100644 packages/ghost-ui/src/components/ui/input-group.tsx create mode 100644 packages/ghost-ui/src/components/ui/input-otp.tsx create mode 100644 packages/ghost-ui/src/components/ui/input.tsx create mode 100644 packages/ghost-ui/src/components/ui/label.tsx create mode 100644 packages/ghost-ui/src/components/ui/menubar.tsx create mode 100644 packages/ghost-ui/src/components/ui/navigation-menu.tsx create mode 100644 packages/ghost-ui/src/components/ui/pagination.tsx create mode 100644 packages/ghost-ui/src/components/ui/popover.tsx create mode 100644 packages/ghost-ui/src/components/ui/progress.tsx create mode 100644 packages/ghost-ui/src/components/ui/radio-group.tsx create mode 100644 packages/ghost-ui/src/components/ui/resizable.tsx create mode 100644 packages/ghost-ui/src/components/ui/scroll-area.tsx create mode 100644 packages/ghost-ui/src/components/ui/select.tsx create mode 100644 packages/ghost-ui/src/components/ui/separator.tsx create mode 100644 packages/ghost-ui/src/components/ui/sheet.tsx create mode 100644 packages/ghost-ui/src/components/ui/sidebar.tsx create mode 100644 packages/ghost-ui/src/components/ui/skeleton.tsx create mode 100644 packages/ghost-ui/src/components/ui/slider.tsx create mode 100644 packages/ghost-ui/src/components/ui/sonner.tsx create mode 100644 packages/ghost-ui/src/components/ui/spinner.tsx create mode 100644 packages/ghost-ui/src/components/ui/switch.tsx create mode 100644 packages/ghost-ui/src/components/ui/table.tsx create mode 100644 packages/ghost-ui/src/components/ui/tabs.tsx create mode 100644 packages/ghost-ui/src/components/ui/textarea.tsx create mode 100644 packages/ghost-ui/src/components/ui/toggle-group.tsx create mode 100644 packages/ghost-ui/src/components/ui/toggle.tsx create mode 100644 packages/ghost-ui/src/components/ui/tooltip.tsx create mode 100644 packages/ghost-ui/src/contexts/ThemeContext.tsx create mode 100644 packages/ghost-ui/src/contexts/ThemePanelContext.tsx create mode 100644 packages/ghost-ui/src/hooks/use-copy-to-clipboard.ts create mode 100644 packages/ghost-ui/src/hooks/use-intersection-observer.ts create mode 100644 packages/ghost-ui/src/hooks/use-mobile.ts create mode 100644 packages/ghost-ui/src/hooks/use-scroll-reveal.ts create mode 100644 packages/ghost-ui/src/hooks/use-text-animator.tsx create mode 100644 packages/ghost-ui/src/lib/component-registry.ts create mode 100644 packages/ghost-ui/src/lib/component-source.ts create mode 100644 packages/ghost-ui/src/lib/theme-defaults.ts create mode 100644 packages/ghost-ui/src/lib/theme-presets.ts create mode 100644 packages/ghost-ui/src/lib/theme-provider.tsx create mode 100644 packages/ghost-ui/src/lib/theme-utils.ts create mode 100644 packages/ghost-ui/src/lib/utils.ts create mode 100644 packages/ghost-ui/src/store/preferences-store.ts create mode 100644 packages/ghost-ui/src/styles/main.css create mode 100644 packages/ghost-ui/src/types/split-type.d.ts create mode 100644 packages/ghost-ui/src/types/theme.ts create mode 100644 packages/ghost-ui/tsconfig.json diff --git a/biome.json b/biome.json index 70f4aec..165530e 100644 --- a/biome.json +++ b/biome.json @@ -22,9 +22,23 @@ } } }, + "css": { + "parser": { + "cssModules": false, + "tailwindDirectives": true + } + }, "javascript": { "formatter": { "quoteStyle": "double" } - } + }, + "overrides": [ + { + "includes": ["packages/ghost-ui/**"], + "linter": { + "enabled": false + } + } + ] } diff --git a/package.json b/package.json index 948fa12..699d1c6 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "test": "vitest run", "test:watch": "vitest", "typecheck": "tsc --build", + "build:ui": "pnpm --filter @ghost/ui build", "check": "biome check . && pnpm typecheck && pnpm check:file-sizes", "check:file-sizes": "node scripts/check-file-sizes.mjs", "fmt": "biome format --write .", diff --git a/packages/ghost-ui/components.json b/packages/ghost-ui/components.json new file mode 100644 index 0000000..9999f29 --- /dev/null +++ b/packages/ghost-ui/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "styles/main.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/packages/ghost-ui/package.json b/packages/ghost-ui/package.json new file mode 100644 index 0000000..6d32d38 --- /dev/null +++ b/packages/ghost-ui/package.json @@ -0,0 +1,92 @@ +{ + "name": "@ghost/ui", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "shadcn build", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@hookform/resolvers": "^5.2.2", + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-aspect-ratio": "^1.1.8", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-context-menu": "^2.2.16", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-menubar": "^1.1.16", + "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slider": "^1.3.6", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-toggle": "^1.1.10", + "@radix-ui/react-toggle-group": "^1.1.11", + "@radix-ui/react-tooltip": "^1.2.8", + "@radix-ui/react-use-controllable-state": "^1.2.2", + "@rive-app/react-webgl2": "^4.27.3", + "@streamdown/cjk": "^1.0.3", + "@streamdown/code": "^1.1.1", + "@streamdown/math": "^1.0.2", + "@streamdown/mermaid": "^1.0.2", + "@tanstack/react-table": "^8.21.3", + "@xyflow/react": "^12.10.2", + "ai": "^6.0.141", + "ansi-to-react": "^6.2.6", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.6.0", + "gsap": "^3.14.2", + "input-otp": "^1.4.2", + "lucide-react": "^1.7.0", + "media-chrome": "^4.18.3", + "motion": "^12.38.0", + "nanoid": "^5.1.7", + "react-day-picker": "9.14.0", + "react-hook-form": "^7.72.0", + "react-jsx-parser": "^2.4.1", + "react-resizable-panels": "^4.8.0", + "recharts": "^3.8.1", + "shiki": "^4.0.2", + "smart-registry": "^1.16.0", + "sonner": "^2.0.7", + "split-type": "^0.3.4", + "streamdown": "^2.5.0", + "tailwind-merge": "^3.5.0", + "tokenlens": "^1.3.1", + "tw-animate-css": "^1.4.0", + "use-stick-to-bottom": "^1.1.3", + "vaul": "^1.1.2", + "zod": "^4.3.6", + "zustand": "^5.0.12" + }, + "peerDependencies": { + "react": ">=19.0.0", + "react-dom": ">=19.0.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.2.2", + "@types/node": "^22.0.0", + "@types/react": "19.1.2", + "@types/react-dom": "19.1.2", + "react": "19.1.0", + "react-dom": "19.1.0", + "shadcn": "4.1.1", + "tailwindcss": "^4.2.2", + "typescript": "^5.7.0" + } +} diff --git a/packages/ghost-ui/registry.json b/packages/ghost-ui/registry.json new file mode 100644 index 0000000..d623750 --- /dev/null +++ b/packages/ghost-ui/registry.json @@ -0,0 +1,1444 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry.json", + "name": "ghost-ui", + "items": [ + { + "name": "styles-main", + "type": "registry:style", + "dependencies": ["tw-animate-css"], + "devDependencies": ["tailwindcss"], + "files": [ + { + "type": "registry:theme", + "target": "app/globals.css", + "path": "src/styles/main.css" + } + ] + }, + { + "name": "utils", + "type": "registry:lib", + "dependencies": ["clsx", "tailwind-merge"], + "files": [ + { + "type": "registry:lib", + "target": "lib/utils.ts", + "path": "src/lib/utils.ts" + } + ] + }, + { + "name": "accordion", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-accordion", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/accordion.tsx", + "path": "src/components/ui/accordion.tsx" + } + ], + "categories": ["layout"] + }, + { + "name": "alert-dialog", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-alert-dialog"], + "registryDependencies": ["utils", "button"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/alert-dialog.tsx", + "path": "src/components/ui/alert-dialog.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "alert", + "type": "registry:ui", + "dependencies": ["class-variance-authority"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/alert.tsx", + "path": "src/components/ui/alert.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "aspect-ratio", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-aspect-ratio"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/aspect-ratio.tsx", + "path": "src/components/ui/aspect-ratio.tsx" + } + ], + "categories": ["layout"] + }, + { + "name": "avatar", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-avatar"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/avatar.tsx", + "path": "src/components/ui/avatar.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "badge", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-slot", "class-variance-authority"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/badge.tsx", + "path": "src/components/ui/badge.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "breadcrumb", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-slot", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/breadcrumb.tsx", + "path": "src/components/ui/breadcrumb.tsx" + } + ], + "categories": ["navigation"] + }, + { + "name": "button-group", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-slot", "class-variance-authority"], + "registryDependencies": ["utils", "separator"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/button-group.tsx", + "path": "src/components/ui/button-group.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "button", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-slot", "class-variance-authority"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/button.tsx", + "path": "src/components/ui/button.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "calendar", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/calendar.tsx", + "path": "src/components/ui/calendar.tsx" + } + ], + "categories": ["display", "input"] + }, + { + "name": "card", + "type": "registry:ui", + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/card.tsx", + "path": "src/components/ui/card.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "carousel", + "type": "registry:ui", + "dependencies": ["embla-carousel-react", "lucide-react"], + "registryDependencies": ["utils", "button"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/carousel.tsx", + "path": "src/components/ui/carousel.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "chart", + "type": "registry:ui", + "dependencies": ["recharts"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/chart.tsx", + "path": "src/components/ui/chart.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "checkbox", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-checkbox", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/checkbox.tsx", + "path": "src/components/ui/checkbox.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "collapsible", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/collapsible.tsx", + "path": "src/components/ui/collapsible.tsx" + } + ], + "categories": ["layout"] + }, + { + "name": "command", + "type": "registry:ui", + "dependencies": ["cmdk", "lucide-react"], + "registryDependencies": ["utils", "dialog"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/command.tsx", + "path": "src/components/ui/command.tsx" + } + ], + "categories": ["input", "navigation"] + }, + { + "name": "context-menu", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-context-menu", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/context-menu.tsx", + "path": "src/components/ui/context-menu.tsx" + } + ], + "categories": ["navigation"] + }, + { + "name": "dialog", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-dialog", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/dialog.tsx", + "path": "src/components/ui/dialog.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "drawer", + "type": "registry:ui", + "dependencies": ["vaul"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/drawer.tsx", + "path": "src/components/ui/drawer.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "dropdown-menu", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-dropdown-menu", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/dropdown-menu.tsx", + "path": "src/components/ui/dropdown-menu.tsx" + } + ], + "categories": ["navigation"] + }, + { + "name": "form", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-label", "@radix-ui/react-slot"], + "registryDependencies": ["utils", "label"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/form.tsx", + "path": "src/components/ui/form.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "hover-card", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-hover-card"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/hover-card.tsx", + "path": "src/components/ui/hover-card.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "input-group", + "type": "registry:ui", + "dependencies": ["class-variance-authority"], + "registryDependencies": ["utils", "button", "input", "textarea"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/input-group.tsx", + "path": "src/components/ui/input-group.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "input-otp", + "type": "registry:ui", + "dependencies": ["input-otp", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/input-otp.tsx", + "path": "src/components/ui/input-otp.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "input", + "type": "registry:ui", + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/input.tsx", + "path": "src/components/ui/input.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "label", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-label"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/label.tsx", + "path": "src/components/ui/label.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "menubar", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-menubar", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/menubar.tsx", + "path": "src/components/ui/menubar.tsx" + } + ], + "categories": ["navigation"] + }, + { + "name": "navigation-menu", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-navigation-menu", + "class-variance-authority", + "lucide-react" + ], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/navigation-menu.tsx", + "path": "src/components/ui/navigation-menu.tsx" + } + ], + "categories": ["navigation"] + }, + { + "name": "pagination", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/pagination.tsx", + "path": "src/components/ui/pagination.tsx" + } + ], + "categories": ["navigation"] + }, + { + "name": "popover", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-popover"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/popover.tsx", + "path": "src/components/ui/popover.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "progress", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-progress"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/progress.tsx", + "path": "src/components/ui/progress.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "radio-group", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-radio-group", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/radio-group.tsx", + "path": "src/components/ui/radio-group.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "resizable", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/resizable.tsx", + "path": "src/components/ui/resizable.tsx" + } + ], + "categories": ["layout"] + }, + { + "name": "scroll-area", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-scroll-area"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/scroll-area.tsx", + "path": "src/components/ui/scroll-area.tsx" + } + ], + "categories": ["layout"] + }, + { + "name": "select", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-select", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/select.tsx", + "path": "src/components/ui/select.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "separator", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-separator"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/separator.tsx", + "path": "src/components/ui/separator.tsx" + } + ], + "categories": ["layout"] + }, + { + "name": "sheet", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-dialog", "lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/sheet.tsx", + "path": "src/components/ui/sheet.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "sidebar", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-slot", + "class-variance-authority", + "lucide-react" + ], + "registryDependencies": [ + "utils", + "button", + "input", + "separator", + "sheet", + "skeleton", + "tooltip" + ], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/sidebar.tsx", + "path": "src/components/ui/sidebar.tsx" + } + ], + "categories": ["navigation", "layout"] + }, + { + "name": "skeleton", + "type": "registry:ui", + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/skeleton.tsx", + "path": "src/components/ui/skeleton.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "slider", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-slider"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/slider.tsx", + "path": "src/components/ui/slider.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "sonner", + "type": "registry:ui", + "dependencies": ["next-themes", "sonner"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/sonner.tsx", + "path": "src/components/ui/sonner.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "spinner", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/spinner.tsx", + "path": "src/components/ui/spinner.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "switch", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-switch"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/switch.tsx", + "path": "src/components/ui/switch.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "table", + "type": "registry:ui", + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/table.tsx", + "path": "src/components/ui/table.tsx" + } + ], + "categories": ["display"] + }, + { + "name": "tabs", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-tabs"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/tabs.tsx", + "path": "src/components/ui/tabs.tsx" + } + ], + "categories": ["navigation"] + }, + { + "name": "textarea", + "type": "registry:ui", + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/textarea.tsx", + "path": "src/components/ui/textarea.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "toggle-group", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-toggle-group", + "class-variance-authority" + ], + "registryDependencies": ["utils", "toggle"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/toggle-group.tsx", + "path": "src/components/ui/toggle-group.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "toggle", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-toggle", "class-variance-authority"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/toggle.tsx", + "path": "src/components/ui/toggle.tsx" + } + ], + "categories": ["input"] + }, + { + "name": "tooltip", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-tooltip"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ui/tooltip.tsx", + "path": "src/components/ui/tooltip.tsx" + } + ], + "categories": ["feedback"] + }, + { + "name": "agent", + "type": "registry:ui", + "dependencies": ["ai", "lucide-react"], + "registryDependencies": ["utils", "accordion", "badge"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/agent.tsx", + "path": "src/components/ai-elements/agent.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "artifact", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button", "tooltip"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/artifact.tsx", + "path": "src/components/ai-elements/artifact.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "attachments", + "type": "registry:ui", + "dependencies": ["ai", "lucide-react"], + "registryDependencies": ["utils", "button", "hover-card"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/attachments.tsx", + "path": "src/components/ai-elements/attachments.tsx" + } + ], + "categories": ["ai", "input", "chat"] + }, + { + "name": "audio-player", + "type": "registry:ui", + "dependencies": ["ai", "media-chrome"], + "registryDependencies": ["utils", "button", "button-group"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/audio-player.tsx", + "path": "src/components/ai-elements/audio-player.tsx" + } + ], + "categories": ["ai", "media"] + }, + { + "name": "canvas", + "type": "registry:ui", + "dependencies": ["@xyflow/react"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/canvas.tsx", + "path": "src/components/ai-elements/canvas.tsx" + } + ], + "categories": ["ai", "layout"] + }, + { + "name": "chain-of-thought", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-use-controllable-state", + "lucide-react" + ], + "registryDependencies": ["utils", "badge", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/chain-of-thought.tsx", + "path": "src/components/ai-elements/chain-of-thought.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "checkpoint", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button", "separator", "tooltip"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/checkpoint.tsx", + "path": "src/components/ai-elements/checkpoint.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "code-block", + "type": "registry:ui", + "dependencies": ["lucide-react", "shiki"], + "registryDependencies": ["utils", "button", "select"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/code-block.tsx", + "path": "src/components/ai-elements/code-block.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "commit", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "avatar", "button", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/commit.tsx", + "path": "src/components/ai-elements/commit.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "confirmation", + "type": "registry:ui", + "dependencies": ["ai"], + "registryDependencies": ["utils", "alert", "button"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/confirmation.tsx", + "path": "src/components/ai-elements/confirmation.tsx" + } + ], + "categories": ["ai", "feedback"] + }, + { + "name": "connection", + "type": "registry:ui", + "dependencies": ["@xyflow/react"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/connection.tsx", + "path": "src/components/ai-elements/connection.tsx" + } + ], + "categories": ["ai", "layout"] + }, + { + "name": "context", + "type": "registry:ui", + "dependencies": ["ai", "tokenlens"], + "registryDependencies": ["utils", "button", "hover-card", "progress"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/context.tsx", + "path": "src/components/ai-elements/context.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "controls", + "type": "registry:ui", + "dependencies": ["@xyflow/react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/controls.tsx", + "path": "src/components/ai-elements/controls.tsx" + } + ], + "categories": ["ai", "layout"] + }, + { + "name": "conversation", + "type": "registry:ui", + "dependencies": ["ai", "lucide-react", "use-stick-to-bottom"], + "registryDependencies": ["utils", "button"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/conversation.tsx", + "path": "src/components/ai-elements/conversation.tsx" + } + ], + "categories": ["ai", "chat"] + }, + { + "name": "edge", + "type": "registry:ui", + "dependencies": ["@xyflow/react"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/edge.tsx", + "path": "src/components/ai-elements/edge.tsx" + } + ], + "categories": ["ai", "layout"] + }, + { + "name": "environment-variables", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "badge", "button", "switch"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/environment-variables.tsx", + "path": "src/components/ai-elements/environment-variables.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "file-tree", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/file-tree.tsx", + "path": "src/components/ai-elements/file-tree.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "image", + "type": "registry:ui", + "dependencies": ["ai"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/image.tsx", + "path": "src/components/ai-elements/image.tsx" + } + ], + "categories": ["ai", "media"] + }, + { + "name": "inline-citation", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "badge", "carousel", "hover-card"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/inline-citation.tsx", + "path": "src/components/ai-elements/inline-citation.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "jsx-preview", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/jsx-preview.tsx", + "path": "src/components/ai-elements/jsx-preview.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "message", + "type": "registry:ui", + "dependencies": [ + "@streamdown/cjk", + "@streamdown/code", + "@streamdown/math", + "@streamdown/mermaid", + "ai", + "lucide-react", + "streamdown" + ], + "registryDependencies": ["utils", "button", "button-group", "tooltip"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/message.tsx", + "path": "src/components/ai-elements/message.tsx" + } + ], + "categories": ["ai", "chat"] + }, + { + "name": "mic-selector", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-use-controllable-state", + "lucide-react" + ], + "registryDependencies": ["utils", "button", "command", "popover"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/mic-selector.tsx", + "path": "src/components/ai-elements/mic-selector.tsx" + } + ], + "categories": ["ai", "input", "media"] + }, + { + "name": "model-selector", + "type": "registry:ui", + "registryDependencies": ["utils", "command", "dialog"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/model-selector.tsx", + "path": "src/components/ai-elements/model-selector.tsx" + } + ], + "categories": ["ai", "input"] + }, + { + "name": "node", + "type": "registry:ui", + "dependencies": ["@xyflow/react"], + "registryDependencies": ["utils", "card"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/node.tsx", + "path": "src/components/ai-elements/node.tsx" + } + ], + "categories": ["ai", "layout"] + }, + { + "name": "open-in-chat", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button", "dropdown-menu"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/open-in-chat.tsx", + "path": "src/components/ai-elements/open-in-chat.tsx" + } + ], + "categories": ["ai", "chat"] + }, + { + "name": "package-info", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "badge"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/package-info.tsx", + "path": "src/components/ai-elements/package-info.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "panel", + "type": "registry:ui", + "dependencies": ["@xyflow/react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/panel.tsx", + "path": "src/components/ai-elements/panel.tsx" + } + ], + "categories": ["ai", "layout"] + }, + { + "name": "persona", + "type": "registry:ui", + "dependencies": ["@rive-app/react-webgl2"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/persona.tsx", + "path": "src/components/ai-elements/persona.tsx" + } + ], + "categories": ["ai", "media"] + }, + { + "name": "plan", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button", "card", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/plan.tsx", + "path": "src/components/ai-elements/plan.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "prompt-input", + "type": "registry:ui", + "dependencies": ["ai", "lucide-react", "nanoid"], + "registryDependencies": [ + "utils", + "command", + "dropdown-menu", + "hover-card", + "input-group", + "select", + "spinner", + "tooltip" + ], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/prompt-input.tsx", + "path": "src/components/ai-elements/prompt-input.tsx" + } + ], + "categories": ["ai", "input", "chat"] + }, + { + "name": "queue", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button", "collapsible", "scroll-area"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/queue.tsx", + "path": "src/components/ai-elements/queue.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "reasoning", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-use-controllable-state", + "@streamdown/cjk", + "@streamdown/code", + "@streamdown/math", + "@streamdown/mermaid", + "lucide-react", + "streamdown" + ], + "registryDependencies": ["utils", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/reasoning.tsx", + "path": "src/components/ai-elements/reasoning.tsx" + } + ], + "categories": ["ai", "chat"] + }, + { + "name": "sandbox", + "type": "registry:ui", + "dependencies": ["ai", "lucide-react"], + "registryDependencies": ["utils", "collapsible", "tabs"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/sandbox.tsx", + "path": "src/components/ai-elements/sandbox.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "schema-display", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "badge", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/schema-display.tsx", + "path": "src/components/ai-elements/schema-display.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "shimmer", + "type": "registry:ui", + "dependencies": ["motion"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/shimmer.tsx", + "path": "src/components/ai-elements/shimmer.tsx" + } + ], + "categories": ["ai", "feedback"] + }, + { + "name": "snippet", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "input-group"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/snippet.tsx", + "path": "src/components/ai-elements/snippet.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "sources", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/sources.tsx", + "path": "src/components/ai-elements/sources.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "speech-input", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "button", "spinner"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/speech-input.tsx", + "path": "src/components/ai-elements/speech-input.tsx" + } + ], + "categories": ["ai", "input", "media"] + }, + { + "name": "stack-trace", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-use-controllable-state", + "lucide-react" + ], + "registryDependencies": ["utils", "button", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/stack-trace.tsx", + "path": "src/components/ai-elements/stack-trace.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "suggestion", + "type": "registry:ui", + "registryDependencies": ["utils", "button", "scroll-area"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/suggestion.tsx", + "path": "src/components/ai-elements/suggestion.tsx" + } + ], + "categories": ["ai", "chat"] + }, + { + "name": "task", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/task.tsx", + "path": "src/components/ai-elements/task.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "terminal", + "type": "registry:ui", + "dependencies": ["ansi-to-react", "lucide-react"], + "registryDependencies": ["utils", "button"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/terminal.tsx", + "path": "src/components/ai-elements/terminal.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "test-results", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": ["utils", "badge", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/test-results.tsx", + "path": "src/components/ai-elements/test-results.tsx" + } + ], + "categories": ["ai", "code"] + }, + { + "name": "tool", + "type": "registry:ui", + "dependencies": ["ai", "lucide-react"], + "registryDependencies": ["utils", "badge", "collapsible"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/tool.tsx", + "path": "src/components/ai-elements/tool.tsx" + } + ], + "categories": ["ai", "display"] + }, + { + "name": "toolbar", + "type": "registry:ui", + "dependencies": ["@xyflow/react"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/toolbar.tsx", + "path": "src/components/ai-elements/toolbar.tsx" + } + ], + "categories": ["ai", "layout"] + }, + { + "name": "transcription", + "type": "registry:ui", + "dependencies": ["@radix-ui/react-use-controllable-state", "ai"], + "registryDependencies": ["utils"], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/transcription.tsx", + "path": "src/components/ai-elements/transcription.tsx" + } + ], + "categories": ["ai", "chat", "media"] + }, + { + "name": "voice-selector", + "type": "registry:ui", + "dependencies": [ + "@radix-ui/react-use-controllable-state", + "lucide-react" + ], + "registryDependencies": [ + "utils", + "button", + "command", + "dialog", + "spinner" + ], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/voice-selector.tsx", + "path": "src/components/ai-elements/voice-selector.tsx" + } + ], + "categories": ["ai", "input", "media"] + }, + { + "name": "web-preview", + "type": "registry:ui", + "dependencies": ["lucide-react"], + "registryDependencies": [ + "utils", + "button", + "collapsible", + "input", + "tooltip" + ], + "files": [ + { + "type": "registry:ui", + "target": "components/ai-elements/web-preview.tsx", + "path": "src/components/ai-elements/web-preview.tsx" + } + ], + "categories": ["ai", "media"] + } + ] +} diff --git a/packages/ghost-ui/src/components/ai-elements/agent.tsx b/packages/ghost-ui/src/components/ai-elements/agent.tsx new file mode 100644 index 0000000..263fe68 --- /dev/null +++ b/packages/ghost-ui/src/components/ai-elements/agent.tsx @@ -0,0 +1,141 @@ +"use client"; + +import type { Tool } from "ai"; +import { BotIcon } from "lucide-react"; +import type { ComponentProps } from "react"; +import { memo } from "react"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Badge } from "@/components/ui/badge"; +import { cn } from "@/lib/utils"; + +import { CodeBlock } from "./code-block"; + +export type AgentProps = ComponentProps<"div">; + +export const Agent = memo(({ className, ...props }: AgentProps) => ( +
+)); + +export type AgentHeaderProps = ComponentProps<"div"> & { + name: string; + model?: string; +}; + +export const AgentHeader = memo( + ({ className, name, model, ...props }: AgentHeaderProps) => ( +
+
+ + {name} + {model && ( + + {model} + + )} +
+
+ ), +); + +export type AgentContentProps = ComponentProps<"div">; + +export const AgentContent = memo( + ({ className, ...props }: AgentContentProps) => ( +
+ ), +); + +export type AgentInstructionsProps = ComponentProps<"div"> & { + children: string; +}; + +export const AgentInstructions = memo( + ({ className, children, ...props }: AgentInstructionsProps) => ( +
+ + Instructions + +
+

{children}

+
+
+ ), +); + +export type AgentToolsProps = ComponentProps; + +export const AgentTools = memo(({ className, ...props }: AgentToolsProps) => ( +
+ Tools + +
+)); + +export type AgentToolProps = ComponentProps & { + tool: Tool; +}; + +export const AgentTool = memo( + ({ className, tool, value, ...props }: AgentToolProps) => { + const schema = + "jsonSchema" in tool && tool.jsonSchema + ? tool.jsonSchema + : tool.inputSchema; + + return ( + + + {tool.description ?? "No description"} + + +
+ +
+
+
+ ); + }, +); + +export type AgentOutputProps = ComponentProps<"div"> & { + schema: string; +}; + +export const AgentOutput = memo( + ({ className, schema, ...props }: AgentOutputProps) => ( +
+ + Output Schema + +
+ +
+
+ ), +); + +Agent.displayName = "Agent"; +AgentHeader.displayName = "AgentHeader"; +AgentContent.displayName = "AgentContent"; +AgentInstructions.displayName = "AgentInstructions"; +AgentTools.displayName = "AgentTools"; +AgentTool.displayName = "AgentTool"; +AgentOutput.displayName = "AgentOutput"; diff --git a/packages/ghost-ui/src/components/ai-elements/artifact.tsx b/packages/ghost-ui/src/components/ai-elements/artifact.tsx new file mode 100644 index 0000000..259e10a --- /dev/null +++ b/packages/ghost-ui/src/components/ai-elements/artifact.tsx @@ -0,0 +1,148 @@ +"use client"; + +import type { LucideIcon } from "lucide-react"; +import { XIcon } from "lucide-react"; +import type { ComponentProps, HTMLAttributes } from "react"; +import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; + +export type ArtifactProps = HTMLAttributes; + +export const Artifact = ({ className, ...props }: ArtifactProps) => ( +
+); + +export type ArtifactHeaderProps = HTMLAttributes; + +export const ArtifactHeader = ({ + className, + ...props +}: ArtifactHeaderProps) => ( +
+); + +export type ArtifactCloseProps = ComponentProps; + +export const ArtifactClose = ({ + className, + children, + size = "sm", + variant = "ghost", + ...props +}: ArtifactCloseProps) => ( + +); + +export type ArtifactTitleProps = HTMLAttributes; + +export const ArtifactTitle = ({ className, ...props }: ArtifactTitleProps) => ( +

+); + +export type ArtifactDescriptionProps = HTMLAttributes; + +export const ArtifactDescription = ({ + className, + ...props +}: ArtifactDescriptionProps) => ( +

+); + +export type ArtifactActionsProps = HTMLAttributes; + +export const ArtifactActions = ({ + className, + ...props +}: ArtifactActionsProps) => ( +

+); + +export type ArtifactActionProps = ComponentProps & { + tooltip?: string; + label?: string; + icon?: LucideIcon; +}; + +export const ArtifactAction = ({ + tooltip, + label, + icon: Icon, + children, + className, + size = "sm", + variant = "ghost", + ...props +}: ArtifactActionProps) => { + const button = ( + + ); + + if (tooltip) { + return ( + + + {button} + +

{tooltip}

+
+
+
+ ); + } + + return button; +}; + +export type ArtifactContentProps = HTMLAttributes; + +export const ArtifactContent = ({ + className, + ...props +}: ArtifactContentProps) => ( +
+); diff --git a/packages/ghost-ui/src/components/ai-elements/attachments.tsx b/packages/ghost-ui/src/components/ai-elements/attachments.tsx new file mode 100644 index 0000000..71245c4 --- /dev/null +++ b/packages/ghost-ui/src/components/ai-elements/attachments.tsx @@ -0,0 +1,426 @@ +"use client"; + +import type { FileUIPart, SourceDocumentUIPart } from "ai"; +import { + FileTextIcon, + GlobeIcon, + ImageIcon, + Music2Icon, + PaperclipIcon, + VideoIcon, + XIcon, +} from "lucide-react"; +import type { ComponentProps, HTMLAttributes, ReactNode } from "react"; +import { createContext, useCallback, useContext, useMemo } from "react"; +import { Button } from "@/components/ui/button"; +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from "@/components/ui/hover-card"; +import { cn } from "@/lib/utils"; + +// ============================================================================ +// Types +// ============================================================================ + +export type AttachmentData = + | (FileUIPart & { id: string }) + | (SourceDocumentUIPart & { id: string }); + +export type AttachmentMediaCategory = + | "image" + | "video" + | "audio" + | "document" + | "source" + | "unknown"; + +export type AttachmentVariant = "grid" | "inline" | "list"; + +const mediaCategoryIcons: Record = { + audio: Music2Icon, + document: FileTextIcon, + image: ImageIcon, + source: GlobeIcon, + unknown: PaperclipIcon, + video: VideoIcon, +}; + +// ============================================================================ +// Utility Functions +// ============================================================================ + +export const getMediaCategory = ( + data: AttachmentData, +): AttachmentMediaCategory => { + if (data.type === "source-document") { + return "source"; + } + + const mediaType = data.mediaType ?? ""; + + if (mediaType.startsWith("image/")) { + return "image"; + } + if (mediaType.startsWith("video/")) { + return "video"; + } + if (mediaType.startsWith("audio/")) { + return "audio"; + } + if (mediaType.startsWith("application/") || mediaType.startsWith("text/")) { + return "document"; + } + + return "unknown"; +}; + +export const getAttachmentLabel = (data: AttachmentData): string => { + if (data.type === "source-document") { + return data.title || data.filename || "Source"; + } + + const category = getMediaCategory(data); + return data.filename || (category === "image" ? "Image" : "Attachment"); +}; + +const renderAttachmentImage = ( + url: string, + filename: string | undefined, + isGrid: boolean, +) => + isGrid ? ( + {filename + ) : ( + {filename + ); + +// ============================================================================ +// Contexts +// ============================================================================ + +interface AttachmentsContextValue { + variant: AttachmentVariant; +} + +const AttachmentsContext = createContext(null); + +interface AttachmentContextValue { + data: AttachmentData; + mediaCategory: AttachmentMediaCategory; + onRemove?: () => void; + variant: AttachmentVariant; +} + +const AttachmentContext = createContext(null); + +// ============================================================================ +// Hooks +// ============================================================================ + +export const useAttachmentsContext = () => + useContext(AttachmentsContext) ?? { variant: "grid" as const }; + +export const useAttachmentContext = () => { + const ctx = useContext(AttachmentContext); + if (!ctx) { + throw new Error("Attachment components must be used within "); + } + return ctx; +}; + +// ============================================================================ +// Attachments - Container +// ============================================================================ + +export type AttachmentsProps = HTMLAttributes & { + variant?: AttachmentVariant; +}; + +export const Attachments = ({ + variant = "grid", + className, + children, + ...props +}: AttachmentsProps) => { + const contextValue = useMemo(() => ({ variant }), [variant]); + + return ( + +
+ {children} +
+
+ ); +}; + +// ============================================================================ +// Attachment - Item +// ============================================================================ + +export type AttachmentProps = HTMLAttributes & { + data: AttachmentData; + onRemove?: () => void; +}; + +export const Attachment = ({ + data, + onRemove, + className, + children, + ...props +}: AttachmentProps) => { + const { variant } = useAttachmentsContext(); + const mediaCategory = getMediaCategory(data); + + const contextValue = useMemo( + () => ({ data, mediaCategory, onRemove, variant }), + [data, mediaCategory, onRemove, variant], + ); + + return ( + +
+ {children} +
+
+ ); +}; + +// ============================================================================ +// AttachmentPreview - Media preview +// ============================================================================ + +export type AttachmentPreviewProps = HTMLAttributes & { + fallbackIcon?: ReactNode; +}; + +export const AttachmentPreview = ({ + fallbackIcon, + className, + ...props +}: AttachmentPreviewProps) => { + const { data, mediaCategory, variant } = useAttachmentContext(); + + const iconSize = variant === "inline" ? "size-3" : "size-4"; + + const renderIcon = (Icon: typeof ImageIcon) => ( + + ); + + const renderContent = () => { + if (mediaCategory === "image" && data.type === "file" && data.url) { + return renderAttachmentImage(data.url, data.filename, variant === "grid"); + } + + if (mediaCategory === "video" && data.type === "file" && data.url) { + return