@nextclaw/ui 0.20.1 → 0.20.3
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/CHANGELOG.md +30 -0
- package/dist/assets/{api-BBFMpRGl.js → api-iMkU_o5P.js} +3 -3
- package/dist/assets/{appearance-settings-page-C1Erayzl.js → appearance-settings-page-CU5k4b-M.js} +1 -1
- package/dist/assets/{book-open-BhTIpBKg.js → book-open-nD8gdMhA.js} +1 -1
- package/dist/assets/channels-list-page-Drlt62kY.js +8 -0
- package/dist/assets/{config-split-page-By7p-4X-.js → config-split-page-BtJZp1E5.js} +1 -1
- package/dist/assets/{confirm-dialog-xpl1Yl7r.js → confirm-dialog-k2-Jq_x3.js} +1 -1
- package/dist/assets/{createLucideIcon-B_8khHn8.js → createLucideIcon-DxlwKLr-.js} +1 -1
- package/dist/assets/{desktop-update-config-CGFfa7-Z.js → desktop-update-config-BiZmy7FC.js} +1 -1
- package/dist/assets/{dist-CXN62DSs.js → dist-Cy_e4kNb.js} +1 -1
- package/dist/assets/{dist-DdE-kCbi.js → dist-m08DUswB.js} +1 -1
- package/dist/assets/doc-browser-CPsPHh12.js +1 -0
- package/dist/assets/{doc-browser-context-B8NCuMv5.js → doc-browser-context-jiy-O4F8.js} +1 -1
- package/dist/assets/doc-browser-npOvWtgf.js +1 -0
- package/dist/assets/{extensions-CMctxyTf.js → extensions-CIb17__o.js} +1 -1
- package/dist/assets/{form-actions-BFgTdxqI.js → form-actions-DGf5zUg_.js} +1 -1
- package/dist/assets/index-C86hxgv5.css +1 -0
- package/dist/assets/index-CYBXR7zN.js +109 -0
- package/dist/assets/loader-circle-BlLuRFVW.js +1 -0
- package/dist/assets/mcp-marketplace-page-B12JNh7Q.js +1 -0
- package/dist/assets/mcp-marketplace-page-kLWW6kzt.js +9 -0
- package/dist/assets/model-config-page-DSeAjVT_.js +1 -0
- package/dist/assets/{navigation-link-BwWrm4k1.js → navigation-link-DAfwFCj4.js} +1 -1
- package/dist/assets/plus-D4LtWtTk.js +1 -0
- package/dist/assets/privacy-settings-page-tN0Mt15x.js +1 -0
- package/dist/assets/providers-config-page-CgPrXR1u.js +1 -0
- package/dist/assets/react-8iBFECZE.js +8 -0
- package/dist/assets/{refresh-cw-B7NmPJ_Z.js → refresh-cw-D_Eibznw.js} +1 -1
- package/dist/assets/remote-Derr_AQp.js +1 -0
- package/dist/assets/{rotate-cw-B0ajMaHu.js → rotate-cw-QQjmoWuq.js} +1 -1
- package/dist/assets/runtime-config-page-Crq96Jjq.js +1 -0
- package/dist/assets/{save-DWnYYpgQ.js → save-W2xthJJ2.js} +1 -1
- package/dist/assets/{search-D6pX7FC-.js → search-Vih1R2BD.js} +1 -1
- package/dist/assets/{search-config-page-Cx3o_675.js → search-config-page-CtBcL2uc.js} +1 -1
- package/dist/assets/{secrets-config-page-tyFl7Rvt.js → secrets-config-page-Q6XfIj0h.js} +2 -2
- package/dist/assets/security-config-CE9rkQvq.js +1 -0
- package/dist/assets/{select-qEtFTWB5.js → select-lNdozU4o.js} +1 -1
- package/dist/assets/skeleton-B__aTcly.js +1 -0
- package/dist/assets/{tag-chip-vcjAYdV_.js → tag-chip-gdaU9bE7.js} +1 -1
- package/dist/assets/x-CWdWBu_U.js +1 -0
- package/dist/index.html +21 -21
- package/package.json +7 -7
- package/src/app/components/layout/sidebar.tsx +7 -0
- package/src/features/apps/components/app-package-card.tsx +29 -7
- package/src/features/apps/components/app-packages-panel.tsx +4 -0
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +18 -2
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +6 -0
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +4 -3
- package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +27 -6
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +17 -17
- package/src/features/chat/components/layout/chat-page-shell.tsx +19 -2
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +33 -5
- package/src/features/chat/components/layout/chat-sidebar.tsx +31 -21
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +41 -1
- package/src/features/chat/features/conversation/hooks/use-session-pending-input-actions.ts +22 -4
- package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +2 -2
- package/src/features/chat/features/message/utils/__tests__/chat-message-trigger-details.utils.test.ts +35 -0
- package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +2 -2
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime-queue.test.tsx +50 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-list-view.test.tsx +35 -6
- package/src/features/chat/features/ncp/hooks/use-ncp-session-list-view.ts +43 -20
- package/src/features/chat/features/ncp/hooks/use-ncp-session-queries.ts +15 -1
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +26 -2
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +55 -27
- package/src/features/chat/features/session/components/chat-sidebar-context-card.tsx +60 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +95 -54
- package/src/features/chat/features/session/components/chat-sidebar-session-entry.tsx +9 -8
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +141 -95
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +11 -2
- package/src/features/chat/features/session/hooks/use-chat-sidebar-context-counts.ts +40 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-token-usage.test.tsx +4 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.scroll-restoration.test.tsx +182 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +16 -9
- package/src/features/chat/features/workspace/components/__tests__/session-cron-job-content.test.tsx +78 -0
- package/src/features/chat/features/workspace/components/__tests__/workspace-markdown-outline.test.tsx +128 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +3 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +65 -2
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +14 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +9 -0
- package/src/features/chat/features/workspace/components/overview/chat-session-token-usage.tsx +17 -0
- package/src/features/chat/features/workspace/components/session-cron-job-content.tsx +269 -75
- package/src/features/chat/features/workspace/components/workspace-markdown-outline.tsx +156 -0
- package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +5 -0
- package/src/features/cron/components/__tests__/cron-config.test.tsx +16 -5
- package/src/features/cron/components/cron-config.tsx +20 -46
- package/src/features/cron/components/cron-job-row.tsx +1 -1
- package/src/features/cron/hooks/__tests__/use-cron-job-actions.test.tsx +95 -0
- package/src/features/cron/hooks/use-cron-job-actions.ts +109 -0
- package/src/features/cron/index.ts +2 -0
- package/src/features/inbox/pages/inbox-page.tsx +18 -2
- package/src/features/marketplace/components/marketplace-page.tsx +7 -0
- package/src/features/panel-apps/pages/panel-app-main-page.tsx +8 -0
- package/src/features/settings/pages/__tests__/privacy-settings-page.test.tsx +14 -5
- package/src/features/settings/pages/privacy-settings-page.tsx +38 -2
- package/src/platforms/desktop/components/desktop-app-shell.tsx +10 -1
- package/src/shared/components/doc-browser/doc-browser.tsx +0 -1
- package/src/shared/components/doc-browser/hooks/use-doc-browser-scroll-restoration.ts +9 -113
- package/src/shared/components/ui/actions/icon-action-button.tsx +1 -8
- package/src/shared/components/ui/tooltip.tsx +16 -1
- package/src/shared/hooks/use-config.ts +11 -0
- package/src/shared/hooks/use-panel-app-scroll-restoration.test.tsx +66 -0
- package/src/shared/hooks/use-panel-app-scroll-restoration.ts +107 -0
- package/src/shared/hooks/use-scroll-restoration.test.tsx +62 -0
- package/src/shared/hooks/use-scroll-restoration.ts +61 -0
- package/src/shared/lib/api/ncp-session-query-cache.utils.ts +1 -0
- package/src/shared/lib/api/ncp-session.types.ts +3 -0
- package/src/shared/lib/api/types.ts +9 -0
- package/src/shared/lib/api/utils/config.utils.ts +5 -0
- package/src/shared/lib/api/utils/ncp-session.utils.ts +3 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +15 -5
- package/src/shared/lib/i18n/locales/en-US/core.json +17 -9
- package/src/shared/lib/i18n/locales/en-US/cron.json +4 -2
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +1 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +15 -5
- package/src/shared/lib/i18n/locales/zh-CN/core.json +17 -9
- package/src/shared/lib/i18n/locales/zh-CN/cron.json +4 -2
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +1 -0
- package/src/shared/lib/navigation-history/index.ts +1 -0
- package/src/shared/lib/navigation-history/scroll-restoration.manager.test.ts +37 -0
- package/src/shared/lib/navigation-history/scroll-restoration.manager.ts +60 -0
- package/dist/assets/channels-list-page-OTCkjMOW.js +0 -8
- package/dist/assets/doc-browser-5xrHcQ2z.js +0 -1
- package/dist/assets/doc-browser-c2yO477-.js +0 -1
- package/dist/assets/index-DUCA8m2w.js +0 -109
- package/dist/assets/index-QrdUWHfm.css +0 -1
- package/dist/assets/loader-circle-CbdTrSp3.js +0 -1
- package/dist/assets/mcp-marketplace-page-DGve0muV.js +0 -9
- package/dist/assets/mcp-marketplace-page-_37VzG2q.js +0 -1
- package/dist/assets/model-config-page-DyC5H0bu.js +0 -1
- package/dist/assets/plus-DbeE4r-Q.js +0 -1
- package/dist/assets/privacy-settings-page--MOnhAZ6.js +0 -1
- package/dist/assets/providers-config-page-ChL3FUvK.js +0 -1
- package/dist/assets/react-DVMmnPWz.js +0 -8
- package/dist/assets/remote-DFoxj4ls.js +0 -1
- package/dist/assets/runtime-config-page-BQdT3nmd.js +0 -1
- package/dist/assets/security-config-CUTUm6JT.js +0 -1
- package/dist/assets/skeleton-7MYPzn8w.js +0 -1
- package/dist/assets/x-DThaS5ZL.js +0 -1
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "@/features/chat/features/session/utils/chat-session-display.utils";
|
|
9
9
|
import { resolveSessionContextView } from "@/features/chat/features/session/utils/session-context.utils";
|
|
10
10
|
import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
11
|
+
import { t } from "@/shared/lib/i18n";
|
|
11
12
|
|
|
12
13
|
export function ChatSidebarSessionEntry(props: {
|
|
13
14
|
item: NcpSessionListItemView;
|
|
@@ -18,6 +19,7 @@ export function ChatSidebarSessionEntry(props: {
|
|
|
18
19
|
{ displayName?: string | null; avatarUrl?: string | null }
|
|
19
20
|
>;
|
|
20
21
|
childSessionsByParentKey: Map<string, NcpSessionListItemView[]>;
|
|
22
|
+
cronJobCount: number;
|
|
21
23
|
editingSessionKey: string | null;
|
|
22
24
|
draftLabel: string;
|
|
23
25
|
savingSessionKey: string | null;
|
|
@@ -25,10 +27,6 @@ export function ChatSidebarSessionEntry(props: {
|
|
|
25
27
|
isPinned: boolean;
|
|
26
28
|
sessionTitle: (session: SessionEntryView) => string;
|
|
27
29
|
onSelectSession: (sessionKey: string) => void;
|
|
28
|
-
onOpenChildSessions: (
|
|
29
|
-
parentSessionKey: string,
|
|
30
|
-
activeChildSessionKey: string | null,
|
|
31
|
-
) => void;
|
|
32
30
|
onStartEditingSessionLabel: (session: SessionEntryView) => void;
|
|
33
31
|
onDraftLabelChange: (value: string) => void;
|
|
34
32
|
onSaveSessionLabel: (session: SessionEntryView) => void;
|
|
@@ -41,6 +39,7 @@ export function ChatSidebarSessionEntry(props: {
|
|
|
41
39
|
optimisticReadAtBySessionKey,
|
|
42
40
|
agentsById,
|
|
43
41
|
childSessionsByParentKey,
|
|
42
|
+
cronJobCount,
|
|
44
43
|
editingSessionKey,
|
|
45
44
|
draftLabel,
|
|
46
45
|
savingSessionKey,
|
|
@@ -48,7 +47,6 @@ export function ChatSidebarSessionEntry(props: {
|
|
|
48
47
|
isPinned,
|
|
49
48
|
sessionTitle,
|
|
50
49
|
onSelectSession,
|
|
51
|
-
onOpenChildSessions,
|
|
52
50
|
onStartEditingSessionLabel,
|
|
53
51
|
onDraftLabelChange,
|
|
54
52
|
onSaveSessionLabel,
|
|
@@ -98,13 +96,16 @@ export function ChatSidebarSessionEntry(props: {
|
|
|
98
96
|
: null
|
|
99
97
|
}
|
|
100
98
|
childSessionCount={childSessions.length}
|
|
99
|
+
cronJobCount={cronJobCount}
|
|
100
|
+
projectName={
|
|
101
|
+
session.projectName?.trim() ||
|
|
102
|
+
session.projectRoot?.trim().split(/[\\/]/).filter(Boolean).at(-1) ||
|
|
103
|
+
t("chatSidebarContextNoProject")
|
|
104
|
+
}
|
|
101
105
|
isEditing={editingSessionKey === session.key}
|
|
102
106
|
draftLabel={draftLabel}
|
|
103
107
|
isSaving={savingSessionKey === session.key}
|
|
104
108
|
onSelect={() => onSelectSession(session.key)}
|
|
105
|
-
onOpenChildSessions={() =>
|
|
106
|
-
onOpenChildSessions(session.key, childSessions[0]?.session.key ?? null)
|
|
107
|
-
}
|
|
108
109
|
onStartEditing={() => onStartEditingSessionLabel(session)}
|
|
109
110
|
onDraftLabelChange={onDraftLabelChange}
|
|
110
111
|
onSave={() => onSaveSessionLabel(session)}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { AgentAvatar } from
|
|
2
|
-
import { SessionContextIconNode } from
|
|
3
|
-
import { SessionRunBadge } from
|
|
4
|
-
import { ChatSessionMoreActionsMenu } from
|
|
5
|
-
import { IconActionButton } from
|
|
6
|
-
import { Input } from
|
|
7
|
-
import { type SessionContextView } from
|
|
8
|
-
import type { SessionRunStatus } from
|
|
9
|
-
import { cn } from
|
|
10
|
-
import { t } from
|
|
11
|
-
import {
|
|
1
|
+
import { AgentAvatar } from "@/shared/components/common/agent-avatar";
|
|
2
|
+
import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
|
|
3
|
+
import { SessionRunBadge } from "@/features/chat/features/session/components/session-run-badge";
|
|
4
|
+
import { ChatSessionMoreActionsMenu } from "@/features/chat/features/session/components/session-header/chat-session-more-actions-menu";
|
|
5
|
+
import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
|
|
6
|
+
import { Input } from "@/shared/components/ui/input";
|
|
7
|
+
import { type SessionContextView } from "@/features/chat/features/session/utils/session-context.utils";
|
|
8
|
+
import type { SessionRunStatus } from "@/features/chat/types/session-run-status.types";
|
|
9
|
+
import { cn } from "@/shared/lib/utils";
|
|
10
|
+
import { t } from "@/shared/lib/i18n";
|
|
11
|
+
import {
|
|
12
|
+
AlarmClock,
|
|
13
|
+
Check,
|
|
14
|
+
Folder,
|
|
15
|
+
GitBranch,
|
|
16
|
+
Pencil,
|
|
17
|
+
Pin,
|
|
18
|
+
X,
|
|
19
|
+
} from "lucide-react";
|
|
20
|
+
import { ChatSidebarContextCard } from "@/features/chat/features/session/components/chat-sidebar-context-card";
|
|
12
21
|
|
|
13
22
|
type ChatSidebarSessionItemProps = {
|
|
14
23
|
sessionKey: string;
|
|
@@ -24,11 +33,12 @@ type ChatSidebarSessionItemProps = {
|
|
|
24
33
|
agentLabel?: string | null;
|
|
25
34
|
agentAvatarUrl?: string | null;
|
|
26
35
|
childSessionCount?: number;
|
|
36
|
+
cronJobCount?: number;
|
|
37
|
+
projectName?: string | null;
|
|
27
38
|
isEditing: boolean;
|
|
28
39
|
draftLabel: string;
|
|
29
40
|
isSaving: boolean;
|
|
30
41
|
onSelect: () => void;
|
|
31
|
-
onOpenChildSessions?: () => void;
|
|
32
42
|
onStartEditing: () => void;
|
|
33
43
|
onDraftLabelChange: (value: string) => void;
|
|
34
44
|
onSave: () => void | Promise<void>;
|
|
@@ -38,7 +48,12 @@ type ChatSidebarSessionItemProps = {
|
|
|
38
48
|
|
|
39
49
|
type ChatSidebarSessionEditingViewProps = Pick<
|
|
40
50
|
ChatSidebarSessionItemProps,
|
|
41
|
-
|
|
51
|
+
| "sessionKey"
|
|
52
|
+
| "draftLabel"
|
|
53
|
+
| "isSaving"
|
|
54
|
+
| "onDraftLabelChange"
|
|
55
|
+
| "onSave"
|
|
56
|
+
| "onCancel"
|
|
42
57
|
>;
|
|
43
58
|
|
|
44
59
|
function ChatSidebarSessionEditingView({
|
|
@@ -47,7 +62,7 @@ function ChatSidebarSessionEditingView({
|
|
|
47
62
|
isSaving,
|
|
48
63
|
onDraftLabelChange,
|
|
49
64
|
onSave,
|
|
50
|
-
onCancel
|
|
65
|
+
onCancel,
|
|
51
66
|
}: ChatSidebarSessionEditingViewProps) {
|
|
52
67
|
return (
|
|
53
68
|
<div className="space-y-2">
|
|
@@ -55,32 +70,34 @@ function ChatSidebarSessionEditingView({
|
|
|
55
70
|
value={draftLabel}
|
|
56
71
|
onChange={(event) => onDraftLabelChange(event.target.value)}
|
|
57
72
|
onKeyDown={(event) => {
|
|
58
|
-
if (event.key ===
|
|
73
|
+
if (event.key === "Enter") {
|
|
59
74
|
event.preventDefault();
|
|
60
75
|
void onSave();
|
|
61
|
-
} else if (event.key ===
|
|
76
|
+
} else if (event.key === "Escape") {
|
|
62
77
|
event.preventDefault();
|
|
63
78
|
onCancel();
|
|
64
79
|
}
|
|
65
80
|
}}
|
|
66
|
-
placeholder={t(
|
|
81
|
+
placeholder={t("sessionsLabelPlaceholder")}
|
|
67
82
|
className="h-8 rounded-lg border-border bg-background text-xs"
|
|
68
83
|
autoFocus
|
|
69
84
|
disabled={isSaving}
|
|
70
85
|
/>
|
|
71
86
|
<div className="flex items-center justify-between gap-2">
|
|
72
|
-
<div className="min-w-0 truncate text-[11px] text-muted-foreground/65">
|
|
87
|
+
<div className="min-w-0 truncate text-[11px] text-muted-foreground/65">
|
|
88
|
+
{sessionKey}
|
|
89
|
+
</div>
|
|
73
90
|
<div className="flex items-center gap-1">
|
|
74
91
|
<IconActionButton
|
|
75
92
|
icon={<Check className="h-3.5 w-3.5" />}
|
|
76
|
-
label={t(
|
|
93
|
+
label={t("save")}
|
|
77
94
|
tooltip={false}
|
|
78
95
|
onClick={() => void onSave()}
|
|
79
96
|
disabled={isSaving}
|
|
80
97
|
/>
|
|
81
98
|
<IconActionButton
|
|
82
99
|
icon={<X className="h-3.5 w-3.5" />}
|
|
83
|
-
label={t(
|
|
100
|
+
label={t("cancel")}
|
|
84
101
|
tooltip={false}
|
|
85
102
|
onClick={onCancel}
|
|
86
103
|
disabled={isSaving}
|
|
@@ -93,8 +110,15 @@ function ChatSidebarSessionEditingView({
|
|
|
93
110
|
|
|
94
111
|
type ChatSidebarSessionDisplayViewProps = Omit<
|
|
95
112
|
ChatSidebarSessionItemProps,
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
| "sessionKey"
|
|
114
|
+
| "isEditing"
|
|
115
|
+
| "draftLabel"
|
|
116
|
+
| "isSaving"
|
|
117
|
+
| "onDraftLabelChange"
|
|
118
|
+
| "onSave"
|
|
119
|
+
| "onCancel"
|
|
120
|
+
> &
|
|
121
|
+
Pick<ChatSidebarSessionItemProps, "sessionKey">;
|
|
98
122
|
|
|
99
123
|
function ChatSidebarSessionDisplayView({
|
|
100
124
|
sessionKey,
|
|
@@ -110,93 +134,113 @@ function ChatSidebarSessionDisplayView({
|
|
|
110
134
|
agentLabel,
|
|
111
135
|
agentAvatarUrl,
|
|
112
136
|
childSessionCount = 0,
|
|
137
|
+
cronJobCount = 0,
|
|
138
|
+
projectName,
|
|
113
139
|
onSelect,
|
|
114
|
-
onOpenChildSessions,
|
|
115
140
|
onStartEditing,
|
|
116
|
-
onTogglePinned
|
|
141
|
+
onTogglePinned,
|
|
117
142
|
}: ChatSidebarSessionDisplayViewProps) {
|
|
118
|
-
const trailingControlsClassName = childSessionCount > 0 && onOpenChildSessions ? 'pr-32' : 'pr-20';
|
|
119
|
-
|
|
120
143
|
return (
|
|
121
144
|
<div className="group/session relative">
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
<ChatSidebarContextCard
|
|
146
|
+
title={title}
|
|
147
|
+
metrics={[
|
|
148
|
+
{
|
|
149
|
+
icon: <Folder className="h-3.5 w-3.5" />,
|
|
150
|
+
label: t("chatSidebarContextProject"),
|
|
151
|
+
value: projectName,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
icon: <GitBranch className="h-3.5 w-3.5" />,
|
|
155
|
+
label: t("chatSidebarContextChildSessions"),
|
|
156
|
+
value: childSessionCount,
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
icon: <AlarmClock className="h-3.5 w-3.5" />,
|
|
160
|
+
label: t("chatSidebarContextScheduledTasks"),
|
|
161
|
+
value: cronJobCount,
|
|
162
|
+
},
|
|
163
|
+
]}
|
|
164
|
+
>
|
|
165
|
+
<button type="button" onClick={onSelect} className="w-full text-left">
|
|
166
|
+
<div
|
|
167
|
+
className={cn(
|
|
168
|
+
"flex min-h-6 min-w-0 items-center",
|
|
169
|
+
runStatus && "pr-6",
|
|
170
|
+
"group-hover/session:pr-20 group-has-[[data-session-actions]:focus-within]/session:pr-20",
|
|
171
|
+
)}
|
|
172
|
+
>
|
|
173
|
+
<span className="flex min-w-0 items-center gap-1.5">
|
|
174
|
+
{agentId?.trim() && agentId.trim().toLowerCase() !== "main" ? (
|
|
175
|
+
<AgentAvatar
|
|
176
|
+
agentId={agentId}
|
|
177
|
+
displayName={agentLabel}
|
|
178
|
+
avatarUrl={agentAvatarUrl}
|
|
179
|
+
className="h-5 w-5 shrink-0"
|
|
180
|
+
/>
|
|
181
|
+
) : null}
|
|
182
|
+
<span className="truncate leading-6 font-medium">{title}</span>
|
|
183
|
+
{context.label ? (
|
|
184
|
+
<span
|
|
185
|
+
className={cn(
|
|
186
|
+
"shrink-0 rounded-full border px-1.5 py-0.5 text-[10px] font-semibold leading-none",
|
|
187
|
+
active
|
|
188
|
+
? "border-border/70 bg-background/80 text-foreground/75"
|
|
189
|
+
: "border-transparent bg-muted/70 text-muted-foreground",
|
|
190
|
+
)}
|
|
191
|
+
>
|
|
192
|
+
{context.label}
|
|
193
|
+
</span>
|
|
194
|
+
) : null}
|
|
195
|
+
{context.icon ? (
|
|
196
|
+
<span className="inline-flex h-4 w-4 shrink-0 items-center justify-center">
|
|
197
|
+
<SessionContextIconNode
|
|
198
|
+
icon={context.icon}
|
|
199
|
+
className={cn(
|
|
200
|
+
"h-[13px] w-[13px]",
|
|
201
|
+
active ? "text-foreground/75" : "text-muted-foreground",
|
|
202
|
+
)}
|
|
203
|
+
/>
|
|
204
|
+
</span>
|
|
205
|
+
) : null}
|
|
206
|
+
</span>
|
|
207
|
+
</div>
|
|
208
|
+
<div className="mt-0.5 flex items-center gap-2 text-[11px] text-muted-foreground/65">
|
|
209
|
+
<span className="min-w-0 truncate">{previewText}</span>
|
|
210
|
+
{showUnreadDot ? (
|
|
135
211
|
<span
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{context.label}
|
|
144
|
-
</span>
|
|
145
|
-
) : null}
|
|
146
|
-
{context.icon ? (
|
|
147
|
-
<span className="inline-flex h-4 w-4 shrink-0 items-center justify-center">
|
|
148
|
-
<SessionContextIconNode icon={context.icon} className={cn('h-[13px] w-[13px]', active ? 'text-foreground/75' : 'text-muted-foreground')} />
|
|
149
|
-
</span>
|
|
150
|
-
) : null}
|
|
151
|
-
</span>
|
|
152
|
-
</div>
|
|
153
|
-
<div className="mt-0.5 flex items-center gap-2 text-[11px] text-muted-foreground/65">
|
|
154
|
-
<span className="min-w-0 truncate">
|
|
155
|
-
{previewText}
|
|
156
|
-
</span>
|
|
157
|
-
{showUnreadDot ? (
|
|
158
|
-
<span
|
|
159
|
-
aria-label={t('chatSessionUnread')}
|
|
160
|
-
className="ml-auto h-2 w-2 shrink-0 rounded-full bg-primary"
|
|
161
|
-
/>
|
|
162
|
-
) : (
|
|
163
|
-
<span className="ml-auto shrink-0">{trailingText}</span>
|
|
164
|
-
)}
|
|
165
|
-
</div>
|
|
166
|
-
</button>
|
|
167
|
-
{childSessionCount > 0 && onOpenChildSessions ? (
|
|
168
|
-
<button
|
|
169
|
-
type="button"
|
|
170
|
-
onClick={(event) => {
|
|
171
|
-
event.stopPropagation();
|
|
172
|
-
onOpenChildSessions();
|
|
173
|
-
}}
|
|
174
|
-
className="pointer-events-none absolute right-12 top-0 inline-flex h-6 items-center gap-1 rounded-md px-1.5 text-[10px] font-medium text-muted-foreground opacity-0 transition-colors hover:bg-background/80 hover:text-foreground group-hover/session:pointer-events-auto group-hover/session:opacity-100 focus-visible:pointer-events-auto focus-visible:opacity-100"
|
|
175
|
-
aria-label={t('chatSessionOpenChildSessions')}
|
|
176
|
-
title={t('chatSessionOpenChildSessions')}
|
|
177
|
-
>
|
|
178
|
-
<GitBranch className="h-3.5 w-3.5" />
|
|
179
|
-
<span>{childSessionCount}</span>
|
|
212
|
+
aria-label={t("chatSessionUnread")}
|
|
213
|
+
className="ml-auto h-2 w-2 shrink-0 rounded-full bg-primary"
|
|
214
|
+
/>
|
|
215
|
+
) : (
|
|
216
|
+
<span className="ml-auto shrink-0">{trailingText}</span>
|
|
217
|
+
)}
|
|
218
|
+
</div>
|
|
180
219
|
</button>
|
|
181
|
-
|
|
220
|
+
</ChatSidebarContextCard>
|
|
182
221
|
{runStatus ? (
|
|
183
222
|
<span className="absolute right-0 top-0 inline-flex h-6 w-6 items-center justify-center transition-opacity group-hover/session:opacity-0">
|
|
184
223
|
<SessionRunBadge status={runStatus} />
|
|
185
224
|
</span>
|
|
186
225
|
) : null}
|
|
187
|
-
<div
|
|
226
|
+
<div
|
|
227
|
+
data-session-actions
|
|
228
|
+
className="pointer-events-none absolute right-0 top-0 flex h-6 items-center gap-0.5 opacity-0 transition-opacity group-hover/session:pointer-events-auto group-hover/session:opacity-100 focus-within:pointer-events-auto focus-within:opacity-100"
|
|
229
|
+
>
|
|
188
230
|
<IconActionButton
|
|
189
231
|
size="sm"
|
|
190
232
|
tone="strong"
|
|
191
233
|
icon={
|
|
192
234
|
<Pin
|
|
193
235
|
className={cn(
|
|
194
|
-
|
|
195
|
-
isPinned &&
|
|
236
|
+
"h-3.5 w-3.5",
|
|
237
|
+
isPinned && "fill-current text-foreground",
|
|
196
238
|
)}
|
|
197
239
|
/>
|
|
198
240
|
}
|
|
199
|
-
label={t(
|
|
241
|
+
label={t(
|
|
242
|
+
isPinned ? "chatSidebarUnpinSession" : "chatSidebarPinSession",
|
|
243
|
+
)}
|
|
200
244
|
tooltipSide="right"
|
|
201
245
|
onClick={(event) => {
|
|
202
246
|
event.stopPropagation();
|
|
@@ -207,7 +251,7 @@ function ChatSidebarSessionDisplayView({
|
|
|
207
251
|
size="sm"
|
|
208
252
|
tone="strong"
|
|
209
253
|
icon={<Pencil className="h-3.5 w-3.5" />}
|
|
210
|
-
label={t(
|
|
254
|
+
label={t("edit")}
|
|
211
255
|
tooltipSide="right"
|
|
212
256
|
onClick={(event) => {
|
|
213
257
|
event.stopPropagation();
|
|
@@ -238,24 +282,25 @@ export function ChatSidebarSessionItem({
|
|
|
238
282
|
agentLabel,
|
|
239
283
|
agentAvatarUrl,
|
|
240
284
|
childSessionCount,
|
|
285
|
+
cronJobCount,
|
|
286
|
+
projectName,
|
|
241
287
|
isEditing,
|
|
242
288
|
draftLabel,
|
|
243
289
|
isSaving,
|
|
244
290
|
onSelect,
|
|
245
|
-
onOpenChildSessions,
|
|
246
291
|
onStartEditing,
|
|
247
292
|
onDraftLabelChange,
|
|
248
293
|
onSave,
|
|
249
294
|
onCancel,
|
|
250
|
-
onTogglePinned
|
|
295
|
+
onTogglePinned,
|
|
251
296
|
}: ChatSidebarSessionItemProps) {
|
|
252
297
|
return (
|
|
253
298
|
<div
|
|
254
299
|
className={cn(
|
|
255
|
-
|
|
300
|
+
"w-full rounded-lg px-2.5 py-2 text-left text-[13px] transition-colors",
|
|
256
301
|
active
|
|
257
|
-
?
|
|
258
|
-
:
|
|
302
|
+
? "bg-background/90 font-medium text-foreground"
|
|
303
|
+
: "text-foreground/80 hover:bg-background/65 hover:text-foreground",
|
|
259
304
|
)}
|
|
260
305
|
>
|
|
261
306
|
{isEditing ? (
|
|
@@ -283,7 +328,8 @@ export function ChatSidebarSessionItem({
|
|
|
283
328
|
agentAvatarUrl={agentAvatarUrl}
|
|
284
329
|
onSelect={onSelect}
|
|
285
330
|
childSessionCount={childSessionCount}
|
|
286
|
-
|
|
331
|
+
cronJobCount={cronJobCount}
|
|
332
|
+
projectName={projectName}
|
|
287
333
|
onStartEditing={onStartEditing}
|
|
288
334
|
onTogglePinned={onTogglePinned}
|
|
289
335
|
/>
|
|
@@ -16,6 +16,7 @@ type ChatSidebarSessionListProps = {
|
|
|
16
16
|
isProjectFirstView: boolean;
|
|
17
17
|
groups: ChatSidebarDateGroup[];
|
|
18
18
|
projectGroups: ChatSidebarProjectGroup[];
|
|
19
|
+
projectCronJobCountByRoot: ReadonlyMap<string, number>;
|
|
19
20
|
defaultSessionType: string;
|
|
20
21
|
sessionTypeOptions: SessionTypeOption[];
|
|
21
22
|
renderSessionItem: (item: NcpSessionListItemView) => ReactNode;
|
|
@@ -36,11 +37,16 @@ export function ChatSidebarSessionList({
|
|
|
36
37
|
isLoading,
|
|
37
38
|
isProjectFirstView,
|
|
38
39
|
projectGroups,
|
|
40
|
+
projectCronJobCountByRoot,
|
|
39
41
|
renderSessionItem,
|
|
40
42
|
sessionTypeOptions,
|
|
41
43
|
}: ChatSidebarSessionListProps) {
|
|
42
44
|
if (isLoading) {
|
|
43
|
-
return
|
|
45
|
+
return (
|
|
46
|
+
<div className="p-3 text-xs text-muted-foreground">
|
|
47
|
+
{t("sessionsLoading")}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
if (isProjectFirstView) {
|
|
@@ -49,6 +55,7 @@ export function ChatSidebarSessionList({
|
|
|
49
55
|
) : (
|
|
50
56
|
<ChatSidebarProjectGroups
|
|
51
57
|
groups={projectGroups}
|
|
58
|
+
projectCronJobCountByRoot={projectCronJobCountByRoot}
|
|
52
59
|
defaultSessionType={defaultSessionType}
|
|
53
60
|
sessionTypeOptions={sessionTypeOptions}
|
|
54
61
|
renderSessionItem={renderSessionItem}
|
|
@@ -67,7 +74,9 @@ export function ChatSidebarSessionList({
|
|
|
67
74
|
<div className="px-2 pb-1 pt-0.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/65">
|
|
68
75
|
{group.label}
|
|
69
76
|
</div>
|
|
70
|
-
<div className="space-y-0.5">
|
|
77
|
+
<div className="space-y-0.5">
|
|
78
|
+
{group.items.map(renderSessionItem)}
|
|
79
|
+
</div>
|
|
71
80
|
</div>
|
|
72
81
|
))}
|
|
73
82
|
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useCronJobs } from "@/features/cron";
|
|
3
|
+
import type { NcpSessionListItemView } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
|
|
4
|
+
|
|
5
|
+
export function useChatSidebarContextCounts(
|
|
6
|
+
allItems: readonly NcpSessionListItemView[],
|
|
7
|
+
) {
|
|
8
|
+
const cronQuery = useCronJobs({ all: true });
|
|
9
|
+
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
const projectRootBySessionKey = new Map(
|
|
12
|
+
allItems.flatMap(({ session }) => {
|
|
13
|
+
const projectRoot = session.projectRoot?.trim();
|
|
14
|
+
return projectRoot ? [[session.key, projectRoot] as const] : [];
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
17
|
+
const cronJobCountBySessionKey = new Map<string, number>();
|
|
18
|
+
const cronJobCountByProjectRoot = new Map<string, number>();
|
|
19
|
+
|
|
20
|
+
for (const job of cronQuery.data?.jobs ?? []) {
|
|
21
|
+
const sessionKey = job.payload.sessionId?.trim();
|
|
22
|
+
if (!sessionKey) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
cronJobCountBySessionKey.set(
|
|
26
|
+
sessionKey,
|
|
27
|
+
(cronJobCountBySessionKey.get(sessionKey) ?? 0) + 1,
|
|
28
|
+
);
|
|
29
|
+
const projectRoot = projectRootBySessionKey.get(sessionKey);
|
|
30
|
+
if (projectRoot) {
|
|
31
|
+
cronJobCountByProjectRoot.set(
|
|
32
|
+
projectRoot,
|
|
33
|
+
(cronJobCountByProjectRoot.get(projectRoot) ?? 0) + 1,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { cronJobCountByProjectRoot, cronJobCountBySessionKey };
|
|
39
|
+
}, [allItems, cronQuery.data?.jobs]);
|
|
40
|
+
}
|
package/src/features/chat/features/workspace/components/__tests__/chat-session-token-usage.test.tsx
CHANGED
|
@@ -66,12 +66,15 @@ beforeEach(() => {
|
|
|
66
66
|
mocks.query.isError = false;
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
it('shows totals, cache
|
|
69
|
+
it('shows totals, model calls, cache read ratio, and usage grouped by model', () => {
|
|
70
70
|
render(<ChatSessionTokenUsage sessionKey="parent-1" />);
|
|
71
71
|
|
|
72
72
|
expect(screen.getByText('Token usage')).toBeTruthy();
|
|
73
73
|
expect(screen.getByText('1,490')).toBeTruthy();
|
|
74
74
|
expect(screen.getAllByText('40%').length).toBeGreaterThan(0);
|
|
75
|
+
expect(screen.getByText('Model calls')).toBeTruthy();
|
|
76
|
+
expect(screen.getByText('Reported calls')).toBeTruthy();
|
|
77
|
+
expect(screen.getByText('2 agent rounds · 3 model calls · Cache read ratio: 40%')).toBeTruthy();
|
|
75
78
|
expect(screen.getByText('openai/gpt-5')).toBeTruthy();
|
|
76
79
|
expect(screen.getByText('anthropic/claude-sonnet-4')).toBeTruthy();
|
|
77
80
|
expect(screen.getAllByText('Partial usage')).toHaveLength(2);
|