rift-ds 1.3.0 → 1.4.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/README.md +2 -2
- package/components/Attachment/Attachment.css +1036 -0
- package/components/Attachment/AttachmentDropZone.d.ts +39 -0
- package/components/Attachment/AttachmentDropZone.js +76 -0
- package/components/Attachment/AttachmentGroup.d.ts +60 -0
- package/components/Attachment/AttachmentGroup.js +179 -0
- package/components/Attachment/AttachmentTile.d.ts +80 -0
- package/components/Attachment/AttachmentTile.js +143 -0
- package/components/Attachment/AttachmentViewer.d.ts +66 -0
- package/components/Attachment/AttachmentViewer.js +215 -0
- package/components/Attachment/fileTypes.d.ts +101 -0
- package/components/Attachment/fileTypes.js +182 -0
- package/components/Attachment/typeMark.d.ts +11 -0
- package/components/Attachment/typeMark.js +51 -0
- package/components/Attachment/useAttachments.d.ts +65 -0
- package/components/Attachment/useAttachments.js +201 -0
- package/components/ChatMessage/ChatMessage.css +24 -0
- package/components/ChatMessage/ChatMessage.d.ts +6 -0
- package/components/ChatMessage/ChatMessage.js +8 -5
- package/components/Composer/Composer.css +95 -2
- package/components/Composer/Composer.d.ts +57 -8
- package/components/Composer/Composer.js +179 -6
- package/components/DocumentChip/DocumentChip.d.ts +7 -5
- package/components/FileInput/FileInput.js +1 -7
- package/components/MessageCard/MessageCard.d.ts +1 -1
- package/components/registry.json +38 -2
- package/components/registry.json.d.ts +38 -2
- package/components/registry.json.js +1 -1
- package/index.d.ts +4 -0
- package/index.js +19 -0
- package/package.json +9 -1
- package/tokens/registry.json +4 -0
- package/tokens/registry.json.d.ts +4 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +6 -0
- package/tokens/tokens-light.css +12 -0
- package/tokens/tokens-primitives.css +10 -0
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
-
import React, { useRef, useState, useLayoutEffect } from "react";
|
|
2
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, { useRef, useState, useLayoutEffect, useEffect } from "react";
|
|
4
|
+
import { AttachmentTile } from "../Attachment/AttachmentTile.js";
|
|
5
|
+
import { formatBytes } from "../Attachment/fileTypes.js";
|
|
4
6
|
import { CircularButton } from "../CircularButton/CircularButton.js";
|
|
5
7
|
import "../../fonts/material-symbols.css";
|
|
6
8
|
import "./Composer.css";
|
|
9
|
+
const defaultFileAnnouncement = ({
|
|
10
|
+
type,
|
|
11
|
+
names
|
|
12
|
+
}) => {
|
|
13
|
+
const list = names.join(", ");
|
|
14
|
+
if (type === "added") return `Attached ${list}`;
|
|
15
|
+
if (type === "removed") return `Removed ${list}`;
|
|
16
|
+
return `Could not attach ${list}`;
|
|
17
|
+
};
|
|
18
|
+
const dragCarriesFiles = (event) => Array.from(event.dataTransfer?.types ?? []).includes("Files");
|
|
7
19
|
const Composer = React.forwardRef(
|
|
8
20
|
({
|
|
9
21
|
value,
|
|
@@ -17,6 +29,19 @@ const Composer = React.forwardRef(
|
|
|
17
29
|
context,
|
|
18
30
|
contextIcon,
|
|
19
31
|
contextPlacement = "inside",
|
|
32
|
+
files,
|
|
33
|
+
onFilesSelected,
|
|
34
|
+
onFileRemove,
|
|
35
|
+
onFileClick,
|
|
36
|
+
accept,
|
|
37
|
+
multiple = true,
|
|
38
|
+
attachButton = true,
|
|
39
|
+
pasteThreshold,
|
|
40
|
+
onPasteAsAttachment,
|
|
41
|
+
attachLabel = "Attach files",
|
|
42
|
+
dropLabel = "Drop files to attach",
|
|
43
|
+
filesLabel = "Attachments",
|
|
44
|
+
formatFileAnnouncement = defaultFileAnnouncement,
|
|
20
45
|
attachments,
|
|
21
46
|
actions,
|
|
22
47
|
trailingActions,
|
|
@@ -25,6 +50,7 @@ const Composer = React.forwardRef(
|
|
|
25
50
|
className = "",
|
|
26
51
|
onChange,
|
|
27
52
|
onKeyDown,
|
|
53
|
+
onPaste,
|
|
28
54
|
...rest
|
|
29
55
|
}, ref) => {
|
|
30
56
|
const baseClass = "ds-composer";
|
|
@@ -34,7 +60,17 @@ const Composer = React.forwardRef(
|
|
|
34
60
|
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? "");
|
|
35
61
|
const currentValue = isControlled ? value : uncontrolledValue;
|
|
36
62
|
const disabled = Boolean(rest.disabled);
|
|
37
|
-
const
|
|
63
|
+
const queued = files ?? [];
|
|
64
|
+
const hasFiles = queued.length > 0;
|
|
65
|
+
const armed = Boolean(onFilesSelected);
|
|
66
|
+
const anyUploading = queued.some((file) => file.status === "uploading");
|
|
67
|
+
const anyReady = queued.some((file) => file.status === "ready");
|
|
68
|
+
const canSend = !disabled && !anyUploading && (currentValue.trim() !== "" || anyReady);
|
|
69
|
+
const fileInputRef = useRef(null);
|
|
70
|
+
const trayRef = useRef(null);
|
|
71
|
+
const [dragging, setDragging] = useState(false);
|
|
72
|
+
const dragDepth = useRef(0);
|
|
73
|
+
const [announcement, setAnnouncement] = useState("");
|
|
38
74
|
const setTextareaRef = (node) => {
|
|
39
75
|
textareaRef.current = node;
|
|
40
76
|
if (typeof ref === "function") ref(node);
|
|
@@ -57,6 +93,92 @@ const Composer = React.forwardRef(
|
|
|
57
93
|
observer.observe(node);
|
|
58
94
|
return () => observer.disconnect();
|
|
59
95
|
}, []);
|
|
96
|
+
const previousFiles = useRef(queued);
|
|
97
|
+
const announceRef = useRef(formatFileAnnouncement);
|
|
98
|
+
announceRef.current = formatFileAnnouncement;
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
const before = previousFiles.current;
|
|
101
|
+
const now = files ?? [];
|
|
102
|
+
previousFiles.current = now;
|
|
103
|
+
if (before === now) return;
|
|
104
|
+
const beforeById = new Map(before.map((file) => [file.id, file]));
|
|
105
|
+
const nowIds = new Set(now.map((file) => file.id));
|
|
106
|
+
const added = now.filter((file) => !beforeById.has(file.id)).map((file) => file.name);
|
|
107
|
+
const removed = before.filter((file) => !nowIds.has(file.id)).map((file) => file.name);
|
|
108
|
+
const failed = now.filter((file) => file.status === "error" && beforeById.get(file.id)?.status !== "error").map((file) => file.name);
|
|
109
|
+
const parts = [
|
|
110
|
+
failed.length > 0 ? announceRef.current({ type: "failed", names: failed }) : "",
|
|
111
|
+
added.length > 0 ? announceRef.current({ type: "added", names: added }) : "",
|
|
112
|
+
removed.length > 0 ? announceRef.current({ type: "removed", names: removed }) : ""
|
|
113
|
+
].filter(Boolean);
|
|
114
|
+
if (parts.length > 0) setAnnouncement(parts.join(". "));
|
|
115
|
+
}, [files]);
|
|
116
|
+
const focusAfterRemove = useRef(null);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
const index = focusAfterRemove.current;
|
|
119
|
+
if (index === null) return;
|
|
120
|
+
focusAfterRemove.current = null;
|
|
121
|
+
const buttons = trayRef.current?.querySelectorAll(
|
|
122
|
+
".ds-attachment-tile__remove"
|
|
123
|
+
);
|
|
124
|
+
const next = buttons?.[Math.min(index, (buttons?.length ?? 0) - 1)];
|
|
125
|
+
if (next) next.focus();
|
|
126
|
+
else textareaRef.current?.focus();
|
|
127
|
+
}, [files]);
|
|
128
|
+
const handleFileRemove = (id, index) => {
|
|
129
|
+
focusAfterRemove.current = index;
|
|
130
|
+
onFileRemove?.(id);
|
|
131
|
+
};
|
|
132
|
+
const handlePick = (event) => {
|
|
133
|
+
const picked = Array.from(event.target.files ?? []);
|
|
134
|
+
event.target.value = "";
|
|
135
|
+
if (picked.length > 0) onFilesSelected?.(picked);
|
|
136
|
+
};
|
|
137
|
+
const handlePaste = (event) => {
|
|
138
|
+
onPaste?.(event);
|
|
139
|
+
if (event.defaultPrevented) return;
|
|
140
|
+
const pasted = Array.from(event.clipboardData?.files ?? []);
|
|
141
|
+
if (armed && pasted.length > 0) {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
onFilesSelected?.(pasted);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (pasteThreshold === void 0 || !onPasteAsAttachment) return;
|
|
147
|
+
const text = event.clipboardData?.getData("text/plain") ?? "";
|
|
148
|
+
if (text.length >= pasteThreshold) {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
onPasteAsAttachment(text);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const takesDrag = (event) => armed && !disabled && dragCarriesFiles(event);
|
|
154
|
+
const handleDragEnter = (event) => {
|
|
155
|
+
if (!takesDrag(event)) return;
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
event.stopPropagation();
|
|
158
|
+
dragDepth.current += 1;
|
|
159
|
+
setDragging(true);
|
|
160
|
+
};
|
|
161
|
+
const handleDragOver = (event) => {
|
|
162
|
+
if (!takesDrag(event)) return;
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
event.stopPropagation();
|
|
165
|
+
event.dataTransfer.dropEffect = "copy";
|
|
166
|
+
};
|
|
167
|
+
const handleDragLeave = (event) => {
|
|
168
|
+
if (!takesDrag(event)) return;
|
|
169
|
+
event.stopPropagation();
|
|
170
|
+
dragDepth.current = Math.max(0, dragDepth.current - 1);
|
|
171
|
+
if (dragDepth.current === 0) setDragging(false);
|
|
172
|
+
};
|
|
173
|
+
const handleDrop = (event) => {
|
|
174
|
+
if (!takesDrag(event)) return;
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
event.stopPropagation();
|
|
177
|
+
dragDepth.current = 0;
|
|
178
|
+
setDragging(false);
|
|
179
|
+
const dropped = Array.from(event.dataTransfer.files ?? []);
|
|
180
|
+
if (dropped.length > 0) onFilesSelected?.(dropped);
|
|
181
|
+
};
|
|
60
182
|
const submit = () => {
|
|
61
183
|
if (streaming || !canSend) return;
|
|
62
184
|
onSubmit?.(currentValue);
|
|
@@ -83,6 +205,7 @@ const Composer = React.forwardRef(
|
|
|
83
205
|
aiGlow ? `${baseClass}--ai-glow` : "",
|
|
84
206
|
streaming ? `${baseClass}--streaming` : "",
|
|
85
207
|
disabled ? `${baseClass}--disabled` : "",
|
|
208
|
+
dragging ? `${baseClass}--dragging` : "",
|
|
86
209
|
// The above placement moves the text onto the chip's rail; the shell
|
|
87
210
|
// needs to know, since the chip is no longer inside it.
|
|
88
211
|
context && contextPlacement === "above" ? `${baseClass}--context-above` : "",
|
|
@@ -107,8 +230,28 @@ const Composer = React.forwardRef(
|
|
|
107
230
|
className: classes,
|
|
108
231
|
style: { "--ds-composer-max-rows": maxRows },
|
|
109
232
|
onClick: handleShellClick,
|
|
233
|
+
onDragEnter: armed ? handleDragEnter : void 0,
|
|
234
|
+
onDragOver: armed ? handleDragOver : void 0,
|
|
235
|
+
onDragLeave: armed ? handleDragLeave : void 0,
|
|
236
|
+
onDrop: armed ? handleDrop : void 0,
|
|
110
237
|
children: [
|
|
111
238
|
!contextAbove && contextChip,
|
|
239
|
+
hasFiles && /* @__PURE__ */ jsx("ul", { className: `${baseClass}__tray`, ref: trayRef, "aria-label": filesLabel, children: queued.map((file, index) => /* @__PURE__ */ jsx("li", { className: `${baseClass}__tray-item`, children: /* @__PURE__ */ jsx(
|
|
240
|
+
AttachmentTile,
|
|
241
|
+
{
|
|
242
|
+
name: file.name,
|
|
243
|
+
kind: file.kind,
|
|
244
|
+
status: file.status,
|
|
245
|
+
progress: file.progress,
|
|
246
|
+
error: file.error,
|
|
247
|
+
previewSrc: file.previewSrc,
|
|
248
|
+
previewAlt: file.previewAlt,
|
|
249
|
+
excerpt: file.excerpt,
|
|
250
|
+
meta: file.size === void 0 ? void 0 : formatBytes(file.size),
|
|
251
|
+
onClick: onFileClick ? () => onFileClick(file) : void 0,
|
|
252
|
+
onRemove: onFileRemove && !disabled ? () => handleFileRemove(file.id, index) : void 0
|
|
253
|
+
}
|
|
254
|
+
) }, file.id)) }),
|
|
112
255
|
attachments && /* @__PURE__ */ jsx("div", { className: `${baseClass}__attachments`, children: attachments }),
|
|
113
256
|
/* @__PURE__ */ jsx("div", { className: `${baseClass}__content`, ref: contentRef, children: /* @__PURE__ */ jsx(
|
|
114
257
|
"textarea",
|
|
@@ -120,11 +263,24 @@ const Composer = React.forwardRef(
|
|
|
120
263
|
value: currentValue,
|
|
121
264
|
"aria-label": ariaLabel,
|
|
122
265
|
onChange: handleChange,
|
|
123
|
-
onKeyDown: handleKeyDown
|
|
266
|
+
onKeyDown: handleKeyDown,
|
|
267
|
+
onPaste: handlePaste
|
|
124
268
|
}
|
|
125
269
|
) }),
|
|
126
270
|
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__footer`, children: [
|
|
127
|
-
actions && /* @__PURE__ */
|
|
271
|
+
(actions || armed && attachButton) && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__actions`, children: [
|
|
272
|
+
armed && attachButton && /* @__PURE__ */ jsx(
|
|
273
|
+
CircularButton,
|
|
274
|
+
{
|
|
275
|
+
icon: "add",
|
|
276
|
+
variant: "tertiary",
|
|
277
|
+
ariaLabel: attachLabel,
|
|
278
|
+
disabled,
|
|
279
|
+
onClick: () => fileInputRef.current?.click()
|
|
280
|
+
}
|
|
281
|
+
),
|
|
282
|
+
actions
|
|
283
|
+
] }),
|
|
128
284
|
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__trailing`, children: [
|
|
129
285
|
trailingActions,
|
|
130
286
|
streaming ? /* @__PURE__ */ jsx(
|
|
@@ -147,7 +303,24 @@ const Composer = React.forwardRef(
|
|
|
147
303
|
}
|
|
148
304
|
)
|
|
149
305
|
] })
|
|
150
|
-
] })
|
|
306
|
+
] }),
|
|
307
|
+
armed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
308
|
+
/* @__PURE__ */ jsx(
|
|
309
|
+
"input",
|
|
310
|
+
{
|
|
311
|
+
ref: fileInputRef,
|
|
312
|
+
className: `${baseClass}__file-input`,
|
|
313
|
+
type: "file",
|
|
314
|
+
accept,
|
|
315
|
+
multiple,
|
|
316
|
+
tabIndex: -1,
|
|
317
|
+
"aria-hidden": "true",
|
|
318
|
+
onChange: handlePick
|
|
319
|
+
}
|
|
320
|
+
),
|
|
321
|
+
/* @__PURE__ */ jsx("div", { className: `${baseClass}__drop-hint`, "aria-hidden": "true", children: dropLabel })
|
|
322
|
+
] }),
|
|
323
|
+
(armed || hasFiles) && /* @__PURE__ */ jsx("span", { className: `${baseClass}__sr-only`, role: "status", children: announcement })
|
|
151
324
|
]
|
|
152
325
|
}
|
|
153
326
|
);
|
|
@@ -28,14 +28,16 @@ type DocumentChipOwnProps = {
|
|
|
28
28
|
export interface DocumentChipProps extends DocumentChipOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof DocumentChipOwnProps> {
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* DocumentChip is
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
31
|
+
* DocumentChip is the earlier, one-row file reference: a type icon, name,
|
|
32
|
+
* metadata line, upload progress, and an optional remove button. It keeps
|
|
33
|
+
* working for existing consumers, and new work uses the attachment family:
|
|
34
|
+
* AttachmentTile for a file, Composer's `files` for a queue, and
|
|
35
|
+
* AttachmentGroup for the files on a sent message.
|
|
36
36
|
*
|
|
37
37
|
* The root is always a `<div>`; passing `onClick` turns the body into a
|
|
38
38
|
* `<button>` so click and remove coexist without nesting controls.
|
|
39
|
+
*
|
|
40
|
+
* @deprecated Use `AttachmentTile` instead.
|
|
39
41
|
*/
|
|
40
42
|
export declare const DocumentChip: React.ForwardRefExoticComponent<DocumentChipProps & React.RefAttributes<HTMLDivElement>>;
|
|
41
43
|
export {};
|
|
@@ -3,14 +3,8 @@ import { jsxs, jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import React, { useState, useRef, useId } from "react";
|
|
4
4
|
import { Field } from "../Field/Field.js";
|
|
5
5
|
import "./FileInput.css";
|
|
6
|
+
import { formatBytes } from "../Attachment/fileTypes.js";
|
|
6
7
|
import "../../fonts/material-symbols.css";
|
|
7
|
-
const formatBytes = (bytes) => {
|
|
8
|
-
if (bytes === 0) return "0 B";
|
|
9
|
-
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
10
|
-
const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
|
|
11
|
-
const value = bytes / Math.pow(1024, exponent);
|
|
12
|
-
return `${exponent === 0 ? value : value.toFixed(1)} ${units[exponent]}`;
|
|
13
|
-
};
|
|
14
8
|
const FileInput = React.forwardRef(
|
|
15
9
|
({
|
|
16
10
|
label,
|
|
@@ -18,7 +18,7 @@ type MessageCardOwnProps = {
|
|
|
18
18
|
actions?: React.ReactNode;
|
|
19
19
|
/** Additional CSS classes */
|
|
20
20
|
className?: string;
|
|
21
|
-
/** Body content between the description and the footer — rich content, a Prose block,
|
|
21
|
+
/** Body content between the description and the footer — rich content, a Prose block, an AttachmentGroup. */
|
|
22
22
|
children?: React.ReactNode;
|
|
23
23
|
};
|
|
24
24
|
export interface MessageCardProps extends MessageCardOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof MessageCardOwnProps> {
|
package/components/registry.json
CHANGED
|
@@ -161,6 +161,42 @@
|
|
|
161
161
|
"folder": "Chart",
|
|
162
162
|
"recharts": true
|
|
163
163
|
},
|
|
164
|
+
{
|
|
165
|
+
"name": "AttachmentDropZone",
|
|
166
|
+
"label": "Attachment drop zone",
|
|
167
|
+
"slug": "attachment-drop-zone",
|
|
168
|
+
"description": "A wrapper that makes a whole surface a file drop target, with an outlined overlay while files hover over it.",
|
|
169
|
+
"category": "ai",
|
|
170
|
+
"client": true,
|
|
171
|
+
"folder": "Attachment"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"name": "AttachmentGroup",
|
|
175
|
+
"label": "Attachment group",
|
|
176
|
+
"slug": "attachment-group",
|
|
177
|
+
"description": "The files on a sent message: one picture at its own shape, or a grid of tiles that collapses behind a count.",
|
|
178
|
+
"category": "ai",
|
|
179
|
+
"client": true,
|
|
180
|
+
"folder": "Attachment"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "AttachmentTile",
|
|
184
|
+
"label": "Attachment tile",
|
|
185
|
+
"slug": "attachment-tile",
|
|
186
|
+
"description": "One file as a square: a picture, a coloured mark for well-known formats, or a neutral badge, with upload states.",
|
|
187
|
+
"category": "ai",
|
|
188
|
+
"client": false,
|
|
189
|
+
"folder": "Attachment"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"name": "AttachmentViewer",
|
|
193
|
+
"label": "Attachment viewer",
|
|
194
|
+
"slug": "attachment-viewer",
|
|
195
|
+
"description": "A preview dialog for a message's files, with download, stepping, and plain states for files it cannot show.",
|
|
196
|
+
"category": "ai",
|
|
197
|
+
"client": true,
|
|
198
|
+
"folder": "Attachment"
|
|
199
|
+
},
|
|
164
200
|
{
|
|
165
201
|
"name": "Avatar",
|
|
166
202
|
"label": "Avatar",
|
|
@@ -369,7 +405,7 @@
|
|
|
369
405
|
"name": "Composer",
|
|
370
406
|
"label": "Composer",
|
|
371
407
|
"slug": "composer",
|
|
372
|
-
"description": "An auto-growing message input with send and stop states, a page-context note,
|
|
408
|
+
"description": "An auto-growing message input with send and stop states, a page-context note, a file tray fed by picker, paste and drop, and Enter-to-send.",
|
|
373
409
|
"category": "ai",
|
|
374
410
|
"client": true
|
|
375
411
|
},
|
|
@@ -441,7 +477,7 @@
|
|
|
441
477
|
"name": "DocumentChip",
|
|
442
478
|
"label": "Document chip",
|
|
443
479
|
"slug": "document-chip",
|
|
444
|
-
"description": "A
|
|
480
|
+
"description": "A legacy one-row file reference, superseded by the attachment tile and kept for existing consumers.",
|
|
445
481
|
"category": "ai",
|
|
446
482
|
"client": false
|
|
447
483
|
},
|
|
@@ -161,6 +161,42 @@ declare const _default: {
|
|
|
161
161
|
"folder": "Chart",
|
|
162
162
|
"recharts": true
|
|
163
163
|
},
|
|
164
|
+
{
|
|
165
|
+
"name": "AttachmentDropZone",
|
|
166
|
+
"label": "Attachment drop zone",
|
|
167
|
+
"slug": "attachment-drop-zone",
|
|
168
|
+
"description": "A wrapper that makes a whole surface a file drop target, with an outlined overlay while files hover over it.",
|
|
169
|
+
"category": "ai",
|
|
170
|
+
"client": true,
|
|
171
|
+
"folder": "Attachment"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"name": "AttachmentGroup",
|
|
175
|
+
"label": "Attachment group",
|
|
176
|
+
"slug": "attachment-group",
|
|
177
|
+
"description": "The files on a sent message: one picture at its own shape, or a grid of tiles that collapses behind a count.",
|
|
178
|
+
"category": "ai",
|
|
179
|
+
"client": true,
|
|
180
|
+
"folder": "Attachment"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "AttachmentTile",
|
|
184
|
+
"label": "Attachment tile",
|
|
185
|
+
"slug": "attachment-tile",
|
|
186
|
+
"description": "One file as a square: a picture, a coloured mark for well-known formats, or a neutral badge, with upload states.",
|
|
187
|
+
"category": "ai",
|
|
188
|
+
"client": false,
|
|
189
|
+
"folder": "Attachment"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"name": "AttachmentViewer",
|
|
193
|
+
"label": "Attachment viewer",
|
|
194
|
+
"slug": "attachment-viewer",
|
|
195
|
+
"description": "A preview dialog for a message's files, with download, stepping, and plain states for files it cannot show.",
|
|
196
|
+
"category": "ai",
|
|
197
|
+
"client": true,
|
|
198
|
+
"folder": "Attachment"
|
|
199
|
+
},
|
|
164
200
|
{
|
|
165
201
|
"name": "Avatar",
|
|
166
202
|
"label": "Avatar",
|
|
@@ -369,7 +405,7 @@ declare const _default: {
|
|
|
369
405
|
"name": "Composer",
|
|
370
406
|
"label": "Composer",
|
|
371
407
|
"slug": "composer",
|
|
372
|
-
"description": "An auto-growing message input with send and stop states, a page-context note,
|
|
408
|
+
"description": "An auto-growing message input with send and stop states, a page-context note, a file tray fed by picker, paste and drop, and Enter-to-send.",
|
|
373
409
|
"category": "ai",
|
|
374
410
|
"client": true
|
|
375
411
|
},
|
|
@@ -441,7 +477,7 @@ declare const _default: {
|
|
|
441
477
|
"name": "DocumentChip",
|
|
442
478
|
"label": "Document chip",
|
|
443
479
|
"slug": "document-chip",
|
|
444
|
-
"description": "A
|
|
480
|
+
"description": "A legacy one-row file reference, superseded by the attachment tile and kept for existing consumers.",
|
|
445
481
|
"category": "ai",
|
|
446
482
|
"client": false
|
|
447
483
|
},
|