@agent-native/toolkit 0.211.0 → 0.212.0-nightly-20261021010049
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-native.eject.json +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +23 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +23 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +23 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +23 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +23 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +23 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +23 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +23 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +23 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +23 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +23 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +23 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/sharing/OwnershipFilter.d.ts +8 -0
- package/dist/app/sharing/OwnershipFilter.d.ts.map +1 -0
- package/dist/app/sharing/OwnershipFilter.js +20 -0
- package/dist/app/sharing/OwnershipFilter.js.map +1 -0
- package/dist/app/sharing/ShareButton.d.ts +43 -1
- package/dist/app/sharing/ShareButton.d.ts.map +1 -1
- package/dist/app/sharing/ShareButton.js +102 -63
- package/dist/app/sharing/ShareButton.js.map +1 -1
- package/dist/app/sharing/ShareDialog.d.ts +3 -1
- package/dist/app/sharing/ShareDialog.d.ts.map +1 -1
- package/dist/app/sharing/ShareDialog.js +18 -12
- package/dist/app/sharing/ShareDialog.js.map +1 -1
- package/dist/app/sharing/ShareVisibilityConfirmDialog.d.ts +20 -0
- package/dist/app/sharing/ShareVisibilityConfirmDialog.d.ts.map +1 -0
- package/dist/app/sharing/ShareVisibilityConfirmDialog.js +57 -0
- package/dist/app/sharing/ShareVisibilityConfirmDialog.js.map +1 -0
- package/dist/app/sharing/VisibilityBadge.d.ts +8 -1
- package/dist/app/sharing/VisibilityBadge.d.ts.map +1 -1
- package/dist/app/sharing/VisibilityBadge.js +16 -1
- package/dist/app/sharing/VisibilityBadge.js.map +1 -1
- package/dist/app/sharing/index.d.ts +2 -0
- package/dist/app/sharing/index.d.ts.map +1 -1
- package/dist/app/sharing/index.js +2 -0
- package/dist/app/sharing/index.js.map +1 -1
- package/dist/chat-history/ChatHistoryList.d.ts +19 -0
- package/dist/chat-history/ChatHistoryList.d.ts.map +1 -1
- package/dist/chat-history/ChatHistoryList.js +19 -7
- package/dist/chat-history/ChatHistoryList.js.map +1 -1
- package/dist/chat-history/ChatHistoryRail.d.ts +55 -8
- package/dist/chat-history/ChatHistoryRail.d.ts.map +1 -1
- package/dist/chat-history/ChatHistoryRail.js +33 -7
- package/dist/chat-history/ChatHistoryRail.js.map +1 -1
- package/dist/chat-history/index.d.ts +3 -3
- package/dist/chat-history/index.d.ts.map +1 -1
- package/dist/chat-history/index.js +1 -1
- package/dist/chat-history/index.js.map +1 -1
- package/dist/chat-history/useChatHistoryRailController.d.ts +44 -0
- package/dist/chat-history/useChatHistoryRailController.d.ts.map +1 -1
- package/dist/chat-history/useChatHistoryRailController.js +46 -0
- package/dist/chat-history/useChatHistoryRailController.js.map +1 -1
- package/dist/chat-history.css +29 -0
- package/dist/composer/TiptapComposer.d.ts +7 -0
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +15 -2
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/sharing/OwnershipFilter.d.ts +21 -0
- package/dist/sharing/OwnershipFilter.d.ts.map +1 -0
- package/dist/sharing/OwnershipFilter.js +30 -0
- package/dist/sharing/OwnershipFilter.js.map +1 -0
- package/dist/sharing/VisibilityBadge.d.ts +12 -2
- package/dist/sharing/VisibilityBadge.d.ts.map +1 -1
- package/dist/sharing/VisibilityBadge.js +13 -4
- package/dist/sharing/VisibilityBadge.js.map +1 -1
- package/dist/sharing/index.d.ts +2 -1
- package/dist/sharing/index.d.ts.map +1 -1
- package/dist/sharing/index.js +2 -1
- package/dist/sharing/index.js.map +1 -1
- package/dist/ui/alert-dialog.d.ts +2 -0
- package/dist/ui/alert-dialog.d.ts.map +1 -1
- package/dist/ui/alert-dialog.js +1 -1
- package/dist/ui/alert-dialog.js.map +1 -1
- package/dist/ui/tooltip.d.ts +6 -1
- package/dist/ui/tooltip.d.ts.map +1 -1
- package/dist/ui/tooltip.js +13 -1
- package/dist/ui/tooltip.js.map +1 -1
- package/package.json +4 -4
- package/src/app/i18n/catalogs/ar-SA.ts +26 -0
- package/src/app/i18n/catalogs/de-DE.ts +25 -0
- package/src/app/i18n/catalogs/en-US.ts +25 -0
- package/src/app/i18n/catalogs/es-ES.ts +25 -0
- package/src/app/i18n/catalogs/fr-FR.ts +25 -0
- package/src/app/i18n/catalogs/hi-IN.ts +25 -0
- package/src/app/i18n/catalogs/ja-JP.ts +25 -0
- package/src/app/i18n/catalogs/ko-KR.ts +25 -0
- package/src/app/i18n/catalogs/pt-BR.ts +25 -0
- package/src/app/i18n/catalogs/zh-CN.ts +25 -0
- package/src/app/i18n/catalogs/zh-TW.ts +25 -0
- package/src/app/sharing/OwnershipFilter.tsx +35 -0
- package/src/app/sharing/ShareButton.tsx +384 -259
- package/src/app/sharing/ShareDialog.spec.tsx +50 -0
- package/src/app/sharing/ShareDialog.tsx +51 -15
- package/src/app/sharing/ShareVisibilityConfirmDialog.spec.tsx +138 -0
- package/src/app/sharing/ShareVisibilityConfirmDialog.tsx +124 -0
- package/src/app/sharing/ShareVisibilityKeyboard.spec.tsx +244 -0
- package/src/app/sharing/VisibilityBadge.spec.tsx +65 -0
- package/src/app/sharing/VisibilityBadge.tsx +31 -3
- package/src/app/sharing/index.ts +2 -0
- package/src/app/sharing/share-visibility-mutation-lifecycle.spec.tsx +1107 -0
- package/src/app/sharing/useShareButtonController.spec.tsx +226 -37
- package/src/app/sharing/useShareDialogController.spec.tsx +357 -9
- package/src/chat-history/ChatHistoryList.spec.tsx +157 -0
- package/src/chat-history/ChatHistoryList.tsx +104 -10
- package/src/chat-history/ChatHistoryRail.spec.tsx +50 -1
- package/src/chat-history/ChatHistoryRail.tsx +177 -26
- package/src/chat-history/ChatHistoryRailSections.spec.tsx +410 -0
- package/src/chat-history/index.ts +8 -0
- package/src/chat-history/useChatHistoryRailController.ts +94 -0
- package/src/chat-history/useChatHistoryRailSectionsController.spec.tsx +133 -0
- package/src/chat-history.css +29 -0
- package/src/composer/TiptapComposer.spec.ts +19 -0
- package/src/composer/TiptapComposer.tsx +26 -5
- package/src/sharing/OwnershipFilter.spec.tsx +108 -0
- package/src/sharing/OwnershipFilter.tsx +103 -0
- package/src/sharing/VisibilityBadge.spec.ts +53 -35
- package/src/sharing/VisibilityBadge.tsx +23 -3
- package/src/sharing/index.ts +11 -0
- package/src/ui/alert-dialog.tsx +7 -2
- package/src/ui/tooltip.tsx +22 -1
|
@@ -9,23 +9,48 @@ import {
|
|
|
9
9
|
} from "@tabler/icons-react";
|
|
10
10
|
import React, { useMemo, useRef, useState } from "react";
|
|
11
11
|
|
|
12
|
+
import { Skeleton } from "../design-system/components.js";
|
|
12
13
|
import { cn } from "../utils.js";
|
|
13
14
|
|
|
15
|
+
/** How the viewer got access to a chat; the app supplies the translated copy. */
|
|
16
|
+
export type ChatHistoryAccessBasis =
|
|
17
|
+
| "owner"
|
|
18
|
+
| "direct"
|
|
19
|
+
| "organization"
|
|
20
|
+
| "public";
|
|
21
|
+
|
|
14
22
|
export interface ChatHistoryItem {
|
|
15
23
|
id: string;
|
|
16
24
|
title: React.ReactNode;
|
|
17
25
|
titleText?: string;
|
|
18
26
|
subtitle?: React.ReactNode;
|
|
19
27
|
detail?: React.ReactNode;
|
|
28
|
+
/** Already-translated owner name, shown on shared rows. */
|
|
29
|
+
ownerLabel?: React.ReactNode;
|
|
30
|
+
/** Already-translated access description, e.g. "Shared with you". */
|
|
31
|
+
accessLabel?: React.ReactNode;
|
|
32
|
+
accessBasis?: ChatHistoryAccessBasis;
|
|
20
33
|
timestamp?: React.ReactNode;
|
|
21
34
|
pinned?: boolean;
|
|
22
35
|
disabled?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Presentation only: drops the row menu and context menu while the row stays
|
|
38
|
+
* selectable. The app decides which rows (e.g. not owned) get no actions.
|
|
39
|
+
*/
|
|
40
|
+
hideActions?: boolean;
|
|
23
41
|
}
|
|
24
42
|
|
|
25
43
|
export interface ChatHistorySection {
|
|
26
44
|
id: string;
|
|
27
45
|
label?: React.ReactNode;
|
|
28
46
|
items: ChatHistoryItem[];
|
|
47
|
+
/** Section-level states; the section stays visible while any is set. */
|
|
48
|
+
/** Initial load: skeleton rows until the first rows arrive. */
|
|
49
|
+
loading?: boolean;
|
|
50
|
+
/** Shown above any rows already loaded: a failed load never reads as empty. */
|
|
51
|
+
error?: React.ReactNode;
|
|
52
|
+
emptyLabel?: React.ReactNode;
|
|
53
|
+
footer?: React.ReactNode;
|
|
29
54
|
}
|
|
30
55
|
|
|
31
56
|
export interface ChatHistoryListLabels {
|
|
@@ -134,6 +159,9 @@ export function ChatHistoryList({
|
|
|
134
159
|
0,
|
|
135
160
|
);
|
|
136
161
|
const hasSearchValue = Boolean(searchValue?.trim());
|
|
162
|
+
const hasSectionState = resolvedSections.some((section) =>
|
|
163
|
+
sectionHasState(section, hasSearchValue),
|
|
164
|
+
);
|
|
137
165
|
|
|
138
166
|
return (
|
|
139
167
|
<div
|
|
@@ -165,20 +193,54 @@ export function ChatHistoryList({
|
|
|
165
193
|
</div>
|
|
166
194
|
) : loading ? (
|
|
167
195
|
<div className="an-chat-history__state">{loadingLabel}</div>
|
|
168
|
-
) : totalCount === 0 ? (
|
|
196
|
+
) : totalCount === 0 && !hasSectionState ? (
|
|
169
197
|
<div className="an-chat-history__state">
|
|
170
198
|
{hasSearchValue ? emptySearchLabel : emptyLabel}
|
|
171
199
|
</div>
|
|
172
200
|
) : (
|
|
173
201
|
resolvedSections.map(
|
|
174
202
|
(section) =>
|
|
175
|
-
section.items.length > 0
|
|
176
|
-
|
|
203
|
+
(section.items.length > 0 ||
|
|
204
|
+
sectionHasState(section, hasSearchValue)) && (
|
|
205
|
+
<div
|
|
206
|
+
key={section.id}
|
|
207
|
+
className="an-chat-history__section"
|
|
208
|
+
data-section-id={section.id}
|
|
209
|
+
>
|
|
177
210
|
{section.label && (
|
|
178
211
|
<div className="an-chat-history__section-label">
|
|
179
212
|
{section.label}
|
|
180
213
|
</div>
|
|
181
214
|
)}
|
|
215
|
+
{section.error ? (
|
|
216
|
+
<div
|
|
217
|
+
className="an-chat-history__state an-chat-history__state--error"
|
|
218
|
+
role="alert"
|
|
219
|
+
>
|
|
220
|
+
{section.error}
|
|
221
|
+
</div>
|
|
222
|
+
) : section.loading && section.items.length === 0 ? (
|
|
223
|
+
<div
|
|
224
|
+
className="an-chat-history__skeletons"
|
|
225
|
+
aria-busy="true"
|
|
226
|
+
data-testid="chat-history-section-skeleton"
|
|
227
|
+
>
|
|
228
|
+
{["100%", "80%", "60%"].map((width) => (
|
|
229
|
+
<Skeleton
|
|
230
|
+
key={width}
|
|
231
|
+
shape="line"
|
|
232
|
+
width={width}
|
|
233
|
+
height={12}
|
|
234
|
+
/>
|
|
235
|
+
))}
|
|
236
|
+
</div>
|
|
237
|
+
) : section.items.length === 0 &&
|
|
238
|
+
!hasSearchValue &&
|
|
239
|
+
section.emptyLabel ? (
|
|
240
|
+
<div className="an-chat-history__state">
|
|
241
|
+
{section.emptyLabel}
|
|
242
|
+
</div>
|
|
243
|
+
) : null}
|
|
182
244
|
{section.items.map((item) => (
|
|
183
245
|
<ChatHistoryRow
|
|
184
246
|
key={item.id}
|
|
@@ -195,6 +257,7 @@ export function ChatHistoryList({
|
|
|
195
257
|
labels={resolvedLabels}
|
|
196
258
|
/>
|
|
197
259
|
))}
|
|
260
|
+
{section.footer}
|
|
198
261
|
</div>
|
|
199
262
|
),
|
|
200
263
|
)
|
|
@@ -205,6 +268,18 @@ export function ChatHistoryList({
|
|
|
205
268
|
);
|
|
206
269
|
}
|
|
207
270
|
|
|
271
|
+
function sectionHasState(
|
|
272
|
+
section: ChatHistorySection,
|
|
273
|
+
searching: boolean,
|
|
274
|
+
): boolean {
|
|
275
|
+
return Boolean(
|
|
276
|
+
section.loading ||
|
|
277
|
+
section.error ||
|
|
278
|
+
(!searching && section.emptyLabel) ||
|
|
279
|
+
section.footer,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
208
283
|
type ChatHistoryRowProps = {
|
|
209
284
|
item: ChatHistoryItem;
|
|
210
285
|
active: boolean;
|
|
@@ -240,13 +315,15 @@ const ChatHistoryRow = React.memo(function ChatHistoryRow({
|
|
|
240
315
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
241
316
|
const renameInputRef = useRef<HTMLInputElement | null>(null);
|
|
242
317
|
|
|
243
|
-
const hasMenu =
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
318
|
+
const hasMenu =
|
|
319
|
+
!item.hideActions &&
|
|
320
|
+
Boolean(
|
|
321
|
+
onTogglePin ||
|
|
322
|
+
onRename ||
|
|
323
|
+
onDelete ||
|
|
324
|
+
renderRowActions ||
|
|
325
|
+
renderAdditionalRowActions,
|
|
326
|
+
);
|
|
250
327
|
|
|
251
328
|
function startRename() {
|
|
252
329
|
setDraftTitle(item.titleText ?? "");
|
|
@@ -337,6 +414,23 @@ const ChatHistoryRow = React.memo(function ChatHistoryRow({
|
|
|
337
414
|
{item.detail != null && (
|
|
338
415
|
<div className="an-chat-history-row__detail">{item.detail}</div>
|
|
339
416
|
)}
|
|
417
|
+
{(item.ownerLabel != null || item.accessLabel != null) && (
|
|
418
|
+
<div
|
|
419
|
+
className="an-chat-history-row__access"
|
|
420
|
+
data-access-basis={item.accessBasis}
|
|
421
|
+
>
|
|
422
|
+
{item.ownerLabel != null && (
|
|
423
|
+
<span className="an-chat-history-row__owner">
|
|
424
|
+
{item.ownerLabel}
|
|
425
|
+
</span>
|
|
426
|
+
)}
|
|
427
|
+
{item.accessLabel != null && (
|
|
428
|
+
<span className="an-chat-history-row__access-label">
|
|
429
|
+
{item.accessLabel}
|
|
430
|
+
</span>
|
|
431
|
+
)}
|
|
432
|
+
</div>
|
|
433
|
+
)}
|
|
340
434
|
</button>
|
|
341
435
|
)}
|
|
342
436
|
|
|
@@ -8,7 +8,10 @@ import { ActionButton, IconButton } from "../design-system/components.js";
|
|
|
8
8
|
import { defineDesignSystem } from "../design-system/definition.js";
|
|
9
9
|
import { ToolkitProvider } from "../provider.js";
|
|
10
10
|
import type { ChatHistoryItem } from "./ChatHistoryList.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
ChatHistoryRail,
|
|
13
|
+
DefaultChatHistoryRailView,
|
|
14
|
+
} from "./ChatHistoryRail.js";
|
|
12
15
|
|
|
13
16
|
const railLabels = {
|
|
14
17
|
newChat: "New chat",
|
|
@@ -67,6 +70,24 @@ describe("ChatHistoryRail", () => {
|
|
|
67
70
|
expect(container.querySelectorAll(".an-chat-history-row")).toHaveLength(5);
|
|
68
71
|
});
|
|
69
72
|
|
|
73
|
+
it("keeps the flat rail's list-level empty state", () => {
|
|
74
|
+
act(() => {
|
|
75
|
+
root.render(
|
|
76
|
+
<ChatHistoryRail
|
|
77
|
+
items={[]}
|
|
78
|
+
onSelect={() => {}}
|
|
79
|
+
onNewChat={() => {}}
|
|
80
|
+
railLabels={railLabels}
|
|
81
|
+
emptyLabel="No chats yet"
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(
|
|
87
|
+
container.querySelector(".an-chat-history__state")?.textContent,
|
|
88
|
+
).toBe("No chats yet");
|
|
89
|
+
});
|
|
90
|
+
|
|
70
91
|
it("changes the disclosure glyph with its state, even when both labels match", () => {
|
|
71
92
|
act(() => {
|
|
72
93
|
root.render(
|
|
@@ -343,6 +364,34 @@ describe("ChatHistoryRail", () => {
|
|
|
343
364
|
expect(onNewChat).toHaveBeenCalledOnce();
|
|
344
365
|
});
|
|
345
366
|
|
|
367
|
+
it("renders the exported default view from the flat controller and listProps alone", () => {
|
|
368
|
+
const onNewChat = vi.fn();
|
|
369
|
+
act(() => {
|
|
370
|
+
root.render(
|
|
371
|
+
<ChatHistoryRail
|
|
372
|
+
items={makeItems(6)}
|
|
373
|
+
onSelect={() => {}}
|
|
374
|
+
onNewChat={onNewChat}
|
|
375
|
+
railLabels={railLabels}
|
|
376
|
+
renderRail={({ controller, listProps }) => (
|
|
377
|
+
<DefaultChatHistoryRailView
|
|
378
|
+
controller={controller}
|
|
379
|
+
listProps={listProps}
|
|
380
|
+
/>
|
|
381
|
+
)}
|
|
382
|
+
/>,
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
expect(container.querySelectorAll(".an-chat-history-row")).toHaveLength(5);
|
|
387
|
+
act(() =>
|
|
388
|
+
container
|
|
389
|
+
.querySelector<HTMLButtonElement>(".an-chat-history-rail__new-chat")
|
|
390
|
+
?.click(),
|
|
391
|
+
);
|
|
392
|
+
expect(onNewChat).toHaveBeenCalledOnce();
|
|
393
|
+
});
|
|
394
|
+
|
|
346
395
|
it("falls back to the default rail when a product renderer fails", () => {
|
|
347
396
|
vi.spyOn(console, "error").mockImplementation(() => {});
|
|
348
397
|
act(() => {
|
|
@@ -7,44 +7,180 @@ import {
|
|
|
7
7
|
ChatHistoryList,
|
|
8
8
|
type ChatHistoryItem,
|
|
9
9
|
type ChatHistoryListProps,
|
|
10
|
+
type ChatHistorySection,
|
|
10
11
|
} from "./ChatHistoryList.js";
|
|
11
12
|
import {
|
|
12
13
|
type ChatHistoryRailController,
|
|
14
|
+
type ChatHistoryRailSectionController,
|
|
15
|
+
type ChatHistoryRailSectionControllerLabels,
|
|
16
|
+
type ChatHistoryRailSectionInput,
|
|
13
17
|
useChatHistoryRailController,
|
|
18
|
+
useChatHistoryRailSectionsController,
|
|
14
19
|
} from "./useChatHistoryRailController.js";
|
|
15
20
|
|
|
16
21
|
export interface ChatHistoryRailLabels {
|
|
17
22
|
newChat: string;
|
|
18
23
|
showMore: string;
|
|
19
24
|
showLess: string;
|
|
25
|
+
/**
|
|
26
|
+
* Translated disclosure, load-more, and retry copy for every section.
|
|
27
|
+
* Required whenever the rail is given `sections`; there are no defaults.
|
|
28
|
+
*/
|
|
29
|
+
sections?: ChatHistoryRailSectionControllerLabels;
|
|
20
30
|
}
|
|
21
31
|
|
|
22
|
-
export interface
|
|
32
|
+
export interface ChatHistoryRailSection extends ChatHistoryRailSectionInput<ChatHistoryItem> {
|
|
33
|
+
label?: ReactNode;
|
|
34
|
+
/** Initial load: renders skeleton rows until the first rows arrive. */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/** A failed load, shown above any rows already loaded. */
|
|
37
|
+
error?: ReactNode;
|
|
38
|
+
onRetry?: () => void;
|
|
39
|
+
emptyLabel?: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type ChatHistoryRailListProps = Omit<
|
|
23
43
|
ChatHistoryListProps,
|
|
24
44
|
"footer" | "items" | "sections" | "variant"
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
interface ChatHistoryRailBaseProps extends ChatHistoryRailListProps {
|
|
27
48
|
onNewChat: () => void;
|
|
28
|
-
railLabels: ChatHistoryRailLabels;
|
|
29
49
|
previewCount?: number;
|
|
30
50
|
expandedCount?: number;
|
|
31
51
|
renderRail?: (context: ChatHistoryRailRenderContext) => ReactNode;
|
|
32
52
|
}
|
|
33
53
|
|
|
54
|
+
interface ChatHistoryRailFlatProps {
|
|
55
|
+
/** One flat list with a single disclosure. */
|
|
56
|
+
items?: ChatHistoryItem[];
|
|
57
|
+
sections?: undefined;
|
|
58
|
+
showNewChat?: undefined;
|
|
59
|
+
railLabels: ChatHistoryRailLabels;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface ChatHistoryRailSectionedProps {
|
|
63
|
+
/**
|
|
64
|
+
* Independent sections, each with its own preview, disclosure, paging, and
|
|
65
|
+
* loading/error/empty states. The app owns which rows each section holds.
|
|
66
|
+
*/
|
|
67
|
+
sections: ChatHistoryRailSection[];
|
|
68
|
+
items?: undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Set false when the surrounding chrome already owns New chat. `onNewChat`
|
|
71
|
+
* stays required and callable; only the rail's own button is dropped.
|
|
72
|
+
*/
|
|
73
|
+
showNewChat?: boolean;
|
|
74
|
+
railLabels: ChatHistoryRailLabels & {
|
|
75
|
+
sections: ChatHistoryRailSectionControllerLabels;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type ChatHistoryRailProps = ChatHistoryRailBaseProps &
|
|
80
|
+
(ChatHistoryRailFlatProps | ChatHistoryRailSectionedProps);
|
|
81
|
+
|
|
82
|
+
export interface ChatHistoryRailSectionRenderState {
|
|
83
|
+
section: ChatHistoryRailSection;
|
|
84
|
+
controller: ChatHistoryRailSectionController<ChatHistoryItem>;
|
|
85
|
+
}
|
|
86
|
+
|
|
34
87
|
export interface ChatHistoryRailRenderContext {
|
|
35
88
|
controller: ChatHistoryRailController<ChatHistoryItem>;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
89
|
+
/** Empty unless the rail was given `sections`. */
|
|
90
|
+
sections: ChatHistoryRailSectionRenderState[];
|
|
91
|
+
listProps: ChatHistoryRailListProps;
|
|
92
|
+
/** Sectioned rails only; defaults to true. */
|
|
93
|
+
showNewChat?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** `sections` stays optional so views written for the flat rail keep working. */
|
|
97
|
+
export type DefaultChatHistoryRailViewProps = Omit<
|
|
98
|
+
ChatHistoryRailRenderContext,
|
|
99
|
+
"sections"
|
|
100
|
+
> &
|
|
101
|
+
Partial<Pick<ChatHistoryRailRenderContext, "sections">>;
|
|
102
|
+
|
|
103
|
+
function SectionFooter({
|
|
104
|
+
section,
|
|
105
|
+
controller,
|
|
106
|
+
}: ChatHistoryRailSectionRenderState) {
|
|
107
|
+
const { labels, loadMore } = controller;
|
|
108
|
+
if (!controller.canExpand) return null;
|
|
109
|
+
return (
|
|
110
|
+
<div className="an-chat-history-rail__section-footer">
|
|
111
|
+
<ActionButton
|
|
112
|
+
type="button"
|
|
113
|
+
className="an-chat-history-rail__section-toggle"
|
|
114
|
+
emphasis="ghost"
|
|
115
|
+
size="compact"
|
|
116
|
+
trailingIcon={
|
|
117
|
+
controller.expanded ? (
|
|
118
|
+
<IconChevronUp size={13} strokeWidth={1.8} aria-hidden="true" />
|
|
119
|
+
) : (
|
|
120
|
+
<IconChevronDown size={13} strokeWidth={1.8} aria-hidden="true" />
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
onPress={controller.toggleExpanded}
|
|
124
|
+
aria-expanded={controller.expanded}
|
|
125
|
+
data-section-id={section.id}
|
|
126
|
+
>
|
|
127
|
+
<span>{controller.expanded ? labels.showLess : labels.showAll}</span>
|
|
128
|
+
</ActionButton>
|
|
129
|
+
{loadMore && (
|
|
130
|
+
<ActionButton
|
|
131
|
+
type="button"
|
|
132
|
+
className="an-chat-history-rail__load-more"
|
|
133
|
+
emphasis="ghost"
|
|
134
|
+
size="compact"
|
|
135
|
+
pending={controller.loadingMore}
|
|
136
|
+
disabled={controller.loadingMore}
|
|
137
|
+
onPress={loadMore}
|
|
138
|
+
>
|
|
139
|
+
<span>
|
|
140
|
+
{controller.loadingMore ? labels.loadingMore : labels.loadMore}
|
|
141
|
+
</span>
|
|
142
|
+
</ActionButton>
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
40
146
|
}
|
|
41
147
|
|
|
42
|
-
|
|
148
|
+
function toListSection(
|
|
149
|
+
state: ChatHistoryRailSectionRenderState,
|
|
150
|
+
): ChatHistorySection {
|
|
151
|
+
const { section, controller } = state;
|
|
152
|
+
return {
|
|
153
|
+
id: section.id,
|
|
154
|
+
label: section.label,
|
|
155
|
+
items: controller.visibleItems,
|
|
156
|
+
loading: section.loading,
|
|
157
|
+
error: section.error ? (
|
|
158
|
+
<>
|
|
159
|
+
<span>{section.error}</span>
|
|
160
|
+
{section.onRetry && (
|
|
161
|
+
<ActionButton
|
|
162
|
+
type="button"
|
|
163
|
+
className="an-chat-history-rail__retry"
|
|
164
|
+
emphasis="ghost"
|
|
165
|
+
size="compact"
|
|
166
|
+
onPress={section.onRetry}
|
|
167
|
+
>
|
|
168
|
+
<span>{controller.labels.retry}</span>
|
|
169
|
+
</ActionButton>
|
|
170
|
+
)}
|
|
171
|
+
</>
|
|
172
|
+
) : undefined,
|
|
173
|
+
emptyLabel: section.emptyLabel,
|
|
174
|
+
footer: controller.canExpand ? <SectionFooter {...state} /> : undefined,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
43
177
|
|
|
44
178
|
export function DefaultChatHistoryRailView({
|
|
45
179
|
controller,
|
|
180
|
+
sections = [],
|
|
46
181
|
listProps,
|
|
47
|
-
|
|
182
|
+
showNewChat = true,
|
|
183
|
+
}: DefaultChatHistoryRailViewProps) {
|
|
48
184
|
const {
|
|
49
185
|
canExpand,
|
|
50
186
|
disclosureLabel,
|
|
@@ -56,20 +192,22 @@ export function DefaultChatHistoryRailView({
|
|
|
56
192
|
} = controller;
|
|
57
193
|
const { className, emptyLabel, ...rest } = listProps;
|
|
58
194
|
|
|
59
|
-
const footer = (
|
|
195
|
+
const footer = (showNewChat || canExpand) && (
|
|
60
196
|
<div className="an-chat-history-rail__footer">
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
197
|
+
{showNewChat && (
|
|
198
|
+
<ActionButton
|
|
199
|
+
type="button"
|
|
200
|
+
className="an-chat-history-rail__new-chat"
|
|
201
|
+
emphasis="ghost"
|
|
202
|
+
size="compact"
|
|
203
|
+
leadingIcon={
|
|
204
|
+
<IconPlus size={13} strokeWidth={1.8} aria-hidden="true" />
|
|
205
|
+
}
|
|
206
|
+
onPress={onNewChat}
|
|
207
|
+
>
|
|
208
|
+
<span>{newChatLabel}</span>
|
|
209
|
+
</ActionButton>
|
|
210
|
+
)}
|
|
73
211
|
{canExpand && (
|
|
74
212
|
<IconButton
|
|
75
213
|
type="button"
|
|
@@ -94,8 +232,10 @@ export function DefaultChatHistoryRailView({
|
|
|
94
232
|
return (
|
|
95
233
|
<ChatHistoryList
|
|
96
234
|
{...rest}
|
|
97
|
-
|
|
98
|
-
|
|
235
|
+
{...(sections.length > 0
|
|
236
|
+
? { sections: sections.map(toListSection) }
|
|
237
|
+
: { items: visibleItems })}
|
|
238
|
+
footer={footer || undefined}
|
|
99
239
|
emptyLabel={emptyLabel ?? null}
|
|
100
240
|
variant="rail"
|
|
101
241
|
className={["an-chat-history-rail", className].filter(Boolean).join(" ")}
|
|
@@ -105,6 +245,8 @@ export function DefaultChatHistoryRailView({
|
|
|
105
245
|
|
|
106
246
|
export function ChatHistoryRail({
|
|
107
247
|
items,
|
|
248
|
+
sections: sectionInputs,
|
|
249
|
+
showNewChat,
|
|
108
250
|
onNewChat,
|
|
109
251
|
railLabels,
|
|
110
252
|
previewCount = 5,
|
|
@@ -115,15 +257,24 @@ export function ChatHistoryRail({
|
|
|
115
257
|
...listProps
|
|
116
258
|
}: ChatHistoryRailProps) {
|
|
117
259
|
const controller = useChatHistoryRailController({
|
|
118
|
-
items,
|
|
260
|
+
items: sectionInputs ? [] : (items ?? []),
|
|
119
261
|
onNewChat,
|
|
120
262
|
labels: railLabels,
|
|
121
263
|
previewCount,
|
|
122
264
|
expandedCount,
|
|
123
265
|
});
|
|
266
|
+
const sectionControllers = useChatHistoryRailSectionsController(
|
|
267
|
+
sectionInputs ?? [],
|
|
268
|
+
railLabels.sections,
|
|
269
|
+
);
|
|
124
270
|
const context: ChatHistoryRailRenderContext = {
|
|
125
271
|
controller,
|
|
272
|
+
sections: (sectionInputs ?? []).map((section, index) => ({
|
|
273
|
+
section,
|
|
274
|
+
controller: sectionControllers[index],
|
|
275
|
+
})),
|
|
126
276
|
listProps: { ...listProps, className, emptyLabel },
|
|
277
|
+
showNewChat,
|
|
127
278
|
};
|
|
128
279
|
const fallback = <DefaultChatHistoryRailView {...context} />;
|
|
129
280
|
|