@godxjp/ui 31.13.0 → 31.15.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.
Files changed (36) hide show
  1. package/agent/START-HERE.md +4 -4
  2. package/agent/components/Image.json +18 -2
  3. package/agent/components/Timeline.json +4 -3
  4. package/agent/components/Tree.json +22 -1
  5. package/agent/components-index.json +2 -2
  6. package/agent/components.json +44 -6
  7. package/agent/index.json +4 -4
  8. package/agent/llms.txt +5 -5
  9. package/agent/tokens.json +18 -0
  10. package/dist/components/data-display/image.d.ts +6 -1
  11. package/dist/components/data-display/image.js +90 -49
  12. package/dist/components/data-display/index.d.ts +1 -1
  13. package/dist/components/data-display/timeline.d.ts +12 -2
  14. package/dist/components/data-display/timeline.js +6 -2
  15. package/dist/components/data-display/tree.d.ts +1 -1
  16. package/dist/components/data-display/tree.js +147 -0
  17. package/dist/contracts/measurement.json +1 -1
  18. package/dist/i18n/messages/en.json +6 -1
  19. package/dist/i18n/messages/ja.json +6 -1
  20. package/dist/i18n/messages/vi.json +6 -1
  21. package/dist/props/components/data-display.prop.d.ts +67 -1
  22. package/dist/props/registry.d.ts +69 -0
  23. package/dist/props/registry.js +71 -0
  24. package/dist/styles/data-display-layout.css +92 -1
  25. package/dist/styles/layers.json +1 -1
  26. package/dist/tokens/components/data-display.css +1 -0
  27. package/dist/tokens/components/tree.css +3 -0
  28. package/docs/DESIGN-AUTHORITY.md +6 -0
  29. package/docs/FRAME-COVERAGE-REPORT.md +1 -1
  30. package/docs/data-display/image.tsx +40 -0
  31. package/docs/data-display/timeline.tsx +28 -0
  32. package/docs/data-display/tree.tsx +82 -0
  33. package/docs/i18n/messages/en.json +24 -0
  34. package/docs/i18n/messages/ja.json +24 -0
  35. package/docs/i18n/messages/vi.json +24 -0
  36. package/package.json +2 -2
@@ -523,7 +523,8 @@
523
523
  hsl(var(--primary) / 0.2);
524
524
  }
525
525
 
526
- .ui-timeline-dot[data-status="pending"] {
526
+ .ui-timeline-dot[data-status="pending"],
527
+ .ui-timeline-dot[data-status="log"] {
527
528
  border-color: hsl(var(--border));
528
529
  background: hsl(var(--background));
529
530
  color: hsl(var(--muted-foreground));
@@ -2126,6 +2127,44 @@
2126
2127
  opacity: var(--disabled-opacity);
2127
2128
  }
2128
2129
 
2130
+ .ui-tree-node[draggable="true"] {
2131
+ cursor: grab;
2132
+ }
2133
+
2134
+ .ui-tree-node[data-dragging="true"] {
2135
+ opacity: var(--disabled-opacity);
2136
+ }
2137
+
2138
+ .ui-tree-node[data-drop-position] {
2139
+ position: relative;
2140
+ }
2141
+
2142
+ .ui-tree-node[data-drop-position="before"]::after,
2143
+ .ui-tree-node[data-drop-position="after"]::after {
2144
+ content: "";
2145
+ position: absolute;
2146
+ inset-inline: calc(
2147
+ var(--tree-node-padding-inline) + var(--tree-node-level, 0) * var(--tree-indent-width)
2148
+ )
2149
+ 0;
2150
+ block-size: var(--tree-drop-indicator-width, var(--stroke-md));
2151
+ background: var(--tree-drop-indicator-color, hsl(var(--primary)));
2152
+ pointer-events: none;
2153
+ }
2154
+
2155
+ .ui-tree-node[data-drop-position="before"]::after {
2156
+ inset-block-start: 0;
2157
+ }
2158
+
2159
+ .ui-tree-node[data-drop-position="after"]::after {
2160
+ inset-block-end: 0;
2161
+ }
2162
+
2163
+ .ui-tree-node[data-drop-position="inside"] {
2164
+ box-shadow: inset 0 0 0 var(--tree-drop-indicator-width, var(--stroke-md))
2165
+ var(--tree-drop-indicator-color, hsl(var(--primary)));
2166
+ }
2167
+
2129
2168
  .ui-tree-switcher {
2130
2169
  display: inline-flex;
2131
2170
  flex: none;
@@ -2687,6 +2726,58 @@
2687
2726
  border-radius: inherit;
2688
2727
  }
2689
2728
 
2729
+ .ui-image[data-sized],
2730
+ .ui-image-tile[data-sized] {
2731
+ inline-size: var(--image-inline-size, auto);
2732
+ }
2733
+
2734
+ .ui-image[data-sized] {
2735
+ block-size: var(--image-block-size, auto);
2736
+ }
2737
+
2738
+ .ui-image[data-sized] > .ui-image-img {
2739
+ inline-size: 100%;
2740
+ max-inline-size: none;
2741
+ block-size: 100%;
2742
+ }
2743
+
2744
+ :is(.ui-image, .ui-image-tile)[data-size="sm"] {
2745
+ --image-block-size: var(--thumbnail-block-size-sm);
2746
+ }
2747
+
2748
+ :is(.ui-image, .ui-image-tile)[data-size="md"] {
2749
+ --image-block-size: var(--thumbnail-block-size);
2750
+ }
2751
+
2752
+ :is(.ui-image, .ui-image-tile)[data-size="lg"] {
2753
+ --image-block-size: var(--thumbnail-block-size-lg);
2754
+ }
2755
+
2756
+ :is(.ui-image, .ui-image-tile)[data-size] {
2757
+ --image-inline-size: calc(var(--image-block-size) * var(--thumbnail-width-ratio));
2758
+ }
2759
+
2760
+ .ui-image-img[data-fit="cover"] {
2761
+ object-fit: cover;
2762
+ }
2763
+
2764
+ .ui-image-img[data-fit="contain"] {
2765
+ object-fit: contain;
2766
+ }
2767
+
2768
+ .ui-image-tile {
2769
+ display: inline-flex;
2770
+ flex-direction: column;
2771
+ gap: var(--space-stack-xs);
2772
+ max-inline-size: 100%;
2773
+ margin: 0;
2774
+ vertical-align: top;
2775
+ }
2776
+
2777
+ .ui-image-caption {
2778
+ min-inline-size: 0;
2779
+ }
2780
+
2690
2781
  .ui-image-placeholder {
2691
2782
  position: absolute;
2692
2783
  inset: 0;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.13.0",
2
+ "version": "31.15.0",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
@@ -169,6 +169,7 @@
169
169
  --thumbnail-block-size-sm: 4rem;
170
170
  --thumbnail-block-size: 6rem;
171
171
  --thumbnail-block-size-lg: 10rem;
172
+ --thumbnail-width-ratio: 4 / 3;
172
173
  --thumbnail-border-width: initial;
173
174
  --thumbnail-radius: initial;
174
175
 
@@ -29,5 +29,8 @@
29
29
 
30
30
  --tree-node-filter-foreground: initial;
31
31
 
32
+ --tree-drop-indicator-color: initial;
33
+ --tree-drop-indicator-width: initial;
34
+
32
35
  --tree-loading-bar-inline-size: 40%;
33
36
  }
@@ -399,6 +399,12 @@ bottomEnd | none` (`TablePaginationPositionProp`, default `['bottomEnd']`), the
399
399
  would rule every existing tree, including the picker-sized and `size="xs"` outlines where a rule
400
400
  every 24px is noise rather than structure. A tree used as page navigation opts in.
401
401
 
402
+ - **`Image size` / `fit` / `caption` have no antd counterpart.** antd's Image takes `width` /
403
+ `height` on the frame and the picture, and those are ported as antd has them (a fixed height
404
+ crops with `fit`, default `cover`, where antd stretches). godx adds `size?: "sm" | "md" | "lg"`
405
+ — the Thumbnail height scale at `--thumbnail-width-ratio` (4 / 3) — `fit?: "cover" | "contain"`,
406
+ and `caption`, a figcaption held to the picture's width so a file name truncates under it.
407
+
402
408
  - **`Timeline density` has no antd counterpart.** antd's Timeline spaces items with one fixed
403
409
  token (`itemPaddingBottom`, 20px) and offers no size or density prop. godx adds
404
410
  `density?: "default" | "compact"` — the ListRow-shaped subset of `DensityProp` — driving one
@@ -212,7 +212,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
212
212
  | Actions | ✓ | · | N/A | N/A | · | · | · | · | · | · |
213
213
  | ThoughtChain | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
214
214
  | OrgChart | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
215
- | Image | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
215
+ | Image | ✓ | · | N/A | N/A | · | · | · | · | · | · |
216
216
  | Attachments | ✓ | N/A | · | N/A | · | · | · | · | · | · |
217
217
  | Marquee | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
218
218
  | Masonry | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
@@ -124,6 +124,46 @@ export default function Demo() {
124
124
  </Flex>
125
125
  </CardContent>
126
126
  </Card>
127
+
128
+ <Card>
129
+ <CardHeader>
130
+ <CardTitle level={2}>Thumbnail size · fit · caption</CardTitle>
131
+ <CardDescription>
132
+ size puts the frame on the Thumbnail height scale (sm 64px, md 96px, lg 160px) at 4:3,
133
+ cropped to fill; fit=&quot;contain&quot; letterboxes instead. caption keeps a long
134
+ file name under the picture, truncated at the picture&apos;s width.
135
+ </CardDescription>
136
+ </CardHeader>
137
+ <CardContent>
138
+ <ImagePreviewGroup>
139
+ <Flex gap="sm" wrap align="start">
140
+ <Image
141
+ src={shotLandscape}
142
+ size="md"
143
+ alt="Dashboard"
144
+ caption={
145
+ <Text size="xs" truncate>
146
+ dashboard-after-the-fix-2026-09-30-full-resolution.png
147
+ </Text>
148
+ }
149
+ />
150
+ <Image
151
+ src={shotPortrait}
152
+ size="md"
153
+ fit="contain"
154
+ alt="List screen"
155
+ caption={
156
+ <Text size="xs" truncate>
157
+ list-screen.png
158
+ </Text>
159
+ }
160
+ />
161
+ <Image src={coverTerrain} size="sm" alt="Cover image" />
162
+ <Image src={portraitIris} size="lg" alt="Portrait" />
163
+ </Flex>
164
+ </ImagePreviewGroup>
165
+ </CardContent>
166
+ </Card>
127
167
  </Flex>
128
168
  </PageContainer>
129
169
  );
@@ -13,6 +13,7 @@ import {
13
13
  } from "@godxjp/ui/data-display";
14
14
  import { Text } from "@godxjp/ui/general";
15
15
  import { Flex, PageContainer } from "@godxjp/ui/layout";
16
+ import { ArrowRightLeft, MessageSquare } from "lucide-react";
16
17
 
17
18
  /**
18
19
  * Timeline — vertical event list with an icon rail; the current item gets a
@@ -89,6 +90,23 @@ const richItems: TimelineItem[] = [
89
90
 
90
91
  export default function Demo() {
91
92
  const { t, locale } = useTranslation();
93
+ // An activity log (gh#1092): every row already happened, so none is "done" or "upcoming" —
94
+ // `status: "log"` keeps the neutral dot and adds no screen-reader status prefix.
95
+ const logItems: TimelineItem[] = [
96
+ {
97
+ title: t("timelineDocs.log.status"),
98
+ time: "10/01 08:12",
99
+ status: "log",
100
+ icon: ArrowRightLeft,
101
+ },
102
+ {
103
+ title: t("timelineDocs.log.comment"),
104
+ time: "10/01 09:30",
105
+ status: "log",
106
+ icon: MessageSquare,
107
+ },
108
+ { title: t("timelineDocs.log.assignee"), time: "10/01 10:05", status: "log" },
109
+ ];
92
110
  const dayLabel = (day: number) =>
93
111
  new Intl.DateTimeFormat(locale, { month: "short", day: "numeric", timeZone: "UTC" }).format(
94
112
  Date.UTC(2026, 8, day),
@@ -139,6 +157,16 @@ export default function Demo() {
139
157
  </CardContent>
140
158
  </Card>
141
159
 
160
+ <Card>
161
+ <CardHeader>
162
+ <CardTitle level={2}>{t("timelineDocs.log.title")}</CardTitle>
163
+ <CardDescription>{t("timelineDocs.log.description")}</CardDescription>
164
+ </CardHeader>
165
+ <CardContent>
166
+ <Timeline density="compact" items={logItems} />
167
+ </CardContent>
168
+ </Card>
169
+
142
170
  <Card>
143
171
  <CardHeader>
144
172
  <CardTitle level={2}>配送ルート進捗</CardTitle>
@@ -10,6 +10,7 @@ import {
10
10
  EmptyState,
11
11
  ScrollArea,
12
12
  Tree,
13
+ type TreeDropInfoProp,
13
14
  type TreeNodeProp,
14
15
  } from "@godxjp/ui/data-display";
15
16
  import { SearchInput } from "@godxjp/ui/data-entry";
@@ -246,6 +247,52 @@ function labelSuiteTree(
246
247
  });
247
248
  }
248
249
 
250
+ // ── card · draggable (gh#1093) ─────────────────────────────────────────────────────────────
251
+ const movablePagesSeed: TreeNodeProp[] = [
252
+ {
253
+ value: "guide",
254
+ label: "guide",
255
+ children: [
256
+ { value: "guide/start", label: "start" },
257
+ { value: "guide/setup", label: "setup" },
258
+ ],
259
+ },
260
+ { value: "faq", label: "faq" },
261
+ { value: "notes", label: "notes" },
262
+ ];
263
+
264
+ /** The seed's labels are catalogue keys (`treeDocs.drag.pages.*`), read in the reader's locale. */
265
+ function labelPages(
266
+ data: TreeNodeProp[],
267
+ t: (key: string) => string,
268
+ ): TreeNodeProp[] {
269
+ return data.map((node) => ({
270
+ ...node,
271
+ label: t(`treeDocs.drag.pages.${String(node.label)}`),
272
+ children: node.children ? labelPages(node.children, t) : undefined,
273
+ }));
274
+ }
275
+
276
+ /** What a consumer does in `onDrop`: take the node out, put it back where the drop says. */
277
+ function moveNode(data: TreeNodeProp[], info: TreeDropInfoProp): TreeNodeProp[] {
278
+ let moved: TreeNodeProp | undefined;
279
+ const without = (list: TreeNodeProp[]): TreeNodeProp[] =>
280
+ list
281
+ .filter((node) => (node.value === info.dragNode.value ? ((moved = node), false) : true))
282
+ .map((node) => (node.children ? { ...node, children: without(node.children) } : node));
283
+ const place = (list: TreeNodeProp[]): TreeNodeProp[] =>
284
+ list.flatMap((node) => {
285
+ if (node.value !== info.node.value) {
286
+ return node.children ? [{ ...node, children: place(node.children) }] : [node];
287
+ }
288
+ if (info.dropPosition === 0)
289
+ return [{ ...node, children: [...(node.children ?? []), moved!] }];
290
+ return info.dropPosition === -1 ? [moved!, node] : [node, moved!];
291
+ });
292
+ const rest = without(data);
293
+ return moved ? place(rest) : data;
294
+ }
295
+
249
296
  export default function Demo() {
250
297
  const { t } = useTranslation();
251
298
  // Card 1 — checks are controlled so the readout beside the tree can never disagree with it.
@@ -253,6 +300,8 @@ export default function Demo() {
253
300
  // Card 2 — selection is controlled and drives the detail pane.
254
301
  const [openFile, setOpenFile] = React.useState<string | undefined>("src/components/tree.tsx");
255
302
  // The page index — the tree IS the navigation, so selection is what the reading pane follows.
303
+ const [movablePages, setMovablePages] = React.useState(movablePagesSeed);
304
+ const [lastMove, setLastMove] = React.useState("");
256
305
  const [openPage, setOpenPage] = React.useState<string | undefined>("handbook/security/passwords");
257
306
  // Card 3 — async children land in state, exactly as a real fetch would.
258
307
  const [departments, setDepartments] = React.useState<Department[]>(initialDepartments);
@@ -452,6 +501,35 @@ export default function Demo() {
452
501
  </CardContent>
453
502
  </Card>
454
503
 
504
+ <Card>
505
+ <CardHeader>
506
+ <CardTitle level={2}>{t("treeDocs.drag.title")}</CardTitle>
507
+ <CardDescription>{t("treeDocs.drag.description")}</CardDescription>
508
+ </CardHeader>
509
+ <CardContent>
510
+ <Flex direction="col" gap="sm">
511
+ <Tree
512
+ aria-label={t("treeDocs.drag.treeLabel")}
513
+ treeData={labelPages(movablePages, t)}
514
+ defaultExpandAll
515
+ draggable
516
+ allowDrop={({ dropNode, dropPosition }) =>
517
+ !(dropNode.value === "notes" && dropPosition === 0)
518
+ }
519
+ onDrop={(info) => {
520
+ setMovablePages((current) => moveNode(current, info));
521
+ setLastMove(
522
+ `${String(info.dragNode.label)} → ${String(info.node.label)}(dropPosition ${info.dropPosition}, dropToGap ${info.dropToGap})`,
523
+ );
524
+ }}
525
+ />
526
+ <Text size="sm" tone="muted">
527
+ {t("treeDocs.drag.lastMove", { move: lastMove || t("treeDocs.drag.none") })}
528
+ </Text>
529
+ </Flex>
530
+ </CardContent>
531
+ </Card>
532
+
455
533
  <Card>
456
534
  <CardHeader>
457
535
  <CardTitle level={2}>組織ツリー(loadData · fieldNames · titleRender)</CardTitle>
@@ -632,6 +710,10 @@ export default function Demo() {
632
710
  },
633
711
  { label: "*", children: "同じ階層の兄弟をすべて展開" },
634
712
  { label: "文字キー", children: "先頭一致で次のノードへジャンプ(type-ahead)" },
713
+ {
714
+ label: "Alt + ↑ / ↓ / ← / →",
715
+ children: t("treeDocs.drag.keys"),
716
+ },
635
717
  ]}
636
718
  />
637
719
  </CardContent>
@@ -1507,6 +1507,21 @@
1507
1507
  "one": "Load {count} more",
1508
1508
  "other": "Load {count} more"
1509
1509
  }
1510
+ },
1511
+ "drag": {
1512
+ "title": "Drag to reorder or reparent (draggable · allowDrop · onDrop)",
1513
+ "description": "Drop on the upper quarter of a row to land before it, the lower quarter after it, the middle inside it. Tree never edits treeData; onDrop reports the move. From the keyboard: Alt+↑ / Alt+↓ move past a sibling, Alt+← outdents, Alt+→ indents into the previous sibling. Every move is announced in a live region. Here allowDrop refuses dropping inside “Release notes”.",
1514
+ "treeLabel": "Reorderable pages",
1515
+ "lastMove": "Last move: {move}",
1516
+ "none": "none",
1517
+ "keys": "When draggable: past a sibling / outdent / indent",
1518
+ "pages": {
1519
+ "guide": "User guide",
1520
+ "start": "Getting started",
1521
+ "setup": "Setup",
1522
+ "faq": "FAQ",
1523
+ "notes": "Release notes"
1524
+ }
1510
1525
  }
1511
1526
  },
1512
1527
  "progressDocs": {
@@ -1525,5 +1540,14 @@
1525
1540
  "home": "Home",
1526
1541
  "longSuite": "Q3 integration test · payment module · invoice re-issue scenarios",
1527
1542
  "longSection": "Acceptance criteria review for the release candidate of September 2026"
1543
+ },
1544
+ "timelineDocs": {
1545
+ "log": {
1546
+ "title": "status=\"log\" (activity log)",
1547
+ "description": "For a record of things that already happened, such as an issue history, use status=\"log\": a neutral dot and hairline rail, and no “Completed:” / “Upcoming:” prefix read out (antd's gray item).",
1548
+ "status": "Tanaka changed the status Open → In progress",
1549
+ "comment": "Nguyen commented",
1550
+ "assignee": "Tanaka changed the assignee"
1551
+ }
1528
1552
  }
1529
1553
  }
@@ -1500,6 +1500,21 @@
1500
1500
  "loadMore": {
1501
1501
  "other": "さらに {count} 件を読み込む"
1502
1502
  }
1503
+ },
1504
+ "drag": {
1505
+ "title": "ドラッグで並べ替え・親を変更(draggable · allowDrop · onDrop)",
1506
+ "description": "行の上 1/4 に落とすと前へ、下 1/4 で後ろへ、中央でその中(子)へ移動します。Tree は treeData を書き換えず、onDrop で移動を知らせるだけです。キーボードでは Alt+↑ / Alt+↓ で兄弟の前後へ、Alt+← で階層を上げ、Alt+→ で直前の兄弟の中へ。移動は毎回ライブリージョンで読み上げられます。ここでは allowDrop で「リリースノート」の中へは落とせません。",
1507
+ "treeLabel": "並べ替えできるページ一覧",
1508
+ "lastMove": "最後の移動: {move}",
1509
+ "none": "なし",
1510
+ "keys": "draggable のとき:兄弟の前後へ/階層を上げる/階層を下げる",
1511
+ "pages": {
1512
+ "guide": "利用ガイド",
1513
+ "start": "はじめに",
1514
+ "setup": "初期設定",
1515
+ "faq": "よくある質問",
1516
+ "notes": "リリースノート"
1517
+ }
1503
1518
  }
1504
1519
  },
1505
1520
  "progressDocs": {
@@ -1518,5 +1533,14 @@
1518
1533
  "home": "ホーム",
1519
1534
  "longSuite": "第3四半期 結合テスト 決済モジュール 請求書再発行シナリオ",
1520
1535
  "longSection": "2026年9月 リリース候補の受入基準レビュー"
1536
+ },
1537
+ "timelineDocs": {
1538
+ "log": {
1539
+ "title": "status=\"log\"(アクティビティログ)",
1540
+ "description": "課題の履歴のように、すべて起きたことの記録には status=\"log\"。中立の点と細い線で描き、「完了:」「予定:」のような読み上げ接頭辞を付けません(antd の gray item と同じ)。",
1541
+ "status": "田中 がステータスを変更 未対応 → 処理中",
1542
+ "comment": "グエン がコメント",
1543
+ "assignee": "田中 が担当者を変更"
1544
+ }
1521
1545
  }
1522
1546
  }
@@ -1500,6 +1500,21 @@
1500
1500
  "loadMore": {
1501
1501
  "other": "Tải thêm {count} ca"
1502
1502
  }
1503
+ },
1504
+ "drag": {
1505
+ "title": "Kéo để sắp xếp hoặc đổi cha (draggable · allowDrop · onDrop)",
1506
+ "description": "Thả vào 1/4 trên của dòng để đặt trước nó, 1/4 dưới để đặt sau, phần giữa để đặt vào trong. Tree không sửa treeData; onDrop báo nước đi. Bàn phím: Alt+↑ / Alt+↓ qua mục ngang hàng, Alt+← lùi cấp, Alt+→ thụt vào mục liền trước. Mỗi lần di chuyển được đọc qua live region. Ở đây allowDrop không cho thả vào trong “Ghi chú phát hành”.",
1507
+ "treeLabel": "Danh sách trang sắp xếp được",
1508
+ "lastMove": "Lần di chuyển cuối: {move}",
1509
+ "none": "chưa có",
1510
+ "keys": "Khi draggable: qua mục ngang hàng / lùi cấp / thụt cấp",
1511
+ "pages": {
1512
+ "guide": "Hướng dẫn sử dụng",
1513
+ "start": "Bắt đầu",
1514
+ "setup": "Cài đặt",
1515
+ "faq": "Câu hỏi thường gặp",
1516
+ "notes": "Ghi chú phát hành"
1517
+ }
1503
1518
  }
1504
1519
  },
1505
1520
  "progressDocs": {
@@ -1518,5 +1533,14 @@
1518
1533
  "home": "Trang chủ",
1519
1534
  "longSuite": "Kiểm thử tích hợp quý 3 · mô-đun thanh toán · kịch bản xuất lại hoá đơn",
1520
1535
  "longSection": "Rà soát tiêu chí nghiệm thu cho bản ứng viên tháng 9/2026"
1536
+ },
1537
+ "timelineDocs": {
1538
+ "log": {
1539
+ "title": "status=\"log\" (nhật ký hoạt động)",
1540
+ "description": "Với bản ghi những việc đã xảy ra như lịch sử issue, dùng status=\"log\": chấm trung tính, đường mảnh, không đọc tiền tố “Hoàn thành:” / “Sắp tới:” (giống item gray của antd).",
1541
+ "status": "Tanaka đổi trạng thái Mở → Đang xử lý",
1542
+ "comment": "Nguyễn đã bình luận",
1543
+ "assignee": "Tanaka đổi người phụ trách"
1544
+ }
1521
1545
  }
1522
1546
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "31.13.0",
4
- "godxUiMcp": "31.13.0",
3
+ "version": "31.15.0",
4
+ "godxUiMcp": "31.15.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",