@epam/ai-dial-ui-kit 0.16.0-dev.34 → 0.16.0-dev.36
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/CHANGELOG.md +4 -0
- package/dist/components/New/Avatar/Avatar.js +17 -16
- package/dist/components/New/EntityIdentity/EntityIdentity.js +29 -28
- package/dist/components/New/PasswordInput/PasswordInput.js +33 -32
- package/dist/components-manifest.json +16 -4
- package/dist/dial-ui-kit.cjs +3 -3
- package/dist/index.css +1 -1
- package/dist/src/components/New/Avatar/Avatar.d.ts +3 -0
- package/dist/src/components/New/EntityIdentity/EntityIdentity.d.ts +3 -2
- package/dist/src/components/New/PasswordInput/PasswordInput.d.ts +6 -0
- package/package.json +1 -1
package/dist/CHANGELOG.md
CHANGED
|
@@ -25,6 +25,8 @@ Versions match the git tags on the `development` branch.
|
|
|
25
25
|
|
|
26
26
|
### Added
|
|
27
27
|
|
|
28
|
+
- **`PasswordInput` (2.0) — `isToggleHidden`** — the reveal toggle could only be removed by disabling the field, so a host with nothing to reveal (a stored secret the server never returns) had to hide the button with a CSS selector on the kit's markup. `isToggleHidden` draws no toggle and keeps the value masked while the field stays editable; a reveal switched on before the toggle went away comes back masked. Additive.
|
|
29
|
+
|
|
28
30
|
- **`Select` (2.0) — `ariaDescribedBy`** — ids of elements that describe the field from outside it, such as an error a form renders under its own label; the field's own `error` / `caption` are linked automatically. Additive.
|
|
29
31
|
|
|
30
32
|
- **`mergeClasses` — the `dial-*-text` type scale is one conflict group** — a caller's `dial-small-text` passed to a component whose default is `dial-tiny-text` kept both classes, and the step declared later in `typography.scss` won whatever the caller asked for. A later type-scale class now replaces an earlier one, as `w-*` or `px-*` do. The public `dial-kit-*-text` classes are not part of the group. Reported from `ai-dial-chat`.
|
|
@@ -55,6 +57,8 @@ Versions match the git tags on the `development` branch.
|
|
|
55
57
|
|
|
56
58
|
### Fixed
|
|
57
59
|
|
|
60
|
+
- **`EntityIdentity` (2.0) — the icon sits on a themable `bg-entity-icon` plate, so dark logos stay visible in a dark theme** — model and vendor logos are often a monochrome dark glyph on a transparent background, and the image was drawn with no background, straight on the dark card. The image now sits on `bg-entity-icon`, which reads `--bg-entity-icon` and falls back to `--bg-layer-sunken`; a dark theme sets `--bg-entity-icon` to a light value to keep such logos legible. A transparent icon now shows the sunken layer behind it until the theme sets the new variable. `Avatar` gains `imageClassName`, applied only while the image is shown, so the plate never overrides the initials' colour pair. Reported in [#971](https://github.com/epam/ai-dial-ui-kit/issues/971).
|
|
61
|
+
|
|
58
62
|
- **`Dropdown` (2.0) — the menu state is on the trigger, not on its wrapper** — `aria-haspopup`, `aria-expanded` and `aria-controls` sat on the role-less `<span class="dial-kit-dropdown">` around the trigger. ARIA does not allow `aria-expanded` there (axe `aria-allowed-attr`), so a screen reader focused on the icon button heard no open/closed state. When the trigger is a single element and the menu opens on click or hover, the three attributes now land on that element, and any the trigger sets itself win (`Select` keeps `aria-haspopup="listbox"`). A text or multi-node trigger, and a context-menu-only trigger such as a `Grid` cell, keep them on the wrapper as before. A test that located the wrapper by `[aria-haspopup="menu"]` now finds the trigger element. Reported from an Accessibility Insights FastPass of ai-dial-chat (31 instances).
|
|
59
63
|
|
|
60
64
|
- **`Radio`, `Checkbox`, `Switch` (2.0) — hovering the label text shows the control's hover state** — the hover colour was on the control's own `<label>` only, so pointing at the text, which also toggles the control, gave no feedback until the pointer reached the 20px circle, box or track. Each component's root is now a `group/choice`, and the control shows its hover state while either of its two labels is hovered (`group-has-[label:hover]/choice:`). Pointing at the empty part of the row or at the label's info button does not count. A disabled `Radio` no longer picks up the hover background. The decorative `CheckboxBox` used inside menu rows keeps reacting to its own hover, since the row owns that interaction. Reported on the toolset editor's Protocol radios in ai-dial-chat.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx as a } from "react/jsx-runtime";
|
|
2
|
-
import { useState as
|
|
3
|
-
import { DIAL_KIT_CLASS as
|
|
2
|
+
import { useState as w } from "react";
|
|
3
|
+
import { DIAL_KIT_CLASS as I } from "../../../constants/public-class-names.js";
|
|
4
4
|
import { AvatarShape as d } from "../../../types/avatar.js";
|
|
5
|
-
import { pickAvatarColor as
|
|
5
|
+
import { pickAvatarColor as N, extractInitials as j } from "../../../utils/avatar.js";
|
|
6
6
|
import { mergeClasses as e } from "../../../utils/merge-classes.js";
|
|
7
|
-
const
|
|
7
|
+
const k = {
|
|
8
8
|
[d.Circle]: "rounded-full",
|
|
9
9
|
[d.Square]: "rounded-md"
|
|
10
|
-
},
|
|
10
|
+
}, D = ({
|
|
11
11
|
name: s,
|
|
12
12
|
initials: c,
|
|
13
13
|
src: r,
|
|
@@ -17,16 +17,17 @@ const j = {
|
|
|
17
17
|
color: h,
|
|
18
18
|
className: u,
|
|
19
19
|
textClassName: l,
|
|
20
|
-
|
|
20
|
+
imageClassName: S,
|
|
21
|
+
onImageError: p
|
|
21
22
|
}) => {
|
|
22
|
-
const [
|
|
23
|
+
const [v, g] = w(), A = !!r && v !== r, i = t !== "", m = (n) => e(
|
|
23
24
|
"inline-flex shrink-0 select-none items-center justify-center overflow-hidden",
|
|
24
|
-
|
|
25
|
+
k[f],
|
|
25
26
|
n,
|
|
26
27
|
u,
|
|
27
|
-
|
|
28
|
+
I.avatar
|
|
28
29
|
);
|
|
29
|
-
if (
|
|
30
|
+
if (A)
|
|
30
31
|
return /* @__PURE__ */ a(
|
|
31
32
|
"img",
|
|
32
33
|
{
|
|
@@ -34,20 +35,20 @@ const j = {
|
|
|
34
35
|
alt: t,
|
|
35
36
|
width: o,
|
|
36
37
|
height: o,
|
|
37
|
-
className: e(m(), "object-cover"),
|
|
38
|
+
className: e(m(S), "object-cover"),
|
|
38
39
|
onError: (n) => {
|
|
39
|
-
|
|
40
|
+
g(r), p?.(n);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
);
|
|
43
|
-
const { background:
|
|
44
|
+
const { background: C, foreground: b } = h ?? N(s);
|
|
44
45
|
return /* @__PURE__ */ a(
|
|
45
46
|
"span",
|
|
46
47
|
{
|
|
47
48
|
role: i ? "img" : void 0,
|
|
48
49
|
"aria-label": i ? t : void 0,
|
|
49
50
|
"aria-hidden": i ? void 0 : !0,
|
|
50
|
-
className: m(e(
|
|
51
|
+
className: m(e(C, b)),
|
|
51
52
|
style: {
|
|
52
53
|
width: o,
|
|
53
54
|
height: o,
|
|
@@ -60,12 +61,12 @@ const j = {
|
|
|
60
61
|
"leading-none",
|
|
61
62
|
l ?? "font-semibold"
|
|
62
63
|
),
|
|
63
|
-
children: c ??
|
|
64
|
+
children: c ?? j(s)
|
|
64
65
|
}
|
|
65
66
|
)
|
|
66
67
|
}
|
|
67
68
|
);
|
|
68
69
|
};
|
|
69
70
|
export {
|
|
70
|
-
|
|
71
|
+
D as Avatar
|
|
71
72
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { jsxs as s, jsx as
|
|
1
|
+
import { jsxs as s, jsx as n } from "react/jsx-runtime";
|
|
2
2
|
import { DIAL_KIT_CLASS as E } from "../../../constants/public-class-names.js";
|
|
3
3
|
import { AvatarShape as _ } from "../../../types/avatar.js";
|
|
4
4
|
import { EntityType as e } from "../../../types/entity-type.js";
|
|
5
|
-
import { mergeClasses as
|
|
5
|
+
import { mergeClasses as l } from "../../../utils/merge-classes.js";
|
|
6
6
|
import { Avatar as w } from "../Avatar/Avatar.js";
|
|
7
7
|
import { EllipsisTooltip as d } from "../EllipsisTooltip/EllipsisTooltip.js";
|
|
8
8
|
import { Highlight as L } from "../Highlight/Highlight.js";
|
|
9
|
-
const
|
|
9
|
+
const C = {
|
|
10
10
|
[e.Model]: "Model",
|
|
11
11
|
[e.Agent]: "Agent",
|
|
12
12
|
[e.Toolset]: "Toolset",
|
|
@@ -18,13 +18,13 @@ const M = {
|
|
|
18
18
|
[e.Toolset]: "text-brown-2",
|
|
19
19
|
[e.Skill]: "text-violet-1",
|
|
20
20
|
[e.Prompt]: "text-violet-2"
|
|
21
|
-
},
|
|
21
|
+
}, M = {
|
|
22
22
|
[e.Model]: "bg-blue",
|
|
23
23
|
[e.Agent]: "bg-green-2",
|
|
24
24
|
[e.Toolset]: "bg-brown",
|
|
25
25
|
[e.Skill]: "bg-violet-2",
|
|
26
26
|
[e.Prompt]: "bg-violet-1"
|
|
27
|
-
},
|
|
27
|
+
}, P = ["h1", "h2", "h3", "h4", "h5", "h6"], k = (t) => P[Math.min(6, Math.max(1, Math.round(t))) - 1], z = ({
|
|
28
28
|
item: t,
|
|
29
29
|
labels: i,
|
|
30
30
|
showVersion: x = !0,
|
|
@@ -36,60 +36,61 @@ const M = {
|
|
|
36
36
|
statusBadge: o,
|
|
37
37
|
className: f,
|
|
38
38
|
nameClassName: v = "dial-h3-text",
|
|
39
|
-
typeClassName:
|
|
40
|
-
versionClassName:
|
|
39
|
+
typeClassName: y = "dial-caption-lead-semi-text",
|
|
40
|
+
versionClassName: T = "dial-tiny-text",
|
|
41
41
|
iconClassName: A,
|
|
42
42
|
featuredChipClassName: S
|
|
43
43
|
}) => {
|
|
44
|
-
const N =
|
|
44
|
+
const N = k(u), m = h && !!t.isFeatured, b = o != null || m, c = x ? t.version : void 0;
|
|
45
45
|
return /* @__PURE__ */ s(
|
|
46
46
|
"div",
|
|
47
47
|
{
|
|
48
|
-
className:
|
|
48
|
+
className: l(
|
|
49
49
|
"flex items-start gap-2",
|
|
50
50
|
f,
|
|
51
51
|
E.entityIdentity
|
|
52
52
|
),
|
|
53
53
|
children: [
|
|
54
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ n(
|
|
55
55
|
w,
|
|
56
56
|
{
|
|
57
57
|
name: t.name,
|
|
58
58
|
src: t.iconUrl,
|
|
59
59
|
size: g,
|
|
60
60
|
shape: _.Square,
|
|
61
|
-
|
|
61
|
+
imageClassName: "bg-entity-icon",
|
|
62
|
+
className: l("rounded-[14px]", A)
|
|
62
63
|
}
|
|
63
64
|
),
|
|
64
65
|
/* @__PURE__ */ s(
|
|
65
66
|
"div",
|
|
66
67
|
{
|
|
67
|
-
className:
|
|
68
|
+
className: l(
|
|
68
69
|
"flex min-w-0 flex-1 flex-col gap-1",
|
|
69
70
|
a != null && "self-stretch"
|
|
70
71
|
),
|
|
71
72
|
children: [
|
|
72
73
|
/* @__PURE__ */ s("div", { className: "relative flex items-center justify-between", children: [
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
+
/* @__PURE__ */ n(
|
|
74
75
|
"span",
|
|
75
76
|
{
|
|
76
|
-
className:
|
|
77
|
+
className: l(
|
|
77
78
|
"uppercase",
|
|
78
|
-
|
|
79
|
+
y,
|
|
79
80
|
p[t.type]
|
|
80
81
|
),
|
|
81
|
-
children: i?.type ??
|
|
82
|
+
children: i?.type ?? C[t.type]
|
|
82
83
|
}
|
|
83
84
|
),
|
|
84
85
|
b && /* @__PURE__ */ s("div", { className: "absolute end-0 top-[-6px] flex items-center gap-2", children: [
|
|
85
86
|
o,
|
|
86
|
-
|
|
87
|
+
m && /* @__PURE__ */ n(
|
|
87
88
|
"span",
|
|
88
89
|
{
|
|
89
|
-
className:
|
|
90
|
+
className: l(
|
|
90
91
|
"flex h-6 items-center justify-center rounded-2xl px-2",
|
|
91
92
|
"dial-caption-lead-semi-text",
|
|
92
|
-
|
|
93
|
+
M[t.type],
|
|
93
94
|
p[t.type],
|
|
94
95
|
S
|
|
95
96
|
),
|
|
@@ -99,30 +100,30 @@ const M = {
|
|
|
99
100
|
] })
|
|
100
101
|
] }),
|
|
101
102
|
/* @__PURE__ */ s("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
102
|
-
/* @__PURE__ */
|
|
103
|
+
/* @__PURE__ */ n(
|
|
103
104
|
N,
|
|
104
105
|
{
|
|
105
|
-
className:
|
|
106
|
+
className: l(
|
|
106
107
|
"min-w-0 shrink text-primary",
|
|
107
108
|
v
|
|
108
109
|
),
|
|
109
|
-
children: r ? /* @__PURE__ */
|
|
110
|
+
children: r ? /* @__PURE__ */ n(L, { text: t.name, query: r, maxLines: 1 }) : /* @__PURE__ */ n(d, { text: t.name })
|
|
110
111
|
}
|
|
111
112
|
),
|
|
112
|
-
|
|
113
|
+
c != null && /* Capped at 30% of the row so a long version truncates instead of
|
|
113
114
|
squeezing the name out. */
|
|
114
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ n(
|
|
115
116
|
d,
|
|
116
117
|
{
|
|
117
|
-
className:
|
|
118
|
+
className: l(
|
|
118
119
|
"max-w-[30%] shrink-0 text-secondary",
|
|
119
|
-
|
|
120
|
+
T
|
|
120
121
|
),
|
|
121
|
-
text:
|
|
122
|
+
text: c
|
|
122
123
|
}
|
|
123
124
|
)
|
|
124
125
|
] }),
|
|
125
|
-
a != null && /* @__PURE__ */
|
|
126
|
+
a != null && /* @__PURE__ */ n("div", { className: "mt-auto pt-1", children: a })
|
|
126
127
|
]
|
|
127
128
|
}
|
|
128
129
|
)
|
|
@@ -1,58 +1,59 @@
|
|
|
1
1
|
import { jsx as e } from "react/jsx-runtime";
|
|
2
|
-
import { IconEyeOff as c, IconEye as
|
|
3
|
-
import { useState as
|
|
4
|
-
import { GhostIconButton as
|
|
5
|
-
import { Input as
|
|
6
|
-
import { DIAL_KIT_ICON_STROKE as
|
|
2
|
+
import { IconEyeOff as c, IconEye as u } from "@tabler/icons-react";
|
|
3
|
+
import { useState as w, useEffect as C } from "react";
|
|
4
|
+
import { GhostIconButton as _ } from "../IconButton/IconButtonWrappers.js";
|
|
5
|
+
import { Input as E } from "../Input/Input.js";
|
|
6
|
+
import { DIAL_KIT_ICON_STROKE as a } from "../constants/icon.js";
|
|
7
7
|
import { DIAL_ICON_SIZE as m } from "../../../constants/icon.js";
|
|
8
|
-
import { ElementSize as
|
|
9
|
-
import { DIAL_KIT_CLASS as
|
|
10
|
-
import { mergeClasses as
|
|
8
|
+
import { ElementSize as p } from "../../../types/size.js";
|
|
9
|
+
import { DIAL_KIT_CLASS as h } from "../../../constants/public-class-names.js";
|
|
10
|
+
import { mergeClasses as A } from "../../../utils/merge-classes.js";
|
|
11
11
|
const K = ({
|
|
12
|
-
showPasswordLabel:
|
|
12
|
+
showPasswordLabel: n = "Show password",
|
|
13
13
|
hidePasswordLabel: f = "Hide password",
|
|
14
|
-
disabled:
|
|
15
|
-
|
|
14
|
+
disabled: s,
|
|
15
|
+
isToggleHidden: I,
|
|
16
|
+
size: l = p.Standard,
|
|
16
17
|
...t
|
|
17
18
|
}) => {
|
|
18
|
-
const [
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
}, [
|
|
22
|
-
|
|
19
|
+
const [d, i] = w(!1), o = !s && !I, r = d && o;
|
|
20
|
+
return C(() => {
|
|
21
|
+
o || i(!1);
|
|
22
|
+
}, [o]), /* @__PURE__ */ e(
|
|
23
|
+
E,
|
|
23
24
|
{
|
|
24
25
|
...t,
|
|
25
|
-
wrapperClassName:
|
|
26
|
+
wrapperClassName: A(
|
|
26
27
|
t.wrapperClassName,
|
|
27
|
-
|
|
28
|
+
h.passwordInput
|
|
28
29
|
),
|
|
29
|
-
size:
|
|
30
|
-
disabled:
|
|
31
|
-
type:
|
|
32
|
-
iconAfter:
|
|
33
|
-
|
|
30
|
+
size: l,
|
|
31
|
+
disabled: s,
|
|
32
|
+
type: r ? "text" : "password",
|
|
33
|
+
iconAfter: o ? /* @__PURE__ */ e(
|
|
34
|
+
_,
|
|
34
35
|
{
|
|
35
|
-
size:
|
|
36
|
-
"aria-label":
|
|
37
|
-
"aria-pressed":
|
|
38
|
-
icon:
|
|
36
|
+
size: p.Small,
|
|
37
|
+
"aria-label": r ? f : n,
|
|
38
|
+
"aria-pressed": r,
|
|
39
|
+
icon: r ? /* @__PURE__ */ e(
|
|
39
40
|
c,
|
|
40
41
|
{
|
|
41
42
|
size: m.SM,
|
|
42
|
-
stroke:
|
|
43
|
+
stroke: a,
|
|
43
44
|
"aria-hidden": "true"
|
|
44
45
|
}
|
|
45
46
|
) : /* @__PURE__ */ e(
|
|
46
|
-
|
|
47
|
+
u,
|
|
47
48
|
{
|
|
48
49
|
size: m.SM,
|
|
49
|
-
stroke:
|
|
50
|
+
stroke: a,
|
|
50
51
|
"aria-hidden": "true"
|
|
51
52
|
}
|
|
52
53
|
),
|
|
53
|
-
onClick: () =>
|
|
54
|
+
onClick: () => i((S) => !S)
|
|
54
55
|
}
|
|
55
|
-
)
|
|
56
|
+
) : void 0
|
|
56
57
|
}
|
|
57
58
|
);
|
|
58
59
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.16.0-dev.
|
|
3
|
-
"generatedAt": "2026-10-
|
|
2
|
+
"version": "0.16.0-dev.36",
|
|
3
|
+
"generatedAt": "2026-10-22T19:28:41.596Z",
|
|
4
4
|
"kit": {
|
|
5
5
|
"name": "@epam/ai-dial-ui-kit",
|
|
6
6
|
"description": "A modern UI kit for building AI DIAL interfaces with React",
|
|
@@ -1692,7 +1692,7 @@
|
|
|
1692
1692
|
"name": "PasswordInput",
|
|
1693
1693
|
"category": "Components_2_0",
|
|
1694
1694
|
"generation": "2.0",
|
|
1695
|
-
"description": "A password field with a reveal toggle, built on .\naliases: SecureInput|ToggleablePassword\nDesign system 2.0\n\nThe toggle is a real `<button>`, so it is reachable by keyboard and announces\nboth its purpose and its state. `type` and `iconAfter` are owned by this\ncomponent; every other prop is passed through.\n\nA disabled field is masked with no toggle at all, and comes back masked if it\nis enabled again.",
|
|
1695
|
+
"description": "A password field with a reveal toggle, built on .\naliases: SecureInput|ToggleablePassword\nDesign system 2.0\n\nThe toggle is a real `<button>`, so it is reachable by keyboard and announces\nboth its purpose and its state. `type` and `iconAfter` are owned by this\ncomponent; every other prop is passed through.\n\nA disabled field is masked with no toggle at all, and comes back masked if it\nis enabled again.\n\n`isToggleHidden` does the same without disabling the field: use it when there\nis nothing to reveal, such as a stored secret the server never returns.",
|
|
1696
1696
|
"props": [
|
|
1697
1697
|
{
|
|
1698
1698
|
"name": "showPasswordLabel",
|
|
@@ -1707,6 +1707,12 @@
|
|
|
1707
1707
|
"required": false,
|
|
1708
1708
|
"defaultValue": "'Hide password'",
|
|
1709
1709
|
"description": "Accessible name of the toggle while the value is visible"
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
"name": "isToggleHidden",
|
|
1713
|
+
"type": "boolean",
|
|
1714
|
+
"required": false,
|
|
1715
|
+
"description": "Hides the reveal toggle and keeps the value masked, leaving the field editable"
|
|
1710
1716
|
}
|
|
1711
1717
|
],
|
|
1712
1718
|
"examples": [
|
|
@@ -3784,6 +3790,12 @@
|
|
|
3784
3790
|
"required": false,
|
|
3785
3791
|
"description": "Type-scale class for the initials, replacing the size-derived font size"
|
|
3786
3792
|
},
|
|
3793
|
+
{
|
|
3794
|
+
"name": "imageClassName",
|
|
3795
|
+
"type": "string",
|
|
3796
|
+
"required": false,
|
|
3797
|
+
"description": "Additional CSS classes applied only while the image is shown"
|
|
3798
|
+
},
|
|
3787
3799
|
{
|
|
3788
3800
|
"name": "onImageError",
|
|
3789
3801
|
"type": "(event: SyntheticEvent<HTMLImageElement>) => void",
|
|
@@ -3800,7 +3812,7 @@
|
|
|
3800
3812
|
"name": "EntityIdentity",
|
|
3801
3813
|
"category": "Components_2_0",
|
|
3802
3814
|
"generation": "2.0",
|
|
3803
|
-
"description": "The identity block of a DIAL entity: its icon, a coloured type label, the\nname and the version, with an optional featured chip.\naliases: EntityHeader|ModelHeader|AppIdentity\nDesign system 2.0\n\nThe type label is plain uppercase text coloured by `item.type`. The icon is\na square `Avatar`, so a missing or broken image falls back to the initials of\nthe name. The icon is decorative: the name next
|
|
3815
|
+
"description": "The identity block of a DIAL entity: its icon, a coloured type label, the\nname and the version, with an optional featured chip.\naliases: EntityHeader|ModelHeader|AppIdentity\nDesign system 2.0\n\nThe type label is plain uppercase text coloured by `item.type`. The icon is\na square `Avatar`, so a missing or broken image falls back to the initials of\nthe name. The image sits on the `bg-entity-icon` plate, so a dark theme can\nkeep a dark, transparent logo legible. The icon is decorative: the name next\nto it already names the entity.",
|
|
3804
3816
|
"props": [
|
|
3805
3817
|
{
|
|
3806
3818
|
"name": "item",
|