Compare commits

..
275 Commits
Author SHA1 Message Date
Artem AshandGitHub e85a120672 Version 0.2.1 (#3)
SiteConf --> SiteDef
ActionButton, DrawerMenu, LinkElement, YouTubeEmbed => primitives
moved next-fonts into next
Significant improvement of next font API and tw font config
2024-01-26 03:22:41 -05:00
artemis prime b91443b595 packages/ui => pkgs/luxdefi-ui 2024-01-24 07:39:41 -08:00
artemis prime df2badbe89 fixed foreground bug 2024-01-24 07:02:33 -08:00
artemis prime da9cdd8fd6 fixed copyright years bug 2024-01-24 06:44:29 -08:00
artemis prime 8226fea5b2 added theme class to <html> in root layout;
added --lx- prefix to color vars in tw plugin
2024-01-24 06:38:00 -08:00
artemis prime fbb57da420 fixed crash in mono 2024-01-24 05:51:44 -08:00
artemis prime 75338609e2 verbatimModuleSyntax (so market could build) 2024-01-23 18:16:44 -08:00
artemis prime 8f53f8b592 minor: cleaned dependencies, silenced warning, bumped version 2024-01-23 15:05:14 -08:00
aa227aa5ba @luxdefi/ui bundles as loose *.tsx and works in a Nextjs app. (#2)
* removed previous /ui
* initial replacement of desired /ui contents
* created clean script for all and clean:all
* Remove exports
* Disable Common JS
* Don't bundle next code
* Silence unsupported CSS nesting
* Enable bundle
* Convert Common JS modules to ES modules
* Use ES module exports
* removed packages/cli

---------

Co-authored-by: Zach Kelling <z@zeekay.io>
2024-01-23 13:25:21 -05:00
Artem AshandGitHub 19bdc90e38 Testing publishing step for /ui (#1)
* removed previous /ui

* initial replacement of desired /ui contents

* ui building

* fixed web build and and tsconfig trees

* creacted clean script for all and clean:all

* previous

* previous
2024-01-21 23:38:30 -05:00
Zach Kelling 69d529bbe8 Add in ui package 2024-01-08 02:30:38 +01:00
Zach Kelling 070f1c3824 Update icon 2024-01-08 01:29:45 +01:00
Zach Kelling 849e6b3038 Update package.json fix bad merge 2024-01-08 01:29:37 +01:00
Zach Kelling a0f859e68d Update UI 2024-01-08 01:28:56 +01:00
김태강andZach Kelling 2327b8e1f4 feat(components): add "use client" directive to carousel, resizable component (#2319)
[Reopen a PR that was accidentally closed.](https://github.com/shadcn-ui/ui/pull/2233)
carousel, resizable components to be used by the app router, a "use client" directive is required.
2024-01-08 01:28:52 +01:00
vinayandZach Kelling a0560c9411 [Docs] Update CLI options while configuring components.json (#2283)
New Line added to the docs.
Just for the consistency.

![image](https://github.com/shadcn-ui/ui/assets/94120295/e0da71c1-72d1-4e5d-bba9-54634a9d0e31)
2024-01-08 01:28:52 +01:00
Nuriman QuddusandZach Kelling 7377a01af9 fix: typo on carousel import (#2216)
# What's changed

- [x] Fixed the typo on carousel component

before fix: "@/registry/new-york/ui/carousel"
after fix: "@/components/ui/carousel"

this is ease the user to copy paste without error
2024-01-08 01:28:52 +01:00
ClarenceandZach Kelling 7eaebb8f6d fix: Github case correction (#2268) 2024-01-08 01:28:52 +01:00
Andrew QiaoandZach Kelling 68c92aa2b0 fix(www): incorrect toggle aria labels and values (#2163)
- some `Toggle` and `ToggleGroup` demos had incorrect `aria-label` or `value` props
2024-01-08 01:28:52 +01:00
ChaseandZach Kelling 43a4e04bf9 fix(docs): Resizable panel direction should be horizontal (#2295)
In the [resizable documentation](https://ui.shadcn.com/docs/components/resizable) the handle example shows a horizontal handle but the code example has `direction="vertical"` when it should be `direction="horizontal"`

<img width="745" alt="Screenshot 2024-01-05 at 9 34 43 AM" src="https://github.com/shadcn-ui/ui/assets/7241069/68c21241-e0c7-41b1-81d7-579306149520">
2024-01-08 01:28:52 +01:00
Anshul KanwarandZach Kelling 8e73fd6fb6 docs: typo in drawer, dialog and sheet (#2306) 2024-01-08 01:28:52 +01:00
Tilak ThapaandZach Kelling 79b3115e0b Fix: Add e.preventDefault() to prevent page reload (#2278)
Fixes #2277

Add `e.preventDefault()` to prevent page reload in mail component

![image](https://github.com/shadcn-ui/ui/assets/106688422/d1373e8f-0c46-4f2e-8caf-2fcd2076b2fb)
2024-01-08 01:28:52 +01:00
Zach Kellingandgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> f05628fb23 chore(release): version packages (#2269)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-08 01:28:49 +01:00
shadcnandZach Kelling 86c8db25f7 feat(cli): add support for custom ui dir (#2266)
* feat(cli): add support for custom ui dir

* docs(www): update docs for aliases.ui

* chore: add changeset
2024-01-08 01:28:45 +01:00
shadcnandZach Kelling 99a03ca026 feat(www): update keyboard handling for command menu (#2264) 2024-01-08 01:28:45 +01:00
kal07andZach Kelling 290297af00 Update pagination.mdx (#2191)
fix the Nextjs import example
2024-01-08 01:28:45 +01:00
Ghribi OuassimandZach Kelling bdcf7098a1 Added new label for mobile navigation (#2182)
- Update the `mobile-nav` component and added `new` label if `item.label` is present in the `docsConfig.sidebarNav`.

![image](https://github.com/shadcn-ui/ui/assets/70029024/e19eddf4-22bb-4afe-8158-ea795ea0c5c0)
2024-01-08 01:28:45 +01:00
shadcnandZach Kelling de512627ce fix: add tailwind.prefix to schema (#2200)
* fix: add tailwind.prefix to schema

* fix(www): format
2024-01-08 01:28:45 +01:00
RishabhandZach Kelling af76c459dc docs(pagination): wrong import path of components in pagination usage (#2180)
This PR:

Fixes: #2150
2024-01-08 01:28:45 +01:00
RishabhandZach Kelling 2fb8b3527c docs(drawer): missing drawer footer import in drawer usage example (#2169)
This PR:

- Fixes:  #2168
2024-01-08 01:28:44 +01:00
arshadandZach Kelling fe13a96567 Update: Rename 'ResizableGroup' to 'ResizablePanelGroup' in Documentation (#2166)
### Overview
This pull request updates the documentation to reflect the correct component name, changing `ResizableGroup` to `ResizablePanelGroup`. This change ensures consistency and correctness in the documentation, aiding developers in correctly implementing the component.

### Changes Made
- In the code examples within the documentation, `ResizableGroup` has been renamed to `ResizablePanelGroup`.
- This change is applied to both horizontal and vertical orientation examples.

### Additional Information
- These changes are confined to documentation and do not alter the actual implementation or functionality of the components in question.

Please review the changes for accuracy and merge if appropriate. Thanks!
2024-01-08 01:28:44 +01:00
db610914ef docs: typo in drawer (#2164)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:44 +01:00
shadcnandZach Kelling 410d318db2 fix(www): mail icon size (#2173) 2024-01-08 01:28:44 +01:00
Zach Kelling fa25586d32 feat: new components (#2144) 2024-01-08 01:28:41 +01:00
Zach Kellingandgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> 4ba63ec643 chore(release): version packages (#1756)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-08 01:28:34 +01:00
56dcbe713c feat(cli): add support for custom Tailwind prefix transformer (#770)
* feat(cli): add support for custom Tailwind prefix

* fix(cli): add tw prefix on classes applied in the css file

* feat(cli): add support for custom tailwind prefix

* chore: add changeset

* style(shadcn-ui): code format

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
ocavueandZach Kelling f2f32ddf35 fix(toast): replace MAX_VALUE with MAX_SAFE_INTEGER (#1982)
This PR replaces the maximum id from `Number.MAX_VALUE` to `Number.MAX_SAFE_INTEGER` in `use-toast.ts`. Considering how JS stores numbers, it's unsafe to plus one if the number is larger than `Number.MAX_SAFE_INTEGER`. Here is an example:

```js 
> let num

> num = Number.MAX_VALUE - 1
> num + 1 === num
true

> num = Number.MAX_SAFE_INTEGER - 1
> num + 1 === num
false
```
2024-01-08 01:28:30 +01:00
Js ParkandZach Kelling 600a20bc1e docs(www): update installation of Astro (#1958)
- The latest version of Astro generates `tailwind.config.mjs` file instead of `tailwind.config.cjs`.
- The `index.astro` file is located in `/src/pages`.
2024-01-08 01:28:30 +01:00
Gravy59andZach Kelling 5f61fad5ab fix(www): typo in metric cards (#1975) 2024-01-08 01:28:30 +01:00
a67cb0ef2a docs(www): add indeterminate state checkboxes on all data table demos and code examples (#1959)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
Dimitri POSTOLOVandZach Kelling 9cf60319c2 add missing return Update page.tsx (#1952) 2024-01-08 01:28:30 +01:00
兰天游andZach Kelling 19505421c6 feat: fix for column grouping (#945) 2024-01-08 01:28:30 +01:00
9700a3467f feat(toggle-group): add toggle-group component (#1547)
* feat: added toggle-group component

* fix(components): ran build:registry script

* fix(components): fixed colors in toggle-group
- Dark mode border color is now consistent with the toggle component

* fix(components): fixed component.json toggle-group
- Added the content field to `components.json` for toggle-group
- Ran build:registry again

* feat(toggle-group): simplify implementation

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
shadcnandZach Kelling 314a969aff chore: build registry 2024-01-08 01:28:30 +01:00
533326fef0 fix(www): update faker version, reduce int() deprecation (#1832)
* fix: update faker version, reduce int() deprecation

* chore: pnpm format:write

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
3738275280 style(command): add import type for Command component (#1490)
* style: add import type for Command component

* style(code): format code

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
Mubin AnsariandZach Kelling d5e0d4e769 Remove Redundant md:w-full class in DialogPrimitive.Content in dialog and alert-dialog (#1640)
closes #1639
2024-01-08 01:28:30 +01:00
Thomas AlberolaandZach Kelling 818c61d1ef Move className overwrite of AccordionContent to the children component (#1670)
In order to have a smooth opening of the accordion, moving the `AccordionContent` `className` overwrite to the children wrapper component allow Radix to calculate correctly the animation and execute a smooth animation in case of `className` on the `AccordionContent` component.

Possibly related to https://github.com/shadcn-ui/ui/issues/944
2024-01-08 01:28:30 +01:00
Chongyi ZhengandZach Kelling 1038f37f7f chore: upgrade @radix-ui/react-select to 2.0.0 (#1688) 2024-01-08 01:28:30 +01:00
shadcnandZach Kelling 70c7083823 feat(select): add scroll up and down button 2024-01-08 01:28:30 +01:00
Tanish BaansalandZach Kelling 1b34ed829f refactor(calendar): updated css so date doesn't show up selected twice in DatePickerComponent (#1852)
React Day Picker also has a unique styling distinction that designates the `day_outside` dates as unselected, and the selection of `day_outside` only highlights the dates in the next month. This PR will fix this issue - https://github.com/shadcn-ui/ui/issues/1762 and help with a cleaner UX 

  
<br/>

| Old Date Range Picker  | New Date Range Picker           |
| ---------------------- | ---------------------- |
| ![old](https://github.com/shadcn-ui/ui/assets/7449806/42e9448f-9e38-486c-b65c-cf00ec1ec7c7) | ![new](https://github.com/shadcn-ui/ui/assets/7449806/804f83d7-1b74-474c-8992-2d6d844dfb35) |

<br/>

#### React Day Picker
<img width="444" alt="image" src="https://github.com/shadcn-ui/ui/assets/7449806/aaeae160-b38c-4c16-bb2d-66898cf290d3">
2024-01-08 01:28:30 +01:00
Gravy59andZach Kelling efc833c58a fix(#1686): remove redundant children prop (#1717)
This pull request resolves #1686.

## Rationale for this PR

This PR affects the code for `RadioGroupItem` in both styles by removing the `children` prop from the component. The children prop is automatically passed in by the use of the spread operator (`...props`) and is redundant because it is never used in the component.

This PR shouldn't affect tests, representation, etc. and is merely a cosmetic change. There is no urgent need to merge this.
2024-01-08 01:28:30 +01:00
William Frank Monroy MamaniandZach Kelling 81ed5f7031 docs: updated contributing guidelines to easily contribute (#1830)
## Explanation
Added detailed info to clone and setup the project to contribute.

Following the PR #1650 and suggestion of @shadcn.  Thanks!
2024-01-08 01:28:30 +01:00
Shubhdeep ChhabraandZach Kelling c6babe939b fix(alert-dialog): removed unused children prop (#1828) 2024-01-08 01:28:30 +01:00
GregandZach Kelling 0e3fc6c5cf fix(scroll-area): horizontal scroll bar not visible (#1829)
PRs #1515 and #1296 interfere with each other and cause the horizontal scroll bar to not be visible. This removes the conditional `flex-1`, however you could also remove `flex-col` to achieve the same result.

before:

https://github.com/shadcn-ui/ui/assets/9381099/6514de2e-e353-4d0b-bd24-aff79e0d5161

after:


https://github.com/shadcn-ui/ui/assets/9381099/3205baad-569b-4096-8dcd-9beb794de536
2024-01-08 01:28:30 +01:00
shadcnandZach Kelling f09bf82384 ci: update tasks name to make debug easier (#1932) 2024-01-08 01:28:30 +01:00
shadcnandZach Kelling 270a90fca3 fix(table): update style for table footer (#1931)
* fix(table): update table footer style

* chore: run registry

* style: fix docs
2024-01-08 01:28:30 +01:00
MartiniandZach Kelling 0bdc88694d docs(www): Fix typo (#1853) 2024-01-08 01:28:30 +01:00
Cole ClineandZach Kelling dce7c53e4f docs(www): Missing import statement (#1877)
Added missing import statement in fonts example in Next.js installation docs
2024-01-08 01:28:30 +01:00
c2b3d0e311 fix(switch): change width unit to rem (#1891)
Signed-off-by: Innei <i@innei.in>
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
Kevin MokandZach Kelling 7f97bac978 docs(www): add remix dark mode docs (#1920)
* docs(www): add remix dark mode docs

* docs(www): add modification to tailwindcss file
2024-01-08 01:28:30 +01:00
Michael StramelandZach Kelling a096054b3c fix(www): destructive contrast increase (#1899) 2024-01-08 01:28:30 +01:00
miquelvirandZach Kelling 3cef56bf0e docs(card): remove unused line #1652 (#1798)
Fixes #1652
2024-01-08 01:28:30 +01:00
miquelvirandZach Kelling 7cf60ccadd Fix combobox examples using labels as value (#1788)
Fix #1785
2024-01-08 01:28:30 +01:00
763510638e fix(www): update twitter icon to X (#1551)
* refactor(icon.tsx): twitter icon updated to latest version X icon

* refactor(icon.tsx): twitter icon updated to latest version X icon

* refactor(icons.tsx): added the same changes to the icon for X twitter icon

* refactor(icons.tsx): change the formating of the code

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
GregandZach Kelling 727efb8aa5 docs(scroll-area): add example for horizontal scroll area (#1515)
Adds documentation to the `<ScrollArea>` component for creating a scroll area with a horizontal scrollbar. There is also a commit to fix the improper sizing of a horizontal scroll bars.

Before:
https://github.com/shadcn-ui/ui/assets/9381099/a8b512f1-37f7-4107-a9fa-42a26e124696

After:
https://github.com/shadcn-ui/ui/assets/9381099/480f881c-b0fe-4b1b-9472-c533135e6769
2024-01-08 01:28:30 +01:00
Bumsik KimandZach Kelling 8a18dcd9ae fix(example): Prevent hydration error in music example (#1569)
<DisalogTrigger> should have asChild when a button used.
2024-01-08 01:28:30 +01:00
3a672f5925 docs(www): bunx scripts run using bun instead of node (#1590)
* fix: using bun to initialize project works now

* style(www): format write

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
8ece7b73c1 fix(calendar): showOutsideDays=false (#1731)
showOutsideDays=false will shift the missing days of a month to the start of the row (cause of 'flex' in classnames: row), to fix it, we can use the same height and width in a cell as in a day

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
e8f29c5c3b fix: updated error msg for jsconfig in cli (#1696)
* fix: updated error msg for jsconfig in cli

* chore: add changeset

* style(cli): format

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
540a86c813 fix: remove invalid collapsible prop (#496)
only available when type="single"

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
0117fc9eb0 docs: add missing tailwind config in Astro install (#1264)
* docs: add missing tailwind config in Astro install

* style: prettier format

---------

Co-authored-by: Peeranat Danaidusadeekul <ppeeranat.d@skooldio.com>
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
onurhanandZach Kelling 07647ad576 fix(docs): present useForm when copy form (#1486)
When I tried it after the attached issue, I realized;
For someone who's absolutely copying and pasting form the guide, this would give an error, as useForm is not imported in the guide. So I fixed that.

Related issue: https://github.com/shadcn-ui/ui/issues/1482

Fix #1482
2024-01-08 01:28:30 +01:00
IDRISSI HAMZAandZach Kelling 2ce7e068a4 fix(components): fix text wrapping issue in buttons (#1548)
## What does this PR do?

This pull request resolves an issue where the button text wraps onto the next line in specific screen sizes when the text contains two or more words. By applying the whitespace-nowrap utility class to the button element, the text now remains on a single line, even on screens with limited space. This enhancement ensures a consistent and visually pleasing user experience across various devices :

### Before (Get Started Button)
![before](https://github.com/shadcn-ui/ui/assets/97639117/50c8211d-2ed7-46d0-8ab2-d4c565d0d6f3)


### After  (Get Started Button)
![after](https://github.com/shadcn-ui/ui/assets/97639117/0f9f7e68-31e8-4011-a2ca-d7e59b3690ee)





This problem arises in other projects utilizing Shadcn, and this pull request addresses and resolves the issue, as demonstrated in the Cal.com project example:

### Before (Set Up Button)
![button-before](https://github.com/calcom/cal.com/assets/97639117/8f588a53-411a-4e9d-95df-76bd42d480d7)

### After  (Set Up Button)
![button-fixed](https://github.com/calcom/cal.com/assets/97639117/4c74e77a-a2cd-4b0c-87ee-a82dbefc23eb)
2024-01-08 01:28:30 +01:00
N8andZach Kelling 412227426e docs: ✏️ Add defaultValues to input in form examples (#1610)
Fixes: https://github.com/shadcn-ui/ui/issues/1609
2024-01-08 01:28:30 +01:00
Lachlan HeywoodandZach Kelling e09fdb19a4 fix(components): remove className from dialog portals (#1606)
Fixes #1595, #1644

This PR changes the components that use the `DialogPortal` element to be aliases rather than components that pass a className prop.

The `DialogPortalProps` type from `@radix/react-dialog` recently had a patch update that probably should have been a minor or maybe a major update which is causing a few people to see the error `Property 'className' does not exist on type 'DialogPortalProps'`.

Since the `DialogPortal` component doesn't actually output any DOM elements, it never technically supported the `className` prop and the fact that it surfaced that prop was really a bug.

The `AlertDialog` and `Dialog` components were updated in #1603, but the `Sheet` component still references `className` which is resolved in this PR.
2024-01-08 01:28:30 +01:00
Olle MånssonandZach Kelling 1bce47d3df Fix support rule in site-header.tsx (#1628)
Fixes https://github.com/shadcn-ui/ui/issues/1627 if that is desired.
2024-01-08 01:28:30 +01:00
Luka HartwigandZach Kelling 5276eea100 Support tailwind.config.ts (#1247)
Fixes #659
Fixes #633

Create Next App is using `tailwind.config.ts` in the TypeScript template. Since this is a very common use case it would be nice to preserve the type safety of the file.

I added new templates for TypeScript files. I see there is an issue #1073 which asks for ESM support as well. This is not included in this PR.

I also fixed the type error in the keyframes that is also handled in #636
2024-01-08 01:28:30 +01:00
CamTheGoblinandZach Kelling 88ca2d1d2a docs(install): Clarify & Match tsconfig Edits (#1642)
This is a small update to the installation instructions for some of the frameworks to make the instructions on editing the tsconfig file consistant across the frameworks, and remove some potentially confusing wording (if people read too fast...like me). 

Mainly applying the gatsby tsconfig instructions to vite and astro, as it is the most clear. 
Additionally changed the wording from:
```
 Add the code below to the compilerOptions...
```
to:
```
Add the following code to the compilerOptions...
```
to avoid people easily misreading it as "add the code **below the** compilerOptions".
2024-01-08 01:28:30 +01:00
Josiah HawkinsandZach Kelling a03905dcb5 docs: remove unused imports (#1661)
- Remove unused import from Alert Default example
- Remove unused imports from Alert Destructive example
- Remove unused imports from Dropdown Menu Radio Group example
2024-01-08 01:28:30 +01:00
shadcnandZach Kelling c2f4c6d718 chore: add kodiak 2024-01-08 01:28:30 +01:00
c77ab3a0dd fix(www): removes redundant class-name from H2 component (#1703)
* chore: removes redundant class from typography-h2

* chore: remove class from new york style

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
cc3225d2d1 style(shadcn-ui): use space instead of tab on config fixture (#1707)
* style: use space instead of tab on config fixture

* style: fix identation on template script

* chore(shadcn-ui): add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
1b57c12efe docs: update Remix config to use ESM (#1710)
* docs(remix): replace postcss config sample to use export default

* docs(remix): replace tailwind config sample to use export default

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:30 +01:00
alexandZach Kelling 6b0009d660 docs(www): add astro dark mode implementation (#1755)
* docs(www): add astro dark mode implementation

* docs(www): reduce redundancy in mode toggle
2024-01-08 01:28:30 +01:00
shadcnandZach Kelling 2403d9121b feat(www): add custom close button example (#1753) 2024-01-08 01:28:30 +01:00
shadcnandZach Kelling e1e6413e4d docs(www): add fonts docs (#1752) 2024-01-08 01:28:30 +01:00
Iain WandlessandZach Kelling 8e5b0150f3 feat(select): update newyork to use radix icons 2024-01-08 01:28:30 +01:00
Iain WandlessandZach Kelling 984bf2a0f7 Revert "feat(select): update registry"
This reverts commit b37fc17f0457348b09c779e09205e7708283cede.
2024-01-08 01:28:30 +01:00
Iain WandlessandZach Kelling 18ac03b505 feat(select): update registry 2024-01-08 01:28:30 +01:00
Iain WandlessandZach Kelling 17f789968d feat(select): scrollable with large datasets
newyork ui
2024-01-08 01:28:30 +01:00
Iain WandlessandZach Kelling c994d56efc feat(select): scrollable with large datasets
update to default ui
2024-01-08 01:28:30 +01:00
shadcnandZach Kelling 57fb484e03 chore: rebuild registry 2024-01-08 01:28:30 +01:00
Zach Kellingandgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> 6fb3f14122 chore(release): version packages (#1663)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-08 01:28:25 +01:00
shadcnandZach Kelling ff066a1ed5 minify cli (#1662)
* chore(shadcn-ui): minify build

* chore: add changeset
2024-01-08 01:28:19 +01:00
1a21d39d36 docs(www): framework is changed to language at language search example (#1646)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:19 +01:00
shadcnandZach Kelling 9a26635bab feat: export portal and overlay for alert-dialog, dialog and sheet (#1660) 2024-01-08 01:28:19 +01:00
RohidandZach Kelling f42a257b7e fix(alert-dialog): update portal component (#1603) 2024-01-08 01:28:19 +01:00
Diego FranchinaandZach Kelling 07c2b855d9 fix(scroll-area): added conditional flex-1 (#1296) 2024-01-08 01:28:19 +01:00
5640d1aade docs(www): add bun support for installation commands (#1445)
* feat(www): add bun support for commands

* chore: remove changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:28:19 +01:00
Shoaib AhmedandZach Kelling e667b6c082 fix(table): add relative class to handle overflow issue (#1370) 2024-01-08 01:28:19 +01:00
Zach Kellingandgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> a42b2a7343 chore(release): version packages (#1556)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-08 01:28:15 +01:00
Zach Kelling 1e2e37cfc1 docs(cli): update link to documentation (#1555)
* docs(cli): update link to documentation

* chore: add changeset
2024-01-08 01:28:07 +01:00
shadcnandZach Kelling 295f9c82d2 feat(cli): do not ask for confirmation (#1554)
* feat(cli): do not confirm

* chore: add changeset
2024-01-08 01:28:00 +01:00
d901ee1224 feat(cli): add overwrite confirmation for existing components (#973)
* feat(cli):  add overwrite confirmation for existing components

* fix(cli): handle overwrite for multiple items

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
9403c5cc71 feat(cli): support adding all components via CLI (#1033)
* feat: support adding all components via CLI

`shadcn-ui add --all`

This was manually tested to work as expected.

* chore: run prettier

* fix(cli): rename to all

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
480bebd8f0 fix(cli): handle ts file extension (#1466)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
bd39c2829c fix(cli): deduplicate classNames in applyColorMapping (#1089)
* fix(cli): deduplicate classNames in applyColorMapping

* refactor: simplify applyColorMapping return

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
c7b448a5e8 fix(www): enable input editing in the dialog demo (#1428)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
58ffb8c048 docs(www): add missing prop to ThemeProvider (#1447)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
a2b6e1d728 fix(www): rename DataTableFacetedFilter interface (#1438)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
989467ebe0 docs(www): update manual.mdx (#1471)
Fix file name

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
Arjun RajandZach Kelling c59a5d2088 docs(www): update cli add options (#1484) 2024-01-08 01:27:43 +01:00
8de3f6cd92 docs(www): update contrubuting.md and next.mdx (#1343)
* docs: Update CONTRIBUTING.md

added commit convention section to the contribution docs.

* docs: Update next.mdx for dark mode

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
29b402ba07 docs(www): update dark-mode for vite (#1118)
Co-authored-by: sanka <sanka.s@fidenz.com>
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
32e6d24741 fix(scroll-area): missing key prop error (#1295)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
2b9a42adb6 fix(www): don't allow empty chat messages (#1137)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
facaee251c docs(www): add Laravel install docs (#1279)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
9e7e0e450d chore(www): remove unneccesary imports (#1311)
* chore: remove uneccesary imports

* chore: remove from new-york style

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
1d69ceba86 chore: remove a duplicate call in init.test (#1319)
* chore: remove duplicate call in init.test

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
jsparkandZach Kelling 0f1cbe73df docs(www): fix typo (#1270) 2024-01-08 01:27:43 +01:00
a07116b071 fix(toast): toast add missing text color class (#1162)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
shadcnandZach Kelling 8136c7370b docs: update CONTRIBUTING.md 2024-01-08 01:27:43 +01:00
shadcnandZach Kelling b2edeb4ce2 docs: add contributing guide (#1266) 2024-01-08 01:27:43 +01:00
shadcnandZach Kelling f207346567 fix(www): issue with border radius in component preview (#1265) 2024-01-08 01:27:43 +01:00
f360f34c25 docs(www): remove unused Link import in form docs. (#1251)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
1af98a1b94 docs(www): @hookform/resolvers installation missing (#1257)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
fede6df5b9 fix(www): error in color values for slaet (#1224)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
dc96d7f9cf fix(www): payment method :has selector not working on Firefox (#1209)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
174c54a00c fix(docs): add missing files in toast manual installation (#1243)
Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
0c9dfc5404 fix(docs): missing form manual installation and aspect-ratio typo (#1242)
* fix(docs): add form manual installation

* fix(docs): typo in aspect-ratio

---------

Co-authored-by: shadcn <m@shadcn.com>
2024-01-08 01:27:43 +01:00
shadcnandZach Kelling cafe648d78 fix(www): extra semi 2024-01-08 01:27:43 +01:00
Zach Kelling 8c36ca3943 fix: remove semicolon duplicated in theme.css (#1146) 2024-01-08 01:27:38 +01:00
Zach Kelling aeb968a00e Update UI 2023-08-12 00:50:40 -07:00
shadcnandGitHub e5a3ff56e3 feat: themes (#1135) 2023-08-07 22:39:16 +04:00
shadcn 14b2fd0fbb docs(www): fix typo 2023-08-04 20:54:46 +04:00
c674ed0693 docs(www): add api reference for components.json (#982)
* docs: add api reference for component.json file

* docs: use Link for internal links in component.json

* docs(www): update docs for components.json

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-08-04 17:03:41 +04:00
15c71cddb5 docs(www): update import in remix installation (#974)
Co-authored-by: shadcn <m@shadcn.com>
2023-08-03 22:27:39 +04:00
vinayandGitHub 4a32aaa0e6 fix(examples): update keyboard shortcut for opening dialog on Windows (#1004) 2023-08-03 22:22:38 +04:00
90ff6f2bcf docs(www): add missing "command" component to imports (#550)
Co-authored-by: shadcn <m@shadcn.com>
2023-08-03 21:57:39 +04:00
d1111419fc docs(www): add dark mode for Vite. (#814)
Co-authored-by: shadcn <m@shadcn.com>
2023-08-03 19:41:33 +04:00
221796c4d3 docs(www): update import path for button (#839)
Co-authored-by: shadcn <m@shadcn.com>
2023-08-03 19:03:44 +04:00
7f9b8fdd02 fix(next-template): remove experimental appdir (#979)
Co-authored-by: shadcn <m@shadcn.com>
2023-08-01 19:21:18 +04:00
Bernardo FerrariandGitHub b260a89462 style(card): remove extra spacing (#1082) 2023-08-01 19:07:03 +04:00
743dfecf83 fix(alert): padding on Firefox (#1059)
Co-authored-by: shadcn <m@shadcn.com>
2023-07-30 15:50:11 +04:00
d8fd3384a7 fix(combobox): search language by label in examples (#989)
Co-authored-by: shadcn <m@shadcn.com>
2023-07-30 15:33:18 +04:00
shadcnandGitHub b95f47243f fix(www): responsive cards (#1066) 2023-07-28 23:14:38 +04:00
a6bdf3ccc2 fix(www): overflow issue in documentation (#1055)
Co-authored-by: shadcn <m@shadcn.com>
2023-07-28 16:03:43 +04:00
munan56andGitHub d9a504621a docs(www): update import path next.mdx (#1062) 2023-07-28 15:56:19 +04:00
shadcnandGitHub fb6b47f283 ci: rename repo owner (#1056) 2023-07-27 13:12:13 +04:00
5843a9036f docs(www): add typescript configuration question to cli docs (#898)
Co-authored-by: shadcn <m@shadcn.com>
2023-07-12 20:34:05 +04:00
c0c12a4027 docs(www): update label in card demo (#799)
Co-authored-by: shadcn <m@shadcn.com>
2023-07-12 20:22:35 +04:00
Lennart GastlerandGitHub 3171a28f79 docs(www): improve astro guide (#858) 2023-07-12 20:17:25 +04:00
42bd7a6c82 docs(www): add links to frameworks (#744)
Co-authored-by: shadcn <m@shadcn.com>
2023-07-05 23:29:14 +04:00
shadcn b2848e3a7e feat: update next-template 2023-07-05 23:00:03 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
dee99fd5f6 chore(release): version packages (#835)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-04 23:26:32 +04:00
shadcnandGitHub fffdec4e5a feat(shadcn-ui): add support for jsx (#834) 2023-07-04 23:22:20 +04:00
shadcnandGitHub ef7883fb46 chore(www): site updates (#829)
* chore(www): update to latest Next.js

* feat(www): replace lucide icons
2023-07-04 17:04:22 +04:00
shadcnandGitHub 43360de49e docs(www): add radix-ui deps install (#827) 2023-07-04 12:01:50 +04:00
LennartandGitHub 40b5a6cd63 docs(www): improve Gatsby guide (#826) 2023-07-04 10:55:01 +04:00
Daniele LuisettoandGitHub 17acccf3c8 docs(www): add astro installation guide (#824) 2023-07-04 10:02:34 +04:00
Moinul MoinandGitHub c804549138 docs(www): add installation guide for Gatsby (#822)
* docs: add installation guide for Gatsby

* docs: add  PR review changes
2023-07-03 22:23:52 +04:00
shadcnandGitHub e94944e32c docs(www): fix link 2023-06-30 23:32:17 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
983ec522ac chore(release): version packages (#737)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-30 22:56:01 +04:00
shadcn 8162576082 feat: restructure installation docs 2023-06-30 22:37:43 +04:00
16c06b531e docs(www): add react with vite installation guide (#714)
* docs: add react with vite installation guide

* refactor(docs): move Ract with vite page into installation.mdx as tab

* fix(docs): remove classnames and wrapper divs

* fix(docs): update tsconfig file path to default @

* docs(www): fix code style

---------

Co-authored-by: Samuel Adebayo <samuel.adebayo@engagetech.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-06-30 21:21:10 +04:00
1cb9fae778 docs(www): add remix and tabbed installation docs (#753)
* docs: add react with vite installation guide

* refactor(docs): move Ract with vite page into installation.mdx as tab

* fix(docs): remove classnames and wrapper divs

* fix(docs): update tsconfig file path to default @

* feat: added remix installation docs

* feat: added tabbed installation docs

* fix: remove log statement

* fix: cleaned up, restored usage notice, removed vite for now

* fix: moved installation.mdx into folder

---------

Co-authored-by: Samuel Adebayo <samuel.adebayo@engagetech.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-06-29 17:27:03 +04:00
8d5900e552 feat(shadcn-cli): add support for bun install (#707)
* add support for bun install

* fix(cli): add changeset for bun install

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-06-27 11:48:51 +04:00
Fady MakramandGitHub 2aa680316d fix(accordion): prevent accordion chevron from shrinking on smaller viewports (#717)
* Prevent accordion chevron from shrinking on smaller viewports

* fix: apply feedback
2023-06-27 11:39:54 +04:00
2478434376 fix(www): update data-table-toolbar to correctly handle isFiltered (#702)
* Update data-table-toolbar.tsx

* style(www): run format:write

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-06-26 16:16:03 +04:00
70db4b9094 chore(www): pnpx to pnpm dlx (#350)
* fix(npm-commands): pnpx to pnpm dlx

* style(www): fix

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-06-26 15:50:53 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
65de31faa2 chore(release): version packages (#696)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-24 21:12:15 +04:00
05abadf6e3 fix(shadcn-ui): use @antfu/ni to detect package manager (#677)
* fix(cli): use @antfu/ni to detect package manager

* chore(cli): cleanup imports

* Create cyan-houses-dress.md

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 21:01:22 +04:00
a972bd07dd docs(www): fix TypeScript typo (#645)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 20:38:10 +04:00
5c8b8f4a95 fix(shadcn-ui): cssVars template typo causing missing value (#682)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 20:10:01 +04:00
b7893f42c6 fix(shadcn-ui): use slash for alpha values in colors (#681)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 20:09:22 +04:00
89c3be7fdb fix(docs): remove duplicate install command (#679)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 15:51:23 +04:00
Bryce KalowandGitHub 75a6dea48d docs(www): directory -> directive (#685) 2023-06-24 15:49:47 +04:00
8efa2e2e26 docs(www): missing RSC anchor link (#664)
adda a missing # for the RSC section anchor link, resulting in 404.

Co-authored-by: shadcn <m@shadcn.com>
2023-06-23 20:08:18 +04:00
shadcnandGitHub c9e2dd3080 docs: update toast.mdx (#670) 2023-06-23 19:13:29 +04:00
shadcnandGitHub 7a3d6b2ed7 feat: add manual installation for components (#666) 2023-06-23 14:28:34 +04:00
Ahmed AbdelbasetandGitHub 2b94bc1e05 style(shadcn-ui): remove unused variable in index.ts (#654) 2023-06-23 10:58:33 +04:00
eb04464a35 fix: update schema.json (#651)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-23 00:27:28 +04:00
shadcnandGitHub 64e9c5a5a6 docs: add latest to cli (#649) 2023-06-23 00:07:19 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
909e7ba5fe chore(release): version packages (#644)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-22 23:08:57 +04:00
shadcnandGitHub 453391a521 fix: schema keys validation (#641)
* fix: schema keys validation

* chore: add changeset

* fix: update registry base url
2023-06-22 23:05:54 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e04b6382bd chore(release): version packages (#640)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-22 22:49:27 +04:00
shadcnandGitHub e475d671ca feat: new CLI, Styles and more (#637) 2023-06-22 22:44:52 +04:00
ba8df39497 fix: class name typo (#398)
* fix: class name

* fix: class names order

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:26:44 +04:00
06e8b83431 fix(www): register field array input once (#466)
Closes: #465

Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:20:43 +04:00
07e65f6ee7 fix(command): modify incorrect className (#473)
text-foreground-muted => text-muted-foreground

Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:16:04 +04:00
afcaadcec4 docs(www): broken link to React Hook Form's useController hook (#479)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:10:34 +04:00
shadcnandGitHub 7c241a0f9d feat(next-template): update template and dependencies (#483)
* feat(next-template): update template and deps

* docs(next-template): update README

* fix(www): update deps

* chore: remove console.log
2023-05-29 11:02:02 +04:00
45d760bb8c feat(cli): add support for config file (#245)
* feat(cli): add config files and examples in docs

* docs(www): remove cli advanced options

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-28 10:21:11 +04:00
8f4d0ab308 chore: update pnpm version in package.json (#314)
* Update pnpm version in package.json

* Update package.json

* 8.5.1

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 22:05:55 +04:00
5f480caae7 fix: tailwind-indicator classnames (#453)
+ Remove unnecessary `hidden` breakpoint classnames

Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 21:05:41 +04:00
Sammy HassandGitHub 5fb0d00f51 fix(docs): command menu unresponsive after no results (#455) 2023-05-25 20:57:49 +04:00
a6af312ec2 feat(sheet): expose close trigger for Sheet (#438)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 16:10:22 +04:00
SpasticandGitHub 96a3eb8e90 fix typings (#287)
* fix typings

* change Toast interface to type

* ran prettier
2023-05-25 15:51:13 +04:00
bec13e0895 feat(cli): use https_proxy and consider previous install locations (#430)
* feat(cli): use system proxy

* feat(cli): adds default installation directory

* style: formatting

* feat: update lockfile

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 14:37:37 +04:00
shadcn 46edd3ffc6 feat(www): add link to view code examples 2023-05-25 13:32:05 +04:00
Raí SiqueiraandGitHub 037e29de6b fix(www): fix language placeholder (#433)
Signed-off-by: Rai Siqueira <rai93siqueira@gmail.com>
2023-05-24 18:21:27 +04:00
6859336b71 docs(www): Fix unmatched closing brace typo (#423)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-23 11:31:32 +04:00
67904a3be3 fix(www): add key to list map components (#413)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-23 11:22:35 +04:00
ZwyxandGitHub 95e3c1f4a8 docs(www): fix inconsistency in forms docs + add note about uncontrolled components (#417)
* fix(forms): correct inconsistency in forms example

* doc(forms): add note about uncontrolled components
2023-05-22 17:00:59 +04:00
shadcn 169cdbd6aa docs(www): update defaultValues for useForm 2023-05-22 13:43:29 +04:00
shadcn b7d2af4a43 fix(www): filter out disabled links 2023-05-20 21:44:26 +04:00
shadcn 0a978fc2e9 feat: switch to stable next 2023-05-20 20:02:42 +04:00
jeremyandGitHub 554f32c33b fix: incorrect example in form docs (#387) 2023-05-20 12:13:06 +04:00
39e6914ccd docs(www): fix typo (#375)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 10:09:42 +04:00
3ac93f3796 fix(radio-group): add aspect-square on radio button (#379)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 10:03:52 +04:00
0e3b7c33b0 docs(www): update react-hook-form.mdx (#378)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 09:58:25 +04:00
c04f85da55 fix(www): refactor controlled inputs API examples (#385)
* refactor: remove usage of uncontrolled input apis within controlled inputs

Closes: #384

* docs: display FormField controlled input usage example

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 08:58:44 +04:00
df3cb64265 fix(form): avoid unnecessary state subscriptions (#383)
* perf: avoid unnecessary state subscriptions

Closes: #382

* fix(form): code format

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 08:52:53 +04:00
moshyfawnandGitHub 026e6777a1 fix(www): display account form name input error (#381)
* docs: display account form name input error

Closes: #380

* style: remove callback return statement
2023-05-20 08:40:01 +04:00
shadcnandGitHub 6b0ba23097 feat: react-hook-form (#377)
* feat(form): add form component

* feat(www): update site styles

* feat: add form examples

* docs(www): add docs for forms

* docs(www): hide tabs for docs demo
2023-05-19 22:56:49 +04:00
c070efc3e3 fix(www): missing ']' in className (#358)
* fix(code-block-wrapper): missing ']' in className

* fix(code-block-wrapper): re-order tailwind className

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-16 15:41:35 +04:00
770eb3ac60 fix(textarea): replace h-20 with min-h-[80px] (#344)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-16 15:04:17 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
2c756221e6 chore(release): version packages (#352)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-13 13:35:46 +04:00
4e42407af3 fix(shadcn-ui): add missing deps for Button (#259)
* fix: add missing deps for Button

* fix: use correct dep

Co-authored-by: Pablo <its.monotype@gmail.com>

* chore: add changeset

---------

Co-authored-by: Pablo <its.monotype@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-05-13 13:07:39 +04:00
JackandGitHub 6c4c0f6195 fix(data-table): uncontrolled input error on data-table (#335) 2023-05-11 10:07:39 +04:00
0c035c5821 fix(next-template): remove unused import Laptop in icons.tsx (#310)
Remove unused import 'Laptop' in icons.tsx (nextjs-example)

Co-authored-by: shadcn <m@shadcn.com>
2023-05-10 21:48:46 +04:00
shadcn 12c8d445bc fix(www): update padding for row actions 2023-05-10 20:51:00 +04:00
shadcn 54ff4d26f8 fix(www): filter display for lg 2023-05-10 16:32:57 +04:00
84f56db1c5 docs(www): fix typo
Co-authored-by: shadcn <m@shadcn.com>
2023-05-10 07:49:17 +04:00
Ben JacobsonandGitHub 9d41b8d61f docs(www): fix link to DataTable (#322) 2023-05-10 00:09:15 +04:00
shadcnandGitHub f24a56bea9 feat: add table and data table (#321) 2023-05-09 23:25:26 +04:00
2bb370a1aa feat(table): add table component (#248)
* Add table.tsx

* Update table.tsx

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-08 15:49:19 +04:00
a0cbee1457 fix(checkbox,radio-group): Increase contrast ratio of checkboxes and radio buttons (#224)
* Update checkbox.tsx

* Update radio-group.tsx

* fix(radio-group): update colors for contrast

* fix(checkbox): update colors for contrast

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-08 15:43:10 +04:00
8d1f15eed9 fix(next-template): strict type check
Co-authored-by: Danazumi <granzin98@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-05-03 18:47:49 +04:00
JamesandGitHub db848556e2 fix(dropdown-menu): dropdown sub menu using wrong text color (#285) 2023-05-03 17:20:18 +04:00
shadcnandGitHub 7d1a326873 docs: move combobox and date picker to their own page (#283) 2023-05-03 15:59:00 +04:00
shadcnandGitHub 6efcbdc0d8 fix(www): update to canary with scrolling fix (#279) 2023-05-03 10:53:59 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
2f252b8b39 chore(release): version packages (#272)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-02 19:25:02 +04:00
shadcnandGitHub fd5adb97be fix(shadcn-ui): update color of cards in dark mode (#271) 2023-05-02 15:39:16 +04:00
Evan RosenfeldandGitHub bae2c4f72e feat(button): support render delegation with asChild parameter (#174) 2023-04-30 12:01:41 +04:00
Elliot AndersonandGitHub cf2d163098 fix(www): set icon fill to current currentColor (#239) 2023-04-29 18:52:25 +04:00
Aron HafnerandGitHub ffa90938d7 docs(www): set correct colors for card in darkmode (#213) 2023-04-20 16:36:38 +04:00
10ac81350c fix(www): component selection in copy-button (#220)
Co-authored-by: shadcn <m@shadcn.com>
2023-04-20 16:20:14 +04:00
155ed253c6 docs(www): Fix typo in alert component usage (#196)
Co-authored-by: shadcn <m@shadcn.com>
2023-04-20 12:49:15 +04:00
shadcnandGitHub 05255d06ea feat(www): implement event tracking (#218)
* feat(www): implement event tracking

* fix(www): always show copy button

* fix(www): update align props for copy button
2023-04-20 12:21:50 +04:00
Elia MainoandGitHub e59667b74e fix(badge): update destructive bg color on hover (#189) 2023-04-20 09:34:32 +04:00
Vincent TaneriandGitHub 9e5253da27 docs(www): fix a typo in FAQ (#198) 2023-04-19 19:56:04 +04:00
Seo Jun HyungandGitHub 013672ad27 fix(www): remove styles for preventing scroll when modal is open (#209) 2023-04-19 19:43:33 +04:00
shadcn 502ad0c46f chore: update template 2023-04-19 15:01:29 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
094f9afc7b chore(release): version packages (#206)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-04-19 13:47:25 +04:00
shadcnandGitHub 368c0ee148 fix: repo name in changeset config (#205) 2023-04-19 13:38:58 +04:00
shadcnandGitHub dc79213dfe chore(www): update deps and fix CI errors (#204) 2023-04-19 13:22:53 +04:00
shadcnandGitHub d26ea9adb2 docs(shadcn-ui): update README (#203) 2023-04-19 09:48:47 +04:00
shadcnandGitHub a9868cf5c4 feat(shadcn-ui): update init message (#197) 2023-04-18 19:12:38 +04:00
shadcnandGitHub 546eaf7604 fix(shadcn-ui): support for array of dependencies (#195) 2023-04-18 18:34:25 +04:00
shadcn 513a4606cc fix(www): color of link 2023-04-18 10:18:13 +04:00
shadcn 4aabb3574d fix(switch): update background color for dark mode 2023-04-18 10:17:00 +04:00
The Command RunnerandGitHub 746a8657c3 docs(www): add type keyword to ClassValue (#184)
Add `type` keyword because Typescript complains, since ClassValue is only imported as a type.
2023-04-18 10:10:19 +04:00
Gimel DickandGitHub db269af1eb fix: typo in badge component (#186) 2023-04-18 05:45:45 +04:00
shadcn d1ee9df661 fix(www): background color for auth form 2023-04-17 19:41:45 +04:00
shadcnandGitHub 1dbfe2b407 feat: CSS variables and CLI (#180) 2023-04-17 19:19:40 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
eeb73544d2 chore(release): version packages (#179)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-04-17 16:55:42 +04:00
shadcnandGitHub a668a219be ci: skip type check temporarily (#178) 2023-04-17 15:52:17 +04:00
shadcnandGitHub e7794e4a25 feat(shadcn-ui): init command 2023-04-17 15:41:03 +04:00
shadcnandGitHub 91c8b5c2ff feat(shadcn-ui): add support for aliases 2023-04-17 12:59:57 +04:00
shadcn aa201de7c2 chore: update template 2023-04-06 12:19:55 +04:00
Raj SavaliyaandGitHub 2e35fde43d chore: replace third party @next/font with next/font (#145) 2023-04-03 09:15:23 +04:00
StormandGitHub 848ba3392e docs(collapsible): add missing imports for collapsible (#150) 2023-04-03 08:30:50 +04:00
Hugo Sainte-MarieandGitHub 147e538ba7 docs(dialog): update dialog (#137) 2023-04-03 08:19:47 +04:00
Hugo Sainte-MarieandGitHub 9932e9cdcd docs(scroll-area): update description for scroll-area (#136) 2023-04-03 08:14:59 +04:00
StormandGitHub 17b3df1e7c docs: change sheet wrong import (#141) 2023-04-01 08:06:06 +04:00
Luke McDonaldandGitHub 6eeaa0a334 fix: remove extra curly brace in style element (#146) 2023-04-01 07:52:48 +04:00
shadcn 55088e08e6 feat(button): remove active:scale-95 2023-04-01 07:11:34 +04:00
shadcn afb2860597 feat: update front page 2023-04-01 06:55:03 +04:00
shadcn 355c591e59 chore: update pnpm lock 2023-04-01 06:47:39 +04:00
shadcn b343bad250 fix: remove dropdown from main nav 2023-04-01 06:46:14 +04:00
shadcn 67d24c7115 fix: remove unist dependency 2023-04-01 06:46:01 +04:00
shadcn d324009381 docs(www): update prod message 2023-03-08 19:27:03 +04:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c582b34cbc chore(release): version packages (#119)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-03-08 19:23:07 +04:00
10887 changed files with 58834 additions and 916769 deletions
+8
View File
@@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "luxdefi/ui" }],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["www", "**-template"]
}
+8 -2
View File
@@ -1,4 +1,10 @@
# editorconfig.org
root = true
[*]
quote_type = single
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
-8
View File
@@ -1,8 +0,0 @@
node_modules/
target/
.next/
build/
dist/
/templates/
/fixtures/
-1
View File
@@ -8,7 +8,6 @@
"plugin:tailwindcss/recommended"
],
"plugins": ["tailwindcss"],
"ignorePatterns": ["**/fixtures/**"],
"rules": {
"@next/next/no-html-link-for-pages": "off",
"tailwindcss/no-custom-classname": "off",
@@ -1,25 +0,0 @@
title: "[blocks]: "
labels: ["Blocks Request"]
body:
- type: markdown
attributes:
value: |
### Thanks for taking the time to create a block request! Please search open/closed requests before submitting, as the block or a similar one may have already been requested.
- type: textarea
id: block-description
attributes:
label: Description
description: Tell us about your block request
placeholder: "A dashboard for an e-commerce website showing sales, orders, and customers..."
validations:
required: true
- type: input
id: block-example-url
attributes:
label: Example
description: Link to an example of the block
placeholder: ex. https://example.com
validations:
required: false
-3
View File
@@ -1,3 +0,0 @@
# These are supported funding model platforms
github: [shadcn]
-85
View File
@@ -1,85 +0,0 @@
name: "Bug report"
description: Report an issue
title: '[bug]: '
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
### Thanks for taking the time to create a bug report. Please search open/closed issues before submitting, as the issue may have already been reported/addressed.
- type: markdown
attributes:
value: |
#### If you aren't sure this is a bug or not, please open a discussion instead:
- [Discussions](https://github.com/shadcn-ui/ui/discussions/new?category=general)
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us how in the description. Thanks!
placeholder: Bug description
validations:
required: true
- type: input
id: components-affected
attributes:
label: Affected component/components
description: Which shadcn/ui components are affected?
placeholder: ex. Button, Checkbox...
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: How to reproduce
description: A step-by-step description of how to reproduce the bug.
placeholder: |
1. Go to '...'
2. Click on '....'
3. See error
validations:
required: true
- type: input
id: codesandbox-stackblitz
attributes:
label: Codesandbox/StackBlitz link
description: |
A link to a CodeSandbox or StackBlitz that includes a minimal reproduction of the problem. In rare cases when not applicable, you can link to a GitHub repository that we can easily run to recreate the issue. If a report is vague and does not have a reproduction, it will be closed without warning.
> [!CAUTION]
> If you skip this step, this issue might be **labeled** with `please add a reproduction` and **closed**.
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs
description: "Please include browser console and server logs around the time this bug occurred. Optional if provided reproduction. Please try not to insert an image but copy paste the log text."
render: bash
- type: textarea
id: system-info
attributes:
label: System Info
description: Information about browsers, system or binaries that's relevant.
render: bash
placeholder: System, Binaries, Browsers
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Before submitting
description: By submitting this issue, you agree to follow our [Contributing Guidelines](https://github.com/shadcn-ui/ui/blob/main/CONTRIBUTING.md).
options:
- label: I've made research efforts and searched the documentation
required: true
- label: I've searched for existing issues
required: true
-5
View File
@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Get Help
url: https://github.com/shadcn-ui/ui/discussions/new?category=general
about: If you can't get something to work the way you expect, open a question in our discussion forums.
@@ -1,55 +0,0 @@
name: "Feature request"
description: Create a feature request for shadcn/ui
title: '[feat]: '
labels: ['area: request']
body:
- type: markdown
attributes:
value: |
### Thanks for taking the time to create a feature request! Please search open/closed issues before submitting, as the issue may have already been reported/addressed.
- type: markdown
attributes:
value: |
#### If you aren't sure this is a bug or not, please open a discussion instead:
- [Discussions](https://github.com/shadcn-ui/ui/discussions/new?category=general)
- type: textarea
id: feature-description
attributes:
label: Feature description
description: Tell us about your feature request
placeholder: 'I think this feature would be great because...'
value: 'Describe your feature request...'
validations:
required: true
- type: input
id: components-affected
attributes:
label: Affected component/components
description: Is this feature request relevant to any of the already existing components?
placeholder: ex. Button, Checkbox...
validations:
required: false
- type: textarea
id: context
attributes:
label: Additional Context
description: Add any other context about the feature here.
placeholder: ex. screenshots, Stack Overflow links, forum links, etc.
value: 'Additional details here...'
validations:
required: false
- type: checkboxes
id: terms
attributes:
label: Before submitting
description: By submitting this issue, you agree to follow our [Contributing Guidelines](https://github.com/shadcn-ui/ui/blob/main/CONTRIBUTING.md).
options:
- label: I've made research efforts and searched the documentation
required: true
- label: I've searched for existing issues and PRs
required: true
+12
View File
@@ -0,0 +1,12 @@
// ORIGINALLY FROM CLOUDFLARE WRANGLER:
// https://github.com/cloudflare/wrangler2/blob/main/.github/changeset-version.js
import { exec } from "child_process"
// This script is used by the `release.yml` workflow to update the version of the packages being released.
// The standard step is only to run `changeset version` but this does not update the package-lock.json file.
// So we also run `npm install`, which does this update.
// This is a workaround until this is handled automatically by `changeset version`.
// See https://github.com/changesets/changesets/issues/421.
exec("npx changeset version")
exec("npm install")
-46
View File
@@ -1,46 +0,0 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/astro-app"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/astro-monorepo"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/next-app"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/next-monorepo"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/react-router-app"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/react-router-monorepo"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/start-app"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/start-monorepo"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/vite-app"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/templates/vite-monorepo"
schedule:
interval: "weekly"
-9
View File
@@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" role="img" aria-label="ui">
<rect width="1280" height="640" fill="#0A0A0A"/>
<svg x="96" y="215" width="210" height="210" viewBox="0 0 67 67"><path d="M22.21 67V44.6369H0V67H22.21Z" fill="#fff"/><path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#fff"/><path d="M22.21 0H0V22.3184H22.21V0Z" fill="#fff"/><path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#fff"/><path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#fff"/></svg>
<text x="378" y="276" font-family="Inter,system-ui,-apple-system,sans-serif" font-size="78" font-weight="800" letter-spacing="-2" fill="#ffffff">ui</text>
<text x="378" y="322" font-family="Inter,system-ui,sans-serif" font-size="30" fill="#ffffff" opacity=".66">React component library for AI applications</text>
<rect x="378" y="338" width="806" height="3" rx="1.5" fill="#ffffff" opacity=".9"/>
<text x="378" y="390" font-family="Inter,system-ui,sans-serif" font-size="24" font-weight="600" fill="#ffffff" opacity=".5">github.com/hanzoai</text>
<text x="1184" y="390" text-anchor="end" font-family="Inter,system-ui,sans-serif" font-size="24" font-weight="600" fill="#ffffff" opacity=".5">hanzo.ai</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

+1 -1
View File
@@ -4,7 +4,7 @@
import { exec } from "child_process"
import fs from "fs"
const pkgJsonPath = "pkgs/shadcn/package.json"
const pkgJsonPath = "packages/cli/package.json"
try {
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
exec("git rev-parse --short HEAD", (err, stdout) => {
+1 -1
View File
@@ -4,7 +4,7 @@
import { exec } from "child_process"
import fs from "fs"
const pkgJsonPath = "pkgs/shadcn/package.json"
const pkgJsonPath = "packages/cli/package.json"
try {
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
exec("git rev-parse --short HEAD", (err, stdout) => {
-9
View File
@@ -1,9 +0,0 @@
name: CI/CD
on:
push: { branches: [main], tags: ["v*"] }
pull_request:
workflow_dispatch:
jobs:
cicd:
uses: hanzoai/ci/.github/workflows/build.yml@v1
secrets: inherit
+116
View File
@@ -0,0 +1,116 @@
name: Code check
on:
pull_request:
branches: ["*"]
jobs:
lint:
runs-on: ubuntu-latest
name: pnpm lint
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- run: pnpm lint
format:
runs-on: ubuntu-latest
name: pnpm format:check
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- run: pnpm format:check
tsc:
runs-on: ubuntu-latest
name: pnpm typecheck
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- run: pnpm typecheck
+65
View File
@@ -0,0 +1,65 @@
# Adapted from create-t3-app.
name: Write Beta Release comment
on:
workflow_run:
workflows: ["Release - Beta"]
types:
- completed
jobs:
comment:
if: |
github.repository_owner == 'luxdefi-ui' &&
${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
name: Write comment to the PR
steps:
- name: "Comment on PR"
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
for (const artifact of allArtifacts.data.artifacts) {
// Extract the PR number and package version from the artifact name
const match = /^npm-package-luxdefi-ui@(.*?)-pr-(\d+)/.exec(artifact.name);
if (match) {
require("fs").appendFileSync(
process.env.GITHUB_ENV,
`\nBETA_PACKAGE_VERSION=${match[1]}` +
`\nWORKFLOW_RUN_PR=${match[2]}` +
`\nWORKFLOW_RUN_ID=${context.payload.workflow_run.id}`
);
break;
}
}
- name: "Comment on PR with Link"
uses: marocchino/sticky-pull-request-comment@v2
with:
number: ${{ env.WORKFLOW_RUN_PR }}
message: |
A new prerelease is available for testing:
```sh
npx luxdefi-ui@${{ env.BETA_PACKAGE_VERSION }}
```
- name: "Remove the autorelease label once published"
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: '${{ env.WORKFLOW_RUN_PR }}',
name: '🚀 autorelease',
});
+60
View File
@@ -0,0 +1,60 @@
# Adapted from create-t3-app.
name: Release - Beta
on:
pull_request:
types: [labeled]
branches:
- main
jobs:
prerelease:
if: |
github.repository_owner == 'luxdefi' &&
contains(github.event.pull_request.labels.*.name, '🚀 autorelease')
name: Build & Publish a beta release to NPM
runs-on: ubuntu-latest
environment: Preview
steps:
- name: Checkout Repo
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Use PNPM
uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.1
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install NPM Dependencies
run: pnpm install
- name: Modify package.json version
run: node .github/version-script-beta.js
- name: Authenticate to NPM
run: echo "//registry.npmjs.org/:_authToken=$NPM_ACCESS_TOKEN" >> packages/cli/.npmrc
env:
NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
- name: Publish Beta to NPM
run: pnpm pub:beta
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@main
with:
path: packages/cli
- name: Upload packaged artifact
uses: actions/upload-artifact@v2
with:
name: npm-package-luxdefi-ui@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
path: packages/cli/dist/index.js
+53
View File
@@ -0,0 +1,53 @@
# Adapted from create-t3-app.
name: Release
on:
push:
branches:
- main
jobs:
release:
if: ${{ github.repository_owner == 'luxdefi-ui' }}
name: Create a PR for release workflow
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Use PNPM
uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.1
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
version: 8.6.1
node-version: 18
cache: "pnpm"
- name: Install NPM Dependencies
run: pnpm install
# - name: Check for errors
# run: pnpm check
- name: Build the package
run: pnpm build:cli
- name: Create Version PR or Publish to NPM
id: changesets
uses: changesets/action@v1.4.1
with:
commit: "chore(release): version packages"
title: "chore(release): version packages"
version: node .github/changeset-version.js
publish: npx changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
NODE_ENV: "production"
+42
View File
@@ -0,0 +1,42 @@
name: Test
on:
pull_request:
branches: ["*"]
jobs:
test:
runs-on: ubuntu-latest
name: pnpm test
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- run: pnpm test
+8 -51
View File
@@ -2,7 +2,6 @@
# dependencies
node_modules
**/node_modules
.pnp
.pnp.js
@@ -11,6 +10,8 @@ coverage
# next.js
.next/
out/
build
# misc
.DS_Store
@@ -23,61 +24,17 @@ yarn-error.log*
.pnpm-debug.log*
# local env files
**/.env
**/.env.*
.env.local
.env.development.local
.env.test.local
.env.production.local
# turbo
.turbo
.contentlayer
**/tsconfig.tsbuildinfo
tsconfig.tsbuildinfo
# build
**/dist
**/.npmrc
**/.next
etc
# lock files
bun.lockb
package-lock.json
# pnpm-lock.yaml - needed for CI/CD, do not ignore
yarn.lock
# AI cruft
AGENTS.md
CLAUDE.md
GEMINI.md
GROK.md
QWEN.md
.playwright-mcp
app/out
app/.source
# test artifacts
tests/reports/
tests/artifacts/
# Note: Block screenshots are pre-generated and committed
# They cannot be generated in CI (requires display/browser)
# app/public/r/styles/*/ -- REMOVED to allow committing screenshots
# Test artifacts
test-results/
playwright-report/
*BADGE_INSPECTION*.md
*BADGE_INSPECTION*.txt
# ide
.idea
.fleet
.notes
.claude/settings.local.json
pkg/ui/dist-test/
app/public/api/
**/.npmrc
-26
View File
@@ -1,26 +0,0 @@
# The whole caller. Every decision this pipeline makes — what to test, what to
# build, where to roll it — is read from the root hanzo.yml, so this file only
# ever names the triggers and the reusable.
#
# `.hanzo/workflows`, not `.github/workflows`, and that is not a style choice:
# CI for this repo runs on git.hanzo.ai, which resolves ONLY `.hanzo/workflows`.
# github.com has zero self-hosted runners registered for the `hanzo-build-*`
# labels this pipeline asks for, so the same file under `.github/workflows`
# would queue there forever and never report anything at all.
name: CI/CD
on:
push:
branches: [main]
tags: ['v*']
pull_request:
workflow_dispatch:
concurrency:
group: cicd-${{ github.ref }}
cancel-in-progress: true
jobs:
cicd:
uses: hanzoai/ci/.hanzo/workflows/build.yml@v1
secrets: inherit
-70
View File
@@ -1,70 +0,0 @@
name: deploy
# RETIRED as an automatic lane. The image is built by hanzo.yml's `images:` block
# through hanzoai/ci (.hanzo/workflows/cicd.yml), and this job is kept only as a
# manually-dispatched fallback.
#
# The reason is one capability, not tidiness. ui.hanzo.ai is a static export, so
# its ingest key has to be BAKED IN at build; the key lives in KMS as
# `deploy/PUBLISHABLE_KEY`, and hanzoai/ci is the only lane that reads it and
# passes it as `--build-arg PUBLISHABLE_KEY=…`. The `docker build -t "$image" .`
# below cannot — there is no KMS hop in it and nowhere to put the value — so any
# image it produced would ship a client with no key at all, and cloud answers
# `401 ingest_key_required` for an unattributed write. With the Dockerfile now
# failing closed on an empty key, this job fails on every run instead of quietly
# publishing an inert site.
#
# Left in place rather than deleted, and dispatch-only rather than on push:
# deleting a repo's only build lane strands the host with no failing run to show
# it, and two push-triggered builders for one image means one commit yields two
# images under two tag schemes. Dispatching this publishes `<short-sha>` with an
# EMPTY key and the Dockerfile will refuse it — so if you dispatch it, pass the
# key yourself or expect the gate to fire.
on:
workflow_dispatch:
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false
jobs:
image:
# hanzo-build-linux-amd64, not ubuntu-latest. This file lives in
# .hanzo/workflows, which GitHub never reads -- git.hanzo.ai is the only
# thing that can run it, and the fleet deliberately advertises no generic
# ubuntu-* label (~1400 mirrored forks all ask for it; one bad job was
# retried ~520 times across 10 runners). An unmatched label is not an
# error here, it is silence: the job queues until the 24h timeout, so this
# deploy has never produced an image and nothing ever said so.
runs-on: hanzo-build-linux-amd64
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: build and push
# GHCR_TOKEN/GHCR_USER, which are the org secrets that actually exist.
# This job asked for REGISTRY_TOKEN, which was never defined anywhere —
# the org has exactly GHCR_TOKEN, GHCR_USER, GH_PAT, KMS_CLIENT_ID,
# KMS_CLIENT_SECRET, OCI_TOKEN, OCI_USER — so the guard below fired on
# every run and nothing was ever published. Every other image build in
# the org already uses the GHCR_* pair; this file and papers were the
# two that invented a name.
#
# The username comes from the secret too, rather than being hardcoded to
# hanzo-dev, so rotating the publishing identity is one org-level change.
env:
GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
GHCR_USER: ${{ secrets.GHCR_USER }}
run: |
set -euo pipefail
if [ -z "${GHCR_TOKEN:-}" ] || [ -z "${GHCR_USER:-}" ]; then
echo "::error::GHCR_TOKEN/GHCR_USER are unset, so nothing was published."
exit 1
fi
tag="$(git rev-parse --short HEAD)"
image="ghcr.io/hanzoai/ui:${tag}"
echo "${GHCR_TOKEN}" | docker login ghcr.io -u "${GHCR_USER}" --password-stdin
docker build -t "${image}" .
docker push "${image}"
echo "published ${image} — set image.tag AND image.digest in universe charts/app/values/hanzo/ui.yaml"
-343
View File
@@ -1,343 +0,0 @@
name: Publish Packages
on:
push:
branches: [main]
paths:
# BOTH package roots, and the singular one is not a typo — `@hanzo/ui` and
# `@hanzo/data` live under pkg/, everything else under pkgs/.
#
# ⚠️ This list and the `for pkg_json in …` loop below are ONE FACT. The loop
# was already fixed to walk both roots, with a comment explaining that a
# `pkgs/*` glob made pkg/ packages invisible — but this gate was left
# matching `pkgs/*` only, so for a pkg/-only change the corrected job could
# never be REACHED. The fix was reasoned about in the body and not applied
# to the thing that decides whether the body runs.
#
# Cost: @hanzo/ui 8.0.29 sat unpublished while npmjs served 8.0.28, so
# every consumer resolving `^8.0.17` silently got a version without the
# `./chat` entrypoint — an import error in the dependent, nothing at all in
# this repo. Add a root here whenever one is added to the loop.
- 'pkg/*/package.json'
- 'pkgs/*/package.json'
# A publish that fails for an infrastructure reason — the runner missing a
# tool, npmjs answering slowly — could only be retried by pushing another
# commit that touches a package.json, i.e. by burning a version number to
# re-run a job that was never wrong about the code. detect-changes reads what
# npmjs SERVES, so a dispatch is idempotent by construction: it publishes
# exactly the packages that are ahead of the registry, and nothing when none
# are.
workflow_dispatch:
jobs:
detect-changes:
runs-on: hanzo-build-linux-amd64
outputs:
packages: ${{ steps.changed.outputs.packages }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Detect changed packages
id: changed
run: |
CHANGED=()
# Both package roots. `@hanzo/ui` and `@hanzo/data` live under pkg/ (singular)
# and were invisible to a `pkgs/*` glob, so no version bump of either could
# ever reach npmjs from here.
for pkg_json in pkg/*/package.json pkgs/*/package.json; do
[ ! -f "$pkg_json" ] && continue
# Skip private packages
IS_PRIVATE=$(jq -r '.private // false' "$pkg_json")
[ "$IS_PRIVATE" = "true" ] && continue
# Only publish @hanzo/* packages
PKG_NAME=$(jq -r '.name' "$pkg_json")
case "$PKG_NAME" in
@hanzo/*) ;;
*) continue ;;
esac
# What npm SERVES, not what the previous commit said. HEAD~1 is the
# FIRST PARENT, so a merge whose first parent is the feature branch
# already carries the new version and the diff is empty — the publish
# is skipped and npm keeps serving the old release, silently. Asking
# the registry is independent of merge topology and of how many
# commits the runner fetched.
NEW=$(jq -r '.version' "$pkg_json")
OLD=$(curl -sf "https://registry.npmjs.org/${PKG_NAME}" \
| jq -r '."dist-tags".latest // "0.0.0"' 2>/dev/null || echo "0.0.0")
# FORWARD ONLY. `!=` fires in both directions, and several packages
# sit BEHIND npm (a release cut elsewhere, a revert) — publishing
# those walks the registry backwards. Only a strictly greater local
# version is a release.
NEWEST=$(printf '%s\n%s\n' "$OLD" "$NEW" | sort -V | tail -1)
if [ "$OLD" != "$NEW" ] && [ "$NEWEST" = "$NEW" ]; then
echo "Version change: $PKG_NAME ($OLD -> $NEW)"
# The package NAME, because every downstream step spends this as a
# `pnpm --filter` argument and a directory basename matches nothing.
# An unmatched filter exits 0, so the build silently no-ops and the
# publish ships an unbuilt tarball — how 8.0.17 and 8.0.19 shipped broken.
CHANGED+=("$PKG_NAME")
fi
done
if [ ${#CHANGED[@]} -eq 0 ]; then
echo "packages=[]" >> $GITHUB_OUTPUT
else
JSON=$(printf '%s\n' "${CHANGED[@]}" | jq -R . | jq -s -c .)
echo "packages=$JSON" >> $GITHUB_OUTPUT
fi
publish:
needs: detect-changes
if: needs.detect-changes.outputs.packages != '[]'
runs-on: hanzo-build-linux-amd64
strategy:
# One package's broken build must not withhold every other package's
# release: the matrix legs are independent publishes, not one artifact.
fail-fast: false
matrix:
package: ${{ fromJson(needs.detect-changes.outputs.packages) }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: pnpm install
- name: Build package
# The trailing `...` is load-bearing: it builds the package's WORKSPACE
# DEPENDENCIES first, in topological order.
#
# Without it, `pnpm --filter @hanzo/ui build` runs alone against siblings
# that have no `dist/`. Those siblings type themselves through
# `"types": "dist/index.d.ts"`, so tsc reports them as missing modules and
# the errors do not name the real cause:
# src/product/SiteNav.tsx: Cannot find module '@hanzo/products'
# src/core/tokens.ts: Cannot find module '@hanzo/tokens'
# src/gitops.ts: Cannot find module '@hanzo/cd'
# plus a wave of implicit-any errors in the files that imported them,
# which read like a source problem in @hanzo/ui and are not.
run: pnpm --filter ${{ matrix.package }}... build
- name: Provision bun (only for a package whose test script asks for it)
# @hanzo/canvas declares `bun test` and this runner has no bun, so its
# suite has been `spawn ENOENT` on every run — which fails the leg before
# the publish and is why canvas has sat at 0.2.2 here against 0.2.1 on
# npmjs. The step that exists to stop an untested release was itself the
# thing stopping the release.
#
# Read from the package's OWN manifest, not from a list here, so a
# package that adopts or drops bun needs no edit to this file. A package
# that does not ask for bun does not pay for it.
run: |
set -euo pipefail
PKG_DIR=$(pnpm --filter ${{ matrix.package }} exec pwd | tail -1)
if ! jq -r '.scripts.test // ""' "$PKG_DIR/package.json" | grep -qw bun; then
echo "::notice::${{ matrix.package }} does not run bun"
exit 0
fi
if command -v bun >/dev/null 2>&1; then
echo "bun already on PATH: $(bun --version)"
exit 0
fi
# From npmjs, not bun.sh/install: this job already reaches the npm
# registry (it is what the job exists to publish to) and setup-node
# already put the global bin on PATH, so this adds no new host to
# trust and no new failure mode. The shell installer would also want
# unzip, which a minimal runner image need not carry.
npm install -g bun
bun --version
- name: Test package
# A published version is IMMUTABLE, so this is the last point at which a
# defect costs nothing. Nothing else runs a package's own tests before it
# ships: ci.yml's test job runs `cd pkgs/ui && …` — that one directory —
# and this workflow went checkout → build → publish. So @hanzo/event's
# suite, which pins the version the client stamps on every event, ran in
# no workflow that gates a release, and the stale stamp it was written to
# catch could still have reached npmjs.
#
# `--if-present` so a package with no test script is skipped rather than
# failing the release: the set of testable packages is read from each
# package.json, not listed here, so a new package is covered the day it
# adds a `test` script.
#
# This step needs `test` to mean the same thing in every package — the
# package's own suite, no browser, no network. @hanzo/ui briefly chained
# its two CONSUMER suites onto it (pack a tarball, npm-install it into a
# throwaway app, drive it in chromium), which is a real gate and belongs
# exactly where hanzo.yml already runs it: on the push, in its own
# `ui-consumer` job, next to the `playwright install` that gives it a
# browser. Here there is no such step, so a chained `test` could only
# fail — the last gate before an immutable version would have been one
# that cannot pass, which is the failure mode this repo has already paid
# for twice.
run: pnpm --filter ${{ matrix.package }} run --if-present test
- name: Verify the tarball carries what package.json promises
# `types` is a PROMISE to every consumer, and tsup writes dist/*.mjs in a
# separate pass from dist/*.d.ts — so a declaration failure yields a
# package whose JS is fine and whose types are absent. That is invisible
# here and surfaces in dependents as "Could not find a declaration file".
#
# Asserted from the package's OWN manifest, so it holds for every package
# this workflow publishes and needs no per-package list.
run: |
set -euo pipefail
PKG_DIR=$(pnpm --filter ${{ matrix.package }} exec pwd | tail -1)
TYPES=$(jq -r '.types // .typings // empty' "$PKG_DIR/package.json")
if [ -z "$TYPES" ]; then
echo "::notice::${{ matrix.package }} declares no types entrypoint — nothing to verify"
exit 0
fi
if [ ! -f "$PKG_DIR/$TYPES" ]; then
echo "::error::${{ matrix.package }} declares types at $TYPES but the build produced no such file."
echo "::error::Publishing would ship a package that silently has no types. Fix the declaration build."
exit 1
fi
echo "types present: $TYPES"
- name: Fetch the npm token from KMS
id: npmtok
# publish.yml used secrets.NPM_TOKEN, which exists on NEITHER the hanzoai
# org nor this repo on git.hanzo.ai. An absent secret interpolates to the
# empty string rather than failing, so every publish reached npmjs
# unauthenticated and 401'd -- the reason @hanzo/ui has never shipped from
# CI and each release was a hand publish.
#
# KMS is where a secret lives, so the token is read at run time from the
# same org path and with the same machine identity every other workflow
# here already uses. Nothing new to rotate, and no npm credential is
# stored on the forge.
env:
KMS_CLIENT_ID: ${{ secrets.KMS_CLIENT_ID }}
KMS_CLIENT_SECRET: ${{ secrets.KMS_CLIENT_SECRET }}
KMS_ENDPOINT: ${{ vars.KMS_ENDPOINT || 'https://kms.hanzo.ai' }}
KMS_ORG: ${{ vars.KMS_ORG || 'hanzo' }}
KMS_SECRET_ENV: ${{ vars.KMS_SECRET_ENV || 'prod' }}
# Where the token actually lives, MEASURED: NPM_TOKEN sits at the org
# root, not under deploy/ (which holds CLOUDFLARE_*, GIT_TOKEN,
# KUBECONFIG, UNIVERSE_PIN_TOKEN). Empty means root; set the
# KMS_NPM_PATH variable to move it without editing this file.
KMS_NPM_PATH: ${{ vars.KMS_NPM_PATH || '' }}
run: |
token=$(curl -fsS "${KMS_ENDPOINT}/v1/kms/auth/login" \
-H 'Content-Type: application/json' \
-d "{\"clientId\":\"${KMS_CLIENT_ID}\",\"clientSecret\":\"${KMS_CLIENT_SECRET}\"}" \
| jq -r '.accessToken // empty')
if [ -z "${token}" ]; then
echo "::error::KMS login failed at ${KMS_ENDPOINT}/v1/kms/auth/login (check KMS_CLIENT_ID/KMS_CLIENT_SECRET)."
exit 1
fi
# The read is org-scoped by the TOKEN's owner claim, not by a path
# segment: the flat form is the contract cloud's embedded KMS serves,
# and it answers {name, env, value}. The older
# /v1/kms/orgs/<org>/secrets/... form with .secret.value 404s here.
secret_url="${KMS_ENDPOINT}/v1/kms/secrets/${KMS_NPM_PATH:+${KMS_NPM_PATH}/}NPM_TOKEN?env=${KMS_SECRET_ENV}"
# No -f: a 404 must report WHICH path was empty, not just exit 22.
npm_token=$(curl -sS "${secret_url}" -H "Authorization: Bearer ${token}" | jq -r '.value // empty')
if [ -z "${npm_token}" ]; then
echo "::error::No NPM_TOKEN at ${secret_url}."
echo "::error::Put it there, or set the KMS_NPM_PATH variable to the path that has it."
exit 1
fi
# Masked before it is ever an output, so it cannot surface in a log.
echo "::add-mask::${npm_token}"
echo "token=${npm_token}" >> "$GITHUB_OUTPUT"
- name: Publish package
# Idempotent on purpose: the desired state is "npmjs holds this version", and
# a version already there satisfies it. Versions are immutable, so a conflict
# means the release already happened (a re-run, or a hand publish) — that is
# success, not a failure. Every other error still fails the job.
env:
NODE_AUTH_TOKEN: ${{ steps.npmtok.outputs.token }}
run: |
set -o pipefail
OUT=$(pnpm --filter ${{ matrix.package }} publish \
--no-git-checks --access public 2>&1) || true
echo "$OUT"
case "$OUT" in
*EPUBLISHCONFLICT*|*"cannot publish over"*|*"previously published versions"*)
echo "::notice::${{ matrix.package }} is already on npmjs at this version — nothing to do" ;;
*)
# Not a conflict: confirm the version really landed, else fail loudly.
#
# RETRIED, because npmjs is read-after-write eventually consistent
# and this asked it exactly once, about two seconds after the
# write. On run 26906 both @hanzo/og@1.0.0 and @hanzo/shop@1.0.0
# failed here having printed npm's own `+ @hanzo/og@1.0.0` success
# line moments earlier, and both are on the registry now: the
# publishes worked and the check was simply too early.
#
# A false red here is worse than a slow green. It reports that a
# release did not happen when it did, so the next person bumps the
# version to "fix" it and burns a number over a replication lag —
# and the real signal, a publish that genuinely failed, becomes
# indistinguishable from the noise.
PKG_DIR=$(pnpm --filter ${{ matrix.package }} exec pwd | tail -1)
NAME=$(jq -r '.name' "$PKG_DIR/package.json")
WANT=$(jq -r '.version' "$PKG_DIR/package.json")
landed=
for attempt in 1 2 3 4 5 6; do
if npm view "$NAME@$WANT" version >/dev/null 2>&1; then landed=1; break; fi
echo "$NAME@$WANT not visible to npmjs yet (attempt $attempt/6); waiting 10s"
sleep 10
done
[ -n "$landed" ] \
|| { echo "::error::$NAME@$WANT is not on npmjs 60s after publish"; exit 1; }
echo "published $NAME@$WANT" ;;
esac
- name: Publish to the Hanzo registry
# Second home for the same tarball: api.hanzo.ai/v1/packages/hanzo/npm, our
# own registry (hanzoai/git), so an install does not have to reach npmjs.
# Same artifact, same version — this republishes what the step above just
# built, it does not rebuild or re-version.
#
# BEST-EFFORT by construction. No token ⇒ skip with a notice, so this can
# land before the credential exists and starts working the moment it does.
# An already-published version is a success, not a failure: the registry
# holding it is the desired state either way. npmjs is authoritative and is
# never affected by anything here.
env:
# REGISTRY_TOKEN is the fleet-wide name (six other workflows use it and
# the hanzoai org carries it). This asked for HANZO_REGISTRY_TOKEN, which
# is defined nowhere, so it read as empty and the step below took its
# "not set" branch and exited 0 on every run -- the mirror to
# api.hanzo.ai has never happened, and said so only as a notice.
HANZO_REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -uo pipefail
if [ -z "${HANZO_REGISTRY_TOKEN:-}" ]; then
echo "::notice::HANZO_REGISTRY_TOKEN not set — package published to npmjs only."
echo "::notice::Add it (a Hanzo Git token with package:write) to mirror to api.hanzo.ai."
exit 0
fi
REG=https://api.hanzo.ai/v1/packages/hanzo/npm/
NPMRC="$RUNNER_TEMP/.npmrc-hanzo"
{
echo "@hanzo:registry=${REG}"
echo "//api.hanzo.ai/v1/packages/hanzo/npm/:_authToken=${HANZO_REGISTRY_TOKEN}"
} > "$NPMRC"
out=$(npm_config_userconfig="$NPMRC" \
pnpm --filter ${{ matrix.package }} publish \
--no-git-checks --access public --registry "$REG" 2>&1) || true
echo "$out"
case "$out" in
*EPUBLISHCONFLICT*|*"cannot publish over"*|*"already exists"*)
echo "::notice::already in the Hanzo registry at this version — nothing to do" ;;
*) echo "$out" | grep -qiE 'error|failed' \
&& echo "::warning::mirror to api.hanzo.ai failed (npmjs publish unaffected)" \
|| echo "mirrored to the Hanzo registry" ;;
esac
-91
View File
@@ -1,91 +0,0 @@
name: Validate Registries
on:
pull_request:
paths:
- "apps/v4/public/r/registries.json"
- "apps/v4/registry/directory.json"
push:
branches:
- main
paths:
- "apps/v4/public/r/registries.json"
- "apps/v4/registry/directory.json"
jobs:
validate:
runs-on: hanzo-build-linux-amd64
name: pnpm validate:registries
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: Block reserved registry namespaces
env:
RESERVED_NAMESPACES: "@shadcn,@ui,@blocks,@components,@block,@component,@util,@utils,@registry,@lib,@hook,@hooks,@theme,@themes,@chart,@charts"
run: |
node <<'EOF'
const fs = require("node:fs")
const files = [
"apps/v4/public/r/registries.json",
"apps/v4/registry/directory.json",
]
const reservedNamespaces = new Set(
process.env.RESERVED_NAMESPACES.split(",").filter(Boolean)
)
function readNames(filePath) {
return JSON.parse(fs.readFileSync(filePath, "utf8")).map(
(entry) => entry.name
)
}
const violations = files.flatMap((filePath) => {
return readNames(filePath)
.filter((name) => reservedNamespaces.has(name))
.map((name) => `${filePath}: ${name}`)
})
if (violations.length > 0) {
console.error("Reserved registry namespaces are not allowed:")
for (const violation of violations) {
console.error(`- ${violation}`)
}
process.exit(1)
}
EOF
- uses: pnpm/action-setup@v4
name: Install pnpm
id: pnpm-install
with:
version: 9.0.6
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Validate registries
run: pnpm --filter=v4 validate:registries
-2
View File
@@ -1,3 +1 @@
auto-install-peers=true
link-workspace-packages=true
puppeteer_skip_download=true
+1 -1
View File
@@ -1 +1 @@
v20.5.1
v18
+1 -9
View File
@@ -3,12 +3,4 @@ node_modules
.next
build
.contentlayer
**/fixtures
deprecated
apps/v4/registry/styles/**/*.css
# Byte-coupled: hz.js carries the marked region of src/anon.js VERBATIM, and
# pkgs/event/src/anon.test.ts compares them byte for byte. Reformatting either
# one alone breaks the build; the same region is also vendored by hanzoai/cloud.
pkgs/event/src/anon.js
pkgs/event/hz.js
apps/www/pages/api/registry.json
+2 -22
View File
@@ -1,24 +1,4 @@
{
"typescript.tsdk": "node_modules/.pnpm/typescript@5.3.3/node_modules/typescript/lib",
"editor.gotoLocation.multipleDefinitions": "goto",
"typescript.preferences.quoteStyle": "single",
"eslint.workingDirectories": [
{ "pattern": "apps/*/" },
{ "pattern": "pkgs/*/" },
{ "pattern": "pkgs/*/" }
],
"tailwindCSS.classFunctions": ["cva", "cn"],
"vitest.debugExclude": [
"<node_internals>/**",
"**/node_modules/**",
"**/fixtures/**"
],
"files.exclude": {
"deprecated": true
},
"search.exclude": {
"apps/v4/registry/radix-*": true,
"apps/v4/public/r/*": true,
"pkgs/shadcn/test/fixtures/*": true
}
}
"editor.gotoLocation.multipleDefinitions": "goto"
}
-129
View File
@@ -1,129 +0,0 @@
# Changelog
All notable changes to the Hanzo UI component library will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Slot support for Badge component with `asChild` prop for composition flexibility
- Modern focus-visible states for Badge component with ring styling
- SVG icon support in Badge component with automatic sizing
- Aria-invalid states for Badge form validation
- Data-slot attributes for better styling hooks
### Changed
- **THEME**: Restored electric blue primary color (`oklch(0.653 0.269 252.44)`) as default
- **THEME**: Updated to use shadcn/ui's neutral gray color scheme for backgrounds and text
- **THEME**: Maintained configurable theme system via theme generator
- Badge component now uses `<span>` instead of `<div>` for better semantics
- Badge font weight changed from `font-semibold` to `font-medium`
- Badge padding adjusted from `px-2.5` to `px-2`
- Badge hover states now only apply within anchor tags (`[a&]:hover`)
- Updated both default (rounded-full) and new-york (rounded-md) badge variants
- **Button component updated to shadcn/ui v4 patterns**:
- Changed focus ring from `ring-2` to `ring-[3px]` with 50% opacity
- Added SVG icon support with `[&_svg]:size-4` default sizing
- Added `has-[>svg]:px-*` conditional padding when button contains icons
- Added new icon size variants: `icon-sm` (size-8) and `icon-lg` (size-10)
- Changed from `forwardRef` to regular function component
- Added `data-slot="button"` attribute
- Improved accessibility with `aria-invalid` states
- Enhanced dark mode support for destructive, outline, and ghost variants
- **Input component updated to shadcn/ui v4 patterns**:
- Changed height from `h-10` to `h-9` (consistent with Button)
- Changed from `forwardRef` to regular function component
- Added `data-slot="input"` attribute
- Changed background to transparent with `dark:bg-input/30` dark mode variant
- Added responsive text sizing: `text-base md:text-sm`
- Added `min-w-0` to prevent flex overflow issues
- Enhanced file input styling with `file:inline-flex file:h-7`
- Added selection highlighting: `selection:bg-primary selection:text-primary-foreground`
- Changed focus ring from `ring-2` to `ring-[3px]` with 50% opacity
- Added `aria-invalid` states for form validation
- Changed transition to `transition-[color,box-shadow]` for smoother animations
### Fixed
- Badge component styling now matches shadcn/ui v4 implementation
- Improved accessibility with proper focus-visible states
- Better form integration with aria-invalid support
- **Security**: Fixed 31 of 32 vulnerabilities (96.9% reduction):
- Upgraded happy-dom from 19.0.2 to 20.0.10 (fixes 2 critical RCE vulnerabilities)
- Upgraded vite from 5.4.20 to 7.1.12 (fixes moderate vulnerability)
- Applied 21 package overrides for next, axios, undici, prismjs, and other dependencies
- All critical (5), all but 1 high (3/4), all moderate (19), and all low (5) vulnerabilities resolved
- Remaining: 1 high severity in lodash.template (no patch available, abandoned package)
### Removed
- Removed hanzo-compatibility documentation page (redundant with main docs)
## [5.0.0] - 2025-10-18
### Added
- Next.js 16.0.0 with Turbopack support
- React 19.2.0 support
- 150+ components in registry
- AI-specific components (ai-chat, ai-playground, ai-assistant, ai-vision, etc.)
- 3D components (3d-button, 3d-card, 3d-carousel, 3d-grid, etc.)
- Animation components (animated-beam, animated-text, animated-cursor, etc.)
- Page builder with drag-drop functionality
- White-label system for Zoo UI and Lux UI forks
- External registry support (35+ sources)
- 24+ full-page block templates
### Changed
- Migrated from Contentlayer to Fumadocs for MDX processing
- Updated documentation structure
- Improved build process with registry-first approach
### Technical
- Tailwind CSS v4.1.14
- TypeScript 5.9.3
- pnpm 9.0.6+ workspace support
- Turborepo build system
---
## Version Comparison
**Hanzo UI vs shadcn/ui:**
- **161 components** vs shadcn's 58 components (3x more)
- **Unique to Hanzo UI**: AI components (12), 3D components (9), Animation components (13), specialized navigation variants (15+)
- **100% compatible** with shadcn/ui CLI and registry system
- **Multi-framework support**: React (100%), Vue (~90%), Svelte (~85%), React Native (~70%)
## Component Status
- **Implemented**: ~127 fully functional components
- **Stub Components**: ~34 need implementation
- **Blocks**: 24+ viewport-sized templates
- **AI Components**: 12 specialized AI/ML components
- **3D Components**: 9 three-dimensional UI elements
- **Animation Components**: 13 motion and transition components
## Migration Notes
### From shadcn/ui
Hanzo UI is a drop-in replacement for shadcn/ui with additional components:
```bash
# Instead of
npx shadcn-ui@latest add button
# Use
npx @hanzo/ui@latest add button
```
All shadcn/ui components work identically in Hanzo UI, plus you get access to 100+ additional components.
## Links
- **Website**: https://ui.hanzo.ai
- **Documentation**: https://ui.hanzo.ai/docs
- **GitHub**: https://github.com/hanzoai/ui
- **npm Package**: https://www.npmjs.com/package/@hanzo/ui
## License
MIT License - see [LICENSE](LICENSE) for details
-190
View File
@@ -1,190 +0,0 @@
# Hanzo UI Consolidation — one library, all polish, no duplication
**Goal.** RIP the fragmentation (`@hanzo/data` vs `@hanzo/ui` vs in-console duplicates) into ONE canonical, cross-platform, presentational, clean-room library: **`@hanzo/ui`, built on `@hanzo/gui`**. Preserve every polished component + token; zero loss; no fork.
**Status.**
- **Step 1 — Audit:** complete (this document; four source trees read file-by-file).
- **Step 2 — Establish `@hanzo/ui` + migrate the stable foundation:** **DONE & GREEN** (`pkg/ui` is now a real package; `tsc --noEmit` = 0 errors, `vitest` = 12/12).
- **Step 3 — Console re-point:** **STAGED, not merged.** Gate is split (backend live, app lanes in-flight) — see [Step 3](#step-3--console-re-point-staged).
**One decision needs CTO sign-off before publish** — see [Naming / version](#naming--version--the-one-decision).
---
## Architecture — three layers, decomplected
```
@hanzo/gui (Tamagui/One) cross-platform primitives: Text, XStack/YStack, Button, Input,
│ Card, Popover, Select, Switch, Slider, Spinner, ScrollView …
@hanzo/data (pkg/data) the metadata-driven RECORD layer: fields → records → views
│ (RecordsView, DataTable, BoardView, RecordDetail, field editors)
@hanzo/ui (pkg/ui) ◄──── THE ONE LIBRARY. Two orthogonal concerns, one package:
• product/app layer → import { … } from '@hanzo/ui'
• record layer → import { … } from '@hanzo/ui/data' (re-exports @hanzo/data)
```
`@hanzo/data` stays the **source of truth** for the record layer; `@hanzo/ui` **composes** it (`export * from '@hanzo/data'` on the `./data` subpath) rather than copying — so the CMS/ERP/Help app lanes that consume `@hanzo/data` today keep working untouched, and there is exactly one home. This mirrors how the gui base wraps `hanzogui`.
Both layers own a `DataTable` (product = generic typed `<T>` list; data = field-driven record grid). Keeping the record layer on the `./data` subpath keeps each name unambiguous — no rename, no collision.
---
## The preserve-and-merge manifest
Legend: **✅ migrated** (in `pkg/ui`, GREEN) · **↪ re-exported** (composed from `@hanzo/data`) · **⏳ staged** (Step 3, after app lanes land) · **🔧 pending prop-injection** (app-coupled; decouple before it can live in a presentational lib) · **🏠 stays in console** (app glue, not a UI component).
### A. Record layer — `@hanzo/data` → `@hanzo/ui/data` ↪
Source: `pkg/data/src/*` (v1.2.0, clean-room, already published). Surfaced through `@hanzo/ui/data` and `@hanzo/ui/primitives/bases/data`. Nothing re-implemented.
| Component / module | Source | New home |
|---|---|---|
| `RecordsView` (flagship: toolbar over table⇆board, filter/sort/search/group, saved views) | `view/RecordsView.tsx`, `view/Toolbar.tsx` | `@hanzo/ui/data` |
| `DataTable` (field-driven record grid: sort headers, drag resize/reorder, row select, inline edit, pagination) | `table/DataTable.tsx` | `@hanzo/ui/data` |
| `BoardView` (kanban, optimistic drag-move) · `RecordCard` | `board/BoardView.tsx`, `table/RecordCard.tsx` | `@hanzo/ui/data` |
| `RecordDetail` / `RecordForm` | `record/RecordDetail.tsx` | `@hanzo/ui/data` |
| Field model + registry (`FieldType` ×24, `FieldDefinition`, `registerField`, routers) | `field/{types,registry,registerDefaults,FieldDisplay,FieldInput}` | `@hanzo/ui/data` |
| Field displays + **editors** (select/currency/date/relation/file/JSON/boolean/fullName/address…) | `field/{displays,inputs}.tsx` | `@hanzo/ui/data` |
| Composable primitives: `Menu`, `CheckBox`, `Toggle`, `Calendar` | `primitives/*` | `@hanzo/ui/data` |
| Pure logic: `applyView`, `sortRecords`, `filterRecords`, `searchRecords`, `paginate`, `boardColumns`, … | `{table,board,view}/logic.ts` | `@hanzo/ui/data` (+ `@hanzo/data/*/logic` subpaths for Node hosts) |
| Tokens: `tokens`, `TAG_TONES`, `tagTone` | `theme.ts` | **also top-level `@hanzo/ui`** (token surface) |
### B. Console `components/ui/*` product primitives → `@hanzo/ui/product` ✅ / 🔧
Source: `console2/src/components/ui/*`. The pure subset was ported **byte-identical** (verified by `diff`) — 100% of the polish preserved (Charts' hover tooltips/axis-ticks/honest-null, DataTable width/flex/hover-gating, Field's 6 variants, SlideOver's focus-trap + scroll-lock, ComboBox's ReDoS-safe filter).
| Component | Source (`console2/src/components/ui/`) | New home | Status |
|---|---|---|---|
| `DataTable<T>` (generic typed list) + `Column` | `DataTable.tsx` | `@hanzo/ui` (`product/DataTable`) | ✅ |
| `PageHeader` | `PageHeader.tsx` | `@hanzo/ui` | ✅ |
| `Field*` (`FieldRow/Text/TextArea/Switch/Select/Slider`) | `Field.tsx` | `@hanzo/ui` | ✅ |
| **Charts** (`Sparkline/LineChart/BarChart/Donut/BarRows`, `CHART_PALETTE`) | `Charts.tsx` | `@hanzo/ui` | ✅ |
| `Donut` (standalone dependency-free ring) | `Donut.tsx` | `@hanzo/ui` as **`DonutRing`** (alias — Charts.Donut is canonical) | ✅ |
| `StatusTag` (health verdict → pill) | `StatusTag.tsx` | `@hanzo/ui` | ✅ |
| `EmptyState` (DO/Vercel-class first-run) | `EmptyState.tsx` | `@hanzo/ui` | ✅ (**decoupled** — see §I) |
| `Metric*` (`MetricCard/MiniBars/UtilBar/LegendDot/Panel/HintButton`, `SERIES`) | `Metric.tsx` | `@hanzo/ui` (its `Sparkline` preserved as **`MetricSparkline`**) | ✅ |
| `ComboBox` + pure `filterOptions`/`isKnownOption` | `ComboBox.tsx`, `combobox/filter.ts` | `@hanzo/ui` | ✅ (+ `filter.test.ts`) |
| `PrimaryButton` · `HanzoMark` · `ProductIcon` · `ProviderLogo` | resp. files | `@hanzo/ui` | ✅ |
| `SlideOver` (a11y drawer) · `SelectMenu` · `Toast` (`useToast`) | resp. files | `@hanzo/ui` | ✅ (SlideOver import decoupled `~/…/color``./color`) |
| `Reorder<T>` (pointer DnD) · `FadeIn` · `ThemeToggle` · `color` (`asColor`/`IconLike`) | resp. files | `@hanzo/ui` | ✅ (+ `Reorder.test.ts`) |
| `DetailPane` (right-side pane over SlideOver) | `components/DetailPane.tsx` | `@hanzo/ui` | ⏳ (liftable; lands with Step 3 — depends only on SlideOver) |
| `ChunkGuard` (stale-deploy chunk-error reload) | `components/ChunkGuard.tsx` | `@hanzo/ui` | ⏳ (fully portable; lands with Step 3) |
### C. Design tokens + motion → `@hanzo/ui` ✅
The brief's premise (`globals.css` `t_dark`/`t_light` token blocks) is **corrected**: `t_dark`/`t_light` are `@hanzo/gui` (Tamagui) theme **class names**; the calm values live in three real places, all preserved:
| Polish | Source | New home |
|---|---|---|
| Calm dark-first tokens (surface, text, accent `#60a5fa`, tag tones) — raw hex, theme-config independent | `pkg/data/src/theme.ts` | `@hanzo/ui` (top-level `tokens`/`TAG_TONES`/`tagTone`) |
| OKLCH shadcn-compatible palette (soft-charcoal `oklch(0.145 0 0)`, `--radius: 0.5rem`, indigo sidebar accent) | `pkgs/ui/style/hanzo-default-colors.css`, `@hanzo/tokens` | unchanged (legacy shadcn line); the gui line uses the hex tokens above |
| **Motion vocabulary**`fade-up` (FadeIn), `collapse`/`slide`/`fade` (shell/drawer), `drag`, `skeleton`/`pulse`/`row-in`; all `prefers-reduced-motion`-guarded | `console2/app/globals.css` (motion section) | **`@hanzo/ui/styles/motion.css`** (verbatim) — `import '@hanzo/ui/styles/motion.css'` once at app root |
### D. Generic DocType renderer → `@hanzo/ui` ⏳ (Step 3, FINAL — active lane)
Source: `console2/src/components/doctype/*`. **Actively edited by the CMS/ERP/Help lanes (untracked).** Dependency-**injected** (`client: FrameworkClient` prop) — not `~/lib/api`-coupled — so the real move-blockers are `~/lib/framework/{types,fields}` + the `ui/*` primitives (now in `@hanzo/ui`) + `@hanzo/data` (now `@hanzo/ui/data`).
| File | Class | Step-3 disposition |
|---|---|---|
| `MediaGrid.tsx` | presentational | → `@hanzo/ui` (cleanest lift; props-only DAM gallery) |
| `DocTypeRecords.tsx` / `DocTypeDetail.tsx` | mixed (data-bound shell over `@hanzo/data` views) | shell **stays in console**; renders `@hanzo/ui/data` views (already true) |
| `CollectionsBrowser.tsx` | mixed (card grid + install/create orchestration) | presentational card-grid → `@hanzo/ui`; orchestration stays |
| `data.ts` | glue (relation loading) | stays in console |
### E. LivingOverview → `@hanzo/ui` ⏳ (Step 3 — active lane)
Source: `console2/src/components/products/overview/living/*` (untracked/modified). Pure/presentational parts portable; app-glue concentrated in `registry.ts` (live API clients).
| Part | Class | Step-3 disposition |
|---|---|---|
| `motion.ts`, `hooks.ts`, `logic.ts`, `config.ts` (types) | pure | → `@hanzo/ui` (unit-tested; count-up, poll clock, unit formatting) |
| `tiles.tsx`, `LivingOverview.tsx` | presentational (reuse `ui/Charts` verbatim) | → `@hanzo/ui` (once `config.ts`'s one `~/lib/products/registry` `ProductIcon` type is swapped for `@hanzo/ui`'s `IconLike`) |
| `adapters.ts`, `registry.ts` | glue (map REAL `/v1` sources → `OverviewData`) | **stay in console** |
### F. Framework / Base-data libs → stay in console 🏠
`src/lib/framework/*` (DocType wire contract + `FrameworkApi` client + pure `fields.ts` mapper to `@hanzo/data`) and `src/lib/base-data/*` (Base REST client + schema→field mapper) are **app data-access glue**, not UI. They stay in the console. Their pure mappers (`docTypeToFields`, `baseCollectionToFields`) could later publish as a small `@hanzo/base-data` adapter, but they are not UI components and are out of scope for `@hanzo/ui`.
### G. App-coupled console components → 🔧 pending prop-injection (NOT yet in the lib)
These import app `~/lib`/session/router/branding and must be made presentational (inject props) before they belong in a host-agnostic lib. Kept in console for now; the injection contract is specified so the eventual lift is mechanical.
| Component | Coupling | Inject to lift |
|---|---|---|
| `BackendStateCard` (`BackendState.tsx`) | `~/lib/api` `ApiError` | accept a numeric `status` (or shared error shape) instead of `ApiError` |
| `BrandLogo` | session + live IAM `organization()` + `~/config` + branding | `orgName`, `logoUrl` (or a `loadOrgLogo` loader), brand mark/name as props |
| `Breadcrumbs` | `next/navigation` + catalog lookups | `crumbs[]` (or `pathname` + resolver) + `onNavigate` |
| `Loader` / `BrandMark` | `~/config` + brand logo pkgs | `brand`/`brandName` + the animated-SVG getter |
| `States` (`ErrorState`, `OperatorAccessRequired`) | `~/lib/api` + session + branding | the error, the account identity, brand strings |
---
## What this pass built (Step 2)
New in **`pkg/ui`** (was a bare `src/` staging dir — no package):
- `package.json``@hanzo/ui` `8.0.0`, source-published (`files: ["src"]`, exports map to `.ts`), peers `@hanzo/gui >=7.2.2`, `@hanzo/data >=1.2.0`, `react >=19`; MIT (repo `LICENSE.md`). Exports: `.`, `./product`, `./data`, `./primitives/bases/data`, `./styles/motion.css`.
- `src/index.ts` — top-level barrel (product layer + tokens).
- `src/product/index.ts` — product barrel; **collision-free** (Charts `Sparkline`/`Donut` canonical; Metric's → `MetricSparkline`; standalone ring → `DonutRing`; `ComboOption` from the one filter module) — **zero loss**.
- `src/data.ts``export * from '@hanzo/data'` (the record layer, one home).
- `src/styles/hanzo-motion.css` — the motion vocabulary, verbatim.
- `tsconfig.json`, `vitest.config.ts`, `gui.config.ts`, `gui.d.ts`, `.npmignore`, `README.md` — mirror the proven `pkg/data` setup.
- **One decouple fix:** `product/EmptyState.tsx` no longer imports `~/lib/products/registry`; it uses the local `IconLike` type — the last host coupling in the product tree is gone.
**Green:** `tsc --noEmit` = 0 errors (strict); `vitest run` = 12/12 (`combobox/filter`, `Reorder`). Every product component is host-agnostic (imports only `react`, `@hanzo/gui`, `@hanzogui/*`, `@hanzo/data`, relative).
## Clean-room guarantee (preserved)
`pkg/data` audited for GPL/Twenty contamination: **none.** No GPL, no copied license headers/SPDX, no Twenty entity/decorator architecture. The field/record model is an independent `FieldType` union + `FieldDefinition` + runtime `Map` registry (records are plain `Record<string, unknown>`). "Twenty" appears **only as benchmark prose** ("Airtable/Twenty-class polish, clean-room"). License: MIT (repo `LICENSE.md`). `@hanzo/ui` inherits the same posture.
---
## Naming / version — DECIDED: `@hanzo/ui@8`, shadcn retired to `@hanzo/ui-shadcn`
**Decision:** the gui-based unified library takes the `@hanzo/ui` name **forward at `8.0.0`** (aligning with the "Hanzo Cloud 8.x" umbrella; major = breaking re-platform). The legacy shadcn/Radix line (`pkgs/ui`, v5.7.0) is **retired by renaming** to `@hanzo/ui-shadcn` (never hard-deleted) — it stays fully alive under the new name, freeing `@hanzo/ui` for v8. Precedent: `pkg/data@1.2.0` already superseded `pkgs/data@1.1.0` under the same name.
This is **DONE (repo-local):** `pkg/ui/package.json` is `@hanzo/ui@8.0.0`, GREEN. The rest is a coordinated, **sequenced** retire — because publishing `@hanzo/ui@8` (a different, gui-based API with no shadcn `Button/Card/Dialog`) under the name ~20 repos consume for shadcn primitives will BREAK any consumer that resolves to `@8`. So order matters:
**Blast radius (measured across `~/work/hanzo`).** Declared deps on `@hanzo/ui` in ~20 repos. Most pin `^5.x` (semver-safe from an `@8` bump): paas, chat, platform, app, hanzo.ai, o11y, docs, mdx, hanzobot, ui-repo `pkgs/checkout` `^5.3`, `pkgs/agent-ui` `^5.0`. **Would break on `@8`:** `hanzoai/identity/app` (`"latest"`), ui-repo `pkgs/commerce` (`>=5.0.0`); `app/` uses `workspace:^`. The `ui.hanzo.ai` docs app + `pkgs/{commerce,checkout,agent-ui}` import the shadcn line internally.
**Publish reality.** `.github/workflows/publish-on-tag.yml` publishes **`pkgs/ui`** (the shadcn line) as `@hanzo/ui` on a `v*` tag — it does not reference `pkg/ui`. So a tag push today publishes shadcn, not v8. Publishing v8 needs the workflow rewired to build/publish `pkg/ui`. `npm` is not authed locally (`npm whoami` empty) — per house rules, publish goes through **CI (self-hosted runners, canonical org `NPM_TOKEN`)**, not a local `npm publish`.
**Safe sequence to fully land v8 (each step reversible until the tag push):**
1.`pkg/ui` = `@hanzo/ui@8.0.0`, GREEN (done, committed to a branch).
2. Rename `pkgs/ui` name → `@hanzo/ui-shadcn`; update the internal ui-repo consumers (`app/`, `pkgs/{commerce,checkout,agent-ui}`) + `check-no-hanzogui`/registry scripts that reference it.
3. Migrate external consumers off `@hanzo/ui``@hanzo/ui-shadcn` (start with the break-risk ones: `identity` `latest`, `commerce` `>=5`; the `^5` pins are safe to migrate at leisure). ~20 repos — do as a tracked sweep or flag per-repo.
4. Rewire `publish-on-tag.yml` (and `release.yml`) to build + publish `pkg/ui` as `@hanzo/ui@8`, and `pkgs/ui-shadcn` as `@hanzo/ui-shadcn`.
5. `npm deprecate '@hanzo/ui@<8' 'moved to @hanzo/ui-shadcn; @hanzo/ui@8+ is the @hanzo/gui-based unified lib'`.
6. Tag `v8.0.0` → CI publishes `@hanzo/ui@8.0.0`. Verify `npm view @hanzo/ui@8.0.0`.
**Gated on the user's direct go-ahead** (irreversible / globally-visible / ecosystem-wide): steps 26 — the `pkgs/ui` rename, the ~20-repo consumer sweep, the CI rewire, `npm deprecate`, and the `v8.0.0` tag push that triggers publish. These were not executed autonomously.
---
## Step 3 — console re-point (STAGED)
**Gate (per the brief):** cloud `/v1/framework/modules` live **AND** console CMS/ERP/Help native.
- ✅ Backend: `/v1/framework/modules[/:module[/install]]` is **implemented + wired** (`cloud/clients/framework/framework.go:71-73`, HIP-0106 order 129, bound in `subsystems.go:130`, real tests).
- ⏳ Console: `CmsModule.tsx`, `ErpModule.tsx`, `HelpModule.tsx`, `components/doctype/` are **untracked/in-flight** on `feat/console-native-cms`.
→ Gate is **split**, so the re-point is **staged, not applied.** I deliberately did **not** touch `console2`'s working tree (it holds the lanes' uncommitted work — editing it would collide and risk loss, the opposite of the goal).
**Ready-to-apply re-point (mechanical, once the lanes land + merge):**
1. `console2` deps: keep `@hanzo/gui`, `@hanzo/data`; add `@hanzo/ui` (`^6.0.0`). (`@hanzo/data` may stay as a direct dep or be dropped in favor of `@hanzo/ui/data` — both resolve to the same source.)
2. Re-point imports (delete the now-duplicated in-console copies — extract, don't copy):
- `~/components/ui/{DataTable,PageHeader,Field,Charts,Donut,StatusTag,EmptyState,Metric,ComboBox,combobox/filter,PrimaryButton,HanzoMark,ProductIcon,ProviderLogo,SlideOver,SelectMenu,Toast,Reorder,FadeIn,ThemeToggle,color}`**`@hanzo/ui`** (or `@hanzo/ui/product`). Two spot-renames: Metric's `Sparkline``MetricSparkline`, standalone `Donut``DonutRing`.
- `@hanzo/data` (in `doctype/*`, `base-data/*`, `products/*Module`) → **`@hanzo/ui/data`** (identical surface).
- `~/components/{DetailPane,ChunkGuard}`**`@hanzo/ui`**.
- `app/globals.css` motion block → `import '@hanzo/ui/styles/motion.css'` (keep base resets local).
- Move the 5 app-coupled components (§G) into the lib only after applying their inject-props contract; until then they stay local.
3. Verify **identical render**: `tsc --noEmit` + `vitest` + `next build` green, then headless-Playwright the live pages pixel-same (the polish is byte-identical, so parity is expected).
**Report:** `@hanzo/ui` is the ONE unified library — product layer + record layer (`@hanzo/data`) + charts + calm tokens + motion, all on `@hanzo/gui`, presentational, cross-platform, clean-room, GREEN. Console re-point is **ready to apply once the CMS/ERP/Help lanes land**; it was not merged to avoid colliding with that in-flight work.
## Follow-ups (semver-minor, with visual e2e)
- Collapse the preserved Sparkline/Donut variants to one each (`MetricSparkline``Sparkline`, `DonutRing``Donut`) once call-sites are proven identical.
- Lift §G's five components after applying their prop-injection contracts.
- Consider publishing the pure `framework`/`base-data` mappers as a small `@hanzo/base-data` adapter (not UI).
+29 -49
View File
@@ -12,9 +12,7 @@ This repository is a monorepo.
- We use [pnpm](https://pnpm.io) and [`workspaces`](https://pnpm.io/workspaces) for development.
- We use [Turborepo](https://turbo.build/repo) as our build system.
- Releases are semver-driven: bump a package's `version` in its `package.json` and
merge to `main`. CI (`.github/workflows/publish.yml`) detects the version change
and publishes that package to npm. One step, one source of truth.
- We use [changesets](https://github.com/changesets/changesets) for managing releases.
## Structure
@@ -22,25 +20,28 @@ This repository is structured as follows:
```
apps
└── v4
└── www
├── app
├── components
├── content
└── registry
── new-york-v4
── default
│ ├── example
│ └── ui
└── new-york
├── example
└── ui
packages
└── shadcn
└── cli
```
| Path | Description |
| -------------------- | ---------------------------------------- |
| `apps/v4/app` | The Next.js application for the website. |
| `apps/v4/components` | The React components for the website. |
| `apps/v4/content` | The content for the website. |
| `apps/v4/registry` | The registry for the components. |
| `packages/shadcn` | The `shadcn` package. |
| Path | Description |
| --------------------- | ---------------------------------------- |
| `apps/www/app` | The Next.js application for the website. |
| `apps/www/components` | The React components for the website. |
| `apps/www/content` | The content for the website. |
| `apps/www/registry` | The registry for the components. |
| `packages/cli` | The `shadcn-ui` package. |
## Development
@@ -81,58 +82,37 @@ You can use the `pnpm --filter=[WORKSPACE]` command to start the development pro
1. To run the `ui.shadcn.com` website:
```bash
pnpm --filter=v4 dev
pnpm --filter=www dev
```
2. To run the `shadcn` package:
2. To run the `shadcn-ui` package:
```bash
pnpm --filter=shadcn dev
pnpm --filter=shadcn-ui dev
```
## Running the CLI Locally
To run the CLI locally, you can follow the workflow:
1. Start by running the dev server:
```bash
pnpm dev
```
2. In another terminal tab, test the CLI by running:
```bash
pnpm shadcn
```
To test the CLI in a specific app, use a command like:
```bash
pnpm shadcn <init | add | ...> -c ~/Desktop/my-app
```
This workflow ensures that you are running the most recent version of the registry and testing the CLI properly in your local environment.
## Documentation
The documentation for this project is located in the `v4` workspace. You can run the documentation locally by running the following command:
The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:
```bash
pnpm --filter=v4 dev
pnpm --filter=www dev
```
Documentation is written using [MDX](https://mdxjs.com). You can find the documentation files in the `apps/v4/content/docs` directory.
Documentation is written using [MDX](https://mdxjs.com). You can find the documentation files in the `apps/www/content/docs` directory.
## Components
We use a registry system for developing components. You can find the source code for the components under `apps/v4/registry`. The components are organized by styles.
We use a registry system for developing components. You can find the source code for the components under `apps/www/registry`. The components are organized by styles.
```bash
apps
└── v4
└── www
└── registry
── new-york-v4
── default
│ ├── example
│ └── ui
└── new-york
├── example
└── ui
```
@@ -141,7 +121,7 @@ When adding or modifying components, please ensure that:
1. You make the changes for every style.
2. You update the documentation.
3. You run `pnpm registry:build` to update the registry.
3. You run `pnpm build:registry` to update the registry.
## Commit Convention
@@ -180,9 +160,9 @@ If you have a request for a new component, please open a discussion on GitHub. W
## CLI
The `shadcn` package is a CLI for adding components to your project. You can find the documentation for the CLI [here](https://ui.shadcn.com/docs/cli).
The `shadcn-ui` package is a CLI for adding components to your project. You can find the documentation for the CLI [here](https://ui.shadcn.com/docs/cli).
Any changes to the CLI should be made in the `packages/shadcn` directory. If you can, it would be great if you could add tests for your changes.
Any changes to the CLI should be made in the `packages/cli` directory. If you can, it would be great if you could add tests for your changes.
## Testing
-106
View File
@@ -1,106 +0,0 @@
# Hanzo Design System — Canonical Tokens
`@hanzo/ui` is the single source of truth for the shared Hanzo product look across
the **Tailwind** apps (hanzo.chat, hanzo.app, hanzo console, commerce, hanzo-desktop).
Change a value here; apps converge on it. This file is that source of truth for the
three things that must read as **one product**: typography, the sidebar/panel system,
and the dark-black palette.
> One library: **`@hanzo/ui@8`** (`pkg/ui`, on **`@hanzo/gui`**) IS the component
> library — the cross-platform product/record layer every surface consumes.
> **`@hanzo/ui-shadcn`** (`pkgs/ui`) is the legacy shadcn/Tailwind/Radix kit, kept
> only for existing v5 consumers (pin `@hanzo/ui-shadcn@^5`; no new adoptions).
> This file stays the source of truth for the *token values* (fonts, dark palette,
> the sidebar glyph) both render.
---
## 1. Typography — Basel Grotesk + Geist Mono
| Role | Family | Notes |
|------|--------|-------|
| UI / body / display / heading (`sans`) | **Basel Grotesk** | Self-hosted. Book = weight **400**, Medium = weight **500**. |
| code / data / mono (`mono`) | **Geist Mono** | `next/font/google` (`Geist_Mono`) or the geist CDN. |
| Arabic / Hebrew (`--font-ar` / `--font-he`) | unchanged | i18n only — keep. |
**Dropped as defaults:** Geist Sans, DM Sans, Figtree, Inter, PT Sans, Roboto Mono.
Basel is a **licensed, non-Google** face — **self-host** the woff2/woff, do NOT use
`next/font/google` for it. Canonical files (mirror lux.exchange):
`Basel-Grotesk-Book.woff2/.woff` (400), `Basel-Grotesk-Medium.woff2/.woff` (500).
`@font-face` (weights 400/500, `font-display: swap`, `font-style: normal`):
```css
@font-face {
font-family: 'Basel';
font-style: normal;
font-weight: 400; /* Book; 500 = Medium */
font-display: swap;
src: url('.../Basel-Grotesk-Book.woff2') format('woff2'),
url('.../Basel-Grotesk-Book.woff') format('woff');
}
```
Per-app adoption (converge the value, keep each app's own mechanism):
- **@hanzo/ui / Next apps** → `next/font/local` for Basel (`--font-basel-sans`) +
`next/font/google` `Geist_Mono` (`--font-geist-mono`). See `app/lib/fonts.ts`;
tailwind `sans → var(--font-basel-sans)`, `mono → var(--font-geist-mono)`.
- **Vite + Tailwind apps** (chat, launcher, desktop) → self-host Basel `@font-face`
+ geist-mono CDN import; tailwind `fontFamily.sans = ['Basel', …]`,
`mono = ['Geist Mono', …]`.
- **Tamagui (console)** → Basel `@font-face` in globals + override the Tamagui
`body`/`heading` font `family` to Basel; Geist Mono for `code`/`pre`.
---
## 2. Sidebar toggle icon — lucide `PanelLeft`
One glyph everywhere: lucide **`PanelLeft`** (the shadcn `SidebarTrigger` default).
Where a directional open/close affordance is wanted, use the pair
**`PanelLeftClose`** (expanded) / **`PanelLeft`** (collapsed). Never a hamburger,
a magnifier, a directional arrow, or a bespoke panel SVG for the sidebar toggle.
- Icon: stroke-2, ~1620px, `currentColor`.
- Button: ghost/outline, square (`size-7`/`h-6 w-6`), subtle hover.
---
## 3. Sidebar + panels
| Spec | Value |
|------|-------|
| Sidebar width (expanded) | **16rem / 256px** (`SIDEBAR_WIDTH`) |
| Sidebar width (collapsed / icon rail) | **3rem / 48px** (apps vary 4870px) |
| Sidebar / panel surface | resting **#0a0a0a** over the true-black page |
| Border / separation | `border-border` — subtle white-alpha ~10% in dark (`border-r` / `border-l`) |
| Item hover | subtle `white/5` |
| Item active | monochrome `white/10`**no colored accent** (the house style is monochrome) |
| Right panel rail | `border-l border-border`, same surface, collapsible |
Prefer the `@hanzo/ui` `Sidebar` primitive (`pkgs/ui/primitives/sidebar.tsx`,
`SidebarTrigger``PanelLeft`) where the app can consume it; otherwise match these
classes/tokens.
---
## 4. Dark-black palette (true-black OLED)
The house dark theme is a **true-black** canvas (matches hanzo.ai marketing +
hanzo.chat OLED), with a shallow surface-depth ladder for cards/panels and quiet
hairline borders — never harsh pure-white on pure-black.
| Token | Value | Use |
|-------|-------|-----|
| Page background | **#000000** (`oklch(0 0 0)`) | body / canvas |
| Surface / sidebar / panel (resting) | **#0a0a0a** | sidebars, panels, cards |
| Press | **#050505** | pressed surface |
| Elevated / hover | **#171717** | hover, raised card |
| Border / divider | **rgba(255,255,255,0.10)** (≈ `#171717` opaque on black) | hairlines |
| Foreground (primary text) | near-white **#ededf1** (`oklch(0.985)`) — not pure `#fff` | body text |
| Muted / secondary text | `white/70` (≈ `#a1a1aa`) | secondary |
Keep each app's theme-token engine (CSS vars / Tailwind tokens / Tamagui `$color*`);
converge the **values/usage** to the table above, don't rip the engine.
Reference: `pkgs/ui/style/hanzo-default-colors.css` (`.dark` / `.hanzo-ui-dark-theme`).
-97
View File
@@ -1,97 +0,0 @@
# ui.hanzo.ai — the @hanzo/ui docs + component registry, served by the house
# static server (ghcr.io/hanzoai/static, a Go binary), same as every other Hanzo
# static site. Built on our own runners, never on a laptop and never by a
# third-party builder.
#
# The app is a Next.js static export: `pnpm build` in app/ writes app/out, which
# is the entire site — docs, the registry JSON the CLI reads, and the static
# /api/registry/*.json index.
FROM node:22 AS builder
WORKDIR /src
ENV NEXT_TELEMETRY_DISABLED=1
# Publishable ingest key (pk-…), baked in at build because a static export has no
# server to read config at runtime. Write-only and HMAC-verified to one org, so it
# is safe in a public bundle; the deployment that builds decides which org the
# site reports as.
#
# ONE name, end to end: KMS holds `deploy/PUBLISHABLE_KEY`, hanzo.yml declares it
# as this image's build_secret, and the KMS name IS the build-arg name. NEXT_PUBLIC_
# is added HERE because that prefix is what makes Next inline it — the app reads
# process.env.NEXT_PUBLIC_PUBLISHABLE_KEY.
#
# Do NOT re-declare `ARG NEXT_PUBLIC_PUBLISHABLE_KEY` after the ENV below. A later
# ARG of the same name shadows the ENV with its own (empty) default, and the build
# stays green while the bundle ships blank — which is exactly how hanzo.chat 1.0.58
# shipped a keyless site from a fully green run.
#
# Fail CLOSED, on BOTH ways this goes wrong.
#
# empty — builds, serves and looks correct while cloud answers
# `401 ingest_key_required` for every anonymous pageview. The previous
# `ARG …=""` default made that the normal outcome of an unattended
# build, which is why no automated lane could ever publish a working
# image.
# `pk_…` — the OLDER key format. v5.7.6 shipped one, passed by hand on a local
# `docker build`, and it is now dead: api.hanzo.ai 401s it on both the
# fetch and beacon transports. A hand-passed key goes stale in silence,
# so requiring the current `pk-` shape refuses the stale one outright.
#
# Neither failure is visible from outside the artifact, so refuse the artifact.
ARG PUBLISHABLE_KEY
ENV NEXT_PUBLIC_PUBLISHABLE_KEY=$PUBLISHABLE_KEY
RUN case "$PUBLISHABLE_KEY" in \
pk-*) : ;; \
'') echo "PUBLISHABLE_KEY is empty - pass --build-arg PUBLISHABLE_KEY=<pk-...> (KMS deploy/PUBLISHABLE_KEY, env prod)" >&2; exit 1 ;; \
*) echo "PUBLISHABLE_KEY is not a publishable key (expected a pk- prefix)" >&2; exit 1 ;; \
esac
# 300+ prerendered pages; the default heap is not enough.
ENV NODE_OPTIONS=--max-old-space-size=8192
RUN corepack enable
COPY . .
# The lockfile is committed, so the build resolves exactly what was reviewed.
RUN pnpm install --frozen-lockfile
# Workspace packages the app imports must be built first: their package.json
# exports point at dist/. Only @hanzo/event qualifies -- .npmrc sets
# link-workspace-packages=true and the lockfile resolves it to link:../pkgs/event.
#
# There used to be a `cd pkgs/ui` line here. pkgs/ui was @hanzo/ui-shadcn, deleted
# in 5dbdb2943 when shadcn was consolidated to one home, and that commit did not
# touch this file -- so every build since has run `cd` into a directory that does
# not exist and died with exit code 2 before compiling anything. The app takes
# @hanzo/ui from the registry now (npm:@hanzo/ui-shadcn@^5), not the workspace.
RUN cd pkgs/event && pnpm build
# Builds the component registry, then the site (app/package.json build script).
#
# ...and then PROVES the key reached the client bundle. The gate above proves a
# key was PASSED; only this proves it was INLINED. Those are different failures:
# a rename on either side of `process.env.NEXT_PUBLIC_PUBLISHABLE_KEY` leaves the
# build-arg intact and the bundle keyless, and a static export cannot report that
# at runtime because there is no runtime.
#
# `&&`, never `;` — a `;` chain returns the LAST command's status, so a failed
# build followed by a passing grep exits 0 and the image is published.
RUN cd app && pnpm build && \
if [ -z "${NEXT_PUBLIC_PUBLISHABLE_KEY}" ]; then \
echo "ERROR: NEXT_PUBLIC_PUBLISHABLE_KEY is empty after a successful build." >&2; exit 1; \
elif grep -rqF "${NEXT_PUBLIC_PUBLISHABLE_KEY}" out; then \
echo "Build OK - ingest key inlined into app/out, verified"; \
else \
echo "ERROR: key supplied but NOT present in app/out - ui would ship unattributed" >&2; exit 1; \
fi
# 0.5.1 serves a directory's index.html in place. On 0.4.1 every page 301'd to
# an explicit /index.html, which leaks that filename into the address bar and
# into the URLs Next builds for its route prefetches.
FROM ghcr.io/hanzoai/static:0.5.2-amd64
COPY --from=builder /src/app/out /public
EXPOSE 3000
# No -spa: the export writes a real index.html per route (trailingSlash), so a
# missing path must 404 rather than silently render the home page.
ENTRYPOINT ["/static", "-port", "3000", "-root", "/public"]
-458
View File
@@ -1,458 +0,0 @@
# UI/UX Improvements - Implementation Summary
**Date**: 2025-11-13
**Status**: Phase 1 Complete ✅
## 🎯 Overview
Following a comprehensive UI/UX review using Playwright automated testing, we've identified and begun implementing **12 high-impact improvements** to elevate Hanzo UI to world-class standards. This document tracks what's been completed and what remains.
---
## ✅ Completed Implementations
### 1. Skip Links for Accessibility (CRITICAL) ✅
**File**: `app/components/skip-links.tsx`
**What it does**:
- Provides keyboard navigation shortcuts for screen reader users
- Meets WCAG Level A compliance requirements
- Hidden by default, visible when focused (Tab key)
**Implementation**:
```tsx
<SkipLinks />
// Renders:
// - Skip to main content
// - Skip to navigation
```
**Impact**:
- ✅ WCAG Level A compliant
- ✅ Screen reader accessible
- ✅ Keyboard navigation improved
- ✅ Zero visual impact (only shows on focus)
**Integrated in**: `app/layout.tsx` - Active globally across all pages
---
### 2. Skeleton Loading Components ✅
**File**: `app/components/skeleton-card.tsx`
**What it provides**:
- `<SkeletonCard />` - Single card skeleton
- `<SkeletonCardGrid />` - Grid of skeleton cards
- `<SkeletonList />` - List view skeleton
- `<SkeletonTable />` - Table skeleton
**Usage Example**:
```tsx
import { Suspense } from 'react'
import { SkeletonCardGrid } from '@/components/skeleton-card'
<Suspense fallback={<SkeletonCardGrid count={6} />}>
<ComponentGallery />
</Suspense>
```
**Impact**:
- ⏩ Reduces perceived load time
- 🎨 Professional loading states
- ♿ Better accessibility (users know content is loading)
- 📱 Works on all devices
**Next Step**: Integrate into:
- `/blocks` page (for block gallery)
- `/docs/components` page (for component list)
- `/builder` page (for block library panel)
---
### 3. Enhanced Global Layout ✅
**File**: `app/app/layout.tsx`
**Changes**:
- ✅ Skip links added at the top of the component tree
- ✅ Proper import order maintained
- ✅ Zero breaking changes to existing functionality
**Before/After**:
```tsx
// Before
<ActiveThemeProvider>
<div vaul-drawer-wrapper="">
{children}
</div>
</ActiveThemeProvider>
// After
<ActiveThemeProvider>
<SkipLinks />
<div vaul-drawer-wrapper="">
{children}
</div>
</ActiveThemeProvider>
```
---
## 📋 Ready to Integrate (Created, Not Yet Used)
### Components Available for Use
1. **Skeleton Components** (`app/components/skeleton-card.tsx`)
- Ready to wrap async content
- Just import and use in Suspense boundaries
2. **Skip Links** (`app/components/skip-links.tsx`)
- Already active globally
- Need to add `id="main-content"` to main content areas
---
## 🚧 Next Phase - Ready for Implementation
### High Priority (Week 1-2)
#### 1. Add ID Attributes for Skip Links
**Files to modify**:
- `app/(app)/layout.tsx` - Add `id="main-content"` to main
- `app/components/site-header.tsx` - Add `id="navigation"` to nav
```tsx
// In layout
<main id="main-content" className="flex-1">
{children}
</main>
// In header
<nav id="navigation">
<MainNav />
</nav>
```
**Effort**: 5 minutes
**Impact**: High - Completes skip links functionality
---
#### 2. Integrate Skeleton Loaders
**Target Pages**:
**A. Blocks Page** (`app/(app)/blocks/page.tsx`)
```tsx
import { Suspense } from 'react'
import { SkeletonCardGrid } from '@/components/skeleton-card'
export default function BlocksPage() {
return (
<Suspense fallback={<SkeletonCardGrid count={9} />}>
<BlockGallery />
</Suspense>
)
}
```
**B. Components Page** (`app/(app)/docs/components/page.tsx`)
```tsx
<Suspense fallback={<SkeletonList count={10} />}>
<ComponentList />
</Suspense>
```
**C. Builder Page** (`app/(app)/builder/page.tsx`)
```tsx
// In the component library panel
<Suspense fallback={<SkeletonCardGrid count={6} />}>
<BlockLibrary />
</Suspense>
```
**Effort**: 30 minutes
**Impact**: High - Dramatically improves perceived performance
---
#### 3. Performance Optimizations (CRITICAL)
**Current**: 14.7s load time
**Target**: <3s load time
**Action Items**:
a. **Image Optimization**
```bash
# Convert images to WebP
pnpm add sharp
# Add next.config.js optimization
images: {
formats: ['image/webp', 'image/avif'],
}
```
b. **Code Splitting**
```tsx
// Dynamic imports for heavy components
const HeavyComponent = dynamic(() => import('./heavy-component'), {
loading: () => <SkeletonCard />,
})
```
c. **Lazy Loading**
```tsx
// Below-the-fold content
import { lazy } from 'react'
const Footer = lazy(() => import('./footer'))
```
**Effort**: 4-6 hours
**Impact**: CRITICAL - 79% faster page loads
---
### Medium Priority (Week 2-3)
#### 4. Toast Notification System
**Package**: Already installed (`sonner`)
**File**: `app/registry/default/ui/sonner.tsx` (already exists!)
**Usage**:
```tsx
import { toast } from 'sonner'
// Success
toast.success('Component copied!')
// Error
toast.error('Failed to load', {
action: {
label: 'Retry',
onClick: () => retry(),
},
})
```
**Effort**: 1 hour (just replace alert() calls)
**Impact**: Medium - Better UX for notifications
---
#### 5. Command Palette (⌘K)
**Component**: Use existing `app/components/command-menu.tsx`
**Enhancement**: Add keyboard shortcut hint
```tsx
<div className="flex items-center gap-2">
<Search className="h-4 w-4" />
<span>Search</span>
<kbd className="ml-auto">K</kbd>
</div>
```
**Effort**: 2 hours
**Impact**: Medium - Power user feature
---
#### 6. Enhanced Hero Section
**File**: `app/(app)/page.tsx`
**Improvements**:
- ✨ Animated gradient background
- 🎯 More prominent CTAs
- 💫 Subtle animations (fade-in-up)
- 🏆 Trust indicators
**Code Available**: See `docs/UX_ENHANCEMENT_PLAN.md` Section 4
**Effort**: 3-4 hours
**Impact**: High - First impression matters
---
### Lower Priority (Week 4+)
7. **Builder Undo/Redo** (8 hours)
8. **Keyboard Shortcuts Panel** (4 hours)
9. **Empty State Components** (3 hours)
10. **Micro-interactions** (4 hours)
11. **Component Card Enhancements** (6 hours)
12. **Drag Indicators** (4 hours)
---
## 📊 Impact Metrics
### Current Status
| Metric | Before | After Phase 1 | Target | Progress |
|--------|--------|---------------|--------|----------|
| Accessibility | Good | WCAG A ✅ | WCAG AAA | 33% |
| Loading UX | None | Skeletons ✅ | Complete | 50% |
| Performance | 14.7s | TBD | <3s | 0% |
| Mobile UX | Good | Good ✅ | Excellent | 80% |
### After Full Implementation
| Metric | Target | Improvement |
|--------|--------|-------------|
| Page Load | <3s | -11.7s (79% faster) |
| Accessibility | WCAG AAA | +2 levels |
| User Engagement | +50% | Better UX |
| Bounce Rate | -20% | Faster loads |
---
## 🧪 Testing
### Automated Tests Available
Run comprehensive UX review anytime:
```bash
pnpm exec playwright test tests/ux-review.spec.ts --headed
```
**What it tests**:
- ✅ Skip links presence (now passing!)
- ✅ Mobile navigation
- ✅ Dark/light mode
- ✅ Accessibility features
- ⏱️ Performance metrics
- 📸 Visual regression (screenshots)
### Manual Testing Checklist
- [ ] Tab through page - skip links should appear
- [ ] Press Tab on any page → "Skip to main content" appears
- [ ] Click skip link → jumps to main content
- [ ] Skeleton loaders appear while content loads
- [ ] No layout shift when content replaces skeletons
---
## 📚 Documentation Created
1. **UX Enhancement Plan**: `docs/UX_ENHANCEMENT_PLAN.md`
- Complete roadmap with code examples
- 12 improvements prioritized
- Success metrics defined
2. **UX Review Summary**: `tests/reports/UX_REVIEW_SUMMARY.md`
- Detailed findings from Playwright tests
- Current vs target performance
- Visual analysis results
3. **Test Suite**: `tests/ux-review.spec.ts`
- 13 comprehensive tests
- Reusable for ongoing monitoring
- Screenshots for visual tracking
4. **This Document**: `IMPROVEMENTS_IMPLEMENTED.md`
- Track implementation progress
- Integration guides
- Priority roadmap
---
## 🚀 Quick Wins (Do These First!)
### 5-Minute Tasks
1. ✅ Skip links - **DONE**
2. Add ID attributes (`id="main-content"`, `id="navigation"`)
3. Import skeletons where needed
### 30-Minute Tasks
4. Wrap async content in Suspense with skeleton fallbacks
5. Replace alert() with toast notifications
6. Add ⌘K hint to search
### 1-Hour Tasks
7. Hero section CTA improvements
8. Add loading="lazy" to below-fold images
---
## 💡 Tips for Implementation
### Using Skeleton Loaders
```tsx
// Pattern 1: Simple async component
<Suspense fallback={<SkeletonCard />}>
<AsyncComponent />
</Suspense>
// Pattern 2: Grid of items
<Suspense fallback={<SkeletonCardGrid count={6} />}>
<ItemGrid />
</Suspense>
// Pattern 3: With error boundary
<ErrorBoundary fallback={<ErrorState />}>
<Suspense fallback={<SkeletonList />}>
<DataList />
</Suspense>
</ErrorBoundary>
```
### Performance Best Practices
```tsx
// 1. Dynamic imports for heavy components
const Chart = dynamic(() => import('./chart'), {
ssr: false,
loading: () => <SkeletonCard />,
})
// 2. Image optimization
import Image from 'next/image'
<Image
src="/image.jpg"
alt="Description"
width={800}
height={600}
loading="lazy"
placeholder="blur"
/>
// 3. Code splitting
const ModalContent = lazy(() => import('./modal-content'))
```
---
## 🎯 Next Steps
**Immediate (This Week)**:
1. Add ID attributes to complete skip links
2. Integrate skeletons on blocks page
3. Test accessibility with screen reader
**Short-term (Next 2 Weeks)**:
4. Performance optimization sprint
5. Hero section redesign
6. Toast notifications integration
**Long-term (Month 2)**:
7. Builder enhancements (undo/redo, drag indicators)
8. Keyboard shortcuts
9. Polish and micro-interactions
---
## 📞 Support
**Questions?** See the detailed plans:
- `docs/UX_ENHANCEMENT_PLAN.md` - Full implementation guide
- `tests/reports/UX_REVIEW_SUMMARY.md` - Test findings
- `tests/ux-review.spec.ts` - Test suite source
**Running Tests**:
```bash
# Full UX review
pnpm test:e2e tests/ux-review.spec.ts
# Just accessibility tests
pnpm test:e2e tests/ux-review.spec.ts -g "Accessibility"
# With UI (see what's happening)
pnpm test:e2e tests/ux-review.spec.ts --headed
```
---
*Last updated: 2025-11-13*
*Phase: 1 of 4 Complete*
*Next review: After Week 1 integrations*
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 hanzo
Copyright (c) 2023 Lux Partners Limited
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
-580
View File
@@ -1,580 +0,0 @@
# @hanzo/ui — LLM context
**What this is.** The React component library for AI applications: 161+
components, 24+ blocks, two themes, and a single typed import surface, all on ONE
substrate (`@hanzo/gui`) so the same import runs on web, native and desktop.
Published as `@hanzo/ui` (v8) on npm. Docs at https://ui.hanzo.ai. Dev port: 3003.
**Canonical role.** This is the canonical impl repo for Hanzo's web UI kit —
frontend components, not an SDK. It sits alongside the two SDK lines (full cloud
SDK generated from OpenAPI in `hanzo-<lang>/sdk` + wrapper in `hanzoai/<lang>-sdk`;
AI/agents lib `hanzo` in `hanzoai/python-sdk` flagship, `@hanzo/ai` in `hanzo-js/ai`).
`@hanzo/event` (telemetry, `POST /v1/event`) lives here in `pkgs/event`. DRY: one
impl, one place — link out, never duplicate.
**Brand rules (hard).**
- Never call Hanzo an "LLM gateway" or position it against LiteLLM — it is a full
AI SDK / AI cloud, not a proxy. Purge that framing on sight.
- Paths are `/v1/…` only — never an `/api/` prefix.
- Zen models are our own family — never name upstream models.
- Voice: "Hanzo — the Open AI Cloud." Developer-first, crisp, no emoji-spam.
**Install / run.**
```bash
pnpm add @hanzo/ui # consume
# dev:
pnpm install && pnpm build:registry && pnpm dev # registry MUST build before app
```
**Key entry points.** `pkg/ui/` (core lib + v8 subpaths: /product /data /canvas
/dashboard /usage /gitops) · `app/registry/{default,new-york}/`
(component SOURCE OF TRUTH) · `pkgs/*` (auto-published `@hanzo/*` packages) ·
`packages/shadcn/` (CLI) · `app/content/docs/` (MDX docs). Publish = bump a
package `version` + merge to main (`.github/workflows/publish.yml`).
**Spec / more context.** Canonical SDK + docs model: `~/work/hanzo/SDK-ARCHITECTURE.md`.
Detailed engineering notes (build order, import surface, telemetry, upstream sync,
gotchas) follow below.
---
## v8 — the canonical `@hanzo/ui` (`pkg/ui`)
`@hanzo/ui@8` (`pkg/ui`) is THE Hanzo component library, and there is ONE
substrate: every component renders through `@hanzo/gui` (Tamagui) primitives on
the `@hanzo/tokens` scale, so one import works on web, native (expo) and desktop
(Tauri). The Radix + Tailwind surface it used to ship alongside is gone — it
lives on as its own package, `@hanzo/shadcn`, and `@hanzo/ui` no longer depends
on it, on any `@radix-ui/*` package, on cva, cmdk or sonner.
`@hanzo/ui-shadcn` (`pkgs/ui`, v5.x) is the legacy standalone package —
superseded, being retired.
### Layout
```
pkg/ui/src/
core/ design core: cn.ts (clsx+tailwind-merge),
tokens.ts (re-export of @hanzo/tokens), fonts.ts (Geist vars)
root.tsx <Hanzo> — the root. Carries the gui config AND the stylesheet.
gallery.tsx EVERY component, once, in every variant. The one list.
theme.css SELF-CONTAINED token CSS vars + Geist Sans/Mono — the identity
backends/gui/ THE component surface on @hanzo/gui. index.ts is its manifest.
product/ the product/app layer (charts, PageHeader, ComboBox, …)
models/ the unified ModelSelector + catalog helpers
primitives/ GENERATED per-member entrypoints (scripts/gen-primitives.mjs)
index.ts root barrel = the component surface + cn
```
### Out of the box — the package carries its own config and its own CSS
```tsx
import { Hanzo, Button } from '@hanzo/ui'
<Hanzo><Button>Ship</Button></Hanzo>
```
That is the entire setup. No `gui.config.ts`, no CSS import, no generator script.
Three things used to be each app's job:
1. **The stylesheet.** gui compiles a style prop to an atomic class the first
time something RENDERS it, so the sheet does not exist until a render has
happened — which is why every app ran a `gen-gui-css.mjs` of its own. hanzo.app
never did: it shipped 103 `_bg-` classes and 26 `_dsp-` classes against a
stylesheet containing ZERO of either, every gui-styled element unstyled in
production, green build throughout. The render happens at OUR publish time now
(`scripts/gen-css.mjs` renders `src/gallery.tsx` in both themes and writes
`dist/styles.css` — 381 KB, 35 KB gzipped, 340 atomic selectors), and
`<Hanzo>` imports it. Styles gui generates at RUNTIME for props we could not
know at publish time still reach the document through `insertStyleRules`;
the shipped sheet is what makes the FIRST paint and every SSR/static render
correct.
2. **The config.** `<Hanzo>` passes `config` from `gui-config.ts` to
`GuiProvider` — as a VALUE, never a bare `import './gui-config'`. Vite 8
(rolldown) ignores package.json `sideEffects` ARRAYS outright: with any array
the registration is dropped and the first render dies on "Missing hanzogui
config"; only `sideEffects: true` keeps it, and that costs +63% bundle
(404 KB → 661 KB measured). Correctness does not live in bundler metadata.
3. **The theme.** gui throws `Missing theme.` for any component with no root
theme context, so a root is structurally required — there is no version of
this with no root at all. Forgetting `<Hanzo>` is therefore a hard crash on
first paint, never a silently unstyled page.
`theme.css` is dark-first at `:root` (it used to claim dark-first while shipping
LIGHT at `:root`, so an app that mounted the dark default and read `--background`
got white). `.light` retunes, and both answer to gui's own `.t_light`/`.t_dark`
that `<Hanzo>` stamps on the body — one theme, named the same by the CSS custom
properties and the component tokens.
### Three tests, one list of components
`src/gallery.tsx` is the specification of "what this package has to style", and
all three layers render THAT — a second copy of the list is how a component gets
styled by one and missed by another.
| Layer | Command | What it catches |
|---|---|---|
| `src/styles.test.tsx` | `pnpm test:unit` | every atomic class the gallery renders vs every class `dist/styles.css` defines a rule for. Not "the intersection is large" — TOTAL. Catches a stale sheet. |
| `src/backends/gui/render.test.tsx` | `pnpm test:unit` | the surface mounts under the real provider; a component that throws on first paint fails. |
| `test/consumer.spec.ts` | `pnpm test:consumer` | packs the tarball, installs it into a temp app OUTSIDE the repo (never a workspace link — that hides `files`/`exports`/`workspace:*` defects), builds, serves, and asserts COMPUTED styles + screenshots at 390 and 1280 in both themes. |
The consumer spec also fails on a solid-white border (the `@hanzo/design`
`border-card: var(--white…)` defect — borders are low-alpha hairlines) and on any
element that has a text child and a zero-height box.
### House rules for a component
- Style through gui props and theme tokens (`$background`, `$color12`,
`$borderColor`) — never a utility class string, never a hard-coded font.
- Touch targets meet the 44px floor via `hitSlop`, never via padding.
- Behaviour (focus, portalling, keyboard, a11y) comes from the matching
`@hanzogui/*` primitive; nothing reimplements it.
- Free-form text children go through `ink()`; `data-slot` markers through
`slot()`. One helper each, one place.
- Module scope stays side-effect free — `forwardRef`/`createContext` calls carry
`/* @__PURE__ */` and nothing assigns `displayName`, so importing one symbol
never drags a neighbour in. A one-symbol import bundles ~4.7KB against ~29KB
for the whole barrel.
- Utility classes carry `hz-`. This sheet used to claim `.row`, `.skeleton`,
`.fade`, `.mono`, `.drag` and `.tnum` at the document level, in a package an
app imports once at its root; an app with its own `.row` got no warning, it got
whichever rule the cascade preferred. The unprefixed selectors survive as
aliases on the same rules for one minor version and are **REMOVED IN 8.1.0**.
Nothing here emits them — `styles.test.tsx` scans every `className=` literal in
`src/` and fails on an unprefixed one. `glass`/`elevation-N` are the one family
still bare: they are an API VALUE (`glass(3).className`), not a typed literal,
so they move on their own change.
- A prop must actually arrive. gui is not the DOM, and two measured cases prove
it in both directions: **`name` is gui's OWN prop** (it names a styled component
and a theme) and is consumed before it reaches the element, so a `name` on a
field type-checks and renders nothing; and gui **drops `secureTextEntry` on
web**, which is why masking needs BOTH spellings via `masked()` in
`backends/gui/mask.ts` and why `<Input type="password">` rendered passwords in
PLAIN TEXT until 8.0.61 (the wrapper destructured `type` out and never
forwarded it, next to an eye offering to reveal what was already visible).
Render it and read the markup before you believe a prop works.
### Subpaths
| Subpath | What |
|---|---|
| `@hanzo/ui` | the component API: Button, Badge, Card*, Checkbox, Dialog*, DropdownMenu*, Input, Toaster, Avatar*, Tabs*, Select*, Tooltip*, Popover*, Command*, Collapsible*, Resizable*, ScrollArea, Slider, Switch, Progress, Separator, Label, Textarea, AspectRatio — + `cn` (the product layer is kept off root, at `/product`) |
| `@hanzo/ui/components` | alias of the root surface, for hosts that shim the package through a `declare module` |
| `@hanzo/ui/product` | the product/app layer: charts, metrics, PageHeader, StatusTag, EmptyState, ComboBox, SlideOver, Toast, Reorder, Field |
| `@hanzo/ui/models` | ModelSelector + fetchModelCatalog + catalog helpers |
| `@hanzo/ui/core` · `/tokens` | cn, Geist font vars, the @hanzo/tokens color/theme/radii/spacing scale |
| `@hanzo/ui/theme.css` | the design tokens alone (custom properties + Geist + touch/elevation) |
| `@hanzo/ui/styles.css` | the COMPLETE sheet — tokens + motion + the generated gui atomic/theme CSS. `<Hanzo>` imports it, so an app never has to |
| `@hanzo/ui/gallery` | every component, once — what the generator, the unit test and the consumer test all render |
| `@hanzo/ui/primitives/<Member>` | per-member entrypoints (for hosts that modularize `@hanzo/ui` imports) |
| `@hanzo/ui/data` | `@hanzo/data`: RecordsView, DataTable, typed field editors |
| `@hanzo/ui/{canvas,dashboard,usage,gitops}` | the optional-peer kits (each re-exports its home package) |
| `@hanzo/ui/product/*` · `/primitives/*` | deep imports — one module without its barrel |
| `@hanzo/ui/product/pure` | the product layer's RULES with none of the layer (below) |
| `@hanzo/ui/product/theme-toggle-next` | the `@hanzogui/next-theme` binding, off the barrel on purpose (below) |
| `@hanzo/ui/css` | `substitute()` — resolve a `var()` chain, for tests jsdom cannot answer (below) |
Everything ships COMPILED from `dist` — every `exports` target is a real file in
the tarball, including `theme.css` and all 90 `primitives/*` entrypoints.
`src/dist.test.ts` asserts that against the built output, wildcards included; a
subpath pointing at a file tsc never emitted is invisible from source.
### Three doors that exist because the barrel is not one
`@hanzo/ui/product` mounts the whole gui runtime to give you one component, and
for three kinds of caller that is not a cost — it is a wall.
**`@hanzo/ui/product/pure` — the rules, without the layer.** `pages()`,
`masked()`, `displayName()`, `tone()`, `orgScope`, `filterOptions`,
`resolveBrand` and the wordmark geometry. Every module it re-exports imports
NOTHING (`src/dist.test.ts` asserts the closure is empty) and is on
`postbuild.mjs`'s `DATA` list, so none is stamped `'use client'` — a stamped
module is a client REFERENCE on React's server layer, and calling `pages()`
through one in a server component throws instead of paging. It loads under a
bare `require()` with no transform and no DOM; the test proves it in a child
node process rather than under vitest, which has vite's transform already
installed and would prove nothing.
The components import these same modules, so there is one definition and not a
testable copy of a shipped one.
**`@hanzo/ui/product/theme-toggle-next` — Next, quarantined.**
`@hanzogui/next-theme`'s provider imports `next/script`, so the product barrel's
one `export { ThemeToggleNext }` line put Next in the graph of every Vite,
Express and Tauri host — the hosts this layer promises to run on. A barrel
re-export is a static edge no bundler can split. It is off the barrel; `<ThemeToggle />`
with no props still reaches it by dynamic import and degrades when next-theme is
absent, and `dist.test.ts` asserts BOTH — no static edge, and the dynamic one
still there, because a test that only asserted the absence would pass on a
deleted feature. `next` is now an OPTIONAL peer here: next-theme requires it and
nothing in this package admitted that.
**`@hanzo/ui/css``substitute(value, vars?)`.** jsdom does not resolve
`var()`; it hands a test the text verbatim. The theme rungs are
`var(--border, rgb(255 255 255 / .10))` on purpose (follow the live cascade
where design's sheet is mounted, keep the audited literal where it is not), so
every consumer trying to assert a border's contrast compared a colour to a
function call. With no `vars` map the answer is exact rather than approximate:
jsdom mounts no design sheet, so the fallback IS what a browser computes. It is
NOT part of `@hanzo/ui/core` — that subpath is ESM-only because @hanzo/design
publishes no `require` condition, and a jest consumer is the caller that needs
this. Importing nothing is what lets it ship both formats.
### One DropdownMenu
There is one `DropdownMenu`, with one API. It is the compound surface (Trigger,
Content, Item, CheckboxItem, RadioItem, Label, Separator, Shortcut, Group,
Portal, Sub*, RadioGroup) AND it accepts the declarative `trigger` + `items`
spec, which it renders through those very same parts. `@hanzo/ui` and
`@hanzo/ui/product` export the same component; there is no second shape.
### modularizeImports support
`scripts/gen-primitives.mjs` reads the gui backend barrel and emits one
`src/primitives/<Member>.tsx` per exported value (re-export from the backend).
This makes `@hanzo/ui/primitives/Button` etc. resolve, so a host whose
`next.config` rewrites `@hanzo/ui``@hanzo/ui/primitives/{{member}}` works
unchanged. Re-run `pnpm gen:primitives` after changing the surface.
### Build — plain `tsc`, one file in, one file out
There is no bundler. `pnpm build` is two `tsc` passes, `scripts/postbuild.mjs`,
then `scripts/gen-css.mjs` (which renders the gallery through vite's SSR pipeline
to harvest `config.getCSS()` into `dist/styles.css` — the slow step, ~2 min):
| Pass | Config | Emits |
|---|---|---|
| ESM + types | `tsconfig.build.json` | `dist/**/*.js`, `.d.ts`, `.js.map`, `.d.ts.map` |
| CJS | `tsconfig.cjs.json` (`--noCheck`) | `dist-cjs/**/*.js`, folded into `dist/**/*.cjs` |
`postbuild.mjs` does the only two things `tsc` will not: it resolves every
relative specifier to a fully-specified path (`./button``./button.js`,
`./x``./x/index.js`; `.cjs` on the CJS half) so Node ESM and strict bundlers
resolve, and it prepends `'use client'` to every emitted module — the whole
library is client-side @hanzo/gui UI and Next's flight-client loader wants the
directive first. It is prepended without a newline so source-map lines hold.
**`@hanzo/data` runs this same script**, with its package root and its own data
modules as arguments (`node ../ui/scripts/postbuild.mjs . theme,table/logic,…`).
It has the same two formats and the same two problems, and a second copy of the
file would be a second place to get the barrel rule wrong. That package used to
point its `exports` at `src/index.ts` and ship raw TSX — every consumer had to
transpile it, and `require('@hanzo/data')` could not load at all, which is one of
the two edges that broke `require('@hanzo/ui')`. It emits `dist/` now, both
formats, from 1.2.2.
The output is UNBUNDLED and mirrors `src/` one-for-one, so a consumer importing
one symbol pulls one module. A bundler here would be actively harmful: tsup's
code splitting emitted 11 shared `chunk-*.js`, and importing `Button` alone
dragged in `chunk-RCMDRI6V.js` (48K of source). Measured with esbuild, `import
{ Button } from '@hanzo/ui'` costs 4717 bytes bundled from tsup output vs 2021
from `tsc` output. Dropping `rollup-plugin-dts` (tsup's `dts` worker) is also
what makes the package build on TypeScript 7, which it cannot do otherwise.
Everything under `src/` is emitted, so every `exports` subpath resolves by
construction — no hand-maintained entry list to drift.
NOTE: `pkg/ui` (singular) sits OUTSIDE the `pkgs/*` pnpm workspace, installs
standalone, and publishes via the maintainer flow, not `publish.yml`. Because the
optional-peer kits (canvas/dashboard/gitops/usage) are not on the public
registry, a standalone install must skip auto-installing peers. `.npmrc` and
`pnpm-lock.yaml` are gitignored here, so create the `.npmrc` once:
```bash
cd pkg/ui
printf 'auto-install-peers=false\nstrict-peer-dependencies=false\n' > .npmrc
pnpm install --ignore-workspace # component-surface deps (radix, cmdk, sonner, …) + @hanzo/tokens
pnpm gen:primitives # refresh the per-member entrypoints
pnpm typecheck:ui # scoped typecheck of the component surface (green)
```
`@hanzo/tokens` (`pkgs/tokens`) must be built first (`pnpm --filter @hanzo/tokens build`)
so the `file:` link resolves. The scoped `typecheck:ui` excludes the optional-peer
subpaths, whose homes aren't installed standalone.
The **kits** = canvas, wallet, network, billing, dashboard, usage, gitops, data.
Add one by mirroring `src/gitops.ts` (a one-line `export *`) + a `./name` export
+ an optional peer/devDep. `pkg/*` is a pnpm workspace member (for `workspace:*`
dev links), but `pkg/ui` publishes via the maintainer flow, not `publish.yml`
(which auto-publishes only `pkgs/*` on a version bump — see PUBLISH_GUIDE.md). The
shared shell lives here too: `AppHeader` + `BrandMark` (@hanzo/logo) +
`OrgSwitcher` + `orgScope` (the console org-scope contract, hoisted per #36). Lux
surfaces use `@luxfi/web3` for wallet/login; `@hanzo/ui/wallet`+`/network` are the
Hanzo-branded equivalents.
## Repository Structure
```
ui/
app/ Hanzo documentation site (Next.js 15.3.1, React 19)
registry/ Component registry (SOURCE OF TRUTH)
default/ui/ 150+ components
default/example/ Usage demos
default/blocks/ 24+ full-page sections
new-york/ Alternative theme
content/docs/ MDX documentation
scripts/ Build scripts
apps/
v4/ Upstream shadcn v4 docs/registry app (port 4000)
packages/
shadcn/ shadcn CLI v4.1.0 (font system, chart colors, scaffold)
tests/ Integration tests for shadcn CLI
og/ OG image generation (Hanzo-only)
pkg/
ui/ Core library (npm)
react/ React primitives
brand/ Branding system
commerce/ E-commerce components
checkout/ Checkout flow
shop/ Shop components
agent-ui/ AI agent UI components
tokens/ Design tokens
skills/
shadcn/ AI skill definitions for shadcn CLI
templates/ Project templates (next, vite, astro, react-router, start + monorepo variants)
template/next/ Hanzo-customized Next.js template
```
## Critical: Build Order
**Registry MUST build before app.** The registry generates JSON that the CLI reads.
```bash
pnpm build:registry # MUST run first
pnpm build # Then build app
```
## Commands
```bash
pnpm dev # Dev server (:3003)
pnpm build:registry # Build component registry
pnpm build # Build app
pnpm lint # Lint all workspaces
pnpm typecheck # Type checking
pnpm test # Unit tests
pnpm test:e2e # Playwright E2E
```
## How this ships
One way, and it runs on our own stack:
push -> github.com/hanzoai/ui (a mirror)
.github/workflows/sync.yml carries refs onward
-> git.hanzo.ai/hanzoai/ui CANONICAL
.hanzo/workflows/ci.yml lint, typecheck, build, test
.hanzo/workflows/registries.yml the v4 registry check
.hanzo/workflows/publish.yml publishes every pkgs/* package
.hanzo/workflows/deploy.yml builds ghcr.io/hanzoai/ui
-> hanzoai/universe crs/ui.yaml names the tag that is live
-> hanzoai/operator reconciles the App
-> hanzoai/static behind hanzoai/ingress serves ui.hanzo.ai
**git.hanzo.ai is canonical; GitHub is a mirror.** `.github/workflows/` holds
exactly one file, `sync.yml`, and its only job is getting refs to the forge. Every
build, check, publish and deploy is a workflow under `.hanzo/workflows/`, which the
forge reads. `.hanzo/workflows` uses GitHub Actions syntax, so a workflow moves
between the two by changing directory and nothing else.
No Vercel. `ci.yml` used to end in a `deploy-preview` job that ran `vercel deploy`
on every PR; previews come from our own stack or not at all, so that job is gone.
Its `status` job never depended on it.
## Publishing
One way: bump a package's `version` in its `package.json` and merge to `main`.
`.hanzo/workflows/publish.yml` detects the changed `@hanzo/*` package and publishes
it to npm (needs `NPM_TOKEN` as a forge secret). No changesets, no version-PR bot
— the semver bump is the trigger.
It is the SOLE publisher of every non-private `@hanzo/*` in `pkgs/*`, and it
mirrors the same tarball to `api.hanzo.ai/v1/packages/hanzo/npm` when
`HANZO_REGISTRY_TOKEN` is present. That mirror is best-effort by construction: no
token means a notice, not a failure, and npmjs stays authoritative either way.
## Deploying the site
`app/` is a Next.js static export (`output: "export"`, `trailingSlash: true`);
`pnpm build` there writes `app/out`, which `Dockerfile` copies into
`ghcr.io/hanzoai/static`. ui.hanzo.ai has been served that way since 2026-07-25 —
no Cloudflare, no GitHub Pages.
What was missing until now is the build. `crs/ui.yaml` is live and promoted, but no
workflow ever produced the image it pins: every tag up to `v5.7.6` was pushed by
hand. `.hanzo/workflows/deploy.yml` is that step. It publishes
`ghcr.io/hanzoai/ui:<sha>` and stops there — a build never deploys itself. A human
sets `spec.image.tag` in `hanzoai/universe`
`infra/k8s/operator/crs/ui.yaml`, which is the one live thing that says which build
serves.
Coverage lives in `ci.yml`'s `test` job, which runs `pnpm test:coverage` so the
lcov it uploads to Codecov actually exists. A separate `coverage.yml` used to run
the same suite a second time for the same upload plus a PR comment through the
GitHub API; one workflow does it now.
`registries.yml` keeps the `apps/v4` registry honest: reserved namespaces are
rejected and `pnpm --filter=v4 validate:registries` must pass. Its other job
labelled and commented on pull requests with the `gh` CLI against the GitHub API,
which does not exist on the forge, so that job did not come along.
## Telemetry — `@hanzo/event` is the ONE client (`pkgs/event`)
`@hanzo/event` is the single canonical telemetry client for every Hanzo surface.
ONE API surface over **TWO** planes — the client never sends the org; the server
resolves the tenant.
1. **Event stream** — pageview/event/identify/group (and an error breadcrumb),
batched to `POST {host}/v1/event` with `{ batch: [Event, …] }`
`-> { accepted, dropped }`. Tenant from the session or a publishable `pk_` key.
2. **Error plane** — every captured exception is ALSO framed as a real **Sentry
envelope** and POSTed to `POST {dsn.origin}/v1/sentry/{projectId}/envelope/?sentry_key=…`.
This is the ONLY thing that reaches the Sentry error dashboard.
> **There is NO server-side fan-out from `/v1/event` into Sentry.** Versions
> ≤ 0.3.1 claimed there was ("lensed server-side into … error tracking"). There
> is not: cloud's handler folds the exception into `properties.$exception`,
> writes one row to the event warehouse (readable via `GET /v1/errors`), and
> stops. Because every property believed that claim, the whole fleet reported
> **zero** errors to Sentry until 0.3.2 added the envelope. Do not re-collapse
> these planes.
Configure the error plane with `dsn` (or `NEXT_PUBLIC_HANZO_EVENT_DSN`), minted
per property via `POST /v1/sentry/projects`. The DSN key is publishable and
write-only — safe in a bundle, same trust class as `pk_`. **No DSN => the error
plane is inert** (fail-safe: nothing sent, nothing thrown, event stream
unaffected); assert `client.errorPlaneEnabled` if you need to know.
There is NO third plane. Web analytics used to be one — `analytics.hanzo.ai/hz.js`
posting a bare JSON array of `{site, ts, type, …}` to a second collector behind an
identical path spelling — and 0.3.7 deleted both. `hz.js` now lives in this package
as the script-tag DISTRIBUTION of this client: same `WireEvent`, same
`{ batch: [ … ] }`, same `POST {host}/v1/event`. Point everything at the API host.
It could not authenticate until 0.3.12: it sent no `Authorization` and no
`?ingest_key=`, so a keyed static surface's writes were unattributed, the door
refused them (`401`), and nothing in the page said so. `data-ingest-key="pk-…"`.
Entries: `.` (framework-agnostic: `createAnalytics`, `EVENTS`, `GOALS`,
attribution + DSN/scrub helpers) and `./react` (`AnalyticsProvider`,
`useAnalytics`, `usePageview`, `ErrorBoundary`). Auto error capture
(window.onerror / unhandledrejection / React boundary) makes it the drop-in
error-tracking replacement. Secrets and PII are scrubbed client-side before an
error leaves the device. SSR-safe, fail-soft, beacon-on-unload.
Build is a tsup dual bundle: **CJS → `.cjs`, ESM → `.mjs`** (required under
`"type": "module"` — a CJS `.js` is parsed as ESM and crashes `require()` with
"exports is not defined"). Each `exports` condition carries its own types.
### Interaction analytics — `<Hanzo analytics>` is the one wiring
An app instruments nothing. `<Hanzo analytics>` (`pkg/ui/src/root.tsx`) is the
whole setup, and every click / change / submit / route change inside the tree
reaches `POST /v1/event` named by the component it happened on:
```tsx
<Hanzo analytics={{ product: 'console', ingestKey: process.env.NEXT_PUBLIC_PUBLISHABLE_KEY }}>
```
Four packages, one of each concern, no duplication:
| Concern | Where | Note |
|---|---|---|
| client + wire | `@hanzo/event` (`pkgs/event`) | one endpoint, one key |
| capture engine | `@hanzo/observe` (`pkgs/observe`) | delegated listeners, semantic annotation, redaction |
| provider + consent | `@hanzogui/telemetry` (`~/work/hanzo/gui`) | `<TelemetryProvider/>`; owns DNT/GPC + stored choice |
| curated events | `@hanzo/ui/product` `instrument.ts` | `emit({component, action})` — what autocapture cannot know |
**`analytics` is a prop, not a default.** Mounting a component library must not
start a network conversation the app did not ask for. Off, no provider renders.
**Component names are real in production.** Every primitive already carries a
`data-slot` (via `slot()`); `componentName()` in `pkgs/observe/src/annotate.ts`
reads it, ranked ABOVE the React fiber owner deliberately — the fiber name is
dev-only, so grouping on it silently empties the dashboard at deploy. Labels keep
the qualifier: `card/button[Save]`.
**It cannot double-count.** The engine installs *delegated* listeners on a root,
so two engines on one root report everything twice — which is what an app got by
mounting a library provider AND `<ObserveProvider/>`, both correct instructions.
Since observe 0.1.7 the first engine claims its root under a `Symbol.for`
registry (page-wide, so duplicate copies of the package still see each other) and
any later one stays inert (`engine.capturing === false`). Verified in Chromium,
not only in jsdom.
**Consent is decided in ONE layer.** The engine takes `enabled` as a value; the
provider resolves policy (GPC, DNT, stored `hz_consent`, build kill switch) and
passes the answer down. Do not add a second, partial copy to the engine — the two
then disagree about an explicit opt-in.
### One way — supersessions (no divergent telemetry client)
| Package | Status | Note |
|---|---|---|
| `@hanzo/event` | **canonical** | `pkgs/event`, posts `/v1/event` only |
| `@hanzo/capture` (npm) | **deprecated → `@hanzo/event`** | the old name of this package; `@hanzo/event` is a superset |
| `pkgs/capture` (`@hanzo/analytics@0.1.0` dup) | **deleted** | stale in-repo duplicate, removed |
| `hanzoai/analytics` `packages/event` (`@hanzo/event@0.2.0`) | **deleted** | An unpublished FORK of this package in another repo. It was the only copy that could actually reach Sentry, while the published one here could not — the fleet's error telemetry died in that gap. Its envelope + scrub implementation was merged here in 0.3.2. Never fork this package again; it publishes from `pkgs/event` only. |
## Three-Layer Architecture
1. **Components** (`registry/{style}/ui/`) — single primitives (Button, Card, Dialog). CLI-installable.
2. **Examples** (`registry/{style}/example/`) — usage demos for docs via `<ComponentPreview />`.
3. **Blocks** (`registry/{style}/blocks/`) — full-page sections (Dashboard, Login). NOT CLI-installable, docs only.
## Import Path Transformation
Registry files use `@/registry/default/ui/button`. After CLI install, rewritten to `@/components/ui/button`.
## Package Exports
```typescript
import { Button, Card } from '@hanzo/ui'
import { Button } from '@hanzo/ui/components'
import * as Dialog from '@hanzo/ui/primitives/dialog'
import { cn } from '@hanzo/ui/lib/utils'
```
## Adding a Component
1. Create in BOTH themes: `app/registry/{default,new-york}/ui/my-component.tsx`
2. Create example: `app/registry/default/example/my-component-demo.tsx`
3. Create docs: `app/content/docs/components/my-component.mdx`
4. Update nav: `app/config/docs.ts`
5. Build: `pnpm build:registry`
## Tech Stack
React 19, Next.js 15.3+, Tailwind CSS 4 (OKLCH colors), Radix UI, Turborepo + pnpm, Fumadocs (MDX), class-variance-authority.
## Upstream Sync
Remote `shadcn` points to a local clone of shadcn-ui/ui.
hanzoai/ui is NOT a GitHub fork — no shared object store, so large merges can fail on push.
Strategy: file-level checkout from shadcn/main for specific directories (not git merge).
- Take theirs: packages/shadcn/, packages/tests/, apps/, templates/, scripts/, skills/
- Keep ours: app/, pkg/, demo/, docs/, template/next/, pnpm-workspace.yaml, package.json
- Regenerate: pnpm-lock.yaml after sync
## Key Features
- **Page Builder** (`/builder`): drag-drop block assembly with @dnd-kit, export to TSX
- **White-Label**: Zoo/Lux forks via `brands/{BRAND}.brand.ts`
- **External Registries**: 35+ sources in `app/registries.json`, install via `npx @hanzo/ui add @aceternity/spotlight`
## Gotchas
- Registry index is `Index[style][name]`, NOT `Index[name]` — caused silent block render failures
- Shiki `getHighlighter` incompatible with static export — replaced with basic pre/code
- Some blocks (login-01, login-02, sidebar-02) have Server Component issues with event handlers
- `@hanzo/auth` v2.6.0 uses a pluggable provider registry: `registerAuthProvider('firebase', FirebaseAuthService)`
## Rules
1. Always build registry before app
2. Keep default and new-york themes in sync
3. Blocks are docs only, not CLI-installable
4. Use pnpm, not npm/yarn
5. Never commit symlinked files (AGENTS.md, CLAUDE.md, etc.)
6. Documentation goes in `app/content/docs/`, not random root MD files
-345
View File
@@ -1,345 +0,0 @@
# Hanzo UI Makefile
# This Makefile provides convenient commands for building, testing, and managing the Hanzo UI monorepo
# Variables
PNPM := pnpm
NODE := node
NPM := npm
TSX := tsx
# Colors for output
CYAN := \033[0;36m
GREEN := \033[0;32m
YELLOW := \033[0;33m
RED := \033[0;31m
NC := \033[0m # No Color
# Default target - runs build, test, and docs
.PHONY: all
all: install build test docs
@echo "$(GREEN)✓ All tasks completed successfully$(NC)"
# Help target - shows available commands
.PHONY: help
help:
@echo "$(CYAN)Hanzo UI Makefile$(NC)"
@echo ""
@echo "$(YELLOW)Available targets:$(NC)"
@echo " $(GREEN)make$(NC) - Run default targets (install, build, test, docs)"
@echo " $(GREEN)make install$(NC) - Install all dependencies"
@echo " $(GREEN)make build$(NC) - Build all packages and the app"
@echo " $(GREEN)make test$(NC) - Run all tests"
@echo " $(GREEN)make dev$(NC) - Start development server"
@echo " $(GREEN)make lint$(NC) - Run linting checks"
@echo " $(GREEN)make format$(NC) - Format code with Prettier"
@echo " $(GREEN)make typecheck$(NC) - Run TypeScript type checking"
@echo " $(GREEN)make docs$(NC) - Generate documentation"
@echo " $(GREEN)make clean$(NC) - Clean build artifacts and node_modules"
@echo " $(GREEN)make registry$(NC) - Build the component registry"
@echo " $(GREEN)make version$(NC) - Show package versions"
@echo ""
@echo "$(YELLOW)Deployment & Publishing:$(NC)"
@echo " $(GREEN)make deploy$(NC) - Deploy to ui.hanzo.ai via GitHub Pages"
@echo " $(GREEN)make deploy-pages$(NC) - Manually trigger GitHub Pages deployment"
@echo " $(GREEN)make deploy-status$(NC)- Check deployment status"
@echo " $(GREEN)make publish$(NC) - Publish packages to npm registry"
@echo " $(GREEN)make publish-dry$(NC) - Dry run npm publish"
@echo ""
@echo "$(YELLOW)Development:$(NC)"
@echo " $(GREEN)make dev-app$(NC) - Start app development server"
@echo " $(GREEN)make dev-ui$(NC) - Build UI package in watch mode"
@echo ""
@echo "$(YELLOW)Quality:$(NC)"
@echo " $(GREEN)make check$(NC) - Run all quality checks (lint, format, typecheck)"
@echo " $(GREEN)make fix$(NC) - Auto-fix linting and formatting issues"
@echo ""
@echo "$(YELLOW)Utilities:$(NC)"
@echo " $(GREEN)make clean-all$(NC) - Deep clean (removes lock file too)"
@echo " $(GREEN)make update-deps$(NC) - Update all dependencies"
# Install dependencies
.PHONY: install
install:
@echo "$(CYAN)Installing dependencies...$(NC)"
@$(PNPM) install
@echo "$(GREEN)✓ Dependencies installed$(NC)"
# Build all packages and app
.PHONY: build
build: install
@echo "$(CYAN)Building all packages...$(NC)"
@$(PNPM) run build
@echo "$(GREEN)✓ Build completed$(NC)"
# Build individual components
.PHONY: build-app
build-app:
@echo "$(CYAN)Building app...$(NC)"
@cd app && $(PNPM) run build
@echo "$(GREEN)✓ App build completed$(NC)"
.PHONY: build-ui
build-ui:
@echo "$(CYAN)Building UI package...$(NC)"
@cd pkg/ui && $(PNPM) run build
@echo "$(GREEN)✓ UI package build completed$(NC)"
.PHONY: build-auth
build-auth:
@echo "$(CYAN)Building Auth package...$(NC)"
@cd pkg/auth && $(PNPM) run build 2>/dev/null || true
@echo "$(GREEN)✓ Auth package build completed$(NC)"
.PHONY: build-commerce
build-commerce:
@echo "$(CYAN)Building Commerce package...$(NC)"
@cd pkg/commerce && $(PNPM) run build 2>/dev/null || true
@echo "$(GREEN)✓ Commerce package build completed$(NC)"
# Run tests
.PHONY: test
test:
@echo "$(CYAN)Running tests...$(NC)"
@if [ -f "app/package.json" ] && grep -q '"test"' app/package.json 2>/dev/null; then \
cd app && $(PNPM) test 2>/dev/null || echo "$(YELLOW)No tests configured for app$(NC)"; \
else \
echo "$(YELLOW)No test script found in app$(NC)"; \
fi
@echo "$(GREEN)✓ Tests completed$(NC)"
# Development server
.PHONY: dev
dev:
@echo "$(CYAN)Starting development server on port 3003...$(NC)"
@cd app && $(PNPM) run dev
.PHONY: dev-app
dev-app: dev
.PHONY: dev-ui
dev-ui:
@echo "$(CYAN)Building UI package in watch mode...$(NC)"
@cd pkg/ui && $(PNPM) run tc --watch
# Linting
.PHONY: lint
lint:
@echo "$(CYAN)Running linters...$(NC)"
@cd app && $(PNPM) run lint
@echo "$(GREEN)✓ Linting completed$(NC)"
.PHONY: lint-fix
lint-fix:
@echo "$(CYAN)Fixing linting issues...$(NC)"
@cd app && $(PNPM) run lint:fix
@echo "$(GREEN)✓ Linting fixes applied$(NC)"
# Formatting
.PHONY: format
format:
@echo "$(CYAN)Checking code formatting...$(NC)"
@cd app && $(PNPM) run format:check
@echo "$(GREEN)✓ Format check completed$(NC)"
.PHONY: format-fix
format-fix:
@echo "$(CYAN)Formatting code...$(NC)"
@cd app && $(PNPM) run format:write
@echo "$(GREEN)✓ Code formatted$(NC)"
# Type checking
.PHONY: typecheck
typecheck:
@echo "$(CYAN)Running TypeScript type checking...$(NC)"
@cd app && $(PNPM) run typecheck
@cd pkg/ui && $(PNPM) run tc --noEmit
@echo "$(GREEN)✓ Type checking completed$(NC)"
# Documentation generation
.PHONY: docs
docs:
@echo "$(CYAN)Generating documentation...$(NC)"
@if [ -f "app/README.md" ]; then \
echo "$(GREEN)✓ README.md exists$(NC)"; \
fi
@if [ -f "LLM.md" ]; then \
echo "$(GREEN)✓ LLM.md exists$(NC)"; \
fi
@echo "$(YELLOW)Note: Full documentation available at https://hanzo.ai/docs$(NC)"
# Registry build
.PHONY: registry
registry:
@echo "$(CYAN)Building component registry...$(NC)"
@cd app && $(PNPM) run registry:build
@echo "$(GREEN)✓ Registry built$(NC)"
.PHONY: registry-capture
registry-capture:
@echo "$(CYAN)Capturing registry...$(NC)"
@cd app && $(PNPM) run registry:capture
@echo "$(GREEN)✓ Registry captured$(NC)"
.PHONY: validate-registries
validate-registries:
@echo "$(CYAN)Validating registries...$(NC)"
@cd app && $(PNPM) run validate:registries
@echo "$(GREEN)✓ Registries validated$(NC)"
# Quality checks
.PHONY: check
check: lint format typecheck
@echo "$(GREEN)✓ All quality checks passed$(NC)"
.PHONY: fix
fix: lint-fix format-fix
@echo "$(GREEN)✓ All fixes applied$(NC)"
# Publishing packages to npm
.PHONY: publish
publish: build test
@echo "$(CYAN)Publishing packages to npm...$(NC)"
@echo "$(YELLOW)Publishing @hanzo/ui...$(NC)"
@cd pkg/ui && $(NPM) publish --access public
@echo "$(YELLOW)Publishing @hanzo/auth...$(NC)"
@cd pkg/auth && $(NPM) publish --access public 2>/dev/null || true
@echo "$(YELLOW)Publishing @hanzo/commerce...$(NC)"
@cd pkg/commerce && $(NPM) publish --access public 2>/dev/null || true
@echo "$(GREEN)✓ Packages published to npm$(NC)"
.PHONY: publish-dry
publish-dry: build test
@echo "$(CYAN)Dry run: Publishing packages...$(NC)"
@cd pkg/ui && $(NPM) publish --dry-run --access public
@cd pkg/auth && $(NPM) publish --dry-run --access public 2>/dev/null || true
@cd pkg/commerce && $(NPM) publish --dry-run --access public 2>/dev/null || true
@echo "$(GREEN)✓ Dry run completed$(NC)"
# Deployment to ui.hanzo.ai
.PHONY: deploy
deploy: build-static
@echo "$(CYAN)Deploying to ui.hanzo.ai...$(NC)"
@echo "$(YELLOW)This will trigger GitHub Actions deployment$(NC)"
@git add -A
@git diff --cached --quiet || (git commit -m "Deploy to ui.hanzo.ai" && git push origin main)
@echo "$(GREEN)✓ Deployment triggered - check GitHub Actions for status$(NC)"
@echo "$(YELLOW)View deployment at: https://github.com/hanzoai/react-sdk/actions$(NC)"
# Build for static export (GitHub Pages)
.PHONY: build-static
build-static: install
@echo "$(CYAN)Building static site for ui.hanzo.ai...$(NC)"
@cd app && $(PNPM) run build
@if [ ! -f app/next.config.mjs ] || ! grep -q "output.*static" app/next.config.mjs; then \
echo "$(YELLOW)Note: For GitHub Pages deployment, consider adding 'output: \"export\"' to next.config.mjs$(NC)"; \
fi
@echo "$(GREEN)✓ Static build completed$(NC)"
# Deploy preview (for PRs)
.PHONY: deploy-preview
deploy-preview: build
@echo "$(CYAN)Creating preview deployment...$(NC)"
@echo "$(YELLOW)Preview deployments are handled by Vercel/Netlify automatically on PR$(NC)"
@echo "$(GREEN)✓ Push your changes to a branch and create a PR for preview$(NC)"
# Check deployment status
.PHONY: deploy-status
deploy-status:
@echo "$(CYAN)Checking deployment status...$(NC)"
@gh workflow view "Deploy to GitHub Pages" --web 2>/dev/null || \
echo "$(YELLOW)Install GitHub CLI (gh) to check deployment status$(NC)"
@echo ""
@echo "$(CYAN)Current site:$(NC) https://ui.hanzo.ai"
@echo "$(CYAN)GitHub Pages:$(NC) https://hanzoai.github.io/react-sdk"
# Manual GitHub Pages deployment trigger
.PHONY: deploy-pages
deploy-pages:
@echo "$(CYAN)Triggering GitHub Pages deployment...$(NC)"
@gh workflow run "Deploy to GitHub Pages" 2>/dev/null || \
(echo "$(YELLOW)Install GitHub CLI or trigger manually at:$(NC)" && \
echo "https://github.com/hanzoai/react-sdk/actions/workflows/deploy-pages.yml")
@echo "$(GREEN)✓ Deployment workflow triggered$(NC)"
# Version management
.PHONY: version
version:
@echo "$(CYAN)Package versions:$(NC)"
@echo " @hanzo/react-sdk: $$(cat package.json | grep '"version"' | head -1 | cut -d'"' -f4)"
@echo " @hanzo/ui: $$(cat pkg/ui/package.json | grep '"version"' | head -1 | cut -d'"' -f4)"
@echo " @hanzo/auth: $$(cat pkg/auth/package.json 2>/dev/null | grep '"version"' | head -1 | cut -d'"' -f4 || echo 'N/A')"
@echo " @hanzo/commerce: $$(cat pkg/commerce/package.json 2>/dev/null | grep '"version"' | head -1 | cut -d'"' -f4 || echo 'N/A')"
@echo " @hanzo/ui-web (app): $$(cat app/package.json | grep '"version"' | head -1 | cut -d'"' -f4)"
.PHONY: version-check
version-check:
@echo "$(CYAN)Latest published versions:$(NC)"
@cd pkg/ui && $(PNPM) run lat
# Cleaning
.PHONY: clean
clean:
@echo "$(CYAN)Cleaning build artifacts and node_modules...$(NC)"
@$(PNPM) run clean:nm
@rm -rf app/.next app/dist app/build
@rm -rf pkg/ui/dist pkg/ui/build
@rm -rf pkg/auth/dist pkg/auth/build 2>/dev/null || true
@rm -rf pkg/commerce/dist pkg/commerce/build 2>/dev/null || true
@echo "$(GREEN)✓ Clean completed$(NC)"
.PHONY: clean-all
clean-all:
@echo "$(CYAN)Deep cleaning (including lock file)...$(NC)"
@$(PNPM) run clean:all
@rm -rf app/.next app/dist app/build
@rm -rf pkg/ui/dist pkg/ui/build
@rm -rf pkg/auth/dist pkg/auth/build 2>/dev/null || true
@rm -rf pkg/commerce/dist pkg/commerce/build 2>/dev/null || true
@echo "$(GREEN)✓ Deep clean completed$(NC)"
# Dependency management
.PHONY: update-deps
update-deps:
@echo "$(CYAN)Updating dependencies...$(NC)"
@$(PNPM) update --latest
@echo "$(GREEN)✓ Dependencies updated$(NC)"
.PHONY: outdated
outdated:
@echo "$(CYAN)Checking for outdated dependencies...$(NC)"
@$(PNPM) outdated
# Production build and start
.PHONY: prod
prod: build
@echo "$(CYAN)Starting production server on port 3001...$(NC)"
@cd app && $(PNPM) run start
# CI/CD helpers
.PHONY: ci
ci: install lint typecheck test build
@echo "$(GREEN)✓ CI checks passed$(NC)"
# Docker support (if needed in future)
.PHONY: docker-build
docker-build:
@echo "$(CYAN)Building Docker image...$(NC)"
@docker build -t hanzo-ui:latest .
@echo "$(GREEN)✓ Docker image built$(NC)"
.PHONY: docker-run
docker-run:
@echo "$(CYAN)Running Docker container...$(NC)"
@docker run -p 3001:3001 hanzo-ui:latest
# Utility to check if all required tools are installed
.PHONY: check-tools
check-tools:
@echo "$(CYAN)Checking required tools...$(NC)"
@command -v $(PNPM) >/dev/null 2>&1 && echo "$(GREEN)✓ pnpm installed$(NC)" || echo "$(RED)✗ pnpm not found$(NC)"
@command -v $(NODE) >/dev/null 2>&1 && echo "$(GREEN)✓ node installed$(NC)" || echo "$(RED)✗ node not found$(NC)"
@command -v $(NPM) >/dev/null 2>&1 && echo "$(GREEN)✓ npm installed$(NC)" || echo "$(RED)✗ npm not found$(NC)"
@command -v git >/dev/null 2>&1 && echo "$(GREEN)✓ git installed$(NC)" || echo "$(RED)✗ git not found$(NC)"
# Default target explanation
.DEFAULT_GOAL := all
-6
View File
@@ -1,6 +0,0 @@
ui
Copyright (c) 2023 Hanzo AI, Inc.
This product includes software from shadcn/ui (https://github.com/shadcn-ui/ui), licensed under MIT:
Copyright (c) 2023 shadcn
-39
View File
@@ -1,39 +0,0 @@
# Publishing
Two lanes, one trigger each. No changesets, no version-PR bot.
## 1. `pkgs/*` — auto-publish on version bump (`publish.yml`)
Bump a package's `version` in `pkgs/<name>/package.json` and merge to `main`.
`.github/workflows/publish.yml` detects the changed public `@hanzo/*` package,
builds it, and publishes to npm (repo secret `NPM_TOKEN`). Patch bumps only
(`x.y.z``x.y.z+1`).
## 2. `pkg/ui` — `@hanzo/ui@8`, the v8 lane (maintainer flow)
`pkg/ui` (with `pkg/data`) is the modern cross-platform library on `@hanzo/gui`.
It publishes from the package directory (`prepack` builds the `types/`):
```bash
cd pkg/ui
pnpm typecheck && pnpm test && pnpm build
# bump "version" in package.json (patch), commit to main, then:
npm publish --access public
```
`@hanzo/ui-shadcn` (`pkgs/ui`) is the legacy v5 kit — existing consumers pin
`@hanzo/ui-shadcn@^5`; it rides lane 1 like any other `pkgs/*` package.
> The old tag-driven flow (`publish-on-tag.yml`, `npm-publish.yml`, the
> `pkg/commerce|brand|react` paths) is gone — do not tag to publish here.
## Prerequisites
- `NPM_TOKEN` repo secret (lane 1) / npm auth as a maintainer (lane 2)
- Every package carries `"publishConfig": { "access": "public" }`
## Checklist
- [ ] `pnpm typecheck` + `pnpm test` green in the package
- [ ] Patch version bump (check the last published patch first)
- [ ] Commit to `main`; lane 1 publishes on merge, lane 2 via `npm publish`
+6 -145
View File
@@ -1,156 +1,17 @@
<p align="center"><img src=".github/hero.svg" alt="@hanzo/ui" width="880"></p>
# @luxdefi/ui
# @hanzo/ui
Accessible and customizable components that you can copy and paste into your DeFi apps.
**The React component library for AI applications.** Accessible, customizable primitives for React, Vue, Svelte, and React Native — built on shadcn/ui, extended with AI, 3D, animation, and commerce components, and a single typed import surface.
<p align="center">
<a href="https://www.npmjs.com/package/@hanzo/ui"><img src="https://img.shields.io/npm/v/@hanzo/ui?color=black&label=%40hanzo%2Fui" alt="npm"></a>
<a href="./LICENSE.md"><img src="https://img.shields.io/badge/license-MIT-black" alt="MIT"></a>
<a href="https://ui.hanzo.ai"><img src="https://img.shields.io/badge/docs-ui.hanzo.ai-black" alt="docs"></a>
</p>
![hero](app/public/og.jpg)
## Features
- **161+ components** — 3x the surface of upstream shadcn/ui
- **Multi-framework** — React, Vue, Svelte, React Native
- **Two themes** — Default & New York variants
- **AI components** — chat, assistants, agent UI, playground
- **3D components** — interactive 3D elements
- **Animations** — advanced motion components
- **Page builder** — visual drag-and-drop assembly, export to TSX
- **Blocks** — 24+ production-ready full-page templates
- **White-label** — fork and rebrand by domain (Zoo, Lux, …)
- **Accessible** — built on Radix UI primitives
- **Customizable** — Tailwind CSS 4 (OKLCH), fully typed TypeScript
## Quick start
### Install
```bash
pnpm add @hanzo/ui
# or
npm install @hanzo/ui
```
### Use
```tsx
import { Button, Card, Input } from '@hanzo/ui'
export function App() {
return (
<Card>
<Card.Header>
<Card.Title>Welcome</Card.Title>
</Card.Header>
<Card.Content>
<Input placeholder="Enter text..." />
</Card.Content>
<Card.Footer>
<Button>Submit</Button>
</Card.Footer>
</Card>
)
}
```
## One import surface (v8)
`@hanzo/ui@8` is the single entry point for the whole kit. Each capability is a
thin subpath that re-exports its home package — code lives once, and each home is
an optional peer, pulled only when you use its subpath.
| Import | What you get |
|---|---|
| `@hanzo/ui` · `/product` | charts, metrics, PageHeader, StatusTag, EmptyState, ComboBox, SlideOver, Toast |
| `@hanzo/ui/data` | RecordsView, DataTable, typed field editors |
| `@hanzo/ui/canvas` | ProjectCanvas, ServiceNode, DeployTimeline, EnvSwitcher |
| `@hanzo/ui/dashboard` | landing + deploy-pipeline + overview kit |
| `@hanzo/ui/usage` | UsageMeter, UsageProviderCard, UsageDashboard |
| `@hanzo/ui/gitops` | GitopsAppList, tree, diff, sync/rollback, HealthBadge |
Also available as granular imports:
```ts
import { Button, Card } from '@hanzo/ui/components'
import * as Dialog from '@hanzo/ui/primitives/dialog'
import { cn } from '@hanzo/ui/lib/utils'
```
## CLI
Add components straight into your project — the CLI copies source you own:
```bash
npx @hanzo/ui add button
npx @hanzo/ui add card dialog
```
Install from 35+ external registries too:
```bash
npx @hanzo/ui add @aceternity/spotlight
```
## Packages
The workspace publishes a family of scoped packages under `@hanzo/*`:
| Package | Purpose |
|---|---|
| `@hanzo/ui` | Core library + the v8 import surface (161+ components) |
| `@hanzo/react` | React primitives |
| `@hanzo/data` | Records, data tables, typed field editors |
| `@hanzo/canvas` | Service/deploy canvas components |
| `@hanzo/dashboard` | Dashboard + deploy-pipeline kit |
| `@hanzo/commerce` · `@hanzo/checkout` · `@hanzo/shop` | Commerce components |
| `@hanzo/agent-ui` | AI agent UI components |
| `@hanzo/brand` · `@hanzo/tokens` | Branding system & design tokens |
| `@hanzo/event` | Telemetry client (`POST /v1/event`) |
## Development
```bash
git clone https://github.com/hanzoai/ui.git
cd ui
pnpm install
pnpm build:registry # generate the component registry FIRST
pnpm dev # docs site + registry (http://localhost:3003)
```
> The registry generates the JSON the CLI reads, so `build:registry` must run
> before `build`. Keep the Default and New York themes in sync when adding
> components. Use pnpm — not npm or yarn.
```bash
pnpm build # build the docs app
pnpm lint # lint all workspaces
pnpm typecheck # type check
pnpm test # unit tests
pnpm test:e2e # Playwright E2E
```
![hero](apps/www/public/og.jpg)
## Documentation
Full docs, live previews, and the component catalog: **[ui.hanzo.ai](https://ui.hanzo.ai)**.
Visit http://ui.lux.finance to view the documentation.
## Contributing
See the [contributing guide](/CONTRIBUTING.md).
Please read the [contributing guide](/CONTRIBUTING.md).
## License
MIT — see [LICENSE.md](./LICENSE.md).
---
## Hanzo — the Open AI Cloud
Open source · every language · on-chain settlement. [hanzo.ai](https://hanzo.ai) · [docs.hanzo.ai](https://docs.hanzo.ai)
**SDKs in every language** — [Python](https://github.com/hanzoai/python-sdk) (flagship) · [TypeScript](https://github.com/hanzo-js/sdk) · [Go](https://github.com/hanzo-go/sdk) · [Rust](https://github.com/hanzo-rs/sdk) · [C++](https://github.com/hanzo-cpp/sdk) · [Swift](https://github.com/hanzo-swift/sdk) · [Kotlin](https://github.com/hanzo-kt/sdk) · [umbrella](https://github.com/hanzoai/sdk)
Licensed under the [MIT license](https://github.com/luxdefi/ui/blob/main/LICENSE.md).
-9
View File
@@ -1,9 +0,0 @@
# Security Policy
If you believe you have found a security vulnerability, we encourage you to let us know right away.
We will investigate all legitimate reports and do our best to quickly fix the problem.
Our preference is that you make use of GitHub's private vulnerability reporting feature to disclose potential security vulnerabilities in our Open Source Software.
To do this, please visit the security tab of the repository and click the [Report a vulnerability](https://github.com/shadcn-ui/ui/security/advisories/new) button.
-3
View File
@@ -1,3 +0,0 @@
{
"extends": ["@commitlint/config-conventional"]
}
-11
View File
@@ -1,11 +0,0 @@
{
"mcpServers": {
"hanzo-ui": {
"command": "npx",
"args": ["@hanzo/ui@latest", "mcp"],
"env": {
"REGISTRY_URL": "https://ui.hanzo.ai/registry/registry.json"
}
}
}
}
-434
View File
@@ -1,434 +0,0 @@
// @ts-nocheck
import { browser } from "@hanzo/docs-mdx/runtime/browser"
import type * as Config from "../source.config"
const create = browser<
typeof Config,
import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
DocData: {}
}
>()
const browserCollections = {
docs: create.doc("docs", {
"about.mdx": () => import("../content/docs/about.mdx?collection=docs"),
"changelog.mdx": () =>
import("../content/docs/changelog.mdx?collection=docs"),
"cli.mdx": () => import("../content/docs/cli.mdx?collection=docs"),
"components-json.mdx": () =>
import("../content/docs/components-json.mdx?collection=docs"),
"components.mdx": () =>
import("../content/docs/components.mdx?collection=docs"),
"figma.mdx": () => import("../content/docs/figma.mdx?collection=docs"),
"index.mdx": () => import("../content/docs/index.mdx?collection=docs"),
"mcp.mdx": () => import("../content/docs/mcp.mdx?collection=docs"),
"theming.mdx": () => import("../content/docs/theming.mdx?collection=docs"),
"ai/actions.mdx": () =>
import("../content/docs/ai/actions.mdx?collection=docs"),
"ai/agents.mdx": () =>
import("../content/docs/ai/agents.mdx?collection=docs"),
"ai/assistant.mdx": () =>
import("../content/docs/ai/assistant.mdx?collection=docs"),
"ai/chat.mdx": () => import("../content/docs/ai/chat.mdx?collection=docs"),
"ai/code.mdx": () => import("../content/docs/ai/code.mdx?collection=docs"),
"ai/index.mdx": () =>
import("../content/docs/ai/index.mdx?collection=docs"),
"ai/models.mdx": () =>
import("../content/docs/ai/models.mdx?collection=docs"),
"ai/playground.mdx": () =>
import("../content/docs/ai/playground.mdx?collection=docs"),
"ai/tools.mdx": () =>
import("../content/docs/ai/tools.mdx?collection=docs"),
"ai/vision.mdx": () =>
import("../content/docs/ai/vision.mdx?collection=docs"),
"ai/voice-settings.mdx": () =>
import("../content/docs/ai/voice-settings.mdx?collection=docs"),
"ai/voice.mdx": () =>
import("../content/docs/ai/voice.mdx?collection=docs"),
"block-examples/billing.mdx": () =>
import("../content/docs/block-examples/billing.mdx?collection=docs"),
"block-examples/compose-spec.mdx": () =>
import("../content/docs/block-examples/compose-spec.mdx?collection=docs"),
"block-examples/newsletter.mdx": () =>
import("../content/docs/block-examples/newsletter.mdx?collection=docs"),
"block-examples/pricing.mdx": () =>
import("../content/docs/block-examples/pricing.mdx?collection=docs"),
"frameworks/index.mdx": () =>
import("../content/docs/frameworks/index.mdx?collection=docs"),
"frameworks/react.mdx": () =>
import("../content/docs/frameworks/react.mdx?collection=docs"),
"frameworks/vue.mdx": () =>
import("../content/docs/frameworks/vue.mdx?collection=docs"),
"dark-mode/astro.mdx": () =>
import("../content/docs/dark-mode/astro.mdx?collection=docs"),
"dark-mode/index.mdx": () =>
import("../content/docs/dark-mode/index.mdx?collection=docs"),
"dark-mode/next.mdx": () =>
import("../content/docs/dark-mode/next.mdx?collection=docs"),
"dark-mode/remix.mdx": () =>
import("../content/docs/dark-mode/remix.mdx?collection=docs"),
"dark-mode/vite.mdx": () =>
import("../content/docs/dark-mode/vite.mdx?collection=docs"),
"guides/page-builder.mdx": () =>
import("../content/docs/guides/page-builder.mdx?collection=docs"),
"guides/visual-workflows.mdx": () =>
import("../content/docs/guides/visual-workflows.mdx?collection=docs"),
"hanzo-react/getting-started.mdx": () =>
import("../content/docs/hanzo-react/getting-started.mdx?collection=docs"),
"hanzo-react/hooks.mdx": () =>
import("../content/docs/hanzo-react/hooks.mdx?collection=docs"),
"installation/astro.mdx": () =>
import("../content/docs/installation/astro.mdx?collection=docs"),
"installation/gatsby.mdx": () =>
import("../content/docs/installation/gatsby.mdx?collection=docs"),
"installation/index.mdx": () =>
import("../content/docs/installation/index.mdx?collection=docs"),
"installation/laravel.mdx": () =>
import("../content/docs/installation/laravel.mdx?collection=docs"),
"installation/manual.mdx": () =>
import("../content/docs/installation/manual.mdx?collection=docs"),
"installation/next.mdx": () =>
import("../content/docs/installation/next.mdx?collection=docs"),
"installation/remix.mdx": () =>
import("../content/docs/installation/remix.mdx?collection=docs"),
"installation/vite.mdx": () =>
import("../content/docs/installation/vite.mdx?collection=docs"),
"packages/index.mdx": () =>
import("../content/docs/packages/index.mdx?collection=docs"),
"components/3d-card.mdx": () =>
import("../content/docs/components/3d-card.mdx?collection=docs"),
"components/3d-marquee.mdx": () =>
import("../content/docs/components/3d-marquee.mdx?collection=docs"),
"components/3d-pin.mdx": () =>
import("../content/docs/components/3d-pin.mdx?collection=docs"),
"components/accordion.mdx": () =>
import("../content/docs/components/accordion.mdx?collection=docs"),
"components/advanced-navigation-bar.mdx": () =>
import(
"../content/docs/components/advanced-navigation-bar.mdx?collection=docs"
),
"components/ai-model-selector-navigation-bar.mdx": () =>
import(
"../content/docs/components/ai-model-selector-navigation-bar.mdx?collection=docs"
),
"components/alert-dialog.mdx": () =>
import("../content/docs/components/alert-dialog.mdx?collection=docs"),
"components/alert.mdx": () =>
import("../content/docs/components/alert.mdx?collection=docs"),
"components/android.mdx": () =>
import("../content/docs/components/android.mdx?collection=docs"),
"components/animated-beam.mdx": () =>
import("../content/docs/components/animated-beam.mdx?collection=docs"),
"components/animated-cursor.mdx": () =>
import("../content/docs/components/animated-cursor.mdx?collection=docs"),
"components/animated-testimonials.mdx": () =>
import(
"../content/docs/components/animated-testimonials.mdx?collection=docs"
),
"components/animated-tooltip.mdx": () =>
import("../content/docs/components/animated-tooltip.mdx?collection=docs"),
"components/animation-components.mdx": () =>
import(
"../content/docs/components/animation-components.mdx?collection=docs"
),
"components/announcement.mdx": () =>
import("../content/docs/components/announcement.mdx?collection=docs"),
"components/app-switcher-navigation-bar.mdx": () =>
import(
"../content/docs/components/app-switcher-navigation-bar.mdx?collection=docs"
),
"components/apple-cards-carousel.mdx": () =>
import(
"../content/docs/components/apple-cards-carousel.mdx?collection=docs"
),
"components/apple-hello-effect.mdx": () =>
import(
"../content/docs/components/apple-hello-effect.mdx?collection=docs"
),
"components/aspect-ratio.mdx": () =>
import("../content/docs/components/aspect-ratio.mdx?collection=docs"),
"components/avatar-group.mdx": () =>
import("../content/docs/components/avatar-group.mdx?collection=docs"),
"components/avatar.mdx": () =>
import("../content/docs/components/avatar.mdx?collection=docs"),
"components/badge.mdx": () =>
import("../content/docs/components/badge.mdx?collection=docs"),
"components/banner.mdx": () =>
import("../content/docs/components/banner.mdx?collection=docs"),
"components/breadcrumb-and-filters-navigation-bar.mdx": () =>
import(
"../content/docs/components/breadcrumb-and-filters-navigation-bar.mdx?collection=docs"
),
"components/breadcrumb-navigation-bar.mdx": () =>
import(
"../content/docs/components/breadcrumb-navigation-bar.mdx?collection=docs"
),
"components/breadcrumb.mdx": () =>
import("../content/docs/components/breadcrumb.mdx?collection=docs"),
"components/button-group.mdx": () =>
import("../content/docs/components/button-group.mdx?collection=docs"),
"components/button.mdx": () =>
import("../content/docs/components/button.mdx?collection=docs"),
"components/calendar.mdx": () =>
import("../content/docs/components/calendar.mdx?collection=docs"),
"components/card.mdx": () =>
import("../content/docs/components/card.mdx?collection=docs"),
"components/carousel.mdx": () =>
import("../content/docs/components/carousel.mdx?collection=docs"),
"components/centered-logo-navigation-bar.mdx": () =>
import(
"../content/docs/components/centered-logo-navigation-bar.mdx?collection=docs"
),
"components/checkbox.mdx": () =>
import("../content/docs/components/checkbox.mdx?collection=docs"),
"components/choicebox.mdx": () =>
import("../content/docs/components/choicebox.mdx?collection=docs"),
"components/code-block.mdx": () =>
import("../content/docs/components/code-block.mdx?collection=docs"),
"components/code-editor.mdx": () =>
import("../content/docs/components/code-editor.mdx?collection=docs"),
"components/code-tabs.mdx": () =>
import("../content/docs/components/code-tabs.mdx?collection=docs"),
"components/collaboration-navigation-bar.mdx": () =>
import(
"../content/docs/components/collaboration-navigation-bar.mdx?collection=docs"
),
"components/collapsible.mdx": () =>
import("../content/docs/components/collapsible.mdx?collection=docs"),
"components/color-picker.mdx": () =>
import("../content/docs/components/color-picker.mdx?collection=docs"),
"components/combobox.mdx": () =>
import("../content/docs/components/combobox.mdx?collection=docs"),
"components/command.mdx": () =>
import("../content/docs/components/command.mdx?collection=docs"),
"components/communication-navigation-bar.mdx": () =>
import(
"../content/docs/components/communication-navigation-bar.mdx?collection=docs"
),
"components/comparison.mdx": () =>
import("../content/docs/components/comparison.mdx?collection=docs"),
"components/context-menu.mdx": () =>
import("../content/docs/components/context-menu.mdx?collection=docs"),
"components/context-switcher-navigation-bar.mdx": () =>
import(
"../content/docs/components/context-switcher-navigation-bar.mdx?collection=docs"
),
"components/credit-card.mdx": () =>
import("../content/docs/components/credit-card.mdx?collection=docs"),
"components/cursor.mdx": () =>
import("../content/docs/components/cursor.mdx?collection=docs"),
"components/dashboard-navigation-bar.mdx": () =>
import(
"../content/docs/components/dashboard-navigation-bar.mdx?collection=docs"
),
"components/data-table.mdx": () =>
import("../content/docs/components/data-table.mdx?collection=docs"),
"components/date-picker.mdx": () =>
import("../content/docs/components/date-picker.mdx?collection=docs"),
"components/desktop-hooks.mdx": () =>
import("../content/docs/components/desktop-hooks.mdx?collection=docs"),
"components/desktop-spotlight.mdx": () =>
import(
"../content/docs/components/desktop-spotlight.mdx?collection=docs"
),
"components/desktop-window.mdx": () =>
import("../content/docs/components/desktop-window.mdx?collection=docs"),
"components/desktop.mdx": () =>
import("../content/docs/components/desktop.mdx?collection=docs"),
"components/dialog-stack.mdx": () =>
import("../content/docs/components/dialog-stack.mdx?collection=docs"),
"components/dialog.mdx": () =>
import("../content/docs/components/dialog.mdx?collection=docs"),
"components/dock.mdx": () =>
import("../content/docs/components/dock.mdx?collection=docs"),
"components/drawer.mdx": () =>
import("../content/docs/components/drawer.mdx?collection=docs"),
"components/dropdown-menu.mdx": () =>
import("../content/docs/components/dropdown-menu.mdx?collection=docs"),
"components/dropzone.mdx": () =>
import("../content/docs/components/dropzone.mdx?collection=docs"),
"components/e-commerce-navigation-bar.mdx": () =>
import(
"../content/docs/components/e-commerce-navigation-bar.mdx?collection=docs"
),
"components/editor.mdx": () =>
import("../content/docs/components/editor.mdx?collection=docs"),
"components/empty.mdx": () =>
import("../content/docs/components/empty.mdx?collection=docs"),
"components/field.mdx": () =>
import("../content/docs/components/field.mdx?collection=docs"),
"components/form.mdx": () =>
import("../content/docs/components/form.mdx?collection=docs"),
"components/gantt.mdx": () =>
import("../content/docs/components/gantt.mdx?collection=docs"),
"components/glimpse.mdx": () =>
import("../content/docs/components/glimpse.mdx?collection=docs"),
"components/grid-pattern.mdx": () =>
import("../content/docs/components/grid-pattern.mdx?collection=docs"),
"components/hover-card.mdx": () =>
import("../content/docs/components/hover-card.mdx?collection=docs"),
"components/icon-navigation-bar.mdx": () =>
import(
"../content/docs/components/icon-navigation-bar.mdx?collection=docs"
),
"components/image-crop.mdx": () =>
import("../content/docs/components/image-crop.mdx?collection=docs"),
"components/image-zoom.mdx": () =>
import("../content/docs/components/image-zoom.mdx?collection=docs"),
"components/index.mdx": () =>
import("../content/docs/components/index.mdx?collection=docs"),
"components/input-group.mdx": () =>
import("../content/docs/components/input-group.mdx?collection=docs"),
"components/input-otp.mdx": () =>
import("../content/docs/components/input-otp.mdx?collection=docs"),
"components/input.mdx": () =>
import("../content/docs/components/input.mdx?collection=docs"),
"components/interactive-grid-pattern.mdx": () =>
import(
"../content/docs/components/interactive-grid-pattern.mdx?collection=docs"
),
"components/iphone-15-pro.mdx": () =>
import("../content/docs/components/iphone-15-pro.mdx?collection=docs"),
"components/item.mdx": () =>
import("../content/docs/components/item.mdx?collection=docs"),
"components/kanban.mdx": () =>
import("../content/docs/components/kanban.mdx?collection=docs"),
"components/kbd.mdx": () =>
import("../content/docs/components/kbd.mdx?collection=docs"),
"components/label.mdx": () =>
import("../content/docs/components/label.mdx?collection=docs"),
"components/limelight-nav.mdx": () =>
import("../content/docs/components/limelight-nav.mdx?collection=docs"),
"components/list.mdx": () =>
import("../content/docs/components/list.mdx?collection=docs"),
"components/macos-dock.mdx": () =>
import("../content/docs/components/macos-dock.mdx?collection=docs"),
"components/magnetic.mdx": () =>
import("../content/docs/components/magnetic.mdx?collection=docs"),
"components/marquee.mdx": () =>
import("../content/docs/components/marquee.mdx?collection=docs"),
"components/menu-dock.mdx": () =>
import("../content/docs/components/menu-dock.mdx?collection=docs"),
"components/menubar.mdx": () =>
import("../content/docs/components/menubar.mdx?collection=docs"),
"components/message-dock.mdx": () =>
import("../content/docs/components/message-dock.mdx?collection=docs"),
"components/mini-calendar.mdx": () =>
import("../content/docs/components/mini-calendar.mdx?collection=docs"),
"components/minimal-tiptap.mdx": () =>
import("../content/docs/components/minimal-tiptap.mdx?collection=docs"),
"components/motion-highlight.mdx": () =>
import("../content/docs/components/motion-highlight.mdx?collection=docs"),
"components/navigation-menu.mdx": () =>
import("../content/docs/components/navigation-menu.mdx?collection=docs"),
"components/pagination.mdx": () =>
import("../content/docs/components/pagination.mdx?collection=docs"),
"components/particles-background.mdx": () =>
import(
"../content/docs/components/particles-background.mdx?collection=docs"
),
"components/particles.mdx": () =>
import("../content/docs/components/particles.mdx?collection=docs"),
"components/pill.mdx": () =>
import("../content/docs/components/pill.mdx?collection=docs"),
"components/pin-list.mdx": () =>
import("../content/docs/components/pin-list.mdx?collection=docs"),
"components/pixel-image.mdx": () =>
import("../content/docs/components/pixel-image.mdx?collection=docs"),
"components/popover.mdx": () =>
import("../content/docs/components/popover.mdx?collection=docs"),
"components/progress.mdx": () =>
import("../content/docs/components/progress.mdx?collection=docs"),
"components/qr-code.mdx": () =>
import("../content/docs/components/qr-code.mdx?collection=docs"),
"components/radio-group.mdx": () =>
import("../content/docs/components/radio-group.mdx?collection=docs"),
"components/relative-time.mdx": () =>
import("../content/docs/components/relative-time.mdx?collection=docs"),
"components/resizable.mdx": () =>
import("../content/docs/components/resizable.mdx?collection=docs"),
"components/safari.mdx": () =>
import("../content/docs/components/safari.mdx?collection=docs"),
"components/sandbox.mdx": () =>
import("../content/docs/components/sandbox.mdx?collection=docs"),
"components/scroll-area.mdx": () =>
import("../content/docs/components/scroll-area.mdx?collection=docs"),
"components/search-and-toggle-navigation-bar.mdx": () =>
import(
"../content/docs/components/search-and-toggle-navigation-bar.mdx?collection=docs"
),
"components/select.mdx": () =>
import("../content/docs/components/select.mdx?collection=docs"),
"components/separator.mdx": () =>
import("../content/docs/components/separator.mdx?collection=docs"),
"components/sheet.mdx": () =>
import("../content/docs/components/sheet.mdx?collection=docs"),
"components/simple-navigation-bar.mdx": () =>
import(
"../content/docs/components/simple-navigation-bar.mdx?collection=docs"
),
"components/skeleton.mdx": () =>
import("../content/docs/components/skeleton.mdx?collection=docs"),
"components/slider.mdx": () =>
import("../content/docs/components/slider.mdx?collection=docs"),
"components/snippet.mdx": () =>
import("../content/docs/components/snippet.mdx?collection=docs"),
"components/sonner.mdx": () =>
import("../content/docs/components/sonner.mdx?collection=docs"),
"components/spinner.mdx": () =>
import("../content/docs/components/spinner.mdx?collection=docs"),
"components/stars-scrolling-wheel.mdx": () =>
import(
"../content/docs/components/stars-scrolling-wheel.mdx?collection=docs"
),
"components/status-dashboard-navigation-bar.mdx": () =>
import(
"../content/docs/components/status-dashboard-navigation-bar.mdx?collection=docs"
),
"components/status.mdx": () =>
import("../content/docs/components/status.mdx?collection=docs"),
"components/switch.mdx": () =>
import("../content/docs/components/switch.mdx?collection=docs"),
"components/table.mdx": () =>
import("../content/docs/components/table.mdx?collection=docs"),
"components/tabs.mdx": () =>
import("../content/docs/components/tabs.mdx?collection=docs"),
"components/tags.mdx": () =>
import("../content/docs/components/tags.mdx?collection=docs"),
"components/team-switcher-navigation-bar.mdx": () =>
import(
"../content/docs/components/team-switcher-navigation-bar.mdx?collection=docs"
),
"components/terminal.mdx": () =>
import("../content/docs/components/terminal.mdx?collection=docs"),
"components/textarea.mdx": () =>
import("../content/docs/components/textarea.mdx?collection=docs"),
"components/timeline.mdx": () =>
import("../content/docs/components/timeline.mdx?collection=docs"),
"components/toast.mdx": () =>
import("../content/docs/components/toast.mdx?collection=docs"),
"components/toggle-group.mdx": () =>
import("../content/docs/components/toggle-group.mdx?collection=docs"),
"components/toggle.mdx": () =>
import("../content/docs/components/toggle.mdx?collection=docs"),
"components/tooltip.mdx": () =>
import("../content/docs/components/tooltip.mdx?collection=docs"),
"components/two-tier-navigation-bar.mdx": () =>
import(
"../content/docs/components/two-tier-navigation-bar.mdx?collection=docs"
),
"components/typography.mdx": () =>
import("../content/docs/components/typography.mdx?collection=docs"),
"components/underline-navigation-bar.mdx": () =>
import(
"../content/docs/components/underline-navigation-bar.mdx?collection=docs"
),
"components/video-player.mdx": () =>
import("../content/docs/components/video-player.mdx?collection=docs"),
"white-label/index.mdx": () =>
import("../content/docs/white-label/index.mdx?collection=docs"),
"testing/index.mdx": () =>
import("../content/docs/testing/index.mdx?collection=docs"),
}),
}
export default browserCollections
-15
View File
@@ -1,15 +0,0 @@
// @ts-nocheck
import { dynamic } from "@hanzo/docs-mdx/runtime/dynamic"
import * as Config from "../source.config"
const create = await dynamic<
typeof Config,
import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
DocData: {}
}
>(
Config,
{ configPath: "source.config.ts", environment: "next", outDir: ".docs" },
{ doc: { passthroughs: ["extractedReferences"] } }
)
-393
View File
@@ -1,393 +0,0 @@
// @ts-nocheck
import { server } from "@hanzo/docs-mdx/runtime/server"
import * as __fd_glob_0 from "../content/docs/about.mdx?collection=docs"
import * as __fd_glob_9 from "../content/docs/ai/actions.mdx?collection=docs"
import * as __fd_glob_10 from "../content/docs/ai/agents.mdx?collection=docs"
import * as __fd_glob_11 from "../content/docs/ai/assistant.mdx?collection=docs"
import * as __fd_glob_12 from "../content/docs/ai/chat.mdx?collection=docs"
import * as __fd_glob_13 from "../content/docs/ai/code.mdx?collection=docs"
import * as __fd_glob_14 from "../content/docs/ai/index.mdx?collection=docs"
import * as __fd_glob_15 from "../content/docs/ai/models.mdx?collection=docs"
import * as __fd_glob_16 from "../content/docs/ai/playground.mdx?collection=docs"
import * as __fd_glob_17 from "../content/docs/ai/tools.mdx?collection=docs"
import * as __fd_glob_18 from "../content/docs/ai/vision.mdx?collection=docs"
import * as __fd_glob_19 from "../content/docs/ai/voice-settings.mdx?collection=docs"
import * as __fd_glob_20 from "../content/docs/ai/voice.mdx?collection=docs"
import * as __fd_glob_21 from "../content/docs/block-examples/billing.mdx?collection=docs"
import * as __fd_glob_22 from "../content/docs/block-examples/compose-spec.mdx?collection=docs"
import * as __fd_glob_23 from "../content/docs/block-examples/newsletter.mdx?collection=docs"
import * as __fd_glob_24 from "../content/docs/block-examples/pricing.mdx?collection=docs"
import * as __fd_glob_1 from "../content/docs/changelog.mdx?collection=docs"
import * as __fd_glob_2 from "../content/docs/cli.mdx?collection=docs"
import * as __fd_glob_3 from "../content/docs/components-json.mdx?collection=docs"
import * as __fd_glob_4 from "../content/docs/components.mdx?collection=docs"
import * as __fd_glob_46 from "../content/docs/components/3d-card.mdx?collection=docs"
import * as __fd_glob_47 from "../content/docs/components/3d-marquee.mdx?collection=docs"
import * as __fd_glob_48 from "../content/docs/components/3d-pin.mdx?collection=docs"
import * as __fd_glob_49 from "../content/docs/components/accordion.mdx?collection=docs"
import * as __fd_glob_50 from "../content/docs/components/advanced-navigation-bar.mdx?collection=docs"
import * as __fd_glob_51 from "../content/docs/components/ai-model-selector-navigation-bar.mdx?collection=docs"
import * as __fd_glob_52 from "../content/docs/components/alert-dialog.mdx?collection=docs"
import * as __fd_glob_53 from "../content/docs/components/alert.mdx?collection=docs"
import * as __fd_glob_54 from "../content/docs/components/android.mdx?collection=docs"
import * as __fd_glob_55 from "../content/docs/components/animated-beam.mdx?collection=docs"
import * as __fd_glob_56 from "../content/docs/components/animated-cursor.mdx?collection=docs"
import * as __fd_glob_57 from "../content/docs/components/animated-testimonials.mdx?collection=docs"
import * as __fd_glob_58 from "../content/docs/components/animated-tooltip.mdx?collection=docs"
import * as __fd_glob_59 from "../content/docs/components/animation-components.mdx?collection=docs"
import * as __fd_glob_60 from "../content/docs/components/announcement.mdx?collection=docs"
import * as __fd_glob_61 from "../content/docs/components/app-switcher-navigation-bar.mdx?collection=docs"
import * as __fd_glob_62 from "../content/docs/components/apple-cards-carousel.mdx?collection=docs"
import * as __fd_glob_63 from "../content/docs/components/apple-hello-effect.mdx?collection=docs"
import * as __fd_glob_64 from "../content/docs/components/aspect-ratio.mdx?collection=docs"
import * as __fd_glob_65 from "../content/docs/components/avatar-group.mdx?collection=docs"
import * as __fd_glob_66 from "../content/docs/components/avatar.mdx?collection=docs"
import * as __fd_glob_67 from "../content/docs/components/badge.mdx?collection=docs"
import * as __fd_glob_68 from "../content/docs/components/banner.mdx?collection=docs"
import * as __fd_glob_69 from "../content/docs/components/breadcrumb-and-filters-navigation-bar.mdx?collection=docs"
import * as __fd_glob_70 from "../content/docs/components/breadcrumb-navigation-bar.mdx?collection=docs"
import * as __fd_glob_71 from "../content/docs/components/breadcrumb.mdx?collection=docs"
import * as __fd_glob_72 from "../content/docs/components/button-group.mdx?collection=docs"
import * as __fd_glob_73 from "../content/docs/components/button.mdx?collection=docs"
import * as __fd_glob_74 from "../content/docs/components/calendar.mdx?collection=docs"
import * as __fd_glob_75 from "../content/docs/components/card.mdx?collection=docs"
import * as __fd_glob_76 from "../content/docs/components/carousel.mdx?collection=docs"
import * as __fd_glob_77 from "../content/docs/components/centered-logo-navigation-bar.mdx?collection=docs"
import * as __fd_glob_78 from "../content/docs/components/checkbox.mdx?collection=docs"
import * as __fd_glob_79 from "../content/docs/components/choicebox.mdx?collection=docs"
import * as __fd_glob_80 from "../content/docs/components/code-block.mdx?collection=docs"
import * as __fd_glob_81 from "../content/docs/components/code-editor.mdx?collection=docs"
import * as __fd_glob_82 from "../content/docs/components/code-tabs.mdx?collection=docs"
import * as __fd_glob_83 from "../content/docs/components/collaboration-navigation-bar.mdx?collection=docs"
import * as __fd_glob_84 from "../content/docs/components/collapsible.mdx?collection=docs"
import * as __fd_glob_85 from "../content/docs/components/color-picker.mdx?collection=docs"
import * as __fd_glob_86 from "../content/docs/components/combobox.mdx?collection=docs"
import * as __fd_glob_87 from "../content/docs/components/command.mdx?collection=docs"
import * as __fd_glob_88 from "../content/docs/components/communication-navigation-bar.mdx?collection=docs"
import * as __fd_glob_89 from "../content/docs/components/comparison.mdx?collection=docs"
import * as __fd_glob_90 from "../content/docs/components/context-menu.mdx?collection=docs"
import * as __fd_glob_91 from "../content/docs/components/context-switcher-navigation-bar.mdx?collection=docs"
import * as __fd_glob_92 from "../content/docs/components/credit-card.mdx?collection=docs"
import * as __fd_glob_93 from "../content/docs/components/cursor.mdx?collection=docs"
import * as __fd_glob_94 from "../content/docs/components/dashboard-navigation-bar.mdx?collection=docs"
import * as __fd_glob_95 from "../content/docs/components/data-table.mdx?collection=docs"
import * as __fd_glob_96 from "../content/docs/components/date-picker.mdx?collection=docs"
import * as __fd_glob_97 from "../content/docs/components/desktop-hooks.mdx?collection=docs"
import * as __fd_glob_98 from "../content/docs/components/desktop-spotlight.mdx?collection=docs"
import * as __fd_glob_99 from "../content/docs/components/desktop-window.mdx?collection=docs"
import * as __fd_glob_100 from "../content/docs/components/desktop.mdx?collection=docs"
import * as __fd_glob_101 from "../content/docs/components/dialog-stack.mdx?collection=docs"
import * as __fd_glob_102 from "../content/docs/components/dialog.mdx?collection=docs"
import * as __fd_glob_103 from "../content/docs/components/dock.mdx?collection=docs"
import * as __fd_glob_104 from "../content/docs/components/drawer.mdx?collection=docs"
import * as __fd_glob_105 from "../content/docs/components/dropdown-menu.mdx?collection=docs"
import * as __fd_glob_106 from "../content/docs/components/dropzone.mdx?collection=docs"
import * as __fd_glob_107 from "../content/docs/components/e-commerce-navigation-bar.mdx?collection=docs"
import * as __fd_glob_108 from "../content/docs/components/editor.mdx?collection=docs"
import * as __fd_glob_109 from "../content/docs/components/empty.mdx?collection=docs"
import * as __fd_glob_110 from "../content/docs/components/field.mdx?collection=docs"
import * as __fd_glob_111 from "../content/docs/components/form.mdx?collection=docs"
import * as __fd_glob_112 from "../content/docs/components/gantt.mdx?collection=docs"
import * as __fd_glob_113 from "../content/docs/components/glimpse.mdx?collection=docs"
import * as __fd_glob_114 from "../content/docs/components/grid-pattern.mdx?collection=docs"
import * as __fd_glob_115 from "../content/docs/components/hover-card.mdx?collection=docs"
import * as __fd_glob_116 from "../content/docs/components/icon-navigation-bar.mdx?collection=docs"
import * as __fd_glob_117 from "../content/docs/components/image-crop.mdx?collection=docs"
import * as __fd_glob_118 from "../content/docs/components/image-zoom.mdx?collection=docs"
import * as __fd_glob_119 from "../content/docs/components/index.mdx?collection=docs"
import * as __fd_glob_120 from "../content/docs/components/input-group.mdx?collection=docs"
import * as __fd_glob_121 from "../content/docs/components/input-otp.mdx?collection=docs"
import * as __fd_glob_122 from "../content/docs/components/input.mdx?collection=docs"
import * as __fd_glob_123 from "../content/docs/components/interactive-grid-pattern.mdx?collection=docs"
import * as __fd_glob_124 from "../content/docs/components/iphone-15-pro.mdx?collection=docs"
import * as __fd_glob_125 from "../content/docs/components/item.mdx?collection=docs"
import * as __fd_glob_126 from "../content/docs/components/kanban.mdx?collection=docs"
import * as __fd_glob_127 from "../content/docs/components/kbd.mdx?collection=docs"
import * as __fd_glob_128 from "../content/docs/components/label.mdx?collection=docs"
import * as __fd_glob_129 from "../content/docs/components/limelight-nav.mdx?collection=docs"
import * as __fd_glob_130 from "../content/docs/components/list.mdx?collection=docs"
import * as __fd_glob_131 from "../content/docs/components/macos-dock.mdx?collection=docs"
import * as __fd_glob_132 from "../content/docs/components/magnetic.mdx?collection=docs"
import * as __fd_glob_133 from "../content/docs/components/marquee.mdx?collection=docs"
import * as __fd_glob_134 from "../content/docs/components/menu-dock.mdx?collection=docs"
import * as __fd_glob_135 from "../content/docs/components/menubar.mdx?collection=docs"
import * as __fd_glob_136 from "../content/docs/components/message-dock.mdx?collection=docs"
import * as __fd_glob_137 from "../content/docs/components/mini-calendar.mdx?collection=docs"
import * as __fd_glob_138 from "../content/docs/components/minimal-tiptap.mdx?collection=docs"
import * as __fd_glob_139 from "../content/docs/components/motion-highlight.mdx?collection=docs"
import * as __fd_glob_140 from "../content/docs/components/navigation-menu.mdx?collection=docs"
import * as __fd_glob_141 from "../content/docs/components/pagination.mdx?collection=docs"
import * as __fd_glob_142 from "../content/docs/components/particles-background.mdx?collection=docs"
import * as __fd_glob_143 from "../content/docs/components/particles.mdx?collection=docs"
import * as __fd_glob_144 from "../content/docs/components/pill.mdx?collection=docs"
import * as __fd_glob_145 from "../content/docs/components/pin-list.mdx?collection=docs"
import * as __fd_glob_146 from "../content/docs/components/pixel-image.mdx?collection=docs"
import * as __fd_glob_147 from "../content/docs/components/popover.mdx?collection=docs"
import * as __fd_glob_148 from "../content/docs/components/progress.mdx?collection=docs"
import * as __fd_glob_149 from "../content/docs/components/qr-code.mdx?collection=docs"
import * as __fd_glob_150 from "../content/docs/components/radio-group.mdx?collection=docs"
import * as __fd_glob_151 from "../content/docs/components/relative-time.mdx?collection=docs"
import * as __fd_glob_152 from "../content/docs/components/resizable.mdx?collection=docs"
import * as __fd_glob_153 from "../content/docs/components/safari.mdx?collection=docs"
import * as __fd_glob_154 from "../content/docs/components/sandbox.mdx?collection=docs"
import * as __fd_glob_155 from "../content/docs/components/scroll-area.mdx?collection=docs"
import * as __fd_glob_156 from "../content/docs/components/search-and-toggle-navigation-bar.mdx?collection=docs"
import * as __fd_glob_157 from "../content/docs/components/select.mdx?collection=docs"
import * as __fd_glob_158 from "../content/docs/components/separator.mdx?collection=docs"
import * as __fd_glob_159 from "../content/docs/components/sheet.mdx?collection=docs"
import * as __fd_glob_160 from "../content/docs/components/simple-navigation-bar.mdx?collection=docs"
import * as __fd_glob_161 from "../content/docs/components/skeleton.mdx?collection=docs"
import * as __fd_glob_162 from "../content/docs/components/slider.mdx?collection=docs"
import * as __fd_glob_163 from "../content/docs/components/snippet.mdx?collection=docs"
import * as __fd_glob_164 from "../content/docs/components/sonner.mdx?collection=docs"
import * as __fd_glob_165 from "../content/docs/components/spinner.mdx?collection=docs"
import * as __fd_glob_166 from "../content/docs/components/stars-scrolling-wheel.mdx?collection=docs"
import * as __fd_glob_167 from "../content/docs/components/status-dashboard-navigation-bar.mdx?collection=docs"
import * as __fd_glob_168 from "../content/docs/components/status.mdx?collection=docs"
import * as __fd_glob_169 from "../content/docs/components/switch.mdx?collection=docs"
import * as __fd_glob_170 from "../content/docs/components/table.mdx?collection=docs"
import * as __fd_glob_171 from "../content/docs/components/tabs.mdx?collection=docs"
import * as __fd_glob_172 from "../content/docs/components/tags.mdx?collection=docs"
import * as __fd_glob_173 from "../content/docs/components/team-switcher-navigation-bar.mdx?collection=docs"
import * as __fd_glob_174 from "../content/docs/components/terminal.mdx?collection=docs"
import * as __fd_glob_175 from "../content/docs/components/textarea.mdx?collection=docs"
import * as __fd_glob_176 from "../content/docs/components/timeline.mdx?collection=docs"
import * as __fd_glob_177 from "../content/docs/components/toast.mdx?collection=docs"
import * as __fd_glob_178 from "../content/docs/components/toggle-group.mdx?collection=docs"
import * as __fd_glob_179 from "../content/docs/components/toggle.mdx?collection=docs"
import * as __fd_glob_180 from "../content/docs/components/tooltip.mdx?collection=docs"
import * as __fd_glob_181 from "../content/docs/components/two-tier-navigation-bar.mdx?collection=docs"
import * as __fd_glob_182 from "../content/docs/components/typography.mdx?collection=docs"
import * as __fd_glob_183 from "../content/docs/components/underline-navigation-bar.mdx?collection=docs"
import * as __fd_glob_184 from "../content/docs/components/video-player.mdx?collection=docs"
import * as __fd_glob_28 from "../content/docs/dark-mode/astro.mdx?collection=docs"
import * as __fd_glob_29 from "../content/docs/dark-mode/index.mdx?collection=docs"
import * as __fd_glob_30 from "../content/docs/dark-mode/next.mdx?collection=docs"
import * as __fd_glob_31 from "../content/docs/dark-mode/remix.mdx?collection=docs"
import * as __fd_glob_32 from "../content/docs/dark-mode/vite.mdx?collection=docs"
import * as __fd_glob_5 from "../content/docs/figma.mdx?collection=docs"
import * as __fd_glob_25 from "../content/docs/frameworks/index.mdx?collection=docs"
import * as __fd_glob_26 from "../content/docs/frameworks/react.mdx?collection=docs"
import * as __fd_glob_27 from "../content/docs/frameworks/vue.mdx?collection=docs"
import * as __fd_glob_33 from "../content/docs/guides/page-builder.mdx?collection=docs"
import * as __fd_glob_34 from "../content/docs/guides/visual-workflows.mdx?collection=docs"
import * as __fd_glob_35 from "../content/docs/hanzo-react/getting-started.mdx?collection=docs"
import * as __fd_glob_36 from "../content/docs/hanzo-react/hooks.mdx?collection=docs"
import * as __fd_glob_6 from "../content/docs/index.mdx?collection=docs"
import * as __fd_glob_37 from "../content/docs/installation/astro.mdx?collection=docs"
import * as __fd_glob_38 from "../content/docs/installation/gatsby.mdx?collection=docs"
import * as __fd_glob_39 from "../content/docs/installation/index.mdx?collection=docs"
import * as __fd_glob_40 from "../content/docs/installation/laravel.mdx?collection=docs"
import * as __fd_glob_41 from "../content/docs/installation/manual.mdx?collection=docs"
import * as __fd_glob_42 from "../content/docs/installation/next.mdx?collection=docs"
import * as __fd_glob_43 from "../content/docs/installation/remix.mdx?collection=docs"
import * as __fd_glob_44 from "../content/docs/installation/vite.mdx?collection=docs"
import * as __fd_glob_7 from "../content/docs/mcp.mdx?collection=docs"
import * as __fd_glob_45 from "../content/docs/packages/index.mdx?collection=docs"
import * as __fd_glob_186 from "../content/docs/testing/index.mdx?collection=docs"
import * as __fd_glob_8 from "../content/docs/theming.mdx?collection=docs"
import * as __fd_glob_185 from "../content/docs/white-label/index.mdx?collection=docs"
import type * as Config from "../source.config"
const create = server<
typeof Config,
import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
DocData: {}
}
>({ doc: { passthroughs: ["extractedReferences"] } })
export const docs = await create.docs(
"docs",
"content/docs",
{},
{
"about.mdx": __fd_glob_0,
"changelog.mdx": __fd_glob_1,
"cli.mdx": __fd_glob_2,
"components-json.mdx": __fd_glob_3,
"components.mdx": __fd_glob_4,
"figma.mdx": __fd_glob_5,
"index.mdx": __fd_glob_6,
"mcp.mdx": __fd_glob_7,
"theming.mdx": __fd_glob_8,
"ai/actions.mdx": __fd_glob_9,
"ai/agents.mdx": __fd_glob_10,
"ai/assistant.mdx": __fd_glob_11,
"ai/chat.mdx": __fd_glob_12,
"ai/code.mdx": __fd_glob_13,
"ai/index.mdx": __fd_glob_14,
"ai/models.mdx": __fd_glob_15,
"ai/playground.mdx": __fd_glob_16,
"ai/tools.mdx": __fd_glob_17,
"ai/vision.mdx": __fd_glob_18,
"ai/voice-settings.mdx": __fd_glob_19,
"ai/voice.mdx": __fd_glob_20,
"block-examples/billing.mdx": __fd_glob_21,
"block-examples/compose-spec.mdx": __fd_glob_22,
"block-examples/newsletter.mdx": __fd_glob_23,
"block-examples/pricing.mdx": __fd_glob_24,
"frameworks/index.mdx": __fd_glob_25,
"frameworks/react.mdx": __fd_glob_26,
"frameworks/vue.mdx": __fd_glob_27,
"dark-mode/astro.mdx": __fd_glob_28,
"dark-mode/index.mdx": __fd_glob_29,
"dark-mode/next.mdx": __fd_glob_30,
"dark-mode/remix.mdx": __fd_glob_31,
"dark-mode/vite.mdx": __fd_glob_32,
"guides/page-builder.mdx": __fd_glob_33,
"guides/visual-workflows.mdx": __fd_glob_34,
"hanzo-react/getting-started.mdx": __fd_glob_35,
"hanzo-react/hooks.mdx": __fd_glob_36,
"installation/astro.mdx": __fd_glob_37,
"installation/gatsby.mdx": __fd_glob_38,
"installation/index.mdx": __fd_glob_39,
"installation/laravel.mdx": __fd_glob_40,
"installation/manual.mdx": __fd_glob_41,
"installation/next.mdx": __fd_glob_42,
"installation/remix.mdx": __fd_glob_43,
"installation/vite.mdx": __fd_glob_44,
"packages/index.mdx": __fd_glob_45,
"components/3d-card.mdx": __fd_glob_46,
"components/3d-marquee.mdx": __fd_glob_47,
"components/3d-pin.mdx": __fd_glob_48,
"components/accordion.mdx": __fd_glob_49,
"components/advanced-navigation-bar.mdx": __fd_glob_50,
"components/ai-model-selector-navigation-bar.mdx": __fd_glob_51,
"components/alert-dialog.mdx": __fd_glob_52,
"components/alert.mdx": __fd_glob_53,
"components/android.mdx": __fd_glob_54,
"components/animated-beam.mdx": __fd_glob_55,
"components/animated-cursor.mdx": __fd_glob_56,
"components/animated-testimonials.mdx": __fd_glob_57,
"components/animated-tooltip.mdx": __fd_glob_58,
"components/animation-components.mdx": __fd_glob_59,
"components/announcement.mdx": __fd_glob_60,
"components/app-switcher-navigation-bar.mdx": __fd_glob_61,
"components/apple-cards-carousel.mdx": __fd_glob_62,
"components/apple-hello-effect.mdx": __fd_glob_63,
"components/aspect-ratio.mdx": __fd_glob_64,
"components/avatar-group.mdx": __fd_glob_65,
"components/avatar.mdx": __fd_glob_66,
"components/badge.mdx": __fd_glob_67,
"components/banner.mdx": __fd_glob_68,
"components/breadcrumb-and-filters-navigation-bar.mdx": __fd_glob_69,
"components/breadcrumb-navigation-bar.mdx": __fd_glob_70,
"components/breadcrumb.mdx": __fd_glob_71,
"components/button-group.mdx": __fd_glob_72,
"components/button.mdx": __fd_glob_73,
"components/calendar.mdx": __fd_glob_74,
"components/card.mdx": __fd_glob_75,
"components/carousel.mdx": __fd_glob_76,
"components/centered-logo-navigation-bar.mdx": __fd_glob_77,
"components/checkbox.mdx": __fd_glob_78,
"components/choicebox.mdx": __fd_glob_79,
"components/code-block.mdx": __fd_glob_80,
"components/code-editor.mdx": __fd_glob_81,
"components/code-tabs.mdx": __fd_glob_82,
"components/collaboration-navigation-bar.mdx": __fd_glob_83,
"components/collapsible.mdx": __fd_glob_84,
"components/color-picker.mdx": __fd_glob_85,
"components/combobox.mdx": __fd_glob_86,
"components/command.mdx": __fd_glob_87,
"components/communication-navigation-bar.mdx": __fd_glob_88,
"components/comparison.mdx": __fd_glob_89,
"components/context-menu.mdx": __fd_glob_90,
"components/context-switcher-navigation-bar.mdx": __fd_glob_91,
"components/credit-card.mdx": __fd_glob_92,
"components/cursor.mdx": __fd_glob_93,
"components/dashboard-navigation-bar.mdx": __fd_glob_94,
"components/data-table.mdx": __fd_glob_95,
"components/date-picker.mdx": __fd_glob_96,
"components/desktop-hooks.mdx": __fd_glob_97,
"components/desktop-spotlight.mdx": __fd_glob_98,
"components/desktop-window.mdx": __fd_glob_99,
"components/desktop.mdx": __fd_glob_100,
"components/dialog-stack.mdx": __fd_glob_101,
"components/dialog.mdx": __fd_glob_102,
"components/dock.mdx": __fd_glob_103,
"components/drawer.mdx": __fd_glob_104,
"components/dropdown-menu.mdx": __fd_glob_105,
"components/dropzone.mdx": __fd_glob_106,
"components/e-commerce-navigation-bar.mdx": __fd_glob_107,
"components/editor.mdx": __fd_glob_108,
"components/empty.mdx": __fd_glob_109,
"components/field.mdx": __fd_glob_110,
"components/form.mdx": __fd_glob_111,
"components/gantt.mdx": __fd_glob_112,
"components/glimpse.mdx": __fd_glob_113,
"components/grid-pattern.mdx": __fd_glob_114,
"components/hover-card.mdx": __fd_glob_115,
"components/icon-navigation-bar.mdx": __fd_glob_116,
"components/image-crop.mdx": __fd_glob_117,
"components/image-zoom.mdx": __fd_glob_118,
"components/index.mdx": __fd_glob_119,
"components/input-group.mdx": __fd_glob_120,
"components/input-otp.mdx": __fd_glob_121,
"components/input.mdx": __fd_glob_122,
"components/interactive-grid-pattern.mdx": __fd_glob_123,
"components/iphone-15-pro.mdx": __fd_glob_124,
"components/item.mdx": __fd_glob_125,
"components/kanban.mdx": __fd_glob_126,
"components/kbd.mdx": __fd_glob_127,
"components/label.mdx": __fd_glob_128,
"components/limelight-nav.mdx": __fd_glob_129,
"components/list.mdx": __fd_glob_130,
"components/macos-dock.mdx": __fd_glob_131,
"components/magnetic.mdx": __fd_glob_132,
"components/marquee.mdx": __fd_glob_133,
"components/menu-dock.mdx": __fd_glob_134,
"components/menubar.mdx": __fd_glob_135,
"components/message-dock.mdx": __fd_glob_136,
"components/mini-calendar.mdx": __fd_glob_137,
"components/minimal-tiptap.mdx": __fd_glob_138,
"components/motion-highlight.mdx": __fd_glob_139,
"components/navigation-menu.mdx": __fd_glob_140,
"components/pagination.mdx": __fd_glob_141,
"components/particles-background.mdx": __fd_glob_142,
"components/particles.mdx": __fd_glob_143,
"components/pill.mdx": __fd_glob_144,
"components/pin-list.mdx": __fd_glob_145,
"components/pixel-image.mdx": __fd_glob_146,
"components/popover.mdx": __fd_glob_147,
"components/progress.mdx": __fd_glob_148,
"components/qr-code.mdx": __fd_glob_149,
"components/radio-group.mdx": __fd_glob_150,
"components/relative-time.mdx": __fd_glob_151,
"components/resizable.mdx": __fd_glob_152,
"components/safari.mdx": __fd_glob_153,
"components/sandbox.mdx": __fd_glob_154,
"components/scroll-area.mdx": __fd_glob_155,
"components/search-and-toggle-navigation-bar.mdx": __fd_glob_156,
"components/select.mdx": __fd_glob_157,
"components/separator.mdx": __fd_glob_158,
"components/sheet.mdx": __fd_glob_159,
"components/simple-navigation-bar.mdx": __fd_glob_160,
"components/skeleton.mdx": __fd_glob_161,
"components/slider.mdx": __fd_glob_162,
"components/snippet.mdx": __fd_glob_163,
"components/sonner.mdx": __fd_glob_164,
"components/spinner.mdx": __fd_glob_165,
"components/stars-scrolling-wheel.mdx": __fd_glob_166,
"components/status-dashboard-navigation-bar.mdx": __fd_glob_167,
"components/status.mdx": __fd_glob_168,
"components/switch.mdx": __fd_glob_169,
"components/table.mdx": __fd_glob_170,
"components/tabs.mdx": __fd_glob_171,
"components/tags.mdx": __fd_glob_172,
"components/team-switcher-navigation-bar.mdx": __fd_glob_173,
"components/terminal.mdx": __fd_glob_174,
"components/textarea.mdx": __fd_glob_175,
"components/timeline.mdx": __fd_glob_176,
"components/toast.mdx": __fd_glob_177,
"components/toggle-group.mdx": __fd_glob_178,
"components/toggle.mdx": __fd_glob_179,
"components/tooltip.mdx": __fd_glob_180,
"components/two-tier-navigation-bar.mdx": __fd_glob_181,
"components/typography.mdx": __fd_glob_182,
"components/underline-navigation-bar.mdx": __fd_glob_183,
"components/video-player.mdx": __fd_glob_184,
"white-label/index.mdx": __fd_glob_185,
"testing/index.mdx": __fd_glob_186,
}
)
-40
View File
@@ -1,40 +0,0 @@
// source.config.ts
import {
defineConfig,
defineDocs,
frontmatterSchema
} from "@hanzo/docs-mdx/config";
import rehypePrettyCode from "rehype-pretty-code";
import { z } from "zod";
var source_config_default = defineConfig({
mdxOptions: {
rehypePlugins: [
[
rehypePrettyCode,
{
theme: {
dark: "github-dark",
light: "github-light-default"
},
keepBackground: false
}
]
]
}
});
var docs = defineDocs({
dir: "content/docs",
docs: {
schema: frontmatterSchema.extend({
links: z.object({
doc: z.string().optional(),
api: z.string().optional()
}).optional(),
toc: z.boolean().optional()
})
}
});
export {
source_config_default as default,
docs
};
-18
View File
@@ -1,18 +0,0 @@
# .kodiak.toml
version = 1
[merge]
automerge_label = "automerge"
require_automerge_label = true
method = "squash"
delete_branch_on_merge = true
optimistic_updates = false
prioritize_ready_to_merge = true
notify_on_conflict = true
[merge.message]
title = "pull_request_title"
body = "pull_request_body"
include_pr_number = true
body_type = "markdown"
strip_html_comments = true
-11
View File
@@ -1,11 +0,0 @@
{
"mcpServers": {
"hanzo-ui": {
"command": "npx",
"args": ["@hanzo/ui@latest", "mcp"],
"env": {
"REGISTRY_URL": "https://ui.hanzo.ai/registry/registry.json"
}
}
}
}
-8
View File
@@ -1,8 +0,0 @@
dist
node_modules
.next
build
.contentlayer
apps/web/pages/api/registry.json
**/fixtures
deprecated
-114
View File
@@ -1,114 +0,0 @@
# Changelog
All notable changes to the Hanzo UI project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.2.0] - 2025-11-04
### Added
#### Page Builder
- **Visual Page Builder** (`/builder`) - Drag-and-drop interface for assembling pages from blocks
- Filterable block library with 1/4 scale previews
- Drag-to-reorder blocks in canvas
- Viewport controls (Desktop/Tablet/Mobile) for responsive preview
- Export to React TSX code
- Copy code to clipboard
- Download as .tsx file
- Deploy with Hanzo integration
- Real-time block rendering with dynamic imports
- Error handling and loading states for blocks
#### Syntax Highlighting
- **Shiki Integration** - Build-time syntax highlighting via Fumadocs
- GitHub Light/Dark themes for automatic light/dark mode support
- Multi-color syntax highlighting for all code blocks
- Copy-to-clipboard button on code blocks
- Line numbers and line highlighting support
- File name labels via meta strings
- Zero client-side JavaScript (all highlighting is static HTML)
#### New Blocks
- **Newsletter Blocks** (5 variants)
- `newsletter-01` - Simple newsletter signup
- `newsletter-02` - Newsletter with description
- `newsletter-03` - Newsletter with image
- `newsletter-04` - Newsletter with testimonial
- `newsletter-05` - Full-width newsletter section
- **Blog Cards** - Modern blog post card layout
- **Photo Grid** - Responsive photo gallery grid
- **Showcase Blocks**
- `showcase-marketing-01` - Marketing website showcase
- `showcase-ecommerce-01` - E-commerce site showcase
- `showcase-app-ui-01` - App UI showcase
#### New AI Components
- **AI Voice Settings** - Voice configuration interface for AI assistants
- Voice model selection
- Speed, pitch, and volume controls
- Preview functionality
- Settings persistence
#### New Documentation
- **AI Components Documentation**
- AI Actions guide
- AI Agents guide
- AI Assistant guide
- AI Code editor guide
- AI Models selector guide
- AI Vision guide
- AI Voice Settings guide
- AI Voice guide
- **Blocks Documentation**
- Newsletter blocks guide with all variants
### Fixed
- **Block Display Component** - Fixed registry access pattern for proper block rendering
- Now uses `getBlock()` function with style parameter
- Proper fallback to `BlockWrapper` for blocks
- Correct handling of component vs block types
- **Stub Components** - Fixed three blocks with missing `@hanzo/ui/billing` dependency
- `invoice-manager-01` - Added placeholder with installation instructions
- `payment-settings-01` - Added placeholder with installation instructions
- `subscription-portal-01` - Added placeholder with installation instructions
- **Dynamic Block Loading** - Replaced iframe-based previews with direct component rendering
- Eliminates 404 errors in block previews
- Better error handling and loading states
- Proper scaling with transform CSS
### Changed
- **Page Builder Layout**
- Reduced sidebar width from w-80 to w-64 for more canvas space
- Removed right sidebar to maximize preview area
- Changed block preview scale from 1/2 to 1/4 in sidebar
- Removed gaps between stacked blocks using border-b pattern
- Added viewport width controls for responsive testing
### Technical Improvements
- **MDX Configuration** - Enhanced with Shiki rehype plugin options
- **MDX Components** - Integrated Fumadocs CodeBlock wrapper for code blocks
- **Build Process** - Improved registry build with proper error handling
- **Type Safety** - Updated component props to use React.ComponentPropsWithoutRef
## [0.1.0] - 2025-10-05
### Initial Release
- Base component library with 150+ components
- Two theme variants (default, new-york)
- 24+ viewport-sized blocks
- Documentation site with MDX support
- CLI for component installation
- External registry support
- Responsive design system
- Dark mode support
---
[0.2.0]: https://github.com/hanzoai/ui/releases/tag/v0.2.0
[0.1.0]: https://github.com/hanzoai/ui/releases/tag/v0.1.0
-254
View File
@@ -1,254 +0,0 @@
# Hanzo Identity Registration System
A decentralized identity registration system for Hanzo, Lux, and Zoo networks.
## Overview
The identity system allows users to claim unique identities across multiple blockchain networks with a tiered pricing structure based on name length.
## Features
- **Multi-Network Support**: Register identities on Hanzo, Lux, and Zoo (mainnet and testnet)
- **Tiered Pricing**:
- Single character (1): 100,000 AI
- Two characters (2): 10,000 AI
- Three characters (3): 1,000 AI
- Four characters (4): 100 AI
- Five or more (5+): 10 AI
- **Referral System**: 50% discount with valid referrer
- **Web3 Wallet Integration**: RainbowKit for seamless wallet connection
- **Real-time Validation**: Check name availability and format validation
- **Responsive UI**: Modern, accessible interface built with Radix UI
## Project Structure
```
/Users/z/work/hanzo/ui/app/
├── app/(app)/identity/page.tsx # Identity registration page
├── components/web3-provider.tsx # Web3/Wagmi provider wrapper
├── lib/
│ ├── wagmi.ts # Wagmi config with custom networks
│ └── contracts.ts # Contract ABIs and addresses
└── .env.example # Environment variable template
```
## Smart Contracts
Located in `/Users/z/work/lux/standard/src/`:
- **HanzoRegistry.sol**: Core identity registration contract
- Manages identity claims
- Handles staking requirements
- Supports delegation and referrals
- Implements reward distribution
- **AIToken.sol**: ERC20 governance token
- Staking and vesting functionality
- Voting power for governance
- Deflationary burn mechanism
- **AIFaucet.sol**: Token distribution faucet
- 100 AI tokens per authenticated user
- 24-hour cooldown period
- Daily limits and rate limiting
## Setup
### 1. Install Dependencies
```bash
cd /Users/z/work/hanzo/ui/app
pnpm install
```
### 2. Configure Environment
Copy `.env.example` to `.env.local`:
```bash
cp .env.example .env.local
```
Get a WalletConnect Project ID from https://cloud.walletconnect.com/ and update:
```
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id_here
```
### 3. Deploy Contracts
Deploy the smart contracts to your target networks:
```bash
cd /Users/z/work/lux/standard
# For Hanzo Mainnet
forge script script/DeployIdentitySystem.s.sol:DeployIdentitySystem \
--rpc-url https://rpc.hanzo.ai \
--broadcast
# For Hanzo Testnet
forge script script/DeployIdentitySystem.s.sol:DeployIdentitySystem \
--rpc-url https://testnet-rpc.hanzo.ai \
--broadcast
# For Lux Mainnet
forge script script/DeployIdentitySystem.s.sol:DeployIdentitySystem \
--rpc-url https://api.lux.network/v1/bc/C/rpc \
--broadcast
# For Zoo Mainnet
forge script script/DeployIdentitySystem.s.sol:DeployIdentitySystem \
--rpc-url https://rpc.zoo.network \
--broadcast
```
### 4. Update Contract Addresses
After deployment, update the contract addresses in `lib/contracts.ts`:
```typescript
export const CONTRACT_ADDRESSES: Record<number, { registry: `0x${string}`; token: `0x${string}` }> = {
36963: { // Hanzo Mainnet
registry: '0xYourRegistryAddress',
token: '0xYourTokenAddress',
},
// ... update other networks
}
```
### 5. Run Development Server
```bash
pnpm dev
```
Visit `http://localhost:3333/identity` to access the identity registration page.
## Networks
| Network | Chain ID | RPC URL | Explorer |
|---------|----------|---------|----------|
| Hanzo Mainnet | 36963 | https://rpc.hanzo.ai | https://explorer.hanzo.ai |
| Hanzo Testnet | 36962 | https://testnet-rpc.hanzo.ai | https://testnet-explorer.hanzo.ai |
| Lux Mainnet | 96369 | https://api.lux.network/v1/bc/C/rpc | https://explorer.lux.network |
| Lux Testnet | 96368 | https://testnet-api.lux.network/v1/bc/C/rpc | https://testnet-explorer.lux.network |
| Zoo Mainnet | 200200 | https://rpc.zoo.network | https://explorer.zoo.network |
| Zoo Testnet | 200201 | https://testnet-rpc.zoo.network | https://testnet-explorer.zoo.network |
## Identity Format
Identities follow the format: `@username.network/profile/type/name`
- **Username**: Letters, numbers, dots, and underscores
- **Network**: Automatically determined by connected chain
- `.hanzo` or `.ai` for Hanzo mainnet
- `.hanzo-testnet` for Hanzo testnet
- `.lux` for Lux mainnet
- `.lux-testnet` for Lux testnet
- `.zoo` for Zoo mainnet
- `.zoo-testnet` for Zoo testnet
- **Profile**: Optional profile path (default: `/profile`)
## Faucet Integration
Users can get AI tokens from the faucet to register identities:
- **Location**: `/faucet` page
- **Amount**: 100 AI tokens per authenticated user
- **Cooldown**: 24 hours between drips
- **Networks**: Hanzo mainnet and testnet only
Configuration in `/Users/z/work/hanzo/faucet/config.json`
## Tech Stack
- **Framework**: Next.js 16 (App Router)
- **Styling**: Tailwind CSS 4
- **UI Components**: Radix UI primitives
- **Web3**: Wagmi, Viem, RainbowKit
- **State Management**: TanStack Query
- **Smart Contracts**: Solidity, Foundry
- **Networks**: Hanzo, Lux, Zoo (mainnet + testnet)
## Development
### Testing Locally
1. Use a testnet for development (Hanzo Testnet recommended)
2. Connect your wallet to the testnet
3. Get test AI tokens from the faucet
4. Test identity registration flow
### Building for Production
```bash
pnpm build
```
### Type Checking
```bash
pnpm typecheck
```
### Linting
```bash
pnpm lint
pnpm lint:fix
```
## Contract Deployment Scripts
Located in `/Users/z/work/lux/standard/script/`:
- `DeployIdentitySystem.s.sol`: Deploys complete identity system
- Deploys AIToken with treasury allocation
- Deploys HanzoNft for identity binding
- Deploys HanzoRegistry with UUPS proxy
- Deploys AIFaucet with 100 AI drip amount
- Configures initial permissions and transfers
## Security Considerations
- All contracts use OpenZeppelin's upgradeable patterns
- Registry uses UUPS proxy for upgradeability
- Access control with Ownable2Step for safe ownership transfer
- Pausable functionality for emergency stops
- Rate limiting on faucet to prevent abuse
- Input validation for identity names
- Proper allowance checks before token transfers
## Troubleshooting
### "Insufficient balance" error
- Get AI tokens from the faucet at `/faucet`
- Ensure you're connected to the correct network
### "Name already taken" error
- Try a different identity name
- Check if the name is available on your current network
### "Invalid format" error
- Use only letters, numbers, dots, and underscores
- No spaces or special characters allowed
### Contract not deployed
- Ensure contracts are deployed to your connected network
- Update contract addresses in `lib/contracts.ts`
- Check RPC URL is correct for your network
## Contributing
When adding new features:
1. Update contracts in `/Users/z/work/lux/standard/src/`
2. Update ABIs in `lib/contracts.ts`
3. Update UI in `app/(app)/identity/page.tsx`
4. Add tests for new functionality
5. Update this README with new features
## License
MIT
-551
View File
@@ -1,551 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository Overview
This is the **Hanzo UI App** - the documentation and showcase site for the @hanzo/ui component library. It's built with Next.js 15.3.1, React 19, and Fumadocs for documentation. The site serves as both documentation and a living demo of 149+ components.
**Key URLs:**
- Production: https://ui.hanzo.ai
- Local Dev: http://localhost:3003
- Repo: github.com/hanzoai/ui (monorepo root is parent directory)
## Essential Commands
### Development
```bash
# Start dev server (port 3003)
pnpm dev
# Build site (includes registry build + Next.js build)
pnpm build
# Build registry only (generates JSON files for CLI)
pnpm registry:build
# Start production server (port 3001)
pnpm start
```
### Code Quality
```bash
# Lint
pnpm lint
pnpm lint:fix
# Type check
pnpm typecheck
# Format
pnpm format:write
pnpm format:check
```
### Registry Management
```bash
# Validate external registries
pnpm validate:registries
# Capture screenshots (for component previews)
pnpm registry:capture
```
## Architecture Overview
### Three-Layer System
**1. Components** (`/registry/{default,new-york}/ui/`)
- Single UI elements (Button, Input, Card, Dialog)
- Reusable primitives that compose into larger UIs
- Two theme variants: default and new-york
- 149 components total (115 implemented, 34 stubs)
**2. Examples** (`/registry/{default,new-york}/example/`)
- Usage demonstrations for each component
- Shows component in isolation with typical props
- Used in documentation via `<ComponentPreview name="..." />`
**3. Blocks** (`/registry/{default,new-york}/blocks/`)
- Viewport-sized sections (Dashboard, Hero, Login, Pricing)
- Compose multiple components into full-page layouts
- Production-ready templates users can copy
- 24+ blocks including dashboard-01, sidebar-07, login-03
### Registry Build System
The registry is the **core of the component distribution system**:
1. **Source Files**: Components in `/registry/{style}/ui/`
2. **Build Script**: `scripts/build-registry.mts` reads components and generates:
- JSON metadata files in `/public/registry/styles/{style}/{name}.json`
- Includes dependencies, files, type info
3. **CLI Consumption**: The `@hanzo/ui` CLI reads these JSON files to install components
4. **Documentation**: MDX files in `/content/docs/components/` reference the registry
**CRITICAL**: Always run `pnpm registry:build` before building the app if you modify components.
### Documentation System
- **Framework**: Fumadocs (replaced Contentlayer in v4 migration)
- **MDX Files**: `/content/docs/` contains all documentation
- **Components in MDX**: Special components available in MDX via `mdx-components.tsx`:
- `<ComponentPreview name="..." />` - Live component demo
- `<ComponentSource name="..." />` - Component source code
- `<CodeTabs>` - Installation method tabs (CLI/Manual)
- `<Steps>` - Step-by-step instructions
- **Navigation**: Configured in `/config/docs.ts`
### Page Builder (`/builder`)
New drag-drop visual builder for assembling pages from blocks:
- **Left Sidebar**: Filterable block library
- **Canvas**: Drag-drop assembly area with reordering
- **Export**: Generates React TSX code
- **Tech**: @dnd-kit for drag-drop, SortableContext for reordering
Access at `/builder` route.
## Key File Locations
### Component Development
```
registry/default/ui/{component}.tsx # Component implementation
registry/default/example/{component}-demo.tsx # Usage example
content/docs/components/{component}.mdx # Documentation
```
### Configuration
```
config/site.ts # Site metadata, links
config/docs.ts # Documentation navigation structure
tailwind.config.cjs # Tailwind configuration
components.json # hanzo CLI configuration
```
### Build & Scripts
```
scripts/build-registry.mts # Registry JSON generator
scripts/validate-registries.mts # External registry validator
__registry__/index.tsx # Auto-generated registry index
```
## Component Creation Workflow
### 1. Create Component Files
```bash
# Create in both variants
touch registry/default/ui/my-component.tsx
touch registry/new-york/ui/my-component.tsx
# Create demo
touch registry/default/example/my-component-demo.tsx
```
### 2. Implement Component
Follow the standard pattern:
```tsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
interface MyComponentProps extends React.HTMLAttributes<HTMLDivElement> {
variant?: "default" | "outline"
}
const MyComponent = React.forwardRef<HTMLDivElement, MyComponentProps>(
({ className, variant = "default", ...props }, ref) => {
return (
<div
ref={ref}
className={cn("base-classes", className)}
{...props}
/>
)
}
)
MyComponent.displayName = "MyComponent"
export { MyComponent }
```
### 3. Create Documentation
```bash
# Create MDX file
touch content/docs/components/my-component.mdx
```
Include:
- `<ComponentPreview name="my-component-demo" />`
- Installation instructions with `<CodeTabs>`
- Usage examples
- API table with props
### 4. Add to Navigation
Edit `config/docs.ts` to add component to sidebar navigation.
### 5. Build Registry
```bash
pnpm registry:build
```
### 6. Test
```bash
pnpm dev
# Visit http://localhost:3003/docs/components/my-component
```
## Registry Architecture
The registry system enables the `npx hanzo-ui@latest add {component}` CLI workflow:
**Registry Entry Structure:**
```json
{
"name": "button",
"type": "components:ui",
"files": ["registry/default/ui/button.tsx"],
"dependencies": ["class-variance-authority"],
"registryDependencies": ["utils"]
}
```
**External Registries** (`registries.json`):
- 35+ external component sources
- Examples: @aceternity, @magicui, @hanzo-editor
- Validated via `pnpm validate:registries`
## Theme System
**CSS Variables** (`/styles/globals.css`):
- Base theme colors defined in HSL
- Primary color: `210 100% 50%` (electric blue)
- Dark mode via `.dark` class
- All components use `hsl(var(--primary))` pattern
**Two Visual Styles:**
- **default**: More rounded, softer shadows
- **new-york**: Flatter, sharper, minimal
Both styles share the same component API, only visual styling differs.
## Build System
### Production Build Process
1. `pnpm registry:build` - Generate registry JSON files
2. `next build` - Build Next.js app
3. Static export to `/out` directory for GitHub Pages
### Local Development
- Uses Turbopack for fast HMR
- Port 3003 (configurable)
- MDX hot reload via Fumadocs
## Component vs Block Distinction
**Components** (149 total):
- Single-purpose UI elements
- Examples: Button, Input, Card, Dialog, Dropdown
- Used as building blocks
- Installed individually via CLI
**Blocks** (24+ available):
- Full viewport sections
- Examples: dashboard-01 (admin layout), login-03 (auth page), sidebar-07
- Compose multiple components
- Ready-to-use page sections
- Users copy entire blocks, not install via CLI
## Import Patterns
Components can be imported three ways:
**1. Package Import** (if using @hanzo/ui package):
```tsx
import { Button, Card } from "@hanzo/ui"
```
**2. Registry Import** (in documentation/examples):
```tsx
import { Button } from "@/registry/default/ui/button"
```
**3. Local Import** (after installing via CLI):
```tsx
import { Button } from "@/components/ui/button"
```
The registry uses `@/registry/{style}/` which gets rewritten to `@/components/` during installation.
## MDX Component System
Components available in all MDX files:
- `<ComponentPreview name="button-demo" />` - Live demo
- `<ComponentSource name="button" />` - Source code display
- `<ComponentExample>` - Example wrapper
- `<CodeTabs>` - Installation tabs (requires useConfig hook)
- `<Steps>` / `<Step>` - Step-by-step instructions
- `<Tabs>` / `<TabsList>` / `<TabsTrigger>` / `<TabsContent>` - Content tabs
- `<Callout>` - Info/warning callouts
## Testing
No formal test suite currently. Testing is done via:
1. Local dev server visual testing
2. Build process catches type errors
3. Registry validation ensures component metadata is correct
## Deployment
**GitHub Pages** (automatic):
- Workflow: `.github/workflows/deploy-gh-pages.yml`
- Triggers on push to main
- Builds app + pkg/ui package
- Deploys to ui.hanzo.ai
- Requires: npm-run-all, del-cli for dependency builds
**Important**: The GitHub Actions workflow builds the `pkg/ui` package first, then the app, to ensure registry has latest component code.
## Common Patterns
### Stub Components
Components marked for future implementation:
```tsx
export default function Component() {
return (
<div className="flex items-center justify-center p-8">
<p className="text-muted-foreground">Component coming soon</p>
</div>
)
}
```
Currently 34 stubs remaining (documented but not implemented).
### Component Dependencies
- Radix UI primitives for accessible base components
- `cn()` utility from `@/lib/utils` for className merging
- `cva` (class-variance-authority) for variant management
- Lucide React for icons
- Framer Motion for animations (some components)
### Registry Dependencies
Components can depend on other registry components:
```json
{
"registryDependencies": ["button", "dialog", "utils"]
}
```
These are auto-installed when user adds the component.
## Recent Architectural Changes (2025-10-05)
1. **Migrated from Contentlayer to Fumadocs** for MDX processing
2. **Upgraded to Next.js 15.3.1** with Turbopack
3. **React 19** with new JSX transform
4. **Added @dnd-kit** for page builder drag-drop
5. **Synced with hanzo/ui v3.4.0** (7 new components: button-group, empty, field, input-group, item, kbd, spinner)
6. **Electric blue primary color** (210 100% 50%)
7. **Page builder feature** at `/builder` route
## Notes for AI Assistants
- **Registry must rebuild** after component changes: `pnpm registry:build`
- **Two theme variants** must be kept in sync: default and new-york
- **Import paths** in components use `@/registry/{style}/` which becomes `@/components/` after CLI install
- **MDX components** are defined in `mdx-components.tsx`, not auto-imported
- **Blocks are different** from components - they're full-page sections, not installable via CLI
- **GitHub Pages deployment** requires building pkg/ui package first (workflow handles this)
## LLM API Route (Added 2025-11-05)
The `/llm` API route serves MDX documentation optimized for LLM consumption by replacing `<ComponentPreview>` tags with actual component source code.
### Files Created
**`/lib/llm.ts`**:
- `processMdxForLLMs(content, style)` - Transforms MDX for LLMs
- Finds `<ComponentPreview name="..." />` tags in MDX
- Replaces with actual component source code in markdown code blocks
- Rewrites import paths from `@/registry/${style}/` to `@/components/`
- Removes `export default` in favor of named exports
**`/app/(app)/llm/[[...slug]]/route.ts`**:
- API route handler using Next.js catch-all routing
- `GET` handler that processes MDX files via `processMdxForLLMs()`
- Returns Content-Type: `text/markdown; charset=utf-8`
- Supports static generation via `generateStaticParams()`
- Uses `revalidate = false` for static export
### Key Adaptations from shadcn/ui
1. **Dynamic Style Support**: Changed from hardcoded `@/registry/new-york-v4/` to dynamic `@/registry/${style}/`
2. **Multi-Style System**: Works with both "default" and "new-york" themes
3. **Active Style Detection**: Uses `getActiveStyle()` to determine current theme
### Usage
Access documentation via:
```bash
# Get LLM-optimized docs for a component
curl http://localhost:3003/llm/docs/components/button
# Returns MDX with inline source code instead of component previews
```
## Deviation Report: shadcn/ui v4 vs Hanzo UI
### Directory Structure Comparison
**Copied from shadcn/ui v4**:
-`/blocks` - Block showcase page with filtering and search
-`/charts` - Chart component demonstrations
-`/colors` - Tailwind color palette reference
-`/docs` - Component documentation
-`/examples` - Component usage examples
-`/llm` - LLM-optimized API endpoint
-`/themes` - Theme customization page
**Hanzo-Specific Features** (not in shadcn):
- 🆕 `/ai` - AI assistant integration page
- 🆕 `/builder` - Visual page builder with drag-drop
- 🆕 `/components` - Component category pages
- 🆕 `/compose` - Composition editor
- 🆕 `/health` - System health check
- 🆕 `/mcp` - Model Context Protocol integration
- 🆕 `/sink` - Kitchen sink / testing page
- 🆕 `/theme-generator` - Interactive theme generator
### Key Implementation Differences
#### 1. Path Handling
- **shadcn**: Hardcoded `@/registry/new-york-v4/` paths
- **Hanzo**: Dynamic `@/registry/${style}/` supporting multiple themes
#### 2. Style System
- **shadcn**: Primary style is "new-york-v4"
- **Hanzo**: Primary style is "default", with "new-york" as alternative
- **Impact**: All copied files had paths adapted: `new-york-v4``default`
#### 3. Branding
- **shadcn**: GitHub logo, shadcn branding
- **Hanzo**: Hanzo logo (OpenInHButton), Hanzo branding
- **Files Modified**:
- `components/open-in-hanzo-button.tsx``components/open-in-h-button.tsx`
- Hanzo logo (H icon) replaces GitHub logo
#### 4. Registry Structure
- **shadcn**: `Index[name]` - flat structure
- **Hanzo**: `Index[style][name]` - nested by style
- **Impact**: Required updates to registry accessor functions
#### 5. Component Count
- **shadcn v4**: ~58 core components
- **Hanzo**: 149 components (115 implemented, 34 stubs)
- **Unique to Hanzo**: 3D components, AI components, animation components, specialized navigation
### Files Requiring Ongoing Sync
When shadcn/ui v4 updates these, Hanzo should review for updates:
1. **Layout Files**:
- `app/(app)/blocks/layout.tsx`
- `app/(app)/colors/layout.tsx`
- `app/(app)/themes/layout.tsx`
2. **Page Files**:
- `app/(app)/blocks/page.tsx`
- `app/(app)/colors/page.tsx`
- `app/(app)/themes/page.tsx`
- `app/(app)/llm/[[...slug]]/route.ts`
3. **Supporting Components**:
- `components/blocks-nav.tsx`
- `components/colors-nav.tsx`
- `components/theme-customizer.tsx`
- `lib/llm.ts`
4. **Registry Functions**:
- `lib/registry.ts` - Any changes to registry access patterns
- `lib/blocks.ts` - Block metadata handling
### Known Issues Requiring Fixes
Per user feedback: "don't suppress errors just do shit right or cover up things"
**1. getActiveStyle Export Error** ✅ FIXED (2025-11-05):
- **Was**: Server logs showed "Export getActiveStyle doesn't exist in target module"
- **Root Cause**: Turbopack had issues with `async function getActiveStyle()` in `force-static` pages. The async nature conflicted with Turbopack's static analysis in force-static mode.
- **Fix**: Made `getActiveStyle()` synchronous by removing `async` keyword
- Updated `registry/styles.ts` line 14: removed `async` from function declaration
- Updated `app/(app)/blocks/page.tsx`: removed `async` from function and `await` from call
- Updated `app/(app)/llm/[[...slug]]/route.ts`: split Promise.all, called getActiveStyle() synchronously
- **Result**: ✅ NO MORE EXPORT ERRORS - Pages load cleanly with HTTP 200, no server errors
- **Note**: Function doesn't actually need to be async since it just returns styles[0]
**2. Client Component Boundary Warnings** ⚠️ LOW PRIORITY:
- **Symptom**: "Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with 'use server'"
- **Locations**: Multiple components in blocks, themes, colors pages (7 warnings with digest `638247047`)
- **Status**: WARNINGS ONLY - Not breaking, pages render correctly with HTTP 200
- **Impact**: No user-facing issues, functionality fully intact
- **Note**: These warnings also appear in shadcn/ui v4, suggesting they may be framework-level issues
- **Action**: Can investigate if desired, but not critical since pages work perfectly
**3. Tabs Hydration Issues** ✅ FIXED:
- **Was**: Tabs component had hydration errors due to SSR/client mismatch
- **Fix**: Updated `registry/default/ui/tabs.tsx` with proper refs and display names
- **Status**: ✅ RESOLVED
**4. macOS Dock Demo SSR Error** ✅ FIXED (2025-11-06):
- **Was**: Build error "Event handlers cannot be passed to Client Component props" on `/view/new-york/macos-dock-demo`
- **Root Cause**: `macos-dock-demo` component has onClick event handlers on DockItem components. During static prerendering with `force-static`, Next.js tries to serialize props but can't serialize functions.
- **Fix**: Added component to skip list in `generateStaticParams()` in `app/(view)/view/[name]/page.tsx`
- Components with event handlers that can't be serialized are excluded from static generation
- They can still be accessed dynamically at runtime
- **Result**: ✅ Build completes successfully, no SSR errors
- **Note**: Component works perfectly in dev mode and at runtime, just skips static prerendering
### Testing Coverage
**Pages Tested**:
-`/blocks` - All 5 featured blocks display correctly
-`/colors` - Full Tailwind palette with copy functionality
-`/themes` - Theme customizer with live preview
-`/llm` - API endpoint compiles and serves MDX
**Components Tested**:
-`<BlockDisplay>` - Renders blocks correctly
-`<BlocksNav>` - Navigation with search/filter
-`<ColorsNav>` - Color palette navigation
-`<OpenInHButton>` - Hanzo-branded button with logo
-`<Tabs>` - No hydration errors
### Maintenance Strategy
**When to Sync with shadcn/ui v4**:
1. Major version updates (v4.x → v4.y)
2. New features in /blocks, /themes, /colors pages
3. Security patches or bug fixes
4. New component additions to showcase pages
**How to Sync**:
1. Compare shadcn v4 file with Hanzo version
2. Identify changes in logic (not just branding)
3. Apply logic changes while preserving:
- Hanzo branding (OpenInHButton, logos)
- Path adaptations (new-york-v4 → default)
- Registry structure differences (Index[style][name])
4. Test thoroughly before deploying
5. Document changes in CLAUDE.md
### Future Work
**Improvements Needed**:
1. Fix getActiveStyle export error properly (not suppress)
2. Resolve Client Component boundary warnings
3. Add comprehensive test suite for copied pages
4. Set up automated diff checking against shadcn v4
5. Document all customizations in component headers
@@ -1,45 +0,0 @@
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export const description =
"A simple login form with email and password. The submit button says 'Sign in'."
export const iframeHeight = "600px"
export const containerClassName =
"w-full h-screen flex items-center justify-center px-4"
export default function LoginForm() {
return (
<Card className="w-full max-w-sm">
<CardHeader>
<CardTitle className="text-2xl">Login</CardTitle>
<CardDescription>
Enter your email below to login to your account.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" required />
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" required />
</div>
</CardContent>
<CardFooter>
<Button className="w-full">Sign in</Button>
</CardFooter>
</Card>
)
}
@@ -1,67 +0,0 @@
import Link from "next/link"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export const description =
"A login form with email and password. There's an option to login with Google and a link to sign up if you don't have an account."
export const iframeHeight = "600px"
export const containerClassName =
"w-full h-screen flex items-center justify-center px-4"
export default function LoginForm() {
return (
<Card className="mx-auto max-w-sm">
<CardHeader>
<CardTitle className="text-2xl">Login</CardTitle>
<CardDescription>
Enter your email below to login to your account
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<div className="flex items-center">
<Label htmlFor="password">Password</Label>
<Link href="#" className="ml-auto inline-block text-sm underline">
Forgot your password?
</Link>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" className="w-full">
Login
</Button>
<Button variant="outline" className="w-full">
Login with Google
</Button>
</div>
<div className="mt-4 text-center text-sm">
Don&apos;t have an account?{" "}
<Link href="#" className="underline">
Sign up
</Link>
</div>
</CardContent>
</Card>
)
}
@@ -1,72 +0,0 @@
import Link from "next/link"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export const description =
"A sign up form with first name, last name, email and password inside a card. There's an option to sign up with GitHub and a link to login if you already have an account"
export const iframeHeight = "600px"
export const containerClassName =
"w-full h-screen flex items-center justify-center px-4"
export default function LoginForm() {
return (
<Card className="mx-auto max-w-sm">
<CardHeader>
<CardTitle className="text-xl">Sign Up</CardTitle>
<CardDescription>
Enter your information to create an account
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-4">
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="first-name">First name</Label>
<Input id="first-name" placeholder="Max" required />
</div>
<div className="grid gap-2">
<Label htmlFor="last-name">Last name</Label>
<Input id="last-name" placeholder="Robinson" required />
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" />
</div>
<Button type="submit" className="w-full">
Create an account
</Button>
<Button variant="outline" className="w-full">
Sign up with GitHub
</Button>
</div>
<div className="mt-4 text-center text-sm">
Already have an account?{" "}
<Link href="#" className="underline">
Sign in
</Link>
</div>
</CardContent>
</Card>
)
}
@@ -1,75 +0,0 @@
import Image from "next/image"
import Link from "next/link"
import { Button } from "@/registry/default/ui/button"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export const description =
"A login page with two columns. The first column has the login form with email and password. There's a Forgot your passwork link and a link to sign up if you do not have an account. The second column has a cover image."
export const iframeHeight = "800px"
export const containerClassName = "w-full h-full p-4 lg:p-0"
export default function Dashboard() {
return (
<div className="w-full lg:grid lg:min-h-[600px] lg:grid-cols-2 xl:min-h-[800px]">
<div className="flex items-center justify-center py-12">
<div className="mx-auto grid w-[350px] gap-6">
<div className="grid gap-2 text-center">
<h1 className="text-3xl font-bold">Login</h1>
<p className="text-balance text-muted-foreground">
Enter your email below to login to your account
</p>
</div>
<div className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<div className="flex items-center">
<Label htmlFor="password">Password</Label>
<Link
href="/forgot-password"
className="ml-auto inline-block text-sm underline"
>
Forgot your password?
</Link>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" className="w-full">
Login
</Button>
<Button variant="outline" className="w-full">
Login with Google
</Button>
</div>
<div className="mt-4 text-center text-sm">
Don&apos;t have an account?{" "}
<Link href="#" className="underline">
Sign up
</Link>
</div>
</div>
</div>
<div className="hidden bg-muted lg:block">
<Image
src="/placeholder.svg"
alt="Authentication page cover image"
width="1920"
height="1080"
className="h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
loading="lazy"
/>
</div>
</div>
)
}
@@ -1,21 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar01() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Calendar
mode="single"
defaultMonth={date}
selected={date}
onSelect={setDate}
className="rounded-lg border shadow-sm"
/>
)
}
@@ -1,22 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar02() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Calendar
mode="single"
defaultMonth={date}
numberOfMonths={2}
selected={date}
onSelect={setDate}
className="rounded-lg border shadow-sm"
/>
)
}
@@ -1,25 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar03() {
const [dates, setDates] = React.useState<Date[]>([
new Date(2025, 5, 12),
new Date(2025, 6, 24),
])
return (
<Calendar
mode="multiple"
numberOfMonths={2}
defaultMonth={dates[0]}
required
selected={dates}
onSelect={setDates}
max={5}
className="rounded-lg border shadow-sm"
/>
)
}
@@ -1,23 +0,0 @@
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar04() {
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 9),
to: new Date(2025, 5, 26),
})
return (
<Calendar
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={setDateRange}
className="rounded-lg border shadow-sm"
/>
)
}
@@ -1,24 +0,0 @@
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar05() {
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 12),
to: new Date(2025, 6, 15),
})
return (
<Calendar
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={2}
className="rounded-lg border shadow-sm"
/>
)
}
@@ -1,30 +0,0 @@
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar06() {
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 12),
to: new Date(2025, 5, 26),
})
return (
<div className="flex min-w-0 flex-col gap-2">
<Calendar
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={1}
min={5}
className="rounded-lg border shadow-sm"
/>
<div className="text-muted-foreground text-center text-xs">
A minimum of 5 days is required
</div>
</div>
)
}
@@ -1,31 +0,0 @@
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar07() {
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 18),
to: new Date(2025, 6, 7),
})
return (
<div className="flex min-w-0 flex-col gap-2">
<Calendar
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={2}
min={2}
max={20}
className="rounded-lg border shadow-sm"
/>
<div className="text-muted-foreground text-center text-xs">
Your stay must be between 2 and 20 nights
</div>
</div>
)
}
@@ -1,24 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar08() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Calendar
mode="single"
defaultMonth={date}
selected={date}
onSelect={setDate}
disabled={{
before: new Date(2025, 5, 12),
}}
className="rounded-lg border shadow-sm"
/>
)
}
@@ -1,26 +0,0 @@
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar09() {
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 17),
to: new Date(2025, 5, 20),
})
return (
<Calendar
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={2}
disabled={{ dayOfWeek: [0, 6] }}
className="rounded-lg border shadow-sm"
excludeDisabled
/>
)
}
@@ -1,50 +0,0 @@
"use client"
import * as React from "react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
export default function Calendar10() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
const [month, setMonth] = React.useState<Date | undefined>(new Date())
return (
<Card>
<CardHeader className="relative">
<CardTitle>Appointment</CardTitle>
<CardDescription>Find a date</CardDescription>
<Button
size="sm"
variant="outline"
className="absolute right-4 top-4"
onClick={() => {
setMonth(new Date())
setDate(new Date())
}}
>
Today
</Button>
</CardHeader>
<CardContent>
<Calendar
mode="single"
month={month}
onMonthChange={setMonth}
selected={date}
onSelect={setDate}
className="bg-transparent p-0"
/>
</CardContent>
</Card>
)
}
@@ -1,31 +0,0 @@
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar11() {
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 17),
to: new Date(2025, 5, 20),
})
return (
<div className="flex min-w-0 flex-col gap-2">
<Calendar
mode="range"
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={2}
startMonth={new Date(2025, 5, 1)}
endMonth={new Date(2025, 6, 31)}
disableNavigation
className="rounded-lg border shadow-sm"
/>
<div className="text-muted-foreground text-center text-xs">
We are open in June and July only.
</div>
</div>
)
}
@@ -1,77 +0,0 @@
"use client"
import * as React from "react"
import { type DateRange } from "react-day-picker"
import { enUS, es } from "react-day-picker/locale"
import { Calendar } from "@/registry/default/ui/calendar"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/default/ui/select"
const localizedStrings = {
en: {
title: "Book an appointment",
description: "Select the dates for your appointment",
},
es: {
title: "Reserva una cita",
description: "Selecciona las fechas para tu cita",
},
} as const
export default function Calendar12() {
const [locale, setLocale] =
React.useState<keyof typeof localizedStrings>("es")
const [dateRange, setDateRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 8, 9),
to: new Date(2025, 8, 17),
})
return (
<Card>
<CardHeader className="relative border-b">
<CardTitle>{localizedStrings[locale].title}</CardTitle>
<CardDescription>
{localizedStrings[locale].description}
</CardDescription>
<Select
value={locale}
onValueChange={(value) =>
setLocale(value as keyof typeof localizedStrings)
}
>
<SelectTrigger className="absolute right-4 top-4 w-[100px]">
<SelectValue placeholder="Language" />
</SelectTrigger>
<SelectContent align="end">
<SelectItem value="es">Español</SelectItem>
<SelectItem value="en">English</SelectItem>
</SelectContent>
</Select>
</CardHeader>
<CardContent className="pt-4">
<Calendar
mode="range"
selected={dateRange}
onSelect={setDateRange}
defaultMonth={dateRange?.from}
numberOfMonths={2}
locale={locale === "es" ? es : enUS}
className="bg-transparent p-0"
/>
</CardContent>
</Card>
)
}
@@ -1,58 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
import { Label } from "@/registry/default/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/default/ui/select"
export default function Calendar13() {
const [dropdown, setDropdown] =
React.useState<React.ComponentProps<typeof Calendar>["captionLayout"]>(
"dropdown"
)
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<div className="flex flex-col gap-4">
<Calendar
mode="single"
defaultMonth={date}
selected={date}
onSelect={setDate}
captionLayout={dropdown}
className="rounded-lg border shadow-sm"
/>
<div className="flex flex-col gap-3">
<Label htmlFor="dropdown" className="px-1">
Dropdown
</Label>
<Select
value={dropdown}
onValueChange={(value) =>
setDropdown(
value as React.ComponentProps<typeof Calendar>["captionLayout"]
)
}
>
<SelectTrigger id="dropdown" className="bg-background w-full">
<SelectValue placeholder="Dropdown" />
</SelectTrigger>
<SelectContent align="center">
<SelectItem value="dropdown">Month and Year</SelectItem>
<SelectItem value="dropdown-months">Month Only</SelectItem>
<SelectItem value="dropdown-years">Year Only</SelectItem>
</SelectContent>
</Select>
</div>
</div>
)
}
@@ -1,32 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar14() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
const bookedDates = Array.from(
{ length: 12 },
(_, i) => new Date(2025, 5, 15 + i)
)
return (
<Calendar
mode="single"
defaultMonth={date}
selected={date}
onSelect={setDate}
disabled={bookedDates}
modifiers={{
booked: bookedDates,
}}
modifiersClassNames={{
booked: "[&>button]:line-through opacity-100",
}}
className="rounded-lg border shadow-sm"
/>
)
}
@@ -1,22 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar15() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Calendar
mode="single"
defaultMonth={date}
selected={date}
onSelect={setDate}
className="rounded-lg border shadow-sm"
showWeekNumber
/>
)
}
@@ -1,56 +0,0 @@
"use client"
import * as React from "react"
import { Clock2Icon } from "lucide-react"
import { Calendar } from "@/registry/default/ui/calendar"
import { Card, CardContent, CardFooter } from "@/registry/default/ui/card"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export default function Calendar16() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Card className="w-fit py-4">
<CardContent className="px-4">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="bg-transparent p-0"
/>
</CardContent>
<CardFooter className="flex flex-col gap-6 border-t px-4 pb-0 pt-4">
<div className="flex w-full flex-col gap-3">
<Label htmlFor="time-from">Start Time</Label>
<div className="relative flex w-full items-center gap-2">
<Clock2Icon className="text-muted-foreground pointer-events-none absolute left-2.5 size-4 select-none" />
<Input
id="time-from"
type="time"
step="1"
defaultValue="10:30:00"
className="appearance-none pl-8 [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</div>
<div className="flex w-full flex-col gap-3">
<Label htmlFor="time-to">End Time</Label>
<div className="relative flex w-full items-center gap-2">
<Clock2Icon className="text-muted-foreground pointer-events-none absolute left-2.5 size-4 select-none" />
<Input
id="time-to"
type="time"
step="1"
defaultValue="12:30:00"
className="appearance-none pl-8 [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</div>
</CardFooter>
</Card>
)
}
@@ -1,54 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
import { Card, CardContent, CardFooter } from "@/registry/default/ui/card"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export default function Calendar17() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Card className="w-fit py-4">
<CardContent className="px-4">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="bg-transparent p-0 [--cell-size:2.8rem]"
/>
</CardContent>
<CardFooter className="*:[div]:w-full flex gap-2 border-t px-4 pb-0 pt-4">
<div className="flex-1">
<Label htmlFor="time-from" className="sr-only">
Start Time
</Label>
<Input
id="time-from"
type="time"
step="1"
defaultValue="10:30:00"
className="appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
<span>-</span>
<div className="flex-1">
<Label htmlFor="time-to" className="sr-only">
End Time
</Label>
<Input
id="time-to"
type="time"
step="1"
defaultValue="12:30:00"
className="appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</CardFooter>
</Card>
)
}
@@ -1,20 +0,0 @@
"use client"
import * as React from "react"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar18() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-lg border [--cell-size:2.75rem] md:[--cell-size:3rem]"
/>
)
}
@@ -1,50 +0,0 @@
"use client"
import * as React from "react"
import { addDays } from "date-fns"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Card, CardContent, CardFooter } from "@/registry/default/ui/card"
export default function Calendar19() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Card className="max-w-[300px] py-4">
<CardContent className="px-4">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
defaultMonth={date}
className="bg-transparent p-0 [--cell-size:2.375rem]"
/>
</CardContent>
<CardFooter className="flex flex-wrap gap-2 border-t px-4 pb-0 pt-4">
{[
{ label: "Today", value: 0 },
{ label: "Tomorrow", value: 1 },
{ label: "In 3 days", value: 3 },
{ label: "In a week", value: 7 },
{ label: "In 2 weeks", value: 14 },
].map((preset) => (
<Button
key={preset.value}
variant="outline"
size="sm"
className="flex-1"
onClick={() => {
const newDate = addDays(new Date(), preset.value)
setDate(newDate)
}}
>
{preset.label}
</Button>
))}
</CardFooter>
</Card>
)
}
@@ -1,97 +0,0 @@
"use client"
import * as React from "react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Card, CardContent, CardFooter } from "@/registry/default/ui/card"
export default function Calendar20() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
const [selectedTime, setSelectedTime] = React.useState<string | null>("10:00")
const timeSlots = Array.from({ length: 37 }, (_, i) => {
const totalMinutes = i * 15
const hour = Math.floor(totalMinutes / 60) + 9
const minute = totalMinutes % 60
return `${hour.toString().padStart(2, "0")}:${minute
.toString()
.padStart(2, "0")}`
})
const bookedDates = Array.from(
{ length: 3 },
(_, i) => new Date(2025, 5, 17 + i)
)
return (
<Card className="gap-0 p-0">
<CardContent className="relative p-0 md:pr-48">
<div className="p-6">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
defaultMonth={date}
disabled={bookedDates}
showOutsideDays={false}
modifiers={{
booked: bookedDates,
}}
modifiersClassNames={{
booked: "[&>button]:line-through opacity-100",
}}
className="bg-transparent p-0 [--cell-size:2.5rem] md:[--cell-size:3rem]"
formatters={{
formatWeekdayName: (date) => {
return date.toLocaleString("en-US", { weekday: "short" })
},
}}
/>
</div>
<div className="no-scrollbar inset-y-0 right-0 flex max-h-72 w-full scroll-pb-6 flex-col gap-4 overflow-y-auto border-t p-6 md:absolute md:max-h-none md:w-48 md:border-l md:border-t-0">
<div className="grid gap-2">
{timeSlots.map((time) => (
<Button
key={time}
variant={selectedTime === time ? "default" : "outline"}
onClick={() => setSelectedTime(time)}
className="w-full shadow-none"
>
{time}
</Button>
))}
</div>
</div>
</CardContent>
<CardFooter className="flex flex-col gap-4 border-t !py-5 px-6 md:flex-row">
<div className="text-sm">
{date && selectedTime ? (
<>
Your meeting is booked for{" "}
<span className="font-medium">
{" "}
{date?.toLocaleDateString("en-US", {
weekday: "long",
day: "numeric",
month: "long",
})}{" "}
</span>
at <span className="font-medium">{selectedTime}</span>.
</>
) : (
<>Select a date and time for your meeting.</>
)}
</div>
<Button
disabled={!date || !selectedTime}
className="w-full md:ml-auto md:w-auto"
variant="outline"
>
Continue
</Button>
</CardFooter>
</Card>
)
}
@@ -1,52 +0,0 @@
"use client"
import * as React from "react"
import { DateRange } from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
export default function Calendar21() {
const [range, setRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 12),
to: new Date(2025, 5, 17),
})
return (
<Calendar
mode="range"
defaultMonth={range?.from}
selected={range}
onSelect={setRange}
numberOfMonths={1}
captionLayout="dropdown"
className="rounded-lg border shadow-sm [--cell-size:2.75rem] md:[--cell-size:3rem]"
formatters={{
formatMonthDropdown: (date) => {
return date.toLocaleString("default", { month: "long" })
},
}}
components={{
DayButton: ({ children, modifiers, day, ...props }) => {
const isWeekend = day.date.getDay() === 0 || day.date.getDay() === 6
return (
<button
className={cn(
buttonVariants({ variant: "ghost" }),
"relative flex flex-col items-center justify-center h-9 w-9 p-0 font-normal aria-selected:opacity-100"
)}
{...props}
>
<div>{children}</div>
{!modifiers.outside && (
<span className="text-xs">{isWeekend ? "$220" : "$100"}</span>
)}
</button>
)
},
}}
/>
)
}
@@ -1,49 +0,0 @@
"use client"
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
export default function Calendar22() {
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<div className="flex flex-col gap-3">
<Label htmlFor="date" className="px-1">
Date of birth
</Label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date"
className="w-48 justify-between font-normal"
>
{date ? date.toLocaleDateString() : "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
onSelect={(date) => {
setDate(date)
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
</div>
)
}
@@ -1,50 +0,0 @@
"use client"
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"
import { type DateRange } from "react-day-picker"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
export default function Calendar23() {
const [range, setRange] = React.useState<DateRange | undefined>(undefined)
return (
<div className="flex flex-col gap-3">
<Label htmlFor="dates" className="px-1">
Select your stay
</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
id="dates"
className="w-56 justify-between font-normal"
>
{range?.from && range?.to
? `${range.from.toLocaleDateString()} - ${range.to.toLocaleDateString()}`
: "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
<Calendar
mode="range"
selected={range}
captionLayout="dropdown"
onSelect={(range) => {
setRange(range)
}}
/>
</PopoverContent>
</Popover>
</div>
)
}
@@ -1,64 +0,0 @@
"use client"
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
export default function Calendar24() {
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<div className="flex gap-4">
<div className="flex flex-col gap-3">
<Label htmlFor="date" className="px-1">
Date
</Label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date"
className="w-32 justify-between font-normal"
>
{date ? date.toLocaleDateString() : "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
onSelect={(date) => {
setDate(date)
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="time" className="px-1">
Time
</Label>
<Input
type="time"
id="time"
step="1"
defaultValue="10:30:00"
className="bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</div>
)
}
@@ -1,78 +0,0 @@
"use client"
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
export default function Calendar25() {
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-3">
<Label htmlFor="date" className="px-1">
Date
</Label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date"
className="w-full justify-between font-normal"
>
{date ? date.toLocaleDateString() : "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
onSelect={(date) => {
setDate(date)
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
</div>
<div className="flex gap-4">
<div className="flex flex-col gap-3">
<Label htmlFor="time-from" className="px-1">
From
</Label>
<Input
type="time"
id="time-from"
step="1"
defaultValue="10:30:00"
className="bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="time-to" className="px-1">
To
</Label>
<Input
type="time"
id="time-to"
step="1"
defaultValue="12:30:00"
className="bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</div>
</div>
)
}
@@ -1,133 +0,0 @@
"use client"
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
export default function Calendar26() {
const [openFrom, setOpenFrom] = React.useState(false)
const [openTo, setOpenTo] = React.useState(false)
const [dateFrom, setDateFrom] = React.useState<Date | undefined>(
new Date("2025-06-01")
)
const [dateTo, setDateTo] = React.useState<Date | undefined>(
new Date("2025-06-03")
)
return (
<div className="flex w-full max-w-64 min-w-0 flex-col gap-6">
<div className="flex gap-4">
<div className="flex flex-1 flex-col gap-3">
<Label htmlFor="date-from" className="px-1">
Check-in
</Label>
<Popover open={openFrom} onOpenChange={setOpenFrom}>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date-from"
className="w-full justify-between font-normal"
>
{dateFrom
? dateFrom.toLocaleDateString("en-US", {
day: "2-digit",
month: "short",
year: "numeric",
})
: "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto overflow-hidden p-0"
align="start"
>
<Calendar
mode="single"
selected={dateFrom}
captionLayout="dropdown"
onSelect={(date) => {
setDateFrom(date)
setOpenFrom(false)
}}
/>
</PopoverContent>
</Popover>
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="time-from" className="invisible px-1">
From
</Label>
<Input
type="time"
id="time-from"
step="1"
defaultValue="10:30:00"
className="bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</div>
<div className="flex gap-4">
<div className="flex flex-1 flex-col gap-3">
<Label htmlFor="date-to" className="px-1">
Check-out
</Label>
<Popover open={openTo} onOpenChange={setOpenTo}>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date-to"
className="w-full justify-between font-normal"
>
{dateTo
? dateTo.toLocaleDateString("en-US", {
day: "2-digit",
month: "short",
year: "numeric",
})
: "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto overflow-hidden p-0"
align="start"
>
<Calendar
mode="single"
selected={dateTo}
captionLayout="dropdown"
onSelect={(date) => {
setDateTo(date)
setOpenTo(false)
}}
disabled={dateFrom && { before: dateFrom }}
/>
</PopoverContent>
</Popover>
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="time-to" className="invisible px-1">
To
</Label>
<Input
type="time"
id="time-to"
step="1"
defaultValue="12:30:00"
className="bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</div>
</div>
)
}
@@ -1,177 +0,0 @@
"use client"
import * as React from "react"
import { CalendarIcon } from "lucide-react"
import { DateRange } from "react-day-picker"
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/registry/default/ui/chart"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
const chartData = [
{ date: "2025-06-01", visitors: 178 },
{ date: "2025-06-02", visitors: 470 },
{ date: "2025-06-03", visitors: 103 },
{ date: "2025-06-04", visitors: 439 },
{ date: "2025-06-05", visitors: 88 },
{ date: "2025-06-06", visitors: 294 },
{ date: "2025-06-07", visitors: 323 },
{ date: "2025-06-08", visitors: 385 },
{ date: "2025-06-09", visitors: 438 },
{ date: "2025-06-10", visitors: 155 },
{ date: "2025-06-11", visitors: 92 },
{ date: "2025-06-12", visitors: 492 },
{ date: "2025-06-13", visitors: 81 },
{ date: "2025-06-14", visitors: 426 },
{ date: "2025-06-15", visitors: 307 },
{ date: "2025-06-16", visitors: 371 },
{ date: "2025-06-17", visitors: 475 },
{ date: "2025-06-18", visitors: 107 },
{ date: "2025-06-19", visitors: 341 },
{ date: "2025-06-20", visitors: 408 },
{ date: "2025-06-21", visitors: 169 },
{ date: "2025-06-22", visitors: 317 },
{ date: "2025-06-23", visitors: 480 },
{ date: "2025-06-24", visitors: 132 },
{ date: "2025-06-25", visitors: 141 },
{ date: "2025-06-26", visitors: 434 },
{ date: "2025-06-27", visitors: 448 },
{ date: "2025-06-28", visitors: 149 },
{ date: "2025-06-29", visitors: 103 },
{ date: "2025-06-30", visitors: 446 },
]
const total = chartData.reduce((acc, curr) => acc + curr.visitors, 0)
const chartConfig = {
visitors: {
label: "Visitors",
color: "hsl(var(--primary))",
},
} satisfies ChartConfig
export default function Calendar27() {
const [range, setRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 5),
to: new Date(2025, 5, 20),
})
const filteredData = React.useMemo(() => {
if (!range?.from && !range?.to) {
return chartData
}
return chartData.filter((item) => {
const date = new Date(item.date)
return date >= range.from! && date <= range.to!
})
}, [range])
return (
<Card className="@container/card w-full max-w-xl">
<CardHeader className="@md/card:grid relative flex flex-col border-b">
<CardTitle>Web Analytics</CardTitle>
<CardDescription>
Showing total visitors for this month.
</CardDescription>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="absolute right-4 top-4">
<CalendarIcon />
{range?.from && range?.to
? `${range.from.toLocaleDateString()} - ${range.to.toLocaleDateString()}`
: "June 2025"}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="end">
<Calendar
className="w-full"
mode="range"
defaultMonth={range?.from}
selected={range}
onSelect={setRange}
disableNavigation
startMonth={range?.from}
fixedWeeks
showOutsideDays
disabled={{
after: new Date(2025, 5, 31),
}}
/>
</PopoverContent>
</Popover>
</CardHeader>
<CardContent className="px-4">
<ChartContainer
config={chartConfig}
className="aspect-auto h-[250px] w-full"
>
<BarChart
accessibilityLayer
data={filteredData}
margin={{
left: 12,
right: 12,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="date"
tickLine={false}
axisLine={false}
tickMargin={8}
minTickGap={20}
tickFormatter={(value) => {
const date = new Date(value)
return date.toLocaleDateString("en-US", {
day: "numeric",
})
}}
/>
<ChartTooltip
content={
<ChartTooltipContent
className="w-[150px]"
nameKey="visitors"
labelFormatter={(value) => {
return new Date(value).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})
}}
/>
}
/>
<Bar dataKey="visitors" fill={`var(--color-visitors)`} radius={4} />
</BarChart>
</ChartContainer>
</CardContent>
<CardFooter className="border-t pt-6">
<div className="text-sm">
You had{" "}
<span className="font-semibold">{total.toLocaleString()}</span>{" "}
visitors for the month of June.
</div>
</CardFooter>
</Card>
)
}
@@ -1,104 +0,0 @@
"use client"
import * as React from "react"
import { CalendarIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
function formatDate(date: Date | undefined) {
if (!date) {
return ""
}
return date.toLocaleDateString("en-US", {
day: "2-digit",
month: "long",
year: "numeric",
})
}
function isValidDate(date: Date | undefined) {
if (!date) {
return false
}
return !isNaN(date.getTime())
}
export default function Calendar28() {
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(
new Date("2025-06-01")
)
const [month, setMonth] = React.useState<Date | undefined>(date)
const [value, setValue] = React.useState(formatDate(date))
return (
<div className="flex flex-col gap-3">
<Label htmlFor="date" className="px-1">
Subscription Date
</Label>
<div className="relative flex gap-2">
<Input
id="date"
value={value}
placeholder="June 01, 2025"
className="bg-background pr-10"
onChange={(e) => {
const date = new Date(e.target.value)
setValue(e.target.value)
if (isValidDate(date)) {
setDate(date)
setMonth(date)
}
}}
onKeyDown={(e) => {
if (e.key === "ArrowDown") {
e.preventDefault()
setOpen(true)
}
}}
/>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
id="date-picker"
variant="ghost"
size="icon"
className="absolute right-2 top-1/2 h-6 w-6 -translate-y-1/2"
>
<CalendarIcon className="size-3" />
<span className="sr-only">Select date</span>
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto overflow-hidden p-0"
align="end"
alignOffset={-8}
sideOffset={10}
>
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
month={month}
onMonthChange={setMonth}
onSelect={(date) => {
setDate(date)
setValue(formatDate(date))
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
</div>
</div>
)
}
@@ -1,96 +0,0 @@
"use client"
import * as React from "react"
// import { parseDate } from "chrono-node" - temporarily disabled
import { CalendarIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
function formatDate(date: Date | undefined) {
if (!date) {
return ""
}
return date.toLocaleDateString("en-US", {
day: "2-digit",
month: "long",
year: "numeric",
})
}
export default function Calendar29() {
const [open, setOpen] = React.useState(false)
const [value, setValue] = React.useState("In 2 days")
const [date, setDate] = React.useState<Date | undefined>(
new Date() // parseDate stub
)
const [month, setMonth] = React.useState<Date | undefined>(date)
return (
<div className="flex flex-col gap-3">
<Label htmlFor="date" className="px-1">
Schedule Date
</Label>
<div className="relative flex gap-2">
<Input
id="date"
value={value}
placeholder="Tomorrow or next week"
className="bg-background pr-10"
onChange={(e) => {
setValue(e.target.value)
const date = null // parseDate stub
if (date) {
setDate(date)
setMonth(date)
}
}}
onKeyDown={(e) => {
if (e.key === "ArrowDown") {
e.preventDefault()
setOpen(true)
}
}}
/>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
id="date-picker"
variant="ghost"
className="absolute top-1/2 right-2 size-6 -translate-y-1/2"
>
<CalendarIcon className="size-3.5" />
<span className="sr-only">Select date</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="end">
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
month={month}
onMonthChange={setMonth}
onSelect={(date) => {
setDate(date)
setValue(formatDate(date))
setOpen(false)
}}
/>
</PopoverContent>
</Popover>
</div>
<div className="text-muted-foreground px-1 text-sm">
Your post will be published on{" "}
<span className="font-medium">{formatDate(date)}</span>.
</div>
</div>
)
}
@@ -1,56 +0,0 @@
"use client"
import * as React from "react"
import { formatDateRange } from "little-date"
import { ChevronDownIcon } from "lucide-react"
import { type DateRange } from "react-day-picker"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Label } from "@/registry/default/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/default/ui/popover"
export default function Calendar30() {
const [range, setRange] = React.useState<DateRange | undefined>({
from: new Date(2025, 5, 4),
to: new Date(2025, 5, 10),
})
return (
<div className="flex flex-col gap-3">
<Label htmlFor="dates" className="px-1">
Select your stay
</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
id="dates"
className="w-56 justify-between font-normal"
>
{range?.from && range?.to
? formatDateRange(range.from, range.to, {
includeTime: false,
})
: "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
<Calendar
mode="range"
selected={range}
captionLayout="dropdown"
onSelect={(range) => {
setRange(range)
}}
/>
</PopoverContent>
</Popover>
</div>
)
}
@@ -1,80 +0,0 @@
"use client"
import * as React from "react"
import { formatDateRange } from "little-date"
import { PlusIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import { Card, CardContent, CardFooter } from "@/registry/default/ui/card"
const events = [
{
title: "Team Sync Meeting",
from: "2025-06-12T09:00:00",
to: "2025-06-12T10:00:00",
},
{
title: "Design Review",
from: "2025-06-12T11:30:00",
to: "2025-06-12T12:30:00",
},
{
title: "Client Presentation",
from: "2025-06-12T14:00:00",
to: "2025-06-12T15:00:00",
},
]
export default function Calendar31() {
const [date, setDate] = React.useState<Date | undefined>(
new Date(2025, 5, 12)
)
return (
<Card className="w-fit py-4">
<CardContent className="px-4">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="bg-transparent p-0"
required
/>
</CardContent>
<CardFooter className="flex flex-col items-start gap-3 border-t px-4 pb-0 pt-4">
<div className="flex w-full items-center justify-between px-1">
<div className="text-sm font-medium">
{date?.toLocaleDateString("en-US", {
day: "numeric",
month: "long",
year: "numeric",
})}
</div>
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
title="Add Event"
>
<PlusIcon />
<span className="sr-only">Add Event</span>
</Button>
</div>
<div className="flex w-full flex-col gap-2">
{events.map((event) => (
<div
key={event.title}
className="bg-muted after:bg-primary/70 relative rounded-md p-2 pl-6 text-sm after:absolute after:inset-y-2 after:left-2 after:w-1 after:rounded-full"
>
<div className="font-medium">{event.title}</div>
<div className="text-muted-foreground text-xs">
{formatDateRange(new Date(event.from), new Date(event.to))}
</div>
</div>
))}
</div>
</CardFooter>
</Card>
)
}
@@ -1,60 +0,0 @@
"use client"
import * as React from "react"
import { CalendarPlusIcon } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import { Calendar } from "@/registry/default/ui/calendar"
import {
Drawer,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/registry/default/ui/drawer"
import { Label } from "@/registry/default/ui/label"
export default function Calendar32() {
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>(undefined)
return (
<div className="flex flex-col gap-3">
<Label htmlFor="date" className="px-1">
Date of birth
</Label>
<Drawer open={open} onOpenChange={setOpen}>
<DrawerTrigger asChild>
<Button
variant="outline"
id="date"
className="w-48 justify-between font-normal"
>
{date ? date.toLocaleDateString() : "Select date"}
<CalendarPlusIcon />
</Button>
</DrawerTrigger>
<DrawerContent className="w-auto overflow-hidden p-0">
<DrawerHeader className="sr-only">
<DrawerTitle>Select date</DrawerTitle>
<DrawerDescription>Set your date of birth</DrawerDescription>
</DrawerHeader>
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
onSelect={(date) => {
setDate(date)
setOpen(false)
}}
className="mx-auto [--cell-size:clamp(0px,calc(100vw/7.5),52px)]"
/>
</DrawerContent>
</Drawer>
<div className="text-muted-foreground px-1 text-sm">
This example works best on mobile.
</div>
</div>
)
}
@@ -1,455 +0,0 @@
import Link from "next/link"
import {
Activity,
ArrowUpRight,
CircleUser,
CreditCard,
DollarSign,
Menu,
Package2,
Search,
Users,
} from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/default/ui/avatar"
import { Badge } from "@/registry/default/ui/badge"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/default/ui/dropdown-menu"
import { Input } from "@/registry/default/ui/input"
import { Sheet, SheetContent, SheetTrigger } from "@/registry/default/ui/sheet"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/registry/default/ui/table"
export const description =
"An application shell with a header and main content area. The header has a navbar, a search input and and a user nav dropdown. The user nav is toggled by a button with an avatar image."
export const iframeHeight = "825px"
export const containerClassName = "w-full h-full"
export default function Dashboard() {
return (
<div className="flex min-h-screen w-full flex-col">
<header className="sticky top-0 flex h-16 items-center gap-4 border-b bg-background px-4 md:px-6">
<nav className="hidden flex-col gap-6 text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6">
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold md:text-base"
>
<Package2 className="h-6 w-6" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="text-foreground transition-colors hover:text-foreground"
>
Dashboard
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Orders
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Products
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Customers
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Analytics
</Link>
</nav>
<Sheet>
<SheetTrigger asChild>
<Button
variant="outline"
size="icon"
className="shrink-0 md:hidden"
>
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left">
<nav className="grid gap-6 text-lg font-medium">
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold"
>
<Package2 className="h-6 w-6" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link href="#" className="hover:text-foreground">
Dashboard
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Orders
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Products
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Customers
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Analytics
</Link>
</nav>
</SheetContent>
</Sheet>
<div className="flex w-full items-center gap-4 md:ml-auto md:gap-2 lg:gap-4">
<form className="ml-auto flex-1 sm:flex-initial">
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search products..."
className="pl-8 sm:w-[300px] md:w-[200px] lg:w-[300px]"
/>
</div>
</form>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary" size="icon" className="rounded-full">
<CircleUser className="h-5 w-5" />
<span className="sr-only">Toggle user menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
<main className="flex flex-1 flex-col gap-4 p-4 md:gap-8 md:p-8">
<div className="grid gap-4 md:grid-cols-2 md:gap-8 lg:grid-cols-4">
<Card x-chunk="dashboard-01-chunk-0">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Total Revenue
</CardTitle>
<DollarSign className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">$45,231.89</div>
<p className="text-xs text-muted-foreground">
+20.1% from last month
</p>
</CardContent>
</Card>
<Card x-chunk="dashboard-01-chunk-1">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Subscriptions
</CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+2350</div>
<p className="text-xs text-muted-foreground">
+180.1% from last month
</p>
</CardContent>
</Card>
<Card x-chunk="dashboard-01-chunk-2">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Sales</CardTitle>
<CreditCard className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+12,234</div>
<p className="text-xs text-muted-foreground">
+19% from last month
</p>
</CardContent>
</Card>
<Card x-chunk="dashboard-01-chunk-3">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Active Now</CardTitle>
<Activity className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+573</div>
<p className="text-xs text-muted-foreground">
+201 since last hour
</p>
</CardContent>
</Card>
</div>
<div className="grid gap-4 md:gap-8 lg:grid-cols-2 xl:grid-cols-3">
<Card className="xl:col-span-2" x-chunk="dashboard-01-chunk-4">
<CardHeader className="flex flex-row items-center">
<div className="grid gap-2">
<CardTitle>Transactions</CardTitle>
<CardDescription>
Recent transactions from your store.
</CardDescription>
</div>
<Button asChild size="sm" className="ml-auto gap-1">
<Link href="#">
View All
<ArrowUpRight className="h-4 w-4" />
</Link>
</Button>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Customer</TableHead>
<TableHead className="hidden xl:table-column">
Type
</TableHead>
<TableHead className="hidden xl:table-column">
Status
</TableHead>
<TableHead className="hidden xl:table-column">
Date
</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>
<div className="font-medium">Liam Johnson</div>
<div className="hidden text-sm text-muted-foreground md:inline">
liam@example.com
</div>
</TableCell>
<TableCell className="hidden xl:table-column">
Sale
</TableCell>
<TableCell className="hidden xl:table-column">
<Badge className="text-xs" variant="outline">
Approved
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell lg:hidden xl:table-column">
2023-06-23
</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Olivia Smith</div>
<div className="hidden text-sm text-muted-foreground md:inline">
olivia@example.com
</div>
</TableCell>
<TableCell className="hidden xl:table-column">
Refund
</TableCell>
<TableCell className="hidden xl:table-column">
<Badge className="text-xs" variant="outline">
Declined
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell lg:hidden xl:table-column">
2023-06-24
</TableCell>
<TableCell className="text-right">$150.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Noah Williams</div>
<div className="hidden text-sm text-muted-foreground md:inline">
noah@example.com
</div>
</TableCell>
<TableCell className="hidden xl:table-column">
Subscription
</TableCell>
<TableCell className="hidden xl:table-column">
<Badge className="text-xs" variant="outline">
Approved
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell lg:hidden xl:table-column">
2023-06-25
</TableCell>
<TableCell className="text-right">$350.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Emma Brown</div>
<div className="hidden text-sm text-muted-foreground md:inline">
emma@example.com
</div>
</TableCell>
<TableCell className="hidden xl:table-column">
Sale
</TableCell>
<TableCell className="hidden xl:table-column">
<Badge className="text-xs" variant="outline">
Approved
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell lg:hidden xl:table-column">
2023-06-26
</TableCell>
<TableCell className="text-right">$450.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Liam Johnson</div>
<div className="hidden text-sm text-muted-foreground md:inline">
liam@example.com
</div>
</TableCell>
<TableCell className="hidden xl:table-column">
Sale
</TableCell>
<TableCell className="hidden xl:table-column">
<Badge className="text-xs" variant="outline">
Approved
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell lg:hidden xl:table-column">
2023-06-27
</TableCell>
<TableCell className="text-right">$550.00</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
<Card x-chunk="dashboard-01-chunk-5">
<CardHeader>
<CardTitle>Recent Sales</CardTitle>
</CardHeader>
<CardContent className="grid gap-8">
<div className="flex items-center gap-4">
<Avatar className="hidden h-9 w-9 sm:flex">
<AvatarImage src="/avatars/01.png" alt="Avatar" />
<AvatarFallback>OM</AvatarFallback>
</Avatar>
<div className="grid gap-1">
<p className="text-sm font-medium leading-none">
Olivia Martin
</p>
<p className="text-sm text-muted-foreground">
olivia.martin@email.com
</p>
</div>
<div className="ml-auto font-medium">+$1,999.00</div>
</div>
<div className="flex items-center gap-4">
<Avatar className="hidden h-9 w-9 sm:flex">
<AvatarImage src="/avatars/02.png" alt="Avatar" />
<AvatarFallback>JL</AvatarFallback>
</Avatar>
<div className="grid gap-1">
<p className="text-sm font-medium leading-none">
Jackson Lee
</p>
<p className="text-sm text-muted-foreground">
jackson.lee@email.com
</p>
</div>
<div className="ml-auto font-medium">+$39.00</div>
</div>
<div className="flex items-center gap-4">
<Avatar className="hidden h-9 w-9 sm:flex">
<AvatarImage src="/avatars/03.png" alt="Avatar" />
<AvatarFallback>IN</AvatarFallback>
</Avatar>
<div className="grid gap-1">
<p className="text-sm font-medium leading-none">
Isabella Nguyen
</p>
<p className="text-sm text-muted-foreground">
isabella.nguyen@email.com
</p>
</div>
<div className="ml-auto font-medium">+$299.00</div>
</div>
<div className="flex items-center gap-4">
<Avatar className="hidden h-9 w-9 sm:flex">
<AvatarImage src="/avatars/04.png" alt="Avatar" />
<AvatarFallback>WK</AvatarFallback>
</Avatar>
<div className="grid gap-1">
<p className="text-sm font-medium leading-none">
William Kim
</p>
<p className="text-sm text-muted-foreground">
will@email.com
</p>
</div>
<div className="ml-auto font-medium">+$99.00</div>
</div>
<div className="flex items-center gap-4">
<Avatar className="hidden h-9 w-9 sm:flex">
<AvatarImage src="/avatars/05.png" alt="Avatar" />
<AvatarFallback>SD</AvatarFallback>
</Avatar>
<div className="grid gap-1">
<p className="text-sm font-medium leading-none">
Sofia Davis
</p>
<p className="text-sm text-muted-foreground">
sofia.davis@email.com
</p>
</div>
<div className="ml-auto font-medium">+$39.00</div>
</div>
</CardContent>
</Card>
</div>
</main>
</div>
)
}
@@ -1,246 +0,0 @@
import Link from "next/link"
import {
Bell,
CircleUser,
Home,
LineChart,
Menu,
Package,
Package2,
Search,
ShoppingCart,
Users,
} from "lucide-react"
import { Badge } from "@/registry/default/ui/badge"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/default/ui/dropdown-menu"
import { Input } from "@/registry/default/ui/input"
import { Sheet, SheetContent, SheetTrigger } from "@/registry/default/ui/sheet"
export const description =
"A products dashboard with a sidebar navigation and a main content area. The dashboard has a header with a search input and a user menu. The sidebar has a logo, navigation links, and a card with a call to action. The main content area shows an empty state with a call to action."
export const iframeHeight = "800px"
export const containerClassName = "w-full h-full"
export default function Dashboard() {
return (
<div className="grid min-h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]">
<div className="hidden border-r bg-muted/40 md:block">
<div className="flex h-full max-h-screen flex-col gap-2">
<div className="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6">
<Link href="/" className="flex items-center gap-2 font-semibold">
<Package2 className="h-6 w-6" />
<span className="">Acme Inc</span>
</Link>
<Button variant="outline" size="icon" className="ml-auto h-8 w-8">
<Bell className="h-4 w-4" />
<span className="sr-only">Toggle notifications</span>
</Button>
</div>
<div className="flex-1">
<nav className="grid items-start px-2 text-sm font-medium lg:px-4">
<Link
href="#"
className="flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary"
>
<Home className="h-4 w-4" />
Dashboard
</Link>
<Link
href="#"
className="flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary"
>
<ShoppingCart className="h-4 w-4" />
Orders
<Badge className="ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-full">
6
</Badge>
</Link>
<Link
href="#"
className="flex items-center gap-3 rounded-lg bg-muted px-3 py-2 text-primary transition-all hover:text-primary"
>
<Package className="h-4 w-4" />
Products{" "}
</Link>
<Link
href="#"
className="flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary"
>
<Users className="h-4 w-4" />
Customers
</Link>
<Link
href="#"
className="flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary"
>
<LineChart className="h-4 w-4" />
Analytics
</Link>
</nav>
</div>
<div className="mt-auto p-4">
<Card x-chunk="dashboard-02-chunk-0">
<CardHeader className="p-2 pt-0 md:p-4">
<CardTitle>Upgrade to Pro</CardTitle>
<CardDescription>
Unlock all features and get unlimited access to our support
team.
</CardDescription>
</CardHeader>
<CardContent className="p-2 pt-0 md:p-4 md:pt-0">
<Button size="sm" className="w-full">
Upgrade
</Button>
</CardContent>
</Card>
</div>
</div>
</div>
<div className="flex flex-col">
<header className="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6">
<Sheet>
<SheetTrigger asChild>
<Button
variant="outline"
size="icon"
className="shrink-0 md:hidden"
>
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="flex flex-col">
<nav className="grid gap-2 text-lg font-medium">
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold"
>
<Package2 className="h-6 w-6" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Home className="h-5 w-5" />
Dashboard
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl bg-muted px-3 py-2 text-foreground hover:text-foreground"
>
<ShoppingCart className="h-5 w-5" />
Orders
<Badge className="ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-full">
6
</Badge>
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Package className="h-5 w-5" />
Products
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Users className="h-5 w-5" />
Customers
</Link>
<Link
href="#"
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<LineChart className="h-5 w-5" />
Analytics
</Link>
</nav>
<div className="mt-auto">
<Card>
<CardHeader>
<CardTitle>Upgrade to Pro</CardTitle>
<CardDescription>
Unlock all features and get unlimited access to our
support team.
</CardDescription>
</CardHeader>
<CardContent>
<Button size="sm" className="w-full">
Upgrade
</Button>
</CardContent>
</Card>
</div>
</SheetContent>
</Sheet>
<div className="w-full flex-1">
<form>
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search products..."
className="w-full appearance-none bg-background pl-8 shadow-none md:w-2/3 lg:w-1/3"
/>
</div>
</form>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary" size="icon" className="rounded-full">
<CircleUser className="h-5 w-5" />
<span className="sr-only">Toggle user menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
<main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6">
<div className="flex items-center">
<h1 className="text-lg font-semibold md:text-2xl">Inventory</h1>
</div>
<div
className="flex flex-1 items-center justify-center rounded-lg border border-dashed shadow-sm" x-chunk="dashboard-02-chunk-1"
>
<div className="flex flex-col items-center gap-1 text-center">
<h3 className="text-2xl font-bold tracking-tight">
You have no products
</h3>
<p className="text-sm text-muted-foreground">
You can start selling as soon as you add a product.
</p>
<Button className="mt-4">Add Product</Button>
</div>
</div>
</main>
</div>
</div>
)
}
@@ -1,462 +0,0 @@
import {
Bird,
Book,
Bot,
Code2,
CornerDownLeft,
LifeBuoy,
Mic,
Paperclip,
Rabbit,
Settings,
Settings2,
Share,
SquareTerminal,
SquareUser,
Triangle,
Turtle,
} from "lucide-react"
import { Badge } from "@/registry/default/ui/badge"
import { Button } from "@/registry/default/ui/button"
import {
Drawer,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/registry/default/ui/drawer"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/default/ui/select"
import { Textarea } from "@/registry/default/ui/textarea"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/default/ui/tooltip"
export const description =
"An AI playground with a sidebar navigation and a main content area. The playground has a header with a settings drawer and a share button. The sidebar has navigation links and a user menu. The main content area shows a form to configure the model and messages."
export const iframeHeight = "740px"
export const containerClassName = "w-full h-full"
export default function Dashboard() {
return (
<div className="grid h-screen w-full pl-[56px]">
<aside className="inset-y fixed left-0 z-20 flex h-full flex-col border-r">
<div className="border-b p-2">
<Button variant="outline" size="icon" aria-label="Home">
<Triangle className="size-5 fill-foreground" />
</Button>
</div>
<nav className="grid gap-1 p-2">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="rounded-lg bg-muted"
aria-label="Playground"
>
<SquareTerminal className="size-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
Playground
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="rounded-lg"
aria-label="Models"
>
<Bot className="size-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
Models
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="rounded-lg"
aria-label="API"
>
<Code2 className="size-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
API
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="rounded-lg"
aria-label="Documentation"
>
<Book className="size-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
Documentation
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="rounded-lg"
aria-label="Settings"
>
<Settings2 className="size-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
Settings
</TooltipContent>
</Tooltip>
</nav>
<nav className="mt-auto grid gap-1 p-2">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="mt-auto rounded-lg"
aria-label="Help"
>
<LifeBuoy className="size-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
Help
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="mt-auto rounded-lg"
aria-label="Account"
>
<SquareUser className="size-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={5}>
Account
</TooltipContent>
</Tooltip>
</nav>
</aside>
<div className="flex flex-col">
<header className="sticky top-0 z-10 flex h-[57px] items-center gap-1 border-b bg-background px-4">
<h1 className="text-xl font-semibold">Playground</h1>
<Drawer>
<DrawerTrigger asChild>
<Button variant="ghost" size="icon" className="md:hidden">
<Settings className="size-4" />
<span className="sr-only">Settings</span>
</Button>
</DrawerTrigger>
<DrawerContent className="max-h-[80vh]">
<DrawerHeader>
<DrawerTitle>Configuration</DrawerTitle>
<DrawerDescription>
Configure the settings for the model and messages.
</DrawerDescription>
</DrawerHeader>
<form className="grid w-full items-start gap-6 overflow-auto p-4 pt-0">
<fieldset className="grid gap-6 rounded-lg border p-4">
<legend className="-ml-1 px-1 text-sm font-medium">
Settings
</legend>
<div className="grid gap-3">
<Label htmlFor="model">Model</Label>
<Select>
<SelectTrigger
id="model"
className="items-start [&_[data-description]]:hidden"
>
<SelectValue placeholder="Select a model" />
</SelectTrigger>
<SelectContent>
<SelectItem value="genesis">
<div className="flex items-start gap-3 text-muted-foreground">
<Rabbit className="size-5" />
<div className="grid gap-0.5">
<p>
Neural{" "}
<span className="font-medium text-foreground">
Genesis
</span>
</p>
<p className="text-xs" data-description>
Our fastest model for general use cases.
</p>
</div>
</div>
</SelectItem>
<SelectItem value="explorer">
<div className="flex items-start gap-3 text-muted-foreground">
<Bird className="size-5" />
<div className="grid gap-0.5">
<p>
Neural{" "}
<span className="font-medium text-foreground">
Explorer
</span>
</p>
<p className="text-xs" data-description>
Performance and speed for efficiency.
</p>
</div>
</div>
</SelectItem>
<SelectItem value="quantum">
<div className="flex items-start gap-3 text-muted-foreground">
<Turtle className="size-5" />
<div className="grid gap-0.5">
<p>
Neural{" "}
<span className="font-medium text-foreground">
Quantum
</span>
</p>
<p className="text-xs" data-description>
The most powerful model for complex
computations.
</p>
</div>
</div>
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-3">
<Label htmlFor="temperature">Temperature</Label>
<Input id="temperature" type="number" placeholder="0.4" />
</div>
<div className="grid gap-3">
<Label htmlFor="top-p">Top P</Label>
<Input id="top-p" type="number" placeholder="0.7" />
</div>
<div className="grid gap-3">
<Label htmlFor="top-k">Top K</Label>
<Input id="top-k" type="number" placeholder="0.0" />
</div>
</fieldset>
<fieldset className="grid gap-6 rounded-lg border p-4">
<legend className="-ml-1 px-1 text-sm font-medium">
Messages
</legend>
<div className="grid gap-3">
<Label htmlFor="role">Role</Label>
<Select defaultValue="system">
<SelectTrigger>
<SelectValue placeholder="Select a role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">System</SelectItem>
<SelectItem value="user">User</SelectItem>
<SelectItem value="assistant">Assistant</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-3">
<Label htmlFor="content">Content</Label>
<Textarea id="content" placeholder="You are a..." />
</div>
</fieldset>
</form>
</DrawerContent>
</Drawer>
<Button
variant="outline"
size="sm"
className="ml-auto gap-1.5 text-sm"
>
<Share className="size-3.5" />
Share
</Button>
</header>
<main className="grid flex-1 gap-4 overflow-auto p-4 md:grid-cols-2 lg:grid-cols-3">
<div
className="relative hidden flex-col items-start gap-8 md:flex" x-chunk="dashboard-03-chunk-0"
>
<form className="grid w-full items-start gap-6">
<fieldset className="grid gap-6 rounded-lg border p-4">
<legend className="-ml-1 px-1 text-sm font-medium">
Settings
</legend>
<div className="grid gap-3">
<Label htmlFor="model">Model</Label>
<Select>
<SelectTrigger
id="model"
className="items-start [&_[data-description]]:hidden"
>
<SelectValue placeholder="Select a model" />
</SelectTrigger>
<SelectContent>
<SelectItem value="genesis">
<div className="flex items-start gap-3 text-muted-foreground">
<Rabbit className="size-5" />
<div className="grid gap-0.5">
<p>
Neural{" "}
<span className="font-medium text-foreground">
Genesis
</span>
</p>
<p className="text-xs" data-description>
Our fastest model for general use cases.
</p>
</div>
</div>
</SelectItem>
<SelectItem value="explorer">
<div className="flex items-start gap-3 text-muted-foreground">
<Bird className="size-5" />
<div className="grid gap-0.5">
<p>
Neural{" "}
<span className="font-medium text-foreground">
Explorer
</span>
</p>
<p className="text-xs" data-description>
Performance and speed for efficiency.
</p>
</div>
</div>
</SelectItem>
<SelectItem value="quantum">
<div className="flex items-start gap-3 text-muted-foreground">
<Turtle className="size-5" />
<div className="grid gap-0.5">
<p>
Neural{" "}
<span className="font-medium text-foreground">
Quantum
</span>
</p>
<p className="text-xs" data-description>
The most powerful model for complex computations.
</p>
</div>
</div>
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-3">
<Label htmlFor="temperature">Temperature</Label>
<Input id="temperature" type="number" placeholder="0.4" />
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-3">
<Label htmlFor="top-p">Top P</Label>
<Input id="top-p" type="number" placeholder="0.7" />
</div>
<div className="grid gap-3">
<Label htmlFor="top-k">Top K</Label>
<Input id="top-k" type="number" placeholder="0.0" />
</div>
</div>
</fieldset>
<fieldset className="grid gap-6 rounded-lg border p-4">
<legend className="-ml-1 px-1 text-sm font-medium">
Messages
</legend>
<div className="grid gap-3">
<Label htmlFor="role">Role</Label>
<Select defaultValue="system">
<SelectTrigger>
<SelectValue placeholder="Select a role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">System</SelectItem>
<SelectItem value="user">User</SelectItem>
<SelectItem value="assistant">Assistant</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-3">
<Label htmlFor="content">Content</Label>
<Textarea
id="content"
placeholder="You are a..."
className="min-h-[9.5rem]"
/>
</div>
</fieldset>
</form>
</div>
<div className="relative flex h-full min-h-[50vh] flex-col rounded-xl bg-muted/50 p-4 lg:col-span-2">
<Badge variant="outline" className="absolute right-3 top-3">
Output
</Badge>
<div className="flex-1" />
<form
className="relative overflow-hidden rounded-lg border bg-background focus-within:ring-1 focus-within:ring-ring" x-chunk="dashboard-03-chunk-1"
>
<Label htmlFor="message" className="sr-only">
Message
</Label>
<Textarea
id="message"
placeholder="Type your message here..."
className="min-h-12 resize-none border-0 p-3 shadow-none focus-visible:ring-0"
/>
<div className="flex items-center p-3 pt-0">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Paperclip className="size-4" />
<span className="sr-only">Attach file</span>
</Button>
</TooltipTrigger>
<TooltipContent side="top">Attach File</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Mic className="size-4" />
<span className="sr-only">Use Microphone</span>
</Button>
</TooltipTrigger>
<TooltipContent side="top">Use Microphone</TooltipContent>
</Tooltip>
<Button type="submit" size="sm" className="ml-auto gap-1.5">
Send Message
<CornerDownLeft className="size-3.5" />
</Button>
</div>
</form>
</div>
</main>
</div>
</div>
)
}
@@ -1,222 +0,0 @@
import Link from "next/link"
import { CircleUser, Menu, Package2, Search } from "lucide-react"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import { Checkbox } from "@/registry/default/ui/checkbox"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/default/ui/dropdown-menu"
import { Input } from "@/registry/default/ui/input"
import { Sheet, SheetContent, SheetTrigger } from "@/registry/default/ui/sheet"
export const description =
"A settings page. The settings page has a sidebar navigation and a main content area. The main content area has a form to update the store name and a form to update the plugins directory. The sidebar navigation has links to general, security, integrations, support, organizations, and advanced settings."
export const iframeHeight = "780px"
export const containerClassName = "w-full h-full"
export default function Dashboard() {
return (
<div className="flex min-h-screen w-full flex-col">
<header className="sticky top-0 flex h-16 items-center gap-4 border-b bg-background px-4 md:px-6">
<nav className="hidden flex-col gap-6 text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6">
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold md:text-base"
>
<Package2 className="h-6 w-6" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Dashboard
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Orders
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Products
</Link>
<Link
href="#"
className="text-muted-foreground transition-colors hover:text-foreground"
>
Customers
</Link>
<Link
href="#"
className="text-foreground transition-colors hover:text-foreground"
>
Settings
</Link>
</nav>
<Sheet>
<SheetTrigger asChild>
<Button
variant="outline"
size="icon"
className="shrink-0 md:hidden"
>
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left">
<nav className="grid gap-6 text-lg font-medium">
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold"
>
<Package2 className="h-6 w-6" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Dashboard
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Orders
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Products
</Link>
<Link
href="#"
className="text-muted-foreground hover:text-foreground"
>
Customers
</Link>
<Link href="#" className="hover:text-foreground">
Settings
</Link>
</nav>
</SheetContent>
</Sheet>
<div className="flex w-full items-center gap-4 md:ml-auto md:gap-2 lg:gap-4">
<form className="ml-auto flex-1 sm:flex-initial">
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search products..."
className="pl-8 sm:w-[300px] md:w-[200px] lg:w-[300px]"
/>
</div>
</form>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary" size="icon" className="rounded-full">
<CircleUser className="h-5 w-5" />
<span className="sr-only">Toggle user menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
<main className="flex min-h-[calc(100vh_-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:gap-8 md:p-10">
<div className="mx-auto grid w-full max-w-6xl gap-2">
<h1 className="text-3xl font-semibold">Settings</h1>
</div>
<div className="mx-auto grid w-full max-w-6xl items-start gap-6 md:grid-cols-[180px_1fr] lg:grid-cols-[250px_1fr]">
<nav
className="grid gap-4 text-sm text-muted-foreground" x-chunk="dashboard-04-chunk-0"
>
<Link href="#" className="font-semibold text-primary">
General
</Link>
<Link href="#">Security</Link>
<Link href="#">Integrations</Link>
<Link href="#">Support</Link>
<Link href="#">Organizations</Link>
<Link href="#">Advanced</Link>
</nav>
<div className="grid gap-6">
<Card x-chunk="dashboard-04-chunk-1">
<CardHeader>
<CardTitle>Store Name</CardTitle>
<CardDescription>
Used to identify your store in the marketplace.
</CardDescription>
</CardHeader>
<CardContent>
<form>
<Input placeholder="Store Name" />
</form>
</CardContent>
<CardFooter className="border-t px-6 py-4">
<Button>Save</Button>
</CardFooter>
</Card>
<Card x-chunk="dashboard-04-chunk-2">
<CardHeader>
<CardTitle>Plugins Directory</CardTitle>
<CardDescription>
The directory within your project, in which your plugins are
located.
</CardDescription>
</CardHeader>
<CardContent>
<form className="flex flex-col gap-4">
<Input
placeholder="Project Name"
defaultValue="/content/plugins"
/>
<div className="flex items-center space-x-2">
<Checkbox id="include" defaultChecked />
<label
htmlFor="include"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Allow administrators to change the directory.
</label>
</div>
</form>
</CardContent>
<CardFooter className="border-t px-6 py-4">
<Button>Save</Button>
</CardFooter>
</Card>
</div>
</div>
</main>
</div>
)
}
@@ -1,721 +0,0 @@
import Image from "next/image"
import Link from "next/link"
import {
ChevronLeft,
ChevronRight,
Copy,
CreditCard,
File,
Home,
LineChart,
ListFilter,
MoreVertical,
Package,
Package2,
PanelLeft,
Search,
Settings,
ShoppingCart,
Truck,
Users2,
} from "lucide-react"
import { Badge } from "@/registry/default/ui/badge"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/registry/default/ui/breadcrumb"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/default/ui/dropdown-menu"
import { Input } from "@/registry/default/ui/input"
import {
Pagination,
PaginationContent,
PaginationItem,
} from "@/registry/default/ui/pagination"
import { Progress } from "@/registry/default/ui/progress"
import { Separator } from "@/registry/default/ui/separator"
import { Sheet, SheetContent, SheetTrigger } from "@/registry/default/ui/sheet"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/registry/default/ui/table"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/default/ui/tabs"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/default/ui/tooltip"
export const description =
"An orders dashboard with a sidebar navigation. The sidebar has icon navigation. The content area has a breadcrumb and search in the header. The main area has a list of recent orders with a filter and export button. The main area also has a detailed view of a single order with order details, shipping information, billing information, customer information, and payment information."
export const iframeHeight = "1112px"
export const containerClassName = "w-full h-full"
export default function Dashboard() {
return (
<div className="flex min-h-screen w-full flex-col bg-muted/40">
<aside className="fixed inset-y-0 left-0 z-10 hidden w-14 flex-col border-r bg-background sm:flex">
<nav className="flex flex-col items-center gap-4 px-2 sm:py-5">
<Link
href="#"
className="group flex h-9 w-9 shrink-0 items-center justify-center gap-2 rounded-full bg-primary text-lg font-semibold text-primary-foreground md:h-8 md:w-8 md:text-base"
>
<Package2 className="h-4 w-4 transition-all group-hover:scale-110" />
<span className="sr-only">Acme Inc</span>
</Link>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Home className="h-5 w-5" />
<span className="sr-only">Dashboard</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Dashboard</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg bg-accent text-accent-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<ShoppingCart className="h-5 w-5" />
<span className="sr-only">Orders</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Orders</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Package className="h-5 w-5" />
<span className="sr-only">Products</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Products</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Users2 className="h-5 w-5" />
<span className="sr-only">Customers</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Customers</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<LineChart className="h-5 w-5" />
<span className="sr-only">Analytics</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Analytics</TooltipContent>
</Tooltip>
</nav>
<nav className="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5">
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Settings className="h-5 w-5" />
<span className="sr-only">Settings</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Settings</TooltipContent>
</Tooltip>
</nav>
</aside>
<div className="flex flex-col sm:gap-4 sm:py-4 sm:pl-14">
<header className="sticky top-0 z-30 flex h-14 items-center gap-4 border-b bg-background px-4 sm:static sm:h-auto sm:border-0 sm:bg-transparent sm:px-6">
<Sheet>
<SheetTrigger asChild>
<Button size="icon" variant="outline" className="sm:hidden">
<PanelLeft className="h-5 w-5" />
<span className="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="sm:max-w-xs">
<nav className="grid gap-6 text-lg font-medium">
<Link
href="#"
className="group flex h-10 w-10 shrink-0 items-center justify-center gap-2 rounded-full bg-primary text-lg font-semibold text-primary-foreground md:text-base"
>
<Package2 className="h-5 w-5 transition-all group-hover:scale-110" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<Home className="h-5 w-5" />
Dashboard
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-foreground"
>
<ShoppingCart className="h-5 w-5" />
Orders
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<Package className="h-5 w-5" />
Products
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<Users2 className="h-5 w-5" />
Customers
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<LineChart className="h-5 w-5" />
Settings
</Link>
</nav>
</SheetContent>
</Sheet>
<Breadcrumb className="hidden md:flex">
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link href="#">Dashboard</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link href="#">Orders</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Recent Orders</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<div className="relative ml-auto flex-1 md:grow-0">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search..."
className="w-full rounded-lg bg-background pl-8 md:w-[200px] lg:w-[336px]"
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon"
className="overflow-hidden rounded-full"
>
<Image
src="/placeholder-user.jpg"
width={36}
height={36}
alt="Avatar"
className="overflow-hidden rounded-full"
loading="lazy"
/>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
<main className="grid flex-1 items-start gap-4 p-4 sm:px-6 sm:py-0 md:gap-8 lg:grid-cols-3 xl:grid-cols-3">
<div className="grid auto-rows-max items-start gap-4 md:gap-8 lg:col-span-2">
<div className="grid gap-4 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-2 xl:grid-cols-4">
<Card className="sm:col-span-2" x-chunk="dashboard-05-chunk-0">
<CardHeader className="pb-3">
<CardTitle>Your Orders</CardTitle>
<CardDescription className="max-w-lg text-balance leading-relaxed">
Introducing Our Dynamic Orders Dashboard for Seamless
Management and Insightful Analysis.
</CardDescription>
</CardHeader>
<CardFooter>
<Button>Create New Order</Button>
</CardFooter>
</Card>
<Card x-chunk="dashboard-05-chunk-1">
<CardHeader className="pb-2">
<CardDescription>This Week</CardDescription>
<CardTitle className="text-4xl">$1,329</CardTitle>
</CardHeader>
<CardContent>
<div className="text-xs text-muted-foreground">
+25% from last week
</div>
</CardContent>
<CardFooter>
<Progress value={25} aria-label="25% increase" />
</CardFooter>
</Card>
<Card x-chunk="dashboard-05-chunk-2">
<CardHeader className="pb-2">
<CardDescription>This Month</CardDescription>
<CardTitle className="text-4xl">$5,329</CardTitle>
</CardHeader>
<CardContent>
<div className="text-xs text-muted-foreground">
+10% from last month
</div>
</CardContent>
<CardFooter>
<Progress value={12} aria-label="12% increase" />
</CardFooter>
</Card>
</div>
<Tabs defaultValue="week">
<div className="flex items-center">
<TabsList>
<TabsTrigger value="week">Week</TabsTrigger>
<TabsTrigger value="month">Month</TabsTrigger>
<TabsTrigger value="year">Year</TabsTrigger>
</TabsList>
<div className="ml-auto flex items-center gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="h-7 gap-1 text-sm"
>
<ListFilter className="h-3.5 w-3.5" />
<span className="sr-only sm:not-sr-only">Filter</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Filter by</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem checked>
Fulfilled
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem>
Declined
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem>
Refunded
</DropdownMenuCheckboxItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
size="sm"
variant="outline"
className="h-7 gap-1 text-sm"
>
<File className="h-3.5 w-3.5" />
<span className="sr-only sm:not-sr-only">Export</span>
</Button>
</div>
</div>
<TabsContent value="week">
<Card x-chunk="dashboard-05-chunk-3">
<CardHeader className="px-7">
<CardTitle>Orders</CardTitle>
<CardDescription>
Recent orders from your store.
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Customer</TableHead>
<TableHead className="hidden sm:table-cell">
Type
</TableHead>
<TableHead className="hidden sm:table-cell">
Status
</TableHead>
<TableHead className="hidden md:table-cell">
Date
</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow className="bg-accent">
<TableCell>
<div className="font-medium">Liam Johnson</div>
<div className="hidden text-sm text-muted-foreground md:inline">
liam@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Sale
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="secondary">
Fulfilled
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-23
</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Olivia Smith</div>
<div className="hidden text-sm text-muted-foreground md:inline">
olivia@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Refund
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="outline">
Declined
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-24
</TableCell>
<TableCell className="text-right">$150.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Noah Williams</div>
<div className="hidden text-sm text-muted-foreground md:inline">
noah@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Subscription
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="secondary">
Fulfilled
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-25
</TableCell>
<TableCell className="text-right">$350.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Emma Brown</div>
<div className="hidden text-sm text-muted-foreground md:inline">
emma@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Sale
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="secondary">
Fulfilled
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-26
</TableCell>
<TableCell className="text-right">$450.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Liam Johnson</div>
<div className="hidden text-sm text-muted-foreground md:inline">
liam@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Sale
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="secondary">
Fulfilled
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-23
</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Liam Johnson</div>
<div className="hidden text-sm text-muted-foreground md:inline">
liam@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Sale
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="secondary">
Fulfilled
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-23
</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Olivia Smith</div>
<div className="hidden text-sm text-muted-foreground md:inline">
olivia@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Refund
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="outline">
Declined
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-24
</TableCell>
<TableCell className="text-right">$150.00</TableCell>
</TableRow>
<TableRow>
<TableCell>
<div className="font-medium">Emma Brown</div>
<div className="hidden text-sm text-muted-foreground md:inline">
emma@example.com
</div>
</TableCell>
<TableCell className="hidden sm:table-cell">
Sale
</TableCell>
<TableCell className="hidden sm:table-cell">
<Badge className="text-xs" variant="secondary">
Fulfilled
</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
2023-06-26
</TableCell>
<TableCell className="text-right">$450.00</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
<div>
<Card className="overflow-hidden" x-chunk="dashboard-05-chunk-4">
<CardHeader className="flex flex-row items-start bg-muted/50">
<div className="grid gap-0.5">
<CardTitle className="group flex items-center gap-2 text-lg">
Order Oe31b70H
<Button
size="icon"
variant="outline"
className="h-6 w-6 opacity-0 transition-opacity group-hover:opacity-100"
>
<Copy className="h-3 w-3" />
<span className="sr-only">Copy Order ID</span>
</Button>
</CardTitle>
<CardDescription>Date: November 23, 2023</CardDescription>
</div>
<div className="ml-auto flex items-center gap-1">
<Button size="sm" variant="outline" className="h-8 gap-1">
<Truck className="h-3.5 w-3.5" />
<span className="lg:sr-only xl:not-sr-only xl:whitespace-nowrap">
Track Order
</span>
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="outline" className="h-8 w-8">
<MoreVertical className="h-3.5 w-3.5" />
<span className="sr-only">More</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Export</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Trash</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</CardHeader>
<CardContent className="p-6 text-sm">
<div className="grid gap-3">
<div className="font-semibold">Order Details</div>
<ul className="grid gap-3">
<li className="flex items-center justify-between">
<span className="text-muted-foreground">
Glimmer Lamps x <span>2</span>
</span>
<span>$250.00</span>
</li>
<li className="flex items-center justify-between">
<span className="text-muted-foreground">
Aqua Filters x <span>1</span>
</span>
<span>$49.00</span>
</li>
</ul>
<Separator className="my-2" />
<ul className="grid gap-3">
<li className="flex items-center justify-between">
<span className="text-muted-foreground">Subtotal</span>
<span>$299.00</span>
</li>
<li className="flex items-center justify-between">
<span className="text-muted-foreground">Shipping</span>
<span>$5.00</span>
</li>
<li className="flex items-center justify-between">
<span className="text-muted-foreground">Tax</span>
<span>$25.00</span>
</li>
<li className="flex items-center justify-between font-semibold">
<span className="text-muted-foreground">Total</span>
<span>$329.00</span>
</li>
</ul>
</div>
<Separator className="my-4" />
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-3">
<div className="font-semibold">Shipping Information</div>
<address className="grid gap-0.5 not-italic text-muted-foreground">
<span>Liam Johnson</span>
<span>1234 Main St.</span>
<span>Anytown, CA 12345</span>
</address>
</div>
<div className="grid auto-rows-max gap-3">
<div className="font-semibold">Billing Information</div>
<div className="text-muted-foreground">
Same as shipping address
</div>
</div>
</div>
<Separator className="my-4" />
<div className="grid gap-3">
<div className="font-semibold">Customer Information</div>
<dl className="grid gap-3">
<div className="flex items-center justify-between">
<dt className="text-muted-foreground">Customer</dt>
<dd>Liam Johnson</dd>
</div>
<div className="flex items-center justify-between">
<dt className="text-muted-foreground">Email</dt>
<dd>
<a href="mailto:">liam@acme.com</a>
</dd>
</div>
<div className="flex items-center justify-between">
<dt className="text-muted-foreground">Phone</dt>
<dd>
<a href="tel:">+1 234 567 890</a>
</dd>
</div>
</dl>
</div>
<Separator className="my-4" />
<div className="grid gap-3">
<div className="font-semibold">Payment Information</div>
<dl className="grid gap-3">
<div className="flex items-center justify-between">
<dt className="flex items-center gap-1 text-muted-foreground">
<CreditCard className="h-4 w-4" />
Visa
</dt>
<dd>**** **** **** 4532</dd>
</div>
</dl>
</div>
</CardContent>
<CardFooter className="flex flex-row items-center border-t bg-muted/50 px-6 py-3">
<div className="text-xs text-muted-foreground">
Updated <time dateTime="2023-11-23">November 23, 2023</time>
</div>
<Pagination className="ml-auto mr-0 w-auto">
<PaginationContent>
<PaginationItem>
<Button size="icon" variant="outline" className="h-6 w-6">
<ChevronLeft className="h-3.5 w-3.5" />
<span className="sr-only">Previous Order</span>
</Button>
</PaginationItem>
<PaginationItem>
<Button size="icon" variant="outline" className="h-6 w-6">
<ChevronRight className="h-3.5 w-3.5" />
<span className="sr-only">Next Order</span>
</Button>
</PaginationItem>
</PaginationContent>
</Pagination>
</CardFooter>
</Card>
</div>
</main>
</div>
</div>
)
}
@@ -1,644 +0,0 @@
import Image from "next/image"
import Link from "next/link"
import {
File,
Home,
LineChart,
ListFilter,
MoreHorizontal,
Package,
Package2,
PanelLeft,
PlusCircle,
Search,
Settings,
ShoppingCart,
Users2,
} from "lucide-react"
import { Badge } from "@/registry/default/ui/badge"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/registry/default/ui/breadcrumb"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/default/ui/dropdown-menu"
import { Input } from "@/registry/default/ui/input"
import { Sheet, SheetContent, SheetTrigger } from "@/registry/default/ui/sheet"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/registry/default/ui/table"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/default/ui/tabs"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/default/ui/tooltip"
export const description =
"An products dashboard with a sidebar navigation. The sidebar has icon navigation. The content area has a breadcrumb and search in the header. It displays a list of products in a table with actions."
export const iframeHeight = "938px"
export const containerClassName = "w-full h-full"
export default function Dashboard() {
return (
<div className="flex min-h-screen w-full flex-col bg-muted/40">
<aside className="fixed inset-y-0 left-0 z-10 hidden w-14 flex-col border-r bg-background sm:flex">
<nav className="flex flex-col items-center gap-4 px-2 sm:py-5">
<Link
href="#"
className="group flex h-9 w-9 shrink-0 items-center justify-center gap-2 rounded-full bg-primary text-lg font-semibold text-primary-foreground md:h-8 md:w-8 md:text-base"
>
<Package2 className="h-4 w-4 transition-all group-hover:scale-110" />
<span className="sr-only">Acme Inc</span>
</Link>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Home className="h-5 w-5" />
<span className="sr-only">Dashboard</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Dashboard</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg bg-accent text-accent-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<ShoppingCart className="h-5 w-5" />
<span className="sr-only">Orders</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Orders</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Package className="h-5 w-5" />
<span className="sr-only">Products</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Products</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Users2 className="h-5 w-5" />
<span className="sr-only">Customers</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Customers</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<LineChart className="h-5 w-5" />
<span className="sr-only">Analytics</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Analytics</TooltipContent>
</Tooltip>
</nav>
<nav className="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5">
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Settings className="h-5 w-5" />
<span className="sr-only">Settings</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Settings</TooltipContent>
</Tooltip>
</nav>
</aside>
<div className="flex flex-col sm:gap-4 sm:py-4 sm:pl-14">
<header className="sticky top-0 z-30 flex h-14 items-center gap-4 border-b bg-background px-4 sm:static sm:h-auto sm:border-0 sm:bg-transparent sm:px-6">
<Sheet>
<SheetTrigger asChild>
<Button size="icon" variant="outline" className="sm:hidden">
<PanelLeft className="h-5 w-5" />
<span className="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="sm:max-w-xs">
<nav className="grid gap-6 text-lg font-medium">
<Link
href="#"
className="group flex h-10 w-10 shrink-0 items-center justify-center gap-2 rounded-full bg-primary text-lg font-semibold text-primary-foreground md:text-base"
>
<Package2 className="h-5 w-5 transition-all group-hover:scale-110" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<Home className="h-5 w-5" />
Dashboard
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<ShoppingCart className="h-5 w-5" />
Orders
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-foreground"
>
<Package className="h-5 w-5" />
Products
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<Users2 className="h-5 w-5" />
Customers
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<LineChart className="h-5 w-5" />
Settings
</Link>
</nav>
</SheetContent>
</Sheet>
<Breadcrumb className="hidden md:flex">
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link href="#">Dashboard</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link href="#">Products</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>All Products</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<div className="relative ml-auto flex-1 md:grow-0">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search..."
className="w-full rounded-lg bg-background pl-8 md:w-[200px] lg:w-[336px]"
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon"
className="overflow-hidden rounded-full"
>
<Image
src="/placeholder-user.jpg"
width={36}
height={36}
alt="Avatar"
className="overflow-hidden rounded-full"
loading="lazy"
/>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
<main className="grid flex-1 items-start gap-4 p-4 sm:px-6 sm:py-0 md:gap-8">
<Tabs defaultValue="all">
<div className="flex items-center">
<TabsList>
<TabsTrigger value="all">All</TabsTrigger>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="draft">Draft</TabsTrigger>
<TabsTrigger value="archived" className="hidden sm:flex">
Archived
</TabsTrigger>
</TabsList>
<div className="ml-auto flex items-center gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-8 gap-1">
<ListFilter className="h-3.5 w-3.5" />
<span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
Filter
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Filter by</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem checked>
Active
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem>Draft</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem>
Archived
</DropdownMenuCheckboxItem>
</DropdownMenuContent>
</DropdownMenu>
<Button size="sm" variant="outline" className="h-8 gap-1">
<File className="h-3.5 w-3.5" />
<span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
Export
</span>
</Button>
<Button size="sm" className="h-8 gap-1">
<PlusCircle className="h-3.5 w-3.5" />
<span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
Add Product
</span>
</Button>
</div>
</div>
<TabsContent value="all">
<Card x-chunk="dashboard-06-chunk-0">
<CardHeader>
<CardTitle>Products</CardTitle>
<CardDescription>
Manage your products and view their sales performance.
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead className="hidden w-[100px] sm:table-cell">
<span className="sr-only">Image</span>
</TableHead>
<TableHead>Name</TableHead>
<TableHead>Status</TableHead>
<TableHead className="hidden md:table-cell">
Price
</TableHead>
<TableHead className="hidden md:table-cell">
Total Sales
</TableHead>
<TableHead className="hidden md:table-cell">
Created at
</TableHead>
<TableHead>
<span className="sr-only">Actions</span>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="hidden sm:table-cell">
<Image
alt="Product image"
className="aspect-square rounded-md object-cover"
height="64"
src="/placeholder.svg"
width="64"
loading="lazy"
/>
</TableCell>
<TableCell className="font-medium">
Laser Lemonade Machine
</TableCell>
<TableCell>
<Badge variant="outline">Draft</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
$499.99
</TableCell>
<TableCell className="hidden md:table-cell">
25
</TableCell>
<TableCell className="hidden md:table-cell">
2023-07-12 10:42 AM
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
aria-haspopup="true"
size="icon"
variant="ghost"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Toggle menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="hidden sm:table-cell">
<Image
alt="Product image"
className="aspect-square rounded-md object-cover"
height="64"
src="/placeholder.svg"
width="64"
loading="lazy"
/>
</TableCell>
<TableCell className="font-medium">
Hypernova Headphones
</TableCell>
<TableCell>
<Badge variant="outline">Active</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
$129.99
</TableCell>
<TableCell className="hidden md:table-cell">
100
</TableCell>
<TableCell className="hidden md:table-cell">
2023-10-18 03:21 PM
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
aria-haspopup="true"
size="icon"
variant="ghost"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Toggle menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="hidden sm:table-cell">
<Image
alt="Product image"
className="aspect-square rounded-md object-cover"
height="64"
src="/placeholder.svg"
width="64"
loading="lazy"
/>
</TableCell>
<TableCell className="font-medium">
AeroGlow Desk Lamp
</TableCell>
<TableCell>
<Badge variant="outline">Active</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
$39.99
</TableCell>
<TableCell className="hidden md:table-cell">
50
</TableCell>
<TableCell className="hidden md:table-cell">
2023-11-29 08:15 AM
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
aria-haspopup="true"
size="icon"
variant="ghost"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Toggle menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="hidden sm:table-cell">
<Image
alt="Product image"
className="aspect-square rounded-md object-cover"
height="64"
src="/placeholder.svg"
width="64"
loading="lazy"
/>
</TableCell>
<TableCell className="font-medium">
TechTonic Energy Drink
</TableCell>
<TableCell>
<Badge variant="secondary">Draft</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
$2.99
</TableCell>
<TableCell className="hidden md:table-cell">
0
</TableCell>
<TableCell className="hidden md:table-cell">
2023-12-25 11:59 PM
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
aria-haspopup="true"
size="icon"
variant="ghost"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Toggle menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="hidden sm:table-cell">
<Image
alt="Product image"
className="aspect-square rounded-md object-cover"
height="64"
src="/placeholder.svg"
width="64"
loading="lazy"
/>
</TableCell>
<TableCell className="font-medium">
Gamer Gear Pro Controller
</TableCell>
<TableCell>
<Badge variant="outline">Active</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
$59.99
</TableCell>
<TableCell className="hidden md:table-cell">
75
</TableCell>
<TableCell className="hidden md:table-cell">
2024-01-01 12:00 AM
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
aria-haspopup="true"
size="icon"
variant="ghost"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Toggle menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="hidden sm:table-cell">
<Image
alt="Product image"
className="aspect-square rounded-md object-cover"
height="64"
src="/placeholder.svg"
width="64"
loading="lazy"
/>
</TableCell>
<TableCell className="font-medium">
Luminous VR Headset
</TableCell>
<TableCell>
<Badge variant="outline">Active</Badge>
</TableCell>
<TableCell className="hidden md:table-cell">
$199.99
</TableCell>
<TableCell className="hidden md:table-cell">
30
</TableCell>
<TableCell className="hidden md:table-cell">
2024-02-14 02:14 PM
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
aria-haspopup="true"
size="icon"
variant="ghost"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Toggle menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
<CardFooter>
<div className="text-xs text-muted-foreground">
Showing <strong>1-10</strong> of <strong>32</strong>{" "}
products
</div>
</CardFooter>
</Card>
</TabsContent>
</Tabs>
</main>
</div>
</div>
)
}
@@ -1,613 +0,0 @@
import Image from "next/image"
import Link from "next/link"
import {
ChevronLeft,
Home,
LineChart,
Package,
Package2,
PanelLeft,
PlusCircle,
Search,
Settings,
ShoppingCart,
Upload,
Users2,
} from "lucide-react"
import { Badge } from "@/registry/default/ui/badge"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/registry/default/ui/breadcrumb"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/default/ui/dropdown-menu"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/default/ui/select"
import { Sheet, SheetContent, SheetTrigger } from "@/registry/default/ui/sheet"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/registry/default/ui/table"
import { Textarea } from "@/registry/default/ui/textarea"
import {
ToggleGroup,
ToggleGroupItem,
} from "@/registry/default/ui/toggle-group"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/default/ui/tooltip"
export const description =
"A product edit page. The product edit page has a form to edit the product details, stock, product category, product status, and product images. The product edit page has a sidebar navigation and a main content area. The main content area has a form to edit the product details, stock, product category, product status, and product images. The sidebar navigation has links to product details, stock, product category, product status, and product images."
export const iframeHeight = "1200px"
export const containerClassName = "w-full h-full"
export default function Dashboard() {
return (
<div className="flex min-h-screen w-full flex-col bg-muted/40">
<aside className="fixed inset-y-0 left-0 z-10 hidden w-14 flex-col border-r bg-background sm:flex">
<nav className="flex flex-col items-center gap-4 px-2 sm:py-5">
<Link
href="#"
className="group flex h-9 w-9 shrink-0 items-center justify-center gap-2 rounded-full bg-primary text-lg font-semibold text-primary-foreground md:h-8 md:w-8 md:text-base"
>
<Package2 className="h-4 w-4 transition-all group-hover:scale-110" />
<span className="sr-only">Acme Inc</span>
</Link>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Home className="h-5 w-5" />
<span className="sr-only">Dashboard</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Dashboard</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg bg-accent text-accent-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<ShoppingCart className="h-5 w-5" />
<span className="sr-only">Orders</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Orders</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Package className="h-5 w-5" />
<span className="sr-only">Products</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Products</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Users2 className="h-5 w-5" />
<span className="sr-only">Customers</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Customers</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<LineChart className="h-5 w-5" />
<span className="sr-only">Analytics</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Analytics</TooltipContent>
</Tooltip>
</nav>
<nav className="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5">
<Tooltip>
<TooltipTrigger asChild>
<Link
href="#"
className="flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:text-foreground md:h-8 md:w-8"
>
<Settings className="h-5 w-5" />
<span className="sr-only">Settings</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right">Settings</TooltipContent>
</Tooltip>
</nav>
</aside>
<div className="flex flex-col sm:gap-4 sm:py-4 sm:pl-14">
<header className="sticky top-0 z-30 flex h-14 items-center gap-4 border-b bg-background px-4 sm:static sm:h-auto sm:border-0 sm:bg-transparent sm:px-6">
<Sheet>
<SheetTrigger asChild>
<Button size="icon" variant="outline" className="sm:hidden">
<PanelLeft className="h-5 w-5" />
<span className="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="sm:max-w-xs">
<nav className="grid gap-6 text-lg font-medium">
<Link
href="#"
className="group flex h-10 w-10 shrink-0 items-center justify-center gap-2 rounded-full bg-primary text-lg font-semibold text-primary-foreground md:text-base"
>
<Package2 className="h-5 w-5 transition-all group-hover:scale-110" />
<span className="sr-only">Acme Inc</span>
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<Home className="h-5 w-5" />
Dashboard
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<ShoppingCart className="h-5 w-5" />
Orders
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-foreground"
>
<Package className="h-5 w-5" />
Products
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<Users2 className="h-5 w-5" />
Customers
</Link>
<Link
href="#"
className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
<LineChart className="h-5 w-5" />
Settings
</Link>
</nav>
</SheetContent>
</Sheet>
<Breadcrumb className="hidden md:flex">
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link href="#">Dashboard</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link href="#">Products</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Edit Product</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<div className="relative ml-auto flex-1 md:grow-0">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search..."
className="w-full rounded-lg bg-background pl-8 md:w-[200px] lg:w-[336px]"
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon"
className="overflow-hidden rounded-full"
>
<Image
src="/placeholder-user.jpg"
width={36}
height={36}
loading="lazy"
alt="Avatar"
className="overflow-hidden rounded-full"
/>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Support</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
<main className="grid flex-1 items-start gap-4 p-4 sm:px-6 sm:py-0 md:gap-8">
<div className="mx-auto grid max-w-[59rem] flex-1 auto-rows-max gap-4">
<div className="flex items-center gap-4">
<Button variant="outline" size="icon" className="h-7 w-7">
<ChevronLeft className="h-4 w-4" />
<span className="sr-only">Back</span>
</Button>
<h1 className="flex-1 shrink-0 whitespace-nowrap text-xl font-semibold tracking-tight sm:grow-0">
Pro Controller
</h1>
<Badge variant="outline" className="ml-auto sm:ml-0">
In stock
</Badge>
<div className="hidden items-center gap-2 md:ml-auto md:flex">
<Button variant="outline" size="sm">
Discard
</Button>
<Button size="sm">Save Product</Button>
</div>
</div>
<div className="grid gap-4 md:grid-cols-[1fr_250px] lg:grid-cols-3 lg:gap-8">
<div className="grid auto-rows-max items-start gap-4 lg:col-span-2 lg:gap-8">
<Card x-chunk="dashboard-07-chunk-0">
<CardHeader>
<CardTitle>Product Details</CardTitle>
<CardDescription>
Lipsum dolor sit amet, consectetur adipiscing elit
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-6">
<div className="grid gap-3">
<Label htmlFor="name">Name</Label>
<Input
id="name"
type="text"
className="w-full"
defaultValue="Gamer Gear Pro Controller"
/>
</div>
<div className="grid gap-3">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
defaultValue="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam auctor, nisl nec ultricies ultricies, nunc nisl ultricies nunc, nec ultricies nunc nisl nec nunc."
className="min-h-32"
/>
</div>
</div>
</CardContent>
</Card>
<Card x-chunk="dashboard-07-chunk-1">
<CardHeader>
<CardTitle>Stock</CardTitle>
<CardDescription>
Lipsum dolor sit amet, consectetur adipiscing elit
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">SKU</TableHead>
<TableHead>Stock</TableHead>
<TableHead>Price</TableHead>
<TableHead className="w-[100px]">Size</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-semibold">
GGPC-001
</TableCell>
<TableCell>
<Label htmlFor="stock-1" className="sr-only">
Stock
</Label>
<Input
id="stock-1"
type="number"
defaultValue="100"
/>
</TableCell>
<TableCell>
<Label htmlFor="price-1" className="sr-only">
Price
</Label>
<Input
id="price-1"
type="number"
defaultValue="99.99"
/>
</TableCell>
<TableCell>
<ToggleGroup
type="single"
defaultValue="s"
variant="outline"
>
<ToggleGroupItem value="s">S</ToggleGroupItem>
<ToggleGroupItem value="m">M</ToggleGroupItem>
<ToggleGroupItem value="l">L</ToggleGroupItem>
</ToggleGroup>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-semibold">
GGPC-002
</TableCell>
<TableCell>
<Label htmlFor="stock-2" className="sr-only">
Stock
</Label>
<Input
id="stock-2"
type="number"
defaultValue="143"
/>
</TableCell>
<TableCell>
<Label htmlFor="price-2" className="sr-only">
Price
</Label>
<Input
id="price-2"
type="number"
defaultValue="99.99"
/>
</TableCell>
<TableCell>
<ToggleGroup
type="single"
defaultValue="m"
variant="outline"
>
<ToggleGroupItem value="s">S</ToggleGroupItem>
<ToggleGroupItem value="m">M</ToggleGroupItem>
<ToggleGroupItem value="l">L</ToggleGroupItem>
</ToggleGroup>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-semibold">
GGPC-003
</TableCell>
<TableCell>
<Label htmlFor="stock-3" className="sr-only">
Stock
</Label>
<Input
id="stock-3"
type="number"
defaultValue="32"
/>
</TableCell>
<TableCell>
<Label htmlFor="price-3" className="sr-only">
Stock
</Label>
<Input
id="price-3"
type="number"
defaultValue="99.99"
/>
</TableCell>
<TableCell>
<ToggleGroup
type="single"
defaultValue="s"
variant="outline"
>
<ToggleGroupItem value="s">S</ToggleGroupItem>
<ToggleGroupItem value="m">M</ToggleGroupItem>
<ToggleGroupItem value="l">L</ToggleGroupItem>
</ToggleGroup>
</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
<CardFooter className="justify-center border-t p-4">
<Button size="sm" variant="ghost" className="gap-1">
<PlusCircle className="h-3.5 w-3.5" />
Add Variant
</Button>
</CardFooter>
</Card>
<Card x-chunk="dashboard-07-chunk-2">
<CardHeader>
<CardTitle>Product Category</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-6 sm:grid-cols-3">
<div className="grid gap-3">
<Label htmlFor="category">Category</Label>
<Select>
<SelectTrigger
id="category"
aria-label="Select category"
>
<SelectValue placeholder="Select category" />
</SelectTrigger>
<SelectContent>
<SelectItem value="clothing">Clothing</SelectItem>
<SelectItem value="electronics">
Electronics
</SelectItem>
<SelectItem value="accessories">
Accessories
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-3">
<Label htmlFor="subcategory">
Subcategory (optional)
</Label>
<Select>
<SelectTrigger
id="subcategory"
aria-label="Select subcategory"
>
<SelectValue placeholder="Select subcategory" />
</SelectTrigger>
<SelectContent>
<SelectItem value="t-shirts">T-Shirts</SelectItem>
<SelectItem value="hoodies">Hoodies</SelectItem>
<SelectItem value="sweatshirts">
Sweatshirts
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</CardContent>
</Card>
</div>
<div className="grid auto-rows-max items-start gap-4 lg:gap-8">
<Card x-chunk="dashboard-07-chunk-3">
<CardHeader>
<CardTitle>Product Status</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-6">
<div className="grid gap-3">
<Label htmlFor="status">Status</Label>
<Select>
<SelectTrigger id="status" aria-label="Select status">
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="draft">Draft</SelectItem>
<SelectItem value="published">Active</SelectItem>
<SelectItem value="archived">Archived</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</CardContent>
</Card>
<Card
className="overflow-hidden" x-chunk="dashboard-07-chunk-4"
>
<CardHeader>
<CardTitle>Product Images</CardTitle>
<CardDescription>
Lipsum dolor sit amet, consectetur adipiscing elit
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-2">
<Image
alt="Product image"
className="aspect-square w-full rounded-md object-cover"
height="300"
src="/placeholder.svg"
width="300"
loading="lazy"
/>
<div className="grid grid-cols-3 gap-2">
<button>
<Image
alt="Product thumbnail image"
className="aspect-square w-full rounded-md object-cover"
height="84"
src="/placeholder.svg"
width="84"
loading="lazy"
/>
</button>
<button>
<Image
alt="Product thumbnail image"
className="aspect-square w-full rounded-md object-cover"
height="84"
src="/placeholder.svg"
width="84"
loading="lazy"
/>
</button>
<button className="flex aspect-square w-full items-center justify-center rounded-md border border-dashed">
<Upload className="h-4 w-4 text-muted-foreground" />
<span className="sr-only">Upload</span>
</button>
</div>
</div>
</CardContent>
</Card>
<Card x-chunk="dashboard-07-chunk-5">
<CardHeader>
<CardTitle>Archive Product</CardTitle>
<CardDescription>
Lipsum dolor sit amet, consectetur adipiscing elit.
</CardDescription>
</CardHeader>
<CardContent>
<div></div>
<Button size="sm" variant="secondary">
Archive Product
</Button>
</CardContent>
</Card>
</div>
</div>
<div className="flex items-center justify-center gap-2 md:hidden">
<Button variant="outline" size="sm">
Discard
</Button>
<Button size="sm">Save Product</Button>
</div>
</div>
</main>
</div>
</div>
)
}
@@ -1,91 +0,0 @@
"use client"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export default function Page() {
return (
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
<div className="w-full max-w-sm">
<LoginForm01 />
</div>
</div>
)
}
export function LoginForm01({
className,
...props
}: React.ComponentPropsWithoutRef<"div">) {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
// TODO: Add your authentication logic here
console.log("Form submitted:", Object.fromEntries(formData))
// Example: await signIn(formData)
}
return (
<div className={cn("flex flex-col gap-6", className)} {...props}>
<Card>
<CardHeader>
<CardTitle className="text-2xl">Login</CardTitle>
<CardDescription>
Enter your email below to login to your account
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit}>
<div className="flex flex-col gap-6">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
name="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<div className="flex items-center">
<Label htmlFor="password">Password</Label>
<a
href="#"
className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
>
Forgot your password?
</a>
</div>
<Input id="password" name="password" type="password" required />
</div>
<Button type="submit" className="w-full">
Login
</Button>
<Button variant="outline" className="w-full">
Login with Google
</Button>
</div>
<div className="mt-4 text-center text-sm">
Don&apos;t have an account?{" "}
<a href="#" className="underline underline-offset-4">
Sign up
</a>
</div>
</form>
</CardContent>
</Card>
</div>
)
}
@@ -1,91 +0,0 @@
"use client"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/default/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/registry/default/ui/card"
import { Input } from "@/registry/default/ui/input"
import { Label } from "@/registry/default/ui/label"
export default function Page() {
return (
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
<div className="w-full max-w-sm">
<LoginForm02 />
</div>
</div>
)
}
export function LoginForm02({
className,
...props
}: React.ComponentPropsWithoutRef<"div">) {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
// TODO: Add your authentication logic here
console.log("Form submitted:", Object.fromEntries(formData))
// Example: await signIn(formData)
}
return (
<div className={cn("flex flex-col gap-6", className)} {...props}>
<Card>
<CardHeader>
<CardTitle className="text-2xl">Login</CardTitle>
<CardDescription>
Enter your email below to login to your account
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit}>
<div className="flex flex-col gap-6">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
name="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<div className="flex items-center">
<Label htmlFor="password">Password</Label>
<a
href="#"
className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
>
Forgot your password?
</a>
</div>
<Input id="password" name="password" type="password" required />
</div>
<Button type="submit" className="w-full">
Login
</Button>
<Button variant="outline" className="w-full">
Login with Google
</Button>
</div>
<div className="mt-4 text-center text-sm">
Don&apos;t have an account?{" "}
<a href="#" className="underline underline-offset-4">
Sign up
</a>
</div>
</form>
</CardContent>
</Card>
</div>
)
}

Some files were not shown because too many files have changed in this diff Show More