Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
103b8c1f91 | ||
|
|
c93c0f0a98 | ||
|
|
d4c5b88519 | ||
|
|
98632915fe | ||
|
|
7f19d72d28 | ||
|
|
1fcf9dce94 | ||
|
|
2515b596b7 | ||
|
|
2ad4de8410 | ||
|
|
88863caa79 | ||
|
|
86c312b7d4 | ||
|
|
becf552ac5 | ||
|
|
6894aac69d | ||
|
|
14219cf9b5 | ||
|
|
54447b5025 | ||
|
|
aca5c3cd41 | ||
|
|
1a9f041acc | ||
|
|
793cf731ab | ||
|
|
45df2ea8b9 | ||
|
|
1a6cc7888b | ||
|
|
179ae0de2e | ||
|
|
51c4d12dce | ||
|
|
6fe7078b06 | ||
|
|
3cdad07e32 | ||
|
|
8a492a2d18 | ||
|
|
fb6da37f9f | ||
|
|
41d618d1d8 | ||
|
|
4cb8cac44c | ||
|
|
5aee8715d7 | ||
|
|
aa7f45b29d | ||
|
|
65d32dda4c | ||
|
|
9ba70a4e2c | ||
|
|
5850560681 | ||
|
|
189b34971f | ||
|
|
e010864f26 | ||
|
|
e74efbab46 | ||
|
|
4884736c69 | ||
|
|
bb48d51d70 | ||
|
|
3d88cc75eb | ||
|
|
bc8945ff4e | ||
|
|
9627ebbbbc | ||
|
|
80147c328f | ||
|
|
29b3652711 | ||
|
|
891818443a | ||
|
|
532d8e692c | ||
|
|
e45833b5f2 | ||
|
|
dffa6cef34 | ||
|
|
04407e9cb5 | ||
|
|
9f0e37d8c3 | ||
|
|
bf18ef7a35 | ||
|
|
edda5a41b8 | ||
|
|
320885b073 | ||
|
|
d97b670580 | ||
|
|
b8ecd6c408 | ||
|
|
4086d54305 | ||
|
|
6831117b97 | ||
|
|
6ea1042d44 | ||
|
|
d9f090bb53 | ||
|
|
e1184390dc | ||
|
|
a42e158a07 | ||
|
|
6347fe0aeb | ||
|
|
f08abfe55a | ||
|
|
fbe58782d9 | ||
|
|
16ffaea878 | ||
|
|
a4f1a1c16f | ||
|
|
8ed0e27d73 | ||
|
|
9958dde439 | ||
|
|
e64e9cdbdc | ||
|
|
095773b2da | ||
|
|
926985e5ad | ||
|
|
24ace5354a | ||
|
|
412ee93a25 | ||
|
|
751111a0d7 | ||
|
|
8dab1688f7 | ||
|
|
7887485e95 | ||
|
|
3e1283365a | ||
|
|
8919c32d0f | ||
|
|
be95271bc4 | ||
|
|
f2752141ff | ||
|
|
b6311022f3 | ||
|
|
07f1182d52 | ||
|
|
bacd545b67 | ||
|
|
d95693b61a | ||
|
|
dce79e1309 |
@@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" role="img" aria-label="ui">
|
||||
<rect width="1280" height="640" fill="#0A0A0A"/>
|
||||
<svg x="96" y="215" width="210" height="210" viewBox="0 0 67 67"><path d="M22.21 67V44.6369H0V67H22.21Z" fill="#fff"/><path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#fff"/><path d="M22.21 0H0V22.3184H22.21V0Z" fill="#fff"/><path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#fff"/><path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#fff"/></svg>
|
||||
<text x="378" y="276" font-family="Inter,system-ui,-apple-system,sans-serif" font-size="78" font-weight="800" letter-spacing="-2" fill="#ffffff">ui</text>
|
||||
<text x="378" y="322" font-family="Inter,system-ui,sans-serif" font-size="30" fill="#ffffff" opacity=".66">React component library for AI applications</text>
|
||||
<rect x="378" y="338" width="806" height="3" rx="1.5" fill="#ffffff" opacity=".9"/>
|
||||
<text x="378" y="390" font-family="Inter,system-ui,sans-serif" font-size="24" font-weight="600" fill="#ffffff" opacity=".5">github.com/hanzoai</text>
|
||||
<text x="1184" y="390" text-anchor="end" font-family="Inter,system-ui,sans-serif" font-size="24" font-weight="600" fill="#ffffff" opacity=".5">hanzo.ai</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -4,7 +4,7 @@
|
||||
import { exec } from "child_process"
|
||||
import fs from "fs"
|
||||
|
||||
const pkgJsonPath = "pkgs/shadcn/package.json"
|
||||
const pkgJsonPath = "packages/shadcn/package.json"
|
||||
try {
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
|
||||
exec("git rev-parse --short HEAD", (err, stdout) => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { exec } from "child_process"
|
||||
import fs from "fs"
|
||||
|
||||
const pkgJsonPath = "pkgs/shadcn/package.json"
|
||||
const pkgJsonPath = "packages/shadcn/package.json"
|
||||
try {
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath))
|
||||
exec("git rev-parse --short HEAD", (err, stdout) => {
|
||||
|
||||
+17
-17
@@ -18,7 +18,7 @@ concurrency:
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -47,14 +47,14 @@ jobs:
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run linting
|
||||
run: pnpm run lint
|
||||
|
||||
typecheck:
|
||||
name: Type Check
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -83,19 +83,19 @@ jobs:
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build UI package
|
||||
run: cd pkgs/ui && pnpm run build
|
||||
run: cd pkg/ui && pnpm run build
|
||||
|
||||
- name: Run type checking
|
||||
run: |
|
||||
cd app && pnpm run typecheck
|
||||
cd ../pkgs/ui && pnpm run tc --noEmit
|
||||
cd ../pkg/ui && pnpm run tc --noEmit
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -124,10 +124,10 @@ jobs:
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build UI package
|
||||
run: cd pkgs/ui && pnpm run build:full
|
||||
run: cd pkg/ui && pnpm run build:full
|
||||
|
||||
- name: Build app
|
||||
run: cd app && pnpm run build
|
||||
@@ -140,11 +140,11 @@ jobs:
|
||||
name: build-artifacts
|
||||
path: |
|
||||
app/.next
|
||||
pkgs/ui/dist
|
||||
pkg/ui/dist
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -173,10 +173,10 @@ jobs:
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run pkgs/ui tests
|
||||
run: cd pkgs/ui && pnpm test -- --run
|
||||
- name: Run pkg/ui tests
|
||||
run: cd pkg/ui && pnpm test -- --run
|
||||
|
||||
- name: Run app tests
|
||||
run: |
|
||||
@@ -190,12 +190,12 @@ jobs:
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./pkgs/ui/coverage/lcov.info
|
||||
files: ./pkg/ui/coverage/lcov.info
|
||||
flags: unit
|
||||
|
||||
deploy-preview:
|
||||
name: Deploy Preview
|
||||
runs-on: hanzo-deploy-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, typecheck, build]
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
@@ -244,7 +244,7 @@ jobs:
|
||||
|
||||
status:
|
||||
name: CI Status
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, typecheck, build, test]
|
||||
if: always()
|
||||
steps:
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
name: pnpm lint
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
- run: pnpm lint
|
||||
|
||||
format:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
name: pnpm format:check
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
- run: pnpm format:check
|
||||
|
||||
tsc:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
name: pnpm typecheck
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
jobs:
|
||||
coverage:
|
||||
name: Test Coverage
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -26,15 +26,15 @@ jobs:
|
||||
version: 9
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: cd pkgs/ui && pnpm test:coverage
|
||||
run: cd pkg/ui && pnpm test:coverage
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./pkgs/ui/coverage/lcov.info
|
||||
files: ./pkg/ui/coverage/lcov.info
|
||||
flags: unit
|
||||
name: hanzo-ui-coverage
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const coverage = JSON.parse(fs.readFileSync('./pkgs/ui/coverage/coverage-summary.json', 'utf8'));
|
||||
const coverage = JSON.parse(fs.readFileSync('./pkg/ui/coverage/coverage-summary.json', 'utf8'));
|
||||
const total = coverage.total;
|
||||
const comment = `## Test Coverage Report\n\n` +
|
||||
`Lines: ${total.lines.pct}%\n` +
|
||||
|
||||
@@ -22,7 +22,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -53,11 +53,11 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pnpm install --no-frozen-lockfile
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build @hanzo/ui package
|
||||
run: |
|
||||
cd pkgs/ui && pnpm build
|
||||
cd pkg/ui && pnpm build
|
||||
|
||||
- name: Capture screenshots (optional)
|
||||
if: github.event.inputs.capture_screenshots == 'true'
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: hanzo-deploy-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
|
||||
@@ -11,7 +11,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
deprecated:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'shadcn-ui'
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
|
||||
@@ -31,7 +31,7 @@ on:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -43,6 +43,7 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -51,31 +52,31 @@ jobs:
|
||||
- name: Build packages
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.package }}" = "all" ] || [ "${{ github.event.inputs.package }}" = "ui" ]; then
|
||||
cd pkgs/ui && pnpm build
|
||||
cd pkg/ui && pnpm build
|
||||
cd ../..
|
||||
fi
|
||||
if [ "${{ github.event.inputs.package }}" = "all" ] || [ "${{ github.event.inputs.package }}" = "ui-mcp" ]; then
|
||||
cd pkgs/ui-mcp && pnpm build
|
||||
cd pkg/ui-mcp && pnpm build
|
||||
cd ../..
|
||||
fi
|
||||
if [ "${{ github.event.inputs.package }}" = "all" ] || [ "${{ github.event.inputs.package }}" = "auth" ]; then
|
||||
cd pkgs/auth && pnpm build
|
||||
cd pkg/auth && pnpm build
|
||||
cd ../..
|
||||
fi
|
||||
if [ "${{ github.event.inputs.package }}" = "all" ] || [ "${{ github.event.inputs.package }}" = "commerce" ]; then
|
||||
cd pkgs/commerce && pnpm build
|
||||
cd pkg/commerce && pnpm build
|
||||
cd ../..
|
||||
fi
|
||||
if [ "${{ github.event.inputs.package }}" = "all" ] || [ "${{ github.event.inputs.package }}" = "checkout" ]; then
|
||||
cd pkgs/checkout && pnpm build
|
||||
cd pkg/checkout && pnpm build
|
||||
cd ../..
|
||||
fi
|
||||
if [ "${{ github.event.inputs.package }}" = "all" ] || [ "${{ github.event.inputs.package }}" = "brand" ]; then
|
||||
cd pkgs/brand && pnpm build
|
||||
cd pkg/brand && pnpm build
|
||||
cd ../..
|
||||
fi
|
||||
if [ "${{ github.event.inputs.package }}" = "all" ] || [ "${{ github.event.inputs.package }}" = "react" ]; then
|
||||
cd pkgs/react && pnpm build
|
||||
cd pkg/react && pnpm build
|
||||
cd ../..
|
||||
fi
|
||||
|
||||
@@ -141,34 +142,34 @@ jobs:
|
||||
|
||||
case "${{ github.event.inputs.package }}" in
|
||||
ui)
|
||||
publish_package "pkgs/ui"
|
||||
publish_package "pkg/ui"
|
||||
;;
|
||||
ui-mcp)
|
||||
publish_package "pkgs/ui-mcp"
|
||||
publish_package "pkg/ui-mcp"
|
||||
;;
|
||||
auth)
|
||||
publish_package "pkgs/auth"
|
||||
publish_package "pkg/auth"
|
||||
;;
|
||||
commerce)
|
||||
publish_package "pkgs/commerce"
|
||||
publish_package "pkg/commerce"
|
||||
;;
|
||||
checkout)
|
||||
publish_package "pkgs/checkout"
|
||||
publish_package "pkg/checkout"
|
||||
;;
|
||||
brand)
|
||||
publish_package "pkgs/brand"
|
||||
publish_package "pkg/brand"
|
||||
;;
|
||||
react)
|
||||
publish_package "pkgs/react"
|
||||
publish_package "pkg/react"
|
||||
;;
|
||||
all)
|
||||
publish_package "pkgs/ui"
|
||||
publish_package "pkgs/ui-mcp"
|
||||
publish_package "pkgs/auth"
|
||||
publish_package "pkgs/commerce"
|
||||
publish_package "pkgs/checkout"
|
||||
publish_package "pkgs/brand"
|
||||
publish_package "pkgs/react"
|
||||
publish_package "pkg/ui"
|
||||
publish_package "pkg/ui-mcp"
|
||||
publish_package "pkg/auth"
|
||||
publish_package "pkg/commerce"
|
||||
publish_package "pkg/checkout"
|
||||
publish_package "pkg/brand"
|
||||
publish_package "pkg/react"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
if: |
|
||||
github.repository_owner == 'shadcn-ui' &&
|
||||
${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
name: Write comment to the PR
|
||||
steps:
|
||||
- name: "Comment on PR"
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
github.repository_owner == 'shadcn-ui' &&
|
||||
contains(github.event.pull_request.labels.*.name, '🚀 autorelease')
|
||||
name: Build & Publish a beta release to NPM
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
environment: Preview
|
||||
|
||||
steps:
|
||||
@@ -37,6 +37,7 @@ jobs:
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Update npm for OIDC support
|
||||
run: npm install -g npm@latest
|
||||
@@ -54,10 +55,10 @@ jobs:
|
||||
id: package-version
|
||||
uses: martinbeentjes/npm-get-version-action@main
|
||||
with:
|
||||
path: pkgs/shadcn
|
||||
path: packages/shadcn
|
||||
|
||||
- name: Upload packaged artifact
|
||||
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: pkgs/shadcn/dist/index.js
|
||||
path: packages/shadcn/dist/index.js
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
test:
|
||||
name: Run Tests
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -20,27 +20,29 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build packages
|
||||
run: |
|
||||
cd pkgs/ui && pnpm build && cd ../..
|
||||
cd pkgs/commerce && pnpm build && cd ../..
|
||||
cd pkgs/brand && pnpm build && cd ../..
|
||||
cd pkgs/react && pnpm build && cd ../..
|
||||
cd pkg/ui && pnpm build && cd ../..
|
||||
cd pkg/auth && pnpm build && cd ../..
|
||||
cd pkg/commerce && pnpm build && cd ../..
|
||||
cd pkg/brand && pnpm build && cd ../..
|
||||
cd pkg/react && pnpm build && cd ../..
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd pkgs/ui && pnpm test
|
||||
cd pkg/ui && pnpm test
|
||||
cd ../react && pnpm test
|
||||
cd ../..
|
||||
|
||||
publish:
|
||||
name: Publish to NPM
|
||||
needs: test
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
@@ -55,28 +57,30 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build all packages
|
||||
run: |
|
||||
cd pkgs/ui && pnpm build && cd ../..
|
||||
cd pkgs/commerce && pnpm build && cd ../..
|
||||
cd pkgs/brand && pnpm build && cd ../..
|
||||
cd pkgs/react && pnpm build && cd ../..
|
||||
cd pkg/ui && pnpm build && cd ../..
|
||||
cd pkg/auth && pnpm build && cd ../..
|
||||
cd pkg/commerce && pnpm build && cd ../..
|
||||
cd pkg/brand && pnpm build && cd ../..
|
||||
cd pkg/react && pnpm build && cd ../..
|
||||
|
||||
- name: Configure npm authentication
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
|
||||
run: |
|
||||
npm config set //registry.npmjs.org/:_authToken $NODE_AUTH_TOKEN
|
||||
npm whoami
|
||||
|
||||
- name: Check and publish packages
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
|
||||
run: |
|
||||
echo "Checking all packages for unpublished versions..."
|
||||
|
||||
@@ -84,8 +88,8 @@ jobs:
|
||||
SKIPPED_COUNT=0
|
||||
PUBLISHED_PACKAGES=""
|
||||
|
||||
for package in ui commerce brand react; do
|
||||
cd "pkgs/$package"
|
||||
for package in ui auth commerce brand react; do
|
||||
cd "pkg/$package"
|
||||
|
||||
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
||||
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
||||
@@ -142,7 +146,7 @@ jobs:
|
||||
|
||||
```bash
|
||||
# Install latest versions
|
||||
npm install @hanzo/ui @hanzo/commerce @hanzo/brand @hanzo/react
|
||||
npm install @hanzo/ui @hanzo/auth @hanzo/commerce @hanzo/brand @hanzo/react
|
||||
```
|
||||
files: |
|
||||
CHANGELOG.md
|
||||
|
||||
@@ -4,12 +4,11 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'pkgs/*/package.json'
|
||||
- 'pkgs/*/package.json'
|
||||
- 'pkg/*/package.json'
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
packages: ${{ steps.changed.outputs.packages }}
|
||||
steps:
|
||||
@@ -20,47 +19,37 @@ jobs:
|
||||
- name: Detect changed packages
|
||||
id: changed
|
||||
run: |
|
||||
CHANGED=()
|
||||
for pkg_json in pkgs/*/package.json pkgs/*/package.json; do
|
||||
[ ! -f "$pkg_json" ] && continue
|
||||
CHANGED_PACKAGES=()
|
||||
for pkg_json in pkg/*/package.json; do
|
||||
pkg_dir=$(dirname "$pkg_json")
|
||||
pkg_name=$(basename "$pkg_dir")
|
||||
|
||||
# Skip private packages
|
||||
IS_PRIVATE=$(jq -r '.private // false' "$pkg_json")
|
||||
[ "$IS_PRIVATE" = "true" ] && continue
|
||||
|
||||
# Only publish @hanzo/* packages
|
||||
PKG_NAME=$(jq -r '.name' "$pkg_json")
|
||||
case "$PKG_NAME" in
|
||||
@hanzo/*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
|
||||
# Check version changed
|
||||
# Check if package.json version changed
|
||||
if git diff HEAD~1 HEAD --quiet "$pkg_json" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
|
||||
OLD=$(git show HEAD~1:"$pkg_json" 2>/dev/null | jq -r '.version' || echo "0.0.0")
|
||||
NEW=$(jq -r '.version' "$pkg_json")
|
||||
# Get old and new versions
|
||||
OLD_VERSION=$(git show HEAD~1:"$pkg_json" 2>/dev/null | jq -r '.version' || echo "0.0.0")
|
||||
NEW_VERSION=$(jq -r '.version' "$pkg_json")
|
||||
|
||||
if [ "$OLD" != "$NEW" ]; then
|
||||
echo "Version change: $PKG_NAME ($OLD -> $NEW)"
|
||||
CHANGED+=("$(basename "$pkg_dir")")
|
||||
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
|
||||
echo "Version change detected: $pkg_name ($OLD_VERSION -> $NEW_VERSION)"
|
||||
CHANGED_PACKAGES+=("$pkg_name")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#CHANGED[@]} -eq 0 ]; then
|
||||
if [ ${#CHANGED_PACKAGES[@]} -eq 0 ]; then
|
||||
echo "packages=[]" >> $GITHUB_OUTPUT
|
||||
else
|
||||
JSON=$(printf '%s\n' "${CHANGED[@]}" | jq -R . | jq -s -c .)
|
||||
JSON=$(printf '%s\n' "${CHANGED_PACKAGES[@]}" | jq -R . | jq -s -c .)
|
||||
echo "packages=$JSON" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
publish:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.packages != '[]'
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package: ${{ fromJson(needs.detect-changes.outputs.packages) }}
|
||||
@@ -73,6 +62,7 @@ jobs:
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
release:
|
||||
if: ${{ github.repository_owner == 'shadcn-ui' }}
|
||||
name: Create a PR for release workflow
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -33,6 +33,7 @@ jobs:
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Update npm for OIDC support
|
||||
run: npm install -g npm@latest
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
name: Sync Forks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
fork:
|
||||
description: 'Fork to sync (luxfi or zoo or all)'
|
||||
required: false
|
||||
default: 'all'
|
||||
|
||||
jobs:
|
||||
sync-luxfi:
|
||||
if: github.event.inputs.fork == 'luxfi' || github.event.inputs.fork == 'all' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Hanzo UI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Git
|
||||
run: |
|
||||
git config --global user.name "Hanzo Bot"
|
||||
git config --global user.email "bot@hanzo.ai"
|
||||
|
||||
- name: Clone Luxfi Fork
|
||||
run: |
|
||||
git clone https://github.com/luxfi/ui.git luxfi-ui
|
||||
cd luxfi-ui
|
||||
git remote add upstream https://github.com/hanzoai/ui.git
|
||||
git fetch upstream
|
||||
|
||||
- name: Sync and Rebrand
|
||||
run: |
|
||||
cd luxfi-ui
|
||||
# Merge upstream changes
|
||||
git checkout main
|
||||
git merge upstream/main --no-edit || true
|
||||
|
||||
# Run rebrand script
|
||||
cp ../scripts/rebrand.sh ./
|
||||
chmod +x rebrand.sh
|
||||
./rebrand.sh luxfi
|
||||
|
||||
# Commit changes
|
||||
git add -A
|
||||
git commit -m "sync: Update from hanzoai/ui and rebrand for Luxfi" || true
|
||||
|
||||
- name: Push to Luxfi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.FORK_SYNC_TOKEN }}
|
||||
run: |
|
||||
cd luxfi-ui
|
||||
git push https://$GITHUB_TOKEN@github.com/luxfi/ui.git main || true
|
||||
|
||||
sync-zoo:
|
||||
if: github.event.inputs.fork == 'zoo' || github.event.inputs.fork == 'all' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Hanzo UI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Git
|
||||
run: |
|
||||
git config --global user.name "Hanzo Bot"
|
||||
git config --global user.email "bot@hanzo.ai"
|
||||
|
||||
- name: Clone Zoo Fork
|
||||
run: |
|
||||
git clone https://github.com/zooai/ui.git zoo-ui
|
||||
cd zoo-ui
|
||||
git remote add upstream https://github.com/hanzoai/ui.git
|
||||
git fetch upstream
|
||||
|
||||
- name: Sync and Rebrand
|
||||
run: |
|
||||
cd zoo-ui
|
||||
# Merge upstream changes
|
||||
git checkout main
|
||||
git merge upstream/main --no-edit || true
|
||||
|
||||
# Run rebrand script
|
||||
cp ../scripts/rebrand.sh ./
|
||||
chmod +x rebrand.sh
|
||||
./rebrand.sh zoo
|
||||
|
||||
# Commit changes
|
||||
git add -A
|
||||
git commit -m "sync: Update from hanzoai/ui and rebrand for Zoo" || true
|
||||
|
||||
- name: Push to Zoo
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.FORK_SYNC_TOKEN }}
|
||||
run: |
|
||||
cd zoo-ui
|
||||
git push https://$GITHUB_TOKEN@github.com/zooai/ui.git main || true
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
name: pnpm test
|
||||
env:
|
||||
NEXT_PUBLIC_APP_URL: http://localhost:4000
|
||||
|
||||
@@ -15,7 +15,7 @@ on:
|
||||
jobs:
|
||||
check-registry-sync:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
name: check-registry-sync
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
exit 1
|
||||
|
||||
validate:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
runs-on: ubuntu-latest
|
||||
name: pnpm validate:registries
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -5,7 +5,6 @@ node_modules
|
||||
**/node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
/pnpm-lock.yaml
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
Vendored
+3
-3
@@ -4,8 +4,8 @@
|
||||
"typescript.preferences.quoteStyle": "single",
|
||||
"eslint.workingDirectories": [
|
||||
{ "pattern": "apps/*/" },
|
||||
{ "pattern": "pkgs/*/" },
|
||||
{ "pattern": "pkgs/*/" }
|
||||
{ "pattern": "packages/*/" },
|
||||
{ "pattern": "pkg/*/" }
|
||||
],
|
||||
"tailwindCSS.classFunctions": ["cva", "cn"],
|
||||
"vitest.debugExclude": [
|
||||
@@ -19,6 +19,6 @@
|
||||
"search.exclude": {
|
||||
"apps/v4/registry/radix-*": true,
|
||||
"apps/v4/public/r/*": true,
|
||||
"pkgs/shadcn/test/fixtures/*": true
|
||||
"packages/shadcn/test/fixtures/*": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
# Hanzo UI Consolidation — one library, all polish, no duplication
|
||||
|
||||
**Goal.** RIP the fragmentation (`@hanzo/data` vs `@hanzo/ui` vs in-console duplicates) into ONE canonical, cross-platform, presentational, clean-room library: **`@hanzo/ui`, built on `@hanzo/gui`**. Preserve every polished component + token; zero loss; no fork.
|
||||
|
||||
**Status.**
|
||||
- **Step 1 — Audit:** complete (this document; four source trees read file-by-file).
|
||||
- **Step 2 — Establish `@hanzo/ui` + migrate the stable foundation:** **DONE & GREEN** (`pkg/ui` is now a real package; `tsc --noEmit` = 0 errors, `vitest` = 12/12).
|
||||
- **Step 3 — Console re-point:** **STAGED, not merged.** Gate is split (backend live, app lanes in-flight) — see [Step 3](#step-3--console-re-point-staged).
|
||||
|
||||
**One decision needs CTO sign-off before publish** — see [Naming / version](#naming--version--the-one-decision).
|
||||
|
||||
---
|
||||
|
||||
## Architecture — three layers, decomplected
|
||||
|
||||
```
|
||||
@hanzo/gui (Tamagui/One) cross-platform primitives: Text, XStack/YStack, Button, Input,
|
||||
│ Card, Popover, Select, Switch, Slider, Spinner, ScrollView …
|
||||
▼
|
||||
@hanzo/data (pkg/data) the metadata-driven RECORD layer: fields → records → views
|
||||
│ (RecordsView, DataTable, BoardView, RecordDetail, field editors)
|
||||
▼
|
||||
@hanzo/ui (pkg/ui) ◄──── THE ONE LIBRARY. Two orthogonal concerns, one package:
|
||||
• product/app layer → import { … } from '@hanzo/ui'
|
||||
• record layer → import { … } from '@hanzo/ui/data' (re-exports @hanzo/data)
|
||||
```
|
||||
|
||||
`@hanzo/data` stays the **source of truth** for the record layer; `@hanzo/ui` **composes** it (`export * from '@hanzo/data'` on the `./data` subpath) rather than copying — so the CMS/ERP/Help app lanes that consume `@hanzo/data` today keep working untouched, and there is exactly one home. This mirrors how the gui base wraps `hanzogui`.
|
||||
|
||||
Both layers own a `DataTable` (product = generic typed `<T>` list; data = field-driven record grid). Keeping the record layer on the `./data` subpath keeps each name unambiguous — no rename, no collision.
|
||||
|
||||
---
|
||||
|
||||
## The preserve-and-merge manifest
|
||||
|
||||
Legend: **✅ migrated** (in `pkg/ui`, GREEN) · **↪ re-exported** (composed from `@hanzo/data`) · **⏳ staged** (Step 3, after app lanes land) · **🔧 pending prop-injection** (app-coupled; decouple before it can live in a presentational lib) · **🏠 stays in console** (app glue, not a UI component).
|
||||
|
||||
### A. Record layer — `@hanzo/data` → `@hanzo/ui/data` ↪
|
||||
|
||||
Source: `pkg/data/src/*` (v1.2.0, clean-room, already published). Surfaced through `@hanzo/ui/data` and `@hanzo/ui/primitives/bases/data`. Nothing re-implemented.
|
||||
|
||||
| Component / module | Source | New home |
|
||||
|---|---|---|
|
||||
| `RecordsView` (flagship: toolbar over table⇆board, filter/sort/search/group, saved views) | `view/RecordsView.tsx`, `view/Toolbar.tsx` | `@hanzo/ui/data` |
|
||||
| `DataTable` (field-driven record grid: sort headers, drag resize/reorder, row select, inline edit, pagination) | `table/DataTable.tsx` | `@hanzo/ui/data` |
|
||||
| `BoardView` (kanban, optimistic drag-move) · `RecordCard` | `board/BoardView.tsx`, `table/RecordCard.tsx` | `@hanzo/ui/data` |
|
||||
| `RecordDetail` / `RecordForm` | `record/RecordDetail.tsx` | `@hanzo/ui/data` |
|
||||
| Field model + registry (`FieldType` ×24, `FieldDefinition`, `registerField`, routers) | `field/{types,registry,registerDefaults,FieldDisplay,FieldInput}` | `@hanzo/ui/data` |
|
||||
| Field displays + **editors** (select/currency/date/relation/file/JSON/boolean/fullName/address…) | `field/{displays,inputs}.tsx` | `@hanzo/ui/data` |
|
||||
| Composable primitives: `Menu`, `CheckBox`, `Toggle`, `Calendar` | `primitives/*` | `@hanzo/ui/data` |
|
||||
| Pure logic: `applyView`, `sortRecords`, `filterRecords`, `searchRecords`, `paginate`, `boardColumns`, … | `{table,board,view}/logic.ts` | `@hanzo/ui/data` (+ `@hanzo/data/*/logic` subpaths for Node hosts) |
|
||||
| Tokens: `tokens`, `TAG_TONES`, `tagTone` | `theme.ts` | **also top-level `@hanzo/ui`** (token surface) |
|
||||
|
||||
### B. Console `components/ui/*` product primitives → `@hanzo/ui/product` ✅ / 🔧
|
||||
|
||||
Source: `console2/src/components/ui/*`. The pure subset was ported **byte-identical** (verified by `diff`) — 100% of the polish preserved (Charts' hover tooltips/axis-ticks/honest-null, DataTable width/flex/hover-gating, Field's 6 variants, SlideOver's focus-trap + scroll-lock, ComboBox's ReDoS-safe filter).
|
||||
|
||||
| Component | Source (`console2/src/components/ui/`) | New home | Status |
|
||||
|---|---|---|---|
|
||||
| `DataTable<T>` (generic typed list) + `Column` | `DataTable.tsx` | `@hanzo/ui` (`product/DataTable`) | ✅ |
|
||||
| `PageHeader` | `PageHeader.tsx` | `@hanzo/ui` | ✅ |
|
||||
| `Field*` (`FieldRow/Text/TextArea/Switch/Select/Slider`) | `Field.tsx` | `@hanzo/ui` | ✅ |
|
||||
| **Charts** (`Sparkline/LineChart/BarChart/Donut/BarRows`, `CHART_PALETTE`) | `Charts.tsx` | `@hanzo/ui` | ✅ |
|
||||
| `Donut` (standalone dependency-free ring) | `Donut.tsx` | `@hanzo/ui` as **`DonutRing`** (alias — Charts.Donut is canonical) | ✅ |
|
||||
| `StatusTag` (health verdict → pill) | `StatusTag.tsx` | `@hanzo/ui` | ✅ |
|
||||
| `EmptyState` (DO/Vercel-class first-run) | `EmptyState.tsx` | `@hanzo/ui` | ✅ (**decoupled** — see §I) |
|
||||
| `Metric*` (`MetricCard/MiniBars/UtilBar/LegendDot/Panel/HintButton`, `SERIES`) | `Metric.tsx` | `@hanzo/ui` (its `Sparkline` preserved as **`MetricSparkline`**) | ✅ |
|
||||
| `ComboBox` + pure `filterOptions`/`isKnownOption` | `ComboBox.tsx`, `combobox/filter.ts` | `@hanzo/ui` | ✅ (+ `filter.test.ts`) |
|
||||
| `PrimaryButton` · `HanzoMark` · `ProductIcon` · `ProviderLogo` | resp. files | `@hanzo/ui` | ✅ |
|
||||
| `SlideOver` (a11y drawer) · `SelectMenu` · `Toast` (`useToast`) | resp. files | `@hanzo/ui` | ✅ (SlideOver import decoupled `~/…/color`→`./color`) |
|
||||
| `Reorder<T>` (pointer DnD) · `FadeIn` · `ThemeToggle` · `color` (`asColor`/`IconLike`) | resp. files | `@hanzo/ui` | ✅ (+ `Reorder.test.ts`) |
|
||||
| `DetailPane` (right-side pane over SlideOver) | `components/DetailPane.tsx` | `@hanzo/ui` | ⏳ (liftable; lands with Step 3 — depends only on SlideOver) |
|
||||
| `ChunkGuard` (stale-deploy chunk-error reload) | `components/ChunkGuard.tsx` | `@hanzo/ui` | ⏳ (fully portable; lands with Step 3) |
|
||||
|
||||
### C. Design tokens + motion → `@hanzo/ui` ✅
|
||||
|
||||
The brief's premise (`globals.css` `t_dark`/`t_light` token blocks) is **corrected**: `t_dark`/`t_light` are `@hanzo/gui` (Tamagui) theme **class names**; the calm values live in three real places, all preserved:
|
||||
|
||||
| Polish | Source | New home |
|
||||
|---|---|---|
|
||||
| Calm dark-first tokens (surface, text, accent `#60a5fa`, tag tones) — raw hex, theme-config independent | `pkg/data/src/theme.ts` | `@hanzo/ui` (top-level `tokens`/`TAG_TONES`/`tagTone`) |
|
||||
| OKLCH shadcn-compatible palette (soft-charcoal `oklch(0.145 0 0)`, `--radius: 0.5rem`, indigo sidebar accent) | `pkgs/ui/style/hanzo-default-colors.css`, `@hanzo/tokens` | unchanged (legacy shadcn line); the gui line uses the hex tokens above |
|
||||
| **Motion vocabulary** — `hz-fade-up` (FadeIn), `hz-collapse`/`hz-slide`/`hz-fade` (shell/drawer), `hz-drag-item`, `hz-skeleton`/`hz-pulse`/`hz-row-in`; all `prefers-reduced-motion`-guarded | `console2/app/globals.css` (motion section) | **`@hanzo/ui/styles/motion.css`** (verbatim) — `import '@hanzo/ui/styles/motion.css'` once at app root |
|
||||
|
||||
### D. Generic DocType renderer → `@hanzo/ui` ⏳ (Step 3, FINAL — active lane)
|
||||
|
||||
Source: `console2/src/components/doctype/*`. **Actively edited by the CMS/ERP/Help lanes (untracked).** Dependency-**injected** (`client: FrameworkClient` prop) — not `~/lib/api`-coupled — so the real move-blockers are `~/lib/framework/{types,fields}` + the `ui/*` primitives (now in `@hanzo/ui`) + `@hanzo/data` (now `@hanzo/ui/data`).
|
||||
|
||||
| File | Class | Step-3 disposition |
|
||||
|---|---|---|
|
||||
| `MediaGrid.tsx` | presentational | → `@hanzo/ui` (cleanest lift; props-only DAM gallery) |
|
||||
| `DocTypeRecords.tsx` / `DocTypeDetail.tsx` | mixed (data-bound shell over `@hanzo/data` views) | shell **stays in console**; renders `@hanzo/ui/data` views (already true) |
|
||||
| `CollectionsBrowser.tsx` | mixed (card grid + install/create orchestration) | presentational card-grid → `@hanzo/ui`; orchestration stays |
|
||||
| `data.ts` | glue (relation loading) | stays in console |
|
||||
|
||||
### E. LivingOverview → `@hanzo/ui` ⏳ (Step 3 — active lane)
|
||||
|
||||
Source: `console2/src/components/products/overview/living/*` (untracked/modified). Pure/presentational parts portable; app-glue concentrated in `registry.ts` (live API clients).
|
||||
|
||||
| Part | Class | Step-3 disposition |
|
||||
|---|---|---|
|
||||
| `motion.ts`, `hooks.ts`, `logic.ts`, `config.ts` (types) | pure | → `@hanzo/ui` (unit-tested; count-up, poll clock, unit formatting) |
|
||||
| `tiles.tsx`, `LivingOverview.tsx` | presentational (reuse `ui/Charts` verbatim) | → `@hanzo/ui` (once `config.ts`'s one `~/lib/products/registry` `ProductIcon` type is swapped for `@hanzo/ui`'s `IconLike`) |
|
||||
| `adapters.ts`, `registry.ts` | glue (map REAL `/v1` sources → `OverviewData`) | **stay in console** |
|
||||
|
||||
### F. Framework / Base-data libs → stay in console 🏠
|
||||
|
||||
`src/lib/framework/*` (DocType wire contract + `FrameworkApi` client + pure `fields.ts` mapper to `@hanzo/data`) and `src/lib/base-data/*` (Base REST client + schema→field mapper) are **app data-access glue**, not UI. They stay in the console. Their pure mappers (`docTypeToFields`, `baseCollectionToFields`) could later publish as a small `@hanzo/base-data` adapter, but they are not UI components and are out of scope for `@hanzo/ui`.
|
||||
|
||||
### G. App-coupled console components → 🔧 pending prop-injection (NOT yet in the lib)
|
||||
|
||||
These import app `~/lib`/session/router/branding and must be made presentational (inject props) before they belong in a host-agnostic lib. Kept in console for now; the injection contract is specified so the eventual lift is mechanical.
|
||||
|
||||
| Component | Coupling | Inject to lift |
|
||||
|---|---|---|
|
||||
| `BackendStateCard` (`BackendState.tsx`) | `~/lib/api` `ApiError` | accept a numeric `status` (or shared error shape) instead of `ApiError` |
|
||||
| `BrandLogo` | session + live IAM `organization()` + `~/config` + branding | `orgName`, `logoUrl` (or a `loadOrgLogo` loader), brand mark/name as props |
|
||||
| `Breadcrumbs` | `next/navigation` + catalog lookups | `crumbs[]` (or `pathname` + resolver) + `onNavigate` |
|
||||
| `Loader` / `BrandMark` | `~/config` + brand logo pkgs | `brand`/`brandName` + the animated-SVG getter |
|
||||
| `States` (`ErrorState`, `OperatorAccessRequired`) | `~/lib/api` + session + branding | the error, the account identity, brand strings |
|
||||
|
||||
---
|
||||
|
||||
## What this pass built (Step 2)
|
||||
|
||||
New in **`pkg/ui`** (was a bare `src/` staging dir — no package):
|
||||
|
||||
- `package.json` — `@hanzo/ui` `8.0.0`, source-published (`files: ["src"]`, exports map to `.ts`), peers `@hanzo/gui >=7.2.2`, `@hanzo/data >=1.2.0`, `react >=19`; BSD-3-Clause. Exports: `.`, `./product`, `./data`, `./primitives/bases/data`, `./styles/motion.css`.
|
||||
- `src/index.ts` — top-level barrel (product layer + tokens).
|
||||
- `src/product/index.ts` — product barrel; **collision-free** (Charts `Sparkline`/`Donut` canonical; Metric's → `MetricSparkline`; standalone ring → `DonutRing`; `ComboOption` from the one filter module) — **zero loss**.
|
||||
- `src/data.ts` — `export * from '@hanzo/data'` (the record layer, one home).
|
||||
- `src/styles/hanzo-motion.css` — the motion vocabulary, verbatim.
|
||||
- `tsconfig.json`, `vitest.config.ts`, `gui.config.ts`, `gui.d.ts`, `.npmignore`, `README.md` — mirror the proven `pkg/data` setup.
|
||||
- **One decouple fix:** `product/EmptyState.tsx` no longer imports `~/lib/products/registry`; it uses the local `IconLike` type — the last host coupling in the product tree is gone.
|
||||
|
||||
**Green:** `tsc --noEmit` = 0 errors (strict); `vitest run` = 12/12 (`combobox/filter`, `Reorder`). Every product component is host-agnostic (imports only `react`, `@hanzo/gui`, `@hanzogui/*`, `@hanzo/data`, relative).
|
||||
|
||||
## Clean-room guarantee (preserved)
|
||||
|
||||
`pkg/data` audited for GPL/Twenty contamination: **none.** No GPL, no copied license headers/SPDX, no Twenty entity/decorator architecture. The field/record model is an independent `FieldType` union + `FieldDefinition` + runtime `Map` registry (records are plain `Record<string, unknown>`). "Twenty" appears **only as benchmark prose** ("Airtable/Twenty-class polish, clean-room"). License: BSD-3-Clause. `@hanzo/ui` inherits the same posture.
|
||||
|
||||
---
|
||||
|
||||
## Naming / version — DECIDED: `@hanzo/ui@8`, shadcn retired to `@hanzo/ui-shadcn`
|
||||
|
||||
**Decision:** the gui-based unified library takes the `@hanzo/ui` name **forward at `8.0.0`** (aligning with the "Hanzo Cloud 8.x" umbrella; major = breaking re-platform). The legacy shadcn/Radix line (`pkgs/ui`, v5.7.0) is **retired by renaming** to `@hanzo/ui-shadcn` (never hard-deleted) — it stays fully alive under the new name, freeing `@hanzo/ui` for v8. Precedent: `pkg/data@1.2.0` already superseded `pkgs/data@1.1.0` under the same name.
|
||||
|
||||
This is **DONE (repo-local):** `pkg/ui/package.json` is `@hanzo/ui@8.0.0`, GREEN. The rest is a coordinated, **sequenced** retire — because publishing `@hanzo/ui@8` (a different, gui-based API with no shadcn `Button/Card/Dialog`) under the name ~20 repos consume for shadcn primitives will BREAK any consumer that resolves to `@8`. So order matters:
|
||||
|
||||
**Blast radius (measured across `~/work/hanzo`).** Declared deps on `@hanzo/ui` in ~20 repos. Most pin `^5.x` (semver-safe from an `@8` bump): paas, chat, platform, app, hanzo.ai, o11y, docs, mdx, hanzobot, ui-repo `pkgs/checkout` `^5.3`, `pkgs/agent-ui` `^5.0`. **Would break on `@8`:** `hanzoai/identity/app` (`"latest"`), ui-repo `pkgs/commerce` (`>=5.0.0`); `app/` uses `workspace:^`. The `ui.hanzo.ai` docs app + `pkgs/{commerce,checkout,agent-ui}` import the shadcn line internally.
|
||||
|
||||
**Publish reality.** `.github/workflows/publish-on-tag.yml` publishes **`pkgs/ui`** (the shadcn line) as `@hanzo/ui` on a `v*` tag — it does not reference `pkg/ui`. So a tag push today publishes shadcn, not v8. Publishing v8 needs the workflow rewired to build/publish `pkg/ui`. `npm` is not authed locally (`npm whoami` empty) — per house rules, publish goes through **CI (self-hosted runners, canonical org `NPM_TOKEN`)**, not a local `npm publish`.
|
||||
|
||||
**Safe sequence to fully land v8 (each step reversible until the tag push):**
|
||||
1. ✅ `pkg/ui` = `@hanzo/ui@8.0.0`, GREEN (done, committed to a branch).
|
||||
2. Rename `pkgs/ui` name → `@hanzo/ui-shadcn`; update the internal ui-repo consumers (`app/`, `pkgs/{commerce,checkout,agent-ui}`) + `check-no-hanzogui`/registry scripts that reference it.
|
||||
3. Migrate external consumers off `@hanzo/ui`→`@hanzo/ui-shadcn` (start with the break-risk ones: `identity` `latest`, `commerce` `>=5`; the `^5` pins are safe to migrate at leisure). ~20 repos — do as a tracked sweep or flag per-repo.
|
||||
4. Rewire `publish-on-tag.yml` (and `release.yml`) to build + publish `pkg/ui` as `@hanzo/ui@8`, and `pkgs/ui-shadcn` as `@hanzo/ui-shadcn`.
|
||||
5. `npm deprecate '@hanzo/ui@<8' 'moved to @hanzo/ui-shadcn; @hanzo/ui@8+ is the @hanzo/gui-based unified lib'`.
|
||||
6. Tag `v8.0.0` → CI publishes `@hanzo/ui@8.0.0`. Verify `npm view @hanzo/ui@8.0.0`.
|
||||
|
||||
**Gated on the user's direct go-ahead** (irreversible / globally-visible / ecosystem-wide): steps 2–6 — the `pkgs/ui` rename, the ~20-repo consumer sweep, the CI rewire, `npm deprecate`, and the `v8.0.0` tag push that triggers publish. These were not executed autonomously.
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — console re-point (STAGED)
|
||||
|
||||
**Gate (per the brief):** cloud `/v1/framework/modules` live **AND** console CMS/ERP/Help native.
|
||||
- ✅ Backend: `/v1/framework/modules[/:module[/install]]` is **implemented + wired** (`cloud/clients/framework/framework.go:71-73`, HIP-0106 order 129, bound in `subsystems.go:130`, real tests).
|
||||
- ⏳ Console: `CmsModule.tsx`, `ErpModule.tsx`, `HelpModule.tsx`, `components/doctype/` are **untracked/in-flight** on `feat/console-native-cms`.
|
||||
|
||||
→ Gate is **split**, so the re-point is **staged, not applied.** I deliberately did **not** touch `console2`'s working tree (it holds the lanes' uncommitted work — editing it would collide and risk loss, the opposite of the goal).
|
||||
|
||||
**Ready-to-apply re-point (mechanical, once the lanes land + merge):**
|
||||
1. `console2` deps: keep `@hanzo/gui`, `@hanzo/data`; add `@hanzo/ui` (`^6.0.0`). (`@hanzo/data` may stay as a direct dep or be dropped in favor of `@hanzo/ui/data` — both resolve to the same source.)
|
||||
2. Re-point imports (delete the now-duplicated in-console copies — extract, don't copy):
|
||||
- `~/components/ui/{DataTable,PageHeader,Field,Charts,Donut,StatusTag,EmptyState,Metric,ComboBox,combobox/filter,PrimaryButton,HanzoMark,ProductIcon,ProviderLogo,SlideOver,SelectMenu,Toast,Reorder,FadeIn,ThemeToggle,color}` → **`@hanzo/ui`** (or `@hanzo/ui/product`). Two spot-renames: Metric's `Sparkline`→`MetricSparkline`, standalone `Donut`→`DonutRing`.
|
||||
- `@hanzo/data` (in `doctype/*`, `base-data/*`, `products/*Module`) → **`@hanzo/ui/data`** (identical surface).
|
||||
- `~/components/{DetailPane,ChunkGuard}` → **`@hanzo/ui`**.
|
||||
- `app/globals.css` motion block → `import '@hanzo/ui/styles/motion.css'` (keep base resets local).
|
||||
- Move the 5 app-coupled components (§G) into the lib only after applying their inject-props contract; until then they stay local.
|
||||
3. Verify **identical render**: `tsc --noEmit` + `vitest` + `next build` green, then headless-Playwright the live pages pixel-same (the polish is byte-identical, so parity is expected).
|
||||
|
||||
**Report:** `@hanzo/ui` is the ONE unified library — product layer + record layer (`@hanzo/data`) + charts + calm tokens + motion, all on `@hanzo/gui`, presentational, cross-platform, clean-room, GREEN. Console re-point is **ready to apply once the CMS/ERP/Help lanes land**; it was not merged to avoid colliding with that in-flight work.
|
||||
|
||||
## Follow-ups (semver-minor, with visual e2e)
|
||||
|
||||
- Collapse the preserved Sparkline/Donut variants to one each (`MetricSparkline`→`Sparkline`, `DonutRing`→`Donut`) once call-sites are proven identical.
|
||||
- Lift §G's five components after applying their prop-injection contracts.
|
||||
- Consider publishing the pure `framework`/`base-data` mappers as a small `@hanzo/base-data` adapter (not UI).
|
||||
@@ -32,6 +32,7 @@ ui/
|
||||
checkout/ Checkout flow
|
||||
shop/ Shop components
|
||||
agent-ui/ AI agent UI components
|
||||
gui/ GUI component packages (@hanzogui/*)
|
||||
tokens/ Design tokens
|
||||
skills/
|
||||
shadcn/ AI skill definitions for shadcn CLI
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
ui
|
||||
Copyright (c) 2023 Hanzo AI, Inc.
|
||||
|
||||
This product includes software from shadcn/ui (https://github.com/shadcn-ui/ui), licensed under MIT:
|
||||
|
||||
Copyright (c) 2023 shadcn
|
||||
@@ -1,5 +1,3 @@
|
||||
<p align="center"><img src=".github/hero.svg" alt="ui" width="880"></p>
|
||||
|
||||
# @hanzo/ui
|
||||
|
||||
Accessible and customizable components for React, Vue, Svelte, and React Native. **Built on shadcn/ui with multi-framework support, 3D components, AI components, and advanced features.**
|
||||
|
||||
@@ -42,8 +42,8 @@ describe("@hanzo/ui namespace imports - Type Resolution", () => {
|
||||
* import { GridPattern } from '@hanzo/ui/pattern/grid'
|
||||
* ```
|
||||
*
|
||||
* The package exports are properly configured in pkgs/ui/package.json.
|
||||
* Build artifacts exist in pkgs/ui/dist/{code,3d,pattern}/.
|
||||
* The package exports are properly configured in pkg/ui/package.json.
|
||||
* Build artifacts exist in pkg/ui/dist/{code,3d,pattern}/.
|
||||
*
|
||||
* Vitest runtime resolution fails due to internal cross-package imports
|
||||
* (e.g., CodeBlock imports from @hanzo/ui/lib/utils), which require
|
||||
|
||||
@@ -62,15 +62,7 @@ export function CommandMenu({ ...props }: DialogProps) {
|
||||
const { recentSearches, addRecentSearch, clearRecentSearches } =
|
||||
useRecentSearches()
|
||||
|
||||
// SSR renders without `window`, so the first client (hydration) render MUST
|
||||
// also produce "Ctrl" — computing the platform-specific glyph during render
|
||||
// makes the hydrated text ("⌘" on macOS) diverge from the server text
|
||||
// ("Ctrl") → React #418 (hydration text mismatch). Start from the SSR-stable
|
||||
// value and upgrade to the platform glyph after mount (client-only effect).
|
||||
const [modKey, setModKey] = React.useState("Ctrl")
|
||||
React.useEffect(() => {
|
||||
if (isMacOS()) setModKey("⌘")
|
||||
}, [])
|
||||
const modKey = React.useMemo(() => (isMacOS() ? "⌘" : "Ctrl"), [])
|
||||
|
||||
React.useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Stock Market
|
||||
description: Display real-time stock market overview with indices and major stocks.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="market-overview"
|
||||
description="Stock market overview widget"
|
||||
/>
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
|
||||
<TabsList>
|
||||
<TabsTrigger value="cli">CLI</TabsTrigger>
|
||||
<TabsTrigger value="manual">Manual</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="cli">
|
||||
|
||||
```bash
|
||||
npx @hanzo/ui@latest add market-overview
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="manual">
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step>Install the following dependencies:</Step>
|
||||
|
||||
```bash
|
||||
npm install @hanzo/ui
|
||||
```
|
||||
|
||||
<Step>Copy and paste the following code into your project.</Step>
|
||||
|
||||
<ComponentSource name="market-overview" />
|
||||
|
||||
<Step>Update the import paths to match your project setup.</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { MarketOverview } from "@hanzo/ui/finance"
|
||||
|
||||
export default function Demo() {
|
||||
return <MarketOverview colorTheme="dark" showFloatingTooltip />
|
||||
}
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
| ------------------- | ----------------- | ---------------------------------------- | ---------------------- |
|
||||
| colorTheme | "light" \| "dark" | "dark" | Color theme |
|
||||
| width | string \| number | "100%" | Widget width |
|
||||
| height | string \| number | 400 | Widget height |
|
||||
| showFloatingTooltip | boolean | false | Show tooltip on hover |
|
||||
| locale | string | "en" | Language locale |
|
||||
| isTransparent | boolean | false | Transparent background |
|
||||
| tabs | string[] | ["indices", "futures", "bonds", "forex"] | Visible tabs |
|
||||
+3
-3
@@ -30,13 +30,13 @@
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@faker-js/faker": "^10.4.0",
|
||||
"@faker-js/faker": "^10.3.0",
|
||||
"@hanzo/docs-core": "16.5.3",
|
||||
"@hanzo/docs-docgen": "3.0.7",
|
||||
"@hanzo/docs-mdx": "14.3.0",
|
||||
"@hanzo/docs-ui": "16.5.3",
|
||||
"@hanzo/logo": "^1.0.5",
|
||||
"@hanzo/ui": "workspace:@hanzo/ui-shadcn@^",
|
||||
"@hanzo/ui": "workspace:^",
|
||||
"@hookform/resolvers": "5.2.2",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@radix-ui/react-accessible-icon": "^1.1.8",
|
||||
@@ -156,7 +156,7 @@
|
||||
"postcss-selector-parser": "^7.1.1",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||
"puppeteer": "^25.1.0",
|
||||
"puppeteer": "^24.40.0",
|
||||
"rehype-pretty-code": "^0.14.3",
|
||||
"rimraf": "^6.1.3",
|
||||
"sharp": "^0.34.5",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { docs } from "@/.source/server"
|
||||
import { docs } from "@/.source"
|
||||
import { loader } from "fumadocs-core/source"
|
||||
|
||||
export const source = loader({
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@faker-js/faker": "^10.4.0",
|
||||
"@faker-js/faker": "^10.1.0",
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@hugeicons/core-free-icons": "^1.2.1",
|
||||
"@hugeicons/react": "^1.1.1",
|
||||
@@ -50,10 +50,10 @@
|
||||
"embla-carousel-autoplay": "8.5.2",
|
||||
"embla-carousel-react": "8.5.2",
|
||||
"front-matter": "^4.0.2",
|
||||
"fumadocs-core": "16.10.3",
|
||||
"fumadocs-docgen": "3.0.10",
|
||||
"fumadocs-mdx": "15.0.12",
|
||||
"fumadocs-ui": "16.10.3",
|
||||
"fumadocs-core": "16.0.5",
|
||||
"fumadocs-docgen": "2.0.0",
|
||||
"fumadocs-mdx": "13.0.2",
|
||||
"fumadocs-ui": "16.0.5",
|
||||
"input-otp": "^1.4.2",
|
||||
"jotai": "^2.15.0",
|
||||
"little-date": "^1.0.0",
|
||||
@@ -103,8 +103,8 @@
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^5",
|
||||
"unist-builder": "4.0.0",
|
||||
"unist-util-visit": "^5.1.0"
|
||||
"unist-builder": "3.0.0",
|
||||
"unist-util-visit": "^4.1.2"
|
||||
},
|
||||
"prettier": {
|
||||
"endOfLine": "lf",
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* The composable sign-in surface, end to end.
|
||||
*
|
||||
* This file is the CONFIGURATION layer — zero auth code. It picks a method set
|
||||
* (`brand.providers`) and hands it to `<SignIn>`. The PRESENTATION layer
|
||||
* (`@hanzo/ui` `<SignIn>` / `<SocialButton>` / `<PasswordForm>`) renders it,
|
||||
* brand-themed by CSS tokens. The MECHANISM layer (`@hanzo/iam`) runs the one
|
||||
* Authorization-Code + PKCE flow — `provider` is the only knob.
|
||||
*
|
||||
* Adoption: every app collapses its hand-rolled `iam-auth.ts` + bespoke login
|
||||
* page to exactly this — `import { SignIn } from '@hanzo/ui/auth'` +
|
||||
* `import { configureIam, startLogin, getIam } from '@hanzo/iam'`.
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { SignIn } from '../src/auth'
|
||||
|
||||
// ── App brand config (the ONLY app-level knob) ──────────────────────────────
|
||||
//
|
||||
// Social/web3 are LIVE: one shared org-level OAuth provider per network in IAM
|
||||
// (admin/provider-google, admin/provider-github), reused by every app via a
|
||||
// `canSignIn` toggle. Apps NEVER register per-app Google/GitHub clients — the
|
||||
// button just delegates with `provider`.
|
||||
const brand = {
|
||||
serverUrl: 'https://hanzo.id',
|
||||
clientId: 'hanzo-app',
|
||||
providers: ['password', 'google', 'github', 'web3'] as const,
|
||||
}
|
||||
|
||||
export function SignInDemo() {
|
||||
// The mechanism comes from `@hanzo/iam`. Configure once; pass the SDK's
|
||||
// `startLogin` (provider knob) and `loginWithPassword` (embedded credential)
|
||||
// into the presentation. Lazy-imported so this demo file stays render-only.
|
||||
const [iam, setIam] = React.useState<null | {
|
||||
startLogin: (o: { provider?: string }) => Promise<void>
|
||||
loginWithPassword: (email: string, password: string) => Promise<string>
|
||||
}>(null)
|
||||
|
||||
React.useEffect(() => {
|
||||
let alive = true
|
||||
void import('@hanzo/iam').then((sdk) => {
|
||||
sdk.configureIam({ issuer: brand.serverUrl, clientId: brand.clientId })
|
||||
const engine = sdk.getIam()
|
||||
if (alive) {
|
||||
setIam({
|
||||
startLogin: (o) => sdk.startLogin(o),
|
||||
loginWithPassword: (email, password) => engine.loginWithPassword(email, password),
|
||||
})
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
alive = false
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex min-h-[480px] w-full max-w-sm items-center justify-center">
|
||||
<SignIn
|
||||
title="Sign in"
|
||||
providers={[...brand.providers]}
|
||||
config={{ serverUrl: brand.serverUrl, clientId: brand.clientId }}
|
||||
// Social/web3: delegate to the SDK's startLogin (adds &provider=…).
|
||||
onLogin={iam ? (opts) => iam.startLogin({ provider: opts.provider }) : undefined}
|
||||
// Password: the SDK's embedded credential login → PKCE-bound code.
|
||||
onPasswordSubmit={
|
||||
iam
|
||||
? async ({ email, password }) => {
|
||||
const callbackUrl = await iam.loginWithPassword(email, password)
|
||||
window.location.href = callbackUrl
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
# Hanzo UI - UX Enhancement Plan
|
||||
**Date**: 2025-11-13
|
||||
**Created By**: UI/UX Design Review via Playwright Testing
|
||||
**Status**: Ready for Implementation
|
||||
|
||||
## Executive Summary
|
||||
|
||||
After comprehensive UI/UX review using Playwright automated testing and visual inspection, we've identified **12 high-impact improvements** that will elevate the Hanzo UI component library to world-class standards. This plan prioritizes user experience, accessibility, and modern design patterns.
|
||||
|
||||
### Key Metrics from Review
|
||||
- ✅ **8/13 tests passed** - Good foundation
|
||||
- ⚠️ **Page load time**: 14.7s (Target: <3s)
|
||||
- ⚠️ **First Contentful Paint**: 8.4s (Target: <1.5s)
|
||||
- ✅ **Theme switcher**: Working perfectly
|
||||
- ⚠️ **Mobile navigation**: Missing hamburger menu
|
||||
- ✅ **Accessibility**: Good alt text coverage (100%)
|
||||
- ⚠️ **Skip links**: Missing (critical for a11y)
|
||||
|
||||
---
|
||||
|
||||
## Priority 1: Critical UX Issues (Immediate)
|
||||
|
||||
### 1. Mobile Navigation Enhancement
|
||||
**Issue**: No hamburger menu on mobile viewports
|
||||
**Impact**: ❌ Critical - Mobile users cannot navigate
|
||||
**Solution**:
|
||||
- Add responsive hamburger menu for viewports <768px
|
||||
- Smooth slide-in animation from right
|
||||
- Overlay with backdrop blur
|
||||
- Touch-optimized buttons (min 44x44px)
|
||||
|
||||
**Implementation**:
|
||||
```tsx
|
||||
// app/components/mobile-nav.tsx
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Menu, X } from "lucide-react"
|
||||
import { Sheet, SheetContent, SheetTrigger } from "@/registry/default/ui/sheet"
|
||||
import { Button } from "@/registry/default/ui/button"
|
||||
|
||||
export function MobileNav() {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild className="md:hidden">
|
||||
<Button variant="ghost" size="icon" aria-label="Open menu">
|
||||
<Menu className="h-5 w-5" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="right" className="w-[300px] sm:w-[400px]">
|
||||
{/* Navigation items */}
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Files to modify**:
|
||||
- `app/components/site-header.tsx` - Add mobile nav
|
||||
- `app/components/mobile-nav.tsx` - New file
|
||||
|
||||
---
|
||||
|
||||
### 2. Performance Optimization
|
||||
**Issue**: 14.7s page load, 8.4s first contentful paint
|
||||
**Impact**: ❌ Critical - Users abandon slow sites
|
||||
**Target**: <3s total load, <1.5s FCP
|
||||
|
||||
**Solutions**:
|
||||
1. **Image Optimization**
|
||||
- Convert all images to WebP/AVIF
|
||||
- Add lazy loading with blur placeholders
|
||||
- Implement next/image for automatic optimization
|
||||
|
||||
2. **Code Splitting**
|
||||
- Dynamic imports for heavy components
|
||||
- Route-based code splitting
|
||||
- Lazy load component preview iframes
|
||||
|
||||
3. **Loading States**
|
||||
- Skeleton screens for component gallery
|
||||
- Progressive loading for blocks page
|
||||
- Suspense boundaries for async content
|
||||
|
||||
**Implementation**:
|
||||
```tsx
|
||||
// app/components/skeleton-card.tsx
|
||||
export function SkeletonCard() {
|
||||
return (
|
||||
<div className="space-y-3 animate-pulse">
|
||||
<div className="h-48 bg-muted rounded-lg" />
|
||||
<div className="h-4 bg-muted rounded w-3/4" />
|
||||
<div className="h-3 bg-muted rounded w-1/2" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Usage in blocks page
|
||||
<Suspense fallback={<SkeletonCard />}>
|
||||
<BlockCard />
|
||||
</Suspense>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Accessibility - Skip Links
|
||||
**Issue**: Missing skip navigation links
|
||||
**Impact**: ❌ High - Screen reader users stuck in nav
|
||||
**WCAG Requirement**: Level A compliance
|
||||
|
||||
**Implementation**:
|
||||
```tsx
|
||||
// app/components/skip-links.tsx
|
||||
export function SkipLinks() {
|
||||
return (
|
||||
<a
|
||||
href="#main-content"
|
||||
className="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary focus:text-primary-foreground focus:rounded-md focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
>
|
||||
Skip to main content
|
||||
</a>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Add to `app/layout.tsx` before header.
|
||||
|
||||
---
|
||||
|
||||
## Priority 2: High-Impact Enhancements
|
||||
|
||||
### 4. Hero Section Redesign
|
||||
**Current Issues**:
|
||||
- CTAs not immediately visible (hidden button)
|
||||
- Visual hierarchy could be stronger
|
||||
- Missing animated elements to draw attention
|
||||
|
||||
**Improvements**:
|
||||
1. **Enhanced Visual Hierarchy**
|
||||
```tsx
|
||||
<section className="relative overflow-hidden">
|
||||
{/* Animated gradient background */}
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-primary/10 via-transparent to-accent/10 animate-gradient" />
|
||||
<div className="absolute inset-0 bg-grid-white/[0.02]" />
|
||||
</div>
|
||||
|
||||
{/* Hero content */}
|
||||
<div className="container py-24 md:py-32 space-y-8">
|
||||
{/* Badge with animation */}
|
||||
<div className="flex justify-center">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-muted text-sm font-medium border animate-fade-in-up">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-primary" />
|
||||
</span>
|
||||
New: 149+ components, Page Builder, and more
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main heading with gradient */}
|
||||
<h1 className="text-center text-5xl md:text-7xl font-bold tracking-tight">
|
||||
<span className="bg-clip-text text-transparent bg-gradient-to-r from-foreground to-foreground/70">
|
||||
Hanzo UI Component Library
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
{/* Subtitle */}
|
||||
<p className="text-center text-xl md:text-2xl text-muted-foreground max-w-3xl mx-auto">
|
||||
Beautifully designed components built by Hanzo AI. Accessible.
|
||||
Customizable. Open Source. Built with React and Tailwind CSS.
|
||||
</p>
|
||||
|
||||
{/* CTAs with clear hierarchy */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||
<Button size="lg" className="gap-2 text-lg h-12 px-8">
|
||||
Get Started
|
||||
<ArrowRight className="h-5 w-5" />
|
||||
</Button>
|
||||
<Button variant="outline" size="lg" className="text-lg h-12 px-8">
|
||||
View Components
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Trust indicators */}
|
||||
<div className="flex justify-center gap-8 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-primary" />
|
||||
Open Source
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-primary" />
|
||||
TypeScript
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-primary" />
|
||||
Accessible
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
2. **Add Component Preview Carousel**
|
||||
- Showcase 3-4 popular components
|
||||
- Auto-rotate with smooth transitions
|
||||
- Interactive - click to navigate to component
|
||||
|
||||
---
|
||||
|
||||
### 5. Enhanced Search Experience
|
||||
**Current State**: Basic search in sidebar
|
||||
**Target**: Instant, fuzzy search with keyboard shortcuts
|
||||
|
||||
**Features**:
|
||||
1. **Command Palette** (⌘K)
|
||||
```tsx
|
||||
// Global keyboard shortcut
|
||||
<CommandDialog>
|
||||
<CommandInput placeholder="Search components..." />
|
||||
<CommandList>
|
||||
<CommandGroup heading="Components">
|
||||
{/* Fuzzy search results */}
|
||||
</CommandGroup>
|
||||
<CommandGroup heading="Blocks">
|
||||
{/* Block results */}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</CommandDialog>
|
||||
```
|
||||
|
||||
2. **Search Features**:
|
||||
- Fuzzy matching (button → "btn" matches)
|
||||
- Category filters
|
||||
- Recent searches
|
||||
- Keyboard navigation (↑↓ to navigate, Enter to select)
|
||||
- Visual preview on hover
|
||||
|
||||
---
|
||||
|
||||
### 6. Component Gallery Enhancements
|
||||
**Current Issues**:
|
||||
- Cards lack visual interest
|
||||
- No quick preview
|
||||
- Missing category badges
|
||||
|
||||
**Improvements**:
|
||||
```tsx
|
||||
<Card className="group relative overflow-hidden transition-all hover:shadow-lg hover:-translate-y-1">
|
||||
{/* Hover effect gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-primary/0 via-primary/5 to-accent/10 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
|
||||
{/* Preview */}
|
||||
<div className="aspect-video bg-muted/50 relative overflow-hidden">
|
||||
<ComponentPreview />
|
||||
{/* Quick action overlay */}
|
||||
<div className="absolute inset-0 bg-background/80 backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center gap-2">
|
||||
<Button size="sm" variant="secondary">
|
||||
<Eye className="h-4 w-4 mr-2" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button size="sm" variant="secondary">
|
||||
<Copy className="h-4 w-4 mr-2" />
|
||||
Copy Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card content */}
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle>Button</CardTitle>
|
||||
<Badge variant="secondary">New</Badge>
|
||||
</div>
|
||||
<CardDescription>Displays a button or a component that looks like a button.</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
{/* Stats */}
|
||||
<CardFooter className="text-xs text-muted-foreground">
|
||||
<div className="flex gap-4">
|
||||
<span>12 variants</span>
|
||||
<span>•</span>
|
||||
<span>Updated 2d ago</span>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. Builder Interface Improvements
|
||||
**Current Issues**:
|
||||
- Drag affordances not obvious
|
||||
- No undo/redo
|
||||
- Limited viewport preview
|
||||
|
||||
**Enhancements**:
|
||||
|
||||
1. **Clear Drag Indicators**
|
||||
```tsx
|
||||
<div className="relative group cursor-move">
|
||||
{/* Drag handle */}
|
||||
<div className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-full pr-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<GripVertical className="h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
{/* Drop zones */}
|
||||
<div className="absolute inset-x-0 -top-1 h-2 bg-primary/20 opacity-0 data-[droppable=true]:opacity-100" />
|
||||
</div>
|
||||
```
|
||||
|
||||
2. **History Management**
|
||||
```tsx
|
||||
<div className="flex items-center gap-1 border rounded-md p-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
disabled={!canUndo}
|
||||
onClick={undo}
|
||||
title="Undo (⌘Z)"
|
||||
>
|
||||
<Undo className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
disabled={!canRedo}
|
||||
onClick={redo}
|
||||
title="Redo (⌘⇧Z)"
|
||||
>
|
||||
<Redo className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
3. **Enhanced Viewport Switcher**
|
||||
```tsx
|
||||
<ToggleGroup type="single" value={viewport}>
|
||||
<ToggleGroupItem value="mobile" aria-label="Mobile view">
|
||||
<Smartphone className="h-4 w-4" />
|
||||
<span className="ml-2 hidden sm:inline">Mobile</span>
|
||||
<span className="ml-1 text-xs text-muted-foreground hidden lg:inline">(375px)</span>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="tablet" aria-label="Tablet view">
|
||||
<Tablet className="h-4 w-4" />
|
||||
<span className="ml-2 hidden sm:inline">Tablet</span>
|
||||
<span className="ml-1 text-xs text-muted-foreground hidden lg:inline">(768px)</span>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="desktop" aria-label="Desktop view">
|
||||
<Monitor className="h-4 w-4" />
|
||||
<span className="ml-2 hidden sm:inline">Desktop</span>
|
||||
<span className="ml-1 text-xs text-muted-foreground hidden lg:inline">(1440px)</span>
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority 3: Polish & Delight
|
||||
|
||||
### 8. Micro-Interactions
|
||||
Add subtle animations to enhance feel:
|
||||
|
||||
```css
|
||||
/* tailwind.config.ts */
|
||||
{
|
||||
animation: {
|
||||
'fade-in-up': 'fade-in-up 0.6s ease-out',
|
||||
'fade-in': 'fade-in 0.4s ease-out',
|
||||
'scale-in': 'scale-in 0.3s ease-out',
|
||||
'gradient': 'gradient 8s ease infinite',
|
||||
},
|
||||
keyframes: {
|
||||
'fade-in-up': {
|
||||
'0%': { opacity: '0', transform: 'translateY(20px)' },
|
||||
'100%': { opacity: '1', transform: 'translateY(0)' },
|
||||
},
|
||||
'fade-in': {
|
||||
'0%': { opacity: '0' },
|
||||
'100%': { opacity: '1' },
|
||||
},
|
||||
'scale-in': {
|
||||
'0%': { opacity: '0', transform: 'scale(0.95)' },
|
||||
'100%': { opacity: '1', transform: 'scale(1)' },
|
||||
},
|
||||
'gradient': {
|
||||
'0%, 100%': { backgroundPosition: '0% 50%' },
|
||||
'50%': { backgroundPosition: '100% 50%' },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Toast Notifications
|
||||
Replace alert() with beautiful toasts:
|
||||
|
||||
```tsx
|
||||
import { toast } from 'sonner'
|
||||
|
||||
// Success
|
||||
toast.success('Component copied to clipboard!')
|
||||
|
||||
// Error
|
||||
toast.error('Failed to load preview', {
|
||||
description: 'Please check your connection and try again.',
|
||||
action: {
|
||||
label: 'Retry',
|
||||
onClick: () => retry(),
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### 10. Enhanced Dark Mode
|
||||
**Current**: Works well
|
||||
**Enhancement**: Add smooth transitions
|
||||
|
||||
```tsx
|
||||
// app/components/theme-provider.tsx
|
||||
<div className="transition-colors duration-300">
|
||||
{children}
|
||||
</div>
|
||||
```
|
||||
|
||||
### 11. Empty States
|
||||
Add delightful empty states:
|
||||
|
||||
```tsx
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<div className="rounded-full bg-muted p-4 mb-4">
|
||||
<Search className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold mb-2">No components found</h3>
|
||||
<p className="text-muted-foreground mb-4 max-w-sm">
|
||||
Try adjusting your search or filters to find what you're looking for.
|
||||
</p>
|
||||
<Button variant="outline" onClick={clearFilters}>
|
||||
Clear filters
|
||||
</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 12. Keyboard Shortcuts Panel
|
||||
Add discoverable shortcuts:
|
||||
|
||||
```tsx
|
||||
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
|
||||
<span className="text-xs">⌘</span>K
|
||||
</kbd>
|
||||
```
|
||||
|
||||
Shortcuts panel (? key):
|
||||
- `⌘K` - Open search
|
||||
- `/` - Focus search
|
||||
- `⌘B` - Toggle sidebar
|
||||
- `⌘D` - Toggle dark mode
|
||||
- `?` - Show shortcuts
|
||||
|
||||
---
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Week 1: Critical Fixes
|
||||
- [ ] Mobile navigation hamburger menu
|
||||
- [ ] Skip links for accessibility
|
||||
- [ ] Performance audit & initial optimizations
|
||||
|
||||
### Week 2: Hero & Search
|
||||
- [ ] Hero section redesign
|
||||
- [ ] Command palette search
|
||||
- [ ] Component preview carousel
|
||||
|
||||
### Week 3: Gallery & Builder
|
||||
- [ ] Component card enhancements
|
||||
- [ ] Builder drag indicators
|
||||
- [ ] Undo/redo functionality
|
||||
|
||||
### Week 4: Polish
|
||||
- [ ] Micro-interactions
|
||||
- [ ] Toast notifications
|
||||
- [ ] Empty states
|
||||
- [ ] Keyboard shortcuts panel
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Performance
|
||||
- ⬇️ Page load: 14.7s → **<3s**
|
||||
- ⬇️ FCP: 8.4s → **<1.5s**
|
||||
- ⬆️ Lighthouse score: ? → **>95**
|
||||
|
||||
### Accessibility
|
||||
- ⬆️ WCAG compliance: A → **AAA**
|
||||
- ✅ All interactive elements keyboard accessible
|
||||
- ✅ Screen reader tested
|
||||
|
||||
### User Engagement
|
||||
- ⬆️ Time on site: +30%
|
||||
- ⬆️ Component copies: +50%
|
||||
- ⬇️ Bounce rate: -20%
|
||||
|
||||
---
|
||||
|
||||
## Testing Plan
|
||||
|
||||
1. **Automated Testing**
|
||||
- Playwright tests for all new features
|
||||
- Visual regression testing
|
||||
- Performance benchmarks
|
||||
|
||||
2. **Manual Testing**
|
||||
- Cross-browser (Chrome, Firefox, Safari, Edge)
|
||||
- Mobile devices (iOS, Android)
|
||||
- Screen readers (NVDA, JAWS, VoiceOver)
|
||||
|
||||
3. **User Testing**
|
||||
- 5-user think-aloud sessions
|
||||
- A/B test hero variants
|
||||
- Heatmap analysis
|
||||
|
||||
---
|
||||
|
||||
## Resources Required
|
||||
|
||||
- **Design**: 20 hours (hero redesign, component cards)
|
||||
- **Development**: 60 hours (implementation)
|
||||
- **Testing**: 15 hours (QA, accessibility)
|
||||
- **Total**: ~95 hours (~2.5 weeks for 1 developer)
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
These enhancements will transform Hanzo UI from a good component library to an **exceptional** one. Focusing on performance, accessibility, and delightful interactions will set it apart from competitors like shadcn/ui and create a world-class developer experience.
|
||||
|
||||
**Next Steps**:
|
||||
1. Review and approve plan
|
||||
2. Prioritize features
|
||||
3. Begin Week 1 critical fixes
|
||||
4. Iterate based on user feedback
|
||||
|
||||
---
|
||||
|
||||
*Document generated from automated UI/UX review*
|
||||
*Last updated: 2025-11-13*
|
||||
+44
-43
@@ -11,7 +11,8 @@
|
||||
"workspaces": [
|
||||
"app",
|
||||
"apps/*",
|
||||
"pkgs/*"
|
||||
"packages/*",
|
||||
"pkg/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
@@ -50,10 +51,10 @@
|
||||
"format:write": "turbo run format:write",
|
||||
"format:check": "turbo run format:check",
|
||||
"sync:templates": "./scripts/sync-templates.sh \"templates/*\"",
|
||||
"check": "bash scripts/check-no-brand-pkg.sh && turbo lint typecheck format:check",
|
||||
"check": "turbo lint typecheck format:check",
|
||||
"release": "changeset version",
|
||||
"pub:beta": "cd pkgs/hanzo && pnpm pub:beta",
|
||||
"pub:release": "cd pkgs/hanzo && pnpm pub:release",
|
||||
"pub:beta": "cd packages/hanzo && pnpm pub:beta",
|
||||
"pub:release": "cd packages/hanzo && pnpm pub:release",
|
||||
"test:dev": "turbo run test --filter=!hanzo-ui --force",
|
||||
"test": "start-server-and-test v4:dev http://localhost:4000 test:dev",
|
||||
"validate:registries": "pnpm --filter=v4 validate:registries",
|
||||
@@ -70,67 +71,67 @@
|
||||
},
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.29.7",
|
||||
"@changesets/changelog-github": "^0.7.0",
|
||||
"@changesets/cli": "^2.31.0",
|
||||
"@commitlint/cli": "^21.0.2",
|
||||
"@commitlint/config-conventional": "^21.0.2",
|
||||
"@babel/core": "^7.29.0",
|
||||
"@changesets/changelog-github": "^0.6.0",
|
||||
"@changesets/cli": "^2.30.0",
|
||||
"@commitlint/cli": "^20.5.0",
|
||||
"@commitlint/config-conventional": "^20.5.0",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@manypkg/cli": "^0.25.1",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@typescript-eslint/parser": "^8.60.1",
|
||||
"autoprefixer": "^10.5.0",
|
||||
"chrono-node": "^2.9.1",
|
||||
"eslint": "^10.4.1",
|
||||
"eslint-config-next": "16.2.7",
|
||||
"@typescript-eslint/parser": "^8.57.1",
|
||||
"autoprefixer": "^10.4.27",
|
||||
"chrono-node": "^2.9.0",
|
||||
"eslint": "^10.1.0",
|
||||
"eslint-config-next": "16.2.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-config-turbo": "^2.9.16",
|
||||
"eslint-config-turbo": "^2.8.20",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"little-date": "^1.2.1",
|
||||
"motion": "^12.40.0",
|
||||
"postcss": "^8.5.15",
|
||||
"puppeteer": "^25.1.0",
|
||||
"recharts": "^3.8.1",
|
||||
"motion": "^12.38.0",
|
||||
"postcss": "^8.5.8",
|
||||
"puppeteer": "^24.40.0",
|
||||
"recharts": "^3.8.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tsx": "^4.22.4",
|
||||
"tsx": "^4.21.0",
|
||||
"vite-tsconfig-paths": "^6.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
|
||||
"@playwright/test": "^1.60.0",
|
||||
"@tailwindcss/oxide": "^4.3.0",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@tailwindcss/oxide": "^4.2.2",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/react": "19.2.16",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/react": "19.2.14",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.2",
|
||||
"@vitest/coverage-v8": "4.1.8",
|
||||
"@vitest/ui": "^4.1.8",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"@vitest/coverage-v8": "4.1.0",
|
||||
"@vitest/ui": "^4.1.0",
|
||||
"chalk": "^5.6.2",
|
||||
"concurrently": "^10.0.3",
|
||||
"concurrently": "^9.2.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint-plugin-tailwindcss": "3.18.3",
|
||||
"happy-dom": "^20.9.0",
|
||||
"eslint-plugin-tailwindcss": "3.18.2",
|
||||
"happy-dom": "^20.8.4",
|
||||
"jest-axe": "^10.0.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"lucide-react": "^1.17.0",
|
||||
"playwright": "^1.60.0",
|
||||
"prettier": "^3.8.3",
|
||||
"jsdom": "^29.0.1",
|
||||
"lucide-react": "^0.577.0",
|
||||
"playwright": "^1.58.2",
|
||||
"prettier": "^3.8.1",
|
||||
"pretty-quick": "^4.2.2",
|
||||
"react": "19.2.7",
|
||||
"react-dom": "19.2.7",
|
||||
"start-server-and-test": "^3.0.6",
|
||||
"tailwindcss": "4.3.0",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"start-server-and-test": "^2.1.5",
|
||||
"tailwindcss": "4.2.2",
|
||||
"tsup": "^8.5.1",
|
||||
"turbo": "^2.9.16",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.0.16",
|
||||
"vitest": "^4.1.8",
|
||||
"turbo": "^2.8.20",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^8.0.1",
|
||||
"vitest": "^4.1.0",
|
||||
"vitest-axe": "^0.1.0"
|
||||
},
|
||||
"pnpm": {
|
||||
@@ -170,6 +171,6 @@
|
||||
]
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"react-resizable-panels": "^4.11.2"
|
||||
"react-resizable-panels": "^3.0.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
"name": "shadcn",
|
||||
"version": "4.1.0",
|
||||
"description": "Add components to your apps.",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "shadcn",
|
||||
@@ -80,7 +83,7 @@
|
||||
"@babel/parser": "^7.28.0",
|
||||
"@babel/plugin-transform-typescript": "^7.28.0",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@dotenvx/dotenvx": "^1.73.1",
|
||||
"@dotenvx/dotenvx": "^1.48.4",
|
||||
"@modelcontextprotocol/sdk": "^1.26.0",
|
||||
"@types/validate-npm-package-name": "^4.0.2",
|
||||
"browserslist": "^4.26.2",
|
||||
@@ -121,6 +124,5 @@
|
||||
"tsup": "^8.5.0",
|
||||
"type-fest": "^4.41.0",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user