@agent-native/toolkit 0.8.3 → 0.9.1
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 +42 -3
- package/agent-native.eject.json +33 -0
- package/dist/chat-history/ChatHistoryRail.d.ts +19 -0
- package/dist/chat-history/ChatHistoryRail.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryRail.js +22 -0
- package/dist/chat-history/ChatHistoryRail.js.map +1 -0
- package/dist/chat-history/ChatHistoryRail.spec.d.ts +2 -0
- package/dist/chat-history/ChatHistoryRail.spec.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryRail.spec.js +81 -0
- package/dist/chat-history/ChatHistoryRail.spec.js.map +1 -0
- package/dist/chat-history/index.d.ts +1 -0
- package/dist/chat-history/index.d.ts.map +1 -1
- package/dist/chat-history/index.js +1 -0
- package/dist/chat-history/index.js.map +1 -1
- package/dist/chat-history.css +59 -0
- package/dist/composer/RealtimeVoiceMode.d.ts +5 -1
- package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/RealtimeVoiceMode.js +27 -8
- package/dist/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/composer/RealtimeVoiceMode.spec.js +51 -11
- package/dist/composer/RealtimeVoiceMode.spec.js.map +1 -1
- package/dist/composer/VoiceButton.d.ts +2 -0
- package/dist/composer/VoiceButton.d.ts.map +1 -1
- package/dist/composer/VoiceButton.js +33 -1
- package/dist/composer/VoiceButton.js.map +1 -1
- package/dist/composer/VoiceButton.spec.js +9 -1
- package/dist/composer/VoiceButton.spec.js.map +1 -1
- package/dist/composer/realtime-voice-audio-level.d.ts.map +1 -1
- package/dist/composer/realtime-voice-audio-level.js +2 -1
- package/dist/composer/realtime-voice-audio-level.js.map +1 -1
- package/dist/composer/realtime-voice-audio-level.spec.js +7 -0
- package/dist/composer/realtime-voice-audio-level.spec.js.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.js +4 -4
- package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/dashboard/DataTable.d.ts +20 -0
- package/dist/dashboard/DataTable.d.ts.map +1 -0
- package/dist/dashboard/DataTable.js +87 -0
- package/dist/dashboard/DataTable.js.map +1 -0
- package/dist/dashboard/DateRangePicker.d.ts +15 -0
- package/dist/dashboard/DateRangePicker.d.ts.map +1 -0
- package/dist/dashboard/DateRangePicker.js +15 -0
- package/dist/dashboard/DateRangePicker.js.map +1 -0
- package/dist/dashboard/GenericChartPanel.d.ts +57 -0
- package/dist/dashboard/GenericChartPanel.d.ts.map +1 -0
- package/dist/dashboard/GenericChartPanel.js +119 -0
- package/dist/dashboard/GenericChartPanel.js.map +1 -0
- package/dist/dashboard/GenericChartPanel.spec.d.ts +2 -0
- package/dist/dashboard/GenericChartPanel.spec.d.ts.map +1 -0
- package/dist/dashboard/GenericChartPanel.spec.js +41 -0
- package/dist/dashboard/GenericChartPanel.spec.js.map +1 -0
- package/dist/dashboard/MetricCard.d.ts +14 -0
- package/dist/dashboard/MetricCard.d.ts.map +1 -0
- package/dist/dashboard/MetricCard.js +10 -0
- package/dist/dashboard/MetricCard.js.map +1 -0
- package/dist/dashboard/StatsCard.d.ts +16 -0
- package/dist/dashboard/StatsCard.d.ts.map +1 -0
- package/dist/dashboard/StatsCard.js +8 -0
- package/dist/dashboard/StatsCard.js.map +1 -0
- package/dist/dashboard/dashboard-layout.d.ts +49 -0
- package/dist/dashboard/dashboard-layout.d.ts.map +1 -0
- package/dist/dashboard/dashboard-layout.js +231 -0
- package/dist/dashboard/dashboard-layout.js.map +1 -0
- package/dist/dashboard/dashboard-layout.spec.d.ts +2 -0
- package/dist/dashboard/dashboard-layout.spec.d.ts.map +1 -0
- package/dist/dashboard/dashboard-layout.spec.js +52 -0
- package/dist/dashboard/dashboard-layout.spec.js.map +1 -0
- package/dist/dashboard/index.d.ts +7 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +7 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +35 -0
- package/package.json +15 -1
- package/src/chat-history/ChatHistoryRail.spec.tsx +138 -0
- package/src/chat-history/ChatHistoryRail.tsx +89 -0
- package/src/chat-history/index.ts +5 -0
- package/src/chat-history.css +59 -0
- package/src/composer/RealtimeVoiceMode.spec.tsx +116 -11
- package/src/composer/RealtimeVoiceMode.tsx +106 -36
- package/src/composer/VoiceButton.spec.ts +17 -1
- package/src/composer/VoiceButton.tsx +49 -0
- package/src/composer/realtime-voice-audio-level.spec.ts +15 -0
- package/src/composer/realtime-voice-audio-level.ts +2 -1
- package/src/composer/useRealtimeVoiceMode.tsx +5 -3
- package/src/dashboard/DataTable.tsx +273 -0
- package/src/dashboard/DateRangePicker.tsx +57 -0
- package/src/dashboard/GenericChartPanel.spec.tsx +53 -0
- package/src/dashboard/GenericChartPanel.tsx +461 -0
- package/src/dashboard/MetricCard.tsx +54 -0
- package/src/dashboard/StatsCard.tsx +49 -0
- package/src/dashboard/dashboard-layout.spec.ts +79 -0
- package/src/dashboard/dashboard-layout.ts +329 -0
- package/src/dashboard/index.ts +6 -0
- package/src/index.ts +1 -0
- package/src/styles.css +35 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildDashboardPanelGroups, clampDashboardColumns, columnExpansionForDropSlot, movePanelToDropSlot, removePanelFromLayout, } from "./dashboard-layout.js";
|
|
3
|
+
function panel(id, width = 1) {
|
|
4
|
+
return { id, width, kind: "chart" };
|
|
5
|
+
}
|
|
6
|
+
const options = { isSection: (item) => item.kind === "section" };
|
|
7
|
+
describe("dashboard layout", () => {
|
|
8
|
+
it("groups a section and balances persisted widths per visible row", () => {
|
|
9
|
+
const panels = [
|
|
10
|
+
panel("a"),
|
|
11
|
+
{ id: "section", kind: "section", columns: 3 },
|
|
12
|
+
panel("b"),
|
|
13
|
+
panel("c"),
|
|
14
|
+
panel("d"),
|
|
15
|
+
];
|
|
16
|
+
const groups = buildDashboardPanelGroups(panels, 2, options);
|
|
17
|
+
expect(groups.map((group) => group.rows.map((row) => row.panels.map((item) => item.id)))).toEqual([[["a"]], [["b", "c", "d"]]]);
|
|
18
|
+
expect(groups[1].columns).toBe(3);
|
|
19
|
+
});
|
|
20
|
+
it("removes without backfilling a later row", () => {
|
|
21
|
+
const next = removePanelFromLayout(["a", "b", "c", "d", "e"].map((id) => panel(id)), "b", 3, options);
|
|
22
|
+
expect(next.map((item) => [item.id, item.width])).toEqual([
|
|
23
|
+
["a", 2],
|
|
24
|
+
["c", 1],
|
|
25
|
+
["d", 2],
|
|
26
|
+
["e", 1],
|
|
27
|
+
]);
|
|
28
|
+
});
|
|
29
|
+
it("moves into a column slot and grows the target group as needed", () => {
|
|
30
|
+
const panels = ["a", "b", "c"].map((id) => panel(id));
|
|
31
|
+
const slot = {
|
|
32
|
+
type: "column",
|
|
33
|
+
groupKey: "intro",
|
|
34
|
+
rowIndex: 0,
|
|
35
|
+
columnIndex: 1,
|
|
36
|
+
};
|
|
37
|
+
const groups = buildDashboardPanelGroups(panels, 2, options);
|
|
38
|
+
expect(columnExpansionForDropSlot(groups, "c", slot)).toEqual({
|
|
39
|
+
columns: 3,
|
|
40
|
+
sectionPanelId: null,
|
|
41
|
+
});
|
|
42
|
+
const next = movePanelToDropSlot(panels, "c", slot, 2, options);
|
|
43
|
+
expect(next.map((item) => item.id)).toEqual(["a", "c", "b"]);
|
|
44
|
+
expect(next.map((item) => item.width)).toEqual([1, 1, 1]);
|
|
45
|
+
});
|
|
46
|
+
it("clamps malformed and out-of-range column counts", () => {
|
|
47
|
+
expect(clampDashboardColumns("4")).toBe(2);
|
|
48
|
+
expect(clampDashboardColumns(-3)).toBe(1);
|
|
49
|
+
expect(clampDashboardColumns(99)).toBe(6);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=dashboard-layout.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-layout.spec.js","sourceRoot":"","sources":["../../src/dashboard/dashboard-layout.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
|
package/dist/styles.css
CHANGED
|
@@ -19,3 +19,38 @@
|
|
|
19
19
|
* compiled `.js` next to it for consumers that only install `dist/`.
|
|
20
20
|
*/
|
|
21
21
|
@source "./**/*.{js,ts,tsx}";
|
|
22
|
+
|
|
23
|
+
.agent-realtime-voice-working::after {
|
|
24
|
+
position: absolute;
|
|
25
|
+
z-index: 0;
|
|
26
|
+
inset: 0;
|
|
27
|
+
border-radius: inherit;
|
|
28
|
+
pointer-events: none;
|
|
29
|
+
content: "";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
33
|
+
.agent-realtime-voice-working::after {
|
|
34
|
+
background: linear-gradient(
|
|
35
|
+
90deg,
|
|
36
|
+
transparent 0%,
|
|
37
|
+
transparent 36%,
|
|
38
|
+
hsl(var(--foreground) / 0.14) 50%,
|
|
39
|
+
transparent 64%,
|
|
40
|
+
transparent 100%
|
|
41
|
+
);
|
|
42
|
+
background-repeat: repeat;
|
|
43
|
+
background-size: 14rem 100%;
|
|
44
|
+
animation: agent-realtime-voice-shine 2.6s linear infinite;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@keyframes agent-realtime-voice-shine {
|
|
48
|
+
from {
|
|
49
|
+
background-position: 0 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
to {
|
|
53
|
+
background-position: 14rem 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Reusable app-building UI and helpers for Agent-Native apps.",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -61,6 +61,20 @@
|
|
|
61
61
|
"import": "./dist/collab-ui/*.js",
|
|
62
62
|
"default": "./dist/collab-ui/*.js"
|
|
63
63
|
},
|
|
64
|
+
"./dashboard": {
|
|
65
|
+
"types": "./dist/dashboard/index.d.ts",
|
|
66
|
+
"browser": "./dist/dashboard/index.js",
|
|
67
|
+
"development": "./dist/dashboard/index.js",
|
|
68
|
+
"import": "./dist/dashboard/index.js",
|
|
69
|
+
"default": "./dist/dashboard/index.js"
|
|
70
|
+
},
|
|
71
|
+
"./dashboard/*": {
|
|
72
|
+
"types": "./dist/dashboard/*.d.ts",
|
|
73
|
+
"browser": "./dist/dashboard/*.js",
|
|
74
|
+
"development": "./dist/dashboard/*.js",
|
|
75
|
+
"import": "./dist/dashboard/*.js",
|
|
76
|
+
"default": "./dist/dashboard/*.js"
|
|
77
|
+
},
|
|
64
78
|
"./chat-history": {
|
|
65
79
|
"types": "./dist/chat-history/index.d.ts",
|
|
66
80
|
"browser": "./dist/chat-history/index.js",
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { act } from "react";
|
|
4
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
import type { ChatHistoryItem } from "./ChatHistoryList.js";
|
|
8
|
+
import { ChatHistoryRail } from "./ChatHistoryRail.js";
|
|
9
|
+
|
|
10
|
+
const railLabels = {
|
|
11
|
+
newChat: "New chat",
|
|
12
|
+
showMore: "Show more chats",
|
|
13
|
+
showLess: "Show fewer chats",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function makeItems(count: number): ChatHistoryItem[] {
|
|
17
|
+
return Array.from({ length: count }, (_, index) => ({
|
|
18
|
+
id: `thread-${index + 1}`,
|
|
19
|
+
title: `Chat ${index + 1}`,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("ChatHistoryRail", () => {
|
|
24
|
+
let container: HTMLDivElement;
|
|
25
|
+
let root: Root;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
29
|
+
container = document.createElement("div");
|
|
30
|
+
document.body.appendChild(container);
|
|
31
|
+
root = createRoot(container);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
act(() => root.unmount());
|
|
36
|
+
container.remove();
|
|
37
|
+
vi.restoreAllMocks();
|
|
38
|
+
vi.unstubAllGlobals();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("shows five recent chats before progressively disclosing up to fifteen", () => {
|
|
42
|
+
act(() => {
|
|
43
|
+
root.render(
|
|
44
|
+
<ChatHistoryRail
|
|
45
|
+
items={makeItems(20)}
|
|
46
|
+
onSelect={() => {}}
|
|
47
|
+
onNewChat={() => {}}
|
|
48
|
+
railLabels={railLabels}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(container.querySelectorAll(".an-chat-history-row")).toHaveLength(5);
|
|
54
|
+
const disclosure = container.querySelector<HTMLButtonElement>(
|
|
55
|
+
".an-chat-history-rail__disclosure",
|
|
56
|
+
);
|
|
57
|
+
expect(disclosure?.getAttribute("aria-label")).toBe("Show more chats");
|
|
58
|
+
|
|
59
|
+
act(() => disclosure?.click());
|
|
60
|
+
expect(container.querySelectorAll(".an-chat-history-row")).toHaveLength(15);
|
|
61
|
+
expect(disclosure?.getAttribute("aria-label")).toBe("Show fewer chats");
|
|
62
|
+
|
|
63
|
+
act(() => disclosure?.click());
|
|
64
|
+
expect(container.querySelectorAll(".an-chat-history-row")).toHaveLength(5);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("keeps the disclosure to the right of new chat and calls its handler", () => {
|
|
68
|
+
const onNewChat = vi.fn();
|
|
69
|
+
act(() => {
|
|
70
|
+
root.render(
|
|
71
|
+
<ChatHistoryRail
|
|
72
|
+
items={makeItems(6)}
|
|
73
|
+
onSelect={() => {}}
|
|
74
|
+
onNewChat={onNewChat}
|
|
75
|
+
railLabels={railLabels}
|
|
76
|
+
/>,
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const disclosure = container.querySelector<HTMLButtonElement>(
|
|
81
|
+
".an-chat-history-rail__disclosure",
|
|
82
|
+
);
|
|
83
|
+
const newChat = container.querySelector<HTMLButtonElement>(
|
|
84
|
+
".an-chat-history-rail__new-chat",
|
|
85
|
+
);
|
|
86
|
+
expect(newChat?.textContent).toBe("New chat");
|
|
87
|
+
expect(newChat?.nextElementSibling).toBe(disclosure);
|
|
88
|
+
expect(newChat?.parentElement?.lastElementChild).toBe(disclosure);
|
|
89
|
+
|
|
90
|
+
act(() => newChat?.click());
|
|
91
|
+
expect(onNewChat).toHaveBeenCalledOnce();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("lets new chat fill the footer when there are no more chats", () => {
|
|
95
|
+
act(() => {
|
|
96
|
+
root.render(
|
|
97
|
+
<ChatHistoryRail
|
|
98
|
+
items={makeItems(3)}
|
|
99
|
+
onSelect={() => {}}
|
|
100
|
+
onNewChat={() => {}}
|
|
101
|
+
railLabels={railLabels}
|
|
102
|
+
/>,
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(
|
|
107
|
+
container.querySelector(".an-chat-history-rail__disclosure"),
|
|
108
|
+
).toBeNull();
|
|
109
|
+
expect(
|
|
110
|
+
container.querySelector(".an-chat-history-rail__new-chat"),
|
|
111
|
+
).not.toBeNull();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("preserves list actions for progressively disclosed rows", () => {
|
|
115
|
+
const onSelect = vi.fn();
|
|
116
|
+
act(() => {
|
|
117
|
+
root.render(
|
|
118
|
+
<ChatHistoryRail
|
|
119
|
+
items={makeItems(6)}
|
|
120
|
+
onSelect={onSelect}
|
|
121
|
+
onNewChat={() => {}}
|
|
122
|
+
railLabels={railLabels}
|
|
123
|
+
/>,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
act(() => {
|
|
128
|
+
container
|
|
129
|
+
.querySelector<HTMLButtonElement>(".an-chat-history-rail__disclosure")
|
|
130
|
+
?.click();
|
|
131
|
+
});
|
|
132
|
+
const rows = container.querySelectorAll<HTMLButtonElement>(
|
|
133
|
+
".an-chat-history-row__button",
|
|
134
|
+
);
|
|
135
|
+
act(() => rows[5]?.click());
|
|
136
|
+
expect(onSelect).toHaveBeenCalledWith("thread-6");
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { IconDots, IconPlus } from "@tabler/icons-react";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ChatHistoryList,
|
|
6
|
+
type ChatHistoryItem,
|
|
7
|
+
type ChatHistoryListProps,
|
|
8
|
+
} from "./ChatHistoryList.js";
|
|
9
|
+
|
|
10
|
+
export interface ChatHistoryRailLabels {
|
|
11
|
+
newChat: string;
|
|
12
|
+
showMore: string;
|
|
13
|
+
showLess: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ChatHistoryRailProps extends Omit<
|
|
17
|
+
ChatHistoryListProps,
|
|
18
|
+
"footer" | "items" | "sections" | "variant"
|
|
19
|
+
> {
|
|
20
|
+
items: ChatHistoryItem[];
|
|
21
|
+
onNewChat: () => void;
|
|
22
|
+
railLabels: ChatHistoryRailLabels;
|
|
23
|
+
previewCount?: number;
|
|
24
|
+
expandedCount?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Compact recent-chat rail for app sidebars. Hosts own thread persistence,
|
|
29
|
+
* sorting, routing, and mutations; the rail only owns progressive disclosure.
|
|
30
|
+
*/
|
|
31
|
+
export function ChatHistoryRail({
|
|
32
|
+
items,
|
|
33
|
+
onNewChat,
|
|
34
|
+
railLabels,
|
|
35
|
+
previewCount = 5,
|
|
36
|
+
expandedCount = 15,
|
|
37
|
+
className,
|
|
38
|
+
emptyLabel,
|
|
39
|
+
...listProps
|
|
40
|
+
}: ChatHistoryRailProps) {
|
|
41
|
+
const [expanded, setExpanded] = useState(false);
|
|
42
|
+
const collapsedLimit = Math.max(1, previewCount);
|
|
43
|
+
const expandedLimit = Math.max(collapsedLimit, expandedCount);
|
|
44
|
+
const canExpand = items.length > collapsedLimit;
|
|
45
|
+
const visibleItems = useMemo(
|
|
46
|
+
() => items.slice(0, expanded ? expandedLimit : collapsedLimit),
|
|
47
|
+
[collapsedLimit, expanded, expandedLimit, items],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!canExpand) setExpanded(false);
|
|
52
|
+
}, [canExpand]);
|
|
53
|
+
|
|
54
|
+
const footer = (
|
|
55
|
+
<div className="an-chat-history-rail__footer">
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
className="an-chat-history-rail__new-chat"
|
|
59
|
+
onClick={onNewChat}
|
|
60
|
+
>
|
|
61
|
+
<IconPlus size={13} strokeWidth={1.8} aria-hidden="true" />
|
|
62
|
+
<span>{railLabels.newChat}</span>
|
|
63
|
+
</button>
|
|
64
|
+
{canExpand && (
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
className="an-chat-history-rail__disclosure"
|
|
68
|
+
onClick={() => setExpanded((current) => !current)}
|
|
69
|
+
aria-expanded={expanded}
|
|
70
|
+
aria-label={expanded ? railLabels.showLess : railLabels.showMore}
|
|
71
|
+
title={expanded ? railLabels.showLess : railLabels.showMore}
|
|
72
|
+
>
|
|
73
|
+
<IconDots size={14} strokeWidth={1.8} aria-hidden="true" />
|
|
74
|
+
</button>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<ChatHistoryList
|
|
81
|
+
{...listProps}
|
|
82
|
+
items={visibleItems}
|
|
83
|
+
footer={footer}
|
|
84
|
+
emptyLabel={emptyLabel ?? null}
|
|
85
|
+
variant="rail"
|
|
86
|
+
className={["an-chat-history-rail", className].filter(Boolean).join(" ")}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
}
|
package/src/chat-history.css
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
.an-chat-history {
|
|
17
17
|
display: flex;
|
|
18
18
|
flex-direction: column;
|
|
19
|
+
min-width: 0;
|
|
19
20
|
min-height: 0;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
|
|
50
51
|
.an-chat-history__list {
|
|
51
52
|
overflow-y: auto;
|
|
53
|
+
min-width: 0;
|
|
52
54
|
padding: 4px 0;
|
|
53
55
|
max-height: 16rem;
|
|
54
56
|
}
|
|
@@ -59,6 +61,10 @@
|
|
|
59
61
|
min-height: 0;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
.an-chat-history-rail .an-chat-history__state:empty {
|
|
65
|
+
display: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
62
68
|
.an-chat-history__state {
|
|
63
69
|
padding: 16px 12px;
|
|
64
70
|
text-align: center;
|
|
@@ -72,6 +78,55 @@
|
|
|
72
78
|
color: hsl(38 92% 50%);
|
|
73
79
|
}
|
|
74
80
|
|
|
81
|
+
.an-chat-history-rail__footer {
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: 2px;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
padding: 1px 4px 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.an-chat-history-rail__disclosure,
|
|
90
|
+
.an-chat-history-rail__new-chat {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
border: 0;
|
|
94
|
+
border-radius: calc(var(--radius) - 2px);
|
|
95
|
+
background: transparent;
|
|
96
|
+
color: hsl(var(--muted-foreground) / 0.72);
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.an-chat-history-rail__disclosure:hover,
|
|
101
|
+
.an-chat-history-rail__new-chat:hover {
|
|
102
|
+
background: hsl(var(--accent) / 0.5);
|
|
103
|
+
color: hsl(var(--accent-foreground));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.an-chat-history-rail__disclosure:focus-visible,
|
|
107
|
+
.an-chat-history-rail__new-chat:focus-visible {
|
|
108
|
+
outline: 2px solid hsl(var(--ring));
|
|
109
|
+
outline-offset: -2px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.an-chat-history-rail__disclosure {
|
|
113
|
+
justify-content: center;
|
|
114
|
+
flex: 0 0 auto;
|
|
115
|
+
width: 30px;
|
|
116
|
+
min-height: 30px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.an-chat-history-rail__new-chat {
|
|
120
|
+
flex: 1 1 auto;
|
|
121
|
+
gap: 8px;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
min-height: 30px;
|
|
124
|
+
padding: 6px 10px;
|
|
125
|
+
font-size: 12px;
|
|
126
|
+
font-weight: 500;
|
|
127
|
+
text-align: start;
|
|
128
|
+
}
|
|
129
|
+
|
|
75
130
|
.an-chat-history__section-label {
|
|
76
131
|
padding: 6px 12px 4px;
|
|
77
132
|
font-size: 10px;
|
|
@@ -83,12 +138,14 @@
|
|
|
83
138
|
.an-chat-history--rail .an-chat-history__section {
|
|
84
139
|
display: grid;
|
|
85
140
|
gap: 1px;
|
|
141
|
+
min-width: 0;
|
|
86
142
|
}
|
|
87
143
|
|
|
88
144
|
/* ── Row ──────────────────────────────────────────────────────────────── */
|
|
89
145
|
|
|
90
146
|
.an-chat-history-row {
|
|
91
147
|
position: relative;
|
|
148
|
+
min-width: 0;
|
|
92
149
|
}
|
|
93
150
|
|
|
94
151
|
.an-chat-history--rail .an-chat-history-row {
|
|
@@ -99,6 +156,7 @@
|
|
|
99
156
|
.an-chat-history-row__button {
|
|
100
157
|
display: block;
|
|
101
158
|
width: 100%;
|
|
159
|
+
min-width: 0;
|
|
102
160
|
padding: 8px 12px;
|
|
103
161
|
border: 0;
|
|
104
162
|
background: transparent;
|
|
@@ -141,6 +199,7 @@
|
|
|
141
199
|
justify-content: space-between;
|
|
142
200
|
gap: 8px;
|
|
143
201
|
min-width: 0;
|
|
202
|
+
overflow: hidden;
|
|
144
203
|
}
|
|
145
204
|
|
|
146
205
|
.an-chat-history-row__title {
|