@objectivex666/dsh-settings-search 1.4.0 → 1.4.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 +4 -4
- package/README.md +3 -6
- package/lib/client.js +1 -1
- package/package.json +1 -2
- package/dsh-settings-search.js +0 -179
package/README.en.md
CHANGED
|
@@ -80,7 +80,6 @@ lib/index.js Host half (no-op — makes the package a valid Cordis plugin
|
|
|
80
80
|
lib/client.js Browser half (search UI, __ModuleLoader__ format)
|
|
81
81
|
cordis.patch.yml Composition patch: inserts the settings-search plugin row
|
|
82
82
|
scripts/check.mjs Structural self-checks (npm test)
|
|
83
|
-
dsh-settings-search.js Dynamic-plugin source (optional: in-session cordis_define usage)
|
|
84
83
|
```
|
|
85
84
|
|
|
86
85
|
## Development
|
|
@@ -89,6 +88,7 @@ dsh-settings-search.js Dynamic-plugin source (optional: in-session cordis_defin
|
|
|
89
88
|
npm test # validates the host half, the dsh.client manifest, and the client bundle handoff
|
|
90
89
|
```
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
> Historical note: up to v1.1 the repo shipped `dsh-settings-search.js` (an
|
|
92
|
+
> in-session `cordis_define` dynamic-plugin source). Because keeping it in
|
|
93
|
+
> sync with the shipping package kept drifting silently, it was removed in
|
|
94
|
+
> v1.4.1; retrieve it from git history (tag `v1.4.0`) if you need a reference.
|
package/README.md
CHANGED
|
@@ -80,7 +80,6 @@ lib/index.js Host half(空实现,保证作为合法 Cordis 插件行
|
|
|
80
80
|
lib/client.js Browser half(搜索框 UI,__ModuleLoader__ 格式)
|
|
81
81
|
cordis.patch.yml 组合补丁:插入 settings-search 插件行
|
|
82
82
|
scripts/check.mjs 结构自检(npm test)
|
|
83
|
-
dsh-settings-search.js 动态插件源(可选:DSH 会话内 cordis_define 用法)
|
|
84
83
|
```
|
|
85
84
|
|
|
86
85
|
## 开发
|
|
@@ -89,8 +88,6 @@ dsh-settings-search.js 动态插件源(可选:DSH 会话内 cordis_define
|
|
|
89
88
|
npm test # 校验 host 半、dsh.client 清单、client bundle handoff
|
|
90
89
|
```
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
动态插件系统(`cordis_define`)注册,仅供会话内临时使用;命令行安装请使用
|
|
96
|
-
上文的 profile bundle 方式。
|
|
91
|
+
> 历史说明:v1.1 及之前仓库曾附带 `dsh-settings-search.js`(会话内
|
|
92
|
+
> `cordis_define` 动态插件源)。因与正式包代码长期双份维护漂移,已于 v1.4.1
|
|
93
|
+
> 移除;如需动态用法可从 git 历史(tag `v1.4.0`)取回参考。
|
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.4.
|
|
33
|
+
const PKG_VERSION = '1.4.1'
|
|
34
34
|
const NPM_NAME = '@objectivex666/dsh-settings-search'
|
|
35
35
|
const zh = {
|
|
36
36
|
title: '设置',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectivex666/dsh-settings-search",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.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",
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"lib",
|
|
34
34
|
"cordis.patch.yml",
|
|
35
35
|
"scripts",
|
|
36
|
-
"dsh-settings-search.js",
|
|
37
36
|
"README.md",
|
|
38
37
|
"README.en.md",
|
|
39
38
|
"LICENSE"
|
package/dsh-settings-search.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* settings-search 动态插件源文件(legacy,仅会话内临时使用)
|
|
3
|
-
*
|
|
4
|
-
* 说明:
|
|
5
|
-
* - 这是 DSH 动态 Cordis 插件(Client half)的源码,通过 cordis_define 注册到运行中的
|
|
6
|
-
* DSH 进程,不落盘。仅供会话内临时使用;命令行安装请改用本仓库的 profile bundle
|
|
7
|
-
* 形态(`dsh plugin add`,见 README),其浏览器半位于 lib/client.js,与本文件等价。
|
|
8
|
-
* - 功能:在设置面板左侧导航栏顶部(settings.header 槽,priority:-1 shadow)添加搜索框,
|
|
9
|
-
* 输入关键词实时搜索所有已注册设置页(settings.section)与通用设置项
|
|
10
|
-
* (settings.general.item),浮层展示结果,点击后显示左侧导航定位引导。
|
|
11
|
-
* - 图标:SVG 放大镜(fill=currentColor),随主题文字颜色。
|
|
12
|
-
* - 源码是 cordis_define 的 code.client 字段值:一个返回 Cordis Plugin 的纯 JS 函数体,
|
|
13
|
-
* 无 TypeScript / JSX / import。
|
|
14
|
-
*
|
|
15
|
-
* 重新注册方式(会话内):
|
|
16
|
-
* cordis_define({ kind: 'new', idPrefix: 'set' }, ...) 传入本文件的 code.client 内容。
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
inject: ['slots', 'locale'],
|
|
21
|
-
apply(ctx) {
|
|
22
|
-
const NS = 'settings-search-v4'
|
|
23
|
-
ctx.effect(() => ctx.locale.register(NS, {
|
|
24
|
-
zh: {
|
|
25
|
-
title: '设置',
|
|
26
|
-
placeholder: '搜索设置…',
|
|
27
|
-
empty: '没有匹配的设置',
|
|
28
|
-
pages: '设置页',
|
|
29
|
-
rows: '通用设置项',
|
|
30
|
-
guide: '该设置位于左侧导航「{label}」,点击左侧导航项即可打开',
|
|
31
|
-
page: '设置页',
|
|
32
|
-
row: '设置项',
|
|
33
|
-
},
|
|
34
|
-
en: {
|
|
35
|
-
title: 'Settings',
|
|
36
|
-
placeholder: 'Search settings…',
|
|
37
|
-
empty: 'No matching settings',
|
|
38
|
-
pages: 'Settings pages',
|
|
39
|
-
rows: 'General items',
|
|
40
|
-
guide: 'This setting lives in "{label}" — open it from the left navigation',
|
|
41
|
-
page: 'Page',
|
|
42
|
-
row: 'Item',
|
|
43
|
-
},
|
|
44
|
-
}), 'settings-search: dictionaries')
|
|
45
|
-
const t = ctx.locale.bind(NS)
|
|
46
|
-
|
|
47
|
-
styles.insert(`
|
|
48
|
-
.sss-wrap { position: relative; display: flex; flex-direction: column; gap: 8px; }
|
|
49
|
-
.sss-title { font-size: 16px; font-weight: 600; line-height: 24px; color: var(--dsw-alias-label-primary); }
|
|
50
|
-
.sss-box { display: flex; align-items: center; gap: 6px; width: 100%; box-sizing: border-box; height: 32px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; background: var(--dsw-alias-bg-layer-1); }
|
|
51
|
-
.sss-icon { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: var(--dsw-alias-label-tertiary); }
|
|
52
|
-
.sss-input { flex: 1; min-width: 0; border: none; outline: none; background: transparent; font-size: 14px; line-height: 22px; color: var(--dsw-alias-label-primary); }
|
|
53
|
-
.sss-input::placeholder { color: var(--dsw-alias-label-dimmed); }
|
|
54
|
-
.sss-clear { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; padding: 0; border: none; border-radius: 4px; background: transparent; color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 1; cursor: pointer; }
|
|
55
|
-
.sss-clear:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
56
|
-
.sss-pop { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 20; background: var(--dsw-alias-bg-overlay); border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; box-shadow: var(--dsw-shadow-lv3); padding: 6px; max-height: 300px; overflow: auto; }
|
|
57
|
-
.sss-groups { display: flex; flex-direction: column; gap: 8px; }
|
|
58
|
-
.sss-group-title { font-size: 11px; color: var(--dsw-alias-label-secondary); letter-spacing: .05em; padding: 2px 6px; }
|
|
59
|
-
.sss-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
|
|
60
|
-
.sss-item { display: flex; align-items: center; gap: 6px; padding: 7px 8px; border-radius: 7px; cursor: pointer; }
|
|
61
|
-
.sss-item:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
62
|
-
.sss-item-label { flex: 1; min-width: 0; color: var(--dsw-alias-label-primary); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
63
|
-
.sss-item-id { font-size: 10px; color: var(--dsw-alias-label-secondary); flex-shrink: 0; }
|
|
64
|
-
.sss-empty { margin: 0; padding: 10px 8px; color: var(--dsw-alias-label-secondary); font-size: 13px; }
|
|
65
|
-
.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; }
|
|
66
|
-
`)
|
|
67
|
-
|
|
68
|
-
const resolveLabel = (entry) => {
|
|
69
|
-
const raw = entry.options.label
|
|
70
|
-
if (typeof raw === 'function') return String(raw())
|
|
71
|
-
return raw == null ? '' : String(raw)
|
|
72
|
-
}
|
|
73
|
-
const readSections = () => ctx.slots.entries('settings.section')
|
|
74
|
-
.filter((e) => e.options.id !== 'settings-search')
|
|
75
|
-
.map((e) => ({ id: e.options.id, order: e.options.order ?? 0, label: resolveLabel(e) }))
|
|
76
|
-
.sort((a, b) => a.order - b.order)
|
|
77
|
-
const readRows = () => ctx.slots.entries('settings.general.item')
|
|
78
|
-
.map((e) => ({ id: e.options.id, order: e.options.order ?? 0, label: resolveLabel(e) }))
|
|
79
|
-
.sort((a, b) => a.order - b.order)
|
|
80
|
-
|
|
81
|
-
const SearchIcon = React.createElement('svg', {
|
|
82
|
-
className: 'sss-icon',
|
|
83
|
-
viewBox: '0 0 16.5 16.5',
|
|
84
|
-
width: 16,
|
|
85
|
-
height: 16,
|
|
86
|
-
xmlns: 'http://www.w3.org/2000/svg',
|
|
87
|
-
'aria-hidden': 'true',
|
|
88
|
-
}, [
|
|
89
|
-
React.createElement('path', {
|
|
90
|
-
d: 'M11.894845 6.647401C11.894845 3.725463 9.534486 1.356779 6.623219 1.35657C3.711786 1.35657 1.351635 3.725338 1.351635 6.647401C1.351843 9.569296 3.711911 11.938273 6.623219 11.938273C9.534361 11.938064 11.894637 9.569171 11.894845 6.647401ZM13.245462 6.647401C13.245254 10.317935 10.280401 13.293613 6.623219 13.293821C2.965871 13.293821 0.000204 10.31806 0 6.647401C0 2.976574 2.965746 0 6.623219 0C10.280526 0.000205 13.245462 2.9767 13.245462 6.647401Z',
|
|
91
|
-
fill: 'currentColor',
|
|
92
|
-
}),
|
|
93
|
-
React.createElement('path', {
|
|
94
|
-
d: 'M16.000417 15.041079L15.044449 16.000433L11.530434 12.473588L12.486298 11.514234L16.000417 15.041079Z',
|
|
95
|
-
fill: 'currentColor',
|
|
96
|
-
}),
|
|
97
|
-
])
|
|
98
|
-
|
|
99
|
-
function SettingsHeaderSearch() {
|
|
100
|
-
const [query, setQuery] = React.useState('')
|
|
101
|
-
const [open, setOpen] = React.useState(false)
|
|
102
|
-
const [sections, setSections] = React.useState(readSections)
|
|
103
|
-
const [rows, setRows] = React.useState(readRows)
|
|
104
|
-
const [selected, setSelected] = React.useState(null)
|
|
105
|
-
React.useEffect(() => {
|
|
106
|
-
const refresh = () => {
|
|
107
|
-
setSections(readSections())
|
|
108
|
-
setRows(readRows())
|
|
109
|
-
}
|
|
110
|
-
const offs = [
|
|
111
|
-
ctx.slots.subscribe('settings.section', refresh),
|
|
112
|
-
ctx.slots.subscribe('settings.general.item', refresh),
|
|
113
|
-
ctx.locale.subscribe(refresh),
|
|
114
|
-
]
|
|
115
|
-
return () => { for (const off of offs) off() }
|
|
116
|
-
}, [])
|
|
117
|
-
|
|
118
|
-
const q = query.trim().toLowerCase()
|
|
119
|
-
const tokens = q.split(/\s+/).filter(Boolean)
|
|
120
|
-
const match = (label, id) => tokens.every((tok) => (label + ' ' + id).toLowerCase().includes(tok))
|
|
121
|
-
const shownSections = tokens.length ? sections.filter((s) => match(s.label, s.id)) : []
|
|
122
|
-
const shownRows = tokens.length ? rows.filter((r) => match(r.label, r.id)) : []
|
|
123
|
-
const none = tokens.length > 0 && shownSections.length === 0 && shownRows.length === 0
|
|
124
|
-
|
|
125
|
-
const renderItem = (item, kind) => React.createElement('li', {
|
|
126
|
-
key: item.id,
|
|
127
|
-
className: 'sss-item',
|
|
128
|
-
onMouseDown: () => { setSelected(item); setOpen(false) },
|
|
129
|
-
}, [
|
|
130
|
-
React.createElement('span', { className: 'sss-item-label' }, item.label || item.id),
|
|
131
|
-
React.createElement('span', { className: 'sss-item-id' }, kind),
|
|
132
|
-
])
|
|
133
|
-
|
|
134
|
-
return React.createElement('div', { className: 'sss-wrap' }, [
|
|
135
|
-
React.createElement('div', { className: 'sss-title' }, t('title')),
|
|
136
|
-
React.createElement('div', { className: 'sss-box' }, [
|
|
137
|
-
SearchIcon,
|
|
138
|
-
React.createElement('input', {
|
|
139
|
-
className: 'sss-input',
|
|
140
|
-
value: query,
|
|
141
|
-
placeholder: t('placeholder'),
|
|
142
|
-
spellCheck: false,
|
|
143
|
-
onChange: (e) => { setQuery(e.target.value); setOpen(true) },
|
|
144
|
-
onFocus: () => setOpen(true),
|
|
145
|
-
onBlur: () => setOpen(false),
|
|
146
|
-
}),
|
|
147
|
-
query !== '' ? React.createElement('button', {
|
|
148
|
-
className: 'sss-clear',
|
|
149
|
-
type: 'button',
|
|
150
|
-
onClick: () => { setQuery(''); setSelected(null); setOpen(false) },
|
|
151
|
-
}, '✕') : null,
|
|
152
|
-
]),
|
|
153
|
-
open && tokens.length > 0 ? React.createElement('div', { className: 'sss-pop' }, [
|
|
154
|
-
none
|
|
155
|
-
? React.createElement('p', { className: 'sss-empty' }, t('empty'))
|
|
156
|
-
: React.createElement('div', { className: 'sss-groups' }, [
|
|
157
|
-
shownSections.length > 0 ? React.createElement('div', { className: 'sss-group' }, [
|
|
158
|
-
React.createElement('div', { className: 'sss-group-title' }, t('pages')),
|
|
159
|
-
React.createElement('ul', { className: 'sss-list' }, shownSections.map((s) => renderItem(s, t('page')))),
|
|
160
|
-
]) : null,
|
|
161
|
-
shownRows.length > 0 ? React.createElement('div', { className: 'sss-group' }, [
|
|
162
|
-
React.createElement('div', { className: 'sss-group-title' }, t('rows')),
|
|
163
|
-
React.createElement('ul', { className: 'sss-list' }, shownRows.map((r) => renderItem(r, t('row')))),
|
|
164
|
-
]) : null,
|
|
165
|
-
]),
|
|
166
|
-
]) : null,
|
|
167
|
-
selected !== null
|
|
168
|
-
? React.createElement('div', { className: 'sss-guide' }, t('guide').replace('{label}', selected.label || selected.id))
|
|
169
|
-
: null,
|
|
170
|
-
])
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
ctx.slots.inject('settings.header', () => ctx.slots.register({
|
|
174
|
-
name: 'settings.header',
|
|
175
|
-
priority: -1,
|
|
176
|
-
locale: NS,
|
|
177
|
-
}, SettingsHeaderSearch))
|
|
178
|
-
},
|
|
179
|
-
}
|