diff --git a/.changeset/moody-files-open.md b/.changeset/moody-files-open.md new file mode 100644 index 0000000000..9970c60656 --- /dev/null +++ b/.changeset/moody-files-open.md @@ -0,0 +1,5 @@ +--- +"shadcn": minor +--- + +add scroll-fade and shimmer utilities diff --git a/.changeset/plenty-cats-allow.md b/.changeset/plenty-cats-allow.md new file mode 100644 index 0000000000..91bf8c53cd --- /dev/null +++ b/.changeset/plenty-cats-allow.md @@ -0,0 +1,5 @@ +--- +"@shadcn/react": minor +--- + +initial release of @shadcn/react diff --git a/.github/collect-prerelease-info.js b/.github/collect-prerelease-info.js new file mode 100644 index 0000000000..1635213c45 --- /dev/null +++ b/.github/collect-prerelease-info.js @@ -0,0 +1,35 @@ +// Collect the packages that a snapshot prerelease just published, so the +// prerelease comment workflow can render an install line per package. +import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs" +import { join } from "node:path" + +const [, , prNumber, channel] = process.argv + +const packagesDir = join(process.cwd(), "packages") +const published = [] + +for (const dir of readdirSync(packagesDir)) { + const pkgPath = join(packagesDir, dir, "package.json") + if (!existsSync(pkgPath)) { + continue + } + + const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) + + // Snapshot versions are stamped `0.0.0--`, so the channel + // marker is how we tell which packages this run actually versioned. + if ( + !pkg.private && + typeof pkg.version === "string" && + pkg.version.includes(`-${channel}`) + ) { + published.push({ name: pkg.name, version: pkg.version }) + } +} + +writeFileSync( + "prerelease-info.json", + JSON.stringify({ pr: prNumber, channel, packages: published }, null, 2) +) + +console.log(`Collected ${published.length} prerelease package(s).`) diff --git a/.github/version-script-prerelease.js b/.github/version-script-prerelease.js deleted file mode 100644 index 3a2dff9b28..0000000000 --- a/.github/version-script-prerelease.js +++ /dev/null @@ -1,37 +0,0 @@ -import fs from "fs" - -const pkgJsonPath = "packages/shadcn/package.json" -const channel = process.argv[2] -const headSha = process.argv[3] - -if (!["beta", "rc"].includes(channel)) { - console.error( - `Expected prerelease channel to be "beta" or "rc", got "${channel}".` - ) - process.exit(1) -} - -if (!headSha) { - console.error("Expected pull request head SHA.") - process.exit(1) -} - -try { - const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8")) - const shortSha = headSha.trim().slice(0, 7) - const baseVersion = channel === "beta" ? "0.0.0" : pkg.version - - if (channel === "rc" && baseVersion.includes("-")) { - console.error( - `Expected a stable planned version for rc, got "${baseVersion}".` - ) - process.exit(1) - } - - pkg.version = `${baseVersion}-${channel}.${shortSha}` - fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, "\t") + "\n") - console.log(`Prepared shadcn@${pkg.version}`) -} catch (error) { - console.error(error) - process.exit(1) -} diff --git a/.github/workflows/browser-tests.yml b/.github/workflows/browser-tests.yml new file mode 100644 index 0000000000..bf576a1063 --- /dev/null +++ b/.github/workflows/browser-tests.yml @@ -0,0 +1,56 @@ +name: Browser tests + +on: + pull_request: + branches: ["*"] + paths: + - "packages/react/**" + - ".github/workflows/browser-tests.yml" + +jobs: + browser: + runs-on: ubuntu-latest + name: pnpm test:browser + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 20 + + - uses: pnpm/action-setup@v4 + name: Install pnpm + id: pnpm-install + with: + version: 10.33.4 + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + run: | + echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + - name: Install dependencies + run: pnpm install + + - name: Cache Playwright browsers + uses: actions/cache@v3 + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-playwright- + + - name: Install Playwright Chromium + run: pnpm --filter=@shadcn/react exec playwright install --with-deps chromium + + - run: pnpm --filter=@shadcn/react test:browser diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml index 10f522772c..7c502f2477 100644 --- a/.github/workflows/code-check.yml +++ b/.github/workflows/code-check.yml @@ -78,7 +78,7 @@ jobs: run: pnpm install - name: Build packages - run: pnpm --filter=shadcn build + run: pnpm build:packages - run: pnpm format:check @@ -117,6 +117,6 @@ jobs: run: pnpm install - name: Build packages - run: pnpm --filter=shadcn build + run: pnpm build:packages - run: pnpm typecheck diff --git a/.github/workflows/prerelease-comment.yml b/.github/workflows/prerelease-comment.yml index 701d1af8d0..b029d481b0 100644 --- a/.github/workflows/prerelease-comment.yml +++ b/.github/workflows/prerelease-comment.yml @@ -16,50 +16,64 @@ jobs: runs-on: ubuntu-latest name: Write comment to the PR steps: - - name: "Comment on PR" + # Stable pushes and no-changeset runs upload no artifact, so a missing + # download is expected — gate the rest of the job on it succeeding. + - name: Download prerelease info + id: download + continue-on-error: true + uses: actions/download-artifact@v4 + with: + name: prerelease-info + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build comment + id: info + if: steps.download.outcome == 'success' uses: actions/github-script@v7 with: - github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id, - }); + const fs = require("fs"); + const info = JSON.parse(fs.readFileSync("prerelease-info.json", "utf8")); - for (const artifact of allArtifacts.data.artifacts) { - // Extract the PR number and package version from the artifact name - const match = /^npm-package-shadcn@(.*?)-pr-(\d+)/.exec(artifact.name); - - if (match) { - const version = match[1]; - const channel = version.includes("-rc.") ? "rc" : "beta"; - require("fs").appendFileSync( - process.env.GITHUB_ENV, - `\nPRERELEASE_PACKAGE_VERSION=${version}` + - `\nPRERELEASE_CHANNEL=${channel}` + - `\nPRERELEASE_LABEL=release: ${channel}` + - `\nWORKFLOW_RUN_PR=${match[2]}` + - `\nWORKFLOW_RUN_ID=${context.payload.workflow_run.id}` - ); - break; - } + if (!info.packages || info.packages.length === 0) { + core.info("No prerelease packages to comment."); + return; } - - name: "Comment on PR with Link" + const installs = info.packages + .map((p) => `pnpm dlx ${p.name}@${p.version}`) + .join("\n"); + const links = info.packages + .map( + (p) => + `- [${p.name}@${p.version}](https://www.npmjs.com/package/${p.name}/v/${p.version})` + ) + .join("\n"); + + const body = [ + `A new ${info.channel} prerelease is available for testing:`, + "", + "```sh", + installs, + "```", + "", + links, + ].join("\n"); + + core.setOutput("pr", info.pr); + core.setOutput("channel", info.channel); + core.setOutput("body", body); + + - name: Comment on PR + if: steps.info.outputs.body uses: marocchino/sticky-pull-request-comment@v2 with: - number: ${{ env.WORKFLOW_RUN_PR }} - message: | - A new ${{ env.PRERELEASE_CHANNEL }} prerelease is available for testing: + number: ${{ steps.info.outputs.pr }} + message: ${{ steps.info.outputs.body }} - ```sh - pnpm dlx shadcn@${{ env.PRERELEASE_PACKAGE_VERSION }} - ``` - - View on npm: https://www.npmjs.com/package/shadcn/v/${{ env.PRERELEASE_PACKAGE_VERSION }} - - - name: "Remove the prerelease label once published" + - name: Remove the prerelease label once published + if: steps.info.outputs.pr uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -68,8 +82,8 @@ jobs: await github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: '${{ env.WORKFLOW_RUN_PR }}', - name: '${{ env.PRERELEASE_LABEL }}', + issue_number: Number("${{ steps.info.outputs.pr }}"), + name: `release: ${{ steps.info.outputs.channel }}`, }); } catch (error) { if (error.status !== 404) { diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf42298067..de2488d863 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,21 +46,8 @@ jobs: ); } - const selected = selectedLabels[0]; - const pullRequest = context.payload.pull_request; - - if ( - selected.channel === "rc" && - (pullRequest.head.ref !== "changeset-release/main" || - pullRequest.title !== "chore(release): version packages") - ) { - throw new Error( - "The release: rc label can only be used on the Changesets version PR from changeset-release/main." - ); - } - - core.setOutput("channel", selected.channel); - core.setOutput("label", selected.name); + core.setOutput("channel", selectedLabels[0].channel); + core.setOutput("label", selectedLabels[0].name); - name: Checkout Repo uses: actions/checkout@v4 @@ -86,37 +73,49 @@ jobs: - name: Install NPM Dependencies run: pnpm install - - name: Modify package.json version - run: node .github/version-script-prerelease.js ${{ steps.prerelease.outputs.channel }} ${{ github.event.pull_request.head.sha }} - - - name: get-npm-version - id: package-version - uses: martinbeentjes/npm-get-version-action@main - with: - path: packages/shadcn - - - name: Check package version on NPM - id: package-exists + # A snapshot prerelease needs changesets to compute versions. The + # Changesets version PR consumes them, so a label on that PR is a no-op. + - name: Check for changesets + id: changesets run: | - if npm view "shadcn@${{ steps.package-version.outputs.current-version }}" version >/dev/null 2>&1; then - echo "exists=true" >> "$GITHUB_OUTPUT" - else - echo "exists=false" >> "$GITHUB_OUTPUT" - fi + shopt -s nullglob + present=false + for file in .changeset/*.md; do + if [ "$(basename "$file")" != "README.md" ]; then + present=true + break + fi + done + echo "present=$present" >> "$GITHUB_OUTPUT" - - name: Publish Prerelease to NPM - if: ${{ steps.package-exists.outputs.exists == 'false' }} - run: pnpm pub:${{ steps.prerelease.outputs.channel }} + - name: No changesets to prerelease + if: steps.changesets.outputs.present == 'false' + run: echo "::notice::No changesets found on this branch; nothing to prerelease." - - name: Build packaged artifact - if: ${{ steps.package-exists.outputs.exists == 'true' }} - run: pnpm shadcn:build + # Snapshot versions are stamped per run (timestamped), so each publish is + # unique and can never collide with a real release on the latest tag. + - name: Version snapshot + if: steps.changesets.outputs.present == 'true' + run: pnpm exec changeset version --snapshot ${{ steps.prerelease.outputs.channel }} - - name: Upload packaged artifact + - name: Build packages + if: steps.changesets.outputs.present == 'true' + run: pnpm build:packages + + - name: Publish snapshot to NPM + if: steps.changesets.outputs.present == 'true' + run: pnpm exec changeset publish --tag ${{ steps.prerelease.outputs.channel }} --no-git-tag + + - name: Collect prerelease info + if: steps.changesets.outputs.present == 'true' + run: node .github/collect-prerelease-info.js "${{ github.event.number }}" "${{ steps.prerelease.outputs.channel }}" + + - name: Upload prerelease info + if: steps.changesets.outputs.present == 'true' uses: actions/upload-artifact@v4 with: - name: npm-package-shadcn@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name - path: packages/shadcn/dist/index.js + name: prerelease-info + path: prerelease-info.json release: if: ${{ github.event_name == 'push' && github.repository_owner == 'shadcn-ui' }} @@ -151,11 +150,10 @@ jobs: - name: Install NPM Dependencies run: pnpm install - # - name: Check for errors - # run: pnpm check - - - name: Build the package - run: pnpm shadcn:build + # Builds every publishable package under packages/* (shadcn, @shadcn/react), + # never apps/v4, so each dist is fresh before changeset publish. + - name: Build the packages + run: pnpm build:packages - name: Import GPG key uses: crazy-max/ghaction-import-gpg@v6 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 382aff4822..3fc475eb9a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,3 +46,39 @@ jobs: run: pnpm install - run: pnpm test + + react: + runs-on: ubuntu-latest + name: pnpm test (@shadcn/react) + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 22 + + - uses: pnpm/action-setup@v4 + name: Install pnpm + id: pnpm-install + with: + version: 10.33.4 + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + run: | + echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + - name: Install dependencies + run: pnpm install + + - run: pnpm --filter=@shadcn/react test diff --git a/.gitignore b/.gitignore index 8e9abde081..b74fbde78d 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,11 @@ tsconfig.tsbuildinfo .playwright-mcp .playwright-cli shadcn-workspace + +# vitest browser mode writes these only on test failure. +__screenshots__ .codex-artifacts +.tmp* + +CONTEXT.md +docs/adr diff --git a/.vscode/settings.json b/.vscode/settings.json index d72f5636fc..0cea827432 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,6 +15,7 @@ "search.exclude": { "apps/v4/registry/radix-*": true, "apps/v4/public/r/*": true, - "packages/shadcn/test/fixtures/*": true + "packages/shadcn/test/fixtures/*": true, + "apps/v4/styles/*": true } } diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000000..14947c9a89 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,59 @@ +# Releasing + +This monorepo publishes two packages independently with [Changesets](https://github.com/changesets/changesets): + +- **`shadcn`** — the CLI and tooling. +- **`@shadcn/react`** — headless React primitives. + +They version on their own lines. A change to one never bumps the other unless a changeset says so. + +## 1. Add a changeset + +Every change that should publish needs a changeset. Run: + +```sh +pnpm changeset +``` + +Select the affected package(s) and bump level. One PR can carry separate changesets for `shadcn` and `@shadcn/react` at different levels. A PR with no changeset publishes nothing. + +## 2. Stable release + +Stable releases are automated by `.github/workflows/release.yml` (the `release` job, on push to `main`): + +1. Merged changesets accumulate on `main`. +2. The Changesets action opens/updates a **"Version Packages"** PR that bumps versions and writes changelogs. +3. Merging that PR triggers `changeset publish`, which builds all packages (`pnpm build:packages`) and publishes any whose version is ahead of npm — each to the `latest` tag. + +`pnpm build:packages` (`turbo run build --filter=./packages/*`) builds `shadcn` and `@shadcn/react` but never `apps/v4`. + +## 3. Prereleases (per-PR snapshots) + +Add the **`release: beta`** or **`release: rc`** label to a PR. The `prerelease` job in `release.yml`: + +1. Verifies the branch has changesets (a label on the version PR is a no-op, since it consumed them). +2. Runs `changeset version --snapshot ` — stamps a unique `0.0.0--` on each changeset'd package. +3. Builds and runs `changeset publish --tag --no-git-tag`. +4. Uploads the published package list; `prerelease-comment.yml` posts a `pnpm dlx` install line per package and removes the label. + +The label selects the **dist-tag/channel**; the **changesets on the branch** select which packages publish. Snapshots are timestamped, so they never touch `latest` and never collide. + +```sh +# Install a snapshot from the PR comment, e.g.: +pnpm dlx @shadcn/react@0.0.0-beta-20260624120000 +``` + +## 4. Prerelease trains (sustained `-beta.N` / `-rc.N`) + +For a baking release line (e.g. `1.0.0-rc.0`, `-rc.1`, …) rather than throwaway snapshots, use Changesets pre mode: + +```sh +pnpm changeset pre enter rc # writes .changeset/pre.json +# ...normal changeset + Version PR cycle now produces -rc.N versions on the rc tag... +pnpm changeset pre exit # back to stable; next Version PR ships X.Y.Z on latest +``` + +## Notes + +- `pnpm-workspace.yaml` sets `minimumReleaseAge: 2880` (48h), so freshly published stable/beta versions take time to resolve in normal installs. Use `pnpm dlx @` to test immediately. +- Publishing uses npm OIDC/provenance (`id-token: write` + `npm@latest`); no `NPM_TOKEN` secret is needed. diff --git a/apps/v4/.gitignore b/apps/v4/.gitignore index db9ea2f256..a6d58dfea0 100644 --- a/apps/v4/.gitignore +++ b/apps/v4/.gitignore @@ -46,3 +46,4 @@ next-env.d.ts .contentlayer .content-collections .source +.devtools diff --git a/apps/v4/app/(app)/(root)/cards/contribution-history.tsx b/apps/v4/app/(app)/(root)/cards/contribution-history.tsx index 33a9eba8d3..63107debcf 100644 --- a/apps/v4/app/(app)/(root)/cards/contribution-history.tsx +++ b/apps/v4/app/(app)/(root)/cards/contribution-history.tsx @@ -17,7 +17,6 @@ const chartData = [ { month: "Feb", amount: 900 }, { month: "Mar", amount: 1300 }, { month: "Apr", amount: 750 }, - { month: "May", amount: 1400 }, ] export function ContributionHistory() { @@ -35,13 +34,14 @@ export function ContributionHistory() { role="img" aria-label="Last 6 months of contribution activity" > - {chartData.map((item) => ( + {chartData.map((item, index) => (
diff --git a/apps/v4/app/(app)/(root)/cards/index.tsx b/apps/v4/app/(app)/(root)/cards/index.tsx index 063f736a09..939f6207a6 100644 --- a/apps/v4/app/(app)/(root)/cards/index.tsx +++ b/apps/v4/app/(app)/(root)/cards/index.tsx @@ -1,3 +1,5 @@ +import { MessageScrollerDemo } from "@/examples/radix/message-scroller-demo" + import { AccountAccess } from "./account-access" import { AnalyticsCard } from "./analytics-card" import { ClaimableBalance } from "./claimable-balance" @@ -79,7 +81,7 @@ export function CardsDemo() { return (
@@ -93,17 +95,20 @@ export function CardsDemo() {
-
+
- +
+ +
+ {/* */}
-
+
diff --git a/apps/v4/app/(app)/(root)/cards/ui-elements.tsx b/apps/v4/app/(app)/(root)/cards/ui-elements.tsx index df6225eb05..a82a502072 100644 --- a/apps/v4/app/(app)/(root)/cards/ui-elements.tsx +++ b/apps/v4/app/(app)/(root)/cards/ui-elements.tsx @@ -117,7 +117,7 @@ export function UIElements() { Dialog - + Allow accessory to connect? diff --git a/apps/v4/app/(app)/create/components/create-devtools.tsx b/apps/v4/app/(app)/create/components/create-devtools.tsx new file mode 100644 index 0000000000..1cb68aa89b --- /dev/null +++ b/apps/v4/app/(app)/create/components/create-devtools.tsx @@ -0,0 +1,72 @@ +"use client" + +import * as React from "react" + +import { BASES } from "@/registry/config" +import { Button } from "@/registry/new-york-v4/ui/button" +import { getDocsPathForItem } from "@/app/(app)/create/lib/devtools" +import { + serializeDesignSystemSearchParams, + useDesignSystemSearchParams, +} from "@/app/(app)/create/lib/search-params" + +export function CreateDevtools() { + const [params, setParams] = useDesignSystemSearchParams() + + const previewUrl = React.useMemo( + () => + serializeDesignSystemSearchParams( + `/preview/${params.base}/${params.item}`, + params + ), + [params] + ) + + const docsUrl = React.useMemo( + () => getDocsPathForItem(params.base, params.item), + [params.base, params.item] + ) + + if (process.env.NODE_ENV === "production") { + return null + } + + return ( +
+ {BASES.map((base) => ( + + ))} +
+ + + ) +} diff --git a/apps/v4/app/(app)/create/components/customizer.tsx b/apps/v4/app/(app)/create/components/customizer.tsx index 1030bd0468..f6749e4c60 100644 --- a/apps/v4/app/(app)/create/components/customizer.tsx +++ b/apps/v4/app/(app)/create/components/customizer.tsx @@ -6,6 +6,7 @@ import { type RegistryItem } from "shadcn/schema" import { useIsMobile } from "@/hooks/use-mobile" import { getThemesForBaseColor, STYLES } from "@/registry/config" +import { Button } from "@/styles/base-nova/ui/button" import { Card, CardContent, @@ -32,11 +33,22 @@ import { ThemePicker } from "@/app/(app)/create/components/theme-picker" import { FONT_HEADING_OPTIONS, FONTS } from "@/app/(app)/create/lib/fonts" import { useDesignSystemSearchParams } from "@/app/(app)/create/lib/search-params" -// Only visible when user clicks "Create Project". -const ProjectForm = dynamic(() => - import("@/app/(app)/create/components/project-form").then( - (m) => m.ProjectForm - ) +// Only visible when user clicks "Create Project". Rendered client-only to +// avoid a useId hydration mismatch on the Base UI dialog trigger. The loading +// placeholder mirrors the trigger button exactly so there is no layout shift. +const ProjectForm = dynamic( + () => + import("@/app/(app)/create/components/project-form").then( + (m) => m.ProjectForm + ), + { + ssr: false, + loading: () => ( + + ), + } ) export function Customizer({ diff --git a/apps/v4/app/(app)/create/components/preview.tsx b/apps/v4/app/(app)/create/components/preview.tsx index f2ad78c611..073bd7b5f0 100644 --- a/apps/v4/app/(app)/create/components/preview.tsx +++ b/apps/v4/app/(app)/create/components/preview.tsx @@ -3,6 +3,7 @@ import * as React from "react" import { CMD_K_FORWARD_TYPE } from "@/app/(app)/create/components/action-menu" +import { CreateDevtools } from "@/app/(app)/create/components/create-devtools" import { REDO_FORWARD_TYPE, UNDO_FORWARD_TYPE, @@ -160,6 +161,7 @@ export function Preview() { title="Preview" />
+
) diff --git a/apps/v4/app/(app)/create/lib/devtools.ts b/apps/v4/app/(app)/create/lib/devtools.ts new file mode 100644 index 0000000000..be85bd15a5 --- /dev/null +++ b/apps/v4/app/(app)/create/lib/devtools.ts @@ -0,0 +1,10 @@ +import { type BaseName } from "@/registry/config" + +export function getDocsPathForItem(base: BaseName, item: string) { + if (item.endsWith("-example")) { + const component = item.slice(0, -"-example".length) + return `/docs/components/${base}/${component}` + } + + return "/docs/components" +} diff --git a/apps/v4/app/(app)/docs/[[...slug]]/page.tsx b/apps/v4/app/(app)/docs/[[...slug]]/page.tsx index 8c8165aa7b..bc17d0e3f7 100644 --- a/apps/v4/app/(app)/docs/[[...slug]]/page.tsx +++ b/apps/v4/app/(app)/docs/[[...slug]]/page.tsx @@ -185,7 +185,7 @@ export default async function Page(props: {
{doc.toc?.length ? ( -
+
) : null} diff --git a/apps/v4/app/(create)/preview/[base]/[name]/page.tsx b/apps/v4/app/(create)/preview/[base]/[name]/page.tsx index e5a795ff59..716f80df9a 100644 --- a/apps/v4/app/(create)/preview/[base]/[name]/page.tsx +++ b/apps/v4/app/(create)/preview/[base]/[name]/page.tsx @@ -16,6 +16,7 @@ import { RandomizeScript } from "@/app/(app)/create/components/random-button" import { getBaseComponent, getBaseItem } from "@/app/(app)/create/lib/api" import "@/app/style-registry.css" +import "streamdown/styles.css" export const revalidate = false export const dynamic = "force-static" diff --git a/apps/v4/app/globals.css b/apps/v4/app/globals.css index 980953ca96..78f40c70b1 100644 --- a/apps/v4/app/globals.css +++ b/apps/v4/app/globals.css @@ -3,6 +3,8 @@ @import "shadcn/tailwind.css"; @import "./legacy-themes.css"; +@source "../node_modules/streamdown/dist/*.js"; + @custom-variant style-vega (&:where(.style-vega *)); @custom-variant style-nova (&:where(.style-nova *)); @custom-variant style-lyra (&:where(.style-lyra *)); diff --git a/apps/v4/app/legacy-themes.css b/apps/v4/app/legacy-themes.css index a6596bf178..4e450a7911 100644 --- a/apps/v4/app/legacy-themes.css +++ b/apps/v4/app/legacy-themes.css @@ -563,3 +563,74 @@ --sidebar-ring: oklch(0.556 0 0); } } + +.theme-blue { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.488 0.243 264.376); + --primary-foreground: oklch(0.97 0.014 254.604); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.809 0.105 251.813); + --chart-2: oklch(0.623 0.214 259.815); + --chart-3: oklch(0.546 0.245 262.881); + --chart-4: oklch(0.488 0.243 264.376); + --chart-5: oklch(0.424 0.199 265.638); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.546 0.245 262.881); + --sidebar-primary-foreground: oklch(0.97 0.014 254.604); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); + --selection: oklch(0.93 0.03 256); + --selection-foreground: oklch(0.145 0 0); + + @variant dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.424 0.199 265.638); + --primary-foreground: oklch(0.97 0.014 254.604); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.809 0.105 251.813); + --chart-2: oklch(0.623 0.214 259.815); + --chart-3: oklch(0.546 0.245 262.881); + --chart-4: oklch(0.488 0.243 264.376); + --chart-5: oklch(0.424 0.199 265.638); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.623 0.214 259.815); + --sidebar-primary-foreground: oklch(0.97 0.014 254.604); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); + } +} diff --git a/apps/v4/components/announcement.tsx b/apps/v4/components/announcement.tsx index dec4ed7efb..de967c4754 100644 --- a/apps/v4/components/announcement.tsx +++ b/apps/v4/components/announcement.tsx @@ -6,8 +6,8 @@ import { Badge } from "@/registry/new-york-v4/ui/badge" export function Announcement() { return ( - - Introducing GitHub Registries + + Components for Chat Interfaces ) diff --git a/apps/v4/components/components-list.tsx b/apps/v4/components/components-list.tsx index 9f81236f08..18a196be7b 100644 --- a/apps/v4/components/components-list.tsx +++ b/apps/v4/components/components-list.tsx @@ -1,33 +1,65 @@ import Link from "next/link" import { PAGES_NEW } from "@/lib/docs" -import { getPagesFromFolder, type PageTreeFolder } from "@/lib/page-tree" +import { + getPagesFromFolder, + type PageTreeFolder, + type PageTreePage, +} from "@/lib/page-tree" + +function ComponentLink({ + component, + showNewIndicator, +}: { + component: PageTreePage + showNewIndicator: boolean +}) { + const isNew = showNewIndicator && PAGES_NEW.includes(component.url) + + return ( + + {component.name} + {isNew && ( + <> + New +