├── .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 | 28 | 37 | 38 | 39 | 40 |
41 | 42 | 57 | 58 |
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 | 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 | 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 | 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 |
16 | {children} 17 |
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 | 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 \n \n \n
\n
\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 | 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 | 25 | 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 | 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 | 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 | 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 | 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 | --------------------------------------------------------------------------------