Skip to content
Open
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
49 changes: 49 additions & 0 deletions apps/v4/components/registry/AddRegistryModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import type { DirectoryRegistry } from "@/lib/directory-registry"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/registry/new-york-v4/ui/dialog"

interface Props {
open: boolean
registry: DirectoryRegistry
}

const props = defineProps<Props>()

const emit = defineEmits<{
(e: "update:open", value: boolean): void
}>()
</script>

<template>
<Dialog :open="open" @update:open="emit('update:open', $event)">
<DialogContent class="dialog-ring animate-none! rounded-xl sm:max-w-md">
<DialogHeader>
<DialogTitle>Add Registry</DialogTitle>
<DialogDescription>
Run this command to add {{ registry.name }} to your project.
</DialogDescription>
</DialogHeader>
<ProsePre
v-if="registry.command"
:code="registry?.command"
language="bash"
/>
<DialogFooter>
<DialogClose as-child>
<Button variant="outline">
Cancel
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
</template>
119 changes: 119 additions & 0 deletions apps/v4/components/registry/RegistryList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script setup lang="ts">
import type { DirectoryRegistry } from "~/lib/directory-registry"
import { IconArrowUpRight } from "@tabler/icons-vue"
import { PlusIcon, Search, X } from "lucide-vue-next"
import { directoryRegistryList } from "~/lib/directory-registry"
import { Button } from "~/registry/new-york-v4/ui/button"
import { Field } from "~/registry/new-york-v4/ui/field"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "~/registry/new-york-v4/ui/input-group"
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemFooter,
ItemGroup,
ItemMedia,
ItemSeparator,
ItemTitle,
} from "~/registry/new-york-v4/ui/item"
import AddRegistryModal from "./AddRegistryModal.vue"

const searchQuery = ref("")
const addRegistryOpen = ref(false)
const selectedRegistry = ref<DirectoryRegistry | null>(null)

const registryList = computed(() => {
return directoryRegistryList.filter((r) => {
return r.name.includes(searchQuery.value) || r.description.includes(searchQuery.value)
})
})
function toggleAddRegistryModal(registry: DirectoryRegistry | null) {
selectedRegistry.value = registry
addRegistryOpen.value = !addRegistryOpen.value
}
</script>

<template>
<div class="mt-6">
<Field>
<InputGroup>
<InputGroupAddon>
<Search />
</InputGroupAddon>
<InputGroupInput v-model="searchQuery" placeholder="search" />
<InputGroupAddon align="inline-end">
<span class="text-muted-foreground tabular-nums sm:text-xs">
{{ directoryRegistryList.length }}
{{ directoryRegistryList.length === 1 ? "Registry" : "Registries" }}
</span>
</InputGroupAddon>
<InputGroupAddon
align="inline-end"
:data-disabled="false"
class-name="data-[disabled=true]:hidden"
>
<InputGroupButton
aria-label="Clear"
title="Clear"
size="icon-xs"
>
<X />
</InputGroupButton>
</inputgroupaddon>
Comment on lines +61 to +68
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Clear button is non-functional and has inconsistent tag casing.

Two issues here:

  1. The clear button is missing a @click handler to actually clear the search query.
  2. Line 68: </inputgroupaddon> uses lowercase while the opening tag uses PascalCase.
πŸ› Proposed fix
           <InputGroupButton
             aria-label="Clear"
             title="Clear"
             size="icon-xs"
+            `@click`="searchQuery = ''"
           >
             <X />
           </InputGroupButton>
-        </inputgroupaddon>
+        </InputGroupAddon>
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<InputGroupButton
aria-label="Clear"
title="Clear"
size="icon-xs"
>
<X />
</InputGroupButton>
</inputgroupaddon>
<InputGroupButton
aria-label="Clear"
title="Clear"
size="icon-xs"
`@click`="searchQuery = ''"
>
<X />
</InputGroupButton>
</InputGroupAddon>
πŸ€– Prompt for AI Agents
In `@apps/v4/components/registry/RegistryList.vue` around lines 61 - 68, The Clear
button is non-functional and the closing tag casing is wrong: add a click
handler on the InputGroupButton (e.g. `@click`="clearSearch" or
`@click`="searchQuery = ''") that resets the component's search model (or calls an
existing method like clearSearch) so the X button actually clears the query, and
correct the mismatched closing tag from </inputgroupaddon> to
</InputGroupAddon>; ensure the clearSearch implementation updates the same
reactive variable used by the input v-model or emits the proper event.

</InputGroup>
</Field>
<ItemGroup>
<template v-for="registry in registryList" :key="registry.name">
<Item class="group/item relative gap-6 px-0">
<ItemMedia
variant="image"
class="*:[svg]:fill-foreground grayscale *:[svg]:size-8"
>
<span v-if="registry.logo.startsWith('<svg')" v-html="registry.logo" />
<NuxtImg v-else :src="registry.logo" />
</ItemMedia>
<ItemContent>
<ItemTitle>
<a
:href="registry.link"
rel="noopener noreferrer external"
class="group flex items-center gap-1"
target="_blank"
>
{{ registry.name }}
<IconArrowUpRight
class="size-4 opacity-0 group-hover:opacity-100"
/>
</a>
</ItemTitle>
<ItemDescription v-if="registry.description" class="text-pretty">
{{ registry.description }}
</ItemDescription>
</ItemContent>
<ItemActions class="">
<Button v-if="registry.command" variant="outline" @click.prevent="toggleAddRegistryModal(registry)">
<PlusIcon />
Add
</Button>
<Button as="a" :href="registry.link" target="_blank" size="sm" variant="outline">
View <IconArrowUpRight />
</Button>
</ItemActions>
<ItemFooter class="justify-start pl-16 sm:hidden">
<Button as="a" :href="registry.link" target="_blank" size="sm" variant="outline">
View <IconArrowUpRight />
</Button>
</ItemFooter>
</Item>
<ItemSeparator />
</template>
</ItemGroup>
<AddRegistryModal v-if="selectedRegistry" :open="addRegistryOpen" :registry="selectedRegistry" @update:open="toggleAddRegistryModal(null)" />
</div>
</template>
19 changes: 19 additions & 0 deletions apps/v4/content/docs/directory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: Registry Directory
description: Discover community registries for shadcn/ui components and blocks.
---

These registries are built into the CLI with no additional configuration required. To add a component, run:

```bash
npx shadcn-vue add @<registry>/<component>.
```
Comment on lines +6 to +10
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Remove the trailing period in the CLI example.

Copy/pasting the command as-is will include the period and fail.

πŸ”§ Proposed fix
-npx shadcn-vue add @<registry>/<component>.
+npx shadcn-vue add @<registry>/<component>
πŸ€– Prompt for AI Agents
In `@apps/v4/content/docs/directory.md` around lines 6 - 10, The CLI example in
the docs contains a trailing period that will be copied into the command and
cause failures; update the example string "npx shadcn-vue add
@<registry>/<component>." to remove the trailing period so it reads "npx
shadcn-vue add @<registry>/<component>" in the docs (refer to the example
command line in apps/v4/content/docs/directory.md where the snippet contains
@<registry>/<component>).


<Callout variant="warning" icon class="gap-2! border-amber-200 bg-amber-50 p-2 font-semibold dark:border-amber-900 dark:bg-amber-950 *:[svg]:translate-y-1">
Community registries are maintained by third-party developers and are not officially curated. Always review code on installation to ensure it meets your security and quality standards.

</Callout>

Don't see a registry? [Learn how to add it here.](/docs/registry)

<RegistryList />
8 changes: 8 additions & 0 deletions apps/v4/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export const siteConfig = {
href: '/charts/area',
label: 'Charts',
},
{
href: '/docs/directory',
label: 'Directory',
},
{
href: '/themes',
label: 'Themes',
},
{
href: '/colors',
label: 'Colors',
Expand Down
10 changes: 10 additions & 0 deletions apps/v4/lib/directory-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface DirectoryRegistry {
name: string
description: string
link: string
command?: string
logo: string
}
import directoryJson from '@/registry/directory.json'

export const directoryRegistryList: DirectoryRegistry[] = [...directoryJson]
22 changes: 22 additions & 0 deletions apps/v4/registry/directory.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"name": "Insipira UI",
"description": "Inspira UI is a collection of reusable, animated components powered by TailwindCSS , motion-v , GSAP & threejs β€” crafted to help you ship faster and better.",
"link": "https://inspira-ui.com/docs/en",
"logo": "https://cdn.inspira-ui.com/logo-dark.svg"
},
{
"name": "Stunning UI",
"description": "Create Stunning Websites That Stand Out",
"link": "https://www.stunningui.com/",
"logo": "https://robertshaw.id/assets/stunning-ui.svg"
},

{
"name": "Mapcn",
"description": "Beautiful maps,made simple.",
"link": "https://mapcn-vue.geoql.in/",
Comment on lines +3 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Fix user-facing typos in registry data.

The name/description mismatch and missing space will show up in UI.

πŸ”§ Proposed fix
-    "name": "Insipira UI",
+    "name": "Inspira UI",
@@
-    "description": "Beautiful maps,made simple.",
+    "description": "Beautiful maps, made simple.",
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"name": "Insipira UI",
"description": "Inspira UI is a collection of reusable, animated components powered by TailwindCSS , motion-v , GSAP & threejs β€” crafted to help you ship faster and better.",
"link": "https://inspira-ui.com/docs/en",
"logo": "https://cdn.inspira-ui.com/logo-dark.svg"
},
{
"name": "Stunning UI",
"description": "Create Stunning Websites That Stand Out",
"link": "https://www.stunningui.com/",
"logo": "https://robertshaw.id/assets/stunning-ui.svg"
},
{
"name": "Mapcn",
"description": "Beautiful maps,made simple.",
"link": "https://mapcn-vue.geoql.in/",
"name": "Inspira UI",
"description": "Inspira UI is a collection of reusable, animated components powered by TailwindCSS , motion-v , GSAP & threejs β€” crafted to help you ship faster and better.",
"link": "https://inspira-ui.com/docs/en",
"logo": "https://cdn.inspira-ui.com/logo-dark.svg"
},
{
"name": "Stunning UI",
"description": "Create Stunning Websites That Stand Out",
"link": "https://www.stunningui.com/",
"logo": "https://robertshaw.id/assets/stunning-ui.svg"
},
{
"name": "Mapcn",
"description": "Beautiful maps, made simple.",
"link": "https://mapcn-vue.geoql.in/",
πŸ€– Prompt for AI Agents
In `@apps/v4/registry/directory.json` around lines 3 - 18, Fix the user-facing
typos in the registry JSON: rename the "Insipira UI" entry to "Inspira UI" so it
matches the description text, remove the stray space before the comma in that
description ("TailwindCSS , motion-v" β†’ "TailwindCSS, motion-v"), and add the
missing space in the "Mapcn" description ("Beautiful maps,made simple." β†’
"Beautiful maps, made simple."); locate and update the entries by their "name"
fields ("Insipira UI"/"Inspira UI" and "Mapcn") in the JSON array.

"logo": "https://raw.githubusercontent.com/geoql/v-maplibre/refs/heads/main/apps/mapcn-vue/public/favicon.svg",
"command": "npx shadcn-vue@latest add https://mapcn-vue.geoql.in/r/map"
}
]
11 changes: 4 additions & 7 deletions apps/v4/registry/new-york-v4/ui/carousel/CarouselContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,20 @@ defineOptions({
})

const props = defineProps<WithClassAsProps>()

// @ts-expect-error Skip this when building since its a neede var
const { carouselRef, orientation } = useCarousel()
</script>

<template>
<div
ref="carouselRef"
data-slot="carousel-content"
class="overflow-hidden"
>
<div ref="carouselRef" data-slot="carousel-content" class="overflow-hidden">
<div
:class="
cn(
'flex',
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
props.class,
)"
)
"
v-bind="$attrs"
>
<slot />
Expand Down