@godxjp/ui 31.15.0 → 31.17.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/agent/START-HERE.md +5 -5
- package/agent/components/Badge.json +2 -2
- package/agent/components/DataTable.json +3 -3
- package/agent/components/Table.json +1 -1
- package/agent/components/Text.json +1 -1
- package/agent/components/TextDiff.json +86 -0
- package/agent/components-index.json +6 -1
- package/agent/components.json +93 -7
- package/agent/index.json +6 -6
- package/agent/llms.txt +6 -6
- package/agent/tokens.json +48 -0
- package/dist/components/data-display/badge.js +17 -1
- package/dist/components/data-display/data-table.js +1 -1
- package/dist/components/data-display/image.js +6 -1
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +4 -0
- package/dist/components/data-display/table.js +1 -1
- package/dist/components/data-display/text-diff.d.ts +45 -0
- package/dist/components/data-display/text-diff.js +240 -0
- package/dist/components/general/typography.js +3 -3
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +12 -0
- package/dist/i18n/messages/ja.json +9 -0
- package/dist/i18n/messages/vi.json +9 -0
- package/dist/props/components/data-display.prop.d.ts +43 -0
- package/dist/props/registry.d.ts +46 -1
- package/dist/props/registry.js +58 -1
- package/dist/props/vocabulary/data.prop.d.ts +6 -0
- package/dist/props/vocabulary/interaction.prop.d.ts +1 -0
- package/dist/styles/badge-layout.css +12 -0
- package/dist/styles/data-display-layout.css +52 -0
- package/dist/styles/layers.json +6 -1
- package/dist/styles/navigation-layout.css +3 -0
- package/dist/styles/table-layout.css +33 -0
- package/dist/styles/text-layout.css +4 -0
- package/dist/tokens/components/data-display.css +8 -0
- package/dist/tokens/components/navigation.css +2 -0
- package/dist/tokens/components/table.css +4 -0
- package/docs/FRAME-COVERAGE-REPORT.md +3 -2
- package/docs/data-display/badge.tsx +15 -0
- package/docs/data-display/text-diff.tsx +89 -0
- package/docs/i18n/messages/en.json +24 -0
- package/docs/i18n/messages/ja.json +24 -0
- package/docs/i18n/messages/vi.json +24 -0
- package/package.json +2 -2
|
@@ -31,6 +31,8 @@ export { EmptyState } from "./empty-state.js";
|
|
|
31
31
|
export { Progress } from "./progress.js";
|
|
32
32
|
export { CodeBlock } from "./code-block.js";
|
|
33
33
|
export type { CodeBlockProp, CodeBlockProps } from "./code-block.js";
|
|
34
|
+
export { TextDiff, diffText, tokenizeText } from "./text-diff.js";
|
|
35
|
+
export type { TextDiffGranularity, TextDiffProp, TextDiffProps, TextDiffSegment, } from "./text-diff.js";
|
|
34
36
|
export { Prose } from "./prose.js";
|
|
35
37
|
export type { ProseProp, ProseProps } from "./prose.js";
|
|
36
38
|
export type { ProgressProps, ProgressSegment, ProgressTone } from "./progress.js";
|
|
@@ -31,6 +31,7 @@ import { DataTable, flexRender } from "./data-table.js";
|
|
|
31
31
|
import { EmptyState } from "./empty-state.js";
|
|
32
32
|
import { Progress } from "./progress.js";
|
|
33
33
|
import { CodeBlock } from "./code-block.js";
|
|
34
|
+
import { TextDiff, diffText, tokenizeText } from "./text-diff.js";
|
|
34
35
|
import { Prose } from "./prose.js";
|
|
35
36
|
import { Timeline } from "./timeline.js";
|
|
36
37
|
import { TimelineGrid } from "./timeline-grid.js";
|
|
@@ -134,6 +135,7 @@ export {
|
|
|
134
135
|
TableHead,
|
|
135
136
|
TableHeader,
|
|
136
137
|
TableRow,
|
|
138
|
+
TextDiff,
|
|
137
139
|
ThoughtChain,
|
|
138
140
|
ThoughtChainItem,
|
|
139
141
|
Thumbnail,
|
|
@@ -141,6 +143,8 @@ export {
|
|
|
141
143
|
TimelineGrid,
|
|
142
144
|
Tree,
|
|
143
145
|
Welcome,
|
|
146
|
+
diffText,
|
|
144
147
|
flexRender,
|
|
148
|
+
tokenizeText,
|
|
145
149
|
useCarousel
|
|
146
150
|
};
|
|
@@ -247,7 +247,7 @@ const TableCell = React.forwardRef(
|
|
|
247
247
|
},
|
|
248
248
|
...props,
|
|
249
249
|
children: [
|
|
250
|
-
label !== void 0 ? /* @__PURE__ */ jsx("span", { className: "ui-table-stacked-collection-label", "aria-hidden": "true", children: label }) : null,
|
|
250
|
+
label !== void 0 && children != null && children !== false && children !== "" ? /* @__PURE__ */ jsx("span", { className: "ui-table-stacked-collection-label", "aria-hidden": "true", children: label }) : null,
|
|
251
251
|
priority === "actions" && preset === "action-collection" ? /* @__PURE__ */ jsx("span", { className: "ui-table-actions-content", children }) : children
|
|
252
252
|
]
|
|
253
253
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { TextDiffProp, TextDiffSegment } from "../../props/components/data-display.prop.js";
|
|
3
|
+
export type { TextDiffProp, TextDiffProp as TextDiffProps, TextDiffSegment, } from "../../props/components/data-display.prop.js";
|
|
4
|
+
/** The unit a change is measured in — see `TextDiffProp["granularity"]`. */
|
|
5
|
+
export type TextDiffGranularity = NonNullable<TextDiffProp["granularity"]>;
|
|
6
|
+
/**
|
|
7
|
+
* Cut a text into diff tokens. `line` keeps each line with its newline; `char` is one grapheme
|
|
8
|
+
* cluster per token (an emoji or a combining mark stays whole); `word` is `Intl.Segmenter` word
|
|
9
|
+
* segmentation in `lang`; `auto` is `word`, except that a CJK run is cut per character, because a
|
|
10
|
+
* dictionary word boundary in Japanese moves when one particle changes. A newline is always its
|
|
11
|
+
* own token so a changed line never swallows the line after it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function tokenizeText(text: string, granularity?: TextDiffGranularity, lang?: string): string[];
|
|
14
|
+
/**
|
|
15
|
+
* The diff `TextDiff` renders, for a caller that needs the runs themselves (a count of changes, a
|
|
16
|
+
* plain-text export). A longest-common-subsequence over {@link tokenizeText} tokens, with the
|
|
17
|
+
* common prefix and suffix trimmed first; adjacent runs of one kind are merged, and a removal is
|
|
18
|
+
* always emitted before the addition that replaces it. Past `maxCells` LCS cells the changed
|
|
19
|
+
* middle is one removal plus one addition — a bounded cost instead of a frozen tab.
|
|
20
|
+
*/
|
|
21
|
+
export declare function diffText(before: string, after: string, options?: {
|
|
22
|
+
granularity?: TextDiffGranularity;
|
|
23
|
+
lang?: string;
|
|
24
|
+
maxCells?: number;
|
|
25
|
+
}): TextDiffSegment[];
|
|
26
|
+
/**
|
|
27
|
+
* TextDiff — what changed between two versions of a text (gh#1096): removed runs in `<del>`,
|
|
28
|
+
* added runs in `<ins>`, each with a struck-through or underlined mark AND a spoken start/end
|
|
29
|
+
* label, so the change is never carried by colour alone. Inline (one flow, GitHub's "unified")
|
|
30
|
+
* or split (before | after side by side); per line, per word, per character, or `auto` (words,
|
|
31
|
+
* with CJK cut per character). Long unchanged runs fold behind a button that reveals them.
|
|
32
|
+
*/
|
|
33
|
+
export declare const TextDiff: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
34
|
+
before: string;
|
|
35
|
+
after: string;
|
|
36
|
+
mode?: "inline" | "split";
|
|
37
|
+
granularity?: "auto" | "word" | "char" | "line";
|
|
38
|
+
collapseUnchanged?: boolean;
|
|
39
|
+
contextLines?: number;
|
|
40
|
+
maxCells?: number;
|
|
41
|
+
beforeLabel?: React.ReactNode;
|
|
42
|
+
afterLabel?: React.ReactNode;
|
|
43
|
+
lang?: string;
|
|
44
|
+
className?: import("../../props/index.js").ClassNameProp;
|
|
45
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
5
|
+
import { cn } from "../../lib/utils.js";
|
|
6
|
+
import { Button } from "../general/button.js";
|
|
7
|
+
import { VisuallyHidden } from "../general/visually-hidden.js";
|
|
8
|
+
const CJK = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
9
|
+
const DEFAULT_MAX_CELLS = 4e6;
|
|
10
|
+
const MIN_HIDDEN_LINES = 2;
|
|
11
|
+
function segmenter(lang, granularity) {
|
|
12
|
+
if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") return null;
|
|
13
|
+
try {
|
|
14
|
+
return new Intl.Segmenter(lang, { granularity });
|
|
15
|
+
} catch {
|
|
16
|
+
return new Intl.Segmenter(void 0, { granularity });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function graphemes(text, lang) {
|
|
20
|
+
const s = segmenter(lang, "grapheme");
|
|
21
|
+
return s ? Array.from(s.segment(text), (part) => part.segment) : Array.from(text);
|
|
22
|
+
}
|
|
23
|
+
function tokenizeText(text, granularity = "auto", lang) {
|
|
24
|
+
if (text === "") return [];
|
|
25
|
+
if (granularity === "line") return text.match(/[^\n]*\n|[^\n]+/g) ?? [];
|
|
26
|
+
if (granularity === "char") return graphemes(text, lang);
|
|
27
|
+
const s = segmenter(lang, "word");
|
|
28
|
+
const words = s ? Array.from(s.segment(text), (part) => part.segment) : text.match(/\s+|[^\s]+/g) ?? [];
|
|
29
|
+
const tokens = [];
|
|
30
|
+
for (const word of words) {
|
|
31
|
+
const pieces = word.length > 1 && word.includes("\n") ? word.split(/(\n)/) : [word];
|
|
32
|
+
for (const piece of pieces) {
|
|
33
|
+
if (piece === "") continue;
|
|
34
|
+
if (granularity === "auto" && CJK.test(piece)) tokens.push(...graphemes(piece, lang));
|
|
35
|
+
else tokens.push(piece);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return tokens;
|
|
39
|
+
}
|
|
40
|
+
function push(segments, kind, text) {
|
|
41
|
+
if (text === "") return;
|
|
42
|
+
const last = segments[segments.length - 1];
|
|
43
|
+
if (last !== void 0 && last.kind === kind) last.text += text;
|
|
44
|
+
else segments.push({ kind, text });
|
|
45
|
+
}
|
|
46
|
+
function diffText(before, after, options = {}) {
|
|
47
|
+
const { granularity = "auto", lang, maxCells = DEFAULT_MAX_CELLS } = options;
|
|
48
|
+
const a = tokenizeText(before, granularity, lang);
|
|
49
|
+
const b = tokenizeText(after, granularity, lang);
|
|
50
|
+
let start = 0;
|
|
51
|
+
while (start < a.length && start < b.length && a[start] === b[start]) start++;
|
|
52
|
+
let endA = a.length;
|
|
53
|
+
let endB = b.length;
|
|
54
|
+
while (endA > start && endB > start && a[endA - 1] === b[endB - 1]) {
|
|
55
|
+
endA--;
|
|
56
|
+
endB--;
|
|
57
|
+
}
|
|
58
|
+
const segments = [];
|
|
59
|
+
push(segments, "same", a.slice(0, start).join(""));
|
|
60
|
+
const midA = a.slice(start, endA);
|
|
61
|
+
const midB = b.slice(start, endB);
|
|
62
|
+
if ((midA.length + 1) * (midB.length + 1) > maxCells) {
|
|
63
|
+
push(segments, "removed", midA.join(""));
|
|
64
|
+
push(segments, "added", midB.join(""));
|
|
65
|
+
} else {
|
|
66
|
+
const width = midB.length + 1;
|
|
67
|
+
const lengths = new Uint32Array((midA.length + 1) * width);
|
|
68
|
+
for (let i2 = midA.length - 1; i2 >= 0; i2--) {
|
|
69
|
+
for (let j2 = midB.length - 1; j2 >= 0; j2--) {
|
|
70
|
+
lengths[i2 * width + j2] = midA[i2] === midB[j2] ? lengths[(i2 + 1) * width + j2 + 1] + 1 : Math.max(lengths[(i2 + 1) * width + j2], lengths[i2 * width + j2 + 1]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
let i = 0;
|
|
74
|
+
let j = 0;
|
|
75
|
+
while (i < midA.length || j < midB.length) {
|
|
76
|
+
if (i < midA.length && j < midB.length && midA[i] === midB[j]) {
|
|
77
|
+
push(segments, "same", midA[i]);
|
|
78
|
+
i++;
|
|
79
|
+
j++;
|
|
80
|
+
} else if (i < midA.length && (j === midB.length || lengths[(i + 1) * width + j] >= lengths[i * width + j + 1])) {
|
|
81
|
+
push(segments, "removed", midA[i]);
|
|
82
|
+
i++;
|
|
83
|
+
} else {
|
|
84
|
+
push(segments, "added", midB[j]);
|
|
85
|
+
j++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
push(segments, "same", a.slice(endA).join(""));
|
|
90
|
+
return segments;
|
|
91
|
+
}
|
|
92
|
+
function toLines(segments) {
|
|
93
|
+
const lines = [];
|
|
94
|
+
let current = [];
|
|
95
|
+
for (const segment of segments) {
|
|
96
|
+
for (const piece of segment.text.split(/(?<=\n)/)) {
|
|
97
|
+
if (piece === "") continue;
|
|
98
|
+
current.push({ kind: segment.kind, text: piece });
|
|
99
|
+
if (piece.endsWith("\n")) {
|
|
100
|
+
lines.push(current);
|
|
101
|
+
current = [];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (current.length > 0) lines.push(current);
|
|
106
|
+
return lines;
|
|
107
|
+
}
|
|
108
|
+
function toItems(lines, collapse, context) {
|
|
109
|
+
const runs = [];
|
|
110
|
+
for (const line of lines) {
|
|
111
|
+
const changed = line.some((piece) => piece.kind !== "same");
|
|
112
|
+
const last = runs[runs.length - 1];
|
|
113
|
+
if (last !== void 0 && last.changed === changed) last.lines.push(line);
|
|
114
|
+
else runs.push({ changed, lines: [line] });
|
|
115
|
+
}
|
|
116
|
+
const anyChange = runs.some((run) => run.changed);
|
|
117
|
+
const items = [];
|
|
118
|
+
runs.forEach((run, index) => {
|
|
119
|
+
if (run.changed || !collapse || !anyChange) {
|
|
120
|
+
items.push({ type: "lines", ...run });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const head = index === 0 ? 0 : context;
|
|
124
|
+
const tail = index === runs.length - 1 ? 0 : context;
|
|
125
|
+
const hidden = run.lines.length - head - tail;
|
|
126
|
+
if (hidden < MIN_HIDDEN_LINES) {
|
|
127
|
+
items.push({ type: "lines", ...run });
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (head > 0) items.push({ type: "lines", changed: false, lines: run.lines.slice(0, head) });
|
|
131
|
+
items.push({ type: "gap", id: index, lines: run.lines.slice(head, head + hidden) });
|
|
132
|
+
if (tail > 0) {
|
|
133
|
+
items.push({ type: "lines", changed: false, lines: run.lines.slice(head + hidden) });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return items;
|
|
137
|
+
}
|
|
138
|
+
function Runs({ lines, side }) {
|
|
139
|
+
const { t } = useTranslation();
|
|
140
|
+
const runs = [];
|
|
141
|
+
for (const piece of lines.flat()) {
|
|
142
|
+
if (side === "before" && piece.kind === "added") continue;
|
|
143
|
+
if (side === "after" && piece.kind === "removed") continue;
|
|
144
|
+
push(runs, piece.kind, piece.text);
|
|
145
|
+
}
|
|
146
|
+
return /* @__PURE__ */ jsx(Fragment, { children: runs.map(
|
|
147
|
+
(run, index) => run.kind === "same" ? /* @__PURE__ */ jsx(React.Fragment, { children: run.text }, index) : run.kind === "removed" ? /* @__PURE__ */ jsxs("del", { className: "ui-text-diff-removed", children: [
|
|
148
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: t("ui.textDiff.removedStart") }),
|
|
149
|
+
run.text,
|
|
150
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: t("ui.textDiff.removedEnd") })
|
|
151
|
+
] }, index) : /* @__PURE__ */ jsxs("ins", { className: "ui-text-diff-added", children: [
|
|
152
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: t("ui.textDiff.addedStart") }),
|
|
153
|
+
run.text,
|
|
154
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: t("ui.textDiff.addedEnd") })
|
|
155
|
+
] }, index)
|
|
156
|
+
) });
|
|
157
|
+
}
|
|
158
|
+
const TextDiff = React.forwardRef(function TextDiff2({
|
|
159
|
+
before,
|
|
160
|
+
after,
|
|
161
|
+
mode = "inline",
|
|
162
|
+
granularity = "auto",
|
|
163
|
+
collapseUnchanged = true,
|
|
164
|
+
contextLines = 2,
|
|
165
|
+
maxCells,
|
|
166
|
+
beforeLabel,
|
|
167
|
+
afterLabel,
|
|
168
|
+
lang,
|
|
169
|
+
className,
|
|
170
|
+
...props
|
|
171
|
+
}, ref) {
|
|
172
|
+
const { t } = useTranslation();
|
|
173
|
+
const items = React.useMemo(
|
|
174
|
+
() => toItems(
|
|
175
|
+
toLines(diffText(before, after, { granularity, lang, maxCells })),
|
|
176
|
+
collapseUnchanged,
|
|
177
|
+
Math.max(0, contextLines)
|
|
178
|
+
),
|
|
179
|
+
[before, after, granularity, lang, maxCells, collapseUnchanged, contextLines]
|
|
180
|
+
);
|
|
181
|
+
const [expanded, setExpanded] = React.useState(() => /* @__PURE__ */ new Set());
|
|
182
|
+
React.useEffect(() => setExpanded(/* @__PURE__ */ new Set()), [items]);
|
|
183
|
+
const gap = (item, split) => /* @__PURE__ */ jsx("div", { className: "ui-text-diff-gap", "data-span": split || void 0, children: /* @__PURE__ */ jsx(
|
|
184
|
+
Button,
|
|
185
|
+
{
|
|
186
|
+
type: "button",
|
|
187
|
+
variant: "ghost",
|
|
188
|
+
size: "xs",
|
|
189
|
+
onClick: () => setExpanded((previous) => new Set(previous).add(item.id)),
|
|
190
|
+
children: t("ui.textDiff.showUnchanged", { count: item.lines.length })
|
|
191
|
+
}
|
|
192
|
+
) }, `gap-${item.id}`);
|
|
193
|
+
const resolved = items.map(
|
|
194
|
+
(item) => item.type === "gap" && expanded.has(item.id) ? { type: "lines", changed: false, lines: item.lines } : item
|
|
195
|
+
);
|
|
196
|
+
return /* @__PURE__ */ jsx(
|
|
197
|
+
"div",
|
|
198
|
+
{
|
|
199
|
+
ref,
|
|
200
|
+
"data-slot": "text-diff",
|
|
201
|
+
"data-mode": mode,
|
|
202
|
+
lang,
|
|
203
|
+
className: cn("ui-text-diff", className),
|
|
204
|
+
...props,
|
|
205
|
+
children: mode === "split" ? /* @__PURE__ */ jsxs("div", { className: "ui-text-diff-split", children: [
|
|
206
|
+
/* @__PURE__ */ jsx("div", { className: "ui-text-diff-heading", children: beforeLabel ?? t("ui.textDiff.before") }),
|
|
207
|
+
/* @__PURE__ */ jsx("div", { className: "ui-text-diff-heading", children: afterLabel ?? t("ui.textDiff.after") }),
|
|
208
|
+
resolved.map(
|
|
209
|
+
(item, index) => item.type === "gap" ? gap(item, true) : /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
210
|
+
/* @__PURE__ */ jsx(
|
|
211
|
+
"div",
|
|
212
|
+
{
|
|
213
|
+
className: "ui-text-diff-cell",
|
|
214
|
+
"data-side": "before",
|
|
215
|
+
"data-changed": item.changed || void 0,
|
|
216
|
+
children: /* @__PURE__ */ jsx(Runs, { lines: item.lines, side: "before" })
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
/* @__PURE__ */ jsx(
|
|
220
|
+
"div",
|
|
221
|
+
{
|
|
222
|
+
className: "ui-text-diff-cell",
|
|
223
|
+
"data-side": "after",
|
|
224
|
+
"data-changed": item.changed || void 0,
|
|
225
|
+
children: /* @__PURE__ */ jsx(Runs, { lines: item.lines, side: "after" })
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
] }, index)
|
|
229
|
+
)
|
|
230
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "ui-text-diff-body", children: resolved.map(
|
|
231
|
+
(item, index) => item.type === "gap" ? gap(item, false) : /* @__PURE__ */ jsx(Runs, { lines: item.lines, side: "both" }, index)
|
|
232
|
+
) })
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
export {
|
|
237
|
+
TextDiff,
|
|
238
|
+
diffText,
|
|
239
|
+
tokenizeText
|
|
240
|
+
};
|
|
@@ -469,9 +469,9 @@ const TextBase = React.forwardRef((props, ref) => {
|
|
|
469
469
|
// always been, on the element itself.
|
|
470
470
|
"data-truncate": truncating && !block.wrapsContent ? "" : void 0,
|
|
471
471
|
"data-clamp": effectiveClamp !== void 0 && !block.wrapsContent ? "" : void 0,
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
"data-whitespace": whitespace
|
|
472
|
+
// Unset emits nothing. An EXPLICIT `normal` is emitted (gh#1083): it releases a `nowrap` the
|
|
473
|
+
// Text inherits, e.g. from a table cell. `truncate` still wins by emitting nothing.
|
|
474
|
+
"data-whitespace": whitespace && !truncating ? whitespace : void 0,
|
|
475
475
|
// Same shape and the same precedence as `whitespace`: `normal` is inert, and one line has
|
|
476
476
|
// nowhere to break to, so `truncate` simply does not emit it (gh#927).
|
|
477
477
|
"data-break": breakMode === "anywhere" && !truncating ? "anywhere" : void 0,
|
|
@@ -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": "31.
|
|
3
|
+
"version": "31.17.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,
|
|
@@ -457,6 +457,18 @@
|
|
|
457
457
|
"copy": "Copy code",
|
|
458
458
|
"copied": "Copied",
|
|
459
459
|
"copiedAnnounce": "Code copied to clipboard"
|
|
460
|
+
},
|
|
461
|
+
"textDiff": {
|
|
462
|
+
"before": "Before",
|
|
463
|
+
"after": "After",
|
|
464
|
+
"removedStart": "[removed: ",
|
|
465
|
+
"removedEnd": "]",
|
|
466
|
+
"addedStart": "[added: ",
|
|
467
|
+
"addedEnd": "]",
|
|
468
|
+
"showUnchanged": {
|
|
469
|
+
"one": "Show {count} unchanged line",
|
|
470
|
+
"other": "Show {count} unchanged lines"
|
|
471
|
+
}
|
|
460
472
|
}
|
|
461
473
|
},
|
|
462
474
|
"navigation": {
|
|
@@ -450,6 +450,15 @@
|
|
|
450
450
|
"copy": "コードをコピー",
|
|
451
451
|
"copied": "コピーしました",
|
|
452
452
|
"copiedAnnounce": "コードをクリップボードにコピーしました"
|
|
453
|
+
},
|
|
454
|
+
"textDiff": {
|
|
455
|
+
"before": "変更前",
|
|
456
|
+
"after": "変更後",
|
|
457
|
+
"removedStart": "[削除: ",
|
|
458
|
+
"removedEnd": "]",
|
|
459
|
+
"addedStart": "[追加: ",
|
|
460
|
+
"addedEnd": "]",
|
|
461
|
+
"showUnchanged": "変更のない{count}行を表示"
|
|
453
462
|
}
|
|
454
463
|
},
|
|
455
464
|
"navigation": {
|
|
@@ -451,6 +451,15 @@
|
|
|
451
451
|
"copy": "Sao chép mã",
|
|
452
452
|
"copied": "Đã sao chép",
|
|
453
453
|
"copiedAnnounce": "Đã sao chép mã vào bộ nhớ tạm"
|
|
454
|
+
},
|
|
455
|
+
"textDiff": {
|
|
456
|
+
"before": "Trước",
|
|
457
|
+
"after": "Sau",
|
|
458
|
+
"removedStart": "[đã xoá: ",
|
|
459
|
+
"removedEnd": "]",
|
|
460
|
+
"addedStart": "[đã thêm: ",
|
|
461
|
+
"addedEnd": "]",
|
|
462
|
+
"showUnchanged": "Hiện {count} dòng không đổi"
|
|
454
463
|
}
|
|
455
464
|
},
|
|
456
465
|
"navigation": {
|
|
@@ -1334,3 +1334,46 @@ export type ImagePreviewGroupProp = {
|
|
|
1334
1334
|
preview?: boolean | ImagePreviewGroupConfigProp;
|
|
1335
1335
|
children?: React.ReactNode;
|
|
1336
1336
|
};
|
|
1337
|
+
/** @see diffText — one run of a diff: kept, taken out of `before`, or put into `after`. */
|
|
1338
|
+
export type TextDiffSegment = {
|
|
1339
|
+
kind: "same" | "removed" | "added";
|
|
1340
|
+
text: string;
|
|
1341
|
+
};
|
|
1342
|
+
/**
|
|
1343
|
+
* @see TextDiff — what changed between two versions of a text (gh#1096).
|
|
1344
|
+
*
|
|
1345
|
+
* antd ships no diff, so the shape follows GitHub/GitLab: `<del>`/`<ins>` with a spoken start/end
|
|
1346
|
+
* label (never colour alone), inline ("unified") or split, unchanged runs folded around the
|
|
1347
|
+
* changes. Text keeps its newlines (`pre-wrap`) and a long token breaks anywhere.
|
|
1348
|
+
*/
|
|
1349
|
+
export type TextDiffProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
1350
|
+
/** The earlier text — what a translation was written against, the saved version. */
|
|
1351
|
+
before: string;
|
|
1352
|
+
/** The current text. */
|
|
1353
|
+
after: string;
|
|
1354
|
+
/** `inline` (default) — one flow; `split` — two columns, before | after, hunks aligned. */
|
|
1355
|
+
mode?: "inline" | "split";
|
|
1356
|
+
/**
|
|
1357
|
+
* The unit a change is measured in. `auto` (default) is words for alphabetic text and one
|
|
1358
|
+
* character per token for Han / Hiragana / Katakana / Hangul runs, which have no spaces to split
|
|
1359
|
+
* on. `word` is `Intl.Segmenter` word segmentation in `lang` (dictionary words for Japanese),
|
|
1360
|
+
* `char` is one grapheme cluster per token, `line` compares whole lines.
|
|
1361
|
+
*/
|
|
1362
|
+
granularity?: "auto" | "word" | "char" | "line";
|
|
1363
|
+
/** Fold long unchanged runs of lines behind a "show N unchanged lines" button. Default `true`. */
|
|
1364
|
+
collapseUnchanged?: boolean;
|
|
1365
|
+
/** Unchanged lines kept visible next to each change when folding. Default `2`. */
|
|
1366
|
+
contextLines?: number;
|
|
1367
|
+
/**
|
|
1368
|
+
* The LCS cost cap (tokens before × tokens after). Past it the changed middle is shown as one
|
|
1369
|
+
* removal and one addition instead of freezing the tab. Default `4_000_000`.
|
|
1370
|
+
*/
|
|
1371
|
+
maxCells?: number;
|
|
1372
|
+
/** Heading of the before column in `split` mode. Default: the localized "Before". */
|
|
1373
|
+
beforeLabel?: React.ReactNode;
|
|
1374
|
+
/** Heading of the after column in `split` mode. Default: the localized "After". */
|
|
1375
|
+
afterLabel?: React.ReactNode;
|
|
1376
|
+
/** BCP-47 language of the text — set on the element and used for `Intl.Segmenter`. */
|
|
1377
|
+
lang?: string;
|
|
1378
|
+
className?: ClassNameProp;
|
|
1379
|
+
};
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -322,7 +322,7 @@ export declare const VOCABULARY_REGISTRY: {
|
|
|
322
322
|
readonly TableExpandableProp: {
|
|
323
323
|
readonly file: "vocabulary/data.prop.ts";
|
|
324
324
|
readonly category: "data";
|
|
325
|
-
readonly description: "DataTable expandable-row config — expandedRowRender | rowExpandable | defaultExpandAllRows | expandedRowKeys | onExpandedRowsChange | expandRowByClick (antd/rc-table ExpandableConfig)";
|
|
325
|
+
readonly description: "DataTable expandable-row config — expandedRowRender | rowExpandable | defaultExpandAllRows | expandedRowKeys | onExpandedRowsChange | expandRowByClick | flush (antd/rc-table ExpandableConfig; the detail cell is padded by default, gh#1083)";
|
|
326
326
|
};
|
|
327
327
|
readonly TableSummaryProp: {
|
|
328
328
|
readonly file: "vocabulary/data.prop.ts";
|
|
@@ -2881,6 +2881,51 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2881
2881
|
readonly reason: "The sample's accessible NAME, which is what lets a read-only colour be shown with no visible label. Absent, the mark is aria-hidden (Legend's rule) — so colour is never the sole carrier either way.";
|
|
2882
2882
|
}];
|
|
2883
2883
|
};
|
|
2884
|
+
readonly TextDiffProp: {
|
|
2885
|
+
readonly group: "data-display";
|
|
2886
|
+
readonly file: "components/data-display.prop.ts";
|
|
2887
|
+
readonly vocabulary: readonly ["ClassNameProp", {
|
|
2888
|
+
readonly field: "before";
|
|
2889
|
+
readonly local: true;
|
|
2890
|
+
readonly reason: "The earlier version of the text being compared — DATA, the left side of a diff (gh#1096).";
|
|
2891
|
+
}, {
|
|
2892
|
+
readonly field: "after";
|
|
2893
|
+
readonly local: true;
|
|
2894
|
+
readonly reason: "The current version of the text being compared — DATA, the right side of a diff (gh#1096).";
|
|
2895
|
+
}, {
|
|
2896
|
+
readonly field: "mode";
|
|
2897
|
+
readonly local: true;
|
|
2898
|
+
readonly reason: "inline | split — GitHub's unified vs side-by-side diff layout; not a size or tone axis.";
|
|
2899
|
+
}, {
|
|
2900
|
+
readonly field: "granularity";
|
|
2901
|
+
readonly local: true;
|
|
2902
|
+
readonly reason: "auto | word | char | line — the token unit, the same word Intl.Segmenter uses for it.";
|
|
2903
|
+
}, {
|
|
2904
|
+
readonly field: "collapseUnchanged";
|
|
2905
|
+
readonly local: true;
|
|
2906
|
+
readonly reason: "Positive boolean: fold long unchanged runs of lines behind a reveal button.";
|
|
2907
|
+
}, {
|
|
2908
|
+
readonly field: "contextLines";
|
|
2909
|
+
readonly local: true;
|
|
2910
|
+
readonly reason: "Unchanged lines kept beside each change when folding — git's -U<n> context.";
|
|
2911
|
+
}, {
|
|
2912
|
+
readonly field: "maxCells";
|
|
2913
|
+
readonly local: true;
|
|
2914
|
+
readonly reason: "Cost cap of the LCS table; past it the changed middle is one removal plus one addition.";
|
|
2915
|
+
}, {
|
|
2916
|
+
readonly field: "beforeLabel";
|
|
2917
|
+
readonly local: true;
|
|
2918
|
+
readonly reason: "Heading of the before column in split mode; defaults to the localized word.";
|
|
2919
|
+
}, {
|
|
2920
|
+
readonly field: "afterLabel";
|
|
2921
|
+
readonly local: true;
|
|
2922
|
+
readonly reason: "Heading of the after column in split mode; defaults to the localized word.";
|
|
2923
|
+
}, {
|
|
2924
|
+
readonly field: "lang";
|
|
2925
|
+
readonly local: true;
|
|
2926
|
+
readonly reason: "The HTML lang attribute, also the Intl.Segmenter locale for word/char segmentation.";
|
|
2927
|
+
}];
|
|
2928
|
+
};
|
|
2884
2929
|
readonly FeatureStateProp: {
|
|
2885
2930
|
readonly group: "data-display";
|
|
2886
2931
|
readonly file: "components/data-display.prop.ts";
|
package/dist/props/registry.js
CHANGED
|
@@ -320,7 +320,7 @@ const VOCABULARY_REGISTRY = {
|
|
|
320
320
|
TableExpandableProp: {
|
|
321
321
|
file: "vocabulary/data.prop.ts",
|
|
322
322
|
category: "data",
|
|
323
|
-
description: "DataTable expandable-row config \u2014 expandedRowRender | rowExpandable | defaultExpandAllRows | expandedRowKeys | onExpandedRowsChange | expandRowByClick (antd/rc-table ExpandableConfig)"
|
|
323
|
+
description: "DataTable expandable-row config \u2014 expandedRowRender | rowExpandable | defaultExpandAllRows | expandedRowKeys | onExpandedRowsChange | expandRowByClick | flush (antd/rc-table ExpandableConfig; the detail cell is padded by default, gh#1083)"
|
|
324
324
|
},
|
|
325
325
|
TableSummaryProp: {
|
|
326
326
|
file: "vocabulary/data.prop.ts",
|
|
@@ -3398,6 +3398,63 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
3398
3398
|
}
|
|
3399
3399
|
]
|
|
3400
3400
|
},
|
|
3401
|
+
TextDiffProp: {
|
|
3402
|
+
group: "data-display",
|
|
3403
|
+
file: "components/data-display.prop.ts",
|
|
3404
|
+
vocabulary: [
|
|
3405
|
+
"ClassNameProp",
|
|
3406
|
+
{
|
|
3407
|
+
field: "before",
|
|
3408
|
+
local: true,
|
|
3409
|
+
reason: "The earlier version of the text being compared \u2014 DATA, the left side of a diff (gh#1096)."
|
|
3410
|
+
},
|
|
3411
|
+
{
|
|
3412
|
+
field: "after",
|
|
3413
|
+
local: true,
|
|
3414
|
+
reason: "The current version of the text being compared \u2014 DATA, the right side of a diff (gh#1096)."
|
|
3415
|
+
},
|
|
3416
|
+
{
|
|
3417
|
+
field: "mode",
|
|
3418
|
+
local: true,
|
|
3419
|
+
reason: "inline | split \u2014 GitHub's unified vs side-by-side diff layout; not a size or tone axis."
|
|
3420
|
+
},
|
|
3421
|
+
{
|
|
3422
|
+
field: "granularity",
|
|
3423
|
+
local: true,
|
|
3424
|
+
reason: "auto | word | char | line \u2014 the token unit, the same word Intl.Segmenter uses for it."
|
|
3425
|
+
},
|
|
3426
|
+
{
|
|
3427
|
+
field: "collapseUnchanged",
|
|
3428
|
+
local: true,
|
|
3429
|
+
reason: "Positive boolean: fold long unchanged runs of lines behind a reveal button."
|
|
3430
|
+
},
|
|
3431
|
+
{
|
|
3432
|
+
field: "contextLines",
|
|
3433
|
+
local: true,
|
|
3434
|
+
reason: "Unchanged lines kept beside each change when folding \u2014 git's -U<n> context."
|
|
3435
|
+
},
|
|
3436
|
+
{
|
|
3437
|
+
field: "maxCells",
|
|
3438
|
+
local: true,
|
|
3439
|
+
reason: "Cost cap of the LCS table; past it the changed middle is one removal plus one addition."
|
|
3440
|
+
},
|
|
3441
|
+
{
|
|
3442
|
+
field: "beforeLabel",
|
|
3443
|
+
local: true,
|
|
3444
|
+
reason: "Heading of the before column in split mode; defaults to the localized word."
|
|
3445
|
+
},
|
|
3446
|
+
{
|
|
3447
|
+
field: "afterLabel",
|
|
3448
|
+
local: true,
|
|
3449
|
+
reason: "Heading of the after column in split mode; defaults to the localized word."
|
|
3450
|
+
},
|
|
3451
|
+
{
|
|
3452
|
+
field: "lang",
|
|
3453
|
+
local: true,
|
|
3454
|
+
reason: "The HTML lang attribute, also the Intl.Segmenter locale for word/char segmentation."
|
|
3455
|
+
}
|
|
3456
|
+
]
|
|
3457
|
+
},
|
|
3401
3458
|
FeatureStateProp: {
|
|
3402
3459
|
group: "data-display",
|
|
3403
3460
|
file: "components/data-display.prop.ts",
|
|
@@ -271,6 +271,12 @@ export type TableExpandableProp<T> = {
|
|
|
271
271
|
/** Clicking anywhere on the row toggles it (antd `expandRowByClick`). */
|
|
272
272
|
expandRowByClick?: boolean;
|
|
273
273
|
columnTitle?: React.ReactNode;
|
|
274
|
+
/**
|
|
275
|
+
* The detail reaches the cell edges: no inset (gh#1083). Off by default — the expanded cell is
|
|
276
|
+
* padded like any other cell (antd), retuned by `--table-row-expanded-padding`. Set it for a
|
|
277
|
+
* detail that owns its inset, such as a nested table or a `ListRow` list.
|
|
278
|
+
*/
|
|
279
|
+
flush?: FlushProp;
|
|
274
280
|
};
|
|
275
281
|
/** Footer totals row — receives the rows currently rendered (antd/rc-table `summary`). */
|
|
276
282
|
export type TableSummaryProp<T> = (rows: readonly T[]) => React.ReactNode;
|
|
@@ -90,6 +90,7 @@ export type TextAlignProp = "start" | "center" | "end";
|
|
|
90
90
|
* `normal` is CSS's own behaviour and the default: newlines and runs of spaces collapse. That is
|
|
91
91
|
* right for a label and wrong for text a PERSON typed into a textarea — a plain-text note, a
|
|
92
92
|
* pasted log, an issue description — where the line breaks and the indentation ARE content.
|
|
93
|
+
* Passed explicitly, `normal` also releases a `nowrap` the text inherits (a table cell's, gh#1083).
|
|
93
94
|
*
|
|
94
95
|
* `pre-wrap` keeps both and still wraps at the container's edge, so the block cannot force a
|
|
95
96
|
* horizontal scrollbar the way `pre` would. It is a closed union rather than a boolean because the
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
display: inline-flex;
|
|
6
6
|
|
|
7
7
|
inline-size: fit-content;
|
|
8
|
+
|
|
9
|
+
max-inline-size: 100%;
|
|
10
|
+
min-inline-size: 0;
|
|
11
|
+
|
|
12
|
+
box-sizing: border-box;
|
|
8
13
|
align-items: center;
|
|
9
14
|
|
|
10
15
|
vertical-align: middle;
|
|
@@ -21,6 +26,13 @@
|
|
|
21
26
|
white-space: nowrap;
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
[data-slot="badge-label"] {
|
|
30
|
+
min-inline-size: 0;
|
|
31
|
+
overflow-x: clip;
|
|
32
|
+
overflow-y: visible;
|
|
33
|
+
text-overflow: ellipsis;
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
@supports (text-box-trim: trim-both) and (text-box-edge: cap alphabetic) {
|
|
25
37
|
[data-slot="badge-label"] {
|
|
26
38
|
text-box-trim: trim-both;
|