@nextclaw/ui 0.14.1-beta.1 → 0.14.2
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 +61 -0
- package/dist/assets/{api-dQ6uQSZG.js → api-a14tR_z1.js} +7 -7
- package/dist/assets/channels-list-page-CFGkm4DT.js +8 -0
- package/dist/assets/chat-page-CEdavJXc.js +105 -0
- package/dist/assets/{config-split-page-CeFfbaqc.js → config-split-page-B5b9B0uE.js} +1 -1
- package/dist/assets/{confirm-dialog-C95Y4QPC.js → confirm-dialog-ClH_Ec7n.js} +1 -1
- package/dist/assets/{desktop-update-config-CcK6jiEb.js → desktop-update-config-DPNDnVpk.js} +1 -1
- package/dist/assets/{dist-DTGzeil6.js → dist-BtCS2Ucq.js} +1 -1
- package/dist/assets/{dist-fGogBmPA.js → dist-CLGGLm8b.js} +1 -1
- package/dist/assets/doc-browser-5nMWYKBD.js +1 -0
- package/dist/assets/doc-browser-BJRLhztv.js +1 -0
- package/dist/assets/doc-browser-context-D4JwMMxK.js +1 -0
- package/dist/assets/{ellipsis-D2F4KMiU.js → ellipsis-CtJXjIol.js} +1 -1
- package/dist/assets/{external-link-CcgtBK26.js → external-link-CdJiG13p.js} +1 -1
- package/dist/assets/index-BDN1zzMQ.js +105 -0
- package/dist/assets/index-g3uSZXoc.css +1 -0
- package/dist/assets/mcp-marketplace-page-BU0kIjhR.js +40 -0
- package/dist/assets/mcp-marketplace-page-sMNFSq_T.js +1 -0
- package/dist/assets/middleware-CkFT3R4v.js +1 -0
- package/dist/assets/model-config-page-D6hYXROd.js +1 -0
- package/dist/assets/plus-DPjb0piO.js +1 -0
- package/dist/assets/provider-scoped-model-input-DAX2HgmR.js +1 -0
- package/dist/assets/providers-config-page-BzAeaACA.js +1 -0
- package/dist/assets/{react-CTqdhFMk.js → react-C6cZgP44.js} +1 -1
- package/dist/assets/remote-B0G80uPr.js +1 -0
- package/dist/assets/rotate-cw-DnEB1Rju.js +1 -0
- package/dist/assets/runtime-config-page-C5xKaV3i.js +1 -0
- package/dist/assets/{save-C9fKWfVB.js → save-DKA0x4zk.js} +1 -1
- package/dist/assets/{search-D5FQ5q0X.js → search-BNlcoj5c.js} +1 -1
- package/dist/assets/{search-config-page-AMEJJNIS.js → search-config-page-Cq9uJ-N8.js} +1 -1
- package/dist/assets/{secrets-config-page-CcgJnvBO.js → secrets-config-page-CxSbZLBo.js} +2 -2
- package/dist/assets/{select-Cu5n0E-3.js → select-CvTlDZTQ.js} +1 -1
- package/dist/assets/{tag-chip-C5aOtjxu.js → tag-chip-8ffF2y2C.js} +1 -1
- package/dist/assets/use-config-DmT0o1gv.js +1 -0
- package/dist/assets/x-BiSSpgqB.js +1 -0
- package/dist/index.html +15 -14
- package/package.json +9 -9
- package/src/app/components/app-presenter-provider.tsx +11 -2
- package/src/app/components/layout/__tests__/app-layout.test.tsx +11 -2
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +110 -79
- package/src/app/components/layout/app-layout.tsx +18 -4
- package/src/app/components/layout/sidebar-items.tsx +194 -71
- package/src/app/components/layout/sidebar-rail.styles.ts +15 -0
- package/src/app/components/layout/sidebar.tsx +340 -83
- package/src/app/managers/viewport-layout.manager.ts +10 -0
- package/src/app/stores/viewport-layout.store.ts +39 -3
- package/src/features/agents/components/__tests__/agents-page.test.tsx +62 -39
- package/src/features/agents/components/agents-page.tsx +10 -6
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +145 -0
- package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +69 -762
- package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +132 -0
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +22 -15
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +14 -15
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +18 -18
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +10 -4
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +52 -11
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +77 -12
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +247 -0
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +182 -21
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +82 -25
- package/src/features/chat/components/layout/chat-sidebar.tsx +79 -93
- package/src/features/chat/components/providers/chat-presenter.provider.tsx +0 -6
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +289 -0
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +427 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +204 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-attachments.ts +66 -0
- package/src/features/chat/features/{input/hooks/use-chat-input-bar-query-state.ts → conversation/hooks/use-session-conversation-input-query.ts} +32 -17
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +281 -0
- package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +158 -0
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +56 -0
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +117 -0
- package/src/features/chat/features/input/hooks/use-chat-model-favorites.ts +2 -1
- package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +77 -0
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +10 -0
- package/src/features/chat/features/input/input-surface-plugins/input-surface-search.utils.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +108 -0
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +161 -0
- package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +22 -1
- package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +26 -13
- package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +7 -28
- package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +6 -0
- package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +26 -51
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +2 -0
- package/src/features/chat/features/input/utils/ncp-chat-input-availability.utils.ts +5 -6
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +47 -12
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +93 -12
- package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +31 -12
- package/src/features/chat/features/message/utils/chat-message-markdown-part.utils.ts +38 -0
- package/src/features/chat/features/message/utils/chat-message-part.utils.ts +1 -1
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +5 -4
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +2 -2
- package/src/features/chat/features/session/hooks/__tests__/use-chat-session-project.test.tsx +3 -30
- package/src/features/chat/features/session/hooks/use-chat-session-project.ts +0 -5
- package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +8 -0
- package/src/features/chat/features/session-type/components/__tests__/{chat-sidebar-create-menu.test.tsx → chat-session-type-menu.test.tsx} +5 -5
- package/src/features/chat/features/session-type/components/__tests__/chat-session-type-option-item.test.tsx +29 -1
- package/src/features/chat/features/session-type/components/{chat-sidebar-create-menu.tsx → chat-session-type-menu.tsx} +13 -9
- package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +17 -5
- package/src/features/chat/features/session-type/hooks/__tests__/use-chat-new-session-type-preference.test.tsx +152 -0
- package/src/features/chat/features/session-type/hooks/use-chat-new-session-type-preference.ts +159 -0
- package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +49 -31
- package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +2 -2
- package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +21 -10
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +2 -2
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +2 -2
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +54 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +44 -76
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +5 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +7 -1
- package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +40 -31
- package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +59 -1
- package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +109 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +44 -4
- package/src/features/chat/index.ts +0 -1
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +51 -99
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +135 -21
- package/src/features/chat/managers/chat-session-list.manager.ts +12 -43
- package/src/features/chat/managers/chat-thread.manager.ts +108 -34
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +48 -1
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +72 -0
- package/src/features/chat/pages/ncp-chat-page.tsx +0 -122
- package/src/features/chat/presenters/chat.presenter.ts +2 -19
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +31 -0
- package/src/features/chat/stores/chat-thread.store.ts +42 -26
- package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +24 -0
- package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +8 -3
- package/src/features/marketplace/components/marketplace-item-list-view.tsx +2 -2
- package/src/features/marketplace/components/marketplace-list-card.tsx +124 -98
- package/src/features/marketplace/components/marketplace-page-parts.tsx +11 -6
- package/src/features/panel-apps/hooks/use-panel-apps.ts +2 -1
- package/src/platforms/desktop/components/__tests__/desktop-app-shell.test.tsx +50 -10
- package/src/platforms/desktop/components/desktop-app-shell.tsx +34 -10
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +41 -16
- package/src/shared/components/common/__tests__/agent-avatar.test.tsx +3 -1
- package/src/shared/components/common/agent-avatar.tsx +5 -2
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +24 -1
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +8 -4
- package/src/shared/lib/api/index.ts +1 -0
- package/src/shared/lib/api/preferences/preference-keys.config.ts +9 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +20 -2
- package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
- package/src/shared/lib/i18n/locales/en-US/marketplace.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -2
- package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +2 -0
- package/src/shared/lib/ui-document-title/__tests__/ui-document-title.test.ts +49 -0
- package/src/shared/lib/ui-document-title/index.ts +50 -21
- package/dist/assets/channels-list-page-DfpjB8XB.js +0 -8
- package/dist/assets/chat-page-D3QaEqjs.js +0 -105
- package/dist/assets/doc-browser-CuBTcBEZ.js +0 -1
- package/dist/assets/doc-browser-DVHhEb96.js +0 -1
- package/dist/assets/doc-browser-context-BfMXwnrz.js +0 -1
- package/dist/assets/index-ByJCbQqr.css +0 -1
- package/dist/assets/index-DjXqiloY.js +0 -103
- package/dist/assets/mcp-marketplace-page-98-V__3u.js +0 -1
- package/dist/assets/mcp-marketplace-page-BNq_PNmO.js +0 -40
- package/dist/assets/model-config-page-aAUkZkSX.js +0 -1
- package/dist/assets/plus-1Rxl5FlV.js +0 -1
- package/dist/assets/provider-scoped-model-input-we4_Z4gP.js +0 -1
- package/dist/assets/providers-config-page-CtMkPSyu.js +0 -1
- package/dist/assets/remote-fQ7N_r8u.js +0 -1
- package/dist/assets/rotate-cw-CzfIeAJj.js +0 -1
- package/dist/assets/runtime-config-page-TD1UrTbG.js +0 -1
- package/dist/assets/use-config-D8TESeo6.js +0 -1
- package/dist/assets/x-1qETdQ70.js +0 -1
- package/src/features/chat/components/conversation/chat-conversation-alerts.tsx +0 -37
- package/src/features/chat/features/input/components/__tests__/chat-attachment-upload-limit.test.ts +0 -38
- package/src/features/chat/features/input/components/chat-input-bar.container.tsx +0 -496
- package/src/features/chat/features/message/utils/chat-message-inline-content.utils.ts +0 -95
- package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +0 -448
- package/src/features/chat/managers/__tests__/chat-run-snapshot.manager.test.ts +0 -129
- package/src/features/chat/managers/__tests__/chat-run.manager.test.ts +0 -236
- package/src/features/chat/managers/__tests__/chat-session-preference-sync.manager.test.ts +0 -219
- package/src/features/chat/managers/chat-input.manager.ts +0 -430
- package/src/features/chat/managers/chat-run.manager.ts +0 -110
- package/src/features/chat/managers/chat-session-preference-sync.manager.ts +0 -142
- package/src/features/chat/stores/chat-input.store.ts +0 -104
- /package/dist/assets/{config-hints-CTqBnCDp.js → config-hints-BkYBc7z0.js} +0 -0
package/src/features/chat/features/session/hooks/__tests__/use-chat-session-project.test.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { act, renderHook } from '@testing-library/react';
|
|
2
|
-
import { afterEach,
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
import { toast } from 'sonner';
|
|
4
|
-
import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
|
|
5
4
|
import { useChatSessionProject } from '@/features/chat/features/session/hooks/use-chat-session-project';
|
|
6
5
|
|
|
7
6
|
const mocks = vi.hoisted(() => ({
|
|
@@ -19,21 +18,11 @@ vi.mock('@/features/chat/features/session/hooks/use-chat-session-update', () =>
|
|
|
19
18
|
}));
|
|
20
19
|
|
|
21
20
|
describe('useChatSessionProject', () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
useChatInputStore.setState((state) => ({
|
|
24
|
-
snapshot: {
|
|
25
|
-
...state.snapshot,
|
|
26
|
-
pendingProjectRoot: null,
|
|
27
|
-
pendingProjectRootSessionKey: null
|
|
28
|
-
}
|
|
29
|
-
}));
|
|
30
|
-
});
|
|
31
|
-
|
|
32
21
|
afterEach(() => {
|
|
33
22
|
vi.clearAllMocks();
|
|
34
23
|
});
|
|
35
24
|
|
|
36
|
-
it('
|
|
25
|
+
it('does not persist draft project root through the session update hook', async () => {
|
|
37
26
|
const { result } = renderHook(() => useChatSessionProject());
|
|
38
27
|
|
|
39
28
|
await act(async () => {
|
|
@@ -45,14 +34,10 @@ describe('useChatSessionProject', () => {
|
|
|
45
34
|
});
|
|
46
35
|
|
|
47
36
|
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
48
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
49
|
-
pendingProjectRoot: '/tmp/project-alpha',
|
|
50
|
-
pendingProjectRootSessionKey: 'draft-session-1'
|
|
51
|
-
});
|
|
52
37
|
expect(toast.success).toHaveBeenCalledTimes(1);
|
|
53
38
|
});
|
|
54
39
|
|
|
55
|
-
it('
|
|
40
|
+
it('does not persist draft project clearing through the session update hook', async () => {
|
|
56
41
|
const { result } = renderHook(() => useChatSessionProject());
|
|
57
42
|
|
|
58
43
|
await act(async () => {
|
|
@@ -64,10 +49,6 @@ describe('useChatSessionProject', () => {
|
|
|
64
49
|
});
|
|
65
50
|
|
|
66
51
|
expect(mocks.updateSession).not.toHaveBeenCalled();
|
|
67
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
68
|
-
pendingProjectRoot: null,
|
|
69
|
-
pendingProjectRootSessionKey: 'draft-session-1'
|
|
70
|
-
});
|
|
71
52
|
expect(toast.success).toHaveBeenCalledTimes(1);
|
|
72
53
|
});
|
|
73
54
|
|
|
@@ -87,10 +68,6 @@ describe('useChatSessionProject', () => {
|
|
|
87
68
|
patch: { projectRoot: '/tmp/project-beta' },
|
|
88
69
|
successMessage: 'Project directory updated'
|
|
89
70
|
});
|
|
90
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
91
|
-
pendingProjectRoot: null,
|
|
92
|
-
pendingProjectRootSessionKey: null
|
|
93
|
-
});
|
|
94
71
|
});
|
|
95
72
|
|
|
96
73
|
it('persists clearing to the server without keeping a session-scoped local override', async () => {
|
|
@@ -109,9 +86,5 @@ describe('useChatSessionProject', () => {
|
|
|
109
86
|
patch: { projectRoot: null },
|
|
110
87
|
successMessage: 'Project directory cleared'
|
|
111
88
|
});
|
|
112
|
-
expect(useChatInputStore.getState().snapshot).toMatchObject({
|
|
113
|
-
pendingProjectRoot: null,
|
|
114
|
-
pendingProjectRootSessionKey: null
|
|
115
|
-
});
|
|
116
89
|
});
|
|
117
90
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { toast } from 'sonner';
|
|
2
2
|
import { t } from '@/shared/lib/i18n';
|
|
3
|
-
import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
|
|
4
3
|
import { useChatSessionUpdate } from '@/features/chat/features/session/hooks/use-chat-session-update';
|
|
5
4
|
|
|
6
5
|
type UpdateChatSessionProjectParams = {
|
|
@@ -17,10 +16,6 @@ export function useChatSessionProject() {
|
|
|
17
16
|
const successMessage = projectRoot ? t('chatSessionProjectUpdated') : t('chatSessionProjectCleared');
|
|
18
17
|
|
|
19
18
|
if (!persistToServer) {
|
|
20
|
-
useChatInputStore.getState().setSnapshot({
|
|
21
|
-
pendingProjectRoot: projectRoot,
|
|
22
|
-
pendingProjectRootSessionKey: sessionKey
|
|
23
|
-
});
|
|
24
19
|
toast.success(successMessage);
|
|
25
20
|
return;
|
|
26
21
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CHAT_SESSION_MATERIALIZATION_METADATA_KEY,
|
|
3
|
+
type AgentRunSessionMaterializationMetadata,
|
|
4
|
+
} from "@nextclaw/shared";
|
|
1
5
|
import { buildInlineSkillTokensFromComposer, CHAT_UI_INLINE_TOKENS_METADATA_KEY } from "@/features/chat/features/input/utils/chat-inline-token.utils";
|
|
2
6
|
import { normalizeRequestedSkills } from "@/features/chat/features/runtime/utils/chat-runtime.utils";
|
|
3
7
|
import { normalizeSessionProjectRootValue } from "@/shared/lib/session-project";
|
|
@@ -17,6 +21,7 @@ export function buildChatRunMetadata(payload: {
|
|
|
17
21
|
projectRoot?: string | null;
|
|
18
22
|
requestedSkills?: string[];
|
|
19
23
|
composerNodes?: Parameters<typeof buildInlineSkillTokensFromComposer>[0];
|
|
24
|
+
sessionMaterialization?: AgentRunSessionMaterializationMetadata | null;
|
|
20
25
|
}): Record<string, unknown> {
|
|
21
26
|
const projectRoot = normalizeSessionProjectRootValue(payload.projectRoot);
|
|
22
27
|
const metadata: Record<string, unknown> = {
|
|
@@ -36,6 +41,9 @@ export function buildChatRunMetadata(payload: {
|
|
|
36
41
|
if (inlineSkillTokens.length > 0) {
|
|
37
42
|
metadata[CHAT_UI_INLINE_TOKENS_METADATA_KEY] = inlineSkillTokens;
|
|
38
43
|
}
|
|
44
|
+
if (payload.sessionMaterialization) {
|
|
45
|
+
metadata[CHAT_SESSION_MATERIALIZATION_METADATA_KEY] = payload.sessionMaterialization;
|
|
46
|
+
}
|
|
39
47
|
return metadata;
|
|
40
48
|
}
|
|
41
49
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { render, screen } from "@testing-library/react";
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import { ChatSessionTypeMenu } from "@/features/chat/features/session-type/components/chat-session-type-menu";
|
|
4
4
|
import { createPopoverAvailableHeightLimit } from "@/shared/components/ui/popover";
|
|
5
5
|
|
|
6
6
|
const options = [
|
|
7
|
-
{ value: "native", label: "Native", ready: true },
|
|
8
|
-
{ value: "codex", label: "Codex", ready: true },
|
|
7
|
+
{ value: "native", label: "Native", icon: null, ready: true },
|
|
8
|
+
{ value: "codex", label: "Codex", icon: null, ready: true },
|
|
9
9
|
];
|
|
10
10
|
|
|
11
|
-
describe("
|
|
11
|
+
describe("ChatSessionTypeMenu", () => {
|
|
12
12
|
it("keeps the session type menu height bounded", () => {
|
|
13
|
-
render(<
|
|
13
|
+
render(<ChatSessionTypeMenu options={options} onSelect={vi.fn()} />);
|
|
14
14
|
|
|
15
15
|
const createMenu = screen.getByText("Session Type").parentElement;
|
|
16
16
|
expect(createMenu?.style.maxHeight).toBe(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
1
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { ChatSessionTypeOptionItem } from "@/features/chat/features/session-type/components/chat-session-type-option-item";
|
|
4
4
|
|
|
@@ -50,6 +50,7 @@ describe("ChatSessionTypeOptionItem", () => {
|
|
|
50
50
|
option={{
|
|
51
51
|
value: "native",
|
|
52
52
|
label: "Native",
|
|
53
|
+
icon: null,
|
|
53
54
|
ready: true,
|
|
54
55
|
}}
|
|
55
56
|
onSelect={vi.fn()}
|
|
@@ -59,4 +60,31 @@ describe("ChatSessionTypeOptionItem", () => {
|
|
|
59
60
|
expect(screen.getByRole("button", { name: /native/i })).toBeTruthy();
|
|
60
61
|
expect(screen.getByText("Native")).toBeTruthy();
|
|
61
62
|
});
|
|
63
|
+
|
|
64
|
+
it("marks selected options and does not call select when disabled", () => {
|
|
65
|
+
const onSelect = vi.fn();
|
|
66
|
+
|
|
67
|
+
render(
|
|
68
|
+
<ChatSessionTypeOptionItem
|
|
69
|
+
disabled
|
|
70
|
+
selected
|
|
71
|
+
option={{
|
|
72
|
+
value: "claude",
|
|
73
|
+
label: "Claude",
|
|
74
|
+
icon: null,
|
|
75
|
+
ready: false,
|
|
76
|
+
reasonMessage: "Configure a provider API key first.",
|
|
77
|
+
}}
|
|
78
|
+
onSelect={onSelect}
|
|
79
|
+
/>,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const optionButton = screen.getByRole("button", { name: /claude/i });
|
|
83
|
+
expect(optionButton.getAttribute("aria-pressed")).toBe("true");
|
|
84
|
+
expect((optionButton as HTMLButtonElement).disabled).toBe(true);
|
|
85
|
+
|
|
86
|
+
fireEvent.click(optionButton);
|
|
87
|
+
|
|
88
|
+
expect(onSelect).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
62
90
|
});
|
|
@@ -1,35 +1,37 @@
|
|
|
1
1
|
import { ChatSessionTypeOptionItem } from "@/features/chat/features/session-type/components/chat-session-type-option-item";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
3
3
|
import { createPopoverAvailableHeightLimit } from "@/shared/components/ui/popover";
|
|
4
4
|
import { t } from "@/shared/lib/i18n";
|
|
5
5
|
import { cn } from "@/shared/lib/utils";
|
|
6
6
|
|
|
7
|
-
type SessionTypeOption =
|
|
8
|
-
const
|
|
7
|
+
type SessionTypeOption = ChatSessionTypeOption;
|
|
8
|
+
const CHAT_SESSION_TYPE_MENU_MAX_HEIGHT =
|
|
9
9
|
createPopoverAvailableHeightLimit("18rem");
|
|
10
|
-
const
|
|
11
|
-
maxHeight:
|
|
10
|
+
const CHAT_SESSION_TYPE_MENU_STYLE = {
|
|
11
|
+
maxHeight: CHAT_SESSION_TYPE_MENU_MAX_HEIGHT,
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
type
|
|
14
|
+
type ChatSessionTypeMenuProps = {
|
|
15
15
|
options: readonly SessionTypeOption[];
|
|
16
16
|
onSelect: (sessionType: string) => void;
|
|
17
|
+
selectedSessionType?: string;
|
|
17
18
|
title?: string;
|
|
18
19
|
className?: string;
|
|
19
20
|
titleClassName?: string;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
export function
|
|
23
|
+
export function ChatSessionTypeMenu({
|
|
23
24
|
className,
|
|
24
25
|
onSelect,
|
|
25
26
|
options,
|
|
27
|
+
selectedSessionType,
|
|
26
28
|
title = t("chatSessionTypeLabel"),
|
|
27
29
|
titleClassName,
|
|
28
|
-
}:
|
|
30
|
+
}: ChatSessionTypeMenuProps) {
|
|
29
31
|
return (
|
|
30
32
|
<div
|
|
31
33
|
className={cn("space-y-1 overflow-y-auto overscroll-contain", className)}
|
|
32
|
-
style={
|
|
34
|
+
style={CHAT_SESSION_TYPE_MENU_STYLE}
|
|
33
35
|
>
|
|
34
36
|
<div
|
|
35
37
|
className={cn(
|
|
@@ -42,7 +44,9 @@ export function ChatSidebarCreateMenu({
|
|
|
42
44
|
{options.map((option) => (
|
|
43
45
|
<ChatSessionTypeOptionItem
|
|
44
46
|
key={option.value}
|
|
47
|
+
disabled={option.ready === false}
|
|
45
48
|
option={option}
|
|
49
|
+
selected={option.value === selectedSessionType}
|
|
46
50
|
onSelect={() => onSelect(option.value)}
|
|
47
51
|
/>
|
|
48
52
|
))}
|
package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import type { ChatInputSnapshot } from "@/features/chat/stores/chat-input.store";
|
|
2
1
|
import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
|
|
2
|
+
import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
3
3
|
import { StatusDot } from "@/shared/components/status/status-dot";
|
|
4
4
|
import { t } from "@/shared/lib/i18n";
|
|
5
|
-
import {
|
|
5
|
+
import { cn } from "@/shared/lib/utils";
|
|
6
|
+
import { Bot, Check } from "lucide-react";
|
|
6
7
|
|
|
7
|
-
type SessionTypeOption =
|
|
8
|
+
type SessionTypeOption = ChatSessionTypeOption;
|
|
8
9
|
|
|
9
10
|
export function ChatSessionTypeOptionItem(props: {
|
|
10
11
|
option: SessionTypeOption;
|
|
12
|
+
selected?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
11
14
|
onSelect: () => void;
|
|
12
15
|
}) {
|
|
13
|
-
const { option, onSelect } = props;
|
|
16
|
+
const { disabled = false, option, onSelect, selected = false } = props;
|
|
14
17
|
const helperText =
|
|
15
18
|
option.ready === false
|
|
16
19
|
? option.reasonMessage?.trim() || t("statusSetup")
|
|
@@ -19,8 +22,14 @@ export function ChatSessionTypeOptionItem(props: {
|
|
|
19
22
|
return (
|
|
20
23
|
<button
|
|
21
24
|
type="button"
|
|
25
|
+
aria-pressed={selected}
|
|
26
|
+
disabled={disabled}
|
|
22
27
|
onClick={onSelect}
|
|
23
|
-
className=
|
|
28
|
+
className={cn(
|
|
29
|
+
"w-full rounded-2xl px-3 py-2.5 text-left transition-colors",
|
|
30
|
+
selected ? "bg-gray-50" : "hover:bg-gray-50",
|
|
31
|
+
disabled ? "cursor-not-allowed opacity-70" : null,
|
|
32
|
+
)}
|
|
24
33
|
>
|
|
25
34
|
<div className="flex items-start gap-3">
|
|
26
35
|
<div className="flex min-w-0 flex-1 items-start gap-2.5">
|
|
@@ -56,6 +65,9 @@ export function ChatSessionTypeOptionItem(props: {
|
|
|
56
65
|
) : null}
|
|
57
66
|
</div>
|
|
58
67
|
</div>
|
|
68
|
+
{selected ? (
|
|
69
|
+
<Check className="mt-1 h-3.5 w-3.5 shrink-0 text-primary" />
|
|
70
|
+
) : null}
|
|
59
71
|
</div>
|
|
60
72
|
</button>
|
|
61
73
|
);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { PREFERENCE_KEYS } from "@/shared/lib/api";
|
|
6
|
+
import { useChatNewSessionTypePreference } from "@/features/chat/features/session-type/hooks/use-chat-new-session-type-preference";
|
|
7
|
+
import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
8
|
+
|
|
9
|
+
const mocks = vi.hoisted(() => ({
|
|
10
|
+
fetchPreference: vi.fn(),
|
|
11
|
+
updatePreference: vi.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
vi.mock("@/shared/lib/api", async (importOriginal) => {
|
|
15
|
+
const actual = await importOriginal();
|
|
16
|
+
return {
|
|
17
|
+
...(actual as Record<string, unknown>),
|
|
18
|
+
fetchPreference: mocks.fetchPreference,
|
|
19
|
+
updatePreference: mocks.updatePreference,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const sessionTypeOptions: ChatSessionTypeOption[] = [
|
|
24
|
+
{
|
|
25
|
+
value: "native",
|
|
26
|
+
label: "Native",
|
|
27
|
+
icon: null,
|
|
28
|
+
ready: true,
|
|
29
|
+
reason: null,
|
|
30
|
+
reasonMessage: null,
|
|
31
|
+
supportedModels: undefined,
|
|
32
|
+
recommendedModel: null,
|
|
33
|
+
modelSelectionMode: "nextclaw",
|
|
34
|
+
runtimeDefaultThinking: null,
|
|
35
|
+
cta: null,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
value: "codex",
|
|
39
|
+
label: "Codex",
|
|
40
|
+
icon: null,
|
|
41
|
+
ready: true,
|
|
42
|
+
reason: null,
|
|
43
|
+
reasonMessage: null,
|
|
44
|
+
supportedModels: undefined,
|
|
45
|
+
recommendedModel: null,
|
|
46
|
+
modelSelectionMode: "runtime-default",
|
|
47
|
+
runtimeDefaultThinking: null,
|
|
48
|
+
cta: null,
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function createWrapper() {
|
|
53
|
+
const queryClient = new QueryClient({
|
|
54
|
+
defaultOptions: {
|
|
55
|
+
queries: { retry: false },
|
|
56
|
+
mutations: { retry: false },
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
return function Wrapper({ children }: { children: ReactNode }) {
|
|
60
|
+
return (
|
|
61
|
+
<QueryClientProvider client={queryClient}>
|
|
62
|
+
{children}
|
|
63
|
+
</QueryClientProvider>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe("useChatNewSessionTypePreference", () => {
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
mocks.fetchPreference.mockReset();
|
|
71
|
+
mocks.updatePreference.mockReset();
|
|
72
|
+
mocks.fetchPreference.mockResolvedValue({
|
|
73
|
+
key: PREFERENCE_KEYS.chat.newSessionType,
|
|
74
|
+
value: null,
|
|
75
|
+
});
|
|
76
|
+
mocks.updatePreference.mockImplementation(
|
|
77
|
+
async (key: string, value: string) => ({
|
|
78
|
+
key,
|
|
79
|
+
value,
|
|
80
|
+
updatedAt: "2026-06-17T00:00:00.000Z",
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("uses the stored new-session type when it is selectable", async () => {
|
|
86
|
+
mocks.fetchPreference.mockResolvedValue({
|
|
87
|
+
key: PREFERENCE_KEYS.chat.newSessionType,
|
|
88
|
+
value: "codex",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const { result } = renderHook(
|
|
92
|
+
() =>
|
|
93
|
+
useChatNewSessionTypePreference({
|
|
94
|
+
defaultSessionType: "native",
|
|
95
|
+
sessionTypeOptions,
|
|
96
|
+
}),
|
|
97
|
+
{ wrapper: createWrapper() },
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
await waitFor(() => {
|
|
101
|
+
expect(result.current.selectedSessionType).toBe("codex");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("falls back to the default type when the stored type is not selectable", async () => {
|
|
106
|
+
mocks.fetchPreference.mockResolvedValue({
|
|
107
|
+
key: PREFERENCE_KEYS.chat.newSessionType,
|
|
108
|
+
value: "claude",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const { result } = renderHook(
|
|
112
|
+
() =>
|
|
113
|
+
useChatNewSessionTypePreference({
|
|
114
|
+
defaultSessionType: "native",
|
|
115
|
+
sessionTypeOptions,
|
|
116
|
+
}),
|
|
117
|
+
{ wrapper: createWrapper() },
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
await waitFor(() => {
|
|
121
|
+
expect(result.current.selectedSessionType).toBe("native");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("persists only selectable session types", async () => {
|
|
126
|
+
const { result } = renderHook(
|
|
127
|
+
() =>
|
|
128
|
+
useChatNewSessionTypePreference({
|
|
129
|
+
defaultSessionType: "native",
|
|
130
|
+
sessionTypeOptions,
|
|
131
|
+
}),
|
|
132
|
+
{ wrapper: createWrapper() },
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
act(() => {
|
|
136
|
+
result.current.setSelectedSessionType("codex");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await waitFor(() => {
|
|
140
|
+
expect(mocks.updatePreference).toHaveBeenCalledWith(
|
|
141
|
+
PREFERENCE_KEYS.chat.newSessionType,
|
|
142
|
+
"codex",
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
act(() => {
|
|
147
|
+
result.current.setSelectedSessionType("claude");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(mocks.updatePreference).toHaveBeenCalledTimes(1);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import {
|
|
4
|
+
fetchPreference,
|
|
5
|
+
PREFERENCE_KEYS,
|
|
6
|
+
updatePreference,
|
|
7
|
+
type PreferenceEntryView,
|
|
8
|
+
} from "@/shared/lib/api";
|
|
9
|
+
import {
|
|
10
|
+
normalizeSessionType,
|
|
11
|
+
type ChatSessionTypeOption,
|
|
12
|
+
} from "@/features/chat/features/session-type/utils/chat-session-type.utils";
|
|
13
|
+
|
|
14
|
+
const CHAT_NEW_SESSION_TYPE_PREFERENCE_KEY =
|
|
15
|
+
PREFERENCE_KEYS.chat.newSessionType;
|
|
16
|
+
const chatNewSessionTypePreferenceQueryKey = [
|
|
17
|
+
"preference",
|
|
18
|
+
CHAT_NEW_SESSION_TYPE_PREFERENCE_KEY,
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
type UseChatNewSessionTypePreferenceParams = {
|
|
22
|
+
defaultSessionType: string;
|
|
23
|
+
sessionTypeOptions: readonly ChatSessionTypeOption[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function findSelectableSessionTypeOption(
|
|
27
|
+
sessionType: string,
|
|
28
|
+
sessionTypeOptions: readonly ChatSessionTypeOption[],
|
|
29
|
+
): ChatSessionTypeOption | null {
|
|
30
|
+
const normalizedSessionType = normalizeSessionType(sessionType);
|
|
31
|
+
return (
|
|
32
|
+
sessionTypeOptions.find(
|
|
33
|
+
(option) =>
|
|
34
|
+
option.value === normalizedSessionType && option.ready !== false,
|
|
35
|
+
) ?? null
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function resolveNewSessionTypeOption(params: {
|
|
40
|
+
preferredSessionType: string | null;
|
|
41
|
+
defaultSessionType: string;
|
|
42
|
+
sessionTypeOptions: readonly ChatSessionTypeOption[];
|
|
43
|
+
}): ChatSessionTypeOption {
|
|
44
|
+
const { defaultSessionType, preferredSessionType, sessionTypeOptions } =
|
|
45
|
+
params;
|
|
46
|
+
const preferredOption = preferredSessionType
|
|
47
|
+
? findSelectableSessionTypeOption(preferredSessionType, sessionTypeOptions)
|
|
48
|
+
: null;
|
|
49
|
+
const defaultOption = findSelectableSessionTypeOption(
|
|
50
|
+
defaultSessionType,
|
|
51
|
+
sessionTypeOptions,
|
|
52
|
+
);
|
|
53
|
+
return (
|
|
54
|
+
preferredOption ??
|
|
55
|
+
defaultOption ??
|
|
56
|
+
sessionTypeOptions.find((option) => option.ready !== false) ??
|
|
57
|
+
sessionTypeOptions[0] ?? {
|
|
58
|
+
value: normalizeSessionType(defaultSessionType),
|
|
59
|
+
label: normalizeSessionType(defaultSessionType),
|
|
60
|
+
icon: null,
|
|
61
|
+
ready: true,
|
|
62
|
+
reason: null,
|
|
63
|
+
reasonMessage: null,
|
|
64
|
+
supportedModels: undefined,
|
|
65
|
+
recommendedModel: null,
|
|
66
|
+
modelSelectionMode: "nextclaw",
|
|
67
|
+
runtimeDefaultThinking: null,
|
|
68
|
+
cta: null,
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function readPreferredSessionType(entry: PreferenceEntryView | undefined) {
|
|
74
|
+
return typeof entry?.value === "string"
|
|
75
|
+
? normalizeSessionType(entry.value)
|
|
76
|
+
: null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useChatNewSessionTypePreference({
|
|
80
|
+
defaultSessionType,
|
|
81
|
+
sessionTypeOptions,
|
|
82
|
+
}: UseChatNewSessionTypePreferenceParams) {
|
|
83
|
+
const queryClient = useQueryClient();
|
|
84
|
+
const preferenceQuery = useQuery({
|
|
85
|
+
queryKey: chatNewSessionTypePreferenceQueryKey,
|
|
86
|
+
queryFn: () => fetchPreference(CHAT_NEW_SESSION_TYPE_PREFERENCE_KEY),
|
|
87
|
+
staleTime: 30_000,
|
|
88
|
+
retry: false,
|
|
89
|
+
});
|
|
90
|
+
const preferredSessionType = useMemo(
|
|
91
|
+
() => readPreferredSessionType(preferenceQuery.data),
|
|
92
|
+
[preferenceQuery.data],
|
|
93
|
+
);
|
|
94
|
+
const selectedSessionTypeOption = useMemo(
|
|
95
|
+
() =>
|
|
96
|
+
resolveNewSessionTypeOption({
|
|
97
|
+
preferredSessionType,
|
|
98
|
+
defaultSessionType,
|
|
99
|
+
sessionTypeOptions,
|
|
100
|
+
}),
|
|
101
|
+
[defaultSessionType, preferredSessionType, sessionTypeOptions],
|
|
102
|
+
);
|
|
103
|
+
const updateNewSessionType = useMutation({
|
|
104
|
+
mutationFn: async (sessionType: string) =>
|
|
105
|
+
await updatePreference(
|
|
106
|
+
CHAT_NEW_SESSION_TYPE_PREFERENCE_KEY,
|
|
107
|
+
normalizeSessionType(sessionType),
|
|
108
|
+
),
|
|
109
|
+
onMutate: async (sessionType) => {
|
|
110
|
+
const normalizedSessionType = normalizeSessionType(sessionType);
|
|
111
|
+
await queryClient.cancelQueries({
|
|
112
|
+
queryKey: chatNewSessionTypePreferenceQueryKey,
|
|
113
|
+
});
|
|
114
|
+
const previous = queryClient.getQueryData<PreferenceEntryView>(
|
|
115
|
+
chatNewSessionTypePreferenceQueryKey,
|
|
116
|
+
);
|
|
117
|
+
queryClient.setQueryData<PreferenceEntryView>(
|
|
118
|
+
chatNewSessionTypePreferenceQueryKey,
|
|
119
|
+
{
|
|
120
|
+
key: CHAT_NEW_SESSION_TYPE_PREFERENCE_KEY,
|
|
121
|
+
value: normalizedSessionType,
|
|
122
|
+
updatedAt: new Date().toISOString(),
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
return { previous };
|
|
126
|
+
},
|
|
127
|
+
onError: (_error, _sessionType, context) => {
|
|
128
|
+
if (context?.previous) {
|
|
129
|
+
queryClient.setQueryData(
|
|
130
|
+
chatNewSessionTypePreferenceQueryKey,
|
|
131
|
+
context.previous,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
onSuccess: (entry) => {
|
|
136
|
+
queryClient.setQueryData(chatNewSessionTypePreferenceQueryKey, entry);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
const setSelectedSessionType = useCallback(
|
|
140
|
+
(sessionType: string) => {
|
|
141
|
+
const nextOption = findSelectableSessionTypeOption(
|
|
142
|
+
sessionType,
|
|
143
|
+
sessionTypeOptions,
|
|
144
|
+
);
|
|
145
|
+
if (!nextOption) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
updateNewSessionType.mutate(nextOption.value);
|
|
149
|
+
},
|
|
150
|
+
[sessionTypeOptions, updateNewSessionType],
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
selectedSessionType: selectedSessionTypeOption.value,
|
|
155
|
+
selectedSessionTypeOption,
|
|
156
|
+
isLoading: preferenceQuery.isLoading,
|
|
157
|
+
setSelectedSessionType,
|
|
158
|
+
};
|
|
159
|
+
}
|