@nextclaw/ui 0.12.13 → 0.12.14
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 +6 -0
- package/dist/assets/{chat-DCi1-y8U.js → chat-BxA-mw53.js} +2 -2
- package/dist/assets/{chat-page-CHKwiqPY.js → chat-page-DLFTPfmu.js} +1 -1
- package/dist/assets/{desktop-update-config-gnna2NaS.js → desktop-update-config-CzGi43xw.js} +1 -1
- package/dist/assets/{index-CxzW1dQ9.js → index-CAYF44Dz.js} +2 -2
- package/dist/assets/{runtime-config-page-BuWmH7fe.js → runtime-config-page-B-y_0HIS.js} +1 -1
- package/dist/assets/{sessions-config-page-DZrdd2zT.js → sessions-config-page-CZGqS32n.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +3 -3
- package/src/features/chat/components/chat-session-type-option-item.test.tsx +16 -0
- package/src/features/chat/components/chat-session-type-option-item.tsx +4 -1
- package/src/features/chat/components/conversation/chat-conversation-panel.test.tsx +18 -1
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +2 -1
- package/src/features/chat/components/layout/chat-sidebar.test.tsx +45 -1
- package/src/features/chat/components/layout/chat-sidebar.tsx +36 -45
|
@@ -11,7 +11,8 @@ import { useChatThreadStore } from "@/features/chat/stores/chat-thread.store";
|
|
|
11
11
|
const mocks = vi.hoisted(() => ({
|
|
12
12
|
deleteSession: vi.fn(),
|
|
13
13
|
goToProviders: vi.fn(),
|
|
14
|
-
createSession: vi.fn(),
|
|
14
|
+
createSession: vi.fn(() => "draft-session-2"),
|
|
15
|
+
goToSession: vi.fn(),
|
|
15
16
|
setSelectedAgentId: vi.fn(),
|
|
16
17
|
setPendingSessionType: vi.fn(),
|
|
17
18
|
stickyBottomScroll: vi.fn(() => ({
|
|
@@ -79,6 +80,9 @@ vi.mock("@/features/chat/components/chat-welcome", () => ({
|
|
|
79
80
|
|
|
80
81
|
vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
|
|
81
82
|
usePresenter: () => ({
|
|
83
|
+
chatUiManager: {
|
|
84
|
+
goToSession: mocks.goToSession,
|
|
85
|
+
},
|
|
82
86
|
chatThreadManager: {
|
|
83
87
|
deleteSession: mocks.deleteSession,
|
|
84
88
|
goToProviders: mocks.goToProviders,
|
|
@@ -158,6 +162,8 @@ describe("ChatConversationPanel", () => {
|
|
|
158
162
|
mocks.deleteSession.mockReset();
|
|
159
163
|
mocks.goToProviders.mockReset();
|
|
160
164
|
mocks.createSession.mockReset();
|
|
165
|
+
mocks.createSession.mockReturnValue("draft-session-2");
|
|
166
|
+
mocks.goToSession.mockReset();
|
|
161
167
|
mocks.setSelectedAgentId.mockReset();
|
|
162
168
|
mocks.setPendingSessionType.mockReset();
|
|
163
169
|
mocks.stickyBottomScroll.mockClear();
|
|
@@ -233,6 +239,17 @@ describe("ChatConversationPanel", () => {
|
|
|
233
239
|
expect(onBackToList).toHaveBeenCalledTimes(1);
|
|
234
240
|
});
|
|
235
241
|
|
|
242
|
+
it("opens the new draft session immediately when mobile welcome creates a session", async () => {
|
|
243
|
+
const user = userEvent.setup();
|
|
244
|
+
|
|
245
|
+
render(<ChatConversationPanel layoutMode="mobile" />);
|
|
246
|
+
|
|
247
|
+
await user.click(screen.getByRole("button", { name: "create draft session" }));
|
|
248
|
+
|
|
249
|
+
expect(mocks.createSession).toHaveBeenCalledWith("native");
|
|
250
|
+
expect(mocks.goToSession).toHaveBeenCalledWith("draft-session-2");
|
|
251
|
+
});
|
|
252
|
+
|
|
236
253
|
it("shows the selected session project badge and more actions trigger", () => {
|
|
237
254
|
useChatThreadStore.setState({
|
|
238
255
|
snapshot: {
|
|
@@ -272,7 +272,8 @@ export function ChatConversationPanel({
|
|
|
272
272
|
resolveDraftAgent(snapshot.agentId ?? "main"),
|
|
273
273
|
defaultSessionType,
|
|
274
274
|
);
|
|
275
|
-
presenter.chatSessionListManager.createSession(sessionType);
|
|
275
|
+
const sessionKey = presenter.chatSessionListManager.createSession(sessionType);
|
|
276
|
+
if (layoutMode === "mobile") presenter.chatUiManager.goToSession(sessionKey);
|
|
276
277
|
};
|
|
277
278
|
const selectDraftAgent = (agentId: string) => {
|
|
278
279
|
presenter.chatSessionListManager.setSelectedAgentId(agentId);
|
|
@@ -7,7 +7,8 @@ import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
|
|
|
7
7
|
import { useChatSessionListStore } from '@/features/chat/stores/chat-session-list.store';
|
|
8
8
|
|
|
9
9
|
const mocks = vi.hoisted(() => ({
|
|
10
|
-
createSession: vi.fn(),
|
|
10
|
+
createSession: vi.fn(() => 'draft-session-key'),
|
|
11
|
+
goToSession: vi.fn(),
|
|
11
12
|
setQuery: vi.fn(),
|
|
12
13
|
setListMode: vi.fn(),
|
|
13
14
|
selectSession: vi.fn(),
|
|
@@ -28,6 +29,9 @@ function createSessionItem(
|
|
|
28
29
|
|
|
29
30
|
vi.mock('@/features/chat/components/providers/chat-presenter.provider', () => ({
|
|
30
31
|
usePresenter: () => ({
|
|
32
|
+
chatUiManager: {
|
|
33
|
+
goToSession: mocks.goToSession,
|
|
34
|
+
},
|
|
31
35
|
chatSessionListManager: {
|
|
32
36
|
createSession: mocks.createSession,
|
|
33
37
|
setQuery: mocks.setQuery,
|
|
@@ -115,6 +119,8 @@ vi.mock('@/features/system-status', () => ({
|
|
|
115
119
|
|
|
116
120
|
function resetSidebarTestState() {
|
|
117
121
|
mocks.createSession.mockReset();
|
|
122
|
+
mocks.createSession.mockReturnValue('draft-session-key');
|
|
123
|
+
mocks.goToSession.mockReset();
|
|
118
124
|
mocks.setQuery.mockReset();
|
|
119
125
|
mocks.setListMode.mockReset();
|
|
120
126
|
mocks.selectSession.mockReset();
|
|
@@ -228,6 +234,7 @@ describe('ChatSidebar create and list basics', () => {
|
|
|
228
234
|
|
|
229
235
|
expect(mocks.setQuery).toHaveBeenCalledWith('release notes');
|
|
230
236
|
expect(mocks.createSession).toHaveBeenCalledWith('codex');
|
|
237
|
+
expect(mocks.goToSession).toHaveBeenCalledWith('draft-session-key');
|
|
231
238
|
});
|
|
232
239
|
|
|
233
240
|
it('creates the default session directly from the compact mobile add button when no menu is needed', () => {
|
|
@@ -248,6 +255,7 @@ describe('ChatSidebar create and list basics', () => {
|
|
|
248
255
|
fireEvent.click(screen.getByRole('button', { name: 'New Task' }));
|
|
249
256
|
|
|
250
257
|
expect(mocks.createSession).toHaveBeenCalledWith('native');
|
|
258
|
+
expect(mocks.goToSession).toHaveBeenCalledWith('draft-session-key');
|
|
251
259
|
});
|
|
252
260
|
|
|
253
261
|
it('shows a session type badge for non-native sessions in the list', () => {
|
|
@@ -408,6 +416,7 @@ describe('ChatSidebar project-first mode', () => {
|
|
|
408
416
|
fireEvent.click(screen.getByText('Codex'));
|
|
409
417
|
|
|
410
418
|
expect(mocks.createSession).toHaveBeenCalledWith('codex', '/tmp/project-beta');
|
|
419
|
+
expect(mocks.goToSession).not.toHaveBeenCalled();
|
|
411
420
|
});
|
|
412
421
|
|
|
413
422
|
it('creates immediately when there is only one available runtime type', () => {
|
|
@@ -447,6 +456,41 @@ describe('ChatSidebar project-first mode', () => {
|
|
|
447
456
|
fireEvent.click(screen.getByRole('button', { name: 'New Task · project-gamma' }));
|
|
448
457
|
|
|
449
458
|
expect(mocks.createSession).toHaveBeenCalledWith('native', '/tmp/project-gamma');
|
|
459
|
+
expect(mocks.goToSession).not.toHaveBeenCalled();
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('opens the draft detail after creating a project-bound session on mobile', () => {
|
|
463
|
+
useChatSessionListStore.setState({
|
|
464
|
+
snapshot: {
|
|
465
|
+
...useChatSessionListStore.getState().snapshot,
|
|
466
|
+
listMode: 'project-first'
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
mocks.sessionItems = [
|
|
470
|
+
createSessionItem({
|
|
471
|
+
key: 'session:project-mobile-1',
|
|
472
|
+
createdAt: '2026-03-19T09:00:00.000Z',
|
|
473
|
+
updatedAt: '2026-03-19T11:05:00.000Z',
|
|
474
|
+
label: 'Grouped Mobile Task',
|
|
475
|
+
projectRoot: '/tmp/project-mobile',
|
|
476
|
+
projectName: 'project-mobile',
|
|
477
|
+
sessionType: 'native',
|
|
478
|
+
sessionTypeMutable: false,
|
|
479
|
+
messageCount: 2
|
|
480
|
+
})
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
render(
|
|
484
|
+
<MemoryRouter>
|
|
485
|
+
<ChatSidebar variant="mobile" />
|
|
486
|
+
</MemoryRouter>
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
fireEvent.click(screen.getByRole('button', { name: 'New Task · project-mobile' }));
|
|
490
|
+
fireEvent.click(screen.getByText('Codex'));
|
|
491
|
+
|
|
492
|
+
expect(mocks.createSession).toHaveBeenCalledWith('codex', '/tmp/project-mobile');
|
|
493
|
+
expect(mocks.goToSession).toHaveBeenCalledWith('draft-session-key');
|
|
450
494
|
});
|
|
451
495
|
});
|
|
452
496
|
|
|
@@ -8,10 +8,7 @@ import { useChatSidebarSessionLabelEditor } from '@/features/chat/hooks/use-chat
|
|
|
8
8
|
import { useNcpSessionListView, type NcpSessionListItemView } from '@/features/chat/hooks/use-ncp-session-list-view';
|
|
9
9
|
import { usePresenter } from '@/features/chat/components/providers/chat-presenter.provider';
|
|
10
10
|
import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
|
|
11
|
-
import {
|
|
12
|
-
shouldShowUnreadSessionIndicator,
|
|
13
|
-
useChatSessionListStore
|
|
14
|
-
} from '@/features/chat/stores/chat-session-list.store';
|
|
11
|
+
import { useChatSessionListStore } from '@/features/chat/stores/chat-session-list.store';
|
|
15
12
|
import { useSystemStatus } from '@/features/system-status';
|
|
16
13
|
import { useAgents } from '@/shared/hooks/use-agents';
|
|
17
14
|
import { getSessionProjectName } from '@/shared/lib/session-project';
|
|
@@ -230,32 +227,33 @@ export function ChatSidebar({
|
|
|
230
227
|
setLanguage(nextLang);
|
|
231
228
|
window.location.reload();
|
|
232
229
|
};
|
|
233
|
-
const renderSessionItem = (item: NcpSessionListItemView) =>
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
230
|
+
const renderSessionItem = (item: NcpSessionListItemView) => (
|
|
231
|
+
<ChatSidebarSessionEntry
|
|
232
|
+
key={item.session.key}
|
|
233
|
+
item={item}
|
|
234
|
+
selectedSessionKey={listSnapshot.selectedSessionKey}
|
|
235
|
+
optimisticReadAtBySessionKey={optimisticReadAtBySessionKey}
|
|
236
|
+
agentsById={agentsById}
|
|
237
|
+
childSessionsByParentKey={childSessionsByParentKey}
|
|
238
|
+
editingSessionKey={editingSessionKey}
|
|
239
|
+
draftLabel={draftLabel}
|
|
240
|
+
savingSessionKey={savingSessionKey}
|
|
241
|
+
sessionTitle={sessionTitle}
|
|
242
|
+
onSelectSession={presenter.chatSessionListManager.selectSession}
|
|
243
|
+
onOpenChildSessions={(parentSessionKey, activeChildSessionKey) => presenter.chatThreadManager.openChildSessionPanel({ parentSessionKey, activeChildSessionKey })}
|
|
244
|
+
onStartEditingSessionLabel={startEditingSessionLabel}
|
|
245
|
+
onDraftLabelChange={setDraftLabel}
|
|
246
|
+
onSaveSessionLabel={saveSessionLabel}
|
|
247
|
+
onCancelEditingSessionLabel={cancelEditingSessionLabel}
|
|
248
|
+
/>
|
|
249
|
+
);
|
|
250
|
+
const createSessionAndOpenIfNeeded = (sessionType: string, projectRoot?: string | null) => {
|
|
251
|
+
const sessionKey = typeof projectRoot === "string"
|
|
252
|
+
? presenter.chatSessionListManager.createSession(sessionType, projectRoot)
|
|
253
|
+
: presenter.chatSessionListManager.createSession(sessionType);
|
|
254
|
+
if (isMobileVariant) presenter.chatUiManager.goToSession(sessionKey);
|
|
255
|
+
};
|
|
256
|
+
|
|
259
257
|
return (
|
|
260
258
|
<aside
|
|
261
259
|
className={cn(
|
|
@@ -282,7 +280,7 @@ export function ChatSidebar({
|
|
|
282
280
|
nonDefaultSessionTypeOptions={nonDefaultSessionTypeOptions}
|
|
283
281
|
isCreateMenuOpen={isCreateMenuOpen}
|
|
284
282
|
onCreateMenuOpenChange={setIsCreateMenuOpen}
|
|
285
|
-
onCreateSession={
|
|
283
|
+
onCreateSession={createSessionAndOpenIfNeeded}
|
|
286
284
|
onQueryChange={presenter.chatSessionListManager.setQuery}
|
|
287
285
|
/>
|
|
288
286
|
) : (
|
|
@@ -293,7 +291,7 @@ export function ChatSidebar({
|
|
|
293
291
|
nonDefaultSessionTypeOptions={nonDefaultSessionTypeOptions}
|
|
294
292
|
isCreateMenuOpen={isCreateMenuOpen}
|
|
295
293
|
onCreateMenuOpenChange={setIsCreateMenuOpen}
|
|
296
|
-
onCreateSession={
|
|
294
|
+
onCreateSession={createSessionAndOpenIfNeeded}
|
|
297
295
|
onQueryChange={presenter.chatSessionListManager.setQuery}
|
|
298
296
|
/>
|
|
299
297
|
)}
|
|
@@ -301,18 +299,11 @@ export function ChatSidebar({
|
|
|
301
299
|
{!isMobileVariant ? (
|
|
302
300
|
<div className="px-3 pb-2">
|
|
303
301
|
<ul className="space-y-0.5">
|
|
304
|
-
{navItems.map((item) =>
|
|
305
|
-
|
|
306
|
-
<
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
label={item.label()}
|
|
310
|
-
icon={item.icon}
|
|
311
|
-
density="compact"
|
|
312
|
-
/>
|
|
313
|
-
</li>
|
|
314
|
-
);
|
|
315
|
-
})}
|
|
302
|
+
{navItems.map((item) => (
|
|
303
|
+
<li key={item.target}>
|
|
304
|
+
<SidebarNavLinkItem to={item.target} label={item.label()} icon={item.icon} density="compact" />
|
|
305
|
+
</li>
|
|
306
|
+
))}
|
|
316
307
|
</ul>
|
|
317
308
|
</div>
|
|
318
309
|
) : null}
|
|
@@ -346,7 +337,7 @@ export function ChatSidebar({
|
|
|
346
337
|
defaultSessionType={defaultSessionType}
|
|
347
338
|
sessionTypeOptions={inputSnapshot.sessionTypeOptions}
|
|
348
339
|
renderSessionItem={renderSessionItem}
|
|
349
|
-
onCreateSession={
|
|
340
|
+
onCreateSession={createSessionAndOpenIfNeeded}
|
|
350
341
|
/>
|
|
351
342
|
)
|
|
352
343
|
) : groups.length === 0 ? (
|