diff --git a/packages/components/shadcn-components.json b/packages/components/shadcn-components.json index 188ddc909..6341f1c3c 100644 --- a/packages/components/shadcn-components.json +++ b/packages/components/shadcn-components.json @@ -127,7 +127,8 @@ "dependencies": [ "radix-ui" ], - "registryDependencies": [] + "registryDependencies": [], + "localEdits": "Tailwind v4 syntax. Local uses `origin-(--radix-context-menu-content-transform-origin)`; upstream still ships v3's `origin-[--radix-…]`, which on Tailwind 4.x compiles to the bare `transform-origin: --radix-…` rather than `var(--radix-…)` — invalid CSS that browsers drop, so the open/close animation would scale from the default origin instead of the one Radix computes. Verified against tailwindcss 4.3.3: the paren form emits `var()`, the bracket form does not. Same for `max-h-(--radix-context-menu-content-available-height)`. Applied repo-wide in 925051db6; do not revert piecemeal." }, "dialog": { "source": "https://ui.shadcn.com/r/styles/default/dialog.json", @@ -148,7 +149,8 @@ "dependencies": [ "radix-ui" ], - "registryDependencies": [] + "registryDependencies": [], + "localEdits": "Tailwind v4 syntax. Local uses `origin-(--radix-dropdown-menu-content-transform-origin)`; upstream still ships v3's `origin-[--radix-…]`, which on Tailwind 4.x compiles to the bare `transform-origin: --radix-…` rather than `var(--radix-…)` — invalid CSS that browsers drop, so the open/close animation would scale from the default origin instead of the one Radix computes. Verified against tailwindcss 4.3.3: the paren form emits `var()`, the bracket form does not. Applied repo-wide in 925051db6; do not revert piecemeal." }, "form": { "source": "https://ui.shadcn.com/r/styles/default/form.json", @@ -160,14 +162,16 @@ "registryDependencies": [ "button", "label" - ] + ], + "localEdits": "FormItem accepts `Record<`data-${string}`, string | undefined>` on top of upstream's `React.HTMLAttributes`, so callers can attach a stable `data-testid` to the field wrapper (ADR-0054 testability contract). Upstream has no equivalent; syncing removes it and the locators go with it." }, "hover-card": { "source": "https://ui.shadcn.com/r/styles/default/hover-card.json", "dependencies": [ "radix-ui" ], - "registryDependencies": [] + "registryDependencies": [], + "localEdits": "Tailwind v4 syntax. Local uses `origin-(--radix-hover-card-content-transform-origin)`; upstream still ships v3's `origin-[--radix-…]`, which on Tailwind 4.x compiles to the bare `transform-origin: --radix-…` rather than `var(--radix-…)` — invalid CSS that browsers drop, so the open/close animation would scale from the default origin instead of the one Radix computes. Verified against tailwindcss 4.3.3: the paren form emits `var()`, the bracket form does not. Applied repo-wide in 925051db6; do not revert piecemeal." }, "input": { "source": "https://ui.shadcn.com/r/styles/default/input.json", @@ -193,7 +197,8 @@ "dependencies": [ "radix-ui" ], - "registryDependencies": [] + "registryDependencies": [], + "localEdits": "Tailwind v4 syntax. Local uses `origin-(--radix-menubar-content-transform-origin)`; upstream still ships v3's `origin-[--radix-…]`, which on Tailwind 4.x compiles to the bare `transform-origin: --radix-…` rather than `var(--radix-…)` — invalid CSS that browsers drop, so the open/close animation would scale from the default origin instead of the one Radix computes. Verified against tailwindcss 4.3.3: the paren form emits `var()`, the bracket form does not. Applied repo-wide in 925051db6; do not revert piecemeal." }, "navigation-menu": { "source": "https://ui.shadcn.com/r/styles/default/navigation-menu.json", @@ -214,7 +219,8 @@ "dependencies": [ "radix-ui" ], - "registryDependencies": [] + "registryDependencies": [], + "localEdits": "Tailwind v4 syntax. Local uses `origin-(--radix-popover-content-transform-origin)`; upstream still ships v3's `origin-[--radix-…]`, which on Tailwind 4.x compiles to the bare `transform-origin: --radix-…` rather than `var(--radix-…)` — invalid CSS that browsers drop, so the open/close animation would scale from the default origin instead of the one Radix computes. Verified against tailwindcss 4.3.3: the paren form emits `var()`, the bracket form does not. Applied repo-wide in 925051db6; do not revert piecemeal." }, "progress": { "source": "https://ui.shadcn.com/r/styles/default/progress.json", @@ -289,7 +295,8 @@ "sonner", "next-themes" ], - "registryDependencies": [] + "registryDependencies": [], + "localEdits": "Re-exports `toast` from the module (`import { Toaster as Sonner, toast }` / `export { Toaster, toast }`) so consumers get it from @object-ui/components rather than depending on `sonner` directly — plugin-form, plugin-kanban and others import it that way. Upstream exports only `Toaster`; syncing breaks those imports." }, "switch": { "source": "https://ui.shadcn.com/r/styles/default/switch.json", @@ -341,7 +348,8 @@ "dependencies": [ "radix-ui" ], - "registryDependencies": [] + "registryDependencies": [], + "localEdits": "Tailwind v4 syntax. Local uses `origin-(--radix-tooltip-content-transform-origin)`; upstream still ships v3's `origin-[--radix-…]`, which on Tailwind 4.x compiles to the bare `transform-origin: --radix-…` rather than `var(--radix-…)` — invalid CSS that browsers drop, so the open/close animation would scale from the default origin instead of the one Radix computes. Verified against tailwindcss 4.3.3: the paren form emits `var()`, the bracket form does not. Applied repo-wide in 925051db6; do not revert piecemeal." } }, "customComponents": { diff --git a/scripts/shadcn-sync.js b/scripts/shadcn-sync.js index eb097b95e..617a64405 100755 --- a/scripts/shadcn-sync.js +++ b/scripts/shadcn-sync.js @@ -257,6 +257,10 @@ async function checkComponent(name, manifest) { status = 'modified'; message = `${localOnly.length} local line(s) upstream lacks — --update would refuse`; if (upstreamOnly.length > 0) message += `, ${upstreamOnly.length} upstream line(s) pending`; + // Divergence with a recorded reason is a decision; divergence without + // one is an open question. Surfacing the split makes the untriaged + // components a visible to-do instead of undifferentiated noise. + message += componentInfo.localEdits ? ' [documented]' : ' [UNDOCUMENTED]'; } else if (upstreamOnly.length > 0) { status = 'outdated'; message = `${upstreamOnly.length} upstream line(s) to pick up — safe to sync`; @@ -272,6 +276,7 @@ async function checkComponent(name, manifest) { shadcnLines, localOnly: localOnly.length, upstreamOnly: upstreamOnly.length, + documented: Boolean(componentInfo.localEdits), message, }; } catch (fetchError) { @@ -349,10 +354,20 @@ async function checkAllComponents() { } if (results.modified.length > 0) { + const undocumented = results.modified.filter((r) => !r.documented); + const documented = results.modified.filter((r) => r.documented); + log('\nThese carry local edits a sync would delete:', 'yellow'); - log(` ${results.modified.map((r) => `${r.name}(${r.localOnly})`).join(', ')}`, 'dim'); - log(' Port the edits onto the new upstream version by hand, or --force to', 'dim'); - log(' take upstream as-is. `--diff ` shows both sides.', 'dim'); + if (documented.length > 0) { + log(` documented (${documented.length}): ${documented.map((r) => `${r.name}(${r.localOnly})`).join(', ')}`, 'dim'); + log(' → reason recorded in `localEdits` in shadcn-components.json', 'dim'); + } + if (undocumented.length > 0) { + log(` UNDOCUMENTED (${undocumented.length}): ${undocumented.map((r) => `${r.name}(${r.localOnly})`).join(', ')}`, 'yellow'); + log(' → nobody has written down why these diverge. Until someone does,', 'dim'); + log(' there is no way to tell a deliberate fix from stale drift.', 'dim'); + log(' Triage with `--diff `, then add a `localEdits` note.', 'dim'); + } } return results; @@ -429,6 +444,12 @@ async function updateComponent(name, manifest, options = {}) { log(`✗ Refusing to overwrite ${name}.tsx — ${lost.length} local line(s) upstream does not have:`, 'red'); lost.slice(0, 8).forEach((l) => log(` ${l.length > 96 ? l.slice(0, 96) + '…' : l}`, 'dim')); if (lost.length > 8) log(` … and ${lost.length - 8} more`, 'dim'); + if (componentInfo.localEdits) { + // Someone already worked out why this diverges. Say so here rather + // than making the next person re-derive it from a wall of diff. + log(` Why it diverges (localEdits in shadcn-components.json):`, 'cyan'); + log(` ${componentInfo.localEdits}`, 'dim'); + } log(` Overwriting deletes these. Port them onto the new upstream version by hand,`, 'yellow'); log(` or re-run with --force if they are genuinely obsolete.`, 'yellow'); return 'skipped';