@nextclaw/ui 0.14.0 → 0.14.1-beta.1
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 +41 -0
- package/dist/assets/{api-B50yVdT8.js → api-dQ6uQSZG.js} +3 -3
- package/dist/assets/channels-list-page-DfpjB8XB.js +8 -0
- package/dist/assets/chat-page-D3QaEqjs.js +105 -0
- package/dist/assets/config-split-page-CeFfbaqc.js +1 -0
- package/dist/assets/{confirm-dialog-DRSelLdI.js → confirm-dialog-C95Y4QPC.js} +1 -1
- package/dist/assets/desktop-update-config-CcK6jiEb.js +1 -0
- package/dist/assets/{dist-D19wiQwB.js → dist-DTGzeil6.js} +1 -1
- package/dist/assets/{dist-NZRV-BxD.js → dist-fGogBmPA.js} +1 -1
- package/dist/assets/doc-browser-CuBTcBEZ.js +1 -0
- package/dist/assets/doc-browser-DVHhEb96.js +1 -0
- package/dist/assets/{doc-browser-context-C8KPbj-p.js → doc-browser-context-BfMXwnrz.js} +1 -1
- package/dist/assets/{ellipsis-BYy8TP0A.js → ellipsis-D2F4KMiU.js} +1 -1
- package/dist/assets/{external-link-528k6MDD.js → external-link-CcgtBK26.js} +1 -1
- package/dist/assets/index-ByJCbQqr.css +1 -0
- package/dist/assets/index-DjXqiloY.js +103 -0
- package/dist/assets/mcp-marketplace-page-98-V__3u.js +1 -0
- package/dist/assets/mcp-marketplace-page-BNq_PNmO.js +40 -0
- package/dist/assets/model-config-page-aAUkZkSX.js +1 -0
- package/dist/assets/plus-1Rxl5FlV.js +1 -0
- package/dist/assets/provider-scoped-model-input-we4_Z4gP.js +1 -0
- package/dist/assets/providers-config-page-CtMkPSyu.js +1 -0
- package/dist/assets/{react-Dsotw5vM.js → react-CTqdhFMk.js} +1 -1
- package/dist/assets/remote-fQ7N_r8u.js +1 -0
- package/dist/assets/rotate-cw-CzfIeAJj.js +1 -0
- package/dist/assets/runtime-config-page-TD1UrTbG.js +1 -0
- package/dist/assets/{save-CHDnDTok.js → save-C9fKWfVB.js} +1 -1
- package/dist/assets/search-D5FQ5q0X.js +1 -0
- package/dist/assets/search-config-page-AMEJJNIS.js +1 -0
- package/dist/assets/secrets-config-page-CcgJnvBO.js +3 -0
- package/dist/assets/select-Cu5n0E-3.js +41 -0
- package/dist/assets/{tag-chip-Cr8GwpRE.js → tag-chip-C5aOtjxu.js} +1 -1
- package/dist/assets/use-config-D8TESeo6.js +1 -0
- package/dist/assets/x-1qETdQ70.js +1 -0
- package/dist/index.html +14 -27
- package/package.json +9 -9
- package/src/app/presenters/app.presenter.ts +2 -0
- package/src/features/account/components/account-panel.tsx +2 -2
- package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +12 -63
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +11 -50
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +1 -0
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +15 -2
- package/src/features/chat/features/input/components/chat-input-bar.container.tsx +122 -85
- package/src/features/chat/features/input/hooks/use-chat-input-bar-query-state.ts +120 -0
- package/src/features/chat/features/input/hooks/use-chat-model-favorites.ts +87 -0
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +66 -41
- package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +1 -0
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +22 -36
- package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +80 -52
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +89 -0
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +9 -1
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +115 -0
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +4 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-file-operation.utils.test.ts +310 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +119 -1
- package/src/features/chat/features/message/utils/__tests__/chat-message-test.utils.ts +43 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +1 -346
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +51 -0
- package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +28 -5
- package/src/features/chat/features/message/utils/chat-message-tool-card.utils.ts +14 -9
- package/src/features/chat/features/message/utils/file-operation/diff.utils.ts +39 -36
- package/src/features/chat/features/message/utils/file-operation/line-builder.utils.ts +46 -38
- package/src/features/chat/features/message/utils/file-operation/parsed-block.utils.ts +18 -15
- package/src/features/chat/features/ncp/hooks/__tests__/use-ui-show-content-event.test.tsx +1 -0
- package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +11 -3
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +27 -0
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-badge.test.tsx +25 -25
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +19 -1
- package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +46 -0
- package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +15 -2
- package/src/features/chat/features/session/hooks/__tests__/use-selected-session-context-window-indicator.test.tsx +63 -0
- package/src/features/chat/features/session/hooks/use-selected-session-context-window-indicator.ts +7 -10
- package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +8 -0
- package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +2 -0
- package/src/features/chat/features/session-type/components/__tests__/chat-sidebar-create-menu.test.tsx +24 -0
- package/src/features/chat/features/session-type/components/chat-sidebar-create-menu.tsx +10 -1
- package/src/features/chat/features/session-type/hooks/use-chat-session-type-state.ts +1 -0
- package/src/features/chat/features/session-type/utils/chat-session-type.utils.ts +4 -0
- package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +193 -0
- package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +123 -0
- package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +104 -0
- package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +72 -0
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +54 -0
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +132 -0
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +82 -0
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +121 -0
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +53 -0
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-selection.utils.test.ts +63 -0
- package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-visibility.utils.test.ts +34 -0
- package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +71 -0
- package/src/features/chat/features/welcome/utils/chat-welcome-selection.utils.ts +79 -0
- package/src/features/chat/features/welcome/utils/chat-welcome-visibility.utils.ts +17 -0
- package/src/features/chat/index.ts +1 -0
- package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +93 -0
- package/src/features/chat/managers/__tests__/chat-run-snapshot.manager.test.ts +129 -0
- package/src/features/chat/managers/__tests__/chat-run.manager.test.ts +34 -0
- package/src/features/chat/managers/__tests__/chat-session-preference-sync.manager.test.ts +143 -1
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +30 -0
- package/src/features/chat/managers/__tests__/recent-selection.manager.test.ts +37 -14
- package/src/features/chat/managers/chat-draft-intent.manager.ts +46 -0
- package/src/features/chat/managers/chat-input.manager.ts +37 -15
- package/src/features/chat/managers/chat-run.manager.ts +3 -0
- package/src/features/chat/managers/chat-session-preference-sync.manager.ts +19 -2
- package/src/features/chat/managers/chat-thread.manager.ts +4 -0
- package/src/features/chat/managers/chat-ui.manager.ts +2 -6
- package/src/features/chat/managers/recent-selection.manager.ts +21 -67
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +47 -1
- package/src/features/chat/pages/ncp-chat-page.tsx +18 -0
- package/src/features/chat/stores/chat-input.store.ts +9 -1
- package/src/features/chat/stores/recent-selection.store.ts +85 -0
- package/src/features/chat/types/chat-input-bar.types.ts +5 -1
- package/src/features/marketplace/utils/marketplace-installed-cache.utils.ts +27 -23
- package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +137 -0
- package/src/features/panel-apps/components/panel-apps-list.tsx +119 -5
- package/src/features/panel-apps/index.ts +2 -0
- package/src/features/panel-apps/utils/panel-app-entry-match.utils.ts +17 -0
- package/src/features/settings/components/config/provider-form.tsx +284 -387
- package/src/features/settings/components/config/provider-status-badge.tsx +4 -4
- package/src/features/settings/components/config/secrets-config-form.tsx +11 -135
- package/src/features/settings/hooks/use-provider-auth-flow.ts +140 -0
- package/src/features/settings/pages/__tests__/model-config-page.test.tsx +16 -0
- package/src/features/settings/pages/secrets-config-page.tsx +2 -1
- package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +60 -1
- package/src/features/settings/utils/__tests__/secrets-config-form.utils.test.ts +66 -0
- package/src/features/settings/utils/provider-form-context.utils.ts +131 -0
- package/src/features/settings/utils/provider-form-model.utils.ts +101 -0
- package/src/features/settings/utils/provider-form-support.utils.ts +102 -1
- package/src/features/settings/utils/secrets-config-form.utils.ts +139 -0
- package/src/features/system-status/components/config/runtime-agent-list-card.tsx +17 -11
- package/src/features/system-status/components/config/runtime-binding-list-card.tsx +14 -9
- package/src/features/system-status/components/config/runtime-config-editor.tsx +8 -5
- package/src/features/system-status/components/config/runtime-entry-list-card.tsx +14 -9
- package/src/features/system-status/components/config/runtime-settings-card.tsx +16 -7
- package/src/features/system-status/components/runtime-security-card.tsx +79 -25
- package/src/shared/components/common/__tests__/searchable-model-input.test.tsx +34 -0
- package/src/shared/components/common/provider-scoped-model-input.tsx +1 -0
- package/src/shared/components/common/searchable-model-input.tsx +102 -87
- package/src/shared/components/ui/README.md +7 -0
- package/src/shared/components/ui/popover.tsx +19 -3
- package/src/shared/components/ui/select.tsx +14 -4
- package/src/shared/hooks/use-infinite-scroll-loader.ts +14 -10
- package/src/shared/lib/api/chat-session-type.types.ts +7 -0
- package/src/shared/lib/api/index.ts +2 -0
- package/src/shared/lib/api/ncp-session.types.ts +1 -0
- package/src/shared/lib/api/preferences/preference.types.ts +22 -0
- package/src/shared/lib/api/preferences/preference.utils.ts +40 -0
- package/src/shared/lib/api/types.ts +3 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +20 -1
- package/src/shared/lib/i18n/locales/en-US/core.json +1 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +10 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -1
- package/src/shared/lib/i18n/locales/zh-CN/core.json +1 -0
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +10 -0
- package/dist/assets/app-presenter-provider-BI-ewLbO.js +0 -3
- package/dist/assets/arrow-left-BOCJyLbQ.js +0 -1
- package/dist/assets/book-open-Bd2Xbrrc.js +0 -1
- package/dist/assets/channels-list-page-moJkpxtU.js +0 -8
- package/dist/assets/chat-page-CO27cl4v.js +0 -105
- package/dist/assets/config-split-page-CsQ2rIdq.js +0 -1
- package/dist/assets/desktop-update-config-qa-SynOc.js +0 -1
- package/dist/assets/doc-browser-2ddUJCA0.js +0 -1
- package/dist/assets/doc-browser-BjHUczQb.js +0 -1
- package/dist/assets/es2015-BUIIKCNh.js +0 -41
- package/dist/assets/host-capabilities-9bB_998Z.js +0 -1
- package/dist/assets/index-CMNrRhnG.js +0 -101
- package/dist/assets/index-UTmTW1eU.css +0 -1
- package/dist/assets/key-round-D3vD9dj7.js +0 -1
- package/dist/assets/loader-circle-DV9TSRns.js +0 -1
- package/dist/assets/mcp-marketplace-page-CS5Lh3oA.js +0 -1
- package/dist/assets/mcp-marketplace-page-DEOMcWb0.js +0 -40
- package/dist/assets/model-config-page-BSc4o7pb.js +0 -1
- package/dist/assets/notice-card-qqlbsOoR.js +0 -1
- package/dist/assets/plus-Bf9qhPDO.js +0 -1
- package/dist/assets/popover-BnmPOXe3.js +0 -1
- package/dist/assets/provider-scoped-model-input-Ca56s475.js +0 -1
- package/dist/assets/providers-config-page-D8Kf4U3u.js +0 -1
- package/dist/assets/refresh-cw-Bmx4wc9P.js +0 -1
- package/dist/assets/remote-DNMEy_c4.js +0 -1
- package/dist/assets/rotate-cw-CL0sVBel.js +0 -1
- package/dist/assets/runtime-config-page-CUFtKr0U.js +0 -1
- package/dist/assets/search-D7L-TCH-.js +0 -1
- package/dist/assets/search-config-page-CA3RVAef.js +0 -1
- package/dist/assets/secrets-config-page-95yaH1fa.js +0 -3
- package/dist/assets/status-dot-Dm4h-bjn.js +0 -1
- package/dist/assets/tabs-CJvw5Lqp.js +0 -1
- package/dist/assets/tooltip-Bxzi6aDL.js +0 -1
- package/dist/assets/trash-2-CSfX-BvU.js +0 -1
- package/dist/assets/use-config-TzwzTgg0.js +0 -1
- package/dist/assets/x-fkIWvi07.js +0 -1
- package/src/features/chat/components/__tests__/chat-welcome.test.tsx +0 -45
- package/src/features/chat/components/chat-welcome.tsx +0 -102
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-bar-controller.test.tsx +0 -152
- package/src/features/chat/features/input/hooks/use-chat-input-bar-controller.ts +0 -142
- /package/dist/assets/{config-hints-Cl2XAhzV.js → config-hints-CTqBnCDp.js} +0 -0
|
@@ -483,6 +483,7 @@ describe('ChatThreadManager showContent', () => {
|
|
|
483
483
|
},
|
|
484
484
|
title: 'Example URL',
|
|
485
485
|
purpose: 'read',
|
|
486
|
+
placement: undefined,
|
|
486
487
|
});
|
|
487
488
|
await manager.handleUiShowContentEvent({
|
|
488
489
|
id: 'tool:call-show-content-1:show-content',
|
|
@@ -495,6 +496,7 @@ describe('ChatThreadManager showContent', () => {
|
|
|
495
496
|
},
|
|
496
497
|
title: 'Example URL',
|
|
497
498
|
purpose: 'read',
|
|
499
|
+
placement: undefined,
|
|
498
500
|
});
|
|
499
501
|
|
|
500
502
|
expect(uiManager.showContent).toHaveBeenCalledTimes(1);
|
|
@@ -541,6 +543,34 @@ describe('ChatThreadManager showContent', () => {
|
|
|
541
543
|
title: 'Reader',
|
|
542
544
|
});
|
|
543
545
|
});
|
|
546
|
+
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
describe('ChatThreadManager inline showContent', () => {
|
|
550
|
+
it('does not auto-open inline panel app content outside the message card', async () => {
|
|
551
|
+
const uiManager = createUiManager();
|
|
552
|
+
const manager = new ChatThreadManager(
|
|
553
|
+
uiManager,
|
|
554
|
+
{} as ConstructorParameters<typeof ChatThreadManager>[1],
|
|
555
|
+
{} as ConstructorParameters<typeof ChatThreadManager>[2],
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
await manager.handleUiShowContentEvent({
|
|
559
|
+
id: 'tool:call-show-content-inline:show-content',
|
|
560
|
+
toolCallId: 'call-show-content-inline',
|
|
561
|
+
target: {
|
|
562
|
+
type: 'panel_app',
|
|
563
|
+
payload: {
|
|
564
|
+
appId: 'reader',
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
title: 'Reader',
|
|
568
|
+
purpose: 'interact',
|
|
569
|
+
placement: 'inline',
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
expect(uiManager.showContent).not.toHaveBeenCalled();
|
|
573
|
+
});
|
|
544
574
|
});
|
|
545
575
|
|
|
546
576
|
describe('ChatThreadManager visible workspace selection', () => {
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import { RecentSelectionManager } from '
|
|
1
|
+
import { RecentSelectionManager } from '@/features/chat/managers/recent-selection.manager';
|
|
2
|
+
import { useRecentSelectionStore } from '@/features/chat/stores/recent-selection.store';
|
|
2
3
|
|
|
3
4
|
describe('RecentSelectionManager', () => {
|
|
4
5
|
const storageKey = 'test.recent-selection-manager';
|
|
5
|
-
let storageState: Record<string, string>;
|
|
6
|
-
let storage: Pick<Storage, 'getItem' | 'setItem'>;
|
|
7
6
|
|
|
8
7
|
beforeEach(() => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
getItem: (key) => storageState[key] ?? null,
|
|
12
|
-
setItem: (key, value) => {
|
|
13
|
-
storageState[key] = value;
|
|
14
|
-
}
|
|
15
|
-
};
|
|
8
|
+
window.localStorage.clear();
|
|
9
|
+
useRecentSelectionStore.setState({ entriesByKey: {} });
|
|
16
10
|
});
|
|
17
11
|
|
|
18
12
|
it('stores recent values in LRU order and respects the size limit', () => {
|
|
19
|
-
const manager = new RecentSelectionManager({ storageKey, limit: 3
|
|
13
|
+
const manager = new RecentSelectionManager({ storageKey, limit: 3 });
|
|
20
14
|
|
|
21
15
|
manager.remember('openai/gpt-5');
|
|
22
16
|
manager.remember('anthropic/claude-sonnet-4');
|
|
@@ -31,7 +25,7 @@ describe('RecentSelectionManager', () => {
|
|
|
31
25
|
});
|
|
32
26
|
|
|
33
27
|
it('filters recent values by the currently available list and threshold', () => {
|
|
34
|
-
const manager = new RecentSelectionManager({ storageKey, limit: 4
|
|
28
|
+
const manager = new RecentSelectionManager({ storageKey, limit: 4 });
|
|
35
29
|
manager.remember('openai/gpt-5');
|
|
36
30
|
manager.remember('anthropic/claude-sonnet-4');
|
|
37
31
|
manager.remember('missing/model');
|
|
@@ -60,9 +54,38 @@ describe('RecentSelectionManager', () => {
|
|
|
60
54
|
});
|
|
61
55
|
|
|
62
56
|
it('returns an empty list when storage content is malformed', () => {
|
|
63
|
-
|
|
64
|
-
const manager = new RecentSelectionManager({ storageKey, limit: 3
|
|
57
|
+
window.localStorage.setItem(storageKey, '{broken-json');
|
|
58
|
+
const manager = new RecentSelectionManager({ storageKey, limit: 3 });
|
|
65
59
|
|
|
66
60
|
expect(manager.read()).toEqual([]);
|
|
67
61
|
});
|
|
62
|
+
|
|
63
|
+
it('does not mutate an empty store while reading recent values', () => {
|
|
64
|
+
const manager = new RecentSelectionManager({ storageKey, limit: 3 });
|
|
65
|
+
|
|
66
|
+
expect(manager.read()).toEqual([]);
|
|
67
|
+
expect(useRecentSelectionStore.getState().entriesByKey).toEqual({});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('keeps namespaced recent values separate from the global list', () => {
|
|
71
|
+
const manager = new RecentSelectionManager({ storageKey, limit: 3 });
|
|
72
|
+
|
|
73
|
+
manager.remember('openai/gpt-5');
|
|
74
|
+
manager.remember('anthropic/claude-sonnet-4', { namespace: 'codex' });
|
|
75
|
+
manager.remember('minimax/MiniMax-M2.7', { namespace: 'hermes' });
|
|
76
|
+
manager.remember('openai/gpt-5.1', { namespace: 'codex' });
|
|
77
|
+
|
|
78
|
+
expect(manager.read()).toEqual(['openai/gpt-5']);
|
|
79
|
+
expect(manager.read({ namespace: 'codex' })).toEqual([
|
|
80
|
+
'openai/gpt-5.1',
|
|
81
|
+
'anthropic/claude-sonnet-4'
|
|
82
|
+
]);
|
|
83
|
+
expect(
|
|
84
|
+
manager.resolveVisible({
|
|
85
|
+
namespace: 'hermes',
|
|
86
|
+
availableValues: ['minimax/MiniMax-M2.7', 'openai/gpt-5'],
|
|
87
|
+
minAvailableCount: 0
|
|
88
|
+
})
|
|
89
|
+
).toEqual(['minimax/MiniMax-M2.7']);
|
|
90
|
+
});
|
|
68
91
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type ChatDraftIntent = {
|
|
2
|
+
id: number;
|
|
3
|
+
prompt: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type ChatDraftIntentListener = (intent: ChatDraftIntent) => void;
|
|
7
|
+
|
|
8
|
+
export class ChatDraftIntentManager {
|
|
9
|
+
private nextId = 0;
|
|
10
|
+
private pendingIntent: ChatDraftIntent | null = null;
|
|
11
|
+
private readonly listeners = new Set<ChatDraftIntentListener>();
|
|
12
|
+
|
|
13
|
+
requestDraft = (prompt: string) => {
|
|
14
|
+
const normalizedPrompt = prompt.trim();
|
|
15
|
+
if (!normalizedPrompt) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const intent: ChatDraftIntent = {
|
|
19
|
+
id: this.nextId + 1,
|
|
20
|
+
prompt: normalizedPrompt,
|
|
21
|
+
};
|
|
22
|
+
this.nextId = intent.id;
|
|
23
|
+
this.pendingIntent = intent;
|
|
24
|
+
this.listeners.forEach((listener) => listener(intent));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
consumePending = (): ChatDraftIntent | null => {
|
|
28
|
+
const intent = this.pendingIntent;
|
|
29
|
+
this.pendingIntent = null;
|
|
30
|
+
return intent;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
markConsumed = (intentId: number) => {
|
|
34
|
+
if (this.pendingIntent?.id !== intentId) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this.pendingIntent = null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
subscribe = (listener: ChatDraftIntentListener) => {
|
|
41
|
+
this.listeners.add(listener);
|
|
42
|
+
return () => {
|
|
43
|
+
this.listeners.delete(listener);
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -29,11 +29,9 @@ import type { ChatModelOption } from '@/features/chat/types/chat-input.types';
|
|
|
29
29
|
import { normalizeSessionType } from '@/features/chat/features/session-type/utils/chat-session-type.utils';
|
|
30
30
|
import { systemStatusManager } from '@/features/system-status';
|
|
31
31
|
import { shouldClearPendingProjectRootOverride } from '@/features/chat/features/session/utils/chat-run-metadata.utils';
|
|
32
|
+
import { normalizeSessionProjectRootValue } from '@/shared/lib/session-project';
|
|
32
33
|
|
|
33
34
|
function resolveModelForSend(value: string): string | undefined {
|
|
34
|
-
if (isRuntimeDefaultModelValue(value)) {
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
35
|
return value || undefined;
|
|
38
36
|
}
|
|
39
37
|
|
|
@@ -55,16 +53,6 @@ export class ChatInputManager {
|
|
|
55
53
|
private sessionListManager: ChatSessionListManager
|
|
56
54
|
) {}
|
|
57
55
|
|
|
58
|
-
private hasSnapshotChanges = (patch: Partial<ChatInputSnapshot>): boolean => {
|
|
59
|
-
const current = useChatInputStore.getState().snapshot;
|
|
60
|
-
for (const [key, value] of Object.entries(patch) as Array<[keyof ChatInputSnapshot, ChatInputSnapshot[keyof ChatInputSnapshot]]>) {
|
|
61
|
-
if (!Object.is(current[key], value)) {
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return false;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
56
|
private resolveUpdateValue = <T>(prev: T, next: SetStateAction<T>): T => {
|
|
69
57
|
if (typeof next === 'function') {
|
|
70
58
|
return (next as (value: T) => T)(prev);
|
|
@@ -75,6 +63,16 @@ export class ChatInputManager {
|
|
|
75
63
|
private isSameStringArray = (left: string[], right: string[]): boolean =>
|
|
76
64
|
left.length === right.length && left.every((value, index) => value === right[index]);
|
|
77
65
|
|
|
66
|
+
private hasSnapshotChanges = (patch: Partial<ChatInputSnapshot>): boolean => {
|
|
67
|
+
const current = useChatInputStore.getState().snapshot;
|
|
68
|
+
for (const [key, value] of Object.entries(patch) as Array<[keyof ChatInputSnapshot, ChatInputSnapshot[keyof ChatInputSnapshot]]>) {
|
|
69
|
+
if (!Object.is(current[key], value)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
|
|
78
76
|
private isRuntimeBlockedForSend = (): boolean =>
|
|
79
77
|
isNcpChatRuntimeBlocked(systemStatusManager.getStatusView());
|
|
80
78
|
|
|
@@ -145,15 +143,19 @@ export class ChatInputManager {
|
|
|
145
143
|
sessionKey: string | null | undefined;
|
|
146
144
|
selectedSessionProjectRoot: string | null | undefined;
|
|
147
145
|
}): string | null => {
|
|
146
|
+
const { sessionKey, selectedSessionProjectRoot } = params;
|
|
148
147
|
const { pendingProjectRoot, pendingProjectRootSessionKey } =
|
|
149
148
|
useChatInputStore.getState().snapshot;
|
|
150
149
|
if (
|
|
151
150
|
pendingProjectRoot !== null &&
|
|
152
|
-
(!
|
|
151
|
+
(!sessionKey || sessionKey === pendingProjectRootSessionKey)
|
|
153
152
|
) {
|
|
154
153
|
return pendingProjectRoot;
|
|
155
154
|
}
|
|
156
|
-
|
|
155
|
+
if (!sessionKey) {
|
|
156
|
+
return useChatInputStore.getState().snapshot.defaultProjectRoot;
|
|
157
|
+
}
|
|
158
|
+
return selectedSessionProjectRoot ?? null;
|
|
157
159
|
};
|
|
158
160
|
|
|
159
161
|
clearPendingProjectRootOverrideForSession = (params: {
|
|
@@ -197,6 +199,15 @@ export class ChatInputManager {
|
|
|
197
199
|
this.syncComposerSnapshot(createChatComposerNodesFromDraft(value));
|
|
198
200
|
};
|
|
199
201
|
|
|
202
|
+
applyPromptSuggestion = (prompt: string) => {
|
|
203
|
+
const normalizedPrompt = prompt.trim();
|
|
204
|
+
if (!normalizedPrompt) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.setDraft(normalizedPrompt);
|
|
208
|
+
this.requestComposerFocusAtEnd();
|
|
209
|
+
};
|
|
210
|
+
|
|
200
211
|
requestComposerFocusAtEnd = () => {
|
|
201
212
|
const currentRequest = useChatInputStore.getState().snapshot.composerFocusRequest;
|
|
202
213
|
useChatInputStore.getState().setSnapshot({
|
|
@@ -260,6 +271,14 @@ export class ChatInputManager {
|
|
|
260
271
|
});
|
|
261
272
|
};
|
|
262
273
|
|
|
274
|
+
setPendingProjectRoot = (projectRoot: string | null) => {
|
|
275
|
+
const normalizedProjectRoot = normalizeSessionProjectRootValue(projectRoot);
|
|
276
|
+
useChatInputStore.getState().setSnapshot({
|
|
277
|
+
pendingProjectRoot: normalizedProjectRoot,
|
|
278
|
+
pendingProjectRootSessionKey: null,
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
|
|
263
282
|
send = async () => {
|
|
264
283
|
const inputSnapshot = useChatInputStore.getState().snapshot;
|
|
265
284
|
const sessionSnapshot = useChatSessionListStore.getState().snapshot;
|
|
@@ -351,6 +370,9 @@ export class ChatInputManager {
|
|
|
351
370
|
|
|
352
371
|
selectModel = (value: string) => {
|
|
353
372
|
this.setSelectedModel(value);
|
|
373
|
+
chatRecentModelsManager.remember(value, {
|
|
374
|
+
namespace: useChatInputStore.getState().snapshot.selectedSessionType,
|
|
375
|
+
});
|
|
354
376
|
if (!isRuntimeDefaultModelValue(value)) {
|
|
355
377
|
chatRecentModelsManager.remember(value);
|
|
356
378
|
}
|
|
@@ -61,6 +61,8 @@ export class ChatRunManager {
|
|
|
61
61
|
|
|
62
62
|
applyRunSnapshot = (snapshot: ChatRunSnapshot) => {
|
|
63
63
|
const isSending = snapshot.isSending || snapshot.isRunning;
|
|
64
|
+
const sessionKey =
|
|
65
|
+
snapshot.routeSessionKey ?? snapshot.materializedSessionKey ?? null;
|
|
64
66
|
useChatInputStore.getState().setSnapshot({
|
|
65
67
|
canStopGeneration: snapshot.isRunning,
|
|
66
68
|
stopDisabledReason: snapshot.isRunning ? null : '__preparing__',
|
|
@@ -70,6 +72,7 @@ export class ChatRunManager {
|
|
|
70
72
|
isSending,
|
|
71
73
|
});
|
|
72
74
|
useChatThreadStore.getState().setSnapshot({
|
|
75
|
+
sessionKey,
|
|
73
76
|
isHistoryLoading: snapshot.isHydrating,
|
|
74
77
|
messages: snapshot.visibleMessages,
|
|
75
78
|
isSending,
|
|
@@ -45,6 +45,7 @@ export class ChatSessionPreferenceSync {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
syncInputSelection = (params: {
|
|
48
|
+
selectedSessionKey?: string | null;
|
|
48
49
|
selectedSessionExists: boolean;
|
|
49
50
|
selectedSessionPreferredModel?: string;
|
|
50
51
|
fallbackPreferredModel?: string;
|
|
@@ -56,15 +57,14 @@ export class ChatSessionPreferenceSync {
|
|
|
56
57
|
defaultModel,
|
|
57
58
|
fallbackPreferredModel,
|
|
58
59
|
fallbackPreferredThinking,
|
|
60
|
+
selectedSessionKey = null,
|
|
59
61
|
selectedSessionExists,
|
|
60
62
|
selectedSessionPreferredModel,
|
|
61
63
|
selectedSessionPreferredThinking,
|
|
62
64
|
} = params;
|
|
63
65
|
const { snapshot } = useChatInputStore.getState();
|
|
64
66
|
const { modelOptions } = snapshot;
|
|
65
|
-
const selectedSessionKey = useChatThreadStore.getState().snapshot.sessionKey;
|
|
66
67
|
const sessionChanged = this.previousPreferenceSessionKey !== selectedSessionKey;
|
|
67
|
-
this.previousPreferenceSessionKey = selectedSessionKey;
|
|
68
68
|
const preserveCurrentPreference = sessionChanged && Boolean(selectedSessionKey) && !selectedSessionExists;
|
|
69
69
|
const selectedModel = resolveSelectedModelValue({
|
|
70
70
|
currentSelectedModel: snapshot.selectedModel,
|
|
@@ -88,10 +88,27 @@ export class ChatSessionPreferenceSync {
|
|
|
88
88
|
preferSessionPreferredThinking: sessionChanged,
|
|
89
89
|
preserveCurrentSelectedThinkingOnSessionChange: preserveCurrentPreference,
|
|
90
90
|
});
|
|
91
|
+
if (
|
|
92
|
+
snapshot.selectedModel === selectedModel &&
|
|
93
|
+
snapshot.selectedThinkingLevel === selectedThinking
|
|
94
|
+
) {
|
|
95
|
+
this.recordSyncedPreferenceSessionKey(selectedSessionKey, selectedSessionExists);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
91
98
|
useChatInputStore.getState().setSnapshot({
|
|
92
99
|
selectedModel,
|
|
93
100
|
selectedThinkingLevel: selectedThinking,
|
|
94
101
|
});
|
|
102
|
+
this.recordSyncedPreferenceSessionKey(selectedSessionKey, selectedSessionExists);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
private recordSyncedPreferenceSessionKey = (
|
|
106
|
+
selectedSessionKey: string | null,
|
|
107
|
+
selectedSessionExists: boolean,
|
|
108
|
+
): void => {
|
|
109
|
+
if (!selectedSessionKey || selectedSessionExists) {
|
|
110
|
+
this.previousPreferenceSessionKey = selectedSessionKey;
|
|
111
|
+
}
|
|
95
112
|
};
|
|
96
113
|
|
|
97
114
|
private enqueue = (next: QueuedSessionPreferenceSync): void => {
|
|
@@ -296,10 +296,14 @@ export class ChatThreadManager {
|
|
|
296
296
|
target: payload.target,
|
|
297
297
|
title: payload.title,
|
|
298
298
|
purpose: payload.purpose,
|
|
299
|
+
placement: payload.placement,
|
|
299
300
|
});
|
|
300
301
|
};
|
|
301
302
|
|
|
302
303
|
private showContent = async (request: ChatUiShowContentRequest): Promise<void> => {
|
|
304
|
+
if (request.placement === 'inline') {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
303
307
|
if (request.target.type === 'file') {
|
|
304
308
|
this.openFilePreview({
|
|
305
309
|
path: request.target.payload.path,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
createPanelAppResourceUri,
|
|
7
7
|
createPanelAppRightPanelResourceTarget,
|
|
8
8
|
} from '@/features/right-panel-resources';
|
|
9
|
+
import { findPanelAppEntryByDisplayId } from '@/features/panel-apps';
|
|
9
10
|
import type { DocBrowserManager } from '@/shared/components/doc-browser/managers/doc-browser.manager';
|
|
10
11
|
import { nextclawClient, type PanelAppEntryView } from '@/shared/lib/api';
|
|
11
12
|
|
|
@@ -26,11 +27,6 @@ type ChatUiDisplayContentTarget = Extract<ChatUiShowContentRequest['target'], {
|
|
|
26
27
|
|
|
27
28
|
const noopConfirm: ChatUiActions['confirm'] = async () => false;
|
|
28
29
|
|
|
29
|
-
function isPanelAppEntryMatch(entry: PanelAppEntryView, value: string): boolean {
|
|
30
|
-
const normalizedValue = value.trim();
|
|
31
|
-
return [entry.id, entry.appId, entry.fileName, entry.title].some((candidate) => candidate.trim() === normalizedValue);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
30
|
export class ChatUiManager {
|
|
35
31
|
private state: ChatUiState = {
|
|
36
32
|
pathname: ''
|
|
@@ -84,7 +80,7 @@ export class ChatUiManager {
|
|
|
84
80
|
return null;
|
|
85
81
|
}
|
|
86
82
|
try {
|
|
87
|
-
return (await nextclawClient.panelApps.listPanelApps()).entries
|
|
83
|
+
return findPanelAppEntryByDisplayId((await nextclawClient.panelApps.listPanelApps()).entries, normalizedValue);
|
|
88
84
|
} catch {
|
|
89
85
|
return null;
|
|
90
86
|
}
|
|
@@ -1,49 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useRecentSelectionStore,
|
|
3
|
+
normalizeRecentSelectionList,
|
|
4
|
+
normalizeRecentSelectionValue,
|
|
5
|
+
resolveRecentSelectionKey,
|
|
6
|
+
type RecentSelectionScope,
|
|
7
|
+
} from '@/features/chat/stores/recent-selection.store';
|
|
8
|
+
|
|
1
9
|
type RecentSelectionManagerOptions = {
|
|
2
10
|
storageKey: string;
|
|
3
11
|
limit: number;
|
|
4
|
-
storage?: Pick<Storage, 'getItem' | 'setItem'> | null;
|
|
5
12
|
};
|
|
6
13
|
|
|
7
14
|
type VisibleRecentSelectionParams = {
|
|
8
15
|
availableValues: string[];
|
|
9
16
|
minAvailableCount: number;
|
|
10
17
|
limit?: number;
|
|
11
|
-
};
|
|
18
|
+
} & RecentSelectionScope;
|
|
12
19
|
|
|
13
20
|
export class RecentSelectionManager {
|
|
14
21
|
constructor(private readonly options: RecentSelectionManagerOptions) {}
|
|
15
22
|
|
|
16
|
-
read = (): string[] => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
try {
|
|
22
|
-
return this.normalizeList(JSON.parse(storage.getItem(this.options.storageKey) ?? '[]'));
|
|
23
|
-
} catch {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
23
|
+
read = (scope: RecentSelectionScope = {}): string[] => {
|
|
24
|
+
const key = resolveRecentSelectionKey(this.options, scope);
|
|
25
|
+
return normalizeRecentSelectionList(useRecentSelectionStore.getState().entriesByKey[key], this.options.limit);
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
remember = (value: string): string[] => {
|
|
29
|
-
const normalizedValue =
|
|
28
|
+
remember = (value: string, scope: RecentSelectionScope = {}): string[] => {
|
|
29
|
+
const normalizedValue = normalizeRecentSelectionValue(value);
|
|
30
30
|
if (!normalizedValue) {
|
|
31
|
-
return this.read();
|
|
31
|
+
return this.read(scope);
|
|
32
32
|
}
|
|
33
|
-
const
|
|
34
|
-
this.
|
|
33
|
+
const key = resolveRecentSelectionKey(this.options, scope);
|
|
34
|
+
const next = [normalizedValue, ...this.read(scope).filter((item) => item !== normalizedValue)]
|
|
35
|
+
.slice(0, this.options.limit);
|
|
36
|
+
useRecentSelectionStore.getState().setEntries(key, next);
|
|
35
37
|
return next;
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
resolveVisible = (params: VisibleRecentSelectionParams): string[] => {
|
|
39
|
-
const availableValues =
|
|
41
|
+
const availableValues = normalizeRecentSelectionList(params.availableValues, Number.POSITIVE_INFINITY);
|
|
40
42
|
if (availableValues.length <= params.minAvailableCount) {
|
|
41
43
|
return [];
|
|
42
44
|
}
|
|
43
45
|
const availableSet = new Set(availableValues);
|
|
44
46
|
const visible: string[] = [];
|
|
45
47
|
const maxVisibleItems = Math.max(1, params.limit ?? this.options.limit);
|
|
46
|
-
for (const value of this.read()) {
|
|
48
|
+
for (const value of this.read(params)) {
|
|
47
49
|
if (!availableSet.has(value) || visible.includes(value)) {
|
|
48
50
|
continue;
|
|
49
51
|
}
|
|
@@ -54,52 +56,4 @@ export class RecentSelectionManager {
|
|
|
54
56
|
}
|
|
55
57
|
return visible;
|
|
56
58
|
};
|
|
57
|
-
|
|
58
|
-
private write = (values: string[]): void => {
|
|
59
|
-
const storage = this.getStorage();
|
|
60
|
-
if (!storage) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
storage.setItem(this.options.storageKey, JSON.stringify(this.normalizeList(values)));
|
|
65
|
-
} catch {
|
|
66
|
-
// Ignore storage write failures and keep the runtime behavior deterministic.
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
private getStorage = (): Storage | null => {
|
|
71
|
-
if (Object.prototype.hasOwnProperty.call(this.options, 'storage')) {
|
|
72
|
-
return (this.options.storage as Storage | null | undefined) ?? null;
|
|
73
|
-
}
|
|
74
|
-
if (typeof window === 'undefined') {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
return window.localStorage;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
private normalizeList = (values: unknown, limit = this.options.limit): string[] => {
|
|
81
|
-
if (!Array.isArray(values)) {
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
const deduped: string[] = [];
|
|
85
|
-
for (const value of values) {
|
|
86
|
-
const normalized = this.normalizeValue(value);
|
|
87
|
-
if (!normalized || deduped.includes(normalized)) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
deduped.push(normalized);
|
|
91
|
-
if (deduped.length >= limit) {
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return deduped;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
private normalizeValue = (value: unknown): string | null => {
|
|
99
|
-
if (typeof value !== 'string') {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
const normalized = value.trim();
|
|
103
|
-
return normalized.length > 0 ? normalized : null;
|
|
104
|
-
};
|
|
105
59
|
}
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
buildChatRunMetadata,
|
|
12
12
|
shouldClearPendingProjectRootOverride
|
|
13
13
|
} from '@/features/chat/features/session/utils/chat-run-metadata.utils';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
filterNcpChatModelOptionsBySessionType,
|
|
16
|
+
} from '@/features/chat/features/ncp/utils/ncp-chat-query-derived.utils';
|
|
15
17
|
|
|
16
18
|
const modelOptions: ChatModelOption[] = [
|
|
17
19
|
{
|
|
@@ -83,6 +85,29 @@ describe('filterNcpChatModelOptionsBySessionType', () => {
|
|
|
83
85
|
}]);
|
|
84
86
|
});
|
|
85
87
|
|
|
88
|
+
it('attaches runtime default thinking capability when the runtime publishes one', () => {
|
|
89
|
+
expect(
|
|
90
|
+
filterNcpChatModelOptionsBySessionType({
|
|
91
|
+
modelOptions,
|
|
92
|
+
modelSelectionMode: 'runtime-default',
|
|
93
|
+
runtimeDefaultModelLabel: 'Runtime default',
|
|
94
|
+
runtimeDefaultThinkingCapability: {
|
|
95
|
+
supported: ['minimal', 'low', 'medium', 'high'],
|
|
96
|
+
default: 'high',
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
).toEqual([{
|
|
100
|
+
value: RUNTIME_DEFAULT_MODEL_VALUE,
|
|
101
|
+
modelLabel: 'Runtime default',
|
|
102
|
+
providerLabel: '',
|
|
103
|
+
isRuntimeDefault: true,
|
|
104
|
+
thinkingCapability: {
|
|
105
|
+
supported: ['minimal', 'low', 'medium', 'high'],
|
|
106
|
+
default: 'high',
|
|
107
|
+
},
|
|
108
|
+
}]);
|
|
109
|
+
});
|
|
110
|
+
|
|
86
111
|
it('adds the runtime default option beside supported models for optional runtime selection', () => {
|
|
87
112
|
expect(
|
|
88
113
|
filterNcpChatModelOptionsBySessionType({
|
|
@@ -102,6 +127,27 @@ describe('filterNcpChatModelOptionsBySessionType', () => {
|
|
|
102
127
|
modelOptions[1],
|
|
103
128
|
]);
|
|
104
129
|
});
|
|
130
|
+
|
|
131
|
+
it('adds runtime default thinking capability beside supported models for optional runtime selection', () => {
|
|
132
|
+
expect(
|
|
133
|
+
filterNcpChatModelOptionsBySessionType({
|
|
134
|
+
modelOptions,
|
|
135
|
+
modelSelectionMode: 'optional',
|
|
136
|
+
runtimeDefaultModelLabel: 'Runtime default',
|
|
137
|
+
runtimeDefaultThinkingCapability: {
|
|
138
|
+
supported: ['minimal', 'low', 'medium', 'high'],
|
|
139
|
+
default: 'high',
|
|
140
|
+
},
|
|
141
|
+
supportedModels: ['openai/gpt-5'],
|
|
142
|
+
})[0]
|
|
143
|
+
).toMatchObject({
|
|
144
|
+
value: RUNTIME_DEFAULT_MODEL_VALUE,
|
|
145
|
+
thinkingCapability: {
|
|
146
|
+
supported: ['minimal', 'low', 'medium', 'high'],
|
|
147
|
+
default: 'high',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
});
|
|
105
151
|
});
|
|
106
152
|
|
|
107
153
|
describe('buildChatRunMetadata', () => {
|
|
@@ -30,6 +30,23 @@ import { isNcpChatRuntimeBlocked, resolveNcpChatSendErrorMessage } from "@/featu
|
|
|
30
30
|
import { useUiShowContentEvent } from "@/features/chat/features/ncp/hooks/use-ui-show-content-event";
|
|
31
31
|
import { readNcpContextWindowValue } from "@/features/chat/features/session/utils/ncp-session-context-metadata.utils";
|
|
32
32
|
|
|
33
|
+
function useChatDraftIntentConsumer() {
|
|
34
|
+
const appPresenter = useAppPresenter();
|
|
35
|
+
const presenter = usePresenter();
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const applyIntent = (intent: { id: number; prompt: string }) => {
|
|
38
|
+
presenter.startAgentCreationDraft(intent.prompt);
|
|
39
|
+
appPresenter.chatDraftIntentManager.markConsumed(intent.id);
|
|
40
|
+
};
|
|
41
|
+
const unsubscribe = appPresenter.chatDraftIntentManager.subscribe(applyIntent);
|
|
42
|
+
const pendingIntent = appPresenter.chatDraftIntentManager.consumePending();
|
|
43
|
+
if (pendingIntent) {
|
|
44
|
+
applyIntent(pendingIntent);
|
|
45
|
+
}
|
|
46
|
+
return unsubscribe;
|
|
47
|
+
}, [appPresenter, presenter]);
|
|
48
|
+
}
|
|
49
|
+
|
|
33
50
|
function useNcpChatRouteSelection() {
|
|
34
51
|
const { sessionId: routeSessionIdParam } = useParams<{ sessionId?: string }>();
|
|
35
52
|
const routeSessionKey = useMemo(
|
|
@@ -170,6 +187,7 @@ function NcpChatPageContent({ view }: ChatPageProps) {
|
|
|
170
187
|
syncRouteSessionSelection:
|
|
171
188
|
presenter.chatSessionListManager.syncRouteSessionSelection,
|
|
172
189
|
});
|
|
190
|
+
useChatDraftIntentConsumer();
|
|
173
191
|
useUiShowContentEvent();
|
|
174
192
|
useChatRunSnapshotSync({
|
|
175
193
|
agent,
|
|
@@ -2,7 +2,12 @@ import { create } from 'zustand';
|
|
|
2
2
|
import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
|
|
3
3
|
import type { NcpDraftAttachment } from '@nextclaw/ncp-react';
|
|
4
4
|
import type { RuntimeModelSelectionMode } from '@nextclaw/shared';
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
RuntimeDefaultThinkingView,
|
|
7
|
+
SessionSkillEntryView,
|
|
8
|
+
SessionTypeIconView,
|
|
9
|
+
ThinkingLevel,
|
|
10
|
+
} from '@/shared/lib/api';
|
|
6
11
|
import type { ChatModelOption } from '@/features/chat/types/chat-input.types';
|
|
7
12
|
import { createInitialChatComposerNodes } from '@/features/chat/features/input/utils/chat-composer-state.utils';
|
|
8
13
|
|
|
@@ -14,6 +19,7 @@ export type ChatInputSnapshot = {
|
|
|
14
19
|
pendingSessionType: string;
|
|
15
20
|
pendingProjectRoot: string | null;
|
|
16
21
|
pendingProjectRootSessionKey: string | null;
|
|
22
|
+
defaultProjectRoot: string | null;
|
|
17
23
|
defaultSessionType: string;
|
|
18
24
|
canStopGeneration: boolean;
|
|
19
25
|
stopDisabledReason: string | null;
|
|
@@ -32,6 +38,7 @@ export type ChatInputSnapshot = {
|
|
|
32
38
|
supportedModels?: string[];
|
|
33
39
|
recommendedModel?: string | null;
|
|
34
40
|
modelSelectionMode?: RuntimeModelSelectionMode;
|
|
41
|
+
runtimeDefaultThinking?: RuntimeDefaultThinkingView | null;
|
|
35
42
|
cta?: {
|
|
36
43
|
kind: string;
|
|
37
44
|
label?: string;
|
|
@@ -63,6 +70,7 @@ const initialSnapshot: ChatInputSnapshot = {
|
|
|
63
70
|
pendingSessionType: 'native',
|
|
64
71
|
pendingProjectRoot: null,
|
|
65
72
|
pendingProjectRootSessionKey: null,
|
|
73
|
+
defaultProjectRoot: null,
|
|
66
74
|
defaultSessionType: 'native',
|
|
67
75
|
canStopGeneration: false,
|
|
68
76
|
stopDisabledReason: null,
|