@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
|
@@ -40,7 +40,29 @@ function SecurityCardFrame({ children, contentClassName = 'space-y-6' }: { child
|
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
type RuntimeSecuritySetupSectionProps = {
|
|
44
|
+
setupUsername: string;
|
|
45
|
+
setSetupUsername: (value: string) => void;
|
|
46
|
+
setupPassword: string;
|
|
47
|
+
setSetupPassword: (value: string) => void;
|
|
48
|
+
setupConfirmPassword: string;
|
|
49
|
+
setSetupConfirmPassword: (value: string) => void;
|
|
50
|
+
canSubmitSetup: boolean;
|
|
51
|
+
isPending: boolean;
|
|
52
|
+
onSubmit: () => void;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function RuntimeSecuritySetupSection({
|
|
56
|
+
canSubmitSetup,
|
|
57
|
+
isPending,
|
|
58
|
+
onSubmit,
|
|
59
|
+
setSetupConfirmPassword,
|
|
60
|
+
setSetupPassword,
|
|
61
|
+
setSetupUsername,
|
|
62
|
+
setupConfirmPassword,
|
|
63
|
+
setupPassword,
|
|
64
|
+
setupUsername
|
|
65
|
+
}: RuntimeSecuritySetupSectionProps) {
|
|
44
66
|
return (
|
|
45
67
|
<SecurityCardFrame contentClassName="space-y-5">
|
|
46
68
|
<div className="rounded-xl border border-dashed border-gray-200 bg-gray-50/70 p-4">
|
|
@@ -52,8 +74,8 @@ function RuntimeSecuritySetupSection(props: { setupUsername: string; setSetupUse
|
|
|
52
74
|
<Label htmlFor="auth-setup-username">{t('authUsername')}</Label>
|
|
53
75
|
<Input
|
|
54
76
|
id="auth-setup-username"
|
|
55
|
-
value={
|
|
56
|
-
onChange={(event) =>
|
|
77
|
+
value={setupUsername}
|
|
78
|
+
onChange={(event) => setSetupUsername(event.target.value)}
|
|
57
79
|
placeholder={t('authUsernamePlaceholder')}
|
|
58
80
|
/>
|
|
59
81
|
</div>
|
|
@@ -62,8 +84,8 @@ function RuntimeSecuritySetupSection(props: { setupUsername: string; setSetupUse
|
|
|
62
84
|
<Input
|
|
63
85
|
id="auth-setup-password"
|
|
64
86
|
type="password"
|
|
65
|
-
value={
|
|
66
|
-
onChange={(event) =>
|
|
87
|
+
value={setupPassword}
|
|
88
|
+
onChange={(event) => setSetupPassword(event.target.value)}
|
|
67
89
|
placeholder={t('authPasswordPlaceholder')}
|
|
68
90
|
/>
|
|
69
91
|
</div>
|
|
@@ -73,43 +95,75 @@ function RuntimeSecuritySetupSection(props: { setupUsername: string; setSetupUse
|
|
|
73
95
|
<Input
|
|
74
96
|
id="auth-setup-confirm"
|
|
75
97
|
type="password"
|
|
76
|
-
value={
|
|
77
|
-
onChange={(event) =>
|
|
98
|
+
value={setupConfirmPassword}
|
|
99
|
+
onChange={(event) => setSetupConfirmPassword(event.target.value)}
|
|
78
100
|
placeholder={t('authConfirmPasswordPlaceholder')}
|
|
79
101
|
/>
|
|
80
102
|
<p className="text-xs text-gray-500">{t('authPasswordMinLengthHint')}</p>
|
|
81
103
|
</div>
|
|
82
|
-
<Button type="button" disabled={!
|
|
83
|
-
{
|
|
104
|
+
<Button type="button" disabled={!canSubmitSetup} onClick={() => void onSubmit()}>
|
|
105
|
+
{isPending ? t('authSettingUp') : t('authSetupAction')}
|
|
84
106
|
</Button>
|
|
85
107
|
</SecurityCardFrame>
|
|
86
108
|
);
|
|
87
109
|
}
|
|
88
110
|
|
|
89
|
-
|
|
111
|
+
type RuntimeSecurityConfiguredSectionProps = {
|
|
112
|
+
authenticated: boolean;
|
|
113
|
+
enabled: boolean;
|
|
114
|
+
username: string | null | undefined;
|
|
115
|
+
nextPassword: string;
|
|
116
|
+
setNextPassword: (value: string) => void;
|
|
117
|
+
nextConfirmPassword: string;
|
|
118
|
+
setNextConfirmPassword: (value: string) => void;
|
|
119
|
+
canUpdatePassword: boolean;
|
|
120
|
+
isUpdatingEnabled: boolean;
|
|
121
|
+
isUpdatingPassword: boolean;
|
|
122
|
+
isLoggingOut: boolean;
|
|
123
|
+
onEnabledChange: (enabled: boolean) => void;
|
|
124
|
+
onPasswordUpdate: () => void;
|
|
125
|
+
onLogout: () => void;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
function RuntimeSecurityConfiguredSection({
|
|
129
|
+
authenticated,
|
|
130
|
+
canUpdatePassword,
|
|
131
|
+
enabled,
|
|
132
|
+
isLoggingOut,
|
|
133
|
+
isUpdatingEnabled,
|
|
134
|
+
isUpdatingPassword,
|
|
135
|
+
nextConfirmPassword,
|
|
136
|
+
nextPassword,
|
|
137
|
+
onEnabledChange,
|
|
138
|
+
onLogout,
|
|
139
|
+
onPasswordUpdate,
|
|
140
|
+
setNextConfirmPassword,
|
|
141
|
+
setNextPassword,
|
|
142
|
+
username
|
|
143
|
+
}: RuntimeSecurityConfiguredSectionProps) {
|
|
90
144
|
return (
|
|
91
145
|
<SecurityCardFrame>
|
|
92
146
|
<div className="rounded-xl border border-gray-200 p-4">
|
|
93
147
|
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
|
|
94
148
|
<div className="space-y-1">
|
|
95
149
|
<p className="text-sm font-medium text-gray-900">{t('authStatusLabel')}</p>
|
|
96
|
-
<p className="text-sm text-gray-600">{t('authStatusConfiguredUser').replace('{username}',
|
|
150
|
+
<p className="text-sm text-gray-600">{t('authStatusConfiguredUser').replace('{username}', username ?? '')}</p>
|
|
97
151
|
<p className="text-xs text-gray-500">{t('authUsernameFixedHelp')}</p>
|
|
98
152
|
</div>
|
|
99
153
|
<span className="inline-flex items-center rounded-full bg-gray-100 px-3 py-1 text-xs font-medium text-gray-700">
|
|
100
|
-
{
|
|
154
|
+
{enabled ? t('enabled') : t('disabled')}
|
|
101
155
|
</span>
|
|
102
156
|
</div>
|
|
103
157
|
<div className="mt-4 flex flex-col gap-4 border-t border-gray-200 pt-4 md:flex-row md:items-center md:justify-between">
|
|
104
158
|
<div className="space-y-1">
|
|
105
159
|
<p className="text-sm font-medium text-gray-900">{t('authEnableLabel')}</p>
|
|
106
|
-
<p className="text-xs text-gray-500">{
|
|
160
|
+
<p className="text-xs text-gray-500">{enabled ? t('authEnableOnHelp') : t('authEnableOffHelp')}</p>
|
|
107
161
|
</div>
|
|
108
162
|
<Switch
|
|
109
|
-
checked={
|
|
110
|
-
disabled={
|
|
163
|
+
checked={enabled}
|
|
164
|
+
disabled={isUpdatingEnabled}
|
|
111
165
|
onCheckedChange={(checked) => {
|
|
112
|
-
void
|
|
166
|
+
void onEnabledChange(checked);
|
|
113
167
|
}}
|
|
114
168
|
/>
|
|
115
169
|
</div>
|
|
@@ -125,8 +179,8 @@ function RuntimeSecurityConfiguredSection(props: { authenticated: boolean; enabl
|
|
|
125
179
|
<Input
|
|
126
180
|
id="auth-password-next"
|
|
127
181
|
type="password"
|
|
128
|
-
value={
|
|
129
|
-
onChange={(event) =>
|
|
182
|
+
value={nextPassword}
|
|
183
|
+
onChange={(event) => setNextPassword(event.target.value)}
|
|
130
184
|
placeholder={t('authPasswordPlaceholder')}
|
|
131
185
|
/>
|
|
132
186
|
</div>
|
|
@@ -135,19 +189,19 @@ function RuntimeSecurityConfiguredSection(props: { authenticated: boolean; enabl
|
|
|
135
189
|
<Input
|
|
136
190
|
id="auth-password-confirm"
|
|
137
191
|
type="password"
|
|
138
|
-
value={
|
|
139
|
-
onChange={(event) =>
|
|
192
|
+
value={nextConfirmPassword}
|
|
193
|
+
onChange={(event) => setNextConfirmPassword(event.target.value)}
|
|
140
194
|
placeholder={t('authConfirmPasswordPlaceholder')}
|
|
141
195
|
/>
|
|
142
196
|
</div>
|
|
143
197
|
</div>
|
|
144
198
|
<div className="flex flex-wrap items-center gap-3">
|
|
145
|
-
<Button type="button" disabled={!
|
|
146
|
-
{
|
|
199
|
+
<Button type="button" disabled={!canUpdatePassword} onClick={() => void onPasswordUpdate()}>
|
|
200
|
+
{isUpdatingPassword ? t('authPasswordUpdating') : t('authPasswordAction')}
|
|
147
201
|
</Button>
|
|
148
|
-
{
|
|
149
|
-
<Button type="button" variant="outline" disabled={
|
|
150
|
-
{
|
|
202
|
+
{enabled && authenticated ? (
|
|
203
|
+
<Button type="button" variant="outline" disabled={isLoggingOut} onClick={() => void onLogout()}>
|
|
204
|
+
{isLoggingOut ? t('authLoggingOut') : t('authLogoutAction')}
|
|
151
205
|
</Button>
|
|
152
206
|
) : null}
|
|
153
207
|
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { SearchableModelInput } from '@/shared/components/common/searchable-model-input';
|
|
4
|
+
import { createPopoverAvailableHeightLimit } from '@/shared/components/ui/popover';
|
|
5
|
+
|
|
6
|
+
describe('SearchableModelInput', () => {
|
|
7
|
+
it('keeps the model option panel bounded with an internal scroll region', async () => {
|
|
8
|
+
render(
|
|
9
|
+
<SearchableModelInput
|
|
10
|
+
value=""
|
|
11
|
+
onChange={vi.fn()}
|
|
12
|
+
options={Array.from({ length: 40 }, (_, index) => `model-${index}`)}
|
|
13
|
+
placeholder="Model"
|
|
14
|
+
emptyText="No models"
|
|
15
|
+
createText="Use {value}"
|
|
16
|
+
toggleLabel="Toggle models"
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
fireEvent.focus(screen.getByPlaceholderText('Model'));
|
|
21
|
+
|
|
22
|
+
const firstOption = await screen.findByRole('button', { name: 'model-0' });
|
|
23
|
+
const panel = firstOption.closest('[data-state="open"]') as HTMLElement | null;
|
|
24
|
+
const scrollRegion = firstOption.closest('.overflow-y-auto');
|
|
25
|
+
|
|
26
|
+
expect(panel?.style.maxHeight).toBe(createPopoverAvailableHeightLimit('15rem'));
|
|
27
|
+
expect(panel?.style.maxHeight).toContain('max(0px');
|
|
28
|
+
expect(panel?.style.maxHeight).toContain('100vh');
|
|
29
|
+
expect(panel?.style.maxHeight).toContain('2rem');
|
|
30
|
+
expect(panel?.className).toContain('flex-col');
|
|
31
|
+
expect(scrollRegion?.className).toContain('flex-1');
|
|
32
|
+
expect(scrollRegion?.className).toContain('overflow-y-auto');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -151,6 +151,7 @@ export function ProviderScopedModelInput({
|
|
|
151
151
|
inputClassName="h-10 rounded-xl"
|
|
152
152
|
emptyText={t('modelPickerNoOptions')}
|
|
153
153
|
createText={t('modelPickerUseCustom')}
|
|
154
|
+
toggleLabel={t('modelPickerToggleOptions')}
|
|
154
155
|
/>
|
|
155
156
|
</div>
|
|
156
157
|
<p className="mt-2 text-xs text-gray-500">{t('modelInputCustomHint')}</p>
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { Check, ChevronsUpDown } from 'lucide-react';
|
|
3
3
|
import { Input } from '@/shared/components/ui/input';
|
|
4
|
+
import {
|
|
5
|
+
Popover,
|
|
6
|
+
PopoverAnchor,
|
|
7
|
+
PopoverContent,
|
|
8
|
+
createPopoverAvailableHeightLimit,
|
|
9
|
+
} from '@/shared/components/ui/popover';
|
|
4
10
|
import { cn } from '@/shared/lib/utils';
|
|
5
11
|
|
|
6
12
|
type SearchableModelInputProps = {
|
|
@@ -12,12 +18,15 @@ type SearchableModelInputProps = {
|
|
|
12
18
|
placeholder?: string;
|
|
13
19
|
className?: string;
|
|
14
20
|
inputClassName?: string;
|
|
15
|
-
emptyText
|
|
16
|
-
createText
|
|
21
|
+
emptyText: string;
|
|
22
|
+
createText: string;
|
|
23
|
+
toggleLabel: string;
|
|
17
24
|
maxItems?: number;
|
|
18
25
|
onEnter?: () => void;
|
|
19
26
|
};
|
|
20
27
|
|
|
28
|
+
const SEARCHABLE_MODEL_MENU_MAX_HEIGHT = createPopoverAvailableHeightLimit('15rem');
|
|
29
|
+
|
|
21
30
|
function normalizeOptions(options: string[]): string[] {
|
|
22
31
|
const deduped = new Set<string>();
|
|
23
32
|
for (const option of options) {
|
|
@@ -40,6 +49,7 @@ export function SearchableModelInput({
|
|
|
40
49
|
inputClassName,
|
|
41
50
|
emptyText,
|
|
42
51
|
createText,
|
|
52
|
+
toggleLabel,
|
|
43
53
|
maxItems = Number.POSITIVE_INFINITY,
|
|
44
54
|
onEnter
|
|
45
55
|
}: SearchableModelInputProps) {
|
|
@@ -77,97 +87,102 @@ export function SearchableModelInput({
|
|
|
77
87
|
}, [normalizedOptions, query, maxItems]);
|
|
78
88
|
|
|
79
89
|
const hasExactMatch = value.trim().length > 0 && normalizedOptions.some((option) => option === value.trim());
|
|
90
|
+
const closeMenu = () => setOpen(false);
|
|
80
91
|
|
|
81
92
|
return (
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
setTimeout(() => setOpen(false), 120);
|
|
86
|
-
}}
|
|
93
|
+
<Popover
|
|
94
|
+
open={open && !disabled}
|
|
95
|
+
onOpenChange={(nextOpen) => setOpen(!disabled && nextOpen)}
|
|
87
96
|
>
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
event.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
>
|
|
126
|
-
<ChevronsUpDown className="h-4 w-4" />
|
|
127
|
-
</button>
|
|
128
|
-
|
|
129
|
-
{open && !disabled && (
|
|
130
|
-
<div className="absolute z-20 mt-1 w-full overflow-hidden rounded-xl border border-gray-200 bg-white shadow-lg">
|
|
131
|
-
<div className="max-h-60 overflow-y-auto py-1">
|
|
132
|
-
{!hasExactMatch && value.trim().length > 0 && (
|
|
133
|
-
<button
|
|
134
|
-
type="button"
|
|
135
|
-
onMouseDown={(event) => event.preventDefault()}
|
|
136
|
-
onClick={() => {
|
|
137
|
-
onChange(value.trim());
|
|
138
|
-
setOpen(false);
|
|
139
|
-
}}
|
|
140
|
-
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-gray-50"
|
|
141
|
-
>
|
|
142
|
-
<Check className="h-4 w-4 text-transparent" />
|
|
143
|
-
<span className="truncate text-gray-700">
|
|
144
|
-
{createText ? createText.replace('{value}', value.trim()) : value.trim()}
|
|
145
|
-
</span>
|
|
146
|
-
</button>
|
|
97
|
+
<PopoverAnchor asChild>
|
|
98
|
+
<div className={cn('relative', className)}>
|
|
99
|
+
<Input
|
|
100
|
+
id={id}
|
|
101
|
+
value={value}
|
|
102
|
+
disabled={disabled}
|
|
103
|
+
onFocus={() => {
|
|
104
|
+
if (!disabled) {
|
|
105
|
+
setOpen(true);
|
|
106
|
+
}
|
|
107
|
+
}}
|
|
108
|
+
onChange={(event) => {
|
|
109
|
+
onChange(event.target.value);
|
|
110
|
+
if (!open && !disabled) {
|
|
111
|
+
setOpen(true);
|
|
112
|
+
}
|
|
113
|
+
}}
|
|
114
|
+
onKeyDown={(event) => {
|
|
115
|
+
if (event.key === 'Enter') {
|
|
116
|
+
if (onEnter) {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
onEnter();
|
|
119
|
+
}
|
|
120
|
+
closeMenu();
|
|
121
|
+
}
|
|
122
|
+
}}
|
|
123
|
+
placeholder={placeholder}
|
|
124
|
+
className={cn('pr-10', inputClassName)}
|
|
125
|
+
/>
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
129
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
130
|
+
disabled={disabled}
|
|
131
|
+
className={cn(
|
|
132
|
+
'absolute inset-y-0 right-0 inline-flex w-10 items-center justify-center',
|
|
133
|
+
disabled ? 'cursor-not-allowed text-gray-300' : 'text-gray-400 hover:text-gray-600'
|
|
147
134
|
)}
|
|
135
|
+
aria-label={toggleLabel}
|
|
136
|
+
>
|
|
137
|
+
<ChevronsUpDown className="h-4 w-4" />
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
</PopoverAnchor>
|
|
141
|
+
<PopoverContent
|
|
142
|
+
align="start"
|
|
143
|
+
className="flex w-[var(--radix-popover-trigger-width)] min-w-[12rem] flex-col overflow-hidden rounded-xl border border-gray-200 p-0 shadow-lg"
|
|
144
|
+
onOpenAutoFocus={(event) => event.preventDefault()}
|
|
145
|
+
style={{ maxHeight: SEARCHABLE_MODEL_MENU_MAX_HEIGHT }}
|
|
146
|
+
>
|
|
147
|
+
<div className="custom-scrollbar min-h-0 flex-1 overflow-y-auto py-1">
|
|
148
|
+
{!hasExactMatch && value.trim().length > 0 && (
|
|
149
|
+
<button
|
|
150
|
+
type="button"
|
|
151
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
152
|
+
onClick={() => {
|
|
153
|
+
onChange(value.trim());
|
|
154
|
+
closeMenu();
|
|
155
|
+
}}
|
|
156
|
+
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-gray-50"
|
|
157
|
+
>
|
|
158
|
+
<Check className="h-4 w-4 text-transparent" />
|
|
159
|
+
<span className="truncate text-gray-700">
|
|
160
|
+
{createText.replace('{value}', value.trim())}
|
|
161
|
+
</span>
|
|
162
|
+
</button>
|
|
163
|
+
)}
|
|
148
164
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
{orderedOptions.map((option) => (
|
|
166
|
+
<button
|
|
167
|
+
key={option}
|
|
168
|
+
type="button"
|
|
169
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
170
|
+
onClick={() => {
|
|
171
|
+
onChange(option);
|
|
172
|
+
closeMenu();
|
|
173
|
+
}}
|
|
174
|
+
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-gray-50"
|
|
175
|
+
>
|
|
176
|
+
<Check className={cn('h-4 w-4', option === value.trim() ? 'text-primary' : 'text-transparent')} />
|
|
177
|
+
<span className="truncate text-gray-700">{option}</span>
|
|
178
|
+
</button>
|
|
179
|
+
))}
|
|
164
180
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
</div>
|
|
181
|
+
{orderedOptions.length === 0 && value.trim().length === 0 && (
|
|
182
|
+
<div className="px-3 py-2 text-sm text-gray-500">{emptyText}</div>
|
|
183
|
+
)}
|
|
169
184
|
</div>
|
|
170
|
-
|
|
171
|
-
</
|
|
185
|
+
</PopoverContent>
|
|
186
|
+
</Popover>
|
|
172
187
|
);
|
|
173
188
|
}
|
|
@@ -3,6 +3,14 @@ import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
|
3
3
|
|
|
4
4
|
import { cn } from '@/shared/lib/utils';
|
|
5
5
|
|
|
6
|
+
const FLOATING_CONTENT_AVAILABLE_HEIGHT_GAP = '2rem';
|
|
7
|
+
|
|
8
|
+
function createPopoverAvailableHeightLimit(limit: string): string {
|
|
9
|
+
return `min(${limit}, max(0px, calc(var(--radix-popover-content-available-height, 100vh) - ${FLOATING_CONTENT_AVAILABLE_HEIGHT_GAP})))`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const POPOVER_CONTENT_MAX_HEIGHT = createPopoverAvailableHeightLimit('24rem');
|
|
13
|
+
|
|
6
14
|
const Popover = PopoverPrimitive.Root;
|
|
7
15
|
|
|
8
16
|
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
@@ -12,20 +20,28 @@ const PopoverAnchor = PopoverPrimitive.Anchor;
|
|
|
12
20
|
const PopoverContent = React.forwardRef<
|
|
13
21
|
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
14
22
|
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
|
15
|
-
>(({ className, sideOffset = 8, align = 'start', ...props }, ref) => (
|
|
23
|
+
>(({ className, sideOffset = 8, align = 'start', collisionPadding = 12, style, ...props }, ref) => (
|
|
16
24
|
<PopoverPrimitive.Portal>
|
|
17
25
|
<PopoverPrimitive.Content
|
|
18
26
|
ref={ref}
|
|
19
27
|
sideOffset={sideOffset}
|
|
20
28
|
align={align}
|
|
29
|
+
collisionPadding={collisionPadding}
|
|
21
30
|
className={cn(
|
|
22
|
-
'z-[var(--z-popover,50)] w-72 overflow-hidden rounded-2xl border border-gray-200/50 bg-white p-4 shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
31
|
+
'z-[var(--z-popover,50)] w-72 overflow-x-hidden overflow-y-auto rounded-2xl border border-gray-200/50 bg-white p-4 shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
23
32
|
className
|
|
24
33
|
)}
|
|
34
|
+
style={{ maxHeight: POPOVER_CONTENT_MAX_HEIGHT, ...style }}
|
|
25
35
|
{...props}
|
|
26
36
|
/>
|
|
27
37
|
</PopoverPrimitive.Portal>
|
|
28
38
|
));
|
|
29
39
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
30
40
|
|
|
31
|
-
export {
|
|
41
|
+
export {
|
|
42
|
+
Popover,
|
|
43
|
+
PopoverTrigger,
|
|
44
|
+
PopoverContent,
|
|
45
|
+
PopoverAnchor,
|
|
46
|
+
createPopoverAvailableHeightLimit,
|
|
47
|
+
};
|
|
@@ -6,6 +6,13 @@ import { cn } from "@/shared/lib/utils"
|
|
|
6
6
|
const Select = SelectPrimitive.Root
|
|
7
7
|
const SelectGroup = SelectPrimitive.Group
|
|
8
8
|
const SelectValue = SelectPrimitive.Value
|
|
9
|
+
const SELECT_CONTENT_AVAILABLE_HEIGHT_GAP = "2rem"
|
|
10
|
+
|
|
11
|
+
function createSelectAvailableHeightLimit(limit: string): string {
|
|
12
|
+
return `min(${limit}, max(0px, calc(var(--radix-select-content-available-height, 100vh) - ${SELECT_CONTENT_AVAILABLE_HEIGHT_GAP})))`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const SELECT_CONTENT_MAX_HEIGHT = createSelectAvailableHeightLimit("24rem")
|
|
9
16
|
|
|
10
17
|
type SelectTriggerProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
|
|
11
18
|
indicator?: React.ReactNode
|
|
@@ -66,24 +73,26 @@ type SelectContentProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.
|
|
|
66
73
|
const SelectContent = React.forwardRef<
|
|
67
74
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
68
75
|
SelectContentProps
|
|
69
|
-
>(({ className, children, position = "popper", viewportClassName, ...props }, ref) => (
|
|
76
|
+
>(({ className, children, collisionPadding = 12, position = "popper", style, viewportClassName, ...props }, ref) => (
|
|
70
77
|
<SelectPrimitive.Portal>
|
|
71
78
|
<SelectPrimitive.Content
|
|
72
79
|
ref={ref}
|
|
73
80
|
className={cn(
|
|
74
|
-
"relative z-[var(--z-popover,50)] max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-white",
|
|
81
|
+
"relative z-[var(--z-popover,50)] flex max-h-96 min-w-[8rem] flex-col overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-white",
|
|
75
82
|
position === "popper" &&
|
|
76
83
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
77
84
|
className
|
|
78
85
|
)}
|
|
86
|
+
collisionPadding={collisionPadding}
|
|
79
87
|
position={position}
|
|
88
|
+
style={{ maxHeight: SELECT_CONTENT_MAX_HEIGHT, ...style }}
|
|
80
89
|
{...props}
|
|
81
90
|
>
|
|
82
91
|
<SelectScrollUpButton />
|
|
83
92
|
<SelectPrimitive.Viewport
|
|
84
93
|
className={cn(
|
|
85
|
-
"p-1",
|
|
86
|
-
position === "popper" && "
|
|
94
|
+
"min-h-0 flex-1 overflow-y-auto overscroll-contain p-1",
|
|
95
|
+
position === "popper" && "w-full min-w-[var(--radix-select-trigger-width)]",
|
|
87
96
|
viewportClassName
|
|
88
97
|
)}
|
|
89
98
|
>
|
|
@@ -144,4 +153,5 @@ export {
|
|
|
144
153
|
SelectSeparator,
|
|
145
154
|
SelectScrollUpButton,
|
|
146
155
|
SelectScrollDownButton,
|
|
156
|
+
createSelectAvailableHeightLimit,
|
|
147
157
|
}
|
|
@@ -9,33 +9,37 @@ type UseInfiniteScrollLoaderParams = {
|
|
|
9
9
|
watchValue?: string | number;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
export function useInfiniteScrollLoader(
|
|
12
|
+
export function useInfiniteScrollLoader({
|
|
13
|
+
disabled,
|
|
14
|
+
onLoadMore,
|
|
15
|
+
thresholdPx = DEFAULT_THRESHOLD_PX,
|
|
16
|
+
watchValue
|
|
17
|
+
}: UseInfiniteScrollLoaderParams) {
|
|
13
18
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
14
19
|
const sentinelRef = useRef<HTMLDivElement | null>(null);
|
|
15
|
-
const onLoadMoreRef = useRef(
|
|
20
|
+
const onLoadMoreRef = useRef(onLoadMore);
|
|
16
21
|
const loadingRef = useRef(false);
|
|
17
22
|
|
|
18
23
|
useEffect(() => {
|
|
19
|
-
onLoadMoreRef.current =
|
|
20
|
-
}, [
|
|
24
|
+
onLoadMoreRef.current = onLoadMore;
|
|
25
|
+
}, [onLoadMore]);
|
|
21
26
|
|
|
22
27
|
useEffect(() => {
|
|
23
|
-
if (
|
|
28
|
+
if (disabled) {
|
|
24
29
|
loadingRef.current = false;
|
|
25
30
|
}
|
|
26
|
-
}, [
|
|
31
|
+
}, [disabled]);
|
|
27
32
|
|
|
28
33
|
useEffect(() => {
|
|
29
34
|
const container = containerRef.current;
|
|
30
35
|
const sentinel = sentinelRef.current;
|
|
31
|
-
const thresholdPx = params.thresholdPx ?? DEFAULT_THRESHOLD_PX;
|
|
32
36
|
|
|
33
|
-
if (
|
|
37
|
+
if (disabled || !container || !sentinel) {
|
|
34
38
|
return;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
const triggerLoadMore = () => {
|
|
38
|
-
if (loadingRef.current ||
|
|
42
|
+
if (loadingRef.current || disabled) {
|
|
39
43
|
return;
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -79,7 +83,7 @@ export function useInfiniteScrollLoader(params: UseInfiniteScrollLoaderParams) {
|
|
|
79
83
|
return () => {
|
|
80
84
|
container.removeEventListener('scroll', maybeLoadMore);
|
|
81
85
|
};
|
|
82
|
-
}, [
|
|
86
|
+
}, [disabled, thresholdPx, watchValue]);
|
|
83
87
|
|
|
84
88
|
return {
|
|
85
89
|
containerRef,
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { RuntimeModelSelectionMode } from '@nextclaw/shared';
|
|
2
|
+
import type { ThinkingLevel } from './types';
|
|
3
|
+
|
|
4
|
+
export type RuntimeDefaultThinkingView = {
|
|
5
|
+
supported: ThinkingLevel[];
|
|
6
|
+
default?: ThinkingLevel | null;
|
|
7
|
+
};
|
|
2
8
|
|
|
3
9
|
export type ChatSessionTypeIconView = {
|
|
4
10
|
kind: "image";
|
|
@@ -22,6 +28,7 @@ export type ChatSessionTypeOptionView = {
|
|
|
22
28
|
supportedModels?: string[];
|
|
23
29
|
recommendedModel?: string | null;
|
|
24
30
|
modelSelectionMode?: RuntimeModelSelectionMode;
|
|
31
|
+
runtimeDefaultThinking?: RuntimeDefaultThinkingView | null;
|
|
25
32
|
cta?: ChatSessionTypeCtaView | null;
|
|
26
33
|
};
|
|
27
34
|
|
|
@@ -10,6 +10,8 @@ export * from './utils/marketplace.utils';
|
|
|
10
10
|
export * from './utils/mcp-marketplace.utils';
|
|
11
11
|
export * from './utils/ncp-attachments.utils';
|
|
12
12
|
export * from './utils/ncp-session.utils';
|
|
13
|
+
export * from './preferences/preference.utils';
|
|
14
|
+
export * from './preferences/preference.types';
|
|
13
15
|
export * from './ncp-session.types';
|
|
14
16
|
export * from './ncp-session-query-cache.utils';
|
|
15
17
|
export * from './raw-client.utils';
|