Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 26, 2026

ObjectUI components are based on Shadcn UI but lack tooling to track versions and sync updates. This adds automated comparison and update capabilities.

Implementation

Component Manifest (shadcn-components.json)

  • Maps 46 Shadcn components to registry sources
  • Identifies 14 custom ObjectUI components
  • Documents dependency graph

Dual-mode Analysis

  • Online sync: shadcn-sync.js fetches from Shadcn registry, updates components with backups
  • Offline analysis: component-analysis.js detects customizations without network access
  • GitHub Actions: weekly automated checks with issue creation

NPM Commands

npm run shadcn:analyze      # offline: analyze 60 components, categorize by customization
npm run shadcn:update toast # online: update component from registry
npm run shadcn:diff button  # online: show differences vs Shadcn

Analysis Results

Components categorized by update safety:

  • 4 safe (minimal diff): calendar, sonner, table, toast
  • 37 review (data-slot attributes): most form/nav components
  • 5 manual (heavy customization): card, form, label, skeleton, tabs
  • 14 custom (ObjectUI-specific): never sync

Key customizations detected:

  • 280+ data-slot attributes across 41 components
  • Glassmorphism effects (backdrop-blur, gradients)
  • Custom CVA variants (icon-sm, icon-lg)
  • React Hook Form integration in form component

Example Usage

# Analyze offline to identify update candidates
npm run shadcn:analyze

# Update safe component with backup
npm run shadcn:update toast -- --backup
git diff packages/components/src/ui/toast.tsx

# Batch update all components
npm run shadcn:update-all  # creates timestamped backups

Documentation

  • docs/README_SHADCN_TOOLS.md - entry point
  • docs/SHADCN_QUICK_START.md - common workflows
  • docs/SHADCN_DEMO.md - real-world examples
  • docs/COMPONENT_SYNC_SUMMARY.md - bilingual (EN/中文)

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • ui.shadcn.com
    • Triggering command: /usr/local/bin/node node scripts/shadcn-sync.js --check (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

对比目前 components 基础组件 和 https://ui.shadcn.com/docs/components 的实现差异,我要求全部使用基础组件有没有办法有脚本来更新最新版


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel
Copy link

vercel bot commented Jan 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
objectui-docs Error Error Jan 26, 2026 3:52am
objectui-site Ready Ready Preview, Comment Jan 26, 2026 3:52am

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@hotlong hotlong marked this pull request as ready for review January 26, 2026 03:47
Copilot AI review requested due to automatic review settings January 26, 2026 03:47
…ools

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@github-actions
Copy link

✅ All checks passed!

  • ✅ Type check passed
  • ✅ Tests passed
  • ✅ Lint check completed

Copilot AI changed the title [WIP] Compare implementations of base components with latest version Add Shadcn component sync tooling and analysis Jan 26, 2026
Copilot AI requested a review from hotlong January 26, 2026 03:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Shadcn UI sync toolset to compare ObjectUI base components against the latest Shadcn registry versions, supporting both online updates and offline analysis, with docs and a scheduled GitHub Action.

Changes:

  • Added an online sync CLI (scripts/shadcn-sync.js) to check/diff/update components from the Shadcn registry.
  • Added an offline analysis CLI (scripts/component-analysis.js) plus a component manifest (packages/components/shadcn-components.json) to track Shadcn vs custom components.
  • Added documentation and a scheduled GitHub Action to automate periodic checks, plus new root npm scripts to run the tooling.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
scripts/shadcn-sync.js New online check/diff/update script to sync components from Shadcn registry.
scripts/component-analysis.js New offline analysis script to classify component customization and summarize deps.
packages/components/shadcn-components.json Manifest defining tracked Shadcn components and custom ObjectUI components.
packages/components/README_SHADCN_SYNC.md Component-package documentation for the sync/analysis workflow.
packages/components/README.md Adds “Keeping Components Updated” section and references sync docs.
package.json Adds ESM mode and new shadcn:* scripts for running the tooling.
docs/SHADCN_SYNC.md Full sync guide documentation.
docs/SHADCN_QUICK_START.md Quick-start documentation for common workflows.
docs/COMPONENT_SYNC_SUMMARY.md Bilingual summary of the tooling and workflow.
.github/workflows/shadcn-check.yml Scheduled workflow to run analysis/check and publish results.

Comment on lines +47 to +49
function log(message, color = 'white') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log() always wraps output in ANSI color codes, which will leak escape sequences into redirected output files in CI (e.g., > analysis.txt). Consider disabling colors when !process.stdout.isTTY / NO_COLOR is set, or add a --no-color option.

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +80
} else if (line.includes('from "') && !line.includes('react')) {
const match = line.match(/from ["']([^"']+)/);
if (match && !match[1].startsWith('.')) {
analysis.imports.external.push(match[1]);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This external import detection excludes any line containing the substring react, which will incorrectly drop dependencies like react-hook-form and react-day-picker from the report. Consider only excluding the actual React import (from "react") instead of substring matching.

Suggested change
} else if (line.includes('from "') && !line.includes('react')) {
const match = line.match(/from ["']([^"']+)/);
if (match && !match[1].startsWith('.')) {
analysis.imports.external.push(match[1]);
} else if (line.includes('from "')) {
const match = line.match(/from ["']([^"']+)/);
if (match) {
const moduleName = match[1];
if (!moduleName.startsWith('.') && moduleName !== 'react') {
analysis.imports.external.push(moduleName);
}

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +74
# Check component status
pnpm shadcn:check

# Update a specific component
pnpm shadcn:update button

# Update all components
pnpm shadcn:update-all

# Show diff for a component
pnpm shadcn:diff button

# List all components
pnpm shadcn:list
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The examples use pnpm shadcn:*, but those scripts are defined in the workspace root package.json, not in packages/components/package.json. Clarify that commands should be run from the repo root, or use pnpm -w shadcn:check / pnpm --workspace-root ... in the examples.

Suggested change
# Check component status
pnpm shadcn:check
# Update a specific component
pnpm shadcn:update button
# Update all components
pnpm shadcn:update-all
# Show diff for a component
pnpm shadcn:diff button
# List all components
pnpm shadcn:list
# NOTE: These scripts are defined in the workspace root package.json.
# Use -w / --workspace-root so they run from anywhere in the monorepo.
# Check component status
pnpm -w shadcn:check
# Update a specific component
pnpm -w shadcn:update button
# Update all components
pnpm -w shadcn:update-all
# Show diff for a component
pnpm -w shadcn:diff button
# List all components
pnpm -w shadcn:list

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +121
- `input-group` - Input with addons
- `input-otp` - OTP input (from Shadcn but customized)
- `item` - Generic item component
- `kbd` - Keyboard key display
- `spinner` - Loading spinner
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input-otp is listed under “Custom ObjectUI Components”, but it’s tracked as a Shadcn component in packages/components/shadcn-components.json (and listed as Shadcn-based in packages/components/README_SHADCN_SYNC.md). Please align the docs/manifest so it’s clear whether input-otp is updatable from Shadcn or not.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +69
async function fetchUrl(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (e) {
resolve(data);
}
});
}).on('error', reject);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchUrl doesn’t check res.statusCode (or handle redirects), so HTML/404 responses can be treated as successful fetches. This can make --check produce incorrect results instead of surfacing a network/registry error. Consider rejecting on non-2xx and validating the parsed response shape before returning it.

Suggested change
async function fetchUrl(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (e) {
resolve(data);
}
});
}).on('error', reject);
async function fetchUrl(url, redirectCount = 0) {
const MAX_REDIRECTS = 5;
return new Promise((resolve, reject) => {
https
.get(url, (res) => {
const { statusCode, headers } = res;
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
// Handle HTTP redirects (3xx)
if (
statusCode &&
statusCode >= 300 &&
statusCode < 400 &&
headers.location
) {
if (redirectCount >= MAX_REDIRECTS) {
return reject(
new Error(
`Too many redirects while fetching ${url} (>${MAX_REDIRECTS})`
)
);
}
const nextUrl = new URL(headers.location, url).toString();
return resolve(fetchUrl(nextUrl, redirectCount + 1));
}
// Reject on non-2xx status codes
if (!statusCode || statusCode < 200 || statusCode >= 300) {
const snippet = data && typeof data === 'string'
? data.slice(0, 200)
: '';
return reject(
new Error(
`Request to ${url} failed with status ${statusCode} ${res.statusMessage || ''}${
snippet ? ` - Response: ${snippet}` : ''
}`
)
);
}
// Successful 2xx response: try JSON, fall back to raw text
try {
resolve(JSON.parse(data));
} catch {
resolve(data);
}
});
})
.on('error', reject);

Copilot uses AI. Check for mistakes.

## 📚 Learn More

- [Full Sync Guide](../docs/SHADCN_SYNC.md) - Comprehensive documentation
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “Full Sync Guide” link points to ../docs/SHADCN_SYNC.md, but this file already lives under docs/. That relative link will resolve to a non-existent path when viewed on GitHub. Use ./SHADCN_SYNC.md (or SHADCN_SYNC.md) instead.

Suggested change
- [Full Sync Guide](../docs/SHADCN_SYNC.md) - Comprehensive documentation
- [Full Sync Guide](./SHADCN_SYNC.md) - Comprehensive documentation

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +37
function log(message, color = 'white') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script always prints ANSI color codes via log(). When output is redirected in CI (e.g., > analysis.txt), the file will contain escape sequences. Consider disabling colors when !process.stdout.isTTY / NO_COLOR is set, or add a --no-color flag.

Copilot uses AI. Check for mistakes.
Comment on lines +116 to +119
// Simple heuristic: check if significantly different
const lineDiff = Math.abs(localLines - shadcnLines);
const isDifferent = lineDiff > 10 || localContent.includes('ObjectUI') || localContent.includes('data-slot');

Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--check will mark every tracked component as modified because the heuristic treats the standard ObjectUI copyright header (localContent.includes('ObjectUI')) as a modification. Since all components include this header, the synced state becomes unreachable. Consider stripping the known header block before comparison (and optionally also ignoring data-slot if it’s an expected ObjectUI delta), or switch to a real diff (e.g., normalize headers/imports and compare hashes).

Copilot uses AI. Check for mistakes.
Comment on lines +200 to +204
async function createBackup(componentName) {
await fs.mkdir(BACKUP_DIR, { recursive: true });
const sourcePath = path.join(COMPONENTS_DIR, `${componentName}.tsx`);
const backupPath = path.join(BACKUP_DIR, `${componentName}.tsx.${Date.now()}.backup`);

Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

componentName is used directly to construct sourcePath/backupPath via path.join(..., ${componentName}.tsx). If --update receives a value with path separators (e.g., ../../x), this can copy/write outside the intended directory. Validate component names (e.g., allowlist from manifest and/or restrict to [a-z0-9-]+) and ensure the resolved path stays within COMPONENTS_DIR/BACKUP_DIR.

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +87
body += '2. Run `pnpm shadcn:analyze` locally for detailed information\n';
body += '3. Update components as needed with `pnpm shadcn:update <component>`\n';
body += '4. See [SHADCN_SYNC.md](../blob/main/docs/SHADCN_SYNC.md) for detailed guide\n\n';
body += '> This issue was automatically created by the Shadcn Components Check workflow.\n';
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated issue body links to ../blob/main/docs/SHADCN_SYNC.md, which is a relative URL and won’t reliably resolve from a GitHub issue. Prefer an absolute URL (can be constructed from context.repo) to the docs file.

Copilot uses AI. Check for mistakes.
@hotlong hotlong merged commit 2b4fdf9 into main Jan 26, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants