@objectivex666/dsh-settings-search 1.5.0 → 1.6.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/README.en.md +1 -0
- package/README.md +1 -0
- package/lib/client.js +115 -18
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- 🔍 **Live search** — a search box at the top of the settings panel's left navigation filters every registered settings page (`settings.section`) and general item (`settings.general.item`) as you type.
|
|
16
16
|
- 📂 **In-page option search (v1.2.0)** — search beyond pages and reach the specific options inside them: Plugins-page tabs (`settings.plugins.tab`), Web UI plugin cards (`web-ui.plugin.item`), general rows, and more, each shown with a "page › option" breadcrumb.
|
|
17
17
|
- 🔤 **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.
|
|
18
|
+
- 🧠 **Intent search (v1.6.0)** — describe the goal in Chinese (e.g. 「太亮了」 "too bright", 「字太小」 "text too small") to surface matching settings without knowing their names, shown in a "You may want" group.
|
|
18
19
|
- 🧭 **Click-to-jump** — selecting a result clicks the matching left-nav entry to open its section; for tab options it also opens the tab and flashes the target row. A manual-path hint appears if automatic navigation is not possible.
|
|
19
20
|
- 🌱 **Progressive indexing** — self-drawing settings rows are harvested from the rendered DOM (via their `[data-slot]` anchors) the first time their page is visited, then stay searchable.
|
|
20
21
|
- 🛠 **Built-in settings page (v1.4.0)** — a "Settings Search" page in the left navigation shows the current version and checks npm for a newer release on demand; when one exists it hands you a copy-ready `dsh plugin update` command.
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- 🔍 **实时搜索**:在设置面板左侧导航顶部嵌入搜索框,输入关键词立即过滤所有已注册的设置页(`settings.section`)与通用设置项(`settings.general.item`)。
|
|
16
16
|
- 📂 **页内选项搜索(v1.2.0)**:不只搜页面,还能直达每个设置页内部的具体选项——插件页的标签页(`settings.plugins.tab`)、Web UI 插件的卡片(`web-ui.plugin.item`)、通用设置行等,结果以「页面 › 选项」面包屑展示。
|
|
17
17
|
- 🔤 **拼音联想搜索(v1.5.0)**:输入拼音首字母(如 `sz`)或全拼(如 `shezhi`)即可联想中文设置项;支持 `↑`/`↓` 选择、`Enter` 跳转、`Esc` 关闭,命中的文字自动高亮。
|
|
18
|
+
- 🧠 **意图联想搜索(v1.6.0)**:直接描述想做的事(如「太亮了」「字太小」「忘记密码」),无需知道选项名即可联想真实设置项,结果在「猜你想找」分组展示。
|
|
18
19
|
- 🧭 **点击即达**:选中结果后自动点击对应的左侧导航项打开所在分区;若是页内标签页会继续点开对应标签,并高亮闪烁目标选项行。无法自动跳转时显示手动路径提示。
|
|
19
20
|
- 🌱 **渐进索引**:自绘界面的设置行会在其所在页面首次被访问时从渲染 DOM 中收割标题并加入索引(此后始终可搜)。
|
|
20
21
|
- 🛠 **自带设置页(v1.4.0)**:左侧导航新增「设置搜索」页——展示当前版本,一键检查 npm 注册表上的最新版本;有更新时直接给出复制就用的 `dsh plugin update` 命令。
|
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.
|
|
33
|
+
const PKG_VERSION = '1.6.0'
|
|
34
34
|
const NPM_NAME = '@objectivex666/dsh-settings-search'
|
|
35
35
|
const zh = {
|
|
36
36
|
title: '设置',
|
|
@@ -38,6 +38,7 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
empty: '没有匹配的设置',
|
|
39
39
|
pages: '设置页',
|
|
40
40
|
options: '页内选项',
|
|
41
|
+
guess: '猜你想找',
|
|
41
42
|
page: '页面',
|
|
42
43
|
option: '选项',
|
|
43
44
|
jumpFail: '未能自动跳转,请手动打开:{path}',
|
|
@@ -60,6 +61,7 @@ window.__ModuleLoader__.load({
|
|
|
60
61
|
empty: 'No matching settings',
|
|
61
62
|
pages: 'Settings pages',
|
|
62
63
|
options: 'In-page options',
|
|
64
|
+
guess: 'You may want',
|
|
63
65
|
page: 'Page',
|
|
64
66
|
option: 'Option',
|
|
65
67
|
jumpFail: 'Could not navigate automatically — open it here: {path}',
|
|
@@ -114,6 +116,10 @@ window.__ModuleLoader__.load({
|
|
|
114
116
|
中zhong 英ying 韩han 法fa 德de 西xi 俄e 翻fan 译yi 简jian 繁fan 历li 史shi 聊liao
|
|
115
117
|
天tian 消xiao 息xi 醒xing 静jing 震zhen 红hong 点dian 角jiao 未wei 读du 部bu
|
|
116
118
|
为wei 已yi 好 hao 坏huai 多duo 少shao
|
|
119
|
+
太tai 刺ci 眼yan 夜ye 暗an 护hu 不bu 调tiao 吵chao 没mei 勿wu 想xiang 被bei 忘wang
|
|
120
|
+
资zi 料liao 解jie 垃la 圾ji 占zhan 缓huan 卡ka 顿dun 慢man 断duan 代dai 理li 别bie
|
|
121
|
+
闭bi 省sheng 耗hao 耐nai 注zhu 退tui 款kuan 到dao 取qu 回hui 送song 按an 短duan
|
|
122
|
+
长chang 收shou 我wo 了le 足zu 烦fan 像xiang 住zhu
|
|
117
123
|
`
|
|
118
124
|
const PINYIN = new Map(
|
|
119
125
|
PINYIN_TABLE.split(/\s+/).filter(Boolean).map((pair) => [pair[0], pair.slice(1)])
|
|
@@ -167,6 +173,56 @@ window.__ModuleLoader__.load({
|
|
|
167
173
|
return consumed
|
|
168
174
|
}
|
|
169
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Intent table for "describe the goal" search: users who know what they
|
|
178
|
+
* want to change but not which option does it. Format: 描述>联想词,逗号分隔.
|
|
179
|
+
* A query token matching a phrase (text or pinyin) additionally matches
|
|
180
|
+
* every listed keyword; expanded-only hits surface in the guess group.
|
|
181
|
+
*/
|
|
182
|
+
const INTENT_TABLE = `
|
|
183
|
+
太亮>深色,主题,亮度,护眼 太暗>亮度,主题,显示 刺眼>深色,主题,亮度 夜间>深色,主题,护眼
|
|
184
|
+
护眼>深色,主题,亮度 看不清>字体,大小,缩放 字太小>字体,大小,缩放 字太大>字体,大小
|
|
185
|
+
调字体>字体,大小 太吵>声音,提示音,静音,音量 没声音>声音,音量,提示音 静音>声音,提示音,音量
|
|
186
|
+
声音太>声音,音量,提示音 提示音>声音,通知 免打扰>通知,免打扰,提醒 勿扰>通知,免打扰
|
|
187
|
+
不想被打扰>通知,免打扰,提醒 提醒>通知,消息 忘记密码>密码,安全,验证,账户 改密码>密码,安全,账户
|
|
188
|
+
密码忘了>密码,安全,验证 换头像>头像,账户,资料 换手机号>手机,绑定,账户 换邮箱>邮箱,绑定,账户
|
|
189
|
+
解绑>绑定,账户 清垃圾>缓存,清除,存储 占空间>缓存,存储,清除 空间不足>存储,缓存,清除
|
|
190
|
+
太卡>缓存,速度,网络 卡顿>缓存,速度 加载慢>网络,速度 太慢>速度,网络
|
|
191
|
+
断网>网络,连接 连不上>网络,连接,代理 换语言>语言 英文界面>语言,英文
|
|
192
|
+
中文界面>语言,中文 切换语言>语言 换设备>同步,备份,账户 新手机>同步,备份,账户
|
|
193
|
+
备份数据>备份,导出,数据 导出聊天>聊天,导出,备份,历史 聊天记录>聊天,历史,备份 不想被推荐>推荐,个性化,隐私
|
|
194
|
+
推荐太烦>推荐,个性化 隐私保护>隐私,安全 别人看到>隐私,安全 自动更新>更新,自动
|
|
195
|
+
关闭更新>更新,自动 省电>电池,节能,省电 耗电>电池,节能 电池不耐用>电池,节能
|
|
196
|
+
注销账号>注销,账户,删除 删除账号>注销,删除,账户 退出登录>登录,注销,账户 退款>支付,退款
|
|
197
|
+
会员到期>会员,订阅,支付 取消订阅>订阅,会员,支付 回车发送>发送,回车,enter,输入,快捷键 按回车>回车,enter,发送,输入
|
|
198
|
+
打字>输入,回车,发送 换行>换行,输入 没有记忆>记忆,上下文,对话 记不住>记忆,上下文
|
|
199
|
+
上下文太短>上下文,记忆,长度 消息不提醒>通知,消息,提醒,声音 收不到通知>通知,提醒,消息 看不到消息>消息,通知,提醒
|
|
200
|
+
主题>外观,皮肤,颜色,深色 换肤>皮肤,外观 颜色>皮肤,外观,主题
|
|
201
|
+
看不懂>语言,中文,英文 翻译>语言,中文,英文 乱改文件>权限,确认 权限太松>权限,确认
|
|
202
|
+
发图片>视觉,识图,自动,图片 看图>视觉,识图,图片 聊天记录>归档,会话,历史,聊天 删会话>会话,删除,归档
|
|
203
|
+
`
|
|
204
|
+
const INTENTS = INTENT_TABLE.split(/\s+/).filter(Boolean).map((entry) => {
|
|
205
|
+
const at = entry.indexOf('>')
|
|
206
|
+
const phrase = entry.slice(0, at)
|
|
207
|
+
return { phrase, keywords: entry.slice(at + 1).split(','), keys: pinyinKeys(phrase) }
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
/** Keywords a query token should additionally search after an intent hit. */
|
|
211
|
+
const intentKeywords = (tok) => {
|
|
212
|
+
const hits = []
|
|
213
|
+
for (const intent of INTENTS) {
|
|
214
|
+
const phraseHit = intent.phrase.includes(tok) || tok.includes(intent.phrase) ||
|
|
215
|
+
(intent.keys !== null &&
|
|
216
|
+
(intent.keys.full.startsWith(tok) || intent.keys.initials === tok))
|
|
217
|
+
if (phraseHit) {
|
|
218
|
+
for (const kw of intent.keywords) {
|
|
219
|
+
if (!hits.includes(kw)) hits.push(kw)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return hits
|
|
224
|
+
}
|
|
225
|
+
|
|
170
226
|
const CSS = `
|
|
171
227
|
.sss-wrap { position: relative; display: flex; flex-direction: column; gap: 8px; }
|
|
172
228
|
.sss-title { font-size: 16px; font-weight: 600; line-height: 24px; color: var(--dsw-alias-label-primary); }
|
|
@@ -188,6 +244,7 @@ window.__ModuleLoader__.load({
|
|
|
188
244
|
.sss-item-label mark { background: transparent; color: inherit; font-weight: 600; }
|
|
189
245
|
.sss-item-path { font-size: 10px; line-height: 14px; color: var(--dsw-alias-label-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
190
246
|
.sss-item-kind { flex-shrink: 0; font-size: 10px; color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l2); border-radius: 4px; padding: 1px 5px; }
|
|
247
|
+
.sss-item-kind.sss-via { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
191
248
|
.sss-empty { margin: 0; padding: 10px 8px; color: var(--dsw-alias-label-secondary); font-size: 13px; }
|
|
192
249
|
.sss-guide { padding: 7px 10px; border-radius: 8px; background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 18px; }
|
|
193
250
|
@keyframes sssFlashBg { 0% { background-color: var(--dsw-alias-interactive-bg-hover); } 100% { background-color: transparent; } }
|
|
@@ -508,20 +565,49 @@ window.__ModuleLoader__.load({
|
|
|
508
565
|
|
|
509
566
|
const sections = model.readSections()
|
|
510
567
|
const deeps = q !== '' ? model.readDeep() : []
|
|
511
|
-
const
|
|
568
|
+
const expandedKeywords = new Map(tokens.map((tok) => [tok, intentKeywords(tok)]))
|
|
569
|
+
const directMatch = (item, haystackLower) =>
|
|
512
570
|
tokens.every((tok) => tokenMatch(haystackLower, item.keys, tok) !== null)
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
571
|
+
const kwMatch = (item, haystackLower, kw) => {
|
|
572
|
+
if (/^[\x00-\x7f]+$/.test(kw)) {
|
|
573
|
+
if (new RegExp(`\\b${kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(haystackLower)) return true
|
|
574
|
+
} else if (haystackLower.includes(kw)) return true
|
|
575
|
+
if (item.keys === null) return false
|
|
576
|
+
const kwKeys = pinyinKeys(kw)
|
|
577
|
+
if (kwKeys === null) return false
|
|
578
|
+
return item.keys.full.includes(kwKeys.full) || item.keys.initials.startsWith(kwKeys.initials)
|
|
579
|
+
}
|
|
580
|
+
const relatedKeyword = (item, haystackLower) => {
|
|
581
|
+
if (directMatch(item, haystackLower)) return null
|
|
582
|
+
for (const tok of tokens) {
|
|
583
|
+
for (const kw of expandedKeywords.get(tok) ?? []) {
|
|
584
|
+
if (kwMatch(item, haystackLower, kw)) return kw
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return null
|
|
588
|
+
}
|
|
589
|
+
const mappedSections = q === '' ? [] : sections.map((s) => ({
|
|
590
|
+
...s,
|
|
591
|
+
keys: pinyinKeys(s.label),
|
|
592
|
+
hay: `${s.label} ${s.id}`.toLowerCase(),
|
|
593
|
+
}))
|
|
594
|
+
const mappedOptions = q === '' ? [] : deeps.map((o) => ({
|
|
595
|
+
...o,
|
|
596
|
+
keys: pinyinKeys(`${o.label} ${o.parentLabel}`),
|
|
597
|
+
hay: `${o.hay} ${o.parentLabel} ${o.id} ${o.slot}`.toLowerCase(),
|
|
598
|
+
}))
|
|
599
|
+
const shownPages = mappedSections.filter((s) => directMatch(s, s.hay))
|
|
600
|
+
const shownOptions = mappedOptions.filter((o) => directMatch(o, o.hay))
|
|
601
|
+
const related = [
|
|
602
|
+
...mappedSections
|
|
603
|
+
.map((s) => ({ item: s, kind: 'page', via: relatedKeyword(s, s.hay) }))
|
|
604
|
+
.filter((r) => r.via !== null),
|
|
605
|
+
...mappedOptions
|
|
606
|
+
.map((o) => ({ item: o, kind: 'option', via: relatedKeyword(o, o.hay) }))
|
|
607
|
+
.filter((r) => r.via !== null),
|
|
608
|
+
].slice(0, 8)
|
|
609
|
+
const none = q !== '' && shownPages.length === 0 && shownOptions.length === 0 && related.length === 0
|
|
610
|
+
const results = [...shownPages, ...shownOptions, ...related.map((r) => r.item)]
|
|
525
611
|
|
|
526
612
|
const [hint, setHint] = React.useState(null)
|
|
527
613
|
const [active, setActive] = React.useState(0)
|
|
@@ -579,16 +665,20 @@ window.__ModuleLoader__.load({
|
|
|
579
665
|
return [label]
|
|
580
666
|
}
|
|
581
667
|
|
|
582
|
-
const
|
|
668
|
+
const kindBadge = (via, fallback) => React.createElement('span', {
|
|
669
|
+
className: 'sss-item-kind' + (via ? ' sss-via' : ''),
|
|
670
|
+
}, via ?? fallback)
|
|
671
|
+
|
|
672
|
+
const renderPage = (item, flatIndex, via) => React.createElement('li', {
|
|
583
673
|
key: 'page:' + item.id,
|
|
584
674
|
className: 'sss-item' + (flatIndex === activeIndex ? ' is-active' : ''),
|
|
585
675
|
onMouseDown: () => pick(item),
|
|
586
676
|
onMouseEnter: () => setActive(flatIndex),
|
|
587
677
|
}, [
|
|
588
678
|
React.createElement('span', { className: 'sss-item-label' }, highlightLabel(item)),
|
|
589
|
-
|
|
679
|
+
kindBadge(via, t('page')),
|
|
590
680
|
])
|
|
591
|
-
const renderOption = (item, flatIndex) => React.createElement('li', {
|
|
681
|
+
const renderOption = (item, flatIndex, via) => React.createElement('li', {
|
|
592
682
|
key: item.key,
|
|
593
683
|
className: 'sss-item' + (flatIndex === activeIndex ? ' is-active' : ''),
|
|
594
684
|
onMouseDown: () => pick(item),
|
|
@@ -598,7 +688,7 @@ window.__ModuleLoader__.load({
|
|
|
598
688
|
React.createElement('span', { className: 'sss-item-label' }, highlightLabel(item)),
|
|
599
689
|
React.createElement('span', { className: 'sss-item-path' }, item.parentLabel),
|
|
600
690
|
]),
|
|
601
|
-
|
|
691
|
+
kindBadge(via, t('option')),
|
|
602
692
|
])
|
|
603
693
|
|
|
604
694
|
return React.createElement('div', { className: 'sss-wrap' }, [
|
|
@@ -648,6 +738,13 @@ window.__ModuleLoader__.load({
|
|
|
648
738
|
React.createElement('ul', { className: 'sss-list' },
|
|
649
739
|
shownOptions.map((item, i) => renderOption(item, shownPages.length + i))),
|
|
650
740
|
]) : null,
|
|
741
|
+
related.length > 0 ? React.createElement('div', { className: 'sss-group' }, [
|
|
742
|
+
React.createElement('div', { className: 'sss-group-title' }, t('guess')),
|
|
743
|
+
React.createElement('ul', { className: 'sss-list' },
|
|
744
|
+
related.map((r, i) => r.kind === 'page'
|
|
745
|
+
? renderPage(r.item, shownPages.length + shownOptions.length + i, r.via)
|
|
746
|
+
: renderOption(r.item, shownPages.length + shownOptions.length + i, r.via))),
|
|
747
|
+
]) : null,
|
|
651
748
|
]),
|
|
652
749
|
]) : null,
|
|
653
750
|
hint !== null
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectivex666/dsh-settings-search",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
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",
|