Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ CI order: **lint → typecheck** (no tests)
## Registry build

`pnpm registry:build` runs `scripts/registry-build.ts`:

1. `shadcn-vue-registry` generates registry.json from `app/registry/`
2. `shadcn-vue build` outputs per-item JSON to `public/r/*.json`
3. Intermediate file `app/registry/registry.json` is auto-deleted
Expand Down
2 changes: 1 addition & 1 deletion app/components/demo/LucideIconComponent.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { Heart } from "lucide-vue-next";
import { LucideIcon } from "~/registry/blocks/lucide-icon";
import { LucideIcon } from "~/registry/ui/lucide-icon";
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion app/components/demo/LucideIconDemo.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { LucideIcon } from "~/registry/blocks/lucide-icon";
import { LucideIcon } from "~/registry/ui/lucide-icon";
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion app/components/demo/LucideIconSize.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { LucideIcon } from "~/registry/blocks/lucide-icon";
import { LucideIcon } from "~/registry/ui/lucide-icon";
</script>

<template>
Expand Down
1 change: 0 additions & 1 deletion app/components/ui/icon/index.ts

This file was deleted.

1 change: 1 addition & 0 deletions app/components/ui/lucide-icon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { LucideIcon, type LucideIconProps } from "~/registry/ui/lucide-icon";
2 changes: 1 addition & 1 deletion app/registry/blocks/chat-message/ChatMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { HTMLAttributes } from "vue";
import type { UIMessage } from "ai";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { LucideIcon } from "@/components/ui/icon";
import { LucideIcon } from "@/components/ui/lucide-icon";
import { cn } from "@/lib/utils";

export interface ChatMessageAction {
Expand Down
8 changes: 0 additions & 8 deletions app/registry/blocks/hello-world/HelloWorld.vue

This file was deleted.

1 change: 0 additions & 1 deletion app/registry/blocks/hello-world/index.ts

This file was deleted.

4 changes: 2 additions & 2 deletions public/r/chat-message.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
],
"registryDependencies": [
"button",
"icon",
"lucide-icon",
"tooltip"
],
"files": [
Expand All @@ -18,7 +18,7 @@
},
{
"path": "blocks/chat-message/ChatMessage.vue",
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\";\nimport type { UIMessage } from \"ai\";\nimport { Button } from \"@/components/ui/button\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@/components/ui/tooltip\";\nimport { LucideIcon } from \"@/components/ui/icon\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface ChatMessageAction {\n label: string;\n icon: string;\n onClick?: (e: MouseEvent, message: UIMessage) => void;\n}\n\nexport interface ChatMessageProps {\n /** Message ID */\n id?: string;\n /** Message role */\n role?: \"user\" | \"assistant\" | \"system\";\n /** Message parts from AI SDK */\n parts?: UIMessage[\"parts\"];\n /** Which side to render the message */\n side?: \"left\" | \"right\";\n /** Action buttons shown on hover */\n actions?: ChatMessageAction[];\n /** Additional CSS classes */\n class?: HTMLAttributes[\"class\"];\n}\n\nconst props = withDefaults(defineProps<ChatMessageProps>(), {\n side: \"left\",\n actions: () => [],\n});\n\nfunction handleAction(e: MouseEvent, action: ChatMessageAction) {\n if (!props.id || !props.role || !props.parts) return;\n action.onClick?.(e, { id: props.id, role: props.role, parts: props.parts } as UIMessage);\n}\n</script>\n\n<template>\n <div\n data-slot=\"chat-message\"\n :class=\"cn(\n 'group flex gap-3',\n side === 'right' ? 'flex-row-reverse' : 'flex-row',\n props.class,\n )\"\n >\n <div\n v-if=\"$slots.leading\"\n class=\"shrink-0\"\n >\n <slot name=\"leading\" />\n </div>\n\n <div :class=\"cn('flex flex-col gap-1', side === 'right' ? 'items-end' : 'items-start')\">\n <div :class=\"cn('max-w-[80ch] space-y-2', side === 'right' && 'bg-muted rounded-xl px-4 py-2.5')\">\n <slot\n :id=\"id\"\n name=\"content\"\n :role=\"role\"\n :parts=\"parts\"\n >\n <template\n v-for=\"(part, index) in parts\"\n :key=\"`${id}-${part.type}-${index}`\"\n >\n <p\n v-if=\"part.type === 'text'\"\n class=\"whitespace-pre-wrap\"\n >\n {{ part.text }}\n </p>\n </template>\n </slot>\n </div>\n\n <div\n v-if=\"actions.length > 0\"\n class=\"flex gap-0.5 opacity-0 transition-opacity group-hover:opacity-100\"\n >\n <TooltipProvider>\n <Tooltip\n v-for=\"action in actions\"\n :key=\"action.label\"\n >\n <TooltipTrigger as-child>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n @click=\"handleAction($event, action)\"\n >\n <LucideIcon\n :name=\"action.icon\"\n class=\"size-3.5\"\n />\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {{ action.label }}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n </div>\n </div>\n</template>\n",
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\";\nimport type { UIMessage } from \"ai\";\nimport { Button } from \"@/components/ui/button\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@/components/ui/tooltip\";\nimport { LucideIcon } from \"@/components/ui/lucide-icon\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface ChatMessageAction {\n label: string;\n icon: string;\n onClick?: (e: MouseEvent, message: UIMessage) => void;\n}\n\nexport interface ChatMessageProps {\n /** Message ID */\n id?: string;\n /** Message role */\n role?: \"user\" | \"assistant\" | \"system\";\n /** Message parts from AI SDK */\n parts?: UIMessage[\"parts\"];\n /** Which side to render the message */\n side?: \"left\" | \"right\";\n /** Action buttons shown on hover */\n actions?: ChatMessageAction[];\n /** Additional CSS classes */\n class?: HTMLAttributes[\"class\"];\n}\n\nconst props = withDefaults(defineProps<ChatMessageProps>(), {\n side: \"left\",\n actions: () => [],\n});\n\nfunction handleAction(e: MouseEvent, action: ChatMessageAction) {\n if (!props.id || !props.role || !props.parts) return;\n action.onClick?.(e, { id: props.id, role: props.role, parts: props.parts } as UIMessage);\n}\n</script>\n\n<template>\n <div\n data-slot=\"chat-message\"\n :class=\"cn(\n 'group flex gap-3',\n side === 'right' ? 'flex-row-reverse' : 'flex-row',\n props.class,\n )\"\n >\n <div\n v-if=\"$slots.leading\"\n class=\"shrink-0\"\n >\n <slot name=\"leading\" />\n </div>\n\n <div :class=\"cn('flex flex-col gap-1', side === 'right' ? 'items-end' : 'items-start')\">\n <div :class=\"cn('max-w-[80ch] space-y-2', side === 'right' && 'bg-muted rounded-xl px-4 py-2.5')\">\n <slot\n :id=\"id\"\n name=\"content\"\n :role=\"role\"\n :parts=\"parts\"\n >\n <template\n v-for=\"(part, index) in parts\"\n :key=\"`${id}-${part.type}-${index}`\"\n >\n <p\n v-if=\"part.type === 'text'\"\n class=\"whitespace-pre-wrap\"\n >\n {{ part.text }}\n </p>\n </template>\n </slot>\n </div>\n\n <div\n v-if=\"actions.length > 0\"\n class=\"flex gap-0.5 opacity-0 transition-opacity group-hover:opacity-100\"\n >\n <TooltipProvider>\n <Tooltip\n v-for=\"action in actions\"\n :key=\"action.label\"\n >\n <TooltipTrigger as-child>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n @click=\"handleAction($event, action)\"\n >\n <LucideIcon\n :name=\"action.icon\"\n class=\"size-3.5\"\n />\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {{ action.label }}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n </div>\n </div>\n</template>\n",
"type": "registry:block"
}
]
Expand Down
10 changes: 5 additions & 5 deletions public/r/lucide-icon.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
"$schema": "https://shadcn-vue.com/schema/registry-item.json",
"name": "lucide-icon",
"type": "registry:block",
"type": "registry:ui",
"dependencies": [
"lucide-vue-next"
],
"files": [
{
"path": "blocks/lucide-icon/index.ts",
"path": "ui/lucide-icon/index.ts",
"content": "export { default as LucideIcon, type LucideIconProps } from \"./LucideIcon.vue\";\n",
"type": "registry:block"
"type": "registry:ui"
},
{
"path": "blocks/lucide-icon/LucideIcon.vue",
"path": "ui/lucide-icon/LucideIcon.vue",
"content": "<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { icons } from \"lucide-vue-next\";\n\nexport interface LucideIconProps {\n /** Icon identifier - accepts `i-lucide-*` style names or Vue components */\n name: string | Component;\n /** Icon dimensions */\n size?: string | number;\n class?: string;\n}\n\nconst props = defineProps<LucideIconProps>();\n\nconst iconStyle = computed(() => {\n if (!props.size) return {};\n const s = typeof props.size === \"number\" ? `${props.size}px` : props.size;\n return { width: s, height: s };\n});\n\nconst isLucide = computed(() => {\n if (typeof props.name !== \"string\") return false;\n return props.name.startsWith(\"i-lucide-\");\n});\n\nconst resolvedComponent = computed(() => {\n if (typeof props.name !== \"string\") {\n return props.name;\n }\n\n if (!isLucide.value) return null;\n\n const pascalName = props.name\n .replace(/^i-lucide-/, \"\")\n .split(\"-\")\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n\n return icons[pascalName as keyof typeof icons] ?? null;\n});\n</script>\n\n<template>\n <component\n :is=\"resolvedComponent\"\n v-if=\"resolvedComponent\"\n :class=\"props.class\"\n :style=\"iconStyle\"\n />\n</template>\n",
"type": "registry:block"
"type": "registry:ui"
}
]
}
26 changes: 6 additions & 20 deletions public/r/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,21 @@
"items": [
{
"name": "lucide-icon",
"type": "registry:block",
"type": "registry:ui",
"files": [
{
"path": "blocks/lucide-icon/index.ts",
"type": "registry:block"
"path": "ui/lucide-icon/index.ts",
"type": "registry:ui"
},
{
"path": "blocks/lucide-icon/LucideIcon.vue",
"type": "registry:block"
"path": "ui/lucide-icon/LucideIcon.vue",
"type": "registry:ui"
}
],
"dependencies": [
"lucide-vue-next"
]
},
{
"name": "hello-world",
"type": "registry:block",
"files": [
{
"path": "blocks/hello-world/index.ts",
"type": "registry:block"
},
{
"path": "blocks/hello-world/HelloWorld.vue",
"type": "registry:block"
}
]
},
{
"name": "chat-prompt-submit",
"type": "registry:block",
Expand Down Expand Up @@ -107,7 +93,7 @@
],
"registryDependencies": [
"button",
"icon",
"lucide-icon",
"tooltip"
]
}
Expand Down
Loading