@godxjp/ui-mcp 0.21.5 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +102 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9535,6 +9535,84 @@ minimal repro, expected vs actual, the installed version, and env \u2014 enough
|
|
|
9535
9535
|
reproduce in one paste.`
|
|
9536
9536
|
}
|
|
9537
9537
|
]
|
|
9538
|
+
},
|
|
9539
|
+
// ── app-performance (consumer) ─────────────────────────────────
|
|
9540
|
+
{
|
|
9541
|
+
id: "app-performance",
|
|
9542
|
+
audience: "consumer",
|
|
9543
|
+
name: "App performance \u2014 measure-first React perf with @godxjp/ui",
|
|
9544
|
+
whenToUse: "A screen built on @godxjp/ui feels slow, Chrome logs [Violation] handler warnings, typing lags in a filter pane, a panel toggle blocks, or the bundle is questioned. Distilled from a real audit (orders screen: keystroke 165ms \u2192 5ms, panel open 270ms \u2192 18ms, app bundle \u221227%). Measure FIRST \u2014 the library's per-control cost is small (~3ms/field dev); page architecture is almost always the culprit.",
|
|
9545
|
+
source: "@godxjp/ui MCP (consumer surface) \u2014 2026-06 exseli audit, verified numbers",
|
|
9546
|
+
sections: [
|
|
9547
|
+
{
|
|
9548
|
+
id: "measure-first",
|
|
9549
|
+
title: "Measure before touching anything",
|
|
9550
|
+
tagline: "Long tasks + a temporary React Profiler tell you WHO is slow \u2014 never guess.",
|
|
9551
|
+
body: `Three probes, run in the page (devtools console / playwright evaluate):
|
|
9552
|
+
1) Long tasks \u2014 anything >50ms in a handler triggers Chrome's [Violation]:
|
|
9553
|
+
const t=[]; new PerformanceObserver(l=>l.getEntries().forEach(e=>t.push(Math.round(e.duration))))
|
|
9554
|
+
.observe({type:"longtask",buffered:true});
|
|
9555
|
+
2) Attribution \u2014 wrap suspect sections temporarily:
|
|
9556
|
+
<Profiler id="sec" onRender={(id,phase,d)=>(window.__perf??=[]).push([id,phase,Math.round(d)])}>
|
|
9557
|
+
Compare each section's actualDuration against the total long task. In the reference audit
|
|
9558
|
+
25 @godxjp/ui composite fields mounted in 79ms \u2014 the other ~150ms was the PAGE re-rendering
|
|
9559
|
+
everything else. Indict with numbers, then fix only what they indict.
|
|
9560
|
+
3) Dev-mode caveat: development React is 3\u20135\xD7 slower; a one-off 60ms task at popup-open is
|
|
9561
|
+
normal. Re-measure AFTER the fix with the same probes and paste before/after numbers.`
|
|
9562
|
+
},
|
|
9563
|
+
{
|
|
9564
|
+
id: "filter-pane-memo",
|
|
9565
|
+
title: "Filter panes: per-field memo + stable setters",
|
|
9566
|
+
tagline: "Page-root state with no memo boundaries = the whole pane re-renders per keystroke.",
|
|
9567
|
+
body: `The classic failure: all search state lives at the page root, every field reads it
|
|
9568
|
+
inline \u2192 one keystroke re-renders ~30 fields + the results table (165ms/key measured).
|
|
9569
|
+
The proven fix shape:
|
|
9570
|
+
- module-level memo field units: const FText = memo(function FText({ id, label, k, value, onSet }) {
|
|
9571
|
+
return <FormField id={id} label={label}><Input id={id} value={value}
|
|
9572
|
+
onChange={(e) => onSet(k, e.target.value)} /></FormField>; });
|
|
9573
|
+
- ONE stable setter per state map: const setField = useCallback((key, value) =>
|
|
9574
|
+
setCond(c => ({ ...c, [key]: value })), []);
|
|
9575
|
+
- the results table in its OWN memo component; pagination pages WITHIN the submitted query
|
|
9576
|
+
(setSubmitted(prev => ({ ...prev, page }))) so its handler stays identity-stable.
|
|
9577
|
+
After: a keystroke re-renders exactly one field (5ms). Don't memo cheap leaves that re-render
|
|
9578
|
+
rarely \u2014 over-memoization is real overhead. react-compiler lint traps: no ref writes during
|
|
9579
|
+
render, no sync setState in effect bodies; the sanctioned latch is the guarded render-time set
|
|
9580
|
+
(if (cond && !state) setState(true)).`
|
|
9581
|
+
},
|
|
9582
|
+
{
|
|
9583
|
+
id: "heavy-panels",
|
|
9584
|
+
title: "Heavy hidden panels: defer, keep mounted, pre-mount at idle",
|
|
9585
|
+
tagline: "Never mount a 25-field subtree synchronously inside a click handler.",
|
|
9586
|
+
body: `A \u8A73\u7D30\u6761\u4EF6-style toggle that conditionally renders a large subtree blocks the click
|
|
9587
|
+
(~230ms measured). Three-layer fix, in order:
|
|
9588
|
+
1) urgent state drives only the button chrome (chevron/aria-expanded);
|
|
9589
|
+
2) const deferredOpen = useDeferredValue(open) gates the FIRST mount into a deferred lane;
|
|
9590
|
+
3) keep it mounted afterwards and toggle visibility: <div className={open ? "contents" : "hidden"}>
|
|
9591
|
+
("contents" keeps children in the parent flex/grid rhythm; field state lives in the page so
|
|
9592
|
+
hiding loses nothing);
|
|
9593
|
+
4) optionally pre-mount during idle after first paint so even the FIRST open is instant:
|
|
9594
|
+
useEffect(() => { const idle = window.requestIdleCallback ?? (cb => setTimeout(cb, 300));
|
|
9595
|
+
const h = idle(() => setMounted(true)); return () => (window.cancelIdleCallback ?? clearTimeout)(h); }, []);
|
|
9596
|
+
Measured after: open 270ms \u2192 18ms, zero post-load long tasks.`
|
|
9597
|
+
},
|
|
9598
|
+
{
|
|
9599
|
+
id: "bundle-budget",
|
|
9600
|
+
title: "Code splitting + what an import costs",
|
|
9601
|
+
tagline: "lazy() every page; know the per-import budget before blaming the library.",
|
|
9602
|
+
body: `Route-level splitting is the default app shape: const Page = lazy(() => import("@/pages/Page"))
|
|
9603
|
+
behind ONE <Suspense fallback={<PageContainer title={<Skeleton \u2026/>}><Skeleton \u2026/></PageContainer>}>.
|
|
9604
|
+
Reference app: initial 929KB \u2192 675KB; the orders screen (day-picker + table stack, 122KB) and
|
|
9605
|
+
login (react-hook-form + zod, 93KB) load only when visited.
|
|
9606
|
+
Per-import minified budget of @godxjp/ui \u226513.10.0 (preserved-module dist \u2014 imports tree-shake
|
|
9607
|
+
for real): StatCard 30KB \xB7 Input 52 \xB7 Button 56 \xB7 DataTable 79 \xB7 Select 165 \xB7 DateRangePicker 207
|
|
9608
|
+
(genuinely needs react-day-picker + date-fns). A shared ~50KB floor is intrinsic (tailwind-merge
|
|
9609
|
+
+ bundled 3-locale i18n) and amortizes across one vendor chunk. Virtualize lists only >100 rows \u2014
|
|
9610
|
+
DataTable at 50/page does not need it. The dist is bundler-oriented ESM (extensionless + JSON
|
|
9611
|
+
imports): fine for vite/webpack consumers; when testing against a local checkout run tests on an
|
|
9612
|
+
npm-pack TARBALL install, not a file: symlink (the symlink's nested node_modules creates a
|
|
9613
|
+
dual-React artifact under vitest).`
|
|
9614
|
+
}
|
|
9615
|
+
]
|
|
9538
9616
|
}
|
|
9539
9617
|
];
|
|
9540
9618
|
function findSkill(id) {
|
|
@@ -9686,6 +9764,29 @@ function routeTask(task, opts) {
|
|
|
9686
9764
|
"If @godxjp/ui itself is at fault, don't fake a workaround \u2014 file a detailed gh issue (use draft_bug_report).",
|
|
9687
9765
|
["design-to-page/report-bug"]
|
|
9688
9766
|
);
|
|
9767
|
+
route(
|
|
9768
|
+
[
|
|
9769
|
+
"slow",
|
|
9770
|
+
"performance",
|
|
9771
|
+
"perf",
|
|
9772
|
+
"violation",
|
|
9773
|
+
"long task",
|
|
9774
|
+
"lag",
|
|
9775
|
+
"janky",
|
|
9776
|
+
"re-render",
|
|
9777
|
+
"rerender",
|
|
9778
|
+
"bundle size",
|
|
9779
|
+
"code splitting",
|
|
9780
|
+
"tree-shak",
|
|
9781
|
+
"ch\u1EADm",
|
|
9782
|
+
"l\u1ED7i hi\u1EC7u n\u0103ng",
|
|
9783
|
+
"\u91CD\u3044"
|
|
9784
|
+
],
|
|
9785
|
+
"app-performance",
|
|
9786
|
+
"measure-first",
|
|
9787
|
+
"Measure FIRST (longtask + temporary Profiler), then apply the matching proven fix \u2014 page architecture, not the library, is almost always the culprit.",
|
|
9788
|
+
["app-performance/filter-pane-memo", "app-performance/heavy-panels", "app-performance/bundle-budget"]
|
|
9789
|
+
);
|
|
9689
9790
|
const filtered = opts?.consumerOnly ? matches.filter((m) => {
|
|
9690
9791
|
const sk = findSkill(m.skill);
|
|
9691
9792
|
return !sk || sk.audience !== "core";
|
|
@@ -11467,7 +11568,7 @@ ${c.example}
|
|
|
11467
11568
|
// package.json
|
|
11468
11569
|
var package_default = {
|
|
11469
11570
|
name: "@godxjp/ui-mcp",
|
|
11470
|
-
version: "0.
|
|
11571
|
+
version: "0.22.0",
|
|
11471
11572
|
description: "Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 45 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",
|
|
11472
11573
|
type: "module",
|
|
11473
11574
|
main: "./dist/index.js",
|