@vibe-forge/client 2.0.1 → 3.0.0-alpha.0
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/dist/assets/{arc-CqviK3HX.js → arc-1JbypnRY.js} +1 -1
- package/dist/assets/{blockDiagram-c4efeb88-BEp50UHp.js → blockDiagram-c4efeb88-jT5b9nId.js} +1 -1
- package/dist/assets/{c4Diagram-c83219d4-C5w55JzM.js → c4Diagram-c83219d4-KNPh-1ta.js} +1 -1
- package/dist/assets/channel-pzvjJnfd.js +1 -0
- package/dist/assets/{classDiagram-beda092f-CQJVtHEy.js → classDiagram-beda092f-vvXuOT3F.js} +1 -1
- package/dist/assets/{classDiagram-v2-2358418a-B37Xl9jB.js → classDiagram-v2-2358418a-vH4j7skr.js} +1 -1
- package/dist/assets/clone-CtWwIeUb.js +1 -0
- package/dist/assets/{createText-1719965b-9YwvWMdV.js → createText-1719965b-COeSYnf3.js} +1 -1
- package/dist/assets/{cssMode-BX88r5f4.js → cssMode-Doc7QRKn.js} +1 -1
- package/dist/assets/{edges-96097737-CNHoXVrD.js → edges-96097737-D09v6R_r.js} +1 -1
- package/dist/assets/{erDiagram-0228fc6a-BoYldy0g.js → erDiagram-0228fc6a-CbZ6rfxl.js} +1 -1
- package/dist/assets/{flowDb-c6c81e3f-CoPw_R-Q.js → flowDb-c6c81e3f-CnZOzhdk.js} +1 -1
- package/dist/assets/{flowDiagram-50d868cf-nCqbSXd-.js → flowDiagram-50d868cf-jv-xWM2p.js} +1 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-BiyrkIKs.js +1 -0
- package/dist/assets/{flowchart-elk-definition-6af322e1-BwMuPTrV.js → flowchart-elk-definition-6af322e1-D8pf677G.js} +1 -1
- package/dist/assets/{freemarker2-DUFDSvgj.js → freemarker2-CL0o23Gj.js} +1 -1
- package/dist/assets/{ganttDiagram-a2739b55-CLNH3S_C.js → ganttDiagram-a2739b55-BAPQzC4u.js} +1 -1
- package/dist/assets/{gitGraphDiagram-82fe8481-uDu1ectX.js → gitGraphDiagram-82fe8481-BI2x71m0.js} +1 -1
- package/dist/assets/{graph-DuC4kt4I.js → graph-CZK4Bjpq.js} +1 -1
- package/dist/assets/{handlebars-BSd4a6l9.js → handlebars-CCG38Pg6.js} +1 -1
- package/dist/assets/{html-H48gEjQd.js → html-BddshTWG.js} +1 -1
- package/dist/assets/{htmlMode-Nqw7-Nqh.js → htmlMode-xKXiYcDz.js} +1 -1
- package/dist/assets/{index-5325376f-rnz0GXAT.js → index-5325376f-BWMTD8RU.js} +1 -1
- package/dist/assets/{index-DeQLT67a.js → index-CBe7kDkV.js} +322 -322
- package/dist/assets/{index-DiOCtPLP.css → index-MWOwVzqE.css} +1 -1
- package/dist/assets/{infoDiagram-8eee0895-BsGB550b.js → infoDiagram-8eee0895-CR78btIF.js} +1 -1
- package/dist/assets/{javascript-0g2herYV.js → javascript-BPlRHPjg.js} +1 -1
- package/dist/assets/{journeyDiagram-c64418c1-DLldlz0H.js → journeyDiagram-c64418c1-DZRv6FKz.js} +1 -1
- package/dist/assets/{jsonMode-CN5ZURMh.js → jsonMode-BYLVfdkf.js} +1 -1
- package/dist/assets/{layout-QKUiDNJK.js → layout-BtudyPU2.js} +1 -1
- package/dist/assets/{line-CeP3XWjD.js → line-DgHXrIhS.js} +1 -1
- package/dist/assets/{linear-74cQVgWT.js → linear-DtBoKICx.js} +1 -1
- package/dist/assets/{liquid-B6cRrfrb.js → liquid-DxBlJk0W.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-C5ogOh5E.js → lspLanguageFeatures-DmKVpmeH.js} +1 -1
- package/dist/assets/{mdx-BBIy-KRj.js → mdx-DrScsd-w.js} +1 -1
- package/dist/assets/{mermaid.core-BhdbV0mr.js → mermaid.core-Cj_NJ_lZ.js} +4 -4
- package/dist/assets/{mindmap-definition-8da855dc-B67VKJuD.js → mindmap-definition-8da855dc-CMXbwtsc.js} +1 -1
- package/dist/assets/{pieDiagram-a8764435-Cxv9WY_E.js → pieDiagram-a8764435-Bd6rfpJv.js} +1 -1
- package/dist/assets/{python-CBdGo8__.js → python-Crbi7B7n.js} +1 -1
- package/dist/assets/{quadrantDiagram-1e28029f-BTkj65P_.js → quadrantDiagram-1e28029f-H-FdBQn6.js} +1 -1
- package/dist/assets/{razor-azKH0Dwj.js → razor-DHyrzIfE.js} +1 -1
- package/dist/assets/{requirementDiagram-08caed73-D4jVXpOT.js → requirementDiagram-08caed73-BSyCVDSG.js} +1 -1
- package/dist/assets/{sankeyDiagram-a04cb91d-CXhutIA1.js → sankeyDiagram-a04cb91d-S6E6BReg.js} +1 -1
- package/dist/assets/{sequenceDiagram-c5b8d532-B56TTZlx.js → sequenceDiagram-c5b8d532-BMHY4KMj.js} +1 -1
- package/dist/assets/{stateDiagram-1ecb1508-Cs0plMcS.js → stateDiagram-1ecb1508-BMrMw6XR.js} +1 -1
- package/dist/assets/{stateDiagram-v2-c2b004d7-LSJaXPJN.js → stateDiagram-v2-c2b004d7-B5SIFUb_.js} +1 -1
- package/dist/assets/{styles-b4e223ce-UdXfHMuu.js → styles-b4e223ce-DqiXwnx3.js} +1 -1
- package/dist/assets/{styles-ca3715f6-EuRy_hTu.js → styles-ca3715f6-jNxW2Db8.js} +1 -1
- package/dist/assets/{styles-d45a18b0-B24zVoK3.js → styles-d45a18b0-C4nlfLcZ.js} +1 -1
- package/dist/assets/{svgDrawCommon-b86b1483-B2S0NW3K.js → svgDrawCommon-b86b1483-FIWFuZfb.js} +1 -1
- package/dist/assets/{timeline-definition-faaaa080-DFWKh9mU.js → timeline-definition-faaaa080-CEQDoqdf.js} +1 -1
- package/dist/assets/{tsMode-FZsHWiOn.js → tsMode-DnNy3rE9.js} +1 -1
- package/dist/assets/{typescript-CYdJ3s3D.js → typescript-YBZ4vw5B.js} +1 -1
- package/dist/assets/{xml-C16X_hpZ.js → xml-BJGGYD70.js} +1 -1
- package/dist/assets/{xychartDiagram-f5964ef8-DyBiBYci.js → xychartDiagram-f5964ef8-DUNOFOk0.js} +1 -1
- package/dist/assets/{yaml-CRjA4-Rj.js → yaml-BMY4mu5s.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +13 -13
- package/src/components/ConfigView.scss +7 -6
- package/src/components/ConfigView.tsx +2 -1
- package/src/components/Sidebar.tsx +1 -0
- package/src/components/automation-view/@components/AutomationTaskComposer.tsx +10 -8
- package/src/components/automation-view/AutomationEmptyLanding.tsx +12 -0
- package/src/components/automation-view/TaskList.scss +5 -1
- package/src/components/chat/ChatHistoryView.tsx +5 -3
- package/src/components/chat/NewSessionGuide.tsx +16 -10
- package/src/components/chat/NewSessionGuideStarterList.tsx +4 -1
- package/src/components/chat/NewSessionGuideStarterSection.tsx +2 -1
- package/src/components/chat/messages/MessageItem.tsx +19 -5
- package/src/components/chat/messages/message-action-utils.ts +11 -0
- package/src/components/chat/sender/Sender.scss +12 -0
- package/src/components/config/AppSettingsPanel.tsx +84 -30
- package/src/components/knowledge-base/components/@hooks/use-skills-cli-modal-controller.ts +157 -0
- package/src/components/knowledge-base/components/@hooks/use-skills-tab-actions.ts +63 -0
- package/src/components/knowledge-base/components/SkillsTab.tsx +40 -193
- package/src/components/sidebar/SidebarHeader.tsx +44 -26
- package/src/components/sidebar/sidebar-search-visibility.ts +18 -0
- package/src/hooks/useQueryParams.ts +91 -23
- package/src/resources/locales/en.json +12 -0
- package/src/resources/locales/zh.json +12 -0
- package/src/routes/ChatRoute.scss +50 -45
- package/src/store/index.ts +111 -3
- package/dist/assets/channel-C6LTxxLg.js +0 -1
- package/dist/assets/clone-CG6ZcokX.js +0 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-CSZTI7GQ.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import './SkillsTab.scss'
|
|
2
2
|
|
|
3
|
-
import { App
|
|
3
|
+
import { App } from 'antd'
|
|
4
4
|
import React from 'react'
|
|
5
5
|
import type { ReactNode } from 'react'
|
|
6
6
|
import { useTranslation } from 'react-i18next'
|
|
@@ -9,22 +9,15 @@ import useSWR from 'swr'
|
|
|
9
9
|
|
|
10
10
|
import type { ConfigResponse } from '@vibe-forge/types'
|
|
11
11
|
|
|
12
|
-
import type {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
importSkillArchive,
|
|
17
|
-
installSkillHubItem,
|
|
18
|
-
installSkillsCliItem,
|
|
19
|
-
listSkills,
|
|
20
|
-
searchSkillsCli
|
|
21
|
-
} from '#~/api.js'
|
|
12
|
+
import type { SkillSummary } from '#~/api.js'
|
|
13
|
+
import { getConfig, listSkills } from '#~/api.js'
|
|
14
|
+
import { useSkillsCliModalController } from './@hooks/use-skills-cli-modal-controller'
|
|
15
|
+
import { useSkillsTabActions } from './@hooks/use-skills-tab-actions'
|
|
22
16
|
import { ProjectSkillsList } from './ProjectSkillsList'
|
|
23
17
|
import { SkillArchiveInput } from './SkillArchiveInput'
|
|
24
18
|
import { SkillMarketView } from './SkillMarketView'
|
|
25
19
|
import { SkillRegistryModal } from './SkillRegistryModal'
|
|
26
20
|
import { SkillsCliModal } from './SkillsCliModal'
|
|
27
|
-
import type { SkillsCliFormValues } from './SkillsCliModal'
|
|
28
21
|
import { SkillsTabActions } from './SkillsTabActions'
|
|
29
22
|
import { TabContent } from './TabContent'
|
|
30
23
|
import { ALL_REGISTRIES, filterProjectSkills } from './skill-hub-utils'
|
|
@@ -33,15 +26,6 @@ import { useSkillMarketFilters } from './use-skill-market-filters'
|
|
|
33
26
|
import { useSkillMarketSearch } from './use-skill-market-search'
|
|
34
27
|
import { useSkillRegistryModal } from './use-skill-registry-modal'
|
|
35
28
|
|
|
36
|
-
const SKILLS_CLI_INITIAL_LIMIT = 100
|
|
37
|
-
const SKILLS_CLI_LIMIT_STEP = 100
|
|
38
|
-
const SKILLS_CLI_MAX_LIMIT = 500
|
|
39
|
-
|
|
40
|
-
const trimOptionalString = (value: string | undefined) => {
|
|
41
|
-
const normalizedValue = value?.trim()
|
|
42
|
-
return normalizedValue == null || normalizedValue === '' ? undefined : normalizedValue
|
|
43
|
-
}
|
|
44
|
-
|
|
45
29
|
interface SkillsTabProps {
|
|
46
30
|
leading?: ReactNode
|
|
47
31
|
installFilter: SkillHubInstallFilter
|
|
@@ -82,20 +66,6 @@ export function SkillsTab({
|
|
|
82
66
|
const { t } = useTranslation()
|
|
83
67
|
const { message } = App.useApp()
|
|
84
68
|
const navigate = useNavigate()
|
|
85
|
-
const importInputRef = React.useRef<HTMLInputElement | null>(null)
|
|
86
|
-
const [installingId, setInstallingId] = React.useState<string | null>(null)
|
|
87
|
-
const [importing, setImporting] = React.useState(false)
|
|
88
|
-
const [skillsCliOpen, setSkillsCliOpen] = React.useState(false)
|
|
89
|
-
const [skillsCliSearching, setSkillsCliSearching] = React.useState(false)
|
|
90
|
-
const [skillsCliLoadingMore, setSkillsCliLoadingMore] = React.useState(false)
|
|
91
|
-
const [skillsCliInstallingId, setSkillsCliInstallingId] = React.useState<string | null>(null)
|
|
92
|
-
const [skillsCliItems, setSkillsCliItems] = React.useState<SkillHubItem[]>([])
|
|
93
|
-
const [skillsCliError, setSkillsCliError] = React.useState<string | null>(null)
|
|
94
|
-
const [skillsCliHasMore, setSkillsCliHasMore] = React.useState(false)
|
|
95
|
-
const [skillsCliHasSearched, setSkillsCliHasSearched] = React.useState(false)
|
|
96
|
-
const [skillsCliLimit, setSkillsCliLimit] = React.useState(SKILLS_CLI_INITIAL_LIMIT)
|
|
97
|
-
const [skillsCliResetKey, setSkillsCliResetKey] = React.useState('')
|
|
98
|
-
const [skillsCliForm] = Form.useForm<SkillsCliFormValues>()
|
|
99
69
|
const {
|
|
100
70
|
data: skillsRes,
|
|
101
71
|
isLoading: isSkillsLoading,
|
|
@@ -109,6 +79,19 @@ export function SkillsTab({
|
|
|
109
79
|
mutateHub: marketSearch.mutate,
|
|
110
80
|
setRegistry: onRegistryChange
|
|
111
81
|
})
|
|
82
|
+
const actions = useSkillsTabActions({
|
|
83
|
+
marketMutate: marketSearch.mutate,
|
|
84
|
+
message,
|
|
85
|
+
mutateConfig,
|
|
86
|
+
mutateSkills,
|
|
87
|
+
onRefresh,
|
|
88
|
+
t
|
|
89
|
+
})
|
|
90
|
+
const skillsCliModal = useSkillsCliModalController({
|
|
91
|
+
message,
|
|
92
|
+
mutateSkills,
|
|
93
|
+
t
|
|
94
|
+
})
|
|
112
95
|
|
|
113
96
|
const skills = skillsRes?.skills ?? []
|
|
114
97
|
const registries = marketSearch.data?.registries ?? []
|
|
@@ -124,156 +107,20 @@ export function SkillsTab({
|
|
|
124
107
|
...registries.map(item => ({ label: item.id, value: item.id }))
|
|
125
108
|
], [registries, t])
|
|
126
109
|
|
|
127
|
-
const handleRefresh = async () => {
|
|
128
|
-
await Promise.all([mutateSkills(), marketSearch.mutate(), mutateConfig(), onRefresh()])
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const handleInstall = async (item: SkillHubItem) => {
|
|
132
|
-
setInstallingId(item.id)
|
|
133
|
-
try {
|
|
134
|
-
await installSkillHubItem({
|
|
135
|
-
registry: item.registry,
|
|
136
|
-
plugin: item.installRef ?? item.name,
|
|
137
|
-
force: item.installed
|
|
138
|
-
})
|
|
139
|
-
await Promise.all([marketSearch.mutate(), mutateSkills()])
|
|
140
|
-
void message.success(t('knowledge.skills.installSuccess'))
|
|
141
|
-
} catch (error) {
|
|
142
|
-
void message.error(getApiErrorMessage(error, t('knowledge.skills.installFailed')))
|
|
143
|
-
} finally {
|
|
144
|
-
setInstallingId(null)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const handleImportArchive = async (file: File) => {
|
|
149
|
-
setImporting(true)
|
|
150
|
-
try {
|
|
151
|
-
const result = await importSkillArchive(file)
|
|
152
|
-
await Promise.all([mutateSkills(), onRefresh()])
|
|
153
|
-
void message.success(t('knowledge.skills.importSuccess', { count: result.fileCount }))
|
|
154
|
-
} catch (error) {
|
|
155
|
-
void message.error(getApiErrorMessage(error, t('knowledge.skills.importFailed')))
|
|
156
|
-
} finally {
|
|
157
|
-
setImporting(false)
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const resolveSkillsCliRequest = React.useCallback(async () => {
|
|
162
|
-
try {
|
|
163
|
-
await skillsCliForm.validateFields(['source'])
|
|
164
|
-
} catch {
|
|
165
|
-
return undefined
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const values = skillsCliForm.getFieldsValue()
|
|
169
|
-
const source = values.source.trim()
|
|
170
|
-
return {
|
|
171
|
-
source,
|
|
172
|
-
query: trimOptionalString(values.query),
|
|
173
|
-
registry: trimOptionalString(values.registry)
|
|
174
|
-
}
|
|
175
|
-
}, [skillsCliForm])
|
|
176
|
-
|
|
177
|
-
const runSkillsCliSearch = React.useCallback(async (nextLimit: number) => {
|
|
178
|
-
const request = await resolveSkillsCliRequest()
|
|
179
|
-
if (request == null) return false
|
|
180
|
-
|
|
181
|
-
const result = await searchSkillsCli({
|
|
182
|
-
limit: nextLimit,
|
|
183
|
-
source: request.source,
|
|
184
|
-
...(request.query != null ? { query: request.query } : {}),
|
|
185
|
-
...(request.registry != null ? { registry: request.registry } : {})
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
setSkillsCliHasSearched(true)
|
|
189
|
-
setSkillsCliLimit(nextLimit)
|
|
190
|
-
setSkillsCliItems(result.items)
|
|
191
|
-
setSkillsCliError(result.error ?? null)
|
|
192
|
-
setSkillsCliHasMore(result.hasMore === true && nextLimit < SKILLS_CLI_MAX_LIMIT)
|
|
193
|
-
setSkillsCliResetKey([request.source, request.query ?? '', request.registry ?? '', String(nextLimit)].join('\0'))
|
|
194
|
-
return true
|
|
195
|
-
}, [resolveSkillsCliRequest])
|
|
196
|
-
|
|
197
|
-
const handleSearchSkillsCli = async () => {
|
|
198
|
-
setSkillsCliSearching(true)
|
|
199
|
-
setSkillsCliLoadingMore(false)
|
|
200
|
-
try {
|
|
201
|
-
await runSkillsCliSearch(SKILLS_CLI_INITIAL_LIMIT)
|
|
202
|
-
} catch (error) {
|
|
203
|
-
void message.error(getApiErrorMessage(error, t('knowledge.skills.skillsCliSearchFailed')))
|
|
204
|
-
} finally {
|
|
205
|
-
setSkillsCliSearching(false)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const handleLoadMoreSkillsCli = async () => {
|
|
210
|
-
const nextLimit = Math.min(skillsCliLimit + SKILLS_CLI_LIMIT_STEP, SKILLS_CLI_MAX_LIMIT)
|
|
211
|
-
if (nextLimit === skillsCliLimit) return
|
|
212
|
-
|
|
213
|
-
setSkillsCliLoadingMore(true)
|
|
214
|
-
try {
|
|
215
|
-
await runSkillsCliSearch(nextLimit)
|
|
216
|
-
} catch (error) {
|
|
217
|
-
void message.error(getApiErrorMessage(error, t('knowledge.skills.skillsCliSearchFailed')))
|
|
218
|
-
} finally {
|
|
219
|
-
setSkillsCliLoadingMore(false)
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const handleInstallSkillsCli = async (item: SkillHubItem) => {
|
|
224
|
-
const request = await resolveSkillsCliRequest()
|
|
225
|
-
if (request == null) return
|
|
226
|
-
|
|
227
|
-
setSkillsCliInstallingId(item.id)
|
|
228
|
-
try {
|
|
229
|
-
await installSkillsCliItem({
|
|
230
|
-
source: request.source,
|
|
231
|
-
skill: item.installRef ?? item.name,
|
|
232
|
-
force: item.installed,
|
|
233
|
-
...(request.registry != null ? { registry: request.registry } : {})
|
|
234
|
-
})
|
|
235
|
-
await mutateSkills()
|
|
236
|
-
try {
|
|
237
|
-
await runSkillsCliSearch(skillsCliLimit)
|
|
238
|
-
} catch {
|
|
239
|
-
// Keep the install success state even if refreshing the source list fails afterwards.
|
|
240
|
-
}
|
|
241
|
-
void message.success(t('knowledge.skills.installSuccess'))
|
|
242
|
-
} catch (error) {
|
|
243
|
-
void message.error(getApiErrorMessage(error, t('knowledge.skills.installFailed')))
|
|
244
|
-
} finally {
|
|
245
|
-
setSkillsCliInstallingId(null)
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const handleCloseSkillsCli = React.useCallback(() => {
|
|
250
|
-
setSkillsCliOpen(false)
|
|
251
|
-
setSkillsCliSearching(false)
|
|
252
|
-
setSkillsCliLoadingMore(false)
|
|
253
|
-
setSkillsCliInstallingId(null)
|
|
254
|
-
setSkillsCliItems([])
|
|
255
|
-
setSkillsCliError(null)
|
|
256
|
-
setSkillsCliHasMore(false)
|
|
257
|
-
setSkillsCliHasSearched(false)
|
|
258
|
-
setSkillsCliLimit(SKILLS_CLI_INITIAL_LIMIT)
|
|
259
|
-
setSkillsCliResetKey('')
|
|
260
|
-
skillsCliForm.resetFields()
|
|
261
|
-
}, [skillsCliForm])
|
|
262
|
-
|
|
263
110
|
return (
|
|
264
111
|
<TabContent className='knowledge-base-view__skills-tab'>
|
|
265
112
|
<SkillsTabActions
|
|
266
|
-
importing={importing}
|
|
113
|
+
importing={actions.importing}
|
|
267
114
|
leading={leading}
|
|
268
115
|
viewMode={viewMode}
|
|
269
|
-
onRefresh={() => void handleRefresh()}
|
|
270
|
-
onImport={() => importInputRef.current?.click()}
|
|
116
|
+
onRefresh={() => void actions.handleRefresh()}
|
|
117
|
+
onImport={() => actions.importInputRef.current?.click()}
|
|
271
118
|
onOpenConfig={() => navigate('/config?tab=plugins')}
|
|
272
119
|
onViewModeChange={onViewModeChange}
|
|
273
120
|
/>
|
|
274
121
|
<SkillArchiveInput
|
|
275
|
-
inputRef={importInputRef}
|
|
276
|
-
onSelect={(file) => void handleImportArchive(file)}
|
|
122
|
+
inputRef={actions.importInputRef}
|
|
123
|
+
onSelect={(file) => void actions.handleImportArchive(file)}
|
|
277
124
|
/>
|
|
278
125
|
{viewMode === 'project' && (
|
|
279
126
|
<ProjectSkillsList
|
|
@@ -286,7 +133,7 @@ export function SkillsTab({
|
|
|
286
133
|
{viewMode === 'market' && (
|
|
287
134
|
<SkillMarketView
|
|
288
135
|
hubItems={marketFilters.filteredHubItems}
|
|
289
|
-
installingId={installingId}
|
|
136
|
+
installingId={actions.installingId}
|
|
290
137
|
installFilter={installFilter}
|
|
291
138
|
isLoading={marketSearch.isLoading && hubItems.length === 0}
|
|
292
139
|
query={marketQuery}
|
|
@@ -299,8 +146,8 @@ export function SkillsTab({
|
|
|
299
146
|
canLoadMore={marketSearch.canLoadMore}
|
|
300
147
|
loadingMore={marketSearch.isValidating && hubItems.length > 0}
|
|
301
148
|
onAddRegistry={() => registryModal.setOpen(true)}
|
|
302
|
-
onOpenSkillsCli={() =>
|
|
303
|
-
onInstall={handleInstall}
|
|
149
|
+
onOpenSkillsCli={() => skillsCliModal.setOpen(true)}
|
|
150
|
+
onInstall={actions.handleInstall}
|
|
304
151
|
onInstallFilterChange={onInstallFilterChange}
|
|
305
152
|
onLoadMore={marketSearch.loadMore}
|
|
306
153
|
onQueryChange={onMarketQueryChange}
|
|
@@ -318,20 +165,20 @@ export function SkillsTab({
|
|
|
318
165
|
onClose={() => registryModal.setOpen(false)}
|
|
319
166
|
/>
|
|
320
167
|
<SkillsCliModal
|
|
321
|
-
canLoadMore={
|
|
322
|
-
form={
|
|
323
|
-
hasSearched={
|
|
324
|
-
installingId={
|
|
325
|
-
items={
|
|
326
|
-
loadingMore={
|
|
327
|
-
open={
|
|
328
|
-
resetKey={
|
|
329
|
-
searchError={
|
|
330
|
-
searching={
|
|
331
|
-
onClose={
|
|
332
|
-
onInstall={
|
|
333
|
-
onLoadMore={() => void
|
|
334
|
-
onSearch={() => void
|
|
168
|
+
canLoadMore={skillsCliModal.hasMore}
|
|
169
|
+
form={skillsCliModal.form}
|
|
170
|
+
hasSearched={skillsCliModal.hasSearched}
|
|
171
|
+
installingId={skillsCliModal.installingId}
|
|
172
|
+
items={skillsCliModal.items}
|
|
173
|
+
loadingMore={skillsCliModal.loadingMore}
|
|
174
|
+
open={skillsCliModal.open}
|
|
175
|
+
resetKey={skillsCliModal.resetKey}
|
|
176
|
+
searchError={skillsCliModal.searchError}
|
|
177
|
+
searching={skillsCliModal.searching}
|
|
178
|
+
onClose={skillsCliModal.handleClose}
|
|
179
|
+
onInstall={skillsCliModal.handleInstall}
|
|
180
|
+
onLoadMore={() => void skillsCliModal.handleLoadMore()}
|
|
181
|
+
onSearch={() => void skillsCliModal.handleSearch()}
|
|
335
182
|
/>
|
|
336
183
|
</TabContent>
|
|
337
184
|
)
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import './SidebarHeader.scss'
|
|
2
2
|
|
|
3
3
|
import { Button, Tooltip } from 'antd'
|
|
4
|
+
import { useAtomValue } from 'jotai'
|
|
4
5
|
import React, { useState } from 'react'
|
|
5
6
|
import { useTranslation } from 'react-i18next'
|
|
6
7
|
|
|
7
8
|
import { SidebarListHeader } from '#~/components/sidebar-list/SidebarListHeader'
|
|
8
9
|
import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
|
|
9
10
|
import type { SidebarSessionSortOrder } from '#~/hooks/use-sidebar-query-state'
|
|
11
|
+
import { sessionListSearchThresholdAtom } from '#~/store/index'
|
|
12
|
+
|
|
10
13
|
import { SidebarHeaderSearchActions } from './SidebarHeaderSearchActions'
|
|
14
|
+
import { shouldShowSidebarSearchRow } from './sidebar-search-visibility'
|
|
11
15
|
|
|
12
16
|
interface SidebarHeaderProps {
|
|
13
17
|
adapterFilters: string[]
|
|
@@ -21,6 +25,7 @@ interface SidebarHeaderProps {
|
|
|
21
25
|
isSidebarCollapsed: boolean
|
|
22
26
|
searchQuery: string
|
|
23
27
|
selectedCount: number
|
|
28
|
+
sessionCount: number
|
|
24
29
|
sortOrder: SidebarSessionSortOrder
|
|
25
30
|
sortSelection?: SidebarSessionSortOrder
|
|
26
31
|
shortcutLabel: string
|
|
@@ -52,6 +57,7 @@ export function SidebarHeader({
|
|
|
52
57
|
isSidebarCollapsed,
|
|
53
58
|
searchQuery,
|
|
54
59
|
selectedCount,
|
|
60
|
+
sessionCount,
|
|
55
61
|
sortOrder,
|
|
56
62
|
sortSelection,
|
|
57
63
|
shortcutLabel,
|
|
@@ -72,8 +78,16 @@ export function SidebarHeader({
|
|
|
72
78
|
}: SidebarHeaderProps) {
|
|
73
79
|
const { t } = useTranslation()
|
|
74
80
|
const { isTouchInteraction } = useResponsiveLayout()
|
|
81
|
+
const sessionListSearchThreshold = useAtomValue(sessionListSearchThresholdAtom)
|
|
75
82
|
const [isSearchActionsOpen, setIsSearchActionsOpen] = useState(false)
|
|
76
83
|
const shouldShowSearchActions = !isSidebarCollapsed && (isSearchActionsOpen || isBatchMode)
|
|
84
|
+
const shouldShowSearchRow = !isSidebarCollapsed && shouldShowSidebarSearchRow({
|
|
85
|
+
hasActiveSearchControls,
|
|
86
|
+
isBatchMode,
|
|
87
|
+
isSearchActionsOpen,
|
|
88
|
+
sessionCount,
|
|
89
|
+
threshold: sessionListSearchThreshold
|
|
90
|
+
})
|
|
77
91
|
|
|
78
92
|
const primaryAction = !isSidebarCollapsed
|
|
79
93
|
? (
|
|
@@ -141,6 +155,35 @@ export function SidebarHeader({
|
|
|
141
155
|
</Tooltip>
|
|
142
156
|
)
|
|
143
157
|
|
|
158
|
+
const headerContent = shouldShowSearchRow
|
|
159
|
+
? (
|
|
160
|
+
<SidebarHeaderSearchActions
|
|
161
|
+
adapterFilters={adapterFilters}
|
|
162
|
+
availableAdapters={availableAdapters}
|
|
163
|
+
availableTags={availableTags}
|
|
164
|
+
hasActiveSearchControls={hasActiveSearchControls}
|
|
165
|
+
isBatchMode={isBatchMode}
|
|
166
|
+
searchQuery={searchQuery}
|
|
167
|
+
selectedCount={selectedCount}
|
|
168
|
+
shouldShowSearchActions={shouldShowSearchActions}
|
|
169
|
+
sortOrder={sortOrder}
|
|
170
|
+
sortSelection={sortSelection}
|
|
171
|
+
tagFilters={tagFilters}
|
|
172
|
+
totalCount={totalCount}
|
|
173
|
+
onBatchArchive={onBatchArchive}
|
|
174
|
+
onBatchDelete={onBatchDelete}
|
|
175
|
+
onBatchStar={onBatchStar}
|
|
176
|
+
onAdapterFilterChange={onAdapterFilterChange}
|
|
177
|
+
onSearchChange={onSearchChange}
|
|
178
|
+
onSortOrderChange={onSortOrderChange}
|
|
179
|
+
onSelectAll={onSelectAll}
|
|
180
|
+
onTagFilterChange={onTagFilterChange}
|
|
181
|
+
onToggleBatchMode={onToggleBatchMode}
|
|
182
|
+
onToggleSearchActions={() => setIsSearchActionsOpen((prev) => !prev)}
|
|
183
|
+
/>
|
|
184
|
+
)
|
|
185
|
+
: null
|
|
186
|
+
|
|
144
187
|
return (
|
|
145
188
|
<SidebarListHeader
|
|
146
189
|
className='sidebar-header'
|
|
@@ -149,32 +192,7 @@ export function SidebarHeader({
|
|
|
149
192
|
primaryAction={primaryAction}
|
|
150
193
|
sideAction={sideAction}
|
|
151
194
|
>
|
|
152
|
-
{
|
|
153
|
-
<SidebarHeaderSearchActions
|
|
154
|
-
adapterFilters={adapterFilters}
|
|
155
|
-
availableAdapters={availableAdapters}
|
|
156
|
-
availableTags={availableTags}
|
|
157
|
-
hasActiveSearchControls={hasActiveSearchControls}
|
|
158
|
-
isBatchMode={isBatchMode}
|
|
159
|
-
searchQuery={searchQuery}
|
|
160
|
-
selectedCount={selectedCount}
|
|
161
|
-
shouldShowSearchActions={shouldShowSearchActions}
|
|
162
|
-
sortOrder={sortOrder}
|
|
163
|
-
sortSelection={sortSelection}
|
|
164
|
-
tagFilters={tagFilters}
|
|
165
|
-
totalCount={totalCount}
|
|
166
|
-
onBatchArchive={onBatchArchive}
|
|
167
|
-
onBatchDelete={onBatchDelete}
|
|
168
|
-
onBatchStar={onBatchStar}
|
|
169
|
-
onAdapterFilterChange={onAdapterFilterChange}
|
|
170
|
-
onSearchChange={onSearchChange}
|
|
171
|
-
onSortOrderChange={onSortOrderChange}
|
|
172
|
-
onSelectAll={onSelectAll}
|
|
173
|
-
onTagFilterChange={onTagFilterChange}
|
|
174
|
-
onToggleBatchMode={onToggleBatchMode}
|
|
175
|
-
onToggleSearchActions={() => setIsSearchActionsOpen((prev) => !prev)}
|
|
176
|
-
/>
|
|
177
|
-
)}
|
|
195
|
+
{headerContent}
|
|
178
196
|
</SidebarListHeader>
|
|
179
197
|
)
|
|
180
198
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const shouldShowSidebarSearchRow = ({
|
|
2
|
+
hasActiveSearchControls,
|
|
3
|
+
isBatchMode,
|
|
4
|
+
isSearchActionsOpen,
|
|
5
|
+
sessionCount,
|
|
6
|
+
threshold
|
|
7
|
+
}: {
|
|
8
|
+
hasActiveSearchControls: boolean
|
|
9
|
+
isBatchMode: boolean
|
|
10
|
+
isSearchActionsOpen: boolean
|
|
11
|
+
sessionCount: number
|
|
12
|
+
threshold: number
|
|
13
|
+
}) => {
|
|
14
|
+
if (threshold <= 0) return true
|
|
15
|
+
if (hasActiveSearchControls || isBatchMode || isSearchActionsOpen) return true
|
|
16
|
+
|
|
17
|
+
return sessionCount > threshold
|
|
18
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react'
|
|
2
|
-
import { useSearchParams } from 'react-router-dom'
|
|
2
|
+
import { useNavigate, useSearchParams } from 'react-router-dom'
|
|
3
|
+
|
|
4
|
+
import { getClientBase } from '#~/runtime-config.js'
|
|
3
5
|
|
|
4
6
|
type QueryParamKey<T> = Extract<keyof T, string>
|
|
5
7
|
|
|
@@ -9,12 +11,79 @@ interface QueryParamConfig<T extends Record<string, string>> {
|
|
|
9
11
|
omit?: Partial<Record<QueryParamKey<T>, (value: string) => boolean>>
|
|
10
12
|
}
|
|
11
13
|
|
|
14
|
+
export const resolveQueryParamPathname = (rawPathname: string, clientBase: string) => {
|
|
15
|
+
if (!rawPathname.startsWith(clientBase)) {
|
|
16
|
+
return rawPathname === '' ? '/' : rawPathname
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const relativePath = rawPathname.slice(clientBase.length)
|
|
20
|
+
if (relativePath === '') {
|
|
21
|
+
return '/'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const buildQueryParamNavigationTarget = <T extends Record<string, string>>({
|
|
28
|
+
clientBase,
|
|
29
|
+
currentHash,
|
|
30
|
+
currentPathname,
|
|
31
|
+
currentSearch,
|
|
32
|
+
defaults,
|
|
33
|
+
keySet,
|
|
34
|
+
keys,
|
|
35
|
+
omit,
|
|
36
|
+
patch
|
|
37
|
+
}: {
|
|
38
|
+
clientBase: string
|
|
39
|
+
currentHash: string
|
|
40
|
+
currentPathname: string
|
|
41
|
+
currentSearch: string
|
|
42
|
+
defaults?: Partial<T>
|
|
43
|
+
keySet: Set<string>
|
|
44
|
+
keys: QueryParamKey<T>[]
|
|
45
|
+
omit?: Partial<Record<QueryParamKey<T>, (value: string) => boolean>>
|
|
46
|
+
patch: Partial<T>
|
|
47
|
+
}) => {
|
|
48
|
+
const currentSearchParams = new URLSearchParams(currentSearch)
|
|
49
|
+
const nextParams = new URLSearchParams()
|
|
50
|
+
const merged = keys.reduce((acc, key) => {
|
|
51
|
+
const raw = currentSearchParams.get(key)
|
|
52
|
+
const fallback = defaults?.[key] ?? ''
|
|
53
|
+
acc[key] = (raw ?? fallback) as T[QueryParamKey<T>]
|
|
54
|
+
return acc
|
|
55
|
+
}, {} as T)
|
|
56
|
+
|
|
57
|
+
Object.assign(merged, patch)
|
|
58
|
+
|
|
59
|
+
keys.forEach((key) => {
|
|
60
|
+
const value = merged[key] ?? ''
|
|
61
|
+
const shouldOmit = value === '' || (omit?.[key] ? omit[key]!(value) : false)
|
|
62
|
+
if (!shouldOmit) nextParams.set(key, value)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
for (const [key, value] of currentSearchParams.entries()) {
|
|
66
|
+
if (!keySet.has(key)) nextParams.append(key, value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (nextParams.toString() === currentSearchParams.toString()) {
|
|
70
|
+
return null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
pathname: resolveQueryParamPathname(currentPathname, clientBase),
|
|
75
|
+
search: nextParams.toString() === '' ? '' : `?${nextParams.toString()}`,
|
|
76
|
+
hash: currentHash
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
12
80
|
export const useQueryParams = <T extends Record<string, string>>({
|
|
13
81
|
keys,
|
|
14
82
|
defaults,
|
|
15
83
|
omit
|
|
16
84
|
}: QueryParamConfig<T>) => {
|
|
17
|
-
const
|
|
85
|
+
const navigate = useNavigate()
|
|
86
|
+
const [searchParams] = useSearchParams()
|
|
18
87
|
const keySet = useMemo(() => new Set<string>(keys), [keys])
|
|
19
88
|
|
|
20
89
|
const values = useMemo(() => {
|
|
@@ -27,30 +96,29 @@ export const useQueryParams = <T extends Record<string, string>>({
|
|
|
27
96
|
}, [defaults, keys, searchParams])
|
|
28
97
|
|
|
29
98
|
const update = useCallback((patch: Partial<T>) => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
99
|
+
const currentLocation = typeof window === 'undefined'
|
|
100
|
+
? {
|
|
101
|
+
hash: '',
|
|
102
|
+
pathname: '/',
|
|
103
|
+
search: searchParams.toString() === '' ? '' : `?${searchParams.toString()}`
|
|
104
|
+
}
|
|
105
|
+
: window.location
|
|
106
|
+
const target = buildQueryParamNavigationTarget<T>({
|
|
107
|
+
clientBase: getClientBase(),
|
|
108
|
+
currentHash: currentLocation.hash,
|
|
109
|
+
currentPathname: currentLocation.pathname,
|
|
110
|
+
currentSearch: currentLocation.search,
|
|
111
|
+
defaults,
|
|
112
|
+
keySet,
|
|
113
|
+
keys,
|
|
114
|
+
omit,
|
|
115
|
+
patch
|
|
44
116
|
})
|
|
45
117
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (nextParams.toString() !== searchParams.toString()) {
|
|
51
|
-
setSearchParams(nextParams, { replace: true })
|
|
118
|
+
if (target != null) {
|
|
119
|
+
void navigate(target, { replace: true })
|
|
52
120
|
}
|
|
53
|
-
}, [defaults, keySet, keys, omit, searchParams
|
|
121
|
+
}, [defaults, keySet, keys, navigate, omit, searchParams])
|
|
54
122
|
|
|
55
123
|
return { values, update, searchParams }
|
|
56
124
|
}
|
|
@@ -1945,15 +1945,27 @@
|
|
|
1945
1945
|
"about": "About"
|
|
1946
1946
|
},
|
|
1947
1947
|
"appSettings": {
|
|
1948
|
+
"themeMode": {
|
|
1949
|
+
"label": "Theme mode",
|
|
1950
|
+
"desc": "Switch between light, dark, or system theme for the interface"
|
|
1951
|
+
},
|
|
1948
1952
|
"senderHeaderDisplay": {
|
|
1949
1953
|
"label": "Composer header default",
|
|
1950
1954
|
"desc": "Default expand or collapse behavior for the composer header when the URL does not override it",
|
|
1951
1955
|
"expanded": "Expanded by default",
|
|
1952
1956
|
"collapsed": "Collapsed by default"
|
|
1953
1957
|
},
|
|
1958
|
+
"sessionListSearchThreshold": {
|
|
1959
|
+
"label": "Session list search threshold",
|
|
1960
|
+
"desc": "Only show the search and filter row when the session count exceeds this value; use 0 to always show it"
|
|
1961
|
+
},
|
|
1954
1962
|
"announcements": {
|
|
1955
1963
|
"label": "Show announcements",
|
|
1956
1964
|
"desc": "Show announcements when creating a new session"
|
|
1965
|
+
},
|
|
1966
|
+
"recommendedActions": {
|
|
1967
|
+
"label": "Show recommended actions",
|
|
1968
|
+
"desc": "Show the recommended action list when creating a new session"
|
|
1957
1969
|
}
|
|
1958
1970
|
}
|
|
1959
1971
|
}
|
|
@@ -1946,15 +1946,27 @@
|
|
|
1946
1946
|
"about": "关于"
|
|
1947
1947
|
},
|
|
1948
1948
|
"appSettings": {
|
|
1949
|
+
"themeMode": {
|
|
1950
|
+
"label": "主题模式",
|
|
1951
|
+
"desc": "切换浅色、深色或跟随系统的界面主题"
|
|
1952
|
+
},
|
|
1949
1953
|
"senderHeaderDisplay": {
|
|
1950
1954
|
"label": "输入区顶栏默认展示",
|
|
1951
1955
|
"desc": "链接未显式指定时,默认展开或折叠输入区顶栏",
|
|
1952
1956
|
"expanded": "默认展开",
|
|
1953
1957
|
"collapsed": "默认折叠"
|
|
1954
1958
|
},
|
|
1959
|
+
"sessionListSearchThreshold": {
|
|
1960
|
+
"label": "会话列表搜索展示阈值",
|
|
1961
|
+
"desc": "只有会话数量超过该值时才展示搜索和筛选区域,0 表示始终展示"
|
|
1962
|
+
},
|
|
1955
1963
|
"announcements": {
|
|
1956
1964
|
"label": "显示公告",
|
|
1957
1965
|
"desc": "新建会话时展示公告信息"
|
|
1966
|
+
},
|
|
1967
|
+
"recommendedActions": {
|
|
1968
|
+
"label": "显示推荐操作",
|
|
1969
|
+
"desc": "新建会话时展示推荐操作列表"
|
|
1958
1970
|
}
|
|
1959
1971
|
}
|
|
1960
1972
|
}
|