@agent-native/core-corpus 0.217.0 → 0.218.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +2 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -167
- package/corpus/templates/analytics/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/assets/CHANGELOG.md +6 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +51 -117
- package/corpus/templates/assets/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +34 -101
- package/corpus/templates/brain/app/routes/sources.tsx +1 -1
- package/corpus/templates/brain/vitest.config.ts +7 -0
- package/corpus/templates/calendar/CHANGELOG.md +6 -0
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-10-16-booking-links-show-localized-visibility-badges.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +62 -101
- package/corpus/templates/clips/CHANGELOG.md +7 -20
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +20 -77
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +63 -90
- package/corpus/templates/clips/app/components/sharing/share-test-support.tsx +180 -0
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -584
- package/corpus/templates/clips/changelog/2026-10-16-share-dialogs-make-it-easier-to-see-who-has-access-and-manag.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-21-share-panels-describe-general-access-in-a-sentence.md +6 -0
- package/corpus/templates/community/qr/package.json +1 -1
- package/corpus/templates/content/CHANGELOG.md +2 -0
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +119 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +56 -13
- package/corpus/templates/content/app/components/editor/SuggestionText.tsx +18 -3
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +89 -22
- package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +383 -34
- package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +31 -0
- package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +82 -4
- package/corpus/templates/content/app/global.css +31 -0
- package/corpus/templates/content/changelog/2026-10-16-accepting-a-suggestion-on-a-page-that-changed-since-it-was-s.md +6 -0
- package/corpus/templates/content/changelog/2026-10-20-suggested-edits-that-add-remove-split-or-merge-paragraphs-li.md +6 -0
- package/corpus/templates/content/shared/suggestion-diff.ts +23 -6
- package/corpus/templates/content/shared/suggestion-formatting.ts +44 -14
- package/corpus/templates/content/shared/suggestion-text.ts +20 -5
- package/corpus/templates/design/CHANGELOG.md +3 -0
- package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
- package/corpus/templates/design/app/pages/Index.tsx +18 -64
- package/corpus/templates/design/changelog/2026-10-16-design-pages-use-localized-visibility-badges-and-a-shared-ow.md +6 -0
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +75 -0
- package/corpus/templates/dispatch/changelog/2026-10-21-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +34 -100
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +1 -1
- package/corpus/templates/plan/CHANGELOG.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +34 -109
- package/corpus/templates/plan/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/slides/CHANGELOG.md +1 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +1 -1
- package/corpus/templates/slides/app/pages/Index.tsx +7 -2
- package/corpus/templates/slides/changelog/2026-10-16-deck-lists-use-the-shared-ownership-filter-and-visibility-ba.md +6 -0
- package/package.json +1 -1
- package/corpus/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
- package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +0 -83
package/corpus/README.md
CHANGED
|
@@ -74,6 +74,7 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
74
74
|
|
|
75
75
|
### Added
|
|
76
76
|
|
|
77
|
+
- Owned and shared chats appear in separate history sections, with each shared chat's owner shown as an avatar and a Copy link action on every chat.
|
|
77
78
|
- In Edit mode, rename or delete a multi-select filter's group from its dropdown while that group is selected.
|
|
78
79
|
- The Content dashboard can show signups per blog article, collected once a day behind a BigQuery cost cap that a dry run measures first
|
|
79
80
|
- Click a table row to see its numbers broken down by app, or another column, in a side panel.
|
|
@@ -96,6 +97,7 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
96
97
|
|
|
97
98
|
### Fixed
|
|
98
99
|
|
|
100
|
+
- The activation funnel counts visitors who decline analytics cookies and then sign up in the same visit
|
|
99
101
|
- A key-date marker opens its card on hover even where a chart's hovered point sits on top of it.
|
|
100
102
|
- Alert polls enforce a query byte limit and report evaluations that exceed it as errors.
|
|
101
103
|
- All-time dashboard panels keep their historical label when you edit a title or description
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appPath } from "@agent-native/core/client/api-path";
|
|
1
2
|
import {
|
|
2
3
|
AppSidebarFooter,
|
|
3
4
|
AppSidebarHeader,
|
|
@@ -96,7 +97,6 @@ function useSettledSyncVersion(version: number): number {
|
|
|
96
97
|
import {
|
|
97
98
|
navigateWithAgentChatViewTransition,
|
|
98
99
|
useChatThreads,
|
|
99
|
-
type ChatThreadSummary,
|
|
100
100
|
} from "@agent-native/core/client/agent-chat";
|
|
101
101
|
import {
|
|
102
102
|
callAction,
|
|
@@ -104,13 +104,14 @@ import {
|
|
|
104
104
|
useChangeVersions,
|
|
105
105
|
} from "@agent-native/core/client/hooks";
|
|
106
106
|
import { useT } from "@agent-native/core/client/i18n";
|
|
107
|
+
import {
|
|
108
|
+
OwnershipChatHistoryRail,
|
|
109
|
+
useOwnershipChatHistory,
|
|
110
|
+
ownDraftRows,
|
|
111
|
+
} from "@agent-native/toolkit/app/chat/ownership-history";
|
|
107
112
|
import { DevDatabaseLink } from "@agent-native/toolkit/app/db-admin";
|
|
108
113
|
import { FeedbackButton } from "@agent-native/toolkit/app/feedback";
|
|
109
114
|
import { OrgSwitcher } from "@agent-native/toolkit/app/org";
|
|
110
|
-
import {
|
|
111
|
-
ChatHistoryRail,
|
|
112
|
-
type ChatHistoryItem,
|
|
113
|
-
} from "@agent-native/toolkit/chat-history";
|
|
114
115
|
import {
|
|
115
116
|
analyticsAskThreadIdFromPath,
|
|
116
117
|
analyticsAskThreadPath,
|
|
@@ -333,10 +334,6 @@ function applyOrder<T extends { id: string }>(
|
|
|
333
334
|
return ordered;
|
|
334
335
|
}
|
|
335
336
|
|
|
336
|
-
function isVisibility(value: unknown): value is Visibility {
|
|
337
|
-
return value === "private" || value === "org" || value === "public";
|
|
338
|
-
}
|
|
339
|
-
|
|
340
337
|
export function matchesVisibilityFilter(
|
|
341
338
|
item: { visibility?: Visibility; ownerEmail?: string | null },
|
|
342
339
|
filter: SidebarVisibilityFilter,
|
|
@@ -345,23 +342,6 @@ export function matchesVisibilityFilter(
|
|
|
345
342
|
return matchesDashboardVisibilityFilter(item, filter, currentUserEmail);
|
|
346
343
|
}
|
|
347
344
|
|
|
348
|
-
export function threadMatchesVisibilityFilter(
|
|
349
|
-
thread: ChatThreadSummary,
|
|
350
|
-
filter: SidebarVisibilityFilter,
|
|
351
|
-
): boolean {
|
|
352
|
-
const runtimeThread = thread as ChatThreadSummary & {
|
|
353
|
-
visibility?: unknown;
|
|
354
|
-
};
|
|
355
|
-
return matchesVisibilityFilter(
|
|
356
|
-
{
|
|
357
|
-
visibility: isVisibility(runtimeThread.visibility)
|
|
358
|
-
? runtimeThread.visibility
|
|
359
|
-
: "private",
|
|
360
|
-
},
|
|
361
|
-
filter,
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
345
|
function SidebarSectionSettingsPopover({
|
|
366
346
|
label,
|
|
367
347
|
sortMode,
|
|
@@ -1368,110 +1348,44 @@ export function prefetchSqlDashboard(
|
|
|
1368
1348
|
});
|
|
1369
1349
|
}
|
|
1370
1350
|
|
|
1371
|
-
function formatThreadAge(updatedAt: number) {
|
|
1372
|
-
const diffMs = Math.max(0, Date.now() - updatedAt);
|
|
1373
|
-
const minutes = Math.floor(diffMs / 60_000);
|
|
1374
|
-
if (minutes < 1) return "now";
|
|
1375
|
-
if (minutes < 60) return `${minutes}m`;
|
|
1376
|
-
const hours = Math.floor(minutes / 60);
|
|
1377
|
-
if (hours < 24) return `${hours}h`;
|
|
1378
|
-
const days = Math.floor(hours / 24);
|
|
1379
|
-
if (days < 7) return `${days}d`;
|
|
1380
|
-
return new Date(updatedAt).toLocaleDateString([], {
|
|
1381
|
-
month: "short",
|
|
1382
|
-
day: "numeric",
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
function threadTitle(thread: ChatThreadSummary, untitledLabel: string) {
|
|
1387
|
-
return thread.title || thread.preview || untitledLabel;
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
1391
|
-
return Number.isFinite(thread.updatedAt)
|
|
1392
|
-
? thread.updatedAt
|
|
1393
|
-
: Number.isFinite(thread.createdAt)
|
|
1394
|
-
? thread.createdAt
|
|
1395
|
-
: 0;
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
function compareThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
|
|
1399
|
-
const aPinned = a.pinnedAt ?? 0;
|
|
1400
|
-
const bPinned = b.pinnedAt ?? 0;
|
|
1401
|
-
if (aPinned || bPinned) return bPinned - aPinned;
|
|
1402
|
-
return threadUpdatedAt(b) - threadUpdatedAt(a);
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
1351
|
function AnalyticsChatsSection({
|
|
1406
1352
|
askThreadId,
|
|
1407
1353
|
open,
|
|
1408
|
-
visibilityFilter,
|
|
1409
1354
|
}: {
|
|
1410
1355
|
/** The saved thread open on /ask/:threadId; null on the blank /ask page. */
|
|
1411
1356
|
askThreadId: string | null;
|
|
1412
1357
|
open: boolean;
|
|
1413
|
-
visibilityFilter: SidebarVisibilityFilter;
|
|
1414
1358
|
}) {
|
|
1415
1359
|
const navigate = useNavigate();
|
|
1416
1360
|
const t = useT();
|
|
1417
|
-
const {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
refreshThreads,
|
|
1424
|
-
} = useChatThreads(undefined, ANALYTICS_CHAT_STORAGE_KEY, undefined, {
|
|
1425
|
-
autoCreate: false,
|
|
1426
|
-
restoreActiveThread: false,
|
|
1427
|
-
});
|
|
1428
|
-
|
|
1429
|
-
const visibleThreads = useMemo(
|
|
1430
|
-
() =>
|
|
1431
|
-
threads
|
|
1432
|
-
.filter(
|
|
1433
|
-
(thread) =>
|
|
1434
|
-
thread.messageCount > 0 &&
|
|
1435
|
-
!thread.archivedAt &&
|
|
1436
|
-
threadMatchesVisibilityFilter(thread, visibilityFilter),
|
|
1437
|
-
)
|
|
1438
|
-
.sort(compareThreads)
|
|
1439
|
-
.slice(0, 15),
|
|
1440
|
-
[threads, visibilityFilter],
|
|
1441
|
-
);
|
|
1442
|
-
const chatItems = useMemo<ChatHistoryItem[]>(
|
|
1443
|
-
() =>
|
|
1444
|
-
visibleThreads.map((thread) => ({
|
|
1445
|
-
id: thread.id,
|
|
1446
|
-
title: threadTitle(thread, t("chat.untitledChat")),
|
|
1447
|
-
titleText: threadTitle(thread, t("chat.untitledChat")),
|
|
1448
|
-
timestamp:
|
|
1449
|
-
thread.id === askThreadId
|
|
1450
|
-
? undefined
|
|
1451
|
-
: formatThreadAge(threadUpdatedAt(thread)),
|
|
1452
|
-
pinned: Boolean(thread.pinnedAt),
|
|
1453
|
-
})),
|
|
1454
|
-
[askThreadId, t, visibleThreads],
|
|
1455
|
-
);
|
|
1361
|
+
const { threads, pinThread, archiveThread, renameThread, isNewThread } =
|
|
1362
|
+
useChatThreads(undefined, ANALYTICS_CHAT_STORAGE_KEY, undefined, {
|
|
1363
|
+
ownership: "mine",
|
|
1364
|
+
autoCreate: false,
|
|
1365
|
+
restoreActiveThread: false,
|
|
1366
|
+
});
|
|
1456
1367
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1368
|
+
const history = useOwnershipChatHistory({
|
|
1369
|
+
getThreadUrl: (threadId) => appPath(analyticsAskThreadPath(threadId)),
|
|
1370
|
+
activeId: askThreadId,
|
|
1371
|
+
localMineRows: ownDraftRows({ threads, isNewThread }),
|
|
1372
|
+
mutations: {
|
|
1373
|
+
pin: pinThread,
|
|
1374
|
+
rename: renameThread,
|
|
1375
|
+
archive: archiveThread,
|
|
1376
|
+
},
|
|
1377
|
+
onMutationFailed: (kind) => {
|
|
1378
|
+
toast.error(
|
|
1379
|
+
t(
|
|
1380
|
+
kind === "archive"
|
|
1381
|
+
? "chat.archiveFailed"
|
|
1382
|
+
: kind === "rename"
|
|
1383
|
+
? "chat.renameFailed"
|
|
1384
|
+
: "agentChat.common.saveFailed",
|
|
1385
|
+
),
|
|
1386
|
+
);
|
|
1387
|
+
},
|
|
1388
|
+
});
|
|
1475
1389
|
|
|
1476
1390
|
function openThread(threadId: string) {
|
|
1477
1391
|
navigateWithAgentChatViewTransition(
|
|
@@ -1494,18 +1408,10 @@ function AnalyticsChatsSection({
|
|
|
1494
1408
|
|
|
1495
1409
|
async function handleArchiveThread(threadId: string) {
|
|
1496
1410
|
const wasActive = threadId === askThreadId;
|
|
1497
|
-
const archived = await
|
|
1498
|
-
if (!archived)
|
|
1499
|
-
toast.error(t("chat.archiveFailed"));
|
|
1500
|
-
return;
|
|
1501
|
-
}
|
|
1411
|
+
const archived = await history.listProps.onDelete!(threadId);
|
|
1412
|
+
if (!archived) return false;
|
|
1502
1413
|
if (wasActive) handleNewChat();
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
function handleRenameThread(threadId: string, title: string) {
|
|
1506
|
-
void renameThread(threadId, title).then((renamed) => {
|
|
1507
|
-
if (!renamed) toast.error(t("chat.renameFailed"));
|
|
1508
|
-
});
|
|
1414
|
+
return true;
|
|
1509
1415
|
}
|
|
1510
1416
|
|
|
1511
1417
|
return (
|
|
@@ -1515,43 +1421,15 @@ function AnalyticsChatsSection({
|
|
|
1515
1421
|
aria-hidden={!open}
|
|
1516
1422
|
>
|
|
1517
1423
|
<div className="ms-4 min-w-0 space-y-0.5">
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
Array.from({ length: 3 }).map((_, i) => (
|
|
1521
|
-
<div
|
|
1522
|
-
key={`chat-skeleton-${i}`}
|
|
1523
|
-
className="flex items-center gap-2 px-3 py-1"
|
|
1524
|
-
>
|
|
1525
|
-
<Skeleton
|
|
1526
|
-
className={cn(
|
|
1527
|
-
"h-3.5 w-3.5 shrink-0 rounded-sm",
|
|
1528
|
-
SIDEBAR_SKELETON_CLASS,
|
|
1529
|
-
)}
|
|
1530
|
-
/>
|
|
1531
|
-
<Skeleton
|
|
1532
|
-
className={cn("h-3 rounded", SIDEBAR_SKELETON_CLASS)}
|
|
1533
|
-
style={{ width: `${60 + ((i * 17) % 30)}%` }}
|
|
1534
|
-
/>
|
|
1535
|
-
</div>
|
|
1536
|
-
))}
|
|
1537
|
-
<ChatHistoryRail
|
|
1538
|
-
items={chatItems}
|
|
1424
|
+
<OwnershipChatHistoryRail
|
|
1425
|
+
{...history}
|
|
1539
1426
|
activeId={askThreadId}
|
|
1540
1427
|
onSelect={(threadId) => openThread(threadId)}
|
|
1541
1428
|
onNewChat={handleNewChat}
|
|
1542
1429
|
railLabels={{
|
|
1543
1430
|
newChat: t("chat.newChat"),
|
|
1544
|
-
showMore: t("sidebar.showMore", {
|
|
1545
|
-
count: Math.max(0, visibleThreads.length - SIDEBAR_PREVIEW_COUNT),
|
|
1546
|
-
}),
|
|
1547
|
-
showLess: t("sidebar.showLess"),
|
|
1548
1431
|
}}
|
|
1549
1432
|
renameMaxLength={160}
|
|
1550
|
-
onTogglePin={(threadId) => {
|
|
1551
|
-
const thread = visibleThreads.find((item) => item.id === threadId);
|
|
1552
|
-
if (thread) void pinThread(threadId, !thread.pinnedAt);
|
|
1553
|
-
}}
|
|
1554
|
-
onRename={handleRenameThread}
|
|
1555
1433
|
onDelete={(threadId) => void handleArchiveThread(threadId)}
|
|
1556
1434
|
labels={{
|
|
1557
1435
|
options: (item) =>
|
|
@@ -1639,7 +1517,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1639
1517
|
const [askOpen, setAskOpen] = useState(
|
|
1640
1518
|
() => getStoredBooleanPreference(ASK_OPEN_KEY) ?? isAskRoute,
|
|
1641
1519
|
);
|
|
1642
|
-
const [askFilter, setAskFilter] = useState<SidebarVisibilityFilter>("all");
|
|
1643
1520
|
const [dashOpen, setDashOpen] = useState(
|
|
1644
1521
|
() =>
|
|
1645
1522
|
getStoredBooleanPreference(DASHBOARDS_OPEN_KEY) ??
|
|
@@ -2511,11 +2388,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2511
2388
|
{t("navigation.ask")}
|
|
2512
2389
|
</span>
|
|
2513
2390
|
</Link>
|
|
2514
|
-
<SidebarSectionSettingsPopover
|
|
2515
|
-
label={t("navigation.ask")}
|
|
2516
|
-
visibilityFilter={askFilter}
|
|
2517
|
-
onVisibilityFilterChange={setAskFilter}
|
|
2518
|
-
/>
|
|
2519
2391
|
<button
|
|
2520
2392
|
type="button"
|
|
2521
2393
|
onClick={toggleAskOpen}
|
|
@@ -2538,7 +2410,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2538
2410
|
<AnalyticsChatsSection
|
|
2539
2411
|
askThreadId={askThreadId}
|
|
2540
2412
|
open={askOpen}
|
|
2541
|
-
visibilityFilter={askFilter}
|
|
2542
2413
|
/>
|
|
2543
2414
|
</div>
|
|
2544
2415
|
|
|
@@ -9,6 +9,12 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
9
9
|
|
|
10
10
|
- Apps no longer keep server functions and databases warm by default; set `AGENT_NATIVE_ENABLE_KEEP_WARM=1` to opt in.
|
|
11
11
|
|
|
12
|
+
## 2026-10-16
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Owned and shared chats appear in separate history sections, with each shared chat's owner shown as an avatar and a Copy link action on every chat.
|
|
17
|
+
|
|
12
18
|
## 2026-10-09
|
|
13
19
|
|
|
14
20
|
### Improved
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
navigateWithAgentChatViewTransition,
|
|
3
3
|
useChatThreads,
|
|
4
|
-
type ChatThreadSummary,
|
|
5
4
|
} from "@agent-native/core/client/agent-chat";
|
|
5
|
+
import { appPath } from "@agent-native/core/client/api-path";
|
|
6
6
|
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
7
7
|
import { useT } from "@agent-native/core/client/i18n";
|
|
8
8
|
import { focusAgentChat } from "@agent-native/toolkit/app/chat";
|
|
9
|
+
import {
|
|
10
|
+
OwnershipChatHistoryRail,
|
|
11
|
+
useOwnershipChatHistory,
|
|
12
|
+
ownDraftRows,
|
|
13
|
+
} from "@agent-native/toolkit/app/chat/ownership-history";
|
|
9
14
|
import { DevDatabaseLink } from "@agent-native/toolkit/app/db-admin";
|
|
10
15
|
import { FeedbackButton } from "@agent-native/toolkit/app/feedback";
|
|
11
16
|
import { OrgSwitcher } from "@agent-native/toolkit/app/org";
|
|
@@ -14,10 +19,6 @@ import {
|
|
|
14
19
|
AppSidebar,
|
|
15
20
|
AppSidebarNavItem,
|
|
16
21
|
} from "@agent-native/toolkit/app/shared";
|
|
17
|
-
import {
|
|
18
|
-
ChatHistoryRail,
|
|
19
|
-
type ChatHistoryItem,
|
|
20
|
-
} from "@agent-native/toolkit/chat-history";
|
|
21
22
|
import {
|
|
22
23
|
IconClipboardList,
|
|
23
24
|
IconLayoutGrid,
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
IconShare3,
|
|
27
28
|
IconTemplate,
|
|
28
29
|
} from "@tabler/icons-react";
|
|
29
|
-
import { useEffect,
|
|
30
|
+
import { useEffect, useState } from "react";
|
|
30
31
|
import { useLocation, useNavigate } from "react-router";
|
|
31
32
|
import { toast } from "sonner";
|
|
32
33
|
|
|
@@ -53,40 +54,6 @@ const auditNavItem = {
|
|
|
53
54
|
const COLLAPSE_KEY = "assets.sidebar.collapsed";
|
|
54
55
|
const ASSETS_ACTIVE_THREAD_KEY = `agent-chat-active-thread:${ASSETS_CHAT_STORAGE_KEY}`;
|
|
55
56
|
|
|
56
|
-
function formatThreadAge(updatedAt: number) {
|
|
57
|
-
const diffMs = Math.max(0, Date.now() - updatedAt);
|
|
58
|
-
const minutes = Math.floor(diffMs / 60_000);
|
|
59
|
-
if (minutes < 1) return "now";
|
|
60
|
-
if (minutes < 60) return `${minutes}m`;
|
|
61
|
-
const hours = Math.floor(minutes / 60);
|
|
62
|
-
if (hours < 24) return `${hours}h`;
|
|
63
|
-
const days = Math.floor(hours / 24);
|
|
64
|
-
if (days < 7) return `${days}d`;
|
|
65
|
-
return new Date(updatedAt).toLocaleDateString([], {
|
|
66
|
-
month: "short",
|
|
67
|
-
day: "numeric",
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function threadTitle(thread: ChatThreadSummary) {
|
|
72
|
-
return thread.title || thread.preview || "Untitled chat";
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
76
|
-
return Number.isFinite(thread.updatedAt)
|
|
77
|
-
? thread.updatedAt
|
|
78
|
-
: Number.isFinite(thread.createdAt)
|
|
79
|
-
? thread.createdAt
|
|
80
|
-
: 0;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function compareThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
|
|
84
|
-
const aPinned = a.pinnedAt ?? 0;
|
|
85
|
-
const bPinned = b.pinnedAt ?? 0;
|
|
86
|
-
if (aPinned || bPinned) return bPinned - aPinned;
|
|
87
|
-
return threadUpdatedAt(b) - threadUpdatedAt(a);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
57
|
function persistedActiveThreadId() {
|
|
91
58
|
try {
|
|
92
59
|
return localStorage.getItem(ASSETS_ACTIVE_THREAD_KEY);
|
|
@@ -129,55 +96,37 @@ function AssetsChatsSection({ open }: { open: boolean }) {
|
|
|
129
96
|
archiveThread,
|
|
130
97
|
renameThread,
|
|
131
98
|
createThreadShareLink,
|
|
132
|
-
|
|
99
|
+
isNewThread,
|
|
133
100
|
} = useChatThreads(undefined, ASSETS_CHAT_STORAGE_KEY, undefined, {
|
|
101
|
+
ownership: "mine",
|
|
134
102
|
autoCreate: false,
|
|
135
103
|
restoreActiveThread: false,
|
|
136
104
|
});
|
|
137
105
|
|
|
138
|
-
const visibleThreads = useMemo(
|
|
139
|
-
() =>
|
|
140
|
-
threads
|
|
141
|
-
.filter((thread) => thread.messageCount > 0 && !thread.archivedAt)
|
|
142
|
-
.sort(compareThreads),
|
|
143
|
-
[threads],
|
|
144
|
-
);
|
|
145
106
|
const displayedActiveThreadId =
|
|
146
107
|
threadIdFromPath(location.pathname) ??
|
|
147
108
|
(location.pathname === "/home" ? null : activeThreadId);
|
|
148
|
-
const
|
|
149
|
-
() =>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (typeof detail?.isRunning === "boolean") refreshThreads();
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
window.addEventListener("agent-chat:threads-updated", refresh);
|
|
173
|
-
window.addEventListener("agentNative.chatRunning", handleRunning);
|
|
174
|
-
window.addEventListener("focus", refresh);
|
|
175
|
-
return () => {
|
|
176
|
-
window.removeEventListener("agent-chat:threads-updated", refresh);
|
|
177
|
-
window.removeEventListener("agentNative.chatRunning", handleRunning);
|
|
178
|
-
window.removeEventListener("focus", refresh);
|
|
179
|
-
};
|
|
180
|
-
}, [refreshThreads]);
|
|
109
|
+
const history = useOwnershipChatHistory({
|
|
110
|
+
getThreadUrl: (threadId) => appPath(chatThreadPath(threadId)),
|
|
111
|
+
activeId: displayedActiveThreadId,
|
|
112
|
+
localMineRows: ownDraftRows({ threads, isNewThread }),
|
|
113
|
+
mutations: {
|
|
114
|
+
pin: pinThread,
|
|
115
|
+
rename: renameThread,
|
|
116
|
+
archive: archiveThread,
|
|
117
|
+
},
|
|
118
|
+
onMutationFailed: (kind) => {
|
|
119
|
+
toast.error(
|
|
120
|
+
t(
|
|
121
|
+
kind === "archive"
|
|
122
|
+
? "chat.archiveFailed"
|
|
123
|
+
: kind === "rename"
|
|
124
|
+
? "chat.renameFailed"
|
|
125
|
+
: "agentChat.common.saveFailed",
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
181
130
|
|
|
182
131
|
function openThread(threadId: string, options?: { isNew?: boolean }) {
|
|
183
132
|
switchThread(threadId);
|
|
@@ -203,14 +152,12 @@ function AssetsChatsSection({ open }: { open: boolean }) {
|
|
|
203
152
|
async function handleArchiveThread(threadId: string) {
|
|
204
153
|
const wasActive =
|
|
205
154
|
threadId === activeThreadId || threadId === persistedActiveThreadId();
|
|
206
|
-
const archived = await
|
|
207
|
-
if (!archived)
|
|
208
|
-
toast.error(t("chat.archiveFailed"));
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
155
|
+
const archived = await history.listProps.onDelete!(threadId);
|
|
156
|
+
if (!archived) return false;
|
|
211
157
|
if (wasActive) {
|
|
212
158
|
await handleNewChat();
|
|
213
159
|
}
|
|
160
|
+
return true;
|
|
214
161
|
}
|
|
215
162
|
|
|
216
163
|
async function handleCopyShareLink(threadId: string) {
|
|
@@ -233,12 +180,6 @@ function AssetsChatsSection({ open }: { open: boolean }) {
|
|
|
233
180
|
}
|
|
234
181
|
}
|
|
235
182
|
|
|
236
|
-
function handleRenameThread(threadId: string, title: string) {
|
|
237
|
-
void renameThread(threadId, title).then((renamed) => {
|
|
238
|
-
if (!renamed) toast.error(t("chat.renameFailed"));
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
|
|
242
183
|
return (
|
|
243
184
|
<div
|
|
244
185
|
className="an-chat-history-rail__collapse"
|
|
@@ -246,39 +187,32 @@ function AssetsChatsSection({ open }: { open: boolean }) {
|
|
|
246
187
|
aria-hidden={!open}
|
|
247
188
|
>
|
|
248
189
|
<div className="ms-4">
|
|
249
|
-
<
|
|
250
|
-
|
|
190
|
+
<OwnershipChatHistoryRail
|
|
191
|
+
{...history}
|
|
251
192
|
activeId={displayedActiveThreadId}
|
|
252
193
|
onSelect={openThread}
|
|
253
194
|
onNewChat={() => void handleNewChat()}
|
|
254
195
|
railLabels={{
|
|
255
196
|
newChat: t("chat.newChat"),
|
|
256
|
-
showMore: t("chat.chats"),
|
|
257
|
-
showLess: t("chat.chats"),
|
|
258
|
-
}}
|
|
259
|
-
previewCount={5}
|
|
260
|
-
expandedCount={15}
|
|
261
|
-
onTogglePin={(threadId) => {
|
|
262
|
-
const thread = visibleThreads.find((item) => item.id === threadId);
|
|
263
|
-
if (thread) void pinThread(threadId, !thread.pinnedAt);
|
|
264
197
|
}}
|
|
265
|
-
onRename={handleRenameThread}
|
|
266
198
|
renameMaxLength={160}
|
|
267
199
|
onDelete={(threadId) => void handleArchiveThread(threadId)}
|
|
268
|
-
renderAdditionalRowActions={(item, closeMenu) =>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
200
|
+
renderAdditionalRowActions={(item, closeMenu) =>
|
|
201
|
+
item.readOnly ? null : (
|
|
202
|
+
<button
|
|
203
|
+
type="button"
|
|
204
|
+
role="menuitem"
|
|
205
|
+
className="an-chat-history-row__menu-item"
|
|
206
|
+
onClick={() => {
|
|
207
|
+
closeMenu();
|
|
208
|
+
void handleCopyShareLink(item.id);
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
<IconShare3 size={13} strokeWidth={1.8} />
|
|
212
|
+
<span>{t("chat.copyShareLink")}</span>
|
|
213
|
+
</button>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
282
216
|
labels={{
|
|
283
217
|
options: (item) =>
|
|
284
218
|
t("chat.optionsFor", { title: item.titleText ?? "" }),
|