@godxjp/ui 27.5.0 → 27.6.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/components/data-display/range-timeline.js +9 -6
- package/dist/contracts/measurement.json +1 -1
- package/dist/props/components/data-entry.prop.d.ts +3 -2
- package/dist/styles/control.css +6 -6
- package/dist/styles/data-display-layout.css +5 -3
- package/docs/data-display/timeline.tsx +36 -0
- package/docs/data-entry/calendar.tsx +2 -3
- package/package.json +2 -2
- package/scripts/_agent-setup.mjs +80 -1
- package/scripts/cli.mjs +5 -1
- package/scripts/init-agent-kit.mjs +3 -0
- package/scripts/postinstall.mjs +5 -0
|
@@ -44,7 +44,10 @@ const RangeTimeline = React.forwardRef(
|
|
|
44
44
|
"div",
|
|
45
45
|
{
|
|
46
46
|
className: "ui-range-timeline-canvas",
|
|
47
|
-
style: {
|
|
47
|
+
style: {
|
|
48
|
+
"--range-timeline-columns": Math.max(1, columns.length),
|
|
49
|
+
"--range-timeline-units": units
|
|
50
|
+
},
|
|
48
51
|
children: [
|
|
49
52
|
bands && bands.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ui-range-timeline-header", children: [
|
|
50
53
|
/* @__PURE__ */ jsx("div", { className: "ui-range-timeline-label", "aria-hidden": "true" }),
|
|
@@ -52,7 +55,7 @@ const RangeTimeline = React.forwardRef(
|
|
|
52
55
|
"div",
|
|
53
56
|
{
|
|
54
57
|
className: "ui-range-timeline-column",
|
|
55
|
-
style: {
|
|
58
|
+
style: { gridColumn: `span ${band.units}` },
|
|
56
59
|
children: /* @__PURE__ */ jsx(Text, { size: "xs", weight: "medium", children: band.label })
|
|
57
60
|
},
|
|
58
61
|
index
|
|
@@ -64,15 +67,15 @@ const RangeTimeline = React.forwardRef(
|
|
|
64
67
|
"div",
|
|
65
68
|
{
|
|
66
69
|
className: "ui-range-timeline-column",
|
|
67
|
-
style: {
|
|
70
|
+
style: { gridColumn: `span ${column.units}` },
|
|
68
71
|
children: /* @__PURE__ */ jsx(Text, { size: "xs", children: column.label })
|
|
69
72
|
},
|
|
70
73
|
index
|
|
71
74
|
)) })
|
|
72
75
|
] }),
|
|
73
76
|
/* @__PURE__ */ jsxs("div", { className: "ui-range-timeline-body", children: [
|
|
74
|
-
(bordered || columns.some((column) => column.muted)) && // Decorative: the same
|
|
75
|
-
//
|
|
77
|
+
(bordered || columns.some((column) => column.muted)) && // Decorative: the same unit tracks as the header columns, laid once behind every row so
|
|
78
|
+
// each vertical rule runs the full body height exactly under its header column.
|
|
76
79
|
/* @__PURE__ */ jsxs("div", { className: "ui-range-timeline-grid", "aria-hidden": "true", children: [
|
|
77
80
|
/* @__PURE__ */ jsx("div", {}),
|
|
78
81
|
/* @__PURE__ */ jsx("div", { className: "ui-range-timeline-columns", children: columns.map((column, index) => /* @__PURE__ */ jsx(
|
|
@@ -80,7 +83,7 @@ const RangeTimeline = React.forwardRef(
|
|
|
80
83
|
{
|
|
81
84
|
className: "ui-range-timeline-grid-column",
|
|
82
85
|
"data-muted": column.muted ? "true" : void 0,
|
|
83
|
-
style: {
|
|
86
|
+
style: { gridColumn: `span ${column.units}` }
|
|
84
87
|
},
|
|
85
88
|
index
|
|
86
89
|
)) })
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.6.0",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|
|
@@ -745,8 +745,9 @@ export type CalendarProp = DayPickerProps & CalendarFooterProp & {
|
|
|
745
745
|
*
|
|
746
746
|
* Default `true` — the owner's call, from a real screen: with no ruling the month read as a
|
|
747
747
|
* cloud of numbers and was "very hard to read". Pass `bordered={false}` for the floating-button
|
|
748
|
-
* look. The line colour is `--calendar-grid-border-color` (default `--input
|
|
749
|
-
*
|
|
748
|
+
* look. The line colour is `--calendar-grid-border-color` (default `--input` at 0.5 alpha,
|
|
749
|
+
* 1.74:1 light / 1.95:1 dark on the popover; the full `--input` read too dark, and the
|
|
750
|
+
* decorative `--border` measured 1.15:1 and was invisible as a 1px ruling).
|
|
750
751
|
*/
|
|
751
752
|
bordered?: boolean;
|
|
752
753
|
/** Decorate a day cell — 祝日, a booked day, a deadline. @see CalendarCellRenderProp */
|
package/dist/styles/control.css
CHANGED
|
@@ -2229,13 +2229,13 @@
|
|
|
2229
2229
|
|
|
2230
2230
|
.ui-calendar[data-bordered="true"] .ui-calendar-day,
|
|
2231
2231
|
.ui-calendar[data-bordered="true"] .ui-calendar-weekday {
|
|
2232
|
-
border-inline-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input)));
|
|
2233
|
-
border-block-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input)));
|
|
2232
|
+
border-inline-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input) / 0.5));
|
|
2233
|
+
border-block-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input) / 0.5));
|
|
2234
2234
|
}
|
|
2235
2235
|
|
|
2236
2236
|
.ui-calendar[data-bordered="true"] .ui-calendar-week,
|
|
2237
2237
|
.ui-calendar[data-bordered="true"] .ui-calendar-weekdays {
|
|
2238
|
-
border-inline-end: 1px solid var(--calendar-grid-border-color, hsl(var(--input)));
|
|
2238
|
+
border-inline-end: 1px solid var(--calendar-grid-border-color, hsl(var(--input) / 0.5));
|
|
2239
2239
|
}
|
|
2240
2240
|
|
|
2241
2241
|
.ui-calendar[data-bordered="true"] .ui-calendar-weekday {
|
|
@@ -2247,7 +2247,7 @@
|
|
|
2247
2247
|
}
|
|
2248
2248
|
|
|
2249
2249
|
.ui-calendar[data-bordered="true"] .ui-calendar-grid {
|
|
2250
|
-
border-block-end: 1px solid var(--calendar-grid-border-color, hsl(var(--input)));
|
|
2250
|
+
border-block-end: 1px solid var(--calendar-grid-border-color, hsl(var(--input) / 0.5));
|
|
2251
2251
|
}
|
|
2252
2252
|
|
|
2253
2253
|
.ui-calendar[data-bordered="true"] .ui-calendar-day.invisible {
|
|
@@ -2268,8 +2268,8 @@
|
|
|
2268
2268
|
|
|
2269
2269
|
.ui-calendar[data-bordered="true"] .ui-calendar-week-number,
|
|
2270
2270
|
.ui-calendar[data-bordered="true"] .ui-calendar-week-number-header {
|
|
2271
|
-
border-inline-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input)));
|
|
2272
|
-
border-block-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input)));
|
|
2271
|
+
border-inline-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input) / 0.5));
|
|
2272
|
+
border-block-start: 1px solid var(--calendar-grid-border-color, hsl(var(--input) / 0.5));
|
|
2273
2273
|
}
|
|
2274
2274
|
|
|
2275
2275
|
.ui-calendar[data-bordered="true"] .ui-calendar-day {
|
|
@@ -1351,8 +1351,10 @@
|
|
|
1351
1351
|
padding: var(--space-2);
|
|
1352
1352
|
border-inline-end: 1px solid hsl(var(--range-timeline-border-color));
|
|
1353
1353
|
}
|
|
1354
|
+
|
|
1354
1355
|
.ui-range-timeline-columns {
|
|
1355
|
-
display:
|
|
1356
|
+
display: grid;
|
|
1357
|
+
grid-template-columns: repeat(var(--range-timeline-units, 1), minmax(0, 1fr));
|
|
1356
1358
|
}
|
|
1357
1359
|
.ui-range-timeline-column {
|
|
1358
1360
|
min-inline-size: 0;
|
|
@@ -1390,12 +1392,12 @@
|
|
|
1390
1392
|
.ui-range-timeline[data-bordered="true"] .ui-range-timeline-column,
|
|
1391
1393
|
.ui-range-timeline[data-bordered="true"] .ui-range-timeline-label,
|
|
1392
1394
|
.ui-range-timeline[data-bordered="true"] .ui-range-timeline-grid-column {
|
|
1393
|
-
border-inline-end-color: var(--range-timeline-grid-color, hsl(var(--input)));
|
|
1395
|
+
border-inline-end-color: var(--range-timeline-grid-color, hsl(var(--input) / 0.5));
|
|
1394
1396
|
}
|
|
1395
1397
|
.ui-range-timeline[data-bordered="true"] .ui-range-timeline-header + .ui-range-timeline-header,
|
|
1396
1398
|
.ui-range-timeline[data-bordered="true"] .ui-range-timeline-row {
|
|
1397
1399
|
border-block-start: var(--range-timeline-grid-width) solid
|
|
1398
|
-
var(--range-timeline-grid-color, hsl(var(--input)));
|
|
1400
|
+
var(--range-timeline-grid-color, hsl(var(--input) / 0.5));
|
|
1399
1401
|
}
|
|
1400
1402
|
.ui-range-timeline-outside {
|
|
1401
1403
|
position: absolute;
|
|
@@ -200,6 +200,42 @@ export default function Demo() {
|
|
|
200
200
|
</CardContent>
|
|
201
201
|
</Card>
|
|
202
202
|
|
|
203
|
+
<Card>
|
|
204
|
+
<CardHeader>
|
|
205
|
+
<CardTitle level={2}>月をまたぐ日単位の軸(bands)</CardTitle>
|
|
206
|
+
<CardDescription>
|
|
207
|
+
9月25日から10月7日まで。bands
|
|
208
|
+
の月の境界は、その月の1日の列の境界にぴったり重なります。
|
|
209
|
+
始まりが月の途中なので、最初の月の帯は6日分だけの幅です。
|
|
210
|
+
</CardDescription>
|
|
211
|
+
</CardHeader>
|
|
212
|
+
<CardContent>
|
|
213
|
+
<RangeTimeline
|
|
214
|
+
label={t("rangeTimeline.schedule")}
|
|
215
|
+
bands={[
|
|
216
|
+
{ label: monthLabel(8), units: 6 },
|
|
217
|
+
{ label: monthLabel(9), units: 7 },
|
|
218
|
+
]}
|
|
219
|
+
columns={Array.from({ length: 13 }, (_, index) => {
|
|
220
|
+
const date = new Date(Date.UTC(2026, 8, 25 + index));
|
|
221
|
+
return {
|
|
222
|
+
label: new Intl.NumberFormat(locale).format(date.getUTCDate()),
|
|
223
|
+
units: 1,
|
|
224
|
+
muted: date.getUTCDay() === 0 || date.getUTCDay() === 6,
|
|
225
|
+
};
|
|
226
|
+
})}
|
|
227
|
+
rows={[
|
|
228
|
+
{ id: "migration", label: "移行", start: 3, end: 8 },
|
|
229
|
+
{ id: "october", label: "10月の作業", start: 6, end: 12 },
|
|
230
|
+
].map((row) => ({
|
|
231
|
+
...row,
|
|
232
|
+
startLabel: dayLabel(25 + row.start),
|
|
233
|
+
endLabel: dayLabel(25 + row.end),
|
|
234
|
+
}))}
|
|
235
|
+
/>
|
|
236
|
+
</CardContent>
|
|
237
|
+
</Card>
|
|
238
|
+
|
|
203
239
|
<Card>
|
|
204
240
|
<CardHeader>
|
|
205
241
|
<CardTitle level={2}>月単位の軸(列ごとに units が異なる)</CardTitle>
|
|
@@ -77,9 +77,8 @@ export default function Demo() {
|
|
|
77
77
|
罫線 (bordered) · 既定で表示 / bordered={"{false}"} で解除
|
|
78
78
|
</CardTitle>
|
|
79
79
|
<CardDescription>
|
|
80
|
-
日と日の間に罫線を引くのが既定。線色は --calendar-grid-border-color (既定
|
|
81
|
-
|
|
82
|
-
以上)。選択日・今日・範囲の始点/中間/終点・無効日・前後月の日が
|
|
80
|
+
日と日の間に罫線を引くのが既定。線色は --calendar-grid-border-color (既定 --input の
|
|
81
|
+
50%、背景に対し約 1.7:1)。選択日・今日・範囲の始点/中間/終点・無効日・前後月の日が
|
|
83
82
|
罫線の中でも判別できる。罫線なしの浮いたボタン表示にしたい場合のみ bordered=
|
|
84
83
|
{"{false}"} を渡す。
|
|
85
84
|
</CardDescription>
|
package/package.json
CHANGED
package/scripts/_agent-setup.mjs
CHANGED
|
@@ -16,12 +16,14 @@ import {
|
|
|
16
16
|
existsSync,
|
|
17
17
|
mkdirSync,
|
|
18
18
|
readFileSync,
|
|
19
|
+
realpathSync,
|
|
19
20
|
renameSync,
|
|
20
21
|
statSync,
|
|
21
22
|
unlinkSync,
|
|
22
23
|
writeFileSync,
|
|
23
24
|
} from "node:fs";
|
|
24
|
-
import {
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
25
27
|
import { fileURLToPath } from "node:url";
|
|
26
28
|
|
|
27
29
|
/** This package's own root — the source of the version we stamp with. */
|
|
@@ -425,6 +427,83 @@ export function ensureMcpJson(root) {
|
|
|
425
427
|
return created ? "created" : "added";
|
|
426
428
|
}
|
|
427
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Registrations of the catalog server OUTSIDE the project — READ-ONLY (gh#722).
|
|
432
|
+
*
|
|
433
|
+
* `.mcp.json` is not the only place an agent's MCP servers come from. Claude Code keeps
|
|
434
|
+
* user-scoped servers in the top-level `mcpServers` of `~/.claude.json` (under
|
|
435
|
+
* `$CLAUDE_CONFIG_DIR` when that is set) and local-scoped ones — private to one checkout — under
|
|
436
|
+
* `projects[<absolute project path>].mcpServers` of the same file. Both are live beside the
|
|
437
|
+
* project's entry. Measured: a user-scoped `godxjp-ui` serving a ≤ 21.0.0 catalog answered next
|
|
438
|
+
* to the project's pinned `godx-ui`, and an agent took a removed component's page from it.
|
|
439
|
+
*
|
|
440
|
+
* That file belongs to the user, spans every project, and is not ours to edit: this only READS
|
|
441
|
+
* it. A missing, unreadable or malformed file yields nothing, silently. Returns
|
|
442
|
+
* `{ key, scope, pin }` per entry — never `env`, which is where secrets live.
|
|
443
|
+
*/
|
|
444
|
+
export function findOutOfProjectUiMcp(
|
|
445
|
+
root,
|
|
446
|
+
configDir = process.env.CLAUDE_CONFIG_DIR || homedir(),
|
|
447
|
+
) {
|
|
448
|
+
try {
|
|
449
|
+
const read = readJsonFile(join(configDir, ".claude.json"));
|
|
450
|
+
if (read.state !== "ok") return [];
|
|
451
|
+
const found = [];
|
|
452
|
+
const collect = (servers, scope) => {
|
|
453
|
+
if (!servers || typeof servers !== "object" || Array.isArray(servers)) return;
|
|
454
|
+
for (const [key, entry] of Object.entries(servers)) {
|
|
455
|
+
if (!runsUiMcp(entry)) continue;
|
|
456
|
+
const parts = [entry.command, ...(Array.isArray(entry.args) ? entry.args : [])];
|
|
457
|
+
const pin = parts.find(
|
|
458
|
+
(part) => typeof part === "string" && /(^|\/)@godxjp\/ui-mcp(@|$)/.test(part),
|
|
459
|
+
);
|
|
460
|
+
found.push({ key, scope, pin });
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
collect(read.json.mcpServers, "user");
|
|
464
|
+
const projects = read.json.projects;
|
|
465
|
+
if (projects && typeof projects === "object" && !Array.isArray(projects)) {
|
|
466
|
+
const paths = new Set([resolve(root)]);
|
|
467
|
+
try {
|
|
468
|
+
paths.add(realpathSync(root));
|
|
469
|
+
} catch {
|
|
470
|
+
// A root that cannot be resolved is looked up by its given path only.
|
|
471
|
+
}
|
|
472
|
+
for (const path of paths) collect(projects[path]?.mcpServers, "local");
|
|
473
|
+
}
|
|
474
|
+
return found;
|
|
475
|
+
} catch {
|
|
476
|
+
return [];
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const shellWord = (word) => (/^[\w.@/-]+$/.test(word) ? word : `'${word.replace(/'/g, "'\\''")}'`);
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* What `sync-rules` and `init-agent` print about out-of-project registrations: key, pin, that it
|
|
484
|
+
* was left untouched, and the command that updates it. Empty string when there are none.
|
|
485
|
+
*/
|
|
486
|
+
export function outOfProjectUiMcpReport(root, configDir) {
|
|
487
|
+
const found = findOutOfProjectUiMcp(root, configDir);
|
|
488
|
+
if (found.length === 0) return "";
|
|
489
|
+
const expected = mcpServerFor(root).args[0];
|
|
490
|
+
let out =
|
|
491
|
+
`\n @godxjp/ui → @godxjp/ui-mcp is ALSO registered outside this project, in Claude Code's ` +
|
|
492
|
+
`.claude.json.\n It answers beside the project's .mcp.json entry (${expected}) and an agent ` +
|
|
493
|
+
`can call either — the tool names differ only by the key.\n`;
|
|
494
|
+
for (const { key, scope, pin } of found) {
|
|
495
|
+
const k = shellWord(key);
|
|
496
|
+
const runs = /@godxjp\/ui-mcp@\S+$/.test(pin ?? "")
|
|
497
|
+
? pin
|
|
498
|
+
: `${pin ?? "@godxjp/ui-mcp"} (unpinned: resolves the latest release)`;
|
|
499
|
+
out +=
|
|
500
|
+
` • "${key}" (${scope} scope) runs ${runs} — outside the project, left untouched.\n` +
|
|
501
|
+
` remove it: claude mcp remove ${k} -s ${scope}\n` +
|
|
502
|
+
` or re-pin: claude mcp remove ${k} -s ${scope} && claude mcp add ${k} -s ${scope} -- npx ${expected}\n`;
|
|
503
|
+
}
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
|
|
428
507
|
/** Ensure `.claude/settings.json` has the audit PostToolUse + workflow SessionStart hooks. */
|
|
429
508
|
export function ensureClaudeHooks(root) {
|
|
430
509
|
const path = join(root, ".claude", "settings.json");
|
package/scripts/cli.mjs
CHANGED
|
@@ -34,7 +34,11 @@ const HELP = {
|
|
|
34
34
|
Refresh the package-owned agent files for the installed @godxjp/ui version: the godx-ui entry in
|
|
35
35
|
.mcp.json, the managed CLAUDE.md block, .claude/godxjp-ui-workflow.md and .ai/rules/godxjp-ui.md.
|
|
36
36
|
It is the postinstall step, run by hand — use it when the app installs with ignore-scripts=true,
|
|
37
|
-
where postinstall never runs and the rules go stale. Silent no-op in CI and when opted out
|
|
37
|
+
where postinstall never runs and the rules go stale. Silent no-op in CI and when opted out.
|
|
38
|
+
|
|
39
|
+
It also READS Claude Code's ~/.claude.json and reports any @godxjp/ui-mcp registered there (user
|
|
40
|
+
scope, or local scope for this project): key, pin, and the claude mcp command that updates it.
|
|
41
|
+
That file is outside the project and is never written; env values are never printed.`,
|
|
38
42
|
audit: `godxjp-ui audit [dir …] [--changed] [--format json] [--quiet] [--rules]
|
|
39
43
|
|
|
40
44
|
Static UI-standardization audit over source (regex, no browser, fast). Exits non-zero on errors.
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
ensureClaudeHooks,
|
|
5
5
|
ensureClaudeMd,
|
|
6
6
|
ensureMcpJson,
|
|
7
|
+
outOfProjectUiMcpReport,
|
|
7
8
|
shouldSkip,
|
|
8
9
|
writeWorkflowMd,
|
|
9
10
|
} from "./_agent-setup.mjs";
|
|
@@ -28,6 +29,8 @@ console.log(
|
|
|
28
29
|
);
|
|
29
30
|
console.log(` • .claude/godxjp-ui-workflow.md — ${md ? "created" : "already present"}`);
|
|
30
31
|
console.log(` • CLAUDE.md — godxjp-ui mandate (${claudeMd})`);
|
|
32
|
+
const outside = outOfProjectUiMcpReport(root);
|
|
33
|
+
if (outside) console.log(outside);
|
|
31
34
|
console.log(`
|
|
32
35
|
The auto-audit hook now runs on every Write/Edit of a .tsx file and feeds findings
|
|
33
36
|
back to the agent — it cannot skip the audit. Restart your agent to load the MCP + hooks.
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ensureClaudeMd,
|
|
9
9
|
ensureMcpJson,
|
|
10
10
|
ensureConsumerRules,
|
|
11
|
+
outOfProjectUiMcpReport,
|
|
11
12
|
refreshGuineaPigSkill,
|
|
12
13
|
shouldSkip,
|
|
13
14
|
writeWorkflowMd,
|
|
@@ -25,6 +26,10 @@ try {
|
|
|
25
26
|
if (r.startsWith("left untouched") || r.startsWith("present (custom godx-ui")) {
|
|
26
27
|
console.log(`\n @godxjp/ui → .mcp.json ${r}\n`);
|
|
27
28
|
}
|
|
29
|
+
// A registration outside the project is reported whatever the project file says, before any of
|
|
30
|
+
// the quiet exits below: it is live beside ours and nothing here will ever change it (gh#722).
|
|
31
|
+
const outside = outOfProjectUiMcpReport(root);
|
|
32
|
+
if (outside) console.log(outside);
|
|
28
33
|
// The mandate is plain text the agent reads every turn (CLAUDE.md block + workflow file). It
|
|
29
34
|
// changes nothing in the dev loop, so it is installed by default: an agent that never saw the
|
|
30
35
|
// but no mandate). Only the hooks — which DO change the loop — stay behind `init-agent`.
|