@nextclaw/ui 0.15.21 → 0.15.22
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 +20 -0
- package/dist/assets/{api-CHo2gekJ.js → api-B1yHQYs-.js} +3 -3
- package/dist/assets/app-presenter-provider-uP9waOzv.js +108 -0
- package/dist/assets/appearance-settings-page-C9Zq3hP9.js +1 -0
- package/dist/assets/{book-open-BrZL4o9L.js → book-open-oSmyVXea.js} +1 -1
- package/dist/assets/{card-B5rRLu_6.js → card-CGvJZTZp.js} +1 -1
- package/dist/assets/{channels-list-page-C1dnm68I.js → channels-list-page-5BTxpQHw.js} +2 -2
- package/dist/assets/chat-page-jTvli8Jp.js +1 -0
- package/dist/assets/config-split-page-BgBsUmp1.js +1 -0
- package/dist/assets/confirm-dialog-h849QRWO.js +5 -0
- package/dist/assets/desktop-update-config-BGGHxClR.js +1 -0
- package/dist/assets/{dist-C9EX46wM.js → dist-CxUhurq9.js} +1 -1
- package/dist/assets/doc-browser-0eL8AX42.js +1 -0
- package/dist/assets/{doc-browser-DH-3hDnI.js → doc-browser-DEqE9lsh.js} +1 -1
- package/dist/assets/{doc-browser-context-TU2heon0.js → doc-browser-context-DkzFIUoK.js} +1 -1
- package/dist/assets/{eye-B-3dyZNA.js → eye-gOWQGt8N.js} +1 -1
- package/dist/assets/{form-actions-DvvxCpL4.js → form-actions-CgjYZUzM.js} +1 -1
- package/dist/assets/{index-k_eWScvN.js → index-DSAUWCfl.js} +2 -2
- package/dist/assets/index-G7cBryJ4.css +1 -0
- package/dist/assets/loader-circle-BAuycGf6.js +1 -0
- package/dist/assets/{logo-badge-DB51c0cS.js → logo-badge-BNcsDsUM.js} +1 -1
- package/dist/assets/{mcp-marketplace-page-DWtsIqml.js → mcp-marketplace-page-BX39MtPa.js} +3 -3
- package/dist/assets/mcp-marketplace-page-CkDtOEA4.js +1 -0
- package/dist/assets/{model-config-page-D6cGNnMM.js → model-config-page-7MIXg2fQ.js} +1 -1
- package/dist/assets/{navigation-link-BSJEUiwD.js → navigation-link-B3iFOdxx.js} +1 -1
- package/dist/assets/plus-yvhFgfal.js +1 -0
- package/dist/assets/{popover-Bp9IpE_N.js → popover-Cpep_rJe.js} +1 -1
- package/dist/assets/{provider-models-CXUgDxGR.js → provider-models-CUrptZ-o.js} +1 -1
- package/dist/assets/provider-scoped-model-input-CHmOzz7x.js +1 -0
- package/dist/assets/providers-config-page-A6J7Y0II.js +1 -0
- package/dist/assets/react-BCJGCjrP.js +8 -0
- package/dist/assets/{refresh-cw-C2e94pmp.js → refresh-cw-CkKwPztT.js} +1 -1
- package/dist/assets/remote-FD-ZfSIn.js +1 -0
- package/dist/assets/rotate-cw-DrJMfHhp.js +1 -0
- package/dist/assets/{runtime-config-page-C57OLdPm.js → runtime-config-page-DwjO-Vs6.js} +1 -1
- package/dist/assets/{save-CkwDOgvt.js → save-BINMWate.js} +1 -1
- package/dist/assets/{search-CLXM2reO.js → search-7M38azRp.js} +1 -1
- package/dist/assets/{search-config-page-tnJXaaSr.js → search-config-page-T6tMhXbP.js} +1 -1
- package/dist/assets/{secrets-config-page-DQ_Curaj.js → secrets-config-page-2mVejXf2.js} +2 -2
- package/dist/assets/security-config-DD5RrjDe.js +1 -0
- package/dist/assets/{select-CNFBf00l.js → select-BvidP5Oa.js} +1 -1
- package/dist/assets/{settings-2-B9IT2HGd.js → settings-2-DqZstREV.js} +1 -1
- package/dist/assets/skeleton-BEQW2Dva.js +1 -0
- package/dist/assets/{tabs-BuNr8LSO.js → tabs-BeLxVmkt.js} +1 -1
- package/dist/assets/{tag-chip-CDkB5smU.js → tag-chip-BoDaLnWt.js} +1 -1
- package/dist/assets/{trash-2-GchaJuEM.js → trash-2-CYUskMMN.js} +1 -1
- package/dist/assets/{use-config-C69rR15i.js → use-config-CxKRXXCl.js} +1 -1
- package/dist/assets/use-confirm-dialog-HASTvOP2.js +1 -0
- package/dist/assets/{use-viewport-layout-BACNZyMY.js → use-viewport-layout-CJxkjoRz.js} +1 -1
- package/dist/assets/x-DdUwjre5.js +1 -0
- package/dist/index.html +29 -29
- package/package.json +7 -7
- package/src/app/components/layout/sidebar-rail.styles.ts +0 -2
- package/src/app/components/layout/sidebar.tsx +2 -2
- package/src/app/styles/design-system.css +12 -0
- package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +2 -2
- package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +19 -1
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +1 -1
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +5 -6
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +1 -1
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +1 -1
- package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +52 -0
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +11 -17
- package/src/features/chat/components/layout/chat-page-shell.tsx +2 -11
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +32 -42
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +13 -64
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +1 -4
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +1 -1
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +15 -4
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +7 -7
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +4 -2
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-attachments.ts +1 -0
- package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +1 -0
- package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +1 -0
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +3 -0
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +0 -1
- package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +3 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +29 -0
- package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +4 -1
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +10 -10
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +2 -2
- package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +1 -1
- package/src/features/chat/features/session/utils/__tests__/chat-session-display.utils.test.ts +26 -0
- package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +4 -2
- package/src/features/chat/features/session/utils/chat-session-display.utils.ts +48 -12
- package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +13 -4
- package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +6 -0
- package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +10 -7
- package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +3 -14
- package/src/features/chat/features/welcome/components/chat-welcome-prompt-suggestions.tsx +37 -0
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +33 -31
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +1 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +1 -1
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +15 -3
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +3 -10
- package/src/features/chat/managers/chat-session-list.manager.ts +3 -23
- package/src/features/chat/managers/chat-thread.manager.ts +2 -5
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +3 -9
- package/src/features/chat/pages/ncp-chat-page.tsx +0 -1
- package/src/features/chat/types/chat-input-bar.types.ts +1 -0
- package/src/features/cron/components/cron-task-discovery.tsx +1 -1
- package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
- package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +3 -0
- package/src/features/notifications/components/app-notification-toast.tsx +1 -1
- package/src/features/panel-apps/components/panel-app-list-item.tsx +1 -1
- package/src/features/panel-apps/components/panel-app-toolbar.tsx +2 -2
- package/src/features/panel-apps/components/panel-apps-list.tsx +4 -4
- package/src/features/service-apps/components/service-apps-panel.tsx +1 -1
- package/src/features/settings/components/config/provider-auth-section.tsx +90 -59
- package/src/features/settings/components/config/provider-form.tsx +10 -17
- package/src/features/settings/components/config/provider-status-badge.tsx +4 -2
- package/src/features/settings/pages/__tests__/providers-config-page.test.tsx +12 -0
- package/src/features/settings/pages/providers-config-page.tsx +2 -1
- package/src/platforms/mobile/components/__tests__/mobile-app-shell.test.tsx +13 -7
- package/src/platforms/mobile/components/__tests__/mobile-bottom-nav.test.tsx +18 -20
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +1 -1
- package/src/shared/components/path-picker/server-path-picker-directory-list.tsx +1 -1
- package/src/shared/components/path-picker/server-path-picker-locations.tsx +1 -1
- package/src/shared/components/path-picker/server-path-picker-navigation-bar.tsx +3 -3
- package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +1 -1
- package/src/shared/components/ui/actions/icon-action-button.tsx +1 -1
- package/src/shared/components/ui/button.tsx +4 -4
- package/src/shared/components/ui/dialog.tsx +1 -1
- package/src/shared/components/ui/input.tsx +1 -1
- package/src/shared/components/ui/scroll-area.tsx +2 -0
- package/src/shared/components/ui/select.tsx +1 -1
- package/src/shared/components/ui/switch.tsx +1 -1
- package/src/shared/components/ui/tabs.tsx +1 -1
- package/src/shared/components/ui/textarea.tsx +1 -1
- package/src/shared/lib/api/ncp-session.types.ts +1 -0
- package/src/shared/lib/api/types.ts +2 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +13 -14
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +13 -14
- package/src/shared/lib/provider-models/__tests__/anonymous-provider-configuration.test.ts +40 -0
- package/src/shared/lib/provider-models/index.ts +4 -3
- package/dist/assets/app-presenter-provider-2_DmWYT9.js +0 -108
- package/dist/assets/appearance-settings-page-BEy2UE7R.js +0 -1
- package/dist/assets/chat-page-Dz6tvZ91.js +0 -1
- package/dist/assets/config-split-page-6y6W6dz4.js +0 -1
- package/dist/assets/confirm-dialog-q1CpEmR8.js +0 -5
- package/dist/assets/desktop-update-config-DrdlwP4j.js +0 -1
- package/dist/assets/doc-browser-BfttDSti.js +0 -1
- package/dist/assets/index-CXDPLDFD.css +0 -1
- package/dist/assets/loader-circle-B35NlHTI.js +0 -1
- package/dist/assets/mcp-marketplace-page-CkPtwPBk.js +0 -1
- package/dist/assets/plus-BRVe5jSJ.js +0 -1
- package/dist/assets/provider-scoped-model-input-dk7fz9NL.js +0 -1
- package/dist/assets/providers-config-page-DTP2YfxA.js +0 -1
- package/dist/assets/react-D6ShNq95.js +0 -8
- package/dist/assets/remote-FllhXW3o.js +0 -1
- package/dist/assets/rotate-cw-CTC-Qszi.js +0 -1
- package/dist/assets/security-config-S00DjFfO.js +0 -1
- package/dist/assets/skeleton-Dc06gatK.js +0 -1
- package/dist/assets/use-confirm-dialog-DF_EgPYx.js +0 -1
- package/dist/assets/x-BXFquY9e.js +0 -1
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +0 -59
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
@layer base {
|
|
8
|
+
input:focus,
|
|
9
|
+
textarea:focus {
|
|
10
|
+
outline: none;
|
|
11
|
+
box-shadow: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
:root {
|
|
9
15
|
color-scheme: light;
|
|
10
16
|
|
|
@@ -70,6 +76,9 @@
|
|
|
70
76
|
/* Accent */
|
|
71
77
|
--accent: var(--brand-50);
|
|
72
78
|
--accent-foreground: var(--brand-700);
|
|
79
|
+
--interaction-hover: hsl(0 0% 96%);
|
|
80
|
+
--interaction-selection: hsl(211 100% 92%);
|
|
81
|
+
--interaction-selection-border: hsl(211 75% 84%);
|
|
73
82
|
|
|
74
83
|
/* Muted */
|
|
75
84
|
--muted: var(--gray-100);
|
|
@@ -717,6 +726,9 @@
|
|
|
717
726
|
--secondary-foreground: var(--gray-800);
|
|
718
727
|
--accent: 151 32% 18%;
|
|
719
728
|
--accent-foreground: 148 34% 84%;
|
|
729
|
+
--interaction-hover: hsl(var(--gray-200));
|
|
730
|
+
--interaction-selection: hsl(211 48% 30%);
|
|
731
|
+
--interaction-selection-border: hsl(211 48% 42%);
|
|
720
732
|
--muted: var(--gray-100);
|
|
721
733
|
--muted-foreground: var(--gray-500);
|
|
722
734
|
--destructive: 0 70% 52%;
|
|
@@ -10,7 +10,7 @@ export function ChatSidebarListModeSwitch({ isProjectFirstView, onSelectMode }:
|
|
|
10
10
|
type="button"
|
|
11
11
|
aria-pressed={!isProjectFirstView}
|
|
12
12
|
onClick={() => onSelectMode('time-first')}
|
|
13
|
-
className={cn('transition-colors', isProjectFirstView ? 'text-muted-foreground/65 hover:text-muted-foreground' : 'font-medium text-foreground')}
|
|
13
|
+
className={cn('rounded-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border', isProjectFirstView ? 'text-muted-foreground/65 hover:text-muted-foreground' : 'font-medium text-foreground')}
|
|
14
14
|
>
|
|
15
15
|
{t('chatSidebarViewTime')}
|
|
16
16
|
</button>
|
|
@@ -19,7 +19,7 @@ export function ChatSidebarListModeSwitch({ isProjectFirstView, onSelectMode }:
|
|
|
19
19
|
type="button"
|
|
20
20
|
aria-pressed={isProjectFirstView}
|
|
21
21
|
onClick={() => onSelectMode('project-first')}
|
|
22
|
-
className={cn('transition-colors', isProjectFirstView ? 'font-medium text-foreground' : 'text-muted-foreground/65 hover:text-muted-foreground')}
|
|
22
|
+
className={cn('rounded-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border', isProjectFirstView ? 'font-medium text-foreground' : 'text-muted-foreground/65 hover:text-muted-foreground')}
|
|
23
23
|
>
|
|
24
24
|
{t('chatSidebarViewProject')}
|
|
25
25
|
</button>
|
package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx
CHANGED
|
@@ -60,6 +60,7 @@ function renderContent(
|
|
|
60
60
|
isContextCompacting?: boolean;
|
|
61
61
|
isLoadingPreviousMessages?: boolean;
|
|
62
62
|
onLoadPreviousMessages?: () => Promise<void>;
|
|
63
|
+
showWelcome?: boolean;
|
|
63
64
|
} = {},
|
|
64
65
|
) {
|
|
65
66
|
return render(
|
|
@@ -74,8 +75,9 @@ function renderContent(
|
|
|
74
75
|
isSending={false}
|
|
75
76
|
messages={messages}
|
|
76
77
|
sessionKey="session-1"
|
|
77
|
-
showWelcome={false}
|
|
78
|
+
showWelcome={options.showWelcome ?? false}
|
|
78
79
|
onLoadPreviousMessages={options.onLoadPreviousMessages ?? vi.fn()}
|
|
80
|
+
welcomeSlot={<div data-testid="conversation-welcome" />}
|
|
79
81
|
/>,
|
|
80
82
|
);
|
|
81
83
|
}
|
|
@@ -94,6 +96,22 @@ it("renders manual context compaction feedback after the message list", () => {
|
|
|
94
96
|
expect(screen.getByTestId("context-compaction-pending")).toBeTruthy();
|
|
95
97
|
});
|
|
96
98
|
|
|
99
|
+
it("fades the conversation into the docked input without obscuring the final message", () => {
|
|
100
|
+
const { container } = renderContent();
|
|
101
|
+
const scrollContainer = container.querySelector<HTMLElement>(
|
|
102
|
+
'[data-chat-scroll-container="true"]',
|
|
103
|
+
);
|
|
104
|
+
expect(scrollContainer?.className).toContain("[mask-image:linear-gradient");
|
|
105
|
+
expect(scrollContainer?.className).toContain("[-webkit-mask-image:linear-gradient");
|
|
106
|
+
expect(scrollContainer?.firstElementChild?.className).toContain("pb-7");
|
|
107
|
+
|
|
108
|
+
const { container: welcomeContainer } = renderContent({ showWelcome: true });
|
|
109
|
+
const welcomeScrollContainer = welcomeContainer.querySelector<HTMLElement>(
|
|
110
|
+
'[data-chat-scroll-container="true"]',
|
|
111
|
+
);
|
|
112
|
+
expect(welcomeScrollContainer?.className).not.toContain("[mask-image:linear-gradient");
|
|
113
|
+
});
|
|
114
|
+
|
|
97
115
|
it("uses the centered reading track in flat message layout", () => {
|
|
98
116
|
useChatMessageLayoutStore.getState().setLayout("flat");
|
|
99
117
|
|
|
@@ -279,7 +279,7 @@ describe("ChatConversationHeaderSection", () => {
|
|
|
279
279
|
renderHeaderSection("mobile");
|
|
280
280
|
await user.click(screen.getByRole("button", { name: "New Task" }));
|
|
281
281
|
|
|
282
|
-
expect(mocks.createSession).toHaveBeenCalledWith("codex");
|
|
282
|
+
expect(mocks.createSession).toHaveBeenCalledWith({ sessionType: "codex" });
|
|
283
283
|
});
|
|
284
284
|
|
|
285
285
|
it("keeps the new-session shortcut mobile-only", () => {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "@/features/chat/features/message/components/chat-message-list.container";
|
|
14
14
|
import { ChatConversationTrack } from "@/features/chat/components/conversation/chat-conversation-track";
|
|
15
15
|
import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
|
|
16
|
+
import { SCROLL_BOTTOM_EDGE_FADE_CLASS } from "@/shared/components/ui/scroll-area";
|
|
16
17
|
import { t } from "@/shared/lib/i18n";
|
|
17
18
|
|
|
18
19
|
type ChatConversationContentProps = {
|
|
@@ -81,13 +82,13 @@ export function ChatConversationContent({
|
|
|
81
82
|
ref={threadRef}
|
|
82
83
|
onScroll={handleScroll}
|
|
83
84
|
data-chat-scroll-container="true"
|
|
84
|
-
className="h-full overflow-y-auto custom-scrollbar"
|
|
85
|
+
className={showWelcome ? "h-full overflow-y-auto custom-scrollbar" : `h-full overflow-y-auto custom-scrollbar ${SCROLL_BOTTOM_EDGE_FADE_CLASS}`}
|
|
85
86
|
style={{ overflowAnchor: "none" }}
|
|
86
87
|
>
|
|
87
88
|
{showWelcome ? (
|
|
88
89
|
(welcomeSlot ?? null)
|
|
89
90
|
) : (
|
|
90
|
-
<div ref={contentRef}>
|
|
91
|
+
<div ref={contentRef} className="pb-7">
|
|
91
92
|
{hasMessages ? (
|
|
92
93
|
<ChatConversationTrack className="relative py-4 sm:py-5">
|
|
93
94
|
{historyError ? (
|
|
@@ -109,9 +110,7 @@ export function ChatConversationContent({
|
|
|
109
110
|
) : null}
|
|
110
111
|
<ChatMessageListContainer
|
|
111
112
|
messages={messages}
|
|
112
|
-
isSending={
|
|
113
|
-
hasMessages && isSending && isAwaitingAssistantOutput
|
|
114
|
-
}
|
|
113
|
+
isSending={isSending && isAwaitingAssistantOutput}
|
|
115
114
|
scrollRef={threadRef}
|
|
116
115
|
sessionKey={sessionKey}
|
|
117
116
|
/>
|
|
@@ -132,7 +131,7 @@ export function ChatConversationContent({
|
|
|
132
131
|
label={t("chatScrollToBottom")}
|
|
133
132
|
onClick={scrollToBottom}
|
|
134
133
|
tooltipSide="top"
|
|
135
|
-
className="absolute bottom-4 left-1/2 z-10 h-9 w-9 -translate-x-1/2 rounded-full border border-border bg-background/90 text-foreground shadow-lg backdrop-blur hover:bg-
|
|
134
|
+
className="absolute bottom-4 left-1/2 z-10 h-9 w-9 -translate-x-1/2 rounded-full border border-border bg-background/90 text-foreground shadow-lg backdrop-blur hover:bg-[var(--interaction-hover)] hover:text-accent-foreground"
|
|
136
135
|
/>
|
|
137
136
|
) : null}
|
|
138
137
|
</div>
|
|
@@ -76,7 +76,7 @@ export function ChatConversationHeader({
|
|
|
76
76
|
icon={<ArrowLeft className="h-4 w-4" />}
|
|
77
77
|
label={t("chat")}
|
|
78
78
|
onClick={onBackToList}
|
|
79
|
-
className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-
|
|
79
|
+
className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground"
|
|
80
80
|
/>
|
|
81
81
|
) : null}
|
|
82
82
|
{leading}
|
|
@@ -189,7 +189,7 @@ describe("ChatSidebar read state sync", () => {
|
|
|
189
189
|
const { container } = renderReadStateSidebar(createTestQueryClient());
|
|
190
190
|
|
|
191
191
|
expect(
|
|
192
|
-
|
|
192
|
+
container.querySelector(".custom-scrollbar")?.className,
|
|
193
193
|
).toContain("[mask-image:linear-gradient");
|
|
194
194
|
expect(
|
|
195
195
|
screen.getByRole("button", { name: "Settings menu" }).parentElement
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { ChatSidebarSessionArea } from "@/features/chat/components/layout/chat-sidebar-desktop-layout";
|
|
4
|
+
|
|
5
|
+
function renderSessionArea(isProjectFirstView: boolean) {
|
|
6
|
+
const onAddProject = vi.fn();
|
|
7
|
+
const onSelectMode = vi.fn();
|
|
8
|
+
|
|
9
|
+
render(
|
|
10
|
+
<ChatSidebarSessionArea
|
|
11
|
+
defaultSessionType="native"
|
|
12
|
+
groups={[]}
|
|
13
|
+
isCollapsed={false}
|
|
14
|
+
isLoading={false}
|
|
15
|
+
isProjectFirstView={isProjectFirstView}
|
|
16
|
+
onAddProject={onAddProject}
|
|
17
|
+
onSelectMode={onSelectMode}
|
|
18
|
+
projectGroups={[]}
|
|
19
|
+
renderSessionItem={() => <></>}
|
|
20
|
+
sessionTypeOptions={[]}
|
|
21
|
+
/>,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return { onAddProject, onSelectMode };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("ChatSidebarSessionArea", () => {
|
|
28
|
+
it("reserves the action-row height while switching list modes", () => {
|
|
29
|
+
const { onSelectMode } = renderSessionArea(false);
|
|
30
|
+
|
|
31
|
+
expect(
|
|
32
|
+
screen.getByRole("button", { name: "Time" }).parentElement?.parentElement?.className,
|
|
33
|
+
).toContain("h-7");
|
|
34
|
+
fireEvent.click(screen.getByRole("button", { name: "Project" }));
|
|
35
|
+
|
|
36
|
+
expect(onSelectMode).toHaveBeenCalledWith("project-first");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("uses a folder-plus icon for the add-project action", () => {
|
|
40
|
+
const { onAddProject } = renderSessionArea(true);
|
|
41
|
+
const addProjectButton = screen.getByRole("button", { name: "Add Project" });
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
addProjectButton
|
|
45
|
+
.querySelector("svg")
|
|
46
|
+
?.classList.contains("lucide-folder-plus"),
|
|
47
|
+
).toBe(true);
|
|
48
|
+
fireEvent.click(addProjectButton);
|
|
49
|
+
|
|
50
|
+
expect(onAddProject).toHaveBeenCalledOnce();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -81,6 +81,9 @@ function expectCodexSelectedInSessionTypeMenu() {
|
|
|
81
81
|
screen.getByRole("button", { name: /Codex/i }).getAttribute("aria-pressed"),
|
|
82
82
|
).toBe("true");
|
|
83
83
|
}
|
|
84
|
+
function expectSessionCreated(sessionType: string, projectRoot?: string) {
|
|
85
|
+
expect(mocks.createSession).toHaveBeenCalledWith({ projectRoot, sessionType });
|
|
86
|
+
}
|
|
84
87
|
|
|
85
88
|
vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
|
|
86
89
|
usePresenter: () => ({
|
|
@@ -249,7 +252,7 @@ describe("ChatSidebar create and list basics", () => {
|
|
|
249
252
|
|
|
250
253
|
fireEvent.click(screen.getByRole("button", { name: "New Task" }));
|
|
251
254
|
|
|
252
|
-
|
|
255
|
+
expectSessionCreated("codex");
|
|
253
256
|
});
|
|
254
257
|
|
|
255
258
|
it("hydrates the desktop new-session type from stored preferences", async () => {
|
|
@@ -265,7 +268,7 @@ describe("ChatSidebar create and list basics", () => {
|
|
|
265
268
|
|
|
266
269
|
fireEvent.click(screen.getByRole("button", { name: "New Task" }));
|
|
267
270
|
|
|
268
|
-
|
|
271
|
+
expectSessionCreated("codex");
|
|
269
272
|
});
|
|
270
273
|
|
|
271
274
|
it("does not animate the desktop create task button width during runtime option hydration", () => {
|
|
@@ -311,7 +314,7 @@ describe("ChatSidebar create and list basics", () => {
|
|
|
311
314
|
it("renders the lightweight list mode switch in the session header row and toggles to project view", () => {
|
|
312
315
|
renderSidebar();
|
|
313
316
|
|
|
314
|
-
expect(screen.
|
|
317
|
+
expect(screen.queryByText("Sessions")).toBeNull();
|
|
315
318
|
fireEvent.click(screen.getByRole("button", { name: "Project" }));
|
|
316
319
|
|
|
317
320
|
expect(mocks.setListMode).toHaveBeenCalledWith("project-first");
|
|
@@ -332,7 +335,7 @@ describe("ChatSidebar create and list basics", () => {
|
|
|
332
335
|
fireEvent.click(screen.getByText("Codex"));
|
|
333
336
|
|
|
334
337
|
expect(mocks.setQuery).toHaveBeenCalledWith("release notes");
|
|
335
|
-
|
|
338
|
+
expectSessionCreated("codex");
|
|
336
339
|
expect(mocks.goToChatRoot).not.toHaveBeenCalled();
|
|
337
340
|
});
|
|
338
341
|
|
|
@@ -377,7 +380,7 @@ describe("ChatSidebar create and list basics", () => {
|
|
|
377
380
|
|
|
378
381
|
fireEvent.click(screen.getByRole("button", { name: "New Task" }));
|
|
379
382
|
|
|
380
|
-
|
|
383
|
+
expectSessionCreated("native");
|
|
381
384
|
expect(mocks.goToChatRoot).not.toHaveBeenCalled();
|
|
382
385
|
});
|
|
383
386
|
|
|
@@ -582,10 +585,7 @@ describe("ChatSidebar project-first mode", () => {
|
|
|
582
585
|
);
|
|
583
586
|
fireEvent.click(screen.getByText("Codex"));
|
|
584
587
|
|
|
585
|
-
|
|
586
|
-
"codex",
|
|
587
|
-
"/tmp/project-beta",
|
|
588
|
-
);
|
|
588
|
+
expectSessionCreated("codex", "/tmp/project-beta");
|
|
589
589
|
expect(mocks.goToSession).not.toHaveBeenCalled();
|
|
590
590
|
});
|
|
591
591
|
|
|
@@ -617,10 +617,7 @@ describe("ChatSidebar project-first mode", () => {
|
|
|
617
617
|
screen.getByRole("button", { name: "New Task · project-gamma" }),
|
|
618
618
|
);
|
|
619
619
|
|
|
620
|
-
|
|
621
|
-
"native",
|
|
622
|
-
"/tmp/project-gamma",
|
|
623
|
-
);
|
|
620
|
+
expectSessionCreated("native", "/tmp/project-gamma");
|
|
624
621
|
expect(mocks.goToSession).not.toHaveBeenCalled();
|
|
625
622
|
});
|
|
626
623
|
|
|
@@ -652,10 +649,7 @@ describe("ChatSidebar project-first mode", () => {
|
|
|
652
649
|
);
|
|
653
650
|
fireEvent.click(screen.getByText("Codex"));
|
|
654
651
|
|
|
655
|
-
|
|
656
|
-
"codex",
|
|
657
|
-
"/tmp/project-mobile",
|
|
658
|
-
);
|
|
652
|
+
expectSessionCreated("codex", "/tmp/project-mobile");
|
|
659
653
|
expect(mocks.goToChatRoot).not.toHaveBeenCalled();
|
|
660
654
|
});
|
|
661
655
|
});
|
|
@@ -12,29 +12,20 @@ export type ChatPageProps = {
|
|
|
12
12
|
view: MainPanelView;
|
|
13
13
|
};
|
|
14
14
|
type UseChatSessionSyncParams = {
|
|
15
|
-
view: MainPanelView;
|
|
16
15
|
routeSessionKey: string | null;
|
|
17
|
-
syncRouteSessionSelection: (
|
|
18
|
-
isChatView: boolean;
|
|
19
|
-
routeSessionKey: string | null;
|
|
20
|
-
}) => void;
|
|
16
|
+
syncRouteSessionSelection: (routeSessionKey: string | null) => void;
|
|
21
17
|
};
|
|
22
18
|
export function useChatSessionSync(params: UseChatSessionSyncParams): void {
|
|
23
19
|
const {
|
|
24
|
-
view,
|
|
25
20
|
routeSessionKey,
|
|
26
21
|
syncRouteSessionSelection,
|
|
27
22
|
} = params;
|
|
28
23
|
|
|
29
24
|
useLayoutEffect(() => {
|
|
30
|
-
syncRouteSessionSelection(
|
|
31
|
-
isChatView: view === "chat",
|
|
32
|
-
routeSessionKey,
|
|
33
|
-
});
|
|
25
|
+
syncRouteSessionSelection(routeSessionKey);
|
|
34
26
|
}, [
|
|
35
27
|
routeSessionKey,
|
|
36
28
|
syncRouteSessionSelection,
|
|
37
|
-
view,
|
|
38
29
|
]);
|
|
39
30
|
}
|
|
40
31
|
type ChatPageLayoutProps = {
|
|
@@ -12,6 +12,7 @@ import { t, type I18nLanguage } from "@/shared/lib/i18n";
|
|
|
12
12
|
import type { UiTheme } from "@/shared/lib/theme";
|
|
13
13
|
import { SidebarNavLinkItem } from "@/app/components/layout/sidebar-items";
|
|
14
14
|
import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
|
|
15
|
+
import { SCROLL_BOTTOM_EDGE_FADE_CLASS } from "@/shared/components/ui/scroll-area";
|
|
15
16
|
import {
|
|
16
17
|
AlarmClock,
|
|
17
18
|
Bot,
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
Inbox,
|
|
20
21
|
PanelLeftClose,
|
|
21
22
|
PanelLeftOpen,
|
|
22
|
-
|
|
23
|
+
FolderPlus,
|
|
23
24
|
} from "lucide-react";
|
|
24
25
|
import { useInboxUnreadCount } from "@/features/inbox";
|
|
25
26
|
import { ChatSidebarSessionList } from "@/features/chat/features/session/components/chat-sidebar-session-list";
|
|
@@ -32,7 +33,6 @@ import {
|
|
|
32
33
|
SIDEBAR_RAIL_PADDING_X_CLASS,
|
|
33
34
|
SIDEBAR_RAIL_STACK_CLASS,
|
|
34
35
|
SIDEBAR_RAIL_SURFACE_CLASS,
|
|
35
|
-
SIDEBAR_SCROLL_EDGE_FADE_CLASS,
|
|
36
36
|
} from "@/app/components/layout/sidebar-rail.styles";
|
|
37
37
|
|
|
38
38
|
const navItems = [
|
|
@@ -86,7 +86,7 @@ export function ChatSidebarDesktopHeader({
|
|
|
86
86
|
? "justify-center px-2 py-1.5"
|
|
87
87
|
: isWindowsHost
|
|
88
88
|
? "justify-end px-3 py-1.5"
|
|
89
|
-
: "gap-2 px-
|
|
89
|
+
: "gap-2 px-4 py-2",
|
|
90
90
|
isCollapsed && shouldReserveMacWindowControls ? "pt-8" : null,
|
|
91
91
|
)}
|
|
92
92
|
>
|
|
@@ -108,36 +108,29 @@ export function ChatSidebarDesktopNav({
|
|
|
108
108
|
}) {
|
|
109
109
|
const unreadCount = useInboxUnreadCount();
|
|
110
110
|
return (
|
|
111
|
-
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<div
|
|
135
|
-
className={cn(
|
|
136
|
-
"border-t border-border/70",
|
|
137
|
-
isCollapsed ? "mx-2 my-1.5" : "mx-4",
|
|
138
|
-
)}
|
|
139
|
-
/>
|
|
140
|
-
</>
|
|
111
|
+
<div className={cn("pb-1", isCollapsed ? "px-0" : "px-3")}>
|
|
112
|
+
<ul className={isCollapsed ? SIDEBAR_RAIL_STACK_CLASS : "space-y-0.5"}>
|
|
113
|
+
{navItems.map((item) => (
|
|
114
|
+
<li
|
|
115
|
+
key={item.target}
|
|
116
|
+
className={isCollapsed ? "flex justify-center" : undefined}
|
|
117
|
+
>
|
|
118
|
+
<SidebarNavLinkItem
|
|
119
|
+
to={item.target}
|
|
120
|
+
label={item.label()}
|
|
121
|
+
icon={item.icon}
|
|
122
|
+
density="compact"
|
|
123
|
+
collapsed={isCollapsed}
|
|
124
|
+
indicator={item.target === "/inbox" && unreadCount > 0}
|
|
125
|
+
trailing={item.target === "/inbox" && unreadCount > 0
|
|
126
|
+
? unreadCount > 99 ? "99+" : unreadCount
|
|
127
|
+
: undefined}
|
|
128
|
+
className={isCollapsed ? undefined : "rounded-lg px-2.5 py-1.5"}
|
|
129
|
+
/>
|
|
130
|
+
</li>
|
|
131
|
+
))}
|
|
132
|
+
</ul>
|
|
133
|
+
</div>
|
|
141
134
|
);
|
|
142
135
|
}
|
|
143
136
|
|
|
@@ -172,14 +165,11 @@ export function ChatSidebarSessionArea({
|
|
|
172
165
|
|
|
173
166
|
return (
|
|
174
167
|
<>
|
|
175
|
-
<div className="flex items-center justify-
|
|
176
|
-
<div className="
|
|
177
|
-
{t("chatSidebarTaskRecords")}
|
|
178
|
-
</div>
|
|
179
|
-
<div className="flex items-center gap-1">
|
|
168
|
+
<div className="flex items-center justify-end px-4 pb-1 pt-2">
|
|
169
|
+
<div className="flex h-7 items-center gap-1.5">
|
|
180
170
|
{isProjectFirstView ? (
|
|
181
171
|
<IconActionButton
|
|
182
|
-
icon={<
|
|
172
|
+
icon={<FolderPlus className="h-3.5 w-3.5" />}
|
|
183
173
|
label={t("chatProjectAdd")}
|
|
184
174
|
onClick={onAddProject}
|
|
185
175
|
/>
|
|
@@ -193,8 +183,8 @@ export function ChatSidebarSessionArea({
|
|
|
193
183
|
|
|
194
184
|
<div
|
|
195
185
|
className={cn(
|
|
196
|
-
"custom-scrollbar min-h-0 flex-1 overflow-y-auto px-3 pb-
|
|
197
|
-
|
|
186
|
+
"custom-scrollbar min-h-0 flex-1 overflow-y-auto px-3 pb-4 pt-1",
|
|
187
|
+
SCROLL_BOTTOM_EDGE_FADE_CLASS,
|
|
198
188
|
)}
|
|
199
189
|
>
|
|
200
190
|
<ChatSidebarSessionList
|
|
@@ -246,7 +236,7 @@ export function ChatSidebarDesktopFooter({
|
|
|
246
236
|
return (
|
|
247
237
|
<div
|
|
248
238
|
className={cn(
|
|
249
|
-
"py-
|
|
239
|
+
"py-2",
|
|
250
240
|
isCollapsed
|
|
251
241
|
? cn("flex justify-center", SIDEBAR_RAIL_PADDING_X_CLASS)
|
|
252
242
|
: "px-3",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Button
|
|
1
|
+
import { Button } from "@/shared/components/ui/button";
|
|
2
2
|
import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
|
|
3
3
|
import { Input } from "@/shared/components/ui/input";
|
|
4
4
|
import {
|
|
@@ -20,51 +20,6 @@ import {
|
|
|
20
20
|
} from "@/app/components/layout/sidebar-rail.styles";
|
|
21
21
|
|
|
22
22
|
type SessionTypeOption = ChatSessionTypeOption;
|
|
23
|
-
type NewSessionActionStyleVariant =
|
|
24
|
-
| "neutralSurface"
|
|
25
|
-
| "brandSoft"
|
|
26
|
-
| "brandTextSurface"
|
|
27
|
-
| "brandSolid";
|
|
28
|
-
|
|
29
|
-
const NEW_SESSION_ACTION_STYLE_VARIANT: NewSessionActionStyleVariant =
|
|
30
|
-
"brandSoft";
|
|
31
|
-
|
|
32
|
-
const NEW_SESSION_ACTION_STYLE_CLASSES: Record<
|
|
33
|
-
NewSessionActionStyleVariant,
|
|
34
|
-
{
|
|
35
|
-
leftVariant: ButtonProps["variant"];
|
|
36
|
-
leftClassName: string;
|
|
37
|
-
rightClassName: string;
|
|
38
|
-
}
|
|
39
|
-
> = {
|
|
40
|
-
neutralSurface: {
|
|
41
|
-
leftVariant: "ghost",
|
|
42
|
-
leftClassName:
|
|
43
|
-
"bg-card text-foreground shadow-none transition-[background-color,color,box-shadow] hover:bg-accent hover:text-accent-foreground hover:shadow-sm active:bg-accent",
|
|
44
|
-
rightClassName:
|
|
45
|
-
"bg-card text-muted-foreground shadow-none transition-[background-color,color,box-shadow] hover:bg-accent hover:text-accent-foreground hover:shadow-sm active:bg-accent",
|
|
46
|
-
},
|
|
47
|
-
brandSoft: {
|
|
48
|
-
leftVariant: "ghost",
|
|
49
|
-
leftClassName:
|
|
50
|
-
"bg-primary/10 text-primary shadow-none ring-1 ring-primary/10 transition-[background-color,color,box-shadow] hover:bg-primary/15 hover:text-primary-700 hover:shadow-sm active:bg-primary/20",
|
|
51
|
-
rightClassName:
|
|
52
|
-
"bg-primary/10 text-primary shadow-none ring-1 ring-primary/10 transition-[background-color,color,box-shadow] hover:bg-primary/15 hover:text-primary-700 hover:shadow-sm active:bg-primary/20",
|
|
53
|
-
},
|
|
54
|
-
brandTextSurface: {
|
|
55
|
-
leftVariant: "ghost",
|
|
56
|
-
leftClassName:
|
|
57
|
-
"bg-card text-primary shadow-none transition-[background-color,color,box-shadow] hover:bg-accent hover:text-primary-700 hover:shadow-sm active:bg-primary/15",
|
|
58
|
-
rightClassName:
|
|
59
|
-
"bg-card text-primary shadow-none transition-[background-color,color,box-shadow] hover:bg-accent hover:text-primary-700 hover:shadow-sm active:bg-primary/15",
|
|
60
|
-
},
|
|
61
|
-
brandSolid: {
|
|
62
|
-
leftVariant: "primary",
|
|
63
|
-
leftClassName: "",
|
|
64
|
-
rightClassName:
|
|
65
|
-
"bg-primary text-primary-foreground shadow-sm hover:bg-primary-600 hover:text-primary-foreground active:bg-primary-700",
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
23
|
|
|
69
24
|
type ChatSidebarToolbarProps = {
|
|
70
25
|
query: string;
|
|
@@ -117,8 +72,6 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
117
72
|
collapsed = false,
|
|
118
73
|
} = props;
|
|
119
74
|
const supportsSessionTypeSwitch = sessionTypeOptions.length > 1;
|
|
120
|
-
const actionStyle =
|
|
121
|
-
NEW_SESSION_ACTION_STYLE_CLASSES[NEW_SESSION_ACTION_STYLE_VARIANT];
|
|
122
75
|
|
|
123
76
|
if (collapsed) {
|
|
124
77
|
return (
|
|
@@ -183,21 +136,20 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
183
136
|
|
|
184
137
|
return (
|
|
185
138
|
<>
|
|
186
|
-
<div className="px-4 pb-
|
|
187
|
-
<div className="flex
|
|
139
|
+
<div className="px-4 pb-2">
|
|
140
|
+
<div className="flex overflow-hidden rounded-lg bg-primary/10 text-primary ring-1 ring-primary/10">
|
|
188
141
|
<Button
|
|
189
|
-
variant=
|
|
142
|
+
variant="ghost"
|
|
190
143
|
className={cn(
|
|
191
|
-
"min-w-0 rounded-
|
|
192
|
-
|
|
193
|
-
supportsSessionTypeSwitch ? "flex-1 rounded-r-md" : "w-full",
|
|
144
|
+
"min-w-0 rounded-none bg-transparent px-3 text-primary shadow-none hover:bg-primary/10 hover:text-primary-700 active:bg-primary/15",
|
|
145
|
+
supportsSessionTypeSwitch ? "flex-1" : "w-full",
|
|
194
146
|
)}
|
|
195
147
|
onClick={() => {
|
|
196
148
|
onCreateMenuOpenChange(false);
|
|
197
149
|
onCreateSession(selectedNewSessionType);
|
|
198
150
|
}}
|
|
199
151
|
>
|
|
200
|
-
<Plus className="mr-
|
|
152
|
+
<Plus className="mr-1.5 h-4 w-4" />
|
|
201
153
|
{t("chatSidebarNewTask")}
|
|
202
154
|
</Button>
|
|
203
155
|
{supportsSessionTypeSwitch ? (
|
|
@@ -208,19 +160,16 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
208
160
|
<PopoverTrigger asChild>
|
|
209
161
|
<IconActionButton
|
|
210
162
|
icon={
|
|
211
|
-
<span className="inline-flex items-center gap-
|
|
163
|
+
<span className="inline-flex items-center gap-1">
|
|
212
164
|
<SessionTypeTriggerIcon
|
|
213
165
|
option={selectedNewSessionTypeOption}
|
|
214
166
|
/>
|
|
215
|
-
<ChevronDown className="h-3 w-3 opacity-
|
|
167
|
+
<ChevronDown className="h-3 w-3 opacity-50" />
|
|
216
168
|
</span>
|
|
217
169
|
}
|
|
218
170
|
label={t("chatSessionTypeLabel")}
|
|
219
171
|
tooltip={t("chatSessionTypeLabel")}
|
|
220
|
-
className=
|
|
221
|
-
"h-9 w-12 shrink-0 rounded-xl rounded-l-md",
|
|
222
|
-
actionStyle.rightClassName,
|
|
223
|
-
)}
|
|
172
|
+
className="h-9 w-12 shrink-0 rounded-none border-l border-primary/10 bg-transparent text-primary shadow-none hover:bg-primary/10 hover:text-primary-700 active:bg-primary/15"
|
|
224
173
|
/>
|
|
225
174
|
</PopoverTrigger>
|
|
226
175
|
<ChatPopoverContent
|
|
@@ -241,14 +190,14 @@ export function ChatSidebarDesktopToolbar(props: ChatSidebarToolbarProps) {
|
|
|
241
190
|
</div>
|
|
242
191
|
</div>
|
|
243
192
|
|
|
244
|
-
<div className="px-4 pb-
|
|
193
|
+
<div className="px-4 pb-2">
|
|
245
194
|
<div className="relative">
|
|
246
|
-
<Search className="pointer-events-none absolute left-
|
|
195
|
+
<Search className="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/60" />
|
|
247
196
|
<Input
|
|
248
197
|
value={query}
|
|
249
198
|
onChange={(event) => onQueryChange(event.target.value)}
|
|
250
199
|
placeholder={t("chatSidebarSearchPlaceholder")}
|
|
251
|
-
className="h-
|
|
200
|
+
className="h-8 rounded-lg border-transparent bg-background/55 pl-8 text-xs shadow-none hover:bg-background/75"
|
|
252
201
|
/>
|
|
253
202
|
</div>
|
|
254
203
|
</div>
|
|
@@ -102,13 +102,10 @@ export function ChatSidebarUtilityMenu({
|
|
|
102
102
|
<button
|
|
103
103
|
type="button"
|
|
104
104
|
aria-label={t("settingsMenu")}
|
|
105
|
-
className="flex w-full items-center gap-2.5 rounded-
|
|
105
|
+
className="flex w-full items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-[13px] font-medium text-muted-foreground transition-colors duration-base hover:bg-background/70 hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
|
|
106
106
|
>
|
|
107
107
|
<Settings className="h-4 w-4 shrink-0 text-muted-foreground/70" />
|
|
108
108
|
<span className="min-w-0 flex-1 text-left">{t("settings")}</span>
|
|
109
|
-
<span className="max-w-[112px] truncate text-[13px] text-muted-foreground">
|
|
110
|
-
{currentThemeLabel} / {currentLanguageLabel}
|
|
111
|
-
</span>
|
|
112
109
|
</button>
|
|
113
110
|
</PopoverTrigger>
|
|
114
111
|
)}
|
|
@@ -389,7 +389,7 @@ describe("SessionConversationArea input boundary", () => {
|
|
|
389
389
|
|
|
390
390
|
expect(screen.getByTestId("conversation-bottom-slot")).toBeTruthy();
|
|
391
391
|
expect(screen.getByText(/出错了|Something went wrong/)).toBeTruthy();
|
|
392
|
-
expect(screen.getByText(
|
|
392
|
+
expect(screen.getByText(/Failed · Invalid API Key|失败 · Invalid API Key/)).toBeTruthy();
|
|
393
393
|
});
|
|
394
394
|
|
|
395
395
|
it("does not surface user-cancelled previews as conversation errors", () => {
|