@objectivex666/dsh-settings-search 1.13.0 → 1.13.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/README.en.md +3 -2
- package/README.md +3 -2
- package/lib/client.js +48 -10
- package/package.json +1 -1
- package/scripts/ai.test.mjs +56 -18
package/README.en.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- 🔍 **Live search** — a search box in the settings panel filters every registered settings page (`settings.section`) and general item (`settings.general.item`) as you type.
|
|
18
18
|
- 🧩 **Three search styles** — choose the classic sidebar, window top bar, or popup search page. The choice is saved in the current browser.
|
|
19
19
|
- ⌨️ **Search shortcut (v1.10.0)** — focus the search box with `Ctrl+F` or `⌘F` inside the settings panel. You can enable or disable it from the "Settings Search" page; disabling keeps the browser's default find behavior.
|
|
20
|
-
- 🤝 **Import DSH model config (v1.9.0)** — import the Base URL and model name from DSH's current default model in the AI config section. For credential safety, the API key is never imported.
|
|
20
|
+
- 🤝 **Import DSH model config (v1.9.0)** — import the Base URL and model name from DSH's current default model in the AI config section. When that default is a built-in DeepSeek route (whose settings carry no Base URL), the import uses DeepSeek's own endpoint, `https://api.deepseek.com/anthropic`. For credential safety, the API key is never imported.
|
|
21
21
|
- 🎯 **First-run onboarding (v1.9.0)** — a welcome modal appears only the first time the settings panel opens after installation. It introduces the four search styles, pinyin/intent/AI hints, click-to-jump, and auto-indexing; there are no "Don't show again" or manual reopen buttons.
|
|
22
22
|
- 📂 **In-page option search (v1.2.0)** — search beyond pages and reach the specific options inside them: Plugins-page tabs (`settings.plugins.tab`), the pre-0.2 plugin-card seats (`settings.plugin.item` / `web-ui.plugin.item`, both gone in 0.2), general rows, and more, each shown with a "page › option" breadcrumb.
|
|
23
23
|
- 🔤 **Pinyin association search (v1.5.0)** — type initials (`sz`) or full pinyin (`shezhi`) to surface Chinese settings items; navigate with `↑`/`↓`, confirm with `Enter`, dismiss with `Esc`, and matched text is highlighted.
|
|
@@ -128,7 +128,8 @@ npm test # structural checks and mocked Ollama / OpenAI / Anthropic regression
|
|
|
128
128
|
Issues are welcome — please report bugs, suggest features, or ask questions in
|
|
129
129
|
[GitHub Issues](https://github.com/objectivex666/dsh-settings-search/issues).
|
|
130
130
|
Pull requests are equally welcome via
|
|
131
|
-
[GitHub Pull Requests](https://github.com/objectivex666/dsh-settings-search/pulls)
|
|
131
|
+
[GitHub Pull Requests](https://github.com/objectivex666/dsh-settings-search/pulls) —
|
|
132
|
+
please read [CONTRIBUTING.en.md](./CONTRIBUTING.en.md) first (layout, conventions, tests, and the release flow).
|
|
132
133
|
|
|
133
134
|
When submitting an issue, please include as much of the following as possible so we can locate and reproduce it:
|
|
134
135
|
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- 🔍 **实时搜索**:在设置面板中嵌入搜索框,输入关键词立即过滤所有已注册的设置页(`settings.section`)与通用设置项(`settings.general.item`)。
|
|
18
18
|
- 🧩 **三种搜索样式**:可选择经典侧栏、窗口顶栏或弹出搜索页;选择会保存在当前浏览器。
|
|
19
19
|
- ⌨️ **搜索快捷键(v1.10.0)**:在设置面板内按 `Ctrl+F` 或 `⌘F` 聚焦搜索框;可在「设置搜索」页启用或关闭,关闭后保留浏览器默认查找。
|
|
20
|
-
- 🤝 **导入 DSH 模型配置(v1.9.0)**:在 AI 配置区一键读取 DSH 当前默认模型的 Base URL
|
|
20
|
+
- 🤝 **导入 DSH 模型配置(v1.9.0)**:在 AI 配置区一键读取 DSH 当前默认模型的 Base URL 与模型名;默认模型是 DSH 内置的 DeepSeek 路由时(设置里不含 Base URL),导入 DeepSeek 的接口地址 `https://api.deepseek.com/anthropic`。为避免凭据扩散,导入始终不包含 API Key。
|
|
21
21
|
- 🎯 **新用户引导(v1.9.0)**:仅在安装后首次打开设置面板时弹出引导弹窗,介绍四种搜索样式、拼音/意图/AI 联想、点击即达与自动导入索引等核心功能;不再提供“不再显示”和手动重开按钮。
|
|
22
22
|
- 📂 **页内选项搜索(v1.2.0)**:不只搜页面,还能直达每个设置页内部的具体选项——插件页的标签页(`settings.plugins.tab`)、旧版的插件卡片座位(`settings.plugin.item` / `web-ui.plugin.item`,0.2 已移除)、通用设置行等,结果以「页面 › 选项」面包屑展示。
|
|
23
23
|
- 🔤 **拼音联想搜索(v1.5.0)**:输入拼音首字母(如 `sz`)或全拼(如 `shezhi`)即可联想中文设置项;支持 `↑`/`↓` 选择、`Enter` 跳转、`Esc` 关闭,命中的文字自动高亮。
|
|
@@ -127,7 +127,8 @@ npm test # 结构检查及 Ollama / OpenAI / Anthropic 模拟接口回归测
|
|
|
127
127
|
|
|
128
128
|
欢迎提交 **Issue** 反馈 Bug、功能建议或使用疑问:
|
|
129
129
|
[GitHub Issues](https://github.com/objectivex666/dsh-settings-search/issues),
|
|
130
|
-
也欢迎直接发起 [Pull Request](https://github.com/objectivex666/dsh-settings-search/pulls)
|
|
130
|
+
也欢迎直接发起 [Pull Request](https://github.com/objectivex666/dsh-settings-search/pulls)——
|
|
131
|
+
动手前请先读一遍 [贡献指南](./CONTRIBUTING.md)(结构、代码约定、测试与发布流程)。
|
|
131
132
|
|
|
132
133
|
提交 Issue 时,请尽量完整地提供以下信息,便于定位与复现:
|
|
133
134
|
|
package/lib/client.js
CHANGED
|
@@ -30,7 +30,7 @@ window.__ModuleLoader__.load({
|
|
|
30
30
|
|
|
31
31
|
const NS = 'settings-search-v5'
|
|
32
32
|
/** Single source of truth is package.json — scripts/check.mjs asserts this matches. */
|
|
33
|
-
const PKG_VERSION = '1.13.
|
|
33
|
+
const PKG_VERSION = '1.13.1'
|
|
34
34
|
const NPM_NAME = '@objectivex666/dsh-settings-search'
|
|
35
35
|
const REPO_URL = 'https://github.com/objectivex666/dsh-settings-search'
|
|
36
36
|
const ISSUES_URL = 'https://github.com/objectivex666/dsh-settings-search/issues/new/choose'
|
|
@@ -107,6 +107,7 @@ window.__ModuleLoader__.load({
|
|
|
107
107
|
aiDshImportOk: '已导入 Base URL 与模型名;API Key 未改动 ✓',
|
|
108
108
|
aiDshImportFail: '导入失败:{reason}',
|
|
109
109
|
aiDshImportEmpty: '未找到 DSH 当前默认模型配置',
|
|
110
|
+
aiDshImportBuiltin: '已导入 DeepSeek 内置地址与模型名;API Key 未改动,插件内使用需填写自己的 DeepSeek API Key',
|
|
110
111
|
aiDshImportApi: '当前 DSH 版本未提供模型配置接口(remote.settings)',
|
|
111
112
|
aiDshImportNoBaseUrl: '默认模型「{model}」在 DSH 设置中没有可导入的 Base URL',
|
|
112
113
|
aiConfigSave: '保存配置',
|
|
@@ -253,6 +254,7 @@ window.__ModuleLoader__.load({
|
|
|
253
254
|
aiDshImportOk: 'Imported Base URL and model; API key unchanged ✓',
|
|
254
255
|
aiDshImportFail: 'Import failed: {reason}',
|
|
255
256
|
aiDshImportEmpty: 'No current DSH default model config found',
|
|
257
|
+
aiDshImportBuiltin: 'Imported the built-in DeepSeek address and model; the API key is unchanged, so fill in your own DeepSeek key to use it here',
|
|
256
258
|
aiDshImportApi: 'This DSH build exposes no model config API (remote.settings)',
|
|
257
259
|
aiDshImportNoBaseUrl: 'The default model "{model}" has no Base URL in DSH settings to import',
|
|
258
260
|
aiConfigSave: 'Save config',
|
|
@@ -1922,15 +1924,36 @@ window.__ModuleLoader__.load({
|
|
|
1922
1924
|
return null
|
|
1923
1925
|
}
|
|
1924
1926
|
|
|
1927
|
+
/** DSH's built-in DeepSeek routes and the namespaces that own them. */
|
|
1928
|
+
const DEEPSEEK_PROVIDERS = new Set(['deepseek-official', 'deepseek-account'])
|
|
1929
|
+
const DEEPSEEK_SETTINGS = new Set(['llm-deepseek', 'llm-deepseek-api-key', 'llm-deepseek-account'])
|
|
1930
|
+
/** The endpoint the shipped DeepSeek adapter falls back to (`PUBLIC_BASE_URL`). */
|
|
1931
|
+
const DEEPSEEK_PUBLIC_BASE_URL = 'https://api.deepseek.com/anthropic'
|
|
1932
|
+
/**
|
|
1933
|
+
* Whether one provider route is a built-in DeepSeek one.
|
|
1934
|
+
* @param providerId - the route's provider id.
|
|
1935
|
+
* @param settingsNs - the settings namespace the route reads, when known.
|
|
1936
|
+
* @returns true for the shipped DeepSeek routes.
|
|
1937
|
+
*/
|
|
1938
|
+
const isBuiltInDeepseekRoute = (providerId, settingsNs) =>
|
|
1939
|
+
DEEPSEEK_PROVIDERS.has(String(providerId ?? '')) || DEEPSEEK_SETTINGS.has(String(settingsNs ?? ''))
|
|
1940
|
+
|
|
1925
1941
|
/**
|
|
1926
1942
|
* Read the model fields of DSH's current default model; credentials are
|
|
1927
1943
|
* never requested. `agent-default-model` names the provider and the model,
|
|
1928
1944
|
* the provider's own settings namespace carries the Base URL, and the model
|
|
1929
1945
|
* profile inside that namespace may override the wire protocol.
|
|
1930
1946
|
*
|
|
1947
|
+
* A built-in DeepSeek route keeps its endpoint in the adapter rather than in
|
|
1948
|
+
* settings, so nothing is importable from the namespace there. Those routes
|
|
1949
|
+
* fall back to the endpoint DSH itself calls — the adapter resolves
|
|
1950
|
+
* `config.baseURL ?? $DEEPSEEK_BASE_URL ?? https://api.deepseek.com/anthropic`,
|
|
1951
|
+
* and only the last of those is visible to a browser — so importing the
|
|
1952
|
+
* default DeepSeek model still yields a usable address.
|
|
1953
|
+
*
|
|
1931
1954
|
* @param ctx - the client plugin context.
|
|
1932
1955
|
* @param t - translate function for the failure messages.
|
|
1933
|
-
* @returns `{ provider, baseUrl, model }`, or null when DSH has no default model.
|
|
1956
|
+
* @returns `{ provider, baseUrl, model, builtin? }`, or null when DSH has no default model.
|
|
1934
1957
|
* @throws when this build exposes no settings API, or the default model has no Base URL.
|
|
1935
1958
|
*/
|
|
1936
1959
|
const readDshModelConfig = async (ctx, t) => {
|
|
@@ -1951,8 +1974,19 @@ window.__ModuleLoader__.load({
|
|
|
1951
1974
|
: modelNamespace?.value?.providers?.[providerId] ?? {}
|
|
1952
1975
|
const configuredModels = Array.isArray(providerProfile.models) ? providerProfile.models : []
|
|
1953
1976
|
const modelProfile = configuredModels.find((entry) => entry?.id === modelId) ?? {}
|
|
1954
|
-
|
|
1955
|
-
|
|
1977
|
+
// The built-in DeepSeek routes fix their protocol in the adapter: whatever
|
|
1978
|
+
// address they use, they speak Messages.
|
|
1979
|
+
const deepseekRoute = isBuiltInDeepseekRoute(providerId, providerRow?.settingsNs)
|
|
1980
|
+
let protocol = String(modelProfile.api ?? providerProfile.api ?? '').trim()
|
|
1981
|
+
let baseUrl = String(providerProfile.baseURL ?? '').trim()
|
|
1982
|
+
if (deepseekRoute && protocol === '') protocol = 'anthropic-messages'
|
|
1983
|
+
let builtin = false
|
|
1984
|
+
if (baseUrl === '' && deepseekRoute) {
|
|
1985
|
+
// Nothing is importable from settings, so mirror the endpoint the
|
|
1986
|
+
// adapter itself falls back to (`PUBLIC_BASE_URL`).
|
|
1987
|
+
baseUrl = DEEPSEEK_PUBLIC_BASE_URL
|
|
1988
|
+
builtin = true
|
|
1989
|
+
}
|
|
1956
1990
|
if (!baseUrl) throw new Error(t('aiDshImportNoBaseUrl').replace('{model}', modelId))
|
|
1957
1991
|
return {
|
|
1958
1992
|
provider: providerId.toLowerCase() === 'ollama' || protocol === 'ollama'
|
|
@@ -1960,6 +1994,7 @@ window.__ModuleLoader__.load({
|
|
|
1960
1994
|
: protocol === 'anthropic-messages' ? 'anthropic' : 'openai',
|
|
1961
1995
|
baseUrl,
|
|
1962
1996
|
model: modelId,
|
|
1997
|
+
...builtin ? { builtin: true } : {},
|
|
1963
1998
|
}
|
|
1964
1999
|
}
|
|
1965
2000
|
|
|
@@ -2900,15 +2935,18 @@ window.__ModuleLoader__.load({
|
|
|
2900
2935
|
try {
|
|
2901
2936
|
const imported = await readDshModelConfig(ctx, t)
|
|
2902
2937
|
if (!imported) throw new Error(t('aiDshImportEmpty'))
|
|
2938
|
+
const { builtin, ...config } = imported
|
|
2903
2939
|
providerDrafts.current[provider] = { baseUrl, model }
|
|
2904
|
-
setAiConfig({ ...
|
|
2905
|
-
setProvider(
|
|
2906
|
-
setBaseUrl(
|
|
2907
|
-
setModel(
|
|
2940
|
+
setAiConfig({ ...config, apiKey })
|
|
2941
|
+
setProvider(config.provider)
|
|
2942
|
+
setBaseUrl(config.baseUrl)
|
|
2943
|
+
setModel(config.model)
|
|
2908
2944
|
resetModelList()
|
|
2909
2945
|
setDshImportState('ok')
|
|
2910
|
-
|
|
2911
|
-
|
|
2946
|
+
// A built-in DeepSeek route has no stored address and no API key of
|
|
2947
|
+
// its own, so the note says where the address came from.
|
|
2948
|
+
setDshImportMsg(builtin === true ? t('aiDshImportBuiltin') : t('aiDshImportOk'))
|
|
2949
|
+
log('info', '导入 DSH 模型配置', { provider: config.provider, model: config.model, builtin: builtin === true })
|
|
2912
2950
|
} catch (error) {
|
|
2913
2951
|
const reason = String(error?.message ?? error).slice(0, 160)
|
|
2914
2952
|
setDshImportState('fail')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectivex666/dsh-settings-search",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.1",
|
|
4
4
|
"description": "DSH settings panel search plugin – instantly filter setting sections and general items with navigation guidance. Installable via `dsh plugin add`.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
package/scripts/ai.test.mjs
CHANGED
|
@@ -497,26 +497,64 @@ test('a DSH build with no settings API fails with a readable reason instead of a
|
|
|
497
497
|
await assert.rejects(api.readDshModelConfig({ get: () => undefined }, (key) => key), { message: 'aiDshImportApi' })
|
|
498
498
|
})
|
|
499
499
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
500
|
+
// --- DSH default-model import: the built-in DeepSeek route ---
|
|
501
|
+
|
|
502
|
+
/** A ctx whose default model is one route, with whatever namespaces it reads. */
|
|
503
|
+
const dshDefaultModel = (provider, model, { settingsNs, namespaces = [] } = {}) => ({
|
|
504
|
+
get: (name) => name === 'remote.settings'
|
|
505
|
+
? {
|
|
506
|
+
describe: async () => describeReply([
|
|
507
|
+
{ ns: 'agent-default-model', value: { provider, model } },
|
|
508
|
+
...namespaces,
|
|
509
|
+
]),
|
|
510
|
+
}
|
|
511
|
+
: name === 'remote.llm'
|
|
504
512
|
? {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
513
|
+
listConfigurableProviders: async () => ({
|
|
514
|
+
ok: true,
|
|
515
|
+
value: [{ provider, settingsNs, settingsPath: [] }],
|
|
516
|
+
}),
|
|
508
517
|
}
|
|
509
|
-
:
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
518
|
+
: undefined,
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
test('a built-in DeepSeek default model imports the adapter\u2019s own endpoint', async () => {
|
|
522
|
+
const { api } = load()
|
|
523
|
+
for (const [provider, settingsNs] of [['deepseek-account', 'llm-deepseek-account'], ['deepseek-official', 'llm-deepseek']]) {
|
|
524
|
+
const ctx = dshDefaultModel(provider, 'deepseek-flash', { settingsNs })
|
|
525
|
+
assert.deepEqual(plain(await api.readDshModelConfig(ctx, (key) => key)), {
|
|
526
|
+
provider: 'anthropic',
|
|
527
|
+
baseUrl: 'https://api.deepseek.com/anthropic',
|
|
528
|
+
model: 'deepseek-flash',
|
|
529
|
+
builtin: true,
|
|
530
|
+
}, `${provider} must fall back to the DeepSeek endpoint`)
|
|
517
531
|
}
|
|
518
|
-
|
|
532
|
+
})
|
|
519
533
|
|
|
534
|
+
test('a configured DeepSeek base URL wins over the built-in fallback', async () => {
|
|
535
|
+
const { api } = load()
|
|
536
|
+
const ctx = dshDefaultModel('deepseek-official', 'deepseek-chat', {
|
|
537
|
+
settingsNs: 'llm-deepseek',
|
|
538
|
+
namespaces: [{ ns: 'llm-deepseek', value: { baseURL: 'https://proxy.test/anthropic' } }],
|
|
539
|
+
})
|
|
540
|
+
assert.deepEqual(plain(await api.readDshModelConfig(ctx, (key) => key)), {
|
|
541
|
+
provider: 'anthropic',
|
|
542
|
+
baseUrl: 'https://proxy.test/anthropic',
|
|
543
|
+
model: 'deepseek-chat',
|
|
544
|
+
})
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
test('a third-party route without an importable Base URL still names the model', async () => {
|
|
548
|
+
const { api } = load()
|
|
549
|
+
const ctx = dshDefaultModel('acme-gateway', 'acme-large', {
|
|
550
|
+
settingsNs: 'llm-pi-ai',
|
|
551
|
+
namespaces: [{ ns: 'llm-pi-ai', value: { providers: { 'acme-gateway': { models: [{ id: 'acme-large' }] } } } }],
|
|
552
|
+
})
|
|
553
|
+
await assert.rejects(api.readDshModelConfig(ctx, (key) => key), { message: 'aiDshImportNoBaseUrl' })
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
test('no default model imports nothing', async () => {
|
|
557
|
+
const { api } = load()
|
|
520
558
|
const idle = {
|
|
521
559
|
get: (name) => name === 'remote.settings'
|
|
522
560
|
? { describe: async () => describeReply([{ ns: 'agent-default-model', value: {} }]) }
|
|
@@ -584,7 +622,7 @@ const logRouteCalls = (calls) => calls.filter((call) => String(call.url).include
|
|
|
584
622
|
|
|
585
623
|
test('log entries are batched to the host log route as one formatted line each', () => {
|
|
586
624
|
const { api, calls } = load()
|
|
587
|
-
api.log('info', '应用装载', { version: '1.13.
|
|
625
|
+
api.log('info', '应用装载', { version: '1.13.1' })
|
|
588
626
|
api.log('error', '导入失败')
|
|
589
627
|
api.flushLogs()
|
|
590
628
|
const [call] = logRouteCalls(calls)
|
|
@@ -593,7 +631,7 @@ test('log entries are batched to the host log route as one formatted line each',
|
|
|
593
631
|
assert.deepEqual(plain(call.headers), { 'content-type': 'application/json' })
|
|
594
632
|
assert.equal(call.body.action, 'append')
|
|
595
633
|
assert.equal(call.body.lines.length, 2)
|
|
596
|
-
assert.match(call.body.lines[0], /^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[info\] \[[0-9a-f]{8}:1\] 应用装载 \{"version":"1\.13\.
|
|
634
|
+
assert.match(call.body.lines[0], /^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[info\] \[[0-9a-f]{8}:1\] 应用装载 \{"version":"1\.13\.1"\}$/)
|
|
597
635
|
assert.match(call.body.lines[1], /\[error\] \[[0-9a-f]{8}:2\] 导入失败$/)
|
|
598
636
|
})
|
|
599
637
|
|