├── .commitlintrc.json
├── .editorconfig
├── .eslintrc.json
├── .github
└── workflows
│ ├── code-check.yml
│ ├── issue-stale.yml
│ ├── prerelease-comment.yml
│ ├── prerelease.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .vscode
└── settings.json
├── LICENSE.md
├── README.md
├── apps
└── www
│ ├── .env.example
│ ├── .gitignore
│ ├── .prettierignore
│ ├── __registry__
│ ├── .autogenerated
│ ├── .gitkeep
│ ├── README.md
│ └── index.tsx
│ ├── app
│ ├── (app)
│ │ ├── docs
│ │ │ ├── [[...slug]]
│ │ │ │ └── page.tsx
│ │ │ └── layout.tsx
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ └── themes
│ │ │ ├── page.tsx
│ │ │ └── tabs.tsx
│ ├── api
│ │ ├── chat
│ │ │ └── route.ts
│ │ └── transcribe
│ │ │ └── route.ts
│ ├── demo
│ │ └── page.tsx
│ ├── layout.tsx
│ └── robots.txt
│ ├── assets
│ └── fonts
│ │ ├── Inter-Bold.ttf
│ │ └── Inter-Regular.ttf
│ ├── components
│ ├── analytics.tsx
│ ├── announcement.tsx
│ ├── callout.tsx
│ ├── code-block-wrapper.tsx
│ ├── command-menu.tsx
│ ├── component-example.tsx
│ ├── component-preview.tsx
│ ├── component-source.tsx
│ ├── copy-button.tsx
│ ├── framework-docs.tsx
│ ├── icons.tsx
│ ├── main-nav.tsx
│ ├── mdx-components.tsx
│ ├── mobile-nav.tsx
│ ├── mode-toggle.tsx
│ ├── page-header.tsx
│ ├── pager.tsx
│ ├── providers.tsx
│ ├── sidebar-nav.tsx
│ ├── site-footer.tsx
│ ├── site-header.tsx
│ ├── tailwind-indicator.tsx
│ ├── theme-customizer.tsx
│ ├── theme-switcher.tsx
│ ├── theme-wrapper.tsx
│ └── toc.tsx
│ ├── config
│ ├── docs.ts
│ └── site.ts
│ ├── content
│ └── docs
│ │ ├── components
│ │ ├── audio-visualizer.mdx
│ │ ├── chat-message.mdx
│ │ ├── chat.mdx
│ │ ├── copy-button.mdx
│ │ ├── file-preview.mdx
│ │ ├── markdown-renderer.mdx
│ │ ├── message-input.mdx
│ │ ├── message-list.mdx
│ │ ├── prompt-suggestions.mdx
│ │ └── typing-indicator.mdx
│ │ ├── index.mdx
│ │ └── installation.mdx
│ ├── contentlayer.config.js
│ ├── hooks
│ ├── use-config.ts
│ └── use-mounted.ts
│ ├── lib
│ ├── delay.ts
│ ├── events.ts
│ ├── fonts.ts
│ ├── highlighter-theme.json
│ ├── rehype-component.ts
│ ├── rehype-npm-command.ts
│ ├── toc.ts
│ ├── utils.ts
│ ├── utils
│ │ └── audio.ts
│ └── weather.ts
│ ├── next-env.d.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── pages
│ ├── .gitkeep
│ └── api
│ │ ├── .gitkeep
│ │ ├── components.json
│ │ └── components.ts
│ ├── postcss.config.cjs
│ ├── public
│ ├── avatars
│ │ ├── 01.png
│ │ ├── 02.png
│ │ ├── 03.png
│ │ ├── 04.png
│ │ ├── 05.png
│ │ └── shadcn.jpg
│ ├── examples
│ │ ├── authentication-dark.png
│ │ ├── authentication-light.png
│ │ ├── cards-dark.png
│ │ ├── cards-light.png
│ │ ├── dashboard-dark.png
│ │ ├── dashboard-light.png
│ │ ├── forms-dark.png
│ │ ├── forms-light.png
│ │ ├── mail-dark.png
│ │ ├── mail-light.png
│ │ ├── music-dark.png
│ │ ├── music-light.png
│ │ ├── playground-dark.png
│ │ ├── playground-light.png
│ │ ├── tasks-dark.png
│ │ └── tasks-light.png
│ ├── favicon.ico
│ ├── images
│ │ ├── blocks
│ │ │ ├── demo-sidebar-controlled-dark.png
│ │ │ ├── demo-sidebar-controlled.png
│ │ │ ├── demo-sidebar-dark.png
│ │ │ ├── demo-sidebar-footer-dark.png
│ │ │ ├── demo-sidebar-footer.png
│ │ │ ├── demo-sidebar-group-action-dark.png
│ │ │ ├── demo-sidebar-group-action.png
│ │ │ ├── demo-sidebar-group-collapsible-dark.png
│ │ │ ├── demo-sidebar-group-collapsible.png
│ │ │ ├── demo-sidebar-group-dark.png
│ │ │ ├── demo-sidebar-group.png
│ │ │ ├── demo-sidebar-header-dark.png
│ │ │ ├── demo-sidebar-header.png
│ │ │ ├── demo-sidebar-menu-action-dark.png
│ │ │ ├── demo-sidebar-menu-action.png
│ │ │ ├── demo-sidebar-menu-badge-dark.png
│ │ │ ├── demo-sidebar-menu-badge.png
│ │ │ ├── demo-sidebar-menu-collapsible-dark.png
│ │ │ ├── demo-sidebar-menu-collapsible.png
│ │ │ ├── demo-sidebar-menu-dark.png
│ │ │ ├── demo-sidebar-menu-sub-dark.png
│ │ │ ├── demo-sidebar-menu-sub.png
│ │ │ ├── demo-sidebar-menu.png
│ │ │ ├── demo-sidebar-rsc-dark.png
│ │ │ ├── demo-sidebar-rsc.png
│ │ │ ├── demo-sidebar.png
│ │ │ ├── login-01-dark.png
│ │ │ ├── login-01.png
│ │ │ ├── sidebar-01-dark.png
│ │ │ ├── sidebar-01.png
│ │ │ ├── sidebar-02-dark.png
│ │ │ ├── sidebar-02.png
│ │ │ ├── sidebar-03-dark.png
│ │ │ ├── sidebar-03.png
│ │ │ ├── sidebar-04-dark.png
│ │ │ ├── sidebar-04.png
│ │ │ ├── sidebar-05-dark.png
│ │ │ ├── sidebar-05.png
│ │ │ ├── sidebar-06-dark.png
│ │ │ ├── sidebar-06.png
│ │ │ ├── sidebar-07-dark.png
│ │ │ ├── sidebar-07.png
│ │ │ ├── sidebar-08-dark.png
│ │ │ ├── sidebar-08.png
│ │ │ ├── sidebar-09-dark.png
│ │ │ ├── sidebar-09.png
│ │ │ ├── sidebar-10-dark.png
│ │ │ ├── sidebar-10.png
│ │ │ ├── sidebar-11-dark.png
│ │ │ ├── sidebar-11.png
│ │ │ ├── sidebar-12-dark.png
│ │ │ ├── sidebar-12.png
│ │ │ ├── sidebar-13-dark.png
│ │ │ ├── sidebar-13.png
│ │ │ ├── sidebar-14-dark.png
│ │ │ ├── sidebar-14.png
│ │ │ ├── sidebar-15-dark.png
│ │ │ └── sidebar-15.png
│ │ ├── dashboard-1-dark.jpg
│ │ ├── dashboard-1.jpg
│ │ ├── dashboard-2-dark.jpg
│ │ ├── dashboard-2.jpg
│ │ ├── dashboard-3-dark.jpg
│ │ ├── dashboard-3.jpg
│ │ ├── lift-mode-dark.png
│ │ ├── lift-mode-light.png
│ │ ├── open-in-v0-dark.png
│ │ ├── open-in-v0.png
│ │ ├── sidebar-menu-dark.png
│ │ ├── sidebar-menu.png
│ │ ├── sidebar-structure-dark.png
│ │ ├── sidebar-structure.png
│ │ ├── style-with-theming.jpg
│ │ └── style.jpg
│ ├── og.jpg
│ ├── placeholder-logo.svg
│ ├── placeholder-user.jpg
│ ├── placeholder.svg
│ ├── r
│ │ ├── audio-utils.json
│ │ ├── audio-visualizer.json
│ │ ├── chat-message.json
│ │ ├── chat.json
│ │ ├── copy-button.json
│ │ ├── file-preview.json
│ │ ├── index.json
│ │ ├── interrupt-prompt.json
│ │ ├── markdown-renderer.json
│ │ ├── message-input.json
│ │ ├── message-list.json
│ │ ├── prompt-suggestions.json
│ │ ├── typing-indicator.json
│ │ ├── use-audio-recording.json
│ │ ├── use-auto-scroll.json
│ │ ├── use-autosize-textarea.json
│ │ └── use-copy-to-clipboard.json
│ ├── registry
│ │ └── themes.css
│ └── schema.json
│ ├── registry
│ ├── .eslintrc.json
│ ├── default
│ │ ├── example
│ │ │ ├── chat-demo.tsx
│ │ │ ├── chat-message-actions-demo.tsx
│ │ │ ├── chat-message-animations-demo.tsx
│ │ │ ├── chat-message-demo.tsx
│ │ │ ├── chat-message-timestamp-demo.tsx
│ │ │ ├── copy-button-code-block-demo.tsx
│ │ │ ├── copy-button-custom-message-demo.tsx
│ │ │ ├── copy-button-demo.tsx
│ │ │ ├── file-preview-demo.tsx
│ │ │ ├── markdown-renderer-demo.tsx
│ │ │ ├── message-input-demo.tsx
│ │ │ ├── message-list-demo.tsx
│ │ │ ├── prompt-suggestions-demo.tsx
│ │ │ └── typing-indicator-demo.tsx
│ │ ├── hooks
│ │ │ ├── use-audio-recording.ts
│ │ │ ├── use-auto-scroll.ts
│ │ │ ├── use-autosize-textarea.ts
│ │ │ ├── use-copy-to-clipboard.ts
│ │ │ └── use-toast.ts
│ │ ├── lib
│ │ │ └── audio-utils.ts
│ │ └── ui
│ │ │ ├── audio-visualizer.tsx
│ │ │ ├── button.tsx
│ │ │ ├── chat-message.tsx
│ │ │ ├── chat.tsx
│ │ │ ├── collapsible.tsx
│ │ │ ├── copy-button.tsx
│ │ │ ├── file-preview.tsx
│ │ │ ├── interrupt-prompt.tsx
│ │ │ ├── markdown-renderer.tsx
│ │ │ ├── message-input.tsx
│ │ │ ├── message-list.tsx
│ │ │ ├── prompt-suggestions.tsx
│ │ │ ├── select.tsx
│ │ │ ├── toast.tsx
│ │ │ ├── toaster.tsx
│ │ │ └── typing-indicator.tsx
│ ├── index.ts
│ ├── new-york
│ │ ├── hooks
│ │ │ └── use-toast.ts
│ │ └── ui
│ │ │ ├── accordion.tsx
│ │ │ ├── alert.tsx
│ │ │ ├── aspect-ratio.tsx
│ │ │ ├── badge.tsx
│ │ │ ├── button.tsx
│ │ │ ├── collapsible.tsx
│ │ │ ├── command.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── drawer.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── label.tsx
│ │ │ ├── popover.tsx
│ │ │ ├── scroll-area.tsx
│ │ │ ├── separator.tsx
│ │ │ ├── sheet.tsx
│ │ │ ├── skeleton.tsx
│ │ │ ├── sonner.tsx
│ │ │ ├── tabs.tsx
│ │ │ ├── toast.tsx
│ │ │ ├── toaster.tsx
│ │ │ └── tooltip.tsx
│ ├── registry-base-colors.ts
│ └── schema.ts
│ ├── scripts
│ ├── build-registry.mts
│ ├── capture-screenshots.mts
│ └── fix-import.mts
│ ├── styles
│ ├── globals.css
│ └── mdx.css
│ ├── tailwind.config.cjs
│ ├── tsconfig.json
│ ├── tsconfig.scripts.json
│ └── types
│ ├── nav.ts
│ └── unist.ts
├── package.json
├── patches
└── @ai-sdk__groq@1.1.14.patch
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── postcss.config.cjs
├── prettier.config.cjs
├── tailwind.config.cjs
├── tsconfig.json
├── turbo.json
└── vitest.config.ts
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@commitlint/config-conventional"]
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/eslintrc",
3 | "root": true,
4 | "extends": [
5 | "next/core-web-vitals",
6 | "turbo",
7 | "prettier",
8 | "plugin:tailwindcss/recommended"
9 | ],
10 | "plugins": ["tailwindcss"],
11 | "ignorePatterns": ["**/fixtures/**"],
12 | "rules": {
13 | "@next/next/no-html-link-for-pages": "off",
14 | "tailwindcss/no-custom-classname": "off",
15 | "tailwindcss/classnames-order": "error"
16 | },
17 | "settings": {
18 | "tailwindcss": {
19 | "callees": ["cn", "cva"],
20 | "config": "tailwind.config.cjs"
21 | },
22 | "next": {
23 | "rootDir": ["apps/*/"]
24 | }
25 | },
26 | "overrides": [
27 | {
28 | "files": ["*.ts", "*.tsx"],
29 | "parser": "@typescript-eslint/parser"
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/issue-stale.yml:
--------------------------------------------------------------------------------
1 | # Adapted from vercel/next.js
2 | name: Issue Stale
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # This runs every day 20 minutes before midnight: https://crontab.guru/#40_23_*_*_*
7 | - cron: "40 23 * * *"
8 |
9 | jobs:
10 | stale:
11 | runs-on: ubuntu-latest
12 | if: github.repository_owner == 'shadcn-ui'
13 | steps:
14 | - uses: actions/stale@v4
15 | id: stale-no-repro
16 | name: "Close stale issues with no reproduction"
17 | with:
18 | repo-token: ${{ secrets.STALE_TOKEN }}
19 | close-issue-message: "This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please reopen or leave a comment. Thank you.\n(This is an automated message.)"
20 | days-before-issue-close: 7
21 | days-before-issue-stale: 30
22 | stale-pr-label: "stale?"
23 | days-before-pr-close: 7
24 | days-before-pr-stale: 15
25 | only-pr-labels: "postpone: more info or changes requested,please add a reproduction"
26 | exempt-issue-labels: "roadmap,next,bug"
27 | operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close
28 |
--------------------------------------------------------------------------------
/.github/workflows/prerelease-comment.yml:
--------------------------------------------------------------------------------
1 | # Adapted from create-t3-app.
2 | name: Write Beta Release comment
3 |
4 | on:
5 | workflow_run:
6 | workflows: ["Release - Beta"]
7 | types:
8 | - completed
9 |
10 | jobs:
11 | comment:
12 | if: |
13 | github.repository_owner == 'shadcn-ui' &&
14 | ${{ github.event.workflow_run.conclusion == 'success' }}
15 | runs-on: ubuntu-latest
16 | name: Write comment to the PR
17 | steps:
18 | - name: "Comment on PR"
19 | uses: actions/github-script@v6
20 | with:
21 | github-token: ${{ secrets.GITHUB_TOKEN }}
22 | script: |
23 | const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
24 | owner: context.repo.owner,
25 | repo: context.repo.repo,
26 | run_id: context.payload.workflow_run.id,
27 | });
28 |
29 | for (const artifact of allArtifacts.data.artifacts) {
30 | // Extract the PR number and package version from the artifact name
31 | const match = /^npm-package-shadcn@(.*?)-pr-(\d+)/.exec(artifact.name);
32 |
33 | if (match) {
34 | require("fs").appendFileSync(
35 | process.env.GITHUB_ENV,
36 | `\nBETA_PACKAGE_VERSION=${match[1]}` +
37 | `\nWORKFLOW_RUN_PR=${match[2]}` +
38 | `\nWORKFLOW_RUN_ID=${context.payload.workflow_run.id}`
39 | );
40 | break;
41 | }
42 | }
43 |
44 | - name: "Comment on PR with Link"
45 | uses: marocchino/sticky-pull-request-comment@v2
46 | with:
47 | number: ${{ env.WORKFLOW_RUN_PR }}
48 | message: |
49 | A new prerelease is available for testing:
50 |
51 | ```sh
52 | npx shadcn@${{ env.BETA_PACKAGE_VERSION }}
53 | ```
54 |
55 | - name: "Remove the autorelease label once published"
56 | uses: actions/github-script@v6
57 | with:
58 | github-token: ${{ secrets.GITHUB_TOKEN }}
59 | script: |
60 | github.rest.issues.removeLabel({
61 | owner: context.repo.owner,
62 | repo: context.repo.repo,
63 | issue_number: '${{ env.WORKFLOW_RUN_PR }}',
64 | name: '🚀 autorelease',
65 | });
66 |
--------------------------------------------------------------------------------
/.github/workflows/prerelease.yml:
--------------------------------------------------------------------------------
1 | # Adapted from create-t3-app.
2 |
3 | name: Release - Beta
4 |
5 | on:
6 | pull_request:
7 | types: [labeled]
8 | branches:
9 | - main
10 | jobs:
11 | prerelease:
12 | if: |
13 | github.repository_owner == 'shadcn-ui' &&
14 | contains(github.event.pull_request.labels.*.name, '🚀 autorelease')
15 | name: Build & Publish a beta release to NPM
16 | runs-on: ubuntu-latest
17 | environment: Preview
18 |
19 | steps:
20 | - name: Checkout Repo
21 | uses: actions/checkout@v3
22 | with:
23 | fetch-depth: 0
24 |
25 | - name: Use PNPM
26 | uses: pnpm/action-setup@v4
27 | with:
28 | version: 9.0.6
29 |
30 | - name: Use Node.js 18
31 | uses: actions/setup-node@v3
32 | with:
33 | node-version: 18
34 | cache: "pnpm"
35 |
36 | - name: Install NPM Dependencies
37 | run: pnpm install
38 |
39 | - name: Modify package.json version
40 | run: node .github/version-script-beta.js
41 |
42 | - name: Authenticate to NPM
43 | run: echo "//registry.npmjs.org/:_authToken=$NPM_ACCESS_TOKEN" >> packages/shadcn/.npmrc
44 | env:
45 | NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
46 |
47 | - name: Publish Beta to NPM
48 | run: pnpm pub:beta
49 |
50 | - name: get-npm-version
51 | id: package-version
52 | uses: martinbeentjes/npm-get-version-action@main
53 | with:
54 | path: packages/shadcn
55 |
56 | - name: Upload packaged artifact
57 | uses: actions/upload-artifact@v3
58 | with:
59 | name: npm-package-shadcn@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
60 | path: packages/shadcn/dist/index.js
61 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # Adapted from create-t3-app.
2 |
3 | name: Release
4 |
5 | on:
6 | push:
7 | branches:
8 | - main
9 |
10 | jobs:
11 | release:
12 | if: ${{ github.repository_owner == 'shadcn-ui' }}
13 | name: Create a PR for release workflow
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout Repo
17 | uses: actions/checkout@v3
18 | with:
19 | fetch-depth: 0
20 |
21 | - name: Use PNPM
22 | uses: pnpm/action-setup@v4
23 | with:
24 | version: 9.0.6
25 |
26 | - name: Use Node.js 18
27 | uses: actions/setup-node@v3
28 | with:
29 | version: 9.0.6
30 | node-version: 18
31 | cache: "pnpm"
32 |
33 | - name: Install NPM Dependencies
34 | run: pnpm install
35 |
36 | # - name: Check for errors
37 | # run: pnpm check
38 |
39 | - name: Build the package
40 | run: pnpm shadcn:build
41 |
42 | - name: Create Version PR or Publish to NPM
43 | id: changesets
44 | uses: changesets/action@v1.4.1
45 | with:
46 | commit: "chore(release): version packages"
47 | title: "chore(release): version packages"
48 | version: node .github/changeset-version.js
49 | publish: npx changeset publish
50 | env:
51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 | NPM_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
53 | NODE_ENV: "production"
54 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | pull_request:
5 | branches: ["*"]
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 | name: pnpm test
11 | steps:
12 | - uses: actions/checkout@v3
13 | with:
14 | fetch-depth: 0
15 |
16 | - name: Install Node.js
17 | uses: actions/setup-node@v3
18 | with:
19 | node-version: 18
20 |
21 | - uses: pnpm/action-setup@v4
22 | name: Install pnpm
23 | id: pnpm-install
24 | with:
25 | version: 9.0.6
26 | run_install: false
27 |
28 | - name: Get pnpm store directory
29 | id: pnpm-cache
30 | run: |
31 | echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
32 | - uses: actions/cache@v3
33 | name: Setup pnpm cache
34 | with:
35 | path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
36 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
37 | restore-keys: |
38 | ${{ runner.os }}-pnpm-store-
39 | - name: Install dependencies
40 | run: pnpm install
41 |
42 | - run: pnpm test
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 |
11 | # next.js
12 | .next/
13 | out/
14 | build
15 |
16 | # misc
17 | .DS_Store
18 | *.pem
19 |
20 | # debug
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | .pnpm-debug.log*
25 |
26 | # local env files
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # turbo
33 | .turbo
34 |
35 | .contentlayer
36 | tsconfig.tsbuildinfo
37 |
38 | # ide
39 | .idea
40 | .fleet
41 | .vscode
42 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | auto-install-peers=true
2 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.5.1
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | .next
4 | build
5 | .contentlayer
6 | apps/www/pages/api/registry.json
7 | **/fixtures
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [
3 | { "pattern": "apps/*/" },
4 | { "pattern": "packages/*/" }
5 | ],
6 | "tailwindCSS.experimental.classRegex": [
7 | ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
8 | ["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
9 | ],
10 | "vitest.debugExclude": [
11 | "/**",
12 | "**/node_modules/**",
13 | "**/fixtures/**"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 - 2024 shadcn, Blazity
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/apps/www/.env.example:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | # App
3 | # -----------------------------------------------------------------------------
4 | NEXT_PUBLIC_APP_URL=http://localhost:3001
5 |
--------------------------------------------------------------------------------
/apps/www/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .env
--------------------------------------------------------------------------------
/apps/www/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | .next
4 | build
5 | .contentlayer
6 | __registry__/index.tsx
7 | app/examples/mail/components/mail.tsx
8 |
--------------------------------------------------------------------------------
/apps/www/__registry__/.autogenerated:
--------------------------------------------------------------------------------
1 | // The content of this directory is autogenerated by the registry server.
2 |
--------------------------------------------------------------------------------
/apps/www/__registry__/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/__registry__/.gitkeep
--------------------------------------------------------------------------------
/apps/www/__registry__/README.md:
--------------------------------------------------------------------------------
1 | > Files inside this directory is autogenerated by `./scripts/build-registry.ts`. **Do not edit them manually.** - shadcn
2 |
--------------------------------------------------------------------------------
/apps/www/app/(app)/docs/layout.tsx:
--------------------------------------------------------------------------------
1 | import { docsConfig } from "@/config/docs"
2 | import { DocsSidebarNav } from "@/components/sidebar-nav"
3 | import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
4 |
5 | interface DocsLayoutProps {
6 | children: React.ReactNode
7 | }
8 |
9 | export default function DocsLayout({ children }: DocsLayoutProps) {
10 | return (
11 |
12 |
13 |
18 | {children}
19 |
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/apps/www/app/(app)/layout.tsx:
--------------------------------------------------------------------------------
1 | import { SiteFooter } from "@/components/site-footer"
2 | import { SiteHeader } from "@/components/site-header"
3 |
4 | interface AppLayoutProps {
5 | children: React.ReactNode
6 | }
7 |
8 | export default function AppLayout({ children }: AppLayoutProps) {
9 | return (
10 | <>
11 |
12 | {children}
13 |
14 | >
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/apps/www/app/(app)/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link"
2 |
3 | import { siteConfig } from "@/config/site"
4 | import {
5 | PageActions,
6 | PageHeader,
7 | PageHeaderDescription,
8 | PageHeaderHeading,
9 | } from "@/components/page-header"
10 | import ChatDemo from "@/registry/default/example/chat-demo"
11 | import { Button } from "@/registry/new-york/ui/button"
12 |
13 | export default function IndexPage() {
14 | return (
15 |
16 |
17 |
18 | Build beautiful AI apps in hours, not days.
19 |
20 |
21 | Beautifully designed chatbot components based on shadcn/ui. Fully
22 | customizable and owned by you.
23 |
24 |
25 |
26 | Get Started
27 |
28 |
29 |
34 | GitHub
35 |
36 |
37 |
38 |
39 |
40 |
59 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/apps/www/app/(app)/themes/page.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next"
2 |
3 | import "public/registry/themes.css"
4 | import { Announcement } from "@/components/announcement"
5 | import {
6 | PageActions,
7 | PageHeader,
8 | PageHeaderDescription,
9 | PageHeaderHeading,
10 | } from "@/components/page-header"
11 | import { ThemeCustomizer } from "@/components/theme-customizer"
12 | import { ThemeWrapper } from "@/components/theme-wrapper"
13 | import { ThemesTabs } from "@/app/(app)/themes/tabs"
14 |
15 | export const metadata: Metadata = {
16 | title: "Themes",
17 | description: "Hand-picked themes that you can copy and paste into your apps.",
18 | }
19 |
20 | export default function ThemesPage() {
21 | return (
22 |
23 |
27 |
28 | {/* */}
29 |
30 | Add colors. Make it yours.
31 |
32 |
33 | Make it yours
34 |
35 |
36 | Hand-picked themes that you can copy and paste into your apps.
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/apps/www/app/(app)/themes/tabs.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 |
5 | import { useConfig } from "@/hooks/use-config"
6 | import { ThemeWrapper } from "@/components/theme-wrapper"
7 | import ChatDemo from "@/registry/default/example/chat-demo"
8 | import { Skeleton } from "@/registry/new-york/ui/skeleton"
9 |
10 | export function ThemesTabs() {
11 | const [mounted, setMounted] = React.useState(false)
12 | const [config] = useConfig()
13 |
14 | React.useEffect(() => {
15 | setMounted(true)
16 | }, [])
17 |
18 | return (
19 |
20 | {!mounted ? (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | ) : (
65 |
66 |
67 |
68 |
69 |
70 | )}
71 |
72 | )
73 | }
74 |
--------------------------------------------------------------------------------
/apps/www/app/api/transcribe/route.ts:
--------------------------------------------------------------------------------
1 | import { Groq } from 'groq-sdk'
2 | import { NextRequest, NextResponse } from 'next/server'
3 |
4 | if (!process.env.GROQ_API_KEY) {
5 | throw new Error('Missing GROQ_API_KEY environment variable')
6 | }
7 |
8 | const client = new Groq({
9 | apiKey: process.env.GROQ_API_KEY,
10 | })
11 |
12 | export async function POST(req: NextRequest) {
13 | try {
14 | const formData = await req.formData()
15 | const audioFile = formData.get('audio') as File
16 |
17 | if (!audioFile) {
18 | return NextResponse.json({ error: 'No audio file provided' }, { status: 400 })
19 | }
20 |
21 | const transcription = await client.audio.transcriptions.create({
22 | file: audioFile,
23 | model: "whisper-large-v3-turbo",
24 | language: "en",
25 | response_format: "json",
26 | temperature: 0.0
27 | })
28 |
29 | return NextResponse.json({ text: transcription.text })
30 | } catch (error) {
31 | console.error('Transcription error:', error)
32 | return NextResponse.json(
33 | { error: 'Failed to transcribe audio' },
34 | { status: 500 }
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/apps/www/app/demo/page.tsx:
--------------------------------------------------------------------------------
1 | import ChatDemo from "@/registry/default/example/chat-demo"
2 |
3 | export default function DemoPage() {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/apps/www/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "@/styles/globals.css"
2 | import { Metadata, Viewport } from "next"
3 |
4 | import { siteConfig } from "@/config/site"
5 | import { fontSans } from "@/lib/fonts"
6 | import { cn } from "@/lib/utils"
7 | import { Analytics } from "@/components/analytics"
8 | import { ThemeProvider } from "@/components/providers"
9 | import { TailwindIndicator } from "@/components/tailwind-indicator"
10 | import { ThemeSwitcher } from "@/components/theme-switcher"
11 | import { Toaster as DefaultToaster } from "@/registry/default/ui/toaster"
12 | import { Toaster as NewYorkSonner } from "@/registry/new-york/ui/sonner"
13 | import { Toaster as NewYorkToaster } from "@/registry/new-york/ui/toaster"
14 |
15 | export const metadata: Metadata = {
16 | title: {
17 | default: siteConfig.name,
18 | template: `%s - ${siteConfig.name}`,
19 | },
20 | metadataBase: new URL(siteConfig.url),
21 | description: siteConfig.description,
22 | keywords: [
23 | "Next.js",
24 | "React",
25 | "Tailwind CSS",
26 | "Server Components",
27 | "Radix UI",
28 | ],
29 | authors: [
30 | {
31 | name: "Blazity",
32 | url: "https://blazity.com",
33 | },
34 | ],
35 | creator: "shadcn",
36 | openGraph: {
37 | type: "website",
38 | locale: "en_US",
39 | url: siteConfig.url,
40 | title: siteConfig.name,
41 | description: siteConfig.description,
42 | siteName: siteConfig.name,
43 | images: [
44 | {
45 | url: siteConfig.ogImage,
46 | width: 1200,
47 | height: 630,
48 | alt: siteConfig.name,
49 | },
50 | ],
51 | },
52 | twitter: {
53 | card: "summary_large_image",
54 | title: siteConfig.name,
55 | description: siteConfig.description,
56 | images: [siteConfig.ogImage],
57 | creator: "@shadcn",
58 | },
59 | icons: {
60 | icon: "/favicon.ico",
61 | },
62 | }
63 |
64 | export const viewport: Viewport = {
65 | themeColor: [
66 | { media: "(prefers-color-scheme: light)", color: "white" },
67 | { media: "(prefers-color-scheme: dark)", color: "black" },
68 | ],
69 | }
70 |
71 | interface RootLayoutProps {
72 | children: React.ReactNode
73 | }
74 |
75 | export default function RootLayout({ children }: RootLayoutProps) {
76 | return (
77 | <>
78 |
79 |
80 |
86 |
92 |
93 |
94 | {children}
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | >
107 | )
108 | }
109 |
--------------------------------------------------------------------------------
/apps/www/app/robots.txt:
--------------------------------------------------------------------------------
1 | User-Agent: *
2 | Allow: /
3 |
--------------------------------------------------------------------------------
/apps/www/assets/fonts/Inter-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/assets/fonts/Inter-Bold.ttf
--------------------------------------------------------------------------------
/apps/www/assets/fonts/Inter-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/assets/fonts/Inter-Regular.ttf
--------------------------------------------------------------------------------
/apps/www/components/analytics.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { Analytics as VercelAnalytics } from "@vercel/analytics/react"
4 |
5 | export function Analytics() {
6 | return
7 | }
8 |
--------------------------------------------------------------------------------
/apps/www/components/announcement.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link"
2 | import { ArrowRightIcon } from "@radix-ui/react-icons"
3 | import { Blocks, PieChart } from "lucide-react"
4 |
5 | import { Separator } from "@/registry/new-york/ui/separator"
6 |
7 | export function Announcement() {
8 | return (
9 |
13 | {" "}
14 | {" "}
15 |
16 | New sidebar component
17 |
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/apps/www/components/callout.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | AlertDescription,
4 | AlertTitle,
5 | } from "@/registry/new-york/ui/alert"
6 |
7 | interface CalloutProps {
8 | icon?: string
9 | title?: string
10 | children?: React.ReactNode
11 | }
12 |
13 | export function Callout({ title, children, icon, ...props }: CalloutProps) {
14 | return (
15 |
16 | {icon && {icon} }
17 | {title && {title} }
18 | {children}
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/apps/www/components/code-block-wrapper.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 |
5 | import { cn } from "@/lib/utils"
6 | import { Button } from "@/registry/new-york/ui/button"
7 | import {
8 | Collapsible,
9 | CollapsibleContent,
10 | CollapsibleTrigger,
11 | } from "@/registry/new-york/ui/collapsible"
12 |
13 | interface CodeBlockProps extends React.HTMLAttributes {
14 | expandButtonTitle?: string
15 | }
16 |
17 | export function CodeBlockWrapper({
18 | expandButtonTitle = "View Code",
19 | className,
20 | children,
21 | ...props
22 | }: CodeBlockProps) {
23 | const [isOpened, setIsOpened] = React.useState(false)
24 |
25 | return (
26 |
27 |
28 |
32 |
38 | {children}
39 |
40 |
41 |
47 |
48 |
49 | {isOpened ? "Collapse" : expandButtonTitle}
50 |
51 |
52 |
53 |
54 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/apps/www/components/component-source.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 |
5 | import { cn } from "@/lib/utils"
6 | import { CodeBlockWrapper } from "@/components/code-block-wrapper"
7 |
8 | interface ComponentSourceProps extends React.HTMLAttributes {
9 | src: string
10 | }
11 |
12 | export function ComponentSource({
13 | children,
14 | className,
15 | ...props
16 | }: ComponentSourceProps) {
17 | return (
18 |
22 | {children}
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/apps/www/components/framework-docs.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { allDocs } from "contentlayer/generated"
5 |
6 | import { Mdx } from "./mdx-components"
7 |
8 | interface FrameworkDocsProps extends React.HTMLAttributes {
9 | data: string
10 | }
11 |
12 | export function FrameworkDocs({ ...props }: FrameworkDocsProps) {
13 | const frameworkDoc = allDocs.find(
14 | (doc) => doc.slug === `/docs/installation/${props.data}`
15 | )
16 |
17 | if (!frameworkDoc) {
18 | return null
19 | }
20 |
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/apps/www/components/main-nav.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import Link from "next/link"
4 | import { usePathname } from "next/navigation"
5 |
6 | import { siteConfig } from "@/config/site"
7 | import { cn } from "@/lib/utils"
8 | import { Icons } from "@/components/icons"
9 |
10 | export function MainNav() {
11 | const pathname = usePathname()
12 |
13 | return (
14 |
15 |
16 | {/* */}
17 |
18 | {siteConfig.name}
19 |
20 |
21 |
22 |
29 | Docs
30 |
31 |
41 | Components
42 |
43 |
50 | Themes
51 |
52 |
53 |
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/apps/www/components/mode-toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { MoonIcon, SunIcon } from "@radix-ui/react-icons"
5 | import { useTheme } from "next-themes"
6 |
7 | import { Button } from "@/registry/new-york/ui/button"
8 | import {
9 | DropdownMenu,
10 | DropdownMenuContent,
11 | DropdownMenuItem,
12 | DropdownMenuTrigger,
13 | } from "@/registry/new-york/ui/dropdown-menu"
14 |
15 | export function ModeToggle() {
16 | const { setTheme } = useTheme()
17 |
18 | return (
19 |
20 |
21 |
22 |
23 |
24 | Toggle theme
25 |
26 |
27 |
28 | setTheme("light")}>
29 | Light
30 |
31 | setTheme("dark")}>
32 | Dark
33 |
34 | setTheme("system")}>
35 | System
36 |
37 |
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/apps/www/components/page-header.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils"
2 |
3 | function PageHeader({
4 | className,
5 | children,
6 | ...props
7 | }: React.HTMLAttributes) {
8 | return (
9 |
18 | )
19 | }
20 |
21 | function PageHeaderHeading({
22 | className,
23 | ...props
24 | }: React.HTMLAttributes) {
25 | return (
26 |
33 | )
34 | }
35 |
36 | function PageHeaderDescription({
37 | className,
38 | ...props
39 | }: React.HTMLAttributes) {
40 | return (
41 |
48 | )
49 | }
50 |
51 | function PageActions({
52 | className,
53 | ...props
54 | }: React.HTMLAttributes) {
55 | return (
56 |
63 | )
64 | }
65 |
66 | export { PageActions, PageHeader, PageHeaderDescription, PageHeaderHeading }
67 |
--------------------------------------------------------------------------------
/apps/www/components/pager.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link"
2 | import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons"
3 | import { Doc } from "contentlayer/generated"
4 | import { NavItem, NavItemWithChildren } from "types/nav"
5 |
6 | import { docsConfig } from "@/config/docs"
7 | import { cn } from "@/lib/utils"
8 | import { buttonVariants } from "@/registry/new-york/ui/button"
9 |
10 | interface DocsPagerProps {
11 | doc: Doc
12 | }
13 |
14 | export function DocsPager({ doc }: DocsPagerProps) {
15 | const pager = getPagerForDoc(doc)
16 |
17 | if (!pager) {
18 | return null
19 | }
20 |
21 | return (
22 |
23 | {pager?.prev?.href && (
24 |
28 |
29 | {pager.prev.title}
30 |
31 | )}
32 | {pager?.next?.href && (
33 |
37 | {pager.next.title}
38 |
39 |
40 | )}
41 |
42 | )
43 | }
44 |
45 | function getPagerForDoc(doc: Doc) {
46 | const nav = docsConfig.sidebarNav
47 | const flattenedLinks = [null, ...flatten(nav), null]
48 | const activeIndex = flattenedLinks.findIndex(
49 | (link) => doc.slug === link?.href
50 | )
51 | const prev = activeIndex !== 0 ? flattenedLinks[activeIndex - 1] : null
52 | const next =
53 | activeIndex !== flattenedLinks.length - 1
54 | ? flattenedLinks[activeIndex + 1]
55 | : null
56 | return {
57 | prev,
58 | next,
59 | }
60 | }
61 |
62 | function flatten(links: NavItemWithChildren[]): NavItem[] {
63 | return links
64 | .reduce((flat, link) => {
65 | return flat.concat(link.items?.length ? flatten(link.items) : link)
66 | }, [])
67 | .filter((link) => !link?.disabled)
68 | }
69 |
--------------------------------------------------------------------------------
/apps/www/components/providers.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { Provider as JotaiProvider } from "jotai"
5 | import { ThemeProvider as NextThemesProvider } from "next-themes"
6 | import { ThemeProviderProps } from "next-themes/dist/types"
7 |
8 | import { TooltipProvider } from "@/registry/new-york/ui/tooltip"
9 |
10 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
11 | return (
12 |
13 |
14 | {children}
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/apps/www/components/sidebar-nav.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import Link from "next/link"
4 | import { usePathname } from "next/navigation"
5 | import { SidebarNavItem } from "types/nav"
6 |
7 | import { type DocsConfig } from "@/config/docs"
8 | import { cn } from "@/lib/utils"
9 |
10 | export interface DocsSidebarNavProps {
11 | config: DocsConfig
12 | }
13 |
14 | export function DocsSidebarNav({ config }: DocsSidebarNavProps) {
15 | const pathname = usePathname()
16 |
17 | const items = config.sidebarNav
18 |
19 | return items.length ? (
20 |
21 | {items.map((item, index) => (
22 |
23 |
24 | {item.title}
25 |
26 | {item?.items?.length && (
27 |
28 | )}
29 |
30 | ))}
31 |
32 | ) : null
33 | }
34 |
35 | interface DocsSidebarNavItemsProps {
36 | items: SidebarNavItem[]
37 | pathname: string | null
38 | }
39 |
40 | function DocsSidebarNavItems({ items, pathname }: DocsSidebarNavItemsProps) {
41 | return items?.length ? (
42 |
43 | {items.map((item, index) =>
44 | item.href && !item.disabled ? (
45 |
58 | {item.title}
59 | {item.label && (
60 |
61 | {item.label}
62 |
63 | )}
64 |
65 | ) : (
66 |
73 | {item.title}
74 | {item.label && (
75 |
76 | {item.label}
77 |
78 | )}
79 |
80 | )
81 | )}
82 |
83 | ) : null
84 | }
85 |
--------------------------------------------------------------------------------
/apps/www/components/site-footer.tsx:
--------------------------------------------------------------------------------
1 | import { siteConfig } from "@/config/site"
2 | import { Icons } from "@/components/icons"
3 |
4 | export function SiteFooter() {
5 | return (
6 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/apps/www/components/site-header.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link"
2 |
3 | import { siteConfig } from "@/config/site"
4 | import { cn } from "@/lib/utils"
5 | import { CommandMenu } from "@/components/command-menu"
6 | import { Icons } from "@/components/icons"
7 | import { MainNav } from "@/components/main-nav"
8 | import { MobileNav } from "@/components/mobile-nav"
9 | import { ModeToggle } from "@/components/mode-toggle"
10 | import { buttonVariants } from "@/registry/new-york/ui/button"
11 |
12 | export function SiteHeader() {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
36 |
37 | GitHub
38 |
39 |
40 |
45 |
53 |
54 | Twitter
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | )
63 | }
64 |
--------------------------------------------------------------------------------
/apps/www/components/tailwind-indicator.tsx:
--------------------------------------------------------------------------------
1 | export function TailwindIndicator() {
2 | if (process.env.NODE_ENV === "production") return null
3 |
4 | return (
5 |
9 |
xs
10 |
sm
11 |
md
12 |
lg
13 |
xl
14 |
2xl
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/apps/www/components/theme-switcher.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { useSelectedLayoutSegment } from "next/navigation"
5 |
6 | import { useConfig } from "@/hooks/use-config"
7 |
8 | export function ThemeSwitcher() {
9 | const [config] = useConfig()
10 | const segment = useSelectedLayoutSegment()
11 |
12 | React.useEffect(() => {
13 | document.body.classList.forEach((className) => {
14 | if (className.match(/^theme.*/)) {
15 | document.body.classList.remove(className)
16 | }
17 | })
18 |
19 | const theme = segment === "themes" ? config.theme : null
20 | if (theme) {
21 | return document.body.classList.add(`theme-${theme}`)
22 | }
23 | }, [segment, config])
24 |
25 | return null
26 | }
27 |
--------------------------------------------------------------------------------
/apps/www/components/theme-wrapper.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { cn } from "@/lib/utils"
4 | import { useConfig } from "@/hooks/use-config"
5 |
6 | interface ThemeWrapperProps extends React.ComponentProps<"div"> {
7 | defaultTheme?: string
8 | }
9 |
10 | export function ThemeWrapper({
11 | defaultTheme,
12 | children,
13 | className,
14 | }: ThemeWrapperProps) {
15 | const [config] = useConfig()
16 |
17 | return (
18 |
30 | {children}
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/apps/www/components/toc.tsx:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | "use client"
3 |
4 | import * as React from "react"
5 |
6 | import { TableOfContents } from "@/lib/toc"
7 | import { cn } from "@/lib/utils"
8 | import { useMounted } from "@/hooks/use-mounted"
9 |
10 | interface TocProps {
11 | toc: TableOfContents
12 | }
13 |
14 | export function DashboardTableOfContents({ toc }: TocProps) {
15 | const itemIds = React.useMemo(
16 | () =>
17 | toc.items
18 | ? toc.items
19 | .flatMap((item) => [item.url, item?.items?.map((item) => item.url)])
20 | .flat()
21 | .filter(Boolean)
22 | .map((id) => id?.split("#")[1])
23 | : [],
24 | [toc]
25 | )
26 | const activeHeading = useActiveItem(itemIds)
27 | const mounted = useMounted()
28 |
29 | if (!toc?.items?.length) {
30 | return null
31 | }
32 |
33 | return (
34 |
35 |
On This Page
36 |
37 |
38 | )
39 | }
40 |
41 | function useActiveItem(itemIds: string[]) {
42 | const [activeId, setActiveId] = React.useState(null)
43 |
44 | React.useEffect(() => {
45 | const observer = new IntersectionObserver(
46 | (entries) => {
47 | entries.forEach((entry) => {
48 | if (entry.isIntersecting) {
49 | setActiveId(entry.target.id)
50 | }
51 | })
52 | },
53 | { rootMargin: `0% 0% -80% 0%` }
54 | )
55 |
56 | itemIds?.forEach((id) => {
57 | const element = document.getElementById(id)
58 | if (element) {
59 | observer.observe(element)
60 | }
61 | })
62 |
63 | return () => {
64 | itemIds?.forEach((id) => {
65 | const element = document.getElementById(id)
66 | if (element) {
67 | observer.unobserve(element)
68 | }
69 | })
70 | }
71 | }, [itemIds])
72 |
73 | return activeId
74 | }
75 |
76 | interface TreeProps {
77 | tree: TableOfContents
78 | level?: number
79 | activeItem?: string
80 | }
81 |
82 | function Tree({ tree, level = 1, activeItem }: TreeProps) {
83 | return tree?.items?.length && level < 3 ? (
84 |
85 | {tree.items.map((item, index) => {
86 | return (
87 |
88 |
97 | {item.title}
98 |
99 | {item.items?.length ? (
100 |
101 | ) : null}
102 |
103 | )
104 | })}
105 |
106 | ) : null
107 | }
108 |
--------------------------------------------------------------------------------
/apps/www/config/docs.ts:
--------------------------------------------------------------------------------
1 | import { MainNavItem, SidebarNavItem } from "types/nav"
2 |
3 | export interface DocsConfig {
4 | mainNav: MainNavItem[]
5 | sidebarNav: SidebarNavItem[]
6 | }
7 |
8 | export const docsConfig: DocsConfig = {
9 | mainNav: [
10 | {
11 | title: "Documentation",
12 | href: "/docs",
13 | },
14 | {
15 | title: "Components",
16 | href: "/docs/components/chat",
17 | },
18 | {
19 | title: "Themes",
20 | href: "/themes",
21 | },
22 | ],
23 | sidebarNav: [
24 | {
25 | title: "Getting Started",
26 | items: [
27 | {
28 | title: "Introduction",
29 | href: "/docs",
30 | items: [],
31 | },
32 | {
33 | title: "Installation",
34 | href: "/docs/installation",
35 | items: [],
36 | },
37 | ],
38 | },
39 | {
40 | title: "Components",
41 | items: [
42 | {
43 | title: "Chat",
44 | href: "/docs/components/chat",
45 | items: [],
46 | },
47 | {
48 | title: "Message Input",
49 | href: "/docs/components/message-input",
50 | items: [],
51 | },
52 | {
53 | title: "Message List",
54 | href: "/docs/components/message-list",
55 | items: [],
56 | },
57 | {
58 | title: "Chat Message",
59 | href: "/docs/components/chat-message",
60 | items: [],
61 | },
62 | {
63 | title: "Markdown Renderer",
64 | href: "/docs/components/markdown-renderer",
65 | items: [],
66 | },
67 | {
68 | title: "Prompt Suggestions",
69 | href: "/docs/components/prompt-suggestions",
70 | items: [],
71 | },
72 | {
73 | title: "Typing Indicator",
74 | href: "/docs/components/typing-indicator",
75 | items: [],
76 | },
77 | {
78 | title: "Copy Button",
79 | href: "/docs/components/copy-button",
80 | items: [],
81 | },
82 | {
83 | title: "File Preview",
84 | href: "/docs/components/file-preview",
85 | items: [],
86 | },
87 | {
88 | title: "Audio Visualizer",
89 | href: "/docs/components/audio-visualizer",
90 | items: [],
91 | },
92 | ],
93 | },
94 | ],
95 | }
96 |
--------------------------------------------------------------------------------
/apps/www/config/site.ts:
--------------------------------------------------------------------------------
1 | export const siteConfig = {
2 | name: "shadcn-chatbot-kit",
3 | url: "https://shadcn-chatbot-kit.vercel.app",
4 | ogImage: "https://shadcn-chatbot-kit.vercel.app/og.jpg",
5 | description:
6 | "Beautifully designed chatbot components based on shadcn/ui. Fully customizable and owned by you.",
7 | links: {
8 | twitter: "https://twitter.com/blazity",
9 | github: "https://github.com/Blazity/shadcn-chatbot-kit",
10 | },
11 | }
12 |
13 | export type SiteConfig = typeof siteConfig
14 |
--------------------------------------------------------------------------------
/apps/www/content/docs/components/audio-visualizer.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Audio Visualizer
3 | description: A real-time audio visualization component that provides visual feedback during voice recording.
4 | component: true
5 | ---
6 |
7 |
12 |
13 | The AudioVisualizer component provides visual feedback during voice recording by creating an animated frequency spectrum display that responds to audio input in real-time.
14 |
15 | ## Installation
16 |
17 |
18 |
19 |
20 | CLI
21 | Manual
22 |
23 |
24 |
25 |
26 | ```bash
27 | npx shadcn@latest add https://shadcn-chatbot-kit.vercel.app/r/audio-visualizer.json
28 | ```
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Copy and paste the following code into your project.
37 |
38 |
39 |
40 | Update the import paths to match your project setup.
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ## Usage
49 |
50 | ```tsx
51 | import { AudioVisualizer } from "@/components/ui/audio-visualizer"
52 |
53 | export function VoiceRecorder() {
54 | return (
55 |
60 | )
61 | }
62 | ```
63 |
64 | ## Props
65 |
66 | | Prop | Type | Description | Default |
67 | | ------------- | ------------- | ---------------------------------------------------------- | -------- |
68 | | `stream` | `MediaStream` | The audio stream to visualize | Required |
69 | | `isRecording` | `boolean` | Whether the component should be actively visualizing audio | Required |
70 | | `onClick` | `() => void` | Callback function when the visualizer is clicked | Required |
71 |
72 | ## Implementation Details
73 |
74 | The AudioVisualizer uses the Web Audio API to analyze the audio stream and create the visualization. Key configuration parameters include:
75 |
76 | ```tsx
77 | const AUDIO_CONFIG = {
78 | FFT_SIZE: 512,
79 | SMOOTHING: 0.8,
80 | MIN_BAR_HEIGHT: 2,
81 | MIN_BAR_WIDTH: 2,
82 | BAR_SPACING: 1,
83 | COLOR: {
84 | MIN_INTENSITY: 100,
85 | MAX_INTENSITY: 255,
86 | INTENSITY_RANGE: 155,
87 | },
88 | }
89 | ```
90 |
91 | These parameters control:
92 |
93 | - Frequency analysis resolution (`FFT_SIZE`)
94 | - Animation smoothness (`SMOOTHING`)
95 | - Visual bar dimensions and spacing
96 | - Color intensity range for the visualization
97 |
--------------------------------------------------------------------------------
/apps/www/content/docs/components/copy-button.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Copy Button
3 | description: A button component that copies content to clipboard with visual feedback.
4 | component: true
5 | ---
6 |
7 |
12 |
13 | The CopyButton component provides a simple way to copy text to the clipboard with visual feedback using a check mark animation when successful.
14 |
15 | ## Installation
16 |
17 |
18 |
19 |
20 | CLI
21 | Manual
22 |
23 |
24 |
25 |
26 | ```bash
27 | npx shadcn@latest add https://shadcn-chatbot-kit.vercel.app/r/copy-button.json
28 | ```
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Add the `Button` component to your project.
37 |
38 | The `CopyButton` component uses the `Button` component. Make sure you have it installed in your project.
39 |
40 | Copy and paste the following code into your project.
41 |
42 | `components/ui/copy-button.tsx`
43 |
44 |
45 |
46 | `hooks/use-copy-to-clipboard.ts`
47 |
48 |
49 |
50 | Update the import paths to match your project setup.
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | ## Usage
59 |
60 | ```tsx
61 | import { CopyButton } from "@/components/ui/copy-button"
62 |
63 | export function Demo() {
64 | return (
65 |
69 | )
70 | }
71 | ```
72 |
73 | ## Props
74 |
75 | | Prop | Type | Description |
76 | | -------------- | -------- | ------------------------------------------------------ |
77 | | `content` | `string` | The text content to be copied to clipboard |
78 | | `copyMessage?` | `string` | Optional custom message to show when content is copied |
79 |
80 | ## Examples
81 |
82 | ### Basic Usage
83 |
84 |
89 |
90 | ### With Custom Success Message
91 |
92 |
97 |
98 | ### Within a Code Block
99 |
100 |
105 |
--------------------------------------------------------------------------------
/apps/www/content/docs/components/file-preview.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: File Preview
3 | description: A component for previewing image and text files.
4 | component: true
5 | ---
6 |
7 |
12 |
13 | The FilePreview component provides a consistent way to display file previews with support for both image and text files.
14 |
15 | ## Installation
16 |
17 |
18 |
19 |
20 | CLI
21 | Manual
22 |
23 |
24 |
25 |
26 | ```bash
27 | npx shadcn@latest add https://shadcn-chatbot-kit.vercel.app/r/file-preview.json
28 | ```
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Install the required dependencies
37 |
38 | ```bash
39 | npm install framer-motion@11
40 | ```
41 |
42 | Copy and paste the following code into your project.
43 |
44 |
45 |
46 | Update the import paths to match your project setup.
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | ## Usage
55 |
56 | ```tsx
57 | import { FilePreview } from "@/registry/default/ui/file-preview"
58 |
59 | export function FilePreviewDemo() {
60 | const [file, setFile] = useState(null)
61 |
62 | return (
63 |
64 | {file && setFile(null)} />}
65 |
66 | )
67 | }
68 | ```
69 |
70 | ## Props
71 |
72 | ### FilePreview
73 |
74 | | Prop | Type | Description |
75 | | -------- | ---------- | ---------------------------------------- |
76 | | file | `File` | The file object to preview |
77 | | onRemove | `Function` | Callback function when remove is clicked |
78 |
--------------------------------------------------------------------------------
/apps/www/content/docs/components/markdown-renderer.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Markdown Renderer
3 | description: A customizable component for rendering Markdown content with consistent styling.
4 | component: true
5 | ---
6 |
7 |
12 |
13 | The MarkdownRenderer component provides a consistent way to render Markdown content with custom styling, powered by react-markdown and remark-gfm.
14 |
15 | ## Features
16 |
17 | - GitHub Flavored Markdown support
18 | - Consistent typography and spacing
19 | - Styled tables, lists, and code blocks
20 | - Responsive design
21 | - Theme-aware styling
22 |
23 | ## Installation
24 |
25 |
26 |
27 |
28 | CLI
29 | Manual
30 |
31 |
32 |
33 |
34 | ```bash
35 | npx shadcn@latest add https://shadcn-chatbot-kit.vercel.app/r/markdown-renderer.json
36 | ```
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | Install the required dependencies
45 |
46 | ```bash
47 | npm install react-markdown remark-gfm shiki
48 | ```
49 |
50 | Add the `CopyButton` component to your project.
51 |
52 | The `MarkdownRenderer` component uses the `CopyButton` component. Make sure you have it installed in your project.
53 |
54 | Copy and paste the following code into your project.
55 |
56 |
57 |
58 | Add the following options into `tailwind.config.js`
59 |
60 | Add the following in the `theme.extend.colors` section of your `tailwind.config.js` file
61 |
62 | ```javascript title="tailwind.config.js"
63 | // ...
64 | shiki: {
65 | light: "var(--shiki-light)",
66 | "light-bg": "var(--shiki-light-bg)",
67 | dark: "var(--shiki-dark)",
68 | "dark-bg": "var(--shiki-dark-bg)",
69 | },
70 | // ...
71 | ```
72 |
73 | Update the import paths to match your project setup.
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | ## Usage
82 |
83 | ```tsx
84 | import { MarkdownRenderer } from "@/components/ui/markdown-renderer"
85 | ```
86 |
87 | ```tsx
88 |
89 | {`# Hello World
90 |
91 | This is a paragraph with **bold** and *italic* text.
92 |
93 | ## Lists
94 | - Item 1
95 | - Item 2
96 | - Nested item
97 |
98 | ## Code
99 | \`\`\`tsx
100 | console.log("Hello World")
101 | \`\`\`
102 |
103 | ## Tables
104 | | Header 1 | Header 2 |
105 | |----------|----------|
106 | | Cell 1 | Cell 2 |
107 | `}
108 |
109 | ```
110 |
111 | ## Props
112 |
113 | | Prop | Type | Description |
114 | | ---------- | -------- | ------------------------------ |
115 | | `children` | `string` | The markdown content to render |
116 |
--------------------------------------------------------------------------------
/apps/www/content/docs/components/prompt-suggestions.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Prompt Suggestions
3 | description: A component that displays clickable prompt suggestions for empty chat states.
4 | component: true
5 | ---
6 |
7 |
12 |
13 | The PromptSuggestions component displays a grid of predefined prompts that users can click to quickly start a conversation.
14 |
15 | ## Installation
16 |
17 |
18 |
19 |
20 | CLI
21 | Manual
22 |
23 |
24 |
25 |
26 | ```bash
27 | npx shadcn@latest add https://shadcn-chatbot-kit.vercel.app/r/prompt-suggestions.json
28 | ```
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Copy and paste the following code into your project.
37 |
38 |
39 |
40 | Update the import paths to match your project setup.
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ## Usage
49 |
50 | ```tsx
51 | import { PromptSuggestions } from "@/components/ui/prompt-suggestions"
52 |
53 | export function PromptSuggestionsDemo() {
54 | const append = (message: { role: "user"; content: string }) => {
55 | // Handle appending message
56 | }
57 |
58 | return (
59 |
68 | )
69 | }
70 | ```
71 |
72 | ## Props
73 |
74 | | Prop | Type | Description |
75 | | ------------- | ------------------------------------------------------ | ------------------------------------------------- |
76 | | `label` | `string` | The heading text displayed above the suggestions |
77 | | `append` | `(message: { role: "user"; content: string }) => void` | Function called when a suggestion is clicked |
78 | | `suggestions` | `string[]` | Array of suggestion strings to display as buttons |
79 |
--------------------------------------------------------------------------------
/apps/www/content/docs/components/typing-indicator.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Typing Indicator
3 | description: A simple animated typing indicator component.
4 | component: true
5 | ---
6 |
7 |
12 |
13 | A minimal and elegant typing indicator component that shows three animated dots to indicate ongoing activity, commonly used in chat interfaces to show when someone is typing.
14 |
15 | ## Installation
16 |
17 |
18 |
19 |
20 | CLI
21 | Manual
22 |
23 |
24 |
25 |
26 | ```bash
27 | npx shadcn@latest add https://shadcn-chatbot-kit.vercel.app/r/typing-indicator.json
28 | ```
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Copy and paste the following code into your project.
37 |
38 |
39 |
40 | Add the following options into `tailwind.config.js`
41 |
42 | Add the following object in the `theme.extend.keyframes` section of your `tailwind.config.js` file
43 |
44 | ```javascript title="tailwind.config.js"
45 | // ...
46 | "typing-dot-bounce": {
47 | "0%,40%": { transform: "translateY(0)" },
48 | "20%": { transform: "translateY(-0.25rem)" },
49 | },
50 | // ...
51 | ```
52 |
53 | Add the following property in the `theme.extend.animation` section of your `tailwind.config.js` file
54 |
55 | ```javascript title="tailwind.config.js"
56 | // ...
57 | "typing-dot-bounce": "typing-dot-bounce 1.25s ease-out infinite",
58 | // ...
59 | ```
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | ## Usage
68 |
69 | ```tsx
70 | import { TypingIndicator } from "@/components/ui/typing-indicator"
71 |
72 | type MessageProps = {
73 | isPending: boolean
74 | content: string
75 | }
76 |
77 | export function Message({ isPending, content }: MessageProps) {
78 | // ...
79 |
80 | return isPending ? : {content}
81 | }
82 | ```
83 |
--------------------------------------------------------------------------------
/apps/www/content/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: A comprehensive chatbot component kit built on top of shadcn/ui ecosystem.
4 | ---
5 |
6 | If you're not familiar with [shadcn/ui](https://ui.shadcn.com/), we encourage you to take a look at its docs to learn more about it.
7 |
8 | This collection of components is heavily inspired by `shadcn/ui` and uses its CLI to aid with automatically installing components.
9 |
10 | ## FAQ
11 |
12 |
13 |
14 |
15 | Is this an official shadcn project?
16 |
17 | No, this is not an official shadcn/ui project. This is a component kit built
18 | by
19 | Blazity , that follows shadcn/ui's design
20 | philosophy and methodology. We've built it to be fully compatible with the shadcn/ui
21 | ecosystem, allowing you to seamlessly integrate it with existing shadcn/ui components
22 | while maintaining the same level of customization and control over your code.
23 |
24 |
25 |
26 |
27 |
28 | How does the AI integration work?
29 |
30 |
31 | The kit is designed to work seamlessly with the Vercel AI SDK out of the box, making it easy to integrate with popular AI providers. Simply use the `useChat` hook from the Vercel AI SDK, and pass the returned properties to our Chat component.
32 |
33 | However, you're not limited to the Vercel AI SDK. The components are built to be provider-agnostic, allowing you to implement your own custom AI integration.
34 |
35 |
36 |
37 |
38 |
39 |
40 | What features are included out of the box?
41 |
42 |
43 | The kit includes several key features:
44 | - Auto-scroll message area with smart scrolling behavior
45 | - Auto-resize message input with file upload support
46 | - Built-in prompt suggestions
47 | - Message actions (copy, rate response, etc.)
48 | - Elegant loading states and transitions
49 | - Markdown support in messages
50 | - File attachments and image preview
51 | - Dark mode support
52 | - TypeScript support
53 |
54 | All components can be used as-is or customized to match your specific requirements.
55 |
56 |
57 |
58 |
59 |
60 |
61 | How do I get started?
62 |
63 |
64 | Getting started is straightforward:
65 |
66 | 1. First, set up shadcn/ui in your project following their installation guide
67 | 2. Use the modern `shadcn` CLI to install our components
68 | 3. If you're using Vercel AI SDK, import the `useChat` hook and pass it to our Chat component
69 | 4. If you're using a custom implementation, follow our interface guide to integrate your AI service
70 |
71 | The components are tree-shakeable and work with any framework that supports React 18 or higher, including Next.js, Remix, Astro, and Gatsby.
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/apps/www/content/docs/installation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installation
3 | description: How to install dependencies and structure your app.
4 | ---
5 |
6 | The installation process is very simple. If you are already using shadcn/ui, you can skip this step and go straight to installing components.
7 |
8 | If not using shadcn/ui yet, you can follow the steps below:
9 |
10 | 1. First, follow the [installation instructions](https://ui.shadcn.com/docs/installation) for shadcn/ui in your project.
11 |
12 | 2. Make sure you're using the modern `shadcn` CLI (not the legacy `shadcn-ui`).
13 |
14 | 3. Install components using the CLI.
15 |
--------------------------------------------------------------------------------
/apps/www/hooks/use-config.ts:
--------------------------------------------------------------------------------
1 | import { useAtom } from "jotai"
2 | import { atomWithStorage } from "jotai/utils"
3 |
4 | import { BaseColor } from "@/registry/registry-base-colors"
5 |
6 | type Config = {
7 | theme: BaseColor["name"]
8 | radius: number
9 | }
10 |
11 | const configAtom = atomWithStorage("config", {
12 | theme: "zinc",
13 | radius: 0.5,
14 | })
15 |
16 | export function useConfig() {
17 | return useAtom(configAtom)
18 | }
19 |
--------------------------------------------------------------------------------
/apps/www/hooks/use-mounted.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | export function useMounted() {
4 | const [mounted, setMounted] = React.useState(false)
5 |
6 | React.useEffect(() => {
7 | setMounted(true)
8 | }, [])
9 |
10 | return mounted
11 | }
12 |
--------------------------------------------------------------------------------
/apps/www/lib/delay.ts:
--------------------------------------------------------------------------------
1 | export async function delay(duration: number): Promise<{ message: string }> {
2 | const milliseconds = duration * 1000
3 |
4 | return new Promise((resolve) => {
5 | setTimeout(() => {
6 | resolve({
7 | message: `Completed delay of ${duration} ${
8 | duration === 1 ? "second" : "seconds"
9 | }`,
10 | })
11 | }, milliseconds)
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/apps/www/lib/events.ts:
--------------------------------------------------------------------------------
1 | import va from "@vercel/analytics"
2 | import { z } from "zod"
3 |
4 | const eventSchema = z.object({
5 | name: z.enum([
6 | "copy_npm_command",
7 | "copy_usage_import_code",
8 | "copy_usage_code",
9 | "copy_primitive_code",
10 | "copy_theme_code",
11 | "copy_block_code",
12 | "copy_chunk_code",
13 | "enable_lift_mode",
14 | "copy_chart_code",
15 | "copy_chart_theme",
16 | "copy_chart_data",
17 | "copy_color",
18 | ]),
19 | // declare type AllowedPropertyValues = string | number | boolean | null
20 | properties: z
21 | .record(z.union([z.string(), z.number(), z.boolean(), z.null()]))
22 | .optional(),
23 | })
24 |
25 | export type Event = z.infer
26 |
27 | export function trackEvent(input: Event): void {
28 | const event = eventSchema.parse(input)
29 | if (event) {
30 | va.track(event.name, event.properties)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/apps/www/lib/fonts.ts:
--------------------------------------------------------------------------------
1 | // import { JetBrains_Mono as FontMono, Inter as FontSans } from "next/font/google"
2 | import { JetBrains_Mono as FontMono } from "next/font/google"
3 | // import { GeistMono } from "geist/font/mono"
4 | import { GeistSans } from "geist/font/sans"
5 |
6 | export const fontSans = GeistSans
7 |
8 | export const fontMono = FontMono({
9 | subsets: ["latin"],
10 | variable: "--font-mono",
11 | })
12 |
--------------------------------------------------------------------------------
/apps/www/lib/rehype-npm-command.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable turbo/no-undeclared-env-vars */
2 | import { UnistNode, UnistTree } from "types/unist"
3 | import { visit } from "unist-util-visit"
4 |
5 | const SITE_URL = process.env.VERCEL_URL
6 | ? `https://${process.env.VERCEL_URL}`
7 | : "https://shadcn-chatbot-kit.vercel.app"
8 |
9 | export function rehypeNpmCommand() {
10 | return (tree: UnistTree) => {
11 | visit(tree, (node: UnistNode) => {
12 | if (node.type !== "element" || node?.tagName !== "pre") {
13 | return
14 | }
15 |
16 | // npm install.
17 | if (node.properties?.["__rawString__"]?.startsWith("npm install")) {
18 | const npmCommand = node.properties?.["__rawString__"]
19 | node.properties["__npmCommand__"] = npmCommand
20 | node.properties["__yarnCommand__"] = npmCommand.replace(
21 | "npm install",
22 | "yarn add"
23 | )
24 | node.properties["__pnpmCommand__"] = npmCommand.replace(
25 | "npm install",
26 | "pnpm add"
27 | )
28 | node.properties["__bunCommand__"] = npmCommand.replace(
29 | "npm install",
30 | "bun add"
31 | )
32 | }
33 |
34 | // npx create.
35 | if (node.properties?.["__rawString__"]?.startsWith("npx create-")) {
36 | const npmCommand = node.properties?.["__rawString__"]
37 | node.properties["__npmCommand__"] = npmCommand
38 | node.properties["__yarnCommand__"] = npmCommand.replace(
39 | "npx create-",
40 | "yarn create "
41 | )
42 | node.properties["__pnpmCommand__"] = npmCommand.replace(
43 | "npx create-",
44 | "pnpm create "
45 | )
46 | node.properties["__bunCommand__"] = npmCommand.replace(
47 | "npx",
48 | "bunx --bun"
49 | )
50 | }
51 |
52 | // npx.
53 | if (
54 | node.properties?.["__rawString__"]?.startsWith("npx") &&
55 | !node.properties?.["__rawString__"]?.startsWith("npx create-")
56 | ) {
57 | const npmCommand = node.properties?.["__rawString__"]
58 | const parts = npmCommand.split(" ")
59 | const componentName = parts.pop()
60 | parts.push(`${SITE_URL}/r/${componentName}.json`)
61 | const modifiedCommand = parts.join(" ")
62 |
63 | node.properties["__npmCommand__"] = modifiedCommand
64 | node.properties["__yarnCommand__"] = modifiedCommand
65 | node.properties["__pnpmCommand__"] = modifiedCommand.replace(
66 | "npx",
67 | "pnpm dlx"
68 | )
69 | node.properties["__bunCommand__"] = modifiedCommand.replace(
70 | "npx",
71 | "bunx --bun"
72 | )
73 | }
74 | })
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/apps/www/lib/toc.ts:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | // TODO: I'll fix this later.
3 |
4 | import { toc } from "mdast-util-toc"
5 | import { remark } from "remark"
6 | import { visit } from "unist-util-visit"
7 |
8 | const textTypes = ["text", "emphasis", "strong", "inlineCode"]
9 |
10 | function flattenNode(node) {
11 | const p = []
12 | visit(node, (node) => {
13 | if (!textTypes.includes(node.type)) return
14 | p.push(node.value)
15 | })
16 | return p.join(``)
17 | }
18 |
19 | interface Item {
20 | title: string
21 | url: string
22 | items?: Item[]
23 | }
24 |
25 | interface Items {
26 | items?: Item[]
27 | }
28 |
29 | function getItems(node, current): Items {
30 | if (!node) {
31 | return {}
32 | }
33 |
34 | if (node.type === "paragraph") {
35 | visit(node, (item) => {
36 | if (item.type === "link") {
37 | current.url = item.url
38 | current.title = flattenNode(node)
39 | }
40 |
41 | if (item.type === "text") {
42 | current.title = flattenNode(node)
43 | }
44 | })
45 |
46 | return current
47 | }
48 |
49 | if (node.type === "list") {
50 | current.items = node.children.map((i) => getItems(i, {}))
51 |
52 | return current
53 | } else if (node.type === "listItem") {
54 | const heading = getItems(node.children[0], {})
55 |
56 | if (node.children.length > 1) {
57 | getItems(node.children[1], heading)
58 | }
59 |
60 | return heading
61 | }
62 |
63 | return {}
64 | }
65 |
66 | const getToc = () => (node, file) => {
67 | const table = toc(node)
68 | const items = getItems(table.map, {})
69 |
70 | file.data = items
71 | }
72 |
73 | export type TableOfContents = Items
74 |
75 | export async function getTableOfContents(
76 | content: string
77 | ): Promise {
78 | const result = await remark().use(getToc).process(content)
79 |
80 | return result.data
81 | }
82 |
--------------------------------------------------------------------------------
/apps/www/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | import { siteConfig } from "@/config/site"
5 |
6 | export function cn(...inputs: ClassValue[]) {
7 | return twMerge(clsx(inputs))
8 | }
9 |
10 | export function formatDate(input: string | number): string {
11 | const date = new Date(input)
12 | return date.toLocaleDateString("en-US", {
13 | month: "long",
14 | day: "numeric",
15 | year: "numeric",
16 | })
17 | }
18 |
19 | export function absoluteUrl(path: string) {
20 | return `${siteConfig.url}${path}`
21 | }
22 |
--------------------------------------------------------------------------------
/apps/www/lib/utils/audio.ts:
--------------------------------------------------------------------------------
1 | export async function transcribeAudio(audioBlob: Blob): Promise {
2 | // Create a File object with the correct MIME type
3 | const audioFile = new File([audioBlob], "recording.webm", {
4 | type: "audio/webm",
5 | })
6 |
7 | const formData = new FormData()
8 | formData.append("audio", audioFile)
9 |
10 | const response = await fetch("/api/transcribe", {
11 | method: "POST",
12 | body: formData,
13 | })
14 |
15 | if (!response.ok) {
16 | const error = await response.json()
17 | throw new Error(error.error || "Failed to transcribe audio")
18 | }
19 |
20 | const data = await response.json()
21 | return data.text
22 | }
23 |
--------------------------------------------------------------------------------
/apps/www/lib/weather.ts:
--------------------------------------------------------------------------------
1 | // Map weather code to description
2 | const weatherCodes: Record = {
3 | 0: "Clear sky",
4 | 1: "Mainly clear",
5 | 2: "Partly cloudy",
6 | 3: "Overcast",
7 | 45: "Foggy",
8 | 48: "Depositing rime fog",
9 | 51: "Light drizzle",
10 | 53: "Moderate drizzle",
11 | 55: "Dense drizzle",
12 | 61: "Slight rain",
13 | 63: "Moderate rain",
14 | 65: "Heavy rain",
15 | 71: "Slight snow",
16 | 73: "Moderate snow",
17 | 75: "Heavy snow",
18 | 77: "Snow grains",
19 | 80: "Slight rain showers",
20 | 81: "Moderate rain showers",
21 | 82: "Violent rain showers",
22 | 85: "Slight snow showers",
23 | 86: "Heavy snow showers",
24 | 95: "Thunderstorm",
25 | 96: "Thunderstorm with slight hail",
26 | 99: "Thunderstorm with heavy hail",
27 | }
28 |
29 | interface Coordinates {
30 | lat: number
31 | lon: number
32 | name: string
33 | country: string
34 | }
35 |
36 | interface WeatherData {
37 | location: string
38 | temperature: string
39 | feelsLike: string
40 | humidity: string
41 | windSpeed: string
42 | precipitation: string
43 | conditions: string
44 | }
45 |
46 | export async function getCoordinates(location: string): Promise {
47 | const response = await fetch(
48 | `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(
49 | location
50 | )}&count=1&language=en&format=json`
51 | )
52 | const data = await response.json()
53 | if (!data.results?.[0]) {
54 | throw new Error(`Location not found: ${location}`)
55 | }
56 | return {
57 | lat: data.results[0].latitude,
58 | lon: data.results[0].longitude,
59 | name: data.results[0].name,
60 | country: data.results[0].country,
61 | }
62 | }
63 |
64 | export async function getWeather(location: string): Promise {
65 | // Get coordinates for the location
66 | const coords = await getCoordinates(location)
67 |
68 | // Fetch weather data
69 | const weatherResponse = await fetch(
70 | `https://api.open-meteo.com/v1/forecast?latitude=${coords.lat}&longitude=${coords.lon}¤t=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m&timezone=auto`
71 | )
72 | const weatherData = await weatherResponse.json()
73 |
74 | const current = weatherData.current
75 | return {
76 | location: `${coords.name}, ${coords.country}`,
77 | temperature: `${current.temperature_2m}°C`,
78 | feelsLike: `${current.apparent_temperature}°C`,
79 | humidity: `${current.relative_humidity_2m}%`,
80 | windSpeed: `${current.wind_speed_10m} km/h`,
81 | precipitation: `${current.precipitation} mm`,
82 | conditions: weatherCodes[current.weather_code] || "Unknown",
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/apps/www/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | // NOTE: This file should not be edited
6 | // see https://nextjs.org/docs/basic-features/typescript for more information.
7 |
--------------------------------------------------------------------------------
/apps/www/next.config.mjs:
--------------------------------------------------------------------------------
1 | import { createContentlayerPlugin } from "next-contentlayer2"
2 |
3 | /** @type {import('next').NextConfig} */
4 | const nextConfig = {
5 | reactStrictMode: true,
6 | swcMinify: true,
7 | images: {
8 | remotePatterns: [
9 | {
10 | protocol: "https",
11 | hostname: "avatars.githubusercontent.com",
12 | },
13 | {
14 | protocol: "https",
15 | hostname: "images.unsplash.com",
16 | },
17 | ],
18 | },
19 | redirects() {
20 | return [
21 | {
22 | source: "/components",
23 | destination: "/docs/components/chat",
24 | permanent: true,
25 | },
26 | {
27 | source: "/docs/components",
28 | destination: "/docs/components/chat",
29 | permanent: true,
30 | },
31 | ]
32 | },
33 | }
34 |
35 | const withContentlayer = createContentlayerPlugin({
36 | // Additional Contentlayer config options
37 | })
38 |
39 | export default withContentlayer(nextConfig)
40 |
--------------------------------------------------------------------------------
/apps/www/pages/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/pages/.gitkeep
--------------------------------------------------------------------------------
/apps/www/pages/api/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/pages/api/.gitkeep
--------------------------------------------------------------------------------
/apps/www/pages/api/components.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from "next"
2 |
3 | import components from "./components.json"
4 |
5 | export default async function handler(
6 | req: NextApiRequest,
7 | res: NextApiResponse
8 | ) {
9 | if (req.method !== "GET") {
10 | return res.status(405).end()
11 | }
12 |
13 | return res.status(200).json(components)
14 | }
15 |
--------------------------------------------------------------------------------
/apps/www/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = require("../../postcss.config.cjs")
2 |
--------------------------------------------------------------------------------
/apps/www/public/avatars/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/avatars/01.png
--------------------------------------------------------------------------------
/apps/www/public/avatars/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/avatars/02.png
--------------------------------------------------------------------------------
/apps/www/public/avatars/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/avatars/03.png
--------------------------------------------------------------------------------
/apps/www/public/avatars/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/avatars/04.png
--------------------------------------------------------------------------------
/apps/www/public/avatars/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/avatars/05.png
--------------------------------------------------------------------------------
/apps/www/public/avatars/shadcn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/avatars/shadcn.jpg
--------------------------------------------------------------------------------
/apps/www/public/examples/authentication-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/authentication-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/authentication-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/authentication-light.png
--------------------------------------------------------------------------------
/apps/www/public/examples/cards-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/cards-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/cards-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/cards-light.png
--------------------------------------------------------------------------------
/apps/www/public/examples/dashboard-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/dashboard-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/dashboard-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/dashboard-light.png
--------------------------------------------------------------------------------
/apps/www/public/examples/forms-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/forms-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/forms-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/forms-light.png
--------------------------------------------------------------------------------
/apps/www/public/examples/mail-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/mail-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/mail-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/mail-light.png
--------------------------------------------------------------------------------
/apps/www/public/examples/music-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/music-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/music-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/music-light.png
--------------------------------------------------------------------------------
/apps/www/public/examples/playground-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/playground-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/playground-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/playground-light.png
--------------------------------------------------------------------------------
/apps/www/public/examples/tasks-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/tasks-dark.png
--------------------------------------------------------------------------------
/apps/www/public/examples/tasks-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/examples/tasks-light.png
--------------------------------------------------------------------------------
/apps/www/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/favicon.ico
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-controlled-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-controlled-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-controlled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-controlled.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-footer-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-footer-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-footer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-footer.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-group-action-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-group-action-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-group-action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-group-action.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-group-collapsible-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-group-collapsible-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-group-collapsible.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-group-collapsible.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-group-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-group-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-group.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-header-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-header-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-header.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-action-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-action-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-action.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-badge-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-badge-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-badge.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-collapsible-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-collapsible-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-collapsible.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-collapsible.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-sub-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-sub-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu-sub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu-sub.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-menu.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-rsc-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-rsc-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar-rsc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar-rsc.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/demo-sidebar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/demo-sidebar.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/login-01-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/login-01-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/login-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/login-01.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-01-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-01-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-01.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-02-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-02-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-02.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-03-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-03-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-03.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-04-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-04-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-04.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-05-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-05-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-05.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-06-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-06-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-06.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-07-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-07-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-07.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-08-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-08-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-08.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-09-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-09-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-09.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-10-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-10-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-10.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-11-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-11-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-11.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-12-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-12-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-12.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-13-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-13-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-13.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-14-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-14-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-14.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-15-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-15-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/blocks/sidebar-15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/blocks/sidebar-15.png
--------------------------------------------------------------------------------
/apps/www/public/images/dashboard-1-dark.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/dashboard-1-dark.jpg
--------------------------------------------------------------------------------
/apps/www/public/images/dashboard-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/dashboard-1.jpg
--------------------------------------------------------------------------------
/apps/www/public/images/dashboard-2-dark.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/dashboard-2-dark.jpg
--------------------------------------------------------------------------------
/apps/www/public/images/dashboard-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/dashboard-2.jpg
--------------------------------------------------------------------------------
/apps/www/public/images/dashboard-3-dark.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/dashboard-3-dark.jpg
--------------------------------------------------------------------------------
/apps/www/public/images/dashboard-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/dashboard-3.jpg
--------------------------------------------------------------------------------
/apps/www/public/images/lift-mode-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/lift-mode-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/lift-mode-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/lift-mode-light.png
--------------------------------------------------------------------------------
/apps/www/public/images/open-in-v0-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/open-in-v0-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/open-in-v0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/open-in-v0.png
--------------------------------------------------------------------------------
/apps/www/public/images/sidebar-menu-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/sidebar-menu-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/sidebar-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/sidebar-menu.png
--------------------------------------------------------------------------------
/apps/www/public/images/sidebar-structure-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/sidebar-structure-dark.png
--------------------------------------------------------------------------------
/apps/www/public/images/sidebar-structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/sidebar-structure.png
--------------------------------------------------------------------------------
/apps/www/public/images/style-with-theming.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/style-with-theming.jpg
--------------------------------------------------------------------------------
/apps/www/public/images/style.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/images/style.jpg
--------------------------------------------------------------------------------
/apps/www/public/og.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/og.jpg
--------------------------------------------------------------------------------
/apps/www/public/placeholder-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/www/public/placeholder-user.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Blazity/shadcn-chatbot-kit/e1f84c4a2c2a2d6255d033f67ad53574f2b0ccc6/apps/www/public/placeholder-user.jpg
--------------------------------------------------------------------------------
/apps/www/public/placeholder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/www/public/r/audio-utils.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "audio-utils",
3 | "type": "registry:lib",
4 | "files": [
5 | {
6 | "path": "lib/audio-utils.ts",
7 | "content": "type RecordAudioType = {\n (stream: MediaStream): Promise\n stop: () => void\n currentRecorder?: MediaRecorder\n}\n\nexport const recordAudio = (function (): RecordAudioType {\n const func = async function recordAudio(stream: MediaStream): Promise {\n try {\n const mediaRecorder = new MediaRecorder(stream, {\n mimeType: \"audio/webm;codecs=opus\",\n })\n const audioChunks: Blob[] = []\n\n return new Promise((resolve, reject) => {\n mediaRecorder.ondataavailable = (event) => {\n if (event.data.size > 0) {\n audioChunks.push(event.data)\n }\n }\n\n mediaRecorder.onstop = () => {\n const audioBlob = new Blob(audioChunks, { type: \"audio/webm\" })\n resolve(audioBlob)\n }\n\n mediaRecorder.onerror = () => {\n reject(new Error(\"MediaRecorder error occurred\"))\n }\n\n mediaRecorder.start(1000)\n ;(func as RecordAudioType).currentRecorder = mediaRecorder\n })\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error occurred\"\n throw new Error(\"Failed to start recording: \" + errorMessage)\n }\n }\n\n ;(func as RecordAudioType).stop = () => {\n const recorder = (func as RecordAudioType).currentRecorder\n if (recorder && recorder.state !== \"inactive\") {\n recorder.stop()\n }\n delete (func as RecordAudioType).currentRecorder\n }\n\n return func as RecordAudioType\n})()\n",
8 | "type": "registry:lib",
9 | "target": ""
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/apps/www/public/r/copy-button.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "copy-button",
3 | "type": "registry:ui",
4 | "registryDependencies": [
5 | "button",
6 | "https://shadcn-chatbot-kit.vercel.app/r/use-copy-to-clipboard.json"
7 | ],
8 | "files": [
9 | {
10 | "path": "ui/copy-button.tsx",
11 | "content": "\"use client\"\n\nimport { Check, Copy } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { useCopyToClipboard } from \"@/registry/default/hooks/use-copy-to-clipboard\"\nimport { Button } from \"@/registry/default/ui/button\"\n\ntype CopyButtonProps = {\n content: string\n copyMessage?: string\n}\n\nexport function CopyButton({ content, copyMessage }: CopyButtonProps) {\n const { isCopied, handleCopy } = useCopyToClipboard({\n text: content,\n copyMessage,\n })\n\n return (\n \n \n \n
\n \n \n )\n}\n",
12 | "type": "registry:ui",
13 | "target": ""
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/apps/www/public/r/interrupt-prompt.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "interrupt-prompt",
3 | "type": "registry:ui",
4 | "dependencies": [
5 | "framer-motion@11"
6 | ],
7 | "files": [
8 | {
9 | "path": "ui/interrupt-prompt.tsx",
10 | "content": "\"use client\"\n\nimport { AnimatePresence, motion } from \"framer-motion\"\nimport { X } from \"lucide-react\"\n\ninterface InterruptPromptProps {\n isOpen: boolean\n close: () => void\n}\n\nexport function InterruptPrompt({ isOpen, close }: InterruptPromptProps) {\n return (\n \n {isOpen && (\n \n Press Enter again to interrupt \n \n \n \n \n )}\n \n )\n}\n",
11 | "type": "registry:ui",
12 | "target": ""
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/apps/www/public/r/message-list.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "message-list",
3 | "type": "registry:ui",
4 | "registryDependencies": [
5 | "https://shadcn-chatbot-kit.vercel.app/r/chat-message.json",
6 | "https://shadcn-chatbot-kit.vercel.app/r/typing-indicator.json"
7 | ],
8 | "files": [
9 | {
10 | "path": "ui/message-list.tsx",
11 | "content": "import {\n ChatMessage,\n type ChatMessageProps,\n type Message,\n} from \"@/registry/default/ui/chat-message\"\nimport { TypingIndicator } from \"@/registry/default/ui/typing-indicator\"\n\ntype AdditionalMessageOptions = Omit\n\ninterface MessageListProps {\n messages: Message[]\n showTimeStamps?: boolean\n isTyping?: boolean\n messageOptions?:\n | AdditionalMessageOptions\n | ((message: Message) => AdditionalMessageOptions)\n}\n\nexport function MessageList({\n messages,\n showTimeStamps = true,\n isTyping = false,\n messageOptions,\n}: MessageListProps) {\n return (\n \n {messages.map((message, index) => {\n const additionalOptions =\n typeof messageOptions === \"function\"\n ? messageOptions(message)\n : messageOptions\n\n return (\n \n )\n })}\n {isTyping && }\n
\n )\n}\n",
12 | "type": "registry:ui",
13 | "target": ""
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/apps/www/public/r/prompt-suggestions.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "prompt-suggestions",
3 | "type": "registry:ui",
4 | "files": [
5 | {
6 | "path": "ui/prompt-suggestions.tsx",
7 | "content": "interface PromptSuggestionsProps {\n label: string\n append: (message: { role: \"user\"; content: string }) => void\n suggestions: string[]\n}\n\nexport function PromptSuggestions({\n label,\n append,\n suggestions,\n}: PromptSuggestionsProps) {\n return (\n \n
{label} \n
\n {suggestions.map((suggestion) => (\n
append({ role: \"user\", content: suggestion })}\n className=\"h-max flex-1 rounded-xl border bg-background p-4 hover:bg-muted\"\n >\n {suggestion}
\n \n ))}\n
\n
\n )\n}\n",
8 | "type": "registry:ui",
9 | "target": ""
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/apps/www/public/r/typing-indicator.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typing-indicator",
3 | "type": "registry:ui",
4 | "files": [
5 | {
6 | "path": "ui/typing-indicator.tsx",
7 | "content": "import { Dot } from \"lucide-react\"\n\nexport function TypingIndicator() {\n return (\n \n )\n}\n",
8 | "type": "registry:ui",
9 | "target": ""
10 | }
11 | ],
12 | "tailwind": {
13 | "config": {
14 | "theme": {
15 | "extend": {
16 | "keyframes": {
17 | "typing-dot-bounce": {
18 | "0%,40%": {
19 | "transform": "translateY(0)"
20 | },
21 | "20%": {
22 | "transform": "translateY(-0.25rem)"
23 | }
24 | }
25 | },
26 | "animation": {
27 | "typing-dot-bounce": "typing-dot-bounce 1.25s ease-out infinite"
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/apps/www/public/r/use-audio-recording.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "use-audio-recording",
3 | "type": "registry:hook",
4 | "registryDependencies": [
5 | "https://shadcn-chatbot-kit.vercel.app/r/audio-utils.json"
6 | ],
7 | "files": [
8 | {
9 | "path": "hooks/use-audio-recording.ts",
10 | "content": "import { useEffect, useRef, useState } from \"react\"\n\nimport { recordAudio } from \"@/registry/default/lib/audio-utils\"\n\ninterface UseAudioRecordingOptions {\n transcribeAudio?: (blob: Blob) => Promise\n onTranscriptionComplete?: (text: string) => void\n}\n\nexport function useAudioRecording({\n transcribeAudio,\n onTranscriptionComplete,\n}: UseAudioRecordingOptions) {\n const [isListening, setIsListening] = useState(false)\n const [isSpeechSupported, setIsSpeechSupported] = useState(!!transcribeAudio)\n const [isRecording, setIsRecording] = useState(false)\n const [isTranscribing, setIsTranscribing] = useState(false)\n const [audioStream, setAudioStream] = useState(null)\n const activeRecordingRef = useRef(null)\n\n useEffect(() => {\n const checkSpeechSupport = async () => {\n const hasMediaDevices = !!(\n navigator.mediaDevices && navigator.mediaDevices.getUserMedia\n )\n setIsSpeechSupported(hasMediaDevices && !!transcribeAudio)\n }\n\n checkSpeechSupport()\n }, [transcribeAudio])\n\n const stopRecording = async () => {\n setIsRecording(false)\n setIsTranscribing(true)\n try {\n // First stop the recording to get the final blob\n recordAudio.stop()\n // Wait for the recording promise to resolve with the final blob\n const recording = await activeRecordingRef.current\n if (transcribeAudio) {\n const text = await transcribeAudio(recording)\n onTranscriptionComplete?.(text)\n }\n } catch (error) {\n console.error(\"Error transcribing audio:\", error)\n } finally {\n setIsTranscribing(false)\n setIsListening(false)\n if (audioStream) {\n audioStream.getTracks().forEach((track) => track.stop())\n setAudioStream(null)\n }\n activeRecordingRef.current = null\n }\n }\n\n const toggleListening = async () => {\n if (!isListening) {\n try {\n setIsListening(true)\n setIsRecording(true)\n // Get audio stream first\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n })\n setAudioStream(stream)\n\n // Start recording with the stream\n activeRecordingRef.current = recordAudio(stream)\n } catch (error) {\n console.error(\"Error recording audio:\", error)\n setIsListening(false)\n setIsRecording(false)\n if (audioStream) {\n audioStream.getTracks().forEach((track) => track.stop())\n setAudioStream(null)\n }\n }\n } else {\n await stopRecording()\n }\n }\n\n return {\n isListening,\n isSpeechSupported,\n isRecording,\n isTranscribing,\n audioStream,\n toggleListening,\n stopRecording,\n }\n}\n",
11 | "type": "registry:hook",
12 | "target": ""
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/apps/www/public/r/use-auto-scroll.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "use-auto-scroll",
3 | "type": "registry:hook",
4 | "files": [
5 | {
6 | "path": "hooks/use-auto-scroll.ts",
7 | "content": "import { useEffect, useRef, useState } from \"react\"\n\n// How many pixels from the bottom of the container to enable auto-scroll\nconst ACTIVATION_THRESHOLD = 50\n// Minimum pixels of scroll-up movement required to disable auto-scroll\nconst MIN_SCROLL_UP_THRESHOLD = 10\n\nexport function useAutoScroll(dependencies: React.DependencyList) {\n const containerRef = useRef(null)\n const previousScrollTop = useRef(null)\n const [shouldAutoScroll, setShouldAutoScroll] = useState(true)\n\n const scrollToBottom = () => {\n if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight\n }\n }\n\n const handleScroll = () => {\n if (containerRef.current) {\n const { scrollTop, scrollHeight, clientHeight } = containerRef.current\n\n const distanceFromBottom = Math.abs(\n scrollHeight - scrollTop - clientHeight\n )\n\n const isScrollingUp = previousScrollTop.current\n ? scrollTop < previousScrollTop.current\n : false\n\n const scrollUpDistance = previousScrollTop.current\n ? previousScrollTop.current - scrollTop\n : 0\n\n const isDeliberateScrollUp =\n isScrollingUp && scrollUpDistance > MIN_SCROLL_UP_THRESHOLD\n\n if (isDeliberateScrollUp) {\n setShouldAutoScroll(false)\n } else {\n const isScrolledToBottom = distanceFromBottom < ACTIVATION_THRESHOLD\n setShouldAutoScroll(isScrolledToBottom)\n }\n\n previousScrollTop.current = scrollTop\n }\n }\n\n const handleTouchStart = () => {\n setShouldAutoScroll(false)\n }\n\n useEffect(() => {\n if (containerRef.current) {\n previousScrollTop.current = containerRef.current.scrollTop\n }\n }, [])\n\n useEffect(() => {\n if (shouldAutoScroll) {\n scrollToBottom()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, dependencies)\n\n return {\n containerRef,\n scrollToBottom,\n handleScroll,\n shouldAutoScroll,\n handleTouchStart,\n }\n}\n",
8 | "type": "registry:hook",
9 | "target": ""
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/apps/www/public/r/use-autosize-textarea.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "use-autosize-textarea",
3 | "type": "registry:hook",
4 | "files": [
5 | {
6 | "path": "hooks/use-autosize-textarea.ts",
7 | "content": "import { useLayoutEffect, useRef } from \"react\"\n\ninterface UseAutosizeTextAreaProps {\n ref: React.RefObject\n maxHeight?: number\n borderWidth?: number\n dependencies: React.DependencyList\n}\n\nexport function useAutosizeTextArea({\n ref,\n maxHeight = Number.MAX_SAFE_INTEGER,\n borderWidth = 0,\n dependencies,\n}: UseAutosizeTextAreaProps) {\n const originalHeight = useRef(null)\n\n useLayoutEffect(() => {\n if (!ref.current) return\n\n const currentRef = ref.current\n const borderAdjustment = borderWidth * 2\n\n if (originalHeight.current === null) {\n originalHeight.current = currentRef.scrollHeight - borderAdjustment\n }\n\n currentRef.style.removeProperty(\"height\")\n const scrollHeight = currentRef.scrollHeight\n\n // Make sure we don't go over maxHeight\n const clampedToMax = Math.min(scrollHeight, maxHeight)\n // Make sure we don't go less than the original height\n const clampedToMin = Math.max(clampedToMax, originalHeight.current)\n\n currentRef.style.height = `${clampedToMin + borderAdjustment}px`\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [maxHeight, ref, ...dependencies])\n}\n",
8 | "type": "registry:hook",
9 | "target": ""
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/apps/www/public/r/use-copy-to-clipboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "use-copy-to-clipboard",
3 | "type": "registry:hook",
4 | "registryDependencies": [
5 | "sonner"
6 | ],
7 | "files": [
8 | {
9 | "path": "hooks/use-copy-to-clipboard.ts",
10 | "content": "import { useCallback, useRef, useState } from \"react\"\nimport { toast } from \"sonner\"\n\ntype UseCopyToClipboardProps = {\n text: string\n copyMessage?: string\n}\n\nexport function useCopyToClipboard({\n text,\n copyMessage = \"Copied to clipboard!\",\n}: UseCopyToClipboardProps) {\n const [isCopied, setIsCopied] = useState(false)\n const timeoutRef = useRef(null)\n\n const handleCopy = useCallback(() => {\n navigator.clipboard\n .writeText(text)\n .then(() => {\n toast.success(copyMessage)\n setIsCopied(true)\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n timeoutRef.current = null\n }\n timeoutRef.current = setTimeout(() => {\n setIsCopied(false)\n }, 2000)\n })\n .catch(() => {\n toast.error(\"Failed to copy to clipboard.\")\n })\n }, [text, copyMessage])\n\n return { isCopied, handleCopy }\n}\n",
11 | "type": "registry:hook",
12 | "target": ""
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/apps/www/public/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "type": "object",
4 | "properties": {
5 | "style": {
6 | "type": "string",
7 | "enum": ["default", "new-york"]
8 | },
9 | "tailwind": {
10 | "type": "object",
11 | "properties": {
12 | "config": {
13 | "type": "string"
14 | },
15 | "css": {
16 | "type": "string"
17 | },
18 | "baseColor": {
19 | "type": "string"
20 | },
21 | "cssVariables": {
22 | "type": "boolean"
23 | },
24 | "prefix": {
25 | "type": "string"
26 | }
27 | },
28 | "required": ["config", "css", "baseColor", "cssVariables"]
29 | },
30 | "rsc": {
31 | "type": "boolean"
32 | },
33 | "tsx": {
34 | "type": "boolean"
35 | },
36 | "aliases": {
37 | "type": "object",
38 | "properties": {
39 | "utils": {
40 | "type": "string"
41 | },
42 | "components": {
43 | "type": "string"
44 | },
45 | "ui": {
46 | "type": "string"
47 | },
48 | "lib": {
49 | "type": "string"
50 | },
51 | "hooks": {
52 | "type": "string"
53 | }
54 | },
55 | "required": ["utils", "components"]
56 | }
57 | },
58 | "required": ["style", "tailwind", "rsc", "aliases"]
59 | }
60 |
--------------------------------------------------------------------------------
/apps/www/registry/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/eslintrc",
3 | "rules": {
4 | "react/no-unescaped-entities": "off"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/chat-demo.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { useChat, type UseChatOptions } from "@ai-sdk/react"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { transcribeAudio } from "@/lib/utils/audio"
8 | import { Chat } from "@/registry/default/ui/chat"
9 | import {
10 | Select,
11 | SelectContent,
12 | SelectItem,
13 | SelectTrigger,
14 | SelectValue,
15 | } from "@/registry/default/ui/select"
16 |
17 | const MODELS = [
18 | { id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B" },
19 | { id: "deepseek-r1-distill-llama-70b", name: "Deepseek R1 70B" },
20 | ]
21 |
22 | type ChatDemoProps = {
23 | initialMessages?: UseChatOptions["initialMessages"]
24 | }
25 |
26 | export default function ChatDemo(props: ChatDemoProps) {
27 | const [selectedModel, setSelectedModel] = useState(MODELS[0].id)
28 |
29 | const {
30 | messages,
31 | input,
32 | handleInputChange,
33 | handleSubmit,
34 | append,
35 | stop,
36 | isLoading,
37 | setMessages,
38 | } = useChat({
39 | ...props,
40 | api: "/api/chat",
41 | body: {
42 | model: selectedModel,
43 | },
44 | })
45 |
46 | return (
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {MODELS.map((model) => (
55 |
56 | {model.name}
57 |
58 | ))}
59 |
60 |
61 |
62 |
63 |
80 |
81 | )
82 | }
83 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/chat-message-actions-demo.tsx:
--------------------------------------------------------------------------------
1 | import { ThumbsDown, ThumbsUp } from "lucide-react"
2 |
3 | import { Button } from "@/registry/default/ui/button"
4 | import { ChatMessage } from "@/registry/default/ui/chat-message"
5 | import { CopyButton } from "@/registry/default/ui/copy-button"
6 |
7 | export default function ChatMessageActionsDemo() {
8 | return (
9 |
10 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | >
29 | }
30 | />
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/chat-message-animations-demo.tsx:
--------------------------------------------------------------------------------
1 | import { ChatMessage } from "@/registry/default/ui/chat-message"
2 |
3 | export default function ChatMessageAnimationsDemo() {
4 | return (
5 |
6 |
12 |
18 |
24 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/chat-message-demo.tsx:
--------------------------------------------------------------------------------
1 | import { ChatMessage } from "@/registry/default/ui/chat-message"
2 |
3 | export default function ChatMessageDemo() {
4 | return (
5 |
6 |
7 |
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/chat-message-timestamp-demo.tsx:
--------------------------------------------------------------------------------
1 | import { ChatMessage } from "@/registry/default/ui/chat-message"
2 |
3 | export default function ChatMessageTimestampDemo() {
4 | const createdAt = new Date("2024-11-28T11:37:00.000Z")
5 |
6 | return (
7 |
8 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/copy-button-code-block-demo.tsx:
--------------------------------------------------------------------------------
1 | import { CopyButton } from "@/registry/default/ui/copy-button"
2 |
3 | export default function CopyButtonCodeBlockDemo() {
4 | return (
5 |
6 | console.log("Hello World")
7 |
8 |
9 |
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/copy-button-custom-message-demo.tsx:
--------------------------------------------------------------------------------
1 | import { CopyButton } from "@/registry/default/ui/copy-button"
2 |
3 | export default function CopyButtonCustomMessageDemo() {
4 | return (
5 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/copy-button-demo.tsx:
--------------------------------------------------------------------------------
1 | import { CopyButton } from "@/registry/default/ui/copy-button"
2 |
3 | export default function CopyButtonDemo() {
4 | return (
5 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/file-preview-demo.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 |
5 | import { Button } from "@/registry/default/ui/button"
6 | import { FilePreview } from "@/registry/default/ui/file-preview"
7 |
8 | export default function FilePreviewDemo() {
9 | const [files, setFiles] = useState([])
10 |
11 | const handleFileSelect = () => {
12 | const input = document.createElement("input")
13 | input.type = "file"
14 | input.multiple = true
15 | input.onchange = (e) => {
16 | const files = (e.target as HTMLInputElement).files
17 | if (files) {
18 | setFiles(Array.from(files))
19 | }
20 | }
21 | input.click()
22 | }
23 |
24 | return (
25 |
26 |
27 | {files.map((file) => (
28 | {
32 | setFiles((files) => files.filter((f) => f !== file))
33 | }}
34 | />
35 | ))}
36 |
37 |
Select Files
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/markdown-renderer-demo.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useEffect, useState } from "react"
4 |
5 | import { MarkdownRenderer } from "@/registry/default/ui/markdown-renderer"
6 |
7 | export default function MarkdownRendererDemo() {
8 | const [content, setContent] = useState("")
9 |
10 | useEffect(() => {
11 | setContent(`# Hello World
12 |
13 | This is a paragraph with **bold** and *italic* text.
14 |
15 | ## Lists
16 | - Item 1
17 | - Item 2
18 | - Nested item
19 |
20 | ## Code
21 | \`\`\`tsx
22 | console.log("Hello World")
23 | \`\`\`
24 |
25 | ## Tables
26 | | Header 1 | Header 2 |
27 | |----------|----------|
28 | | Cell 1 | Cell 2 |
29 | `)
30 | }, [])
31 |
32 | return (
33 |
34 | {content}
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/message-input-demo.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useRef, useState } from "react"
4 |
5 | import { transcribeAudio } from "@/lib/utils/audio"
6 |
7 | import { ChatForm } from "../ui/chat"
8 | import { MessageInput } from "../ui/message-input"
9 |
10 | export default function MessageInputDemo() {
11 | const [value, setValue] = useState("")
12 | const [isGenerating, setIsGenerating] = useState(false)
13 | const timeout = useRef()
14 |
15 | const cancelTimeout = () => {
16 | if (timeout.current) {
17 | window.clearTimeout(timeout.current)
18 | }
19 | }
20 |
21 | const setNewTimeout = (callback: () => void, ms: number) => {
22 | cancelTimeout()
23 | const id = window.setTimeout(callback, ms)
24 | timeout.current = id
25 | }
26 |
27 | return (
28 | {
32 | event?.preventDefault?.()
33 | setValue("")
34 | setIsGenerating(true)
35 | setNewTimeout(() => {
36 | setIsGenerating(false)
37 | }, 2000)
38 | }}
39 | >
40 | {({ files, setFiles }) => (
41 | {
44 | setValue(event.target.value)
45 | }}
46 | allowAttachments
47 | files={files}
48 | setFiles={setFiles}
49 | stop={() => {
50 | setIsGenerating(false)
51 | cancelTimeout()
52 | }}
53 | isGenerating={isGenerating}
54 | transcribeAudio={transcribeAudio}
55 | />
56 | )}
57 |
58 | )
59 | }
60 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/message-list-demo.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useEffect, useRef, useState } from "react"
4 |
5 | import { MessageList } from "@/registry/default/ui/message-list"
6 |
7 | export default function MessageListDemo() {
8 | const effectRan = useRef(false)
9 | const [isTyping, setIsTyping] = useState(true)
10 | const [messages, setMessages] = useState([
11 | {
12 | id: "1",
13 | role: "user",
14 | content: "Hello! What is your name?",
15 | },
16 | ])
17 |
18 | useEffect(() => {
19 | if (effectRan.current) return
20 | effectRan.current = true
21 |
22 | setTimeout(() => {
23 | setIsTyping(false)
24 | setMessages((messages) => [
25 | ...messages,
26 | {
27 | id: "2",
28 | role: "assistant",
29 | content: "Hello! I go by ChatGPT. How are you?",
30 | },
31 | ])
32 | }, 1500)
33 | }, [])
34 |
35 | return (
36 |
37 |
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/prompt-suggestions-demo.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { toast } from "sonner"
4 |
5 | import { PromptSuggestions } from "@/registry/default/ui/prompt-suggestions"
6 |
7 | export default function PromptSuggestionsDemo() {
8 | return (
9 |
10 |
{
13 | toast(`Clicked on "${message.content}"`)
14 | }}
15 | suggestions={[
16 | "What is the capital of France?",
17 | "Who won the 2022 FIFA World Cup?",
18 | "Give me a vegan lasagna recipe for 3 people.",
19 | ]}
20 | />
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/apps/www/registry/default/example/typing-indicator-demo.tsx:
--------------------------------------------------------------------------------
1 | import { TypingIndicator } from "@/registry/default/ui/typing-indicator"
2 |
3 | export default function TypingIndicatorDemo() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/apps/www/registry/default/hooks/use-audio-recording.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from "react"
2 |
3 | import { recordAudio } from "@/registry/default/lib/audio-utils"
4 |
5 | interface UseAudioRecordingOptions {
6 | transcribeAudio?: (blob: Blob) => Promise
7 | onTranscriptionComplete?: (text: string) => void
8 | }
9 |
10 | export function useAudioRecording({
11 | transcribeAudio,
12 | onTranscriptionComplete,
13 | }: UseAudioRecordingOptions) {
14 | const [isListening, setIsListening] = useState(false)
15 | const [isSpeechSupported, setIsSpeechSupported] = useState(!!transcribeAudio)
16 | const [isRecording, setIsRecording] = useState(false)
17 | const [isTranscribing, setIsTranscribing] = useState(false)
18 | const [audioStream, setAudioStream] = useState(null)
19 | const activeRecordingRef = useRef(null)
20 |
21 | useEffect(() => {
22 | const checkSpeechSupport = async () => {
23 | const hasMediaDevices = !!(
24 | navigator.mediaDevices && navigator.mediaDevices.getUserMedia
25 | )
26 | setIsSpeechSupported(hasMediaDevices && !!transcribeAudio)
27 | }
28 |
29 | checkSpeechSupport()
30 | }, [transcribeAudio])
31 |
32 | const stopRecording = async () => {
33 | setIsRecording(false)
34 | setIsTranscribing(true)
35 | try {
36 | // First stop the recording to get the final blob
37 | recordAudio.stop()
38 | // Wait for the recording promise to resolve with the final blob
39 | const recording = await activeRecordingRef.current
40 | if (transcribeAudio) {
41 | const text = await transcribeAudio(recording)
42 | onTranscriptionComplete?.(text)
43 | }
44 | } catch (error) {
45 | console.error("Error transcribing audio:", error)
46 | } finally {
47 | setIsTranscribing(false)
48 | setIsListening(false)
49 | if (audioStream) {
50 | audioStream.getTracks().forEach((track) => track.stop())
51 | setAudioStream(null)
52 | }
53 | activeRecordingRef.current = null
54 | }
55 | }
56 |
57 | const toggleListening = async () => {
58 | if (!isListening) {
59 | try {
60 | setIsListening(true)
61 | setIsRecording(true)
62 | // Get audio stream first
63 | const stream = await navigator.mediaDevices.getUserMedia({
64 | audio: true,
65 | })
66 | setAudioStream(stream)
67 |
68 | // Start recording with the stream
69 | activeRecordingRef.current = recordAudio(stream)
70 | } catch (error) {
71 | console.error("Error recording audio:", error)
72 | setIsListening(false)
73 | setIsRecording(false)
74 | if (audioStream) {
75 | audioStream.getTracks().forEach((track) => track.stop())
76 | setAudioStream(null)
77 | }
78 | }
79 | } else {
80 | await stopRecording()
81 | }
82 | }
83 |
84 | return {
85 | isListening,
86 | isSpeechSupported,
87 | isRecording,
88 | isTranscribing,
89 | audioStream,
90 | toggleListening,
91 | stopRecording,
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/apps/www/registry/default/hooks/use-auto-scroll.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from "react"
2 |
3 | // How many pixels from the bottom of the container to enable auto-scroll
4 | const ACTIVATION_THRESHOLD = 50
5 | // Minimum pixels of scroll-up movement required to disable auto-scroll
6 | const MIN_SCROLL_UP_THRESHOLD = 10
7 |
8 | export function useAutoScroll(dependencies: React.DependencyList) {
9 | const containerRef = useRef(null)
10 | const previousScrollTop = useRef(null)
11 | const [shouldAutoScroll, setShouldAutoScroll] = useState(true)
12 |
13 | const scrollToBottom = () => {
14 | if (containerRef.current) {
15 | containerRef.current.scrollTop = containerRef.current.scrollHeight
16 | }
17 | }
18 |
19 | const handleScroll = () => {
20 | if (containerRef.current) {
21 | const { scrollTop, scrollHeight, clientHeight } = containerRef.current
22 |
23 | const distanceFromBottom = Math.abs(
24 | scrollHeight - scrollTop - clientHeight
25 | )
26 |
27 | const isScrollingUp = previousScrollTop.current
28 | ? scrollTop < previousScrollTop.current
29 | : false
30 |
31 | const scrollUpDistance = previousScrollTop.current
32 | ? previousScrollTop.current - scrollTop
33 | : 0
34 |
35 | const isDeliberateScrollUp =
36 | isScrollingUp && scrollUpDistance > MIN_SCROLL_UP_THRESHOLD
37 |
38 | if (isDeliberateScrollUp) {
39 | setShouldAutoScroll(false)
40 | } else {
41 | const isScrolledToBottom = distanceFromBottom < ACTIVATION_THRESHOLD
42 | setShouldAutoScroll(isScrolledToBottom)
43 | }
44 |
45 | previousScrollTop.current = scrollTop
46 | }
47 | }
48 |
49 | const handleTouchStart = () => {
50 | setShouldAutoScroll(false)
51 | }
52 |
53 | useEffect(() => {
54 | if (containerRef.current) {
55 | previousScrollTop.current = containerRef.current.scrollTop
56 | }
57 | }, [])
58 |
59 | useEffect(() => {
60 | if (shouldAutoScroll) {
61 | scrollToBottom()
62 | }
63 | // eslint-disable-next-line react-hooks/exhaustive-deps
64 | }, dependencies)
65 |
66 | return {
67 | containerRef,
68 | scrollToBottom,
69 | handleScroll,
70 | shouldAutoScroll,
71 | handleTouchStart,
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/apps/www/registry/default/hooks/use-autosize-textarea.ts:
--------------------------------------------------------------------------------
1 | import { useLayoutEffect, useRef } from "react"
2 |
3 | interface UseAutosizeTextAreaProps {
4 | ref: React.RefObject
5 | maxHeight?: number
6 | borderWidth?: number
7 | dependencies: React.DependencyList
8 | }
9 |
10 | export function useAutosizeTextArea({
11 | ref,
12 | maxHeight = Number.MAX_SAFE_INTEGER,
13 | borderWidth = 0,
14 | dependencies,
15 | }: UseAutosizeTextAreaProps) {
16 | const originalHeight = useRef(null)
17 |
18 | useLayoutEffect(() => {
19 | if (!ref.current) return
20 |
21 | const currentRef = ref.current
22 | const borderAdjustment = borderWidth * 2
23 |
24 | if (originalHeight.current === null) {
25 | originalHeight.current = currentRef.scrollHeight - borderAdjustment
26 | }
27 |
28 | currentRef.style.removeProperty("height")
29 | const scrollHeight = currentRef.scrollHeight
30 |
31 | // Make sure we don't go over maxHeight
32 | const clampedToMax = Math.min(scrollHeight, maxHeight)
33 | // Make sure we don't go less than the original height
34 | const clampedToMin = Math.max(clampedToMax, originalHeight.current)
35 |
36 | currentRef.style.height = `${clampedToMin + borderAdjustment}px`
37 | // eslint-disable-next-line react-hooks/exhaustive-deps
38 | }, [maxHeight, ref, ...dependencies])
39 | }
40 |
--------------------------------------------------------------------------------
/apps/www/registry/default/hooks/use-copy-to-clipboard.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useRef, useState } from "react"
2 | import { toast } from "sonner"
3 |
4 | type UseCopyToClipboardProps = {
5 | text: string
6 | copyMessage?: string
7 | }
8 |
9 | export function useCopyToClipboard({
10 | text,
11 | copyMessage = "Copied to clipboard!",
12 | }: UseCopyToClipboardProps) {
13 | const [isCopied, setIsCopied] = useState(false)
14 | const timeoutRef = useRef(null)
15 |
16 | const handleCopy = useCallback(() => {
17 | navigator.clipboard
18 | .writeText(text)
19 | .then(() => {
20 | toast.success(copyMessage)
21 | setIsCopied(true)
22 | if (timeoutRef.current) {
23 | clearTimeout(timeoutRef.current)
24 | timeoutRef.current = null
25 | }
26 | timeoutRef.current = setTimeout(() => {
27 | setIsCopied(false)
28 | }, 2000)
29 | })
30 | .catch(() => {
31 | toast.error("Failed to copy to clipboard.")
32 | })
33 | }, [text, copyMessage])
34 |
35 | return { isCopied, handleCopy }
36 | }
37 |
--------------------------------------------------------------------------------
/apps/www/registry/default/lib/audio-utils.ts:
--------------------------------------------------------------------------------
1 | type RecordAudioType = {
2 | (stream: MediaStream): Promise
3 | stop: () => void
4 | currentRecorder?: MediaRecorder
5 | }
6 |
7 | export const recordAudio = (function (): RecordAudioType {
8 | const func = async function recordAudio(stream: MediaStream): Promise {
9 | try {
10 | const mediaRecorder = new MediaRecorder(stream, {
11 | mimeType: "audio/webm;codecs=opus",
12 | })
13 | const audioChunks: Blob[] = []
14 |
15 | return new Promise((resolve, reject) => {
16 | mediaRecorder.ondataavailable = (event) => {
17 | if (event.data.size > 0) {
18 | audioChunks.push(event.data)
19 | }
20 | }
21 |
22 | mediaRecorder.onstop = () => {
23 | const audioBlob = new Blob(audioChunks, { type: "audio/webm" })
24 | resolve(audioBlob)
25 | }
26 |
27 | mediaRecorder.onerror = () => {
28 | reject(new Error("MediaRecorder error occurred"))
29 | }
30 |
31 | mediaRecorder.start(1000)
32 | ;(func as RecordAudioType).currentRecorder = mediaRecorder
33 | })
34 | } catch (error) {
35 | const errorMessage =
36 | error instanceof Error ? error.message : "Unknown error occurred"
37 | throw new Error("Failed to start recording: " + errorMessage)
38 | }
39 | }
40 |
41 | ;(func as RecordAudioType).stop = () => {
42 | const recorder = (func as RecordAudioType).currentRecorder
43 | if (recorder && recorder.state !== "inactive") {
44 | recorder.stop()
45 | }
46 | delete (func as RecordAudioType).currentRecorder
47 | }
48 |
49 | return func as RecordAudioType
50 | })()
51 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/button.tsx:
--------------------------------------------------------------------------------
1 | // This component will not get downloaded by the CLI, it will get pulled from the main registry
2 | // It is only kept in this repo for internal usage
3 |
4 | import * as React from "react"
5 | import { Slot } from "@radix-ui/react-slot"
6 | import { cva, type VariantProps } from "class-variance-authority"
7 |
8 | import { cn } from "@/lib/utils"
9 |
10 | const buttonVariants = cva(
11 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
12 | {
13 | variants: {
14 | variant: {
15 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
16 | destructive:
17 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
18 | outline:
19 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
20 | secondary:
21 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
22 | ghost: "hover:bg-accent hover:text-accent-foreground",
23 | link: "text-primary underline-offset-4 hover:underline",
24 | },
25 | size: {
26 | default: "h-10 px-4 py-2",
27 | sm: "h-9 rounded-md px-3",
28 | lg: "h-11 rounded-md px-8",
29 | icon: "h-10 w-10",
30 | },
31 | },
32 | defaultVariants: {
33 | variant: "default",
34 | size: "default",
35 | },
36 | }
37 | )
38 |
39 | export interface ButtonProps
40 | extends React.ButtonHTMLAttributes,
41 | VariantProps {
42 | asChild?: boolean
43 | }
44 |
45 | const Button = React.forwardRef(
46 | ({ className, variant, size, asChild = false, ...props }, ref) => {
47 | const Comp = asChild ? Slot : "button"
48 | return (
49 |
54 | )
55 | }
56 | )
57 | Button.displayName = "Button"
58 |
59 | export { Button, buttonVariants }
60 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/copy-button.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { Check, Copy } from "lucide-react"
4 |
5 | import { cn } from "@/lib/utils"
6 | import { useCopyToClipboard } from "@/registry/default/hooks/use-copy-to-clipboard"
7 | import { Button } from "@/registry/default/ui/button"
8 |
9 | type CopyButtonProps = {
10 | content: string
11 | copyMessage?: string
12 | }
13 |
14 | export function CopyButton({ content, copyMessage }: CopyButtonProps) {
15 | const { isCopied, handleCopy } = useCopyToClipboard({
16 | text: content,
17 | copyMessage,
18 | })
19 |
20 | return (
21 |
28 |
29 |
35 |
36 |
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/interrupt-prompt.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { AnimatePresence, motion } from "framer-motion"
4 | import { X } from "lucide-react"
5 |
6 | interface InterruptPromptProps {
7 | isOpen: boolean
8 | close: () => void
9 | }
10 |
11 | export function InterruptPrompt({ isOpen, close }: InterruptPromptProps) {
12 | return (
13 |
14 | {isOpen && (
15 |
28 | Press Enter again to interrupt
29 |
35 |
36 |
37 |
38 | )}
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/message-list.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ChatMessage,
3 | type ChatMessageProps,
4 | type Message,
5 | } from "@/registry/default/ui/chat-message"
6 | import { TypingIndicator } from "@/registry/default/ui/typing-indicator"
7 |
8 | type AdditionalMessageOptions = Omit
9 |
10 | interface MessageListProps {
11 | messages: Message[]
12 | showTimeStamps?: boolean
13 | isTyping?: boolean
14 | messageOptions?:
15 | | AdditionalMessageOptions
16 | | ((message: Message) => AdditionalMessageOptions)
17 | }
18 |
19 | export function MessageList({
20 | messages,
21 | showTimeStamps = true,
22 | isTyping = false,
23 | messageOptions,
24 | }: MessageListProps) {
25 | return (
26 |
27 | {messages.map((message, index) => {
28 | const additionalOptions =
29 | typeof messageOptions === "function"
30 | ? messageOptions(message)
31 | : messageOptions
32 |
33 | return (
34 |
40 | )
41 | })}
42 | {isTyping && }
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/prompt-suggestions.tsx:
--------------------------------------------------------------------------------
1 | interface PromptSuggestionsProps {
2 | label: string
3 | append: (message: { role: "user"; content: string }) => void
4 | suggestions: string[]
5 | }
6 |
7 | export function PromptSuggestions({
8 | label,
9 | append,
10 | suggestions,
11 | }: PromptSuggestionsProps) {
12 | return (
13 |
14 |
{label}
15 |
16 | {suggestions.map((suggestion) => (
17 |
append({ role: "user", content: suggestion })}
20 | className="h-max flex-1 rounded-xl border bg-background p-4 hover:bg-muted"
21 | >
22 | {suggestion}
23 |
24 | ))}
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useToast } from "@/registry/default/hooks/use-toast"
4 | import {
5 | Toast,
6 | ToastClose,
7 | ToastDescription,
8 | ToastProvider,
9 | ToastTitle,
10 | ToastViewport,
11 | } from "@/registry/default/ui/toast"
12 |
13 | export function Toaster() {
14 | const { toasts } = useToast()
15 |
16 | return (
17 |
18 | {toasts.map(function ({ id, title, description, action, ...props }) {
19 | return (
20 |
21 |
22 | {title && {title} }
23 | {description && (
24 | {description}
25 | )}
26 |
27 | {action}
28 |
29 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/apps/www/registry/default/ui/typing-indicator.tsx:
--------------------------------------------------------------------------------
1 | import { Dot } from "lucide-react"
2 |
3 | export function TypingIndicator() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/accordion.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AccordionPrimitive from "@radix-ui/react-accordion"
5 | import { ChevronDownIcon } from "@radix-ui/react-icons"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Accordion = AccordionPrimitive.Root
10 |
11 | const AccordionItem = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
20 | ))
21 | AccordionItem.displayName = "AccordionItem"
22 |
23 | const AccordionTrigger = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, children, ...props }, ref) => (
27 |
28 | svg]:rotate-180",
32 | className
33 | )}
34 | {...props}
35 | >
36 | {children}
37 |
38 |
39 |
40 | ))
41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42 |
43 | const AccordionContent = React.forwardRef<
44 | React.ElementRef,
45 | React.ComponentPropsWithoutRef
46 | >(({ className, children, ...props }, ref) => (
47 |
52 | {children}
53 |
54 | ))
55 | AccordionContent.displayName = AccordionPrimitive.Content.displayName
56 |
57 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
58 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/alert.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const alertVariants = cva(
7 | "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
8 | {
9 | variants: {
10 | variant: {
11 | default: "bg-background text-foreground",
12 | destructive:
13 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
14 | },
15 | },
16 | defaultVariants: {
17 | variant: "default",
18 | },
19 | }
20 | )
21 |
22 | const Alert = React.forwardRef<
23 | HTMLDivElement,
24 | React.HTMLAttributes & VariantProps
25 | >(({ className, variant, ...props }, ref) => (
26 |
32 | ))
33 | Alert.displayName = "Alert"
34 |
35 | const AlertTitle = React.forwardRef<
36 | HTMLParagraphElement,
37 | React.HTMLAttributes
38 | >(({ className, ...props }, ref) => (
39 |
44 | ))
45 | AlertTitle.displayName = "AlertTitle"
46 |
47 | const AlertDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | AlertDescription.displayName = "AlertDescription"
58 |
59 | export { Alert, AlertTitle, AlertDescription }
60 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/aspect-ratio.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
4 |
5 | const AspectRatio = AspectRatioPrimitive.Root
6 |
7 | export { AspectRatio }
8 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
13 | secondary:
14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15 | destructive:
16 | "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
17 | outline: "text-foreground",
18 | },
19 | },
20 | defaultVariants: {
21 | variant: "default",
22 | },
23 | }
24 | )
25 |
26 | interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | )
34 | }
35 |
36 | export { badgeVariants }
37 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14 | destructive:
15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16 | outline:
17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18 | secondary:
19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20 | ghost: "hover:bg-accent hover:text-accent-foreground",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-4 py-2",
25 | sm: "h-8 rounded-md px-3 text-xs",
26 | lg: "h-10 rounded-md px-8",
27 | icon: "h-9 w-9",
28 | },
29 | },
30 | defaultVariants: {
31 | variant: "default",
32 | size: "default",
33 | },
34 | }
35 | )
36 |
37 | export interface ButtonProps
38 | extends React.ButtonHTMLAttributes,
39 | VariantProps {
40 | asChild?: boolean
41 | }
42 |
43 | const Button = React.forwardRef(
44 | ({ className, variant, size, asChild = false, ...props }, ref) => {
45 | const Comp = asChild ? Slot : "button"
46 | return (
47 |
52 | )
53 | }
54 | )
55 | Button.displayName = "Button"
56 |
57 | export { Button, buttonVariants }
58 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as LabelPrimitive from "@radix-ui/react-label"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const labelVariants = cva(
10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11 | )
12 |
13 | const Label = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef &
16 | VariantProps
17 | >(({ className, ...props }, ref) => (
18 |
23 | ))
24 | Label.displayName = LabelPrimitive.Root.displayName
25 |
26 | export { Label }
27 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as PopoverPrimitive from "@radix-ui/react-popover"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Popover = PopoverPrimitive.Root
9 |
10 | const PopoverTrigger = PopoverPrimitive.Trigger
11 |
12 | const PopoverAnchor = PopoverPrimitive.Anchor
13 |
14 | const PopoverContent = React.forwardRef<
15 | React.ElementRef,
16 | React.ComponentPropsWithoutRef
17 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
18 |
19 |
29 |
30 | ))
31 | PopoverContent.displayName = PopoverPrimitive.Content.displayName
32 |
33 | export { Popover, PopoverTrigger, PopoverContent }
34 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/scroll-area.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const ScrollArea = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, children, ...props }, ref) => (
12 |
17 |
18 | {children}
19 |
20 |
21 |
22 |
23 | ))
24 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
25 |
26 | const ScrollBar = React.forwardRef<
27 | React.ElementRef,
28 | React.ComponentPropsWithoutRef
29 | >(({ className, orientation = "vertical", ...props }, ref) => (
30 |
43 |
44 |
45 | ))
46 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
47 |
48 | export { ScrollArea }
49 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref
15 | ) => (
16 |
27 | )
28 | )
29 | Separator.displayName = SeparatorPrimitive.Root.displayName
30 |
31 | export { Separator }
32 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils"
2 |
3 | function Skeleton({
4 | className,
5 | ...props
6 | }: React.HTMLAttributes) {
7 | return (
8 |
12 | )
13 | }
14 |
15 | export { Skeleton }
16 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useTheme } from "next-themes"
4 | import { Toaster as Sonner } from "sonner"
5 |
6 | type ToasterProps = React.ComponentProps
7 |
8 | const Toaster = ({ ...props }: ToasterProps) => {
9 | const { theme = "system" } = useTheme()
10 |
11 | return (
12 |
28 | )
29 | }
30 |
31 | export { Toaster }
32 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TabsPrimitive from "@radix-ui/react-tabs"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Tabs = TabsPrimitive.Root
9 |
10 | const TabsList = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ))
23 | TabsList.displayName = TabsPrimitive.List.displayName
24 |
25 | const TabsTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef
28 | >(({ className, ...props }, ref) => (
29 |
37 | ))
38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
39 |
40 | const TabsContent = React.forwardRef<
41 | React.ElementRef,
42 | React.ComponentPropsWithoutRef
43 | >(({ className, ...props }, ref) => (
44 |
52 | ))
53 | TabsContent.displayName = TabsPrimitive.Content.displayName
54 |
55 | export { Tabs, TabsList, TabsTrigger, TabsContent }
56 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useToast } from "@/registry/new-york/hooks/use-toast"
4 | import {
5 | Toast,
6 | ToastClose,
7 | ToastDescription,
8 | ToastProvider,
9 | ToastTitle,
10 | ToastViewport,
11 | } from "@/registry/new-york/ui/toast"
12 |
13 | export function Toaster() {
14 | const { toasts } = useToast()
15 |
16 | return (
17 |
18 | {toasts.map(function ({ id, title, description, action, ...props }) {
19 | return (
20 |
21 |
22 | {title && {title} }
23 | {description && (
24 | {description}
25 | )}
26 |
27 | {action}
28 |
29 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/apps/www/registry/new-york/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const TooltipProvider = TooltipPrimitive.Provider
9 |
10 | const Tooltip = TooltipPrimitive.Root
11 |
12 | const TooltipTrigger = TooltipPrimitive.Trigger
13 |
14 | const TooltipContent = React.forwardRef<
15 | React.ElementRef,
16 | React.ComponentPropsWithoutRef
17 | >(({ className, sideOffset = 4, ...props }, ref) => (
18 |
19 |
28 |
29 | ))
30 | TooltipContent.displayName = TooltipPrimitive.Content.displayName
31 |
32 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
33 |
--------------------------------------------------------------------------------
/apps/www/registry/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod"
2 |
3 | export const blockChunkSchema = z.object({
4 | name: z.string(),
5 | description: z.string(),
6 | component: z.any(),
7 | file: z.string(),
8 | code: z.string().optional(),
9 | container: z
10 | .object({
11 | className: z.string().nullish(),
12 | })
13 | .optional(),
14 | })
15 |
16 | export const registryItemTypeSchema = z.enum([
17 | "registry:style",
18 | "registry:lib",
19 | "registry:example",
20 | "registry:block",
21 | "registry:component",
22 | "registry:ui",
23 | "registry:hook",
24 | "registry:theme",
25 | "registry:page",
26 | ])
27 |
28 | export const registryItemFileSchema = z.union([
29 | z.string(),
30 | z.object({
31 | path: z.string(),
32 | content: z.string().optional(),
33 | type: registryItemTypeSchema,
34 | target: z.string().optional(),
35 | }),
36 | ])
37 |
38 | export const registryItemTailwindSchema = z.object({
39 | config: z.object({
40 | content: z.array(z.string()).optional(),
41 | theme: z.record(z.string(), z.any()).optional(),
42 | plugins: z.array(z.string()).optional(),
43 | }),
44 | })
45 |
46 | export const registryItemCssVarsSchema = z.object({
47 | light: z.record(z.string(), z.string()).optional(),
48 | dark: z.record(z.string(), z.string()).optional(),
49 | })
50 |
51 | export const registryEntrySchema = z.object({
52 | name: z.string(),
53 | type: registryItemTypeSchema,
54 | description: z.string().optional(),
55 | dependencies: z.array(z.string()).optional(),
56 | devDependencies: z.array(z.string()).optional(),
57 | registryDependencies: z.array(z.string()).optional(),
58 | files: z.array(registryItemFileSchema).optional(),
59 | tailwind: registryItemTailwindSchema.optional(),
60 | cssVars: registryItemCssVarsSchema.optional(),
61 | source: z.string().optional(),
62 | category: z.string().optional(),
63 | subcategory: z.string().optional(),
64 | chunks: z.array(blockChunkSchema).optional(),
65 | docs: z.string().optional(),
66 | })
67 |
68 | export const registrySchema = z.array(registryEntrySchema)
69 |
70 | export type RegistryEntry = z.infer
71 |
72 | export type Registry = z.infer
73 |
74 | export const blockSchema = registryEntrySchema.extend({
75 | type: z.literal("registry:block"),
76 | style: z.enum(["default", "new-york"]),
77 | component: z.any(),
78 | container: z
79 | .object({
80 | height: z.string().nullish(),
81 | className: z.string().nullish(),
82 | })
83 | .optional(),
84 | code: z.string(),
85 | highlightedCode: z.string(),
86 | })
87 |
88 | export type Block = z.infer
89 |
90 | export type BlockChunk = z.infer
91 |
--------------------------------------------------------------------------------
/apps/www/scripts/capture-screenshots.mts:
--------------------------------------------------------------------------------
1 | import puppeteer from "puppeteer"
2 |
3 | const BLOCKS = [
4 | "login-01",
5 | "sidebar-01",
6 | "sidebar-02",
7 | "sidebar-03",
8 | "sidebar-04",
9 | "sidebar-05",
10 | "sidebar-06",
11 | "sidebar-07",
12 | "sidebar-08",
13 | "sidebar-09",
14 | "sidebar-10",
15 | "sidebar-11",
16 | "sidebar-12",
17 | "sidebar-13",
18 | "sidebar-14",
19 | "sidebar-15",
20 | "demo-sidebar",
21 | "demo-sidebar-header",
22 | "demo-sidebar-footer",
23 | "demo-sidebar-group",
24 | "demo-sidebar-group-collapsible",
25 | "demo-sidebar-group-action",
26 | "demo-sidebar-menu",
27 | "demo-sidebar-menu-action",
28 | "demo-sidebar-menu-sub",
29 | "demo-sidebar-menu-collapsible",
30 | "demo-sidebar-menu-badge",
31 | "demo-sidebar-rsc",
32 | "demo-sidebar-controlled",
33 | ]
34 |
35 | try {
36 | const browser = await puppeteer.launch({
37 | defaultViewport: {
38 | width: 1440,
39 | height: 900,
40 | deviceScaleFactor: 2,
41 | },
42 | })
43 |
44 | console.log("☀️ Capturing screenshots for light theme")
45 | for (const block of BLOCKS) {
46 | const pageUrl = `http://localhost:3333/blocks/new-york/${block}`
47 | console.log(`- ${block}`)
48 |
49 | const page = await browser.newPage()
50 | await page.goto(pageUrl, {
51 | waitUntil: "networkidle2",
52 | })
53 |
54 | // Hide Tailwind indicator
55 | await page.evaluate(() => {
56 | const indicator = document.querySelector("[data-tailwind-indicator]")
57 | if (indicator) {
58 | indicator.remove()
59 | }
60 | })
61 |
62 | await page.screenshot({
63 | path: `./public/images/blocks/${block}.png`,
64 | })
65 | }
66 |
67 | console.log("🌙 Capturing screenshots for dark theme")
68 | for (const block of BLOCKS) {
69 | const pageUrl = `http://localhost:3333/blocks/new-york/${block}`
70 | console.log(`- ${block}`)
71 |
72 | const page = await browser.newPage()
73 | await page.goto(pageUrl, {
74 | waitUntil: "networkidle2",
75 | })
76 |
77 | // Hide Tailwind indicator
78 | await page.evaluate(() => {
79 | const indicator = document.querySelector("[data-tailwind-indicator]")
80 | if (indicator) {
81 | indicator.remove()
82 | }
83 | })
84 |
85 | // Set theme to dark
86 | await page.evaluate(() => {
87 | localStorage.setItem("theme", "dark")
88 | })
89 |
90 | await page.screenshot({
91 | path: `./public/images/blocks/${block}-dark.png`,
92 | })
93 | }
94 |
95 | await browser.close()
96 | console.log("✅ Done!")
97 | } catch (error) {
98 | console.error(error)
99 | process.exit(1)
100 | }
101 |
--------------------------------------------------------------------------------
/apps/www/scripts/fix-import.mts:
--------------------------------------------------------------------------------
1 | export function fixImport(content: string) {
2 | const regex = /@\/(.+?)\/((?:.*?\/)?(?:components|ui|hooks|lib))\/([\w-]+)/g
3 |
4 | const replacement = (
5 | match: string,
6 | path: string,
7 | type: string,
8 | component: string
9 | ) => {
10 | if (type.endsWith("components")) {
11 | return `@/components/${component}`
12 | } else if (type.endsWith("ui")) {
13 | return `@/components/ui/${component}`
14 | } else if (type.endsWith("hooks")) {
15 | return `@/hooks/${component}`
16 | } else if (type.endsWith("lib")) {
17 | return `@/lib/${component}`
18 | }
19 |
20 | return match
21 | }
22 |
23 | return content.replace(regex, replacement)
24 | }
25 |
--------------------------------------------------------------------------------
/apps/www/styles/mdx.css:
--------------------------------------------------------------------------------
1 | [data-theme="light"] {
2 | display: block;
3 | }
4 |
5 | [data-theme="dark"] {
6 | display: none;
7 | }
8 |
9 | .dark [data-theme="light"] {
10 | display: none;
11 | }
12 |
13 | .dark [data-theme="dark"] {
14 | display: block;
15 | }
16 |
17 | [data-rehype-pretty-code-figure] {
18 | @apply relative text-white;
19 | }
20 |
21 | [data-rehype-pretty-code-figure] code {
22 | @apply grid min-w-full break-words rounded-none border-0 bg-transparent p-0;
23 | counter-reset: line;
24 | box-decoration-break: clone;
25 | }
26 |
27 | [data-rehype-pretty-code-figure] .line {
28 | @apply px-4 min-h-[1rem] py-0.5 w-full inline-block;
29 | }
30 |
31 | [data-rehype-pretty-code-figure] [data-line-numbers] .line {
32 | @apply px-2;
33 | }
34 |
35 | [data-rehype-pretty-code-figure] [data-line-numbers] > .line::before {
36 | @apply text-zinc-50/40 text-xs;
37 | counter-increment: line;
38 | content: counter(line);
39 | display: inline-block;
40 | width: 1.8rem;
41 | margin-right: 1.4rem;
42 | text-align: right;
43 | }
44 |
45 | [data-rehype-pretty-code-figure] .line--highlighted {
46 | @apply bg-zinc-700/50;
47 | }
48 |
49 | [data-rehype-pretty-code-figure] .line-highlighted span {
50 | @apply relative;
51 | }
52 |
53 | [data-rehype-pretty-code-figure] .word--highlighted {
54 | @apply rounded-md bg-zinc-700/50 border-zinc-700/70 p-1;
55 | }
56 |
57 | .dark [data-rehype-pretty-code-figure] .word--highlighted {
58 | @apply bg-zinc-900;
59 | }
60 |
61 | [data-rehype-pretty-code-title] {
62 | @apply mt-2 pt-6 px-4 text-sm font-medium text-foreground;
63 | }
64 |
65 | [data-rehype-pretty-code-title] + pre {
66 | @apply mt-2;
67 | }
68 |
69 | .mdx > .steps:first-child > h3:first-child {
70 | @apply mt-0;
71 | }
72 |
73 | .steps > h3 {
74 | @apply mt-8 mb-4 text-base font-semibold;
75 | }
76 |
--------------------------------------------------------------------------------
/apps/www/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | const baseConfig = require("../../tailwind.config.cjs")
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | module.exports = {
5 | ...baseConfig,
6 | theme: {
7 | ...baseConfig.theme,
8 | extend: {
9 | ...baseConfig.theme.extend,
10 | colors: {
11 | ...baseConfig.theme.extend.colors,
12 | blazity: "#FF4400",
13 | },
14 | },
15 | },
16 | content: [
17 | ...baseConfig.content,
18 | "content/**/*.mdx",
19 | "registry/**/*.{ts,tsx}",
20 | ],
21 | }
22 |
--------------------------------------------------------------------------------
/apps/www/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "target": "es5",
6 | "lib": ["dom", "dom.iterable", "esnext"],
7 | "allowJs": true,
8 | "skipLibCheck": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noEmit": true,
12 | "incremental": true,
13 | "esModuleInterop": true,
14 | "module": "esnext",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "baseUrl": ".",
19 | "paths": {
20 | "@/*": ["./*"],
21 | "contentlayer/generated": ["./.contentlayer/generated"]
22 | },
23 | "plugins": [
24 | {
25 | "name": "next"
26 | }
27 | ]
28 | },
29 | "include": [
30 | "next-env.d.ts",
31 | "**/*.ts",
32 | "**/*.tsx",
33 | ".next/types/**/*.ts",
34 | ".contentlayer/generated"
35 | ],
36 | "exclude": ["node_modules", "./scripts/build-registry.mts", "__registry__"]
37 | }
38 |
--------------------------------------------------------------------------------
/apps/www/tsconfig.scripts.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "target": "es6",
6 | "module": "ESNext",
7 | "moduleResolution": "node",
8 | "esModuleInterop": true,
9 | "isolatedModules": false
10 | },
11 | "include": [".contentlayer/generated", "scripts/**/*.ts"],
12 | "exclude": ["node_modules"]
13 | }
14 |
--------------------------------------------------------------------------------
/apps/www/types/nav.ts:
--------------------------------------------------------------------------------
1 | import { Icons } from "@/components/icons"
2 |
3 | export interface NavItem {
4 | title: string
5 | href?: string
6 | disabled?: boolean
7 | external?: boolean
8 | icon?: keyof typeof Icons
9 | label?: string
10 | }
11 |
12 | export interface NavItemWithChildren extends NavItem {
13 | items: NavItemWithChildren[]
14 | }
15 |
16 | export interface MainNavItem extends NavItem {}
17 |
18 | export interface SidebarNavItem extends NavItemWithChildren {}
19 |
--------------------------------------------------------------------------------
/apps/www/types/unist.ts:
--------------------------------------------------------------------------------
1 | import { Node } from "unist-builder"
2 |
3 | export interface UnistNode extends Node {
4 | type: string
5 | name?: string
6 | tagName?: string
7 | value?: string
8 | properties?: {
9 | __rawString__?: string
10 | __className__?: string
11 | __event__?: string
12 | [key: string]: unknown
13 | } & NpmCommands
14 | attributes?: {
15 | name: string
16 | value: unknown
17 | type?: string
18 | }[]
19 | children?: UnistNode[]
20 | }
21 |
22 | export interface UnistTree extends Node {
23 | children: UnistNode[]
24 | }
25 |
26 | export interface NpmCommands {
27 | __npmCommand__?: string
28 | __yarnCommand__?: string
29 | __pnpmCommand__?: string
30 | __bunCommand__?: string
31 | }
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shadcn-chatbot-kit",
3 | "version": "0.0.1",
4 | "private": true,
5 | "type": "module",
6 | "workspaces": [
7 | "apps/*"
8 | ],
9 | "scripts": {
10 | "build": "turbo run build",
11 | "build:registry": "pnpm --filter=www build:registry && pnpm format:write -- --loglevel silent",
12 | "registry:build": "pnpm --filter=www build:registry && pnpm format:write -- --loglevel silent",
13 | "registry:capture": "pnpm --filter=www registry:capture",
14 | "dev": "turbo run dev --parallel",
15 | "docs:build": "pnpm --filter=www build:docs",
16 | "www:dev": "pnpm --filter=www dev",
17 | "www:build": "pnpm --filter=www build",
18 | "lint": "turbo run lint",
19 | "lint:fix": "turbo run lint:fix",
20 | "preview": "turbo run preview",
21 | "typecheck": "turbo run typecheck",
22 | "format:write": "turbo run format:write",
23 | "format:check": "turbo run format:check",
24 | "check": "turbo lint typecheck format:check"
25 | },
26 | "dependencies": {
27 | "@commitlint/config-conventional": "^17.6.3",
28 | "@ianvs/prettier-plugin-sort-imports": "^3.7.2",
29 | "@typescript-eslint/parser": "^5.59.7",
30 | "autoprefixer": "^10.4.14",
31 | "eslint": "^8.41.0",
32 | "eslint-config-next": "13.3.0",
33 | "eslint-config-prettier": "^8.8.0",
34 | "eslint-config-turbo": "^1.9.9",
35 | "eslint-plugin-tailwindcss": "3.13.1",
36 | "postcss": "^8.4.24",
37 | "prettier": "^2.8.8",
38 | "puppeteer": "^23.6.0",
39 | "tailwindcss": "3.4.6",
40 | "tailwindcss-animate": "^1.0.5",
41 | "tsx": "^4.1.4",
42 | "turbo": "^1.9.9",
43 | "vite": "^5.4.1",
44 | "vite-tsconfig-paths": "^4.2.0",
45 | "vitest": "^2.0.5"
46 | },
47 | "devDependencies": {
48 | "@types/node": "^20.11.27",
49 | "typescript": "^5.5.3"
50 | },
51 | "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903",
52 | "pnpm": {
53 | "patchedDependencies": {
54 | "@ai-sdk/groq@1.1.14": "patches/@ai-sdk__groq@1.1.14.patch"
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/patches/@ai-sdk__groq@1.1.14.patch:
--------------------------------------------------------------------------------
1 | diff --git a/dist/index.js b/dist/index.js
2 | index b7f9db25763efa16a976b261efd17e00fde0e08d..43c4f52e76f60b5ba414ef78c2bb3beb65708d36 100644
3 | --- a/dist/index.js
4 | +++ b/dist/index.js
5 | @@ -468,6 +468,12 @@ var GroqChatLanguageModel = class {
6 | textDelta: delta.content
7 | });
8 | }
9 | + if (delta.reasoning != null) {
10 | + controller.enqueue({
11 | + type: "reasoning",
12 | + textDelta: delta.reasoning
13 | + });
14 | + }
15 | if (delta.tool_calls != null) {
16 | for (const toolCallDelta of delta.tool_calls) {
17 | const index = toolCallDelta.index;
18 | @@ -610,6 +616,7 @@ var groqChatChunkSchema = import_zod2.z.union([
19 | delta: import_zod2.z.object({
20 | role: import_zod2.z.enum(["assistant"]).nullish(),
21 | content: import_zod2.z.string().nullish(),
22 | + reasoning: import_zod2.z.string().nullish(),
23 | tool_calls: import_zod2.z.array(
24 | import_zod2.z.object({
25 | index: import_zod2.z.number(),
26 | diff --git a/dist/index.mjs b/dist/index.mjs
27 | index a17ca758f75089f645e5a9e3a3492373bf68724e..0545f57fb07996ecae298c5ccfc0767780f5ee2a 100644
28 | --- a/dist/index.mjs
29 | +++ b/dist/index.mjs
30 | @@ -459,6 +459,12 @@ var GroqChatLanguageModel = class {
31 | textDelta: delta.content
32 | });
33 | }
34 | + if (delta.reasoning != null) {
35 | + controller.enqueue({
36 | + type: "reasoning",
37 | + textDelta: delta.reasoning
38 | + });
39 | + }
40 | if (delta.tool_calls != null) {
41 | for (const toolCallDelta of delta.tool_calls) {
42 | const index = toolCallDelta.index;
43 | @@ -601,6 +607,7 @@ var groqChatChunkSchema = z2.union([
44 | delta: z2.object({
45 | role: z2.enum(["assistant"]).nullish(),
46 | content: z2.string().nullish(),
47 | + reasoning: z2.string().nullish(),
48 | tool_calls: z2.array(
49 | z2.object({
50 | index: z2.number(),
51 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "apps/*"
3 |
--------------------------------------------------------------------------------
/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/prettier.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('prettier').Config} */
2 | module.exports = {
3 | endOfLine: "lf",
4 | semi: false,
5 | singleQuote: false,
6 | tabWidth: 2,
7 | trailingComma: "es5",
8 | importOrder: [
9 | "^(react/(.*)$)|^(react$)",
10 | "^(next/(.*)$)|^(next$)",
11 | "",
12 | "",
13 | "^types$",
14 | "^@/types/(.*)$",
15 | "^@/config/(.*)$",
16 | "^@/lib/(.*)$",
17 | "^@/hooks/(.*)$",
18 | "^@/components/ui/(.*)$",
19 | "^@/components/(.*)$",
20 | "^@/registry/(.*)$",
21 | "^@/styles/(.*)$",
22 | "^@/app/(.*)$",
23 | "",
24 | "^[./]",
25 | ],
26 | importOrderSeparation: false,
27 | importOrderSortSpecifiers: true,
28 | importOrderBuiltinModulesToTop: true,
29 | importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"],
30 | importOrderMergeDuplicateImports: true,
31 | importOrderCombineTypeAndValueImports: true,
32 | plugins: ["@ianvs/prettier-plugin-sort-imports"],
33 | }
34 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "composite": false,
6 | "declaration": true,
7 | "declarationMap": true,
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "inlineSources": false,
11 | "isolatedModules": true,
12 | "moduleResolution": "node",
13 | "noUnusedLocals": false,
14 | "noUnusedParameters": false,
15 | "preserveWatchOutput": true,
16 | "skipLibCheck": true,
17 | "strict": true
18 | },
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "globalEnv": ["NODE_ENV"],
4 | "pipeline": {
5 | "build": {
6 | "dependsOn": ["^build"],
7 | "env": [
8 | "NEXT_PUBLIC_APP_URL",
9 | "UPSTASH_REDIS_REST_URL",
10 | "UPSTASH_REDIS_REST_TOKEN",
11 | "COMPONENTS_REGISTRY_URL",
12 | "REGISTRY_URL",
13 | "npm_config_user_agent",
14 | "https_proxy",
15 | "V0_URL",
16 | "V0_EDIT_SECRET",
17 | "DEPLOYMENT_PROTECTION_BYPASS",
18 | "GROQ_API_KEY"
19 | ],
20 | "outputs": ["dist/**", ".next/**"]
21 | },
22 | "preview": {
23 | "env": ["NEXT_PUBLIC_APP_URL"],
24 | "outputs": ["dist/**", ".next/**"]
25 | },
26 | "start": {
27 | "dependsOn": ["^build"]
28 | },
29 | "lint": {
30 | "cache": false,
31 | "outputs": []
32 | },
33 | "lint:fix": {
34 | "cache": false,
35 | "outputs": []
36 | },
37 | "format:check": {
38 | "cache": false,
39 | "outputs": []
40 | },
41 | "format:write": {
42 | "cache": false,
43 | "outputs": []
44 | },
45 | "typecheck": {},
46 | "dev": {
47 | "cache": false
48 | },
49 | "check": {
50 | "cache": false
51 | },
52 | "test": {
53 | "cache": false,
54 | "outputs": []
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import tsconfigPaths from "vite-tsconfig-paths"
2 | import { configDefaults, defineConfig } from "vitest/config"
3 |
4 | export default defineConfig({
5 | test: {
6 | exclude: [
7 | ...configDefaults.exclude,
8 | "**/node_modules/**",
9 | "**/fixtures/**",
10 | ],
11 | },
12 | plugins: [tsconfigPaths()],
13 | })
14 |
--------------------------------------------------------------------------------