@godxjp/ui 30.9.0 → 31.0.2
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/README.md +2 -1
- package/agent/START-HERE.md +1 -1
- package/agent/components/PrefetchLink.json +3 -3
- package/agent/components-index.json +1 -1
- package/agent/components.json +3 -3
- package/agent/index.json +2 -2
- package/agent/llms.txt +3 -3
- package/agent/patterns/settings-page-responsive.json +1 -1
- package/agent/patterns.json +1 -1
- package/dist/components/query/index.d.ts +0 -2
- package/dist/components/query/index.js +0 -2
- package/dist/components/react-router/index.d.ts +2 -0
- package/dist/components/react-router/index.js +4 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/styles/alert-layout.css +4 -2
- package/dist/styles/layers.json +1 -1
- package/docs/CONSUMER-RULES.md +1 -1
- package/docs/data-display/permission-matrix.tsx +30 -28
- package/docs/data-entry/cascader.tsx +22 -20
- package/docs/data-entry/date-picker.tsx +47 -43
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +80 -76
- package/docs/data-entry/form-field/examples/create-form.tsx +33 -31
- package/docs/data-entry/form-field/index.tsx +61 -57
- package/docs/data-entry/form.tsx +112 -106
- package/docs/data-entry/input.tsx +297 -270
- package/docs/data-entry/label.tsx +11 -9
- package/docs/data-entry/number-input.tsx +86 -78
- package/docs/data-entry/radio-group.tsx +33 -26
- package/docs/data-entry/rating.tsx +29 -25
- package/docs/data-entry/select-matrix.tsx +49 -47
- package/docs/data-entry/select.tsx +38 -35
- package/docs/data-entry/slider.tsx +45 -41
- package/docs/data-entry/textarea.tsx +44 -42
- package/docs/data-entry/time-picker.tsx +51 -47
- package/docs/feedback/sheet.tsx +84 -79
- package/docs/foundation/theme-editor.tsx +133 -124
- package/docs/general/button/examples/form-actions.tsx +17 -15
- package/docs/layout/auth-recovery/examples/password-recovery.tsx +31 -29
- package/docs/layout/auth-shell-registration.tsx +75 -72
- package/docs/layout/auth-shell-variants.tsx +55 -51
- package/docs/layout/space-compact.tsx +11 -8
- package/docs/{query → react-router}/prefetch-link.tsx +1 -1
- package/docs/showcase/caimono-price-comparison.tsx +59 -51
- package/docs/showcase/case3-approval-workflow.tsx +27 -25
- package/docs/showcase/case4-login.tsx +37 -34
- package/docs/showcase/permission-matrix.tsx +44 -42
- package/docs/showcase/theme-customization.tsx +108 -99
- package/package.json +6 -2
- package/scripts/consumer-rule.md +14 -0
- package/scripts/ui-audit.mjs +99 -2
- /package/dist/components/{query → react-router}/prefetch-link.d.ts +0 -0
- /package/dist/components/{query → react-router}/prefetch-link.js +0 -0
|
@@ -136,6 +136,7 @@ import {
|
|
|
136
136
|
type TimelineItem,
|
|
137
137
|
} from "@godxjp/ui/data-display";
|
|
138
138
|
import {
|
|
139
|
+
Form,
|
|
139
140
|
CheckboxGroup,
|
|
140
141
|
ColorPicker,
|
|
141
142
|
DatePicker,
|
|
@@ -586,85 +587,91 @@ function ComponentsBoard(props: {
|
|
|
586
587
|
pad={{ inline: "lg", block: "md" }}
|
|
587
588
|
>
|
|
588
589
|
{/* 1 · text entry */}
|
|
589
|
-
<
|
|
590
|
-
<
|
|
591
|
-
|
|
592
|
-
label={t("themeShowcase.form.email")}
|
|
593
|
-
helper={t("themeShowcase.form.emailHint")}
|
|
594
|
-
>
|
|
595
|
-
<Input
|
|
590
|
+
<Form>
|
|
591
|
+
<Flex direction="col" gap="md">
|
|
592
|
+
<FormField
|
|
596
593
|
id="board-email"
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
594
|
+
label={t("themeShowcase.form.email")}
|
|
595
|
+
helper={t("themeShowcase.form.emailHint")}
|
|
596
|
+
>
|
|
597
|
+
<Input
|
|
598
|
+
id="board-email"
|
|
599
|
+
type="email"
|
|
600
|
+
autoComplete="email"
|
|
601
|
+
prefix={<Mail aria-hidden="true" />}
|
|
602
|
+
placeholder={t("themeShowcase.form.emailPlaceholder")}
|
|
603
|
+
defaultValue="release-desk@ops.example.jp"
|
|
604
|
+
/>
|
|
605
|
+
</FormField>
|
|
606
|
+
<FormField id="board-handle" label={t("themeShowcase.form.handle")}>
|
|
607
|
+
<Input
|
|
608
|
+
id="board-handle"
|
|
609
|
+
addonBefore={<AtSign aria-hidden="true" />}
|
|
610
|
+
defaultValue="release-desk"
|
|
611
|
+
count={{ max: 24, show: true }}
|
|
612
|
+
/>
|
|
613
|
+
</FormField>
|
|
614
|
+
</Flex>
|
|
615
|
+
</Form>
|
|
613
616
|
|
|
614
617
|
{/* 2 · multi-select with removable tags + free tags */}
|
|
615
|
-
<
|
|
616
|
-
<
|
|
617
|
-
|
|
618
|
-
label={t("themeShowcase.form.regions")}
|
|
619
|
-
helper={regions.length > 0 ? regionList : t("themeShowcase.form.regionsEmpty")}
|
|
620
|
-
>
|
|
621
|
-
<Select
|
|
618
|
+
<Form>
|
|
619
|
+
<Flex direction="col" gap="md">
|
|
620
|
+
<FormField
|
|
622
621
|
id="board-regions"
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
622
|
+
label={t("themeShowcase.form.regions")}
|
|
623
|
+
helper={regions.length > 0 ? regionList : t("themeShowcase.form.regionsEmpty")}
|
|
624
|
+
>
|
|
625
|
+
<Select
|
|
626
|
+
id="board-regions"
|
|
627
|
+
mode="multiple"
|
|
628
|
+
maxTagCount={2}
|
|
629
|
+
value={regions}
|
|
630
|
+
onValueChange={setRegions}
|
|
631
|
+
placeholder={t("themeShowcase.form.regionsPlaceholder")}
|
|
632
|
+
options={REGION_CODES.map((code) => ({
|
|
633
|
+
value: code,
|
|
634
|
+
label: new Intl.DisplayNames([props.locale], { type: "region" }).of(code) ?? code,
|
|
635
|
+
}))}
|
|
636
|
+
/>
|
|
637
|
+
</FormField>
|
|
638
|
+
<FormField id="board-labels" label={t("themeShowcase.form.labels")}>
|
|
639
|
+
<TagInput
|
|
640
|
+
id="board-labels"
|
|
641
|
+
value={labels}
|
|
642
|
+
onValueChange={setLabels}
|
|
643
|
+
maxTagCount={2}
|
|
644
|
+
/* The ceiling the 71-character id is measured against (gh#840). 16 is the widest cut
|
|
641
645
|
that still leaves room for a second chip and the `+1` on one row at 375px; the value
|
|
642
646
|
is untouched — it stays in the chip's `title` and in the remover's accessible name,
|
|
643
647
|
which is the only reason truncating an identifier is allowed at all. */
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
648
|
+
maxTagTextLength={16}
|
|
649
|
+
placeholder={t("themeShowcase.form.labelsPlaceholder")}
|
|
650
|
+
/>
|
|
651
|
+
</FormField>
|
|
652
|
+
</Flex>
|
|
653
|
+
</Form>
|
|
649
654
|
|
|
650
655
|
{/* 3 · dropdown + date */}
|
|
651
|
-
<
|
|
652
|
-
<
|
|
653
|
-
<
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
{
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
<
|
|
666
|
-
|
|
667
|
-
|
|
656
|
+
<Form>
|
|
657
|
+
<Flex direction="col" gap="md">
|
|
658
|
+
<FormField id="board-plan" label={t("themeShowcase.form.plan")}>
|
|
659
|
+
<Select
|
|
660
|
+
id="board-plan"
|
|
661
|
+
value={plan}
|
|
662
|
+
onValueChange={setPlan}
|
|
663
|
+
options={[
|
|
664
|
+
{ value: "starter", label: t("themeShowcase.plan.starter") },
|
|
665
|
+
{ value: "standard", label: t("themeShowcase.plan.standard") },
|
|
666
|
+
{ value: "scale", label: t("themeShowcase.plan.scale") },
|
|
667
|
+
]}
|
|
668
|
+
/>
|
|
669
|
+
</FormField>
|
|
670
|
+
<FormField id="board-date" label={t("themeShowcase.form.releaseDate")}>
|
|
671
|
+
<DatePicker id="board-date" defaultValue={new Date("2026-09-12T00:00:00Z")} />
|
|
672
|
+
</FormField>
|
|
673
|
+
</Flex>
|
|
674
|
+
</Form>
|
|
668
675
|
|
|
669
676
|
{/* 4 · checkboxes — a three-line label beside a one-word one, and a disabled row */}
|
|
670
677
|
<Flex direction="col" gap="md">
|
|
@@ -929,35 +936,37 @@ function ComponentsBoard(props: {
|
|
|
929
936
|
<CardDescription>{t("themeShowcase.signup.description")}</CardDescription>
|
|
930
937
|
</CardHeader>
|
|
931
938
|
<CardContent>
|
|
932
|
-
<
|
|
933
|
-
<
|
|
934
|
-
<
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
<
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
<
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
<
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
939
|
+
<Form>
|
|
940
|
+
<Flex direction="col" gap="md">
|
|
941
|
+
<FormField id="signup-email" label={t("themeShowcase.signup.email")}>
|
|
942
|
+
<Input
|
|
943
|
+
id="signup-email"
|
|
944
|
+
type="email"
|
|
945
|
+
autoComplete="email"
|
|
946
|
+
placeholder="you@example.jp"
|
|
947
|
+
/>
|
|
948
|
+
</FormField>
|
|
949
|
+
<FormField id="signup-secret" label={t("themeShowcase.signup.password")}>
|
|
950
|
+
<PasswordInput
|
|
951
|
+
id="signup-secret"
|
|
952
|
+
value={secret}
|
|
953
|
+
onChange={(event) => setSecret(event.target.value)}
|
|
954
|
+
autoComplete="new-password"
|
|
955
|
+
/>
|
|
956
|
+
</FormField>
|
|
957
|
+
<PasswordStrength value={secret} />
|
|
958
|
+
<Button fullWidth>{t("themeShowcase.signup.submit")}</Button>
|
|
959
|
+
<AuthDivider label={t("themeShowcase.signup.or")} />
|
|
960
|
+
<Button variant="outline" fullWidth>
|
|
961
|
+
<ShieldCheck aria-hidden="true" />
|
|
962
|
+
{t("themeShowcase.signup.sso")}
|
|
963
|
+
</Button>
|
|
964
|
+
<Button variant="outline" fullWidth>
|
|
965
|
+
<Fingerprint aria-hidden="true" />
|
|
966
|
+
{t("themeShowcase.signup.passkey")}
|
|
967
|
+
</Button>
|
|
968
|
+
</Flex>
|
|
969
|
+
</Form>
|
|
961
970
|
</CardContent>
|
|
962
971
|
</Card>
|
|
963
972
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/ui",
|
|
3
|
-
"version": "
|
|
4
|
-
"godxUiMcp": "
|
|
3
|
+
"version": "31.0.2",
|
|
4
|
+
"godxUiMcp": "31.0.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -141,6 +141,10 @@
|
|
|
141
141
|
"types": "./dist/inertia/index.d.ts",
|
|
142
142
|
"import": "./dist/inertia/index.js"
|
|
143
143
|
},
|
|
144
|
+
"./react-router": {
|
|
145
|
+
"types": "./dist/components/react-router/index.d.ts",
|
|
146
|
+
"import": "./dist/components/react-router/index.js"
|
|
147
|
+
},
|
|
144
148
|
"./app": {
|
|
145
149
|
"types": "./dist/app/index.d.ts",
|
|
146
150
|
"import": "./dist/app/index.js"
|
package/scripts/consumer-rule.md
CHANGED
|
@@ -120,6 +120,20 @@ ngày: năm thứ cần đều ĐÃ CÓ và vẫn bị dựng lại bằng thứ
|
|
|
120
120
|
Lỗi không phải "đoán sai tên prop" mà là **cho rằng nó không tồn tại nên không
|
|
121
121
|
hỏi**.
|
|
122
122
|
|
|
123
|
+
## Form: `Form` bọc `FormField`, bề rộng theo nội dung, form dài là PAGE
|
|
124
|
+
|
|
125
|
+
godx-mailer dính bốn lỗi form trong một ngày, cả bốn đều là kit ĐÃ CÓ đồ đúng
|
|
126
|
+
(gh#998). Giờ ui-audit chặn:
|
|
127
|
+
|
|
128
|
+
| Sai | Đúng | rule |
|
|
129
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------- |
|
|
130
|
+
| ≥2 `FormField` không có `Form`, hoặc xếp hàng bằng `<Flex>` | `<Form layout="horizontal" labelWidth controlWidth>`; hàng = `SpaceCompact` / `Form columns` | `formfield-needs-form` |
|
|
131
|
+
| ≥3 `FormField` trong `DialogBody` | page riêng (route riêng) | `dialog-form-too-big` |
|
|
132
|
+
| `Select` hai lựa chọn kéo full width | `controlWidth` trên field hoặc một lần trên `Form` | `select-width-hint` |
|
|
133
|
+
|
|
134
|
+
Ca kit không diễn đạt được thì **mở issue ở godx-jp/godxjp-ui**, tạm dùng cách
|
|
135
|
+
hợp lệ gần nhất kèm `// TODO(godxjp-ui#<n>)` — không tự chế.
|
|
136
|
+
|
|
123
137
|
## Dialog và AlertDialog là MỘT họ — `variant` là lối chuẩn
|
|
124
138
|
|
|
125
139
|
Đừng với tay sang 12 export `AlertDialog*` nữa. Chúng **vẫn chạy y như cũ** (gỡ
|
package/scripts/ui-audit.mjs
CHANGED
|
@@ -85,8 +85,6 @@ function changedFiles() {
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
88
|
const parts = [
|
|
91
89
|
run(["diff", "--name-only", "--diff-filter=ACMR", mergeBase.trim(), "--"]),
|
|
92
90
|
run(["diff", "--name-only", "--diff-filter=ACMR", "--cached"]),
|
|
@@ -1895,6 +1893,105 @@ for (const dir of SCAN_DIRS) {
|
|
|
1895
1893
|
snippet: match[0].replace(/\s+/g, " ").slice(0, 120),
|
|
1896
1894
|
});
|
|
1897
1895
|
}
|
|
1896
|
+
|
|
1897
|
+
/*
|
|
1898
|
+
* THE LAYER ABOVE THE FIELD (gh#998). `bare-control-needs-formfield` stops a control without a
|
|
1899
|
+
* FormField; nothing stopped a FormField without a Form, or a Form without a width, or a form
|
|
1900
|
+
* that belongs on a page stuffed into a Dialog. godx-mailer hit all four in one day. Validated
|
|
1901
|
+
* on the reporter's own files before and after its fixes: every defect caught before, none in
|
|
1902
|
+
* the fixed files except one the fix missed (four FormFields in a hand-rolled wrapping Flex).
|
|
1903
|
+
*/
|
|
1904
|
+
if (isJsx) {
|
|
1905
|
+
const lineAt = (i) => scanContent.slice(0, i).split("\n").length;
|
|
1906
|
+
/*
|
|
1907
|
+
* TWO OR MORE FormFields under ONE parent with no <Form> above them. That is the shape of
|
|
1908
|
+
* every defect in the report — a hand-rolled <Flex> row of fields, a long form in a Dialog, a
|
|
1909
|
+
* page of fields each choosing its own layout. A LONE field is not: a search box, the
|
|
1910
|
+
* ConfirmDialog's type-to-confirm input, a field component whose whole output is one
|
|
1911
|
+
* FormField. Flagging those made this repo's own library code and 29 docs pages red on a
|
|
1912
|
+
* rule whose defect they do not have.
|
|
1913
|
+
*
|
|
1914
|
+
* The walk keeps a stack of open JSX elements so "same parent" is structural. `(?<![\w.$])`
|
|
1915
|
+
* skips a generic argument (`useRef<HTMLDivElement>`), which is preceded by an identifier.
|
|
1916
|
+
*/
|
|
1917
|
+
const stack = [];
|
|
1918
|
+
const bareByParent = new Map();
|
|
1919
|
+
let formDepth = 0;
|
|
1920
|
+
for (const m of scanContent.matchAll(
|
|
1921
|
+
/(?<![\w.$])<(\/?)([A-Za-z][\w.]*)(?=[\s>/])([^<>]*?(?:\{[^{}]*\}[^<>]*?)*)(\/?)>/g,
|
|
1922
|
+
)) {
|
|
1923
|
+
const [, close, tag, , selfClose] = m;
|
|
1924
|
+
if (close) {
|
|
1925
|
+
for (let i = stack.length - 1; i >= 0; i -= 1) {
|
|
1926
|
+
if (stack[i].tag === tag) {
|
|
1927
|
+
stack.length = i;
|
|
1928
|
+
break;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
if (tag === "Form") formDepth = Math.max(0, formDepth - 1);
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
if (tag === "FormField" && formDepth === 0) {
|
|
1935
|
+
const parent = stack.at(-1)?.index ?? -1;
|
|
1936
|
+
(bareByParent.get(parent) ?? bareByParent.set(parent, []).get(parent)).push(m.index);
|
|
1937
|
+
}
|
|
1938
|
+
if (selfClose) continue;
|
|
1939
|
+
stack.push({ tag, index: m.index });
|
|
1940
|
+
if (tag === "Form") formDepth += 1;
|
|
1941
|
+
}
|
|
1942
|
+
for (const group of bareByParent.values()) {
|
|
1943
|
+
if (group.length < 2) continue;
|
|
1944
|
+
for (const at of group) {
|
|
1945
|
+
const lineNo = lineAt(at);
|
|
1946
|
+
if (suppressed("formfield-needs-form", lineNo - 1)) continue;
|
|
1947
|
+
findings.push({
|
|
1948
|
+
file: rel,
|
|
1949
|
+
line: lineNo,
|
|
1950
|
+
rule: "formfield-needs-form",
|
|
1951
|
+
severity: "error",
|
|
1952
|
+
standard: "@godxjp/ui Form (gh#998)",
|
|
1953
|
+
message: `${group.length} FormFields under one parent with no <Form> around them. <Form layout="horizontal" labelWidth controlWidth> is what lines fields up — without it each form picks its own layout, and a row of fields in a hand-rolled <Flex> drops the required field's label out of line. Wrap them in <Form>; a row that belongs together is <SpaceCompact> or <Form columns>. A lone field (a search box, a type-to-confirm input) is not flagged.`,
|
|
1954
|
+
snippet: scanContent.slice(at, at + 120).replace(/\s+/g, " "),
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
for (const m of scanContent.matchAll(/<(Dialog\.Body|DialogBody)(?=[\s>])[\s\S]*?<\/\1>/g)) {
|
|
1960
|
+
const count = (m[0].match(/<FormField(?=[\s>/])/g) ?? []).length;
|
|
1961
|
+
if (count < 3) continue;
|
|
1962
|
+
const lineNo = lineAt(m.index);
|
|
1963
|
+
if (suppressed("dialog-form-too-big", lineNo - 1)) continue;
|
|
1964
|
+
findings.push({
|
|
1965
|
+
file: rel,
|
|
1966
|
+
line: lineNo,
|
|
1967
|
+
rule: "dialog-form-too-big",
|
|
1968
|
+
severity: "error",
|
|
1969
|
+
standard: "@godxjp/ui form placement (gh#998)",
|
|
1970
|
+
message: `${count} FormFields in a ${m[1]} — a form this size is a page of its own (its own route, back button, and room to scroll), not a modal. Keep dialogs for a confirmation or one or two fields. A Sheet is not flagged: a side drawer is where an advanced filter or an edit form belongs (antd Drawer).`,
|
|
1971
|
+
snippet: m[0].slice(0, 120).replace(/\s+/g, " "),
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
for (const m of scanContent.matchAll(/<FormField(?=[\s>])([^>]*)>([\s\S]*?)<\/FormField>/g)) {
|
|
1976
|
+
if (!/<Select(?=[\s>/])/.test(m[2]) || /controlWidth=/.test(m[1])) continue;
|
|
1977
|
+
const before = scanContent.slice(0, m.index);
|
|
1978
|
+
const lastForm = [...before.matchAll(/<Form(?=[\s>])([^>]*)>/g)].at(-1);
|
|
1979
|
+
const insideForm = lastForm && before.lastIndexOf("</Form>") < lastForm.index;
|
|
1980
|
+
if (insideForm && /controlWidth=/.test(lastForm[1])) continue;
|
|
1981
|
+
const lineNo = lineAt(m.index);
|
|
1982
|
+
if (suppressed("select-width-hint", lineNo - 1)) continue;
|
|
1983
|
+
findings.push({
|
|
1984
|
+
file: rel,
|
|
1985
|
+
line: lineNo,
|
|
1986
|
+
rule: "select-width-hint",
|
|
1987
|
+
severity: "warn",
|
|
1988
|
+
standard: "GOV.UK Design System · text input width",
|
|
1989
|
+
message:
|
|
1990
|
+
"A <Select> in a FormField with no controlWidth on the field or its <Form> stretches to the full column — a two-option select as wide as an address line. Size the control for its content (`controlWidth` on the FormField, or once on the Form). https://design-system.service.gov.uk/components/text-input/",
|
|
1991
|
+
snippet: scanContent.slice(m.index, m.index + 120).replace(/\s+/g, " "),
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1898
1995
|
}
|
|
1899
1996
|
}
|
|
1900
1997
|
|
|
File without changes
|
|
File without changes
|