@objectivex666/dsh-settings-search 1.3.0 → 1.4.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 +126 -0
- package/package.json +1 -1
- package/scripts/check.mjs +6 -0
package/README.en.md
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
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
|
- 🧭 **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.
|
|
18
18
|
- 🌱 **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.
|
|
19
|
+
- 🛠 **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.
|
|
19
20
|
- 🌐 **i18n** — built-in Chinese/English dictionaries that follow the DSH localization system and are easy to extend.
|
|
20
21
|
- 🎨 **Theme-aware** — styled entirely with CSS variables, automatically following the DSH theme (light/dark).
|
|
21
22
|
- ⚡ **Reactive** — subscribes to settings entries being added, removed, or changed, and refreshes results automatically.
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
- 📂 **页内选项搜索(v1.2.0)**:不只搜页面,还能直达每个设置页内部的具体选项——插件页的标签页(`settings.plugins.tab`)、Web UI 插件的卡片(`web-ui.plugin.item`)、通用设置行等,结果以「页面 › 选项」面包屑展示。
|
|
17
17
|
- 🧭 **点击即达**:选中结果后自动点击对应的左侧导航项打开所在分区;若是页内标签页会继续点开对应标签,并高亮闪烁目标选项行。无法自动跳转时显示手动路径提示。
|
|
18
18
|
- 🌱 **渐进索引**:自绘界面的设置行会在其所在页面首次被访问时从渲染 DOM 中收割标题并加入索引(此后始终可搜)。
|
|
19
|
+
- 🛠 **自带设置页(v1.4.0)**:左侧导航新增「设置搜索」页——展示当前版本,一键检查 npm 注册表上的最新版本;有更新时直接给出复制就用的 `dsh plugin update` 命令。
|
|
19
20
|
- 🌐 **多语言支持**:内置中英文国际化,可跟随 DSH 本地化系统扩展。
|
|
20
21
|
- 🎨 **主题适配**:完全使用 CSS 变量,自动跟随 DSH 主题(亮/暗)。
|
|
21
22
|
- ⚡ **动态响应**:实时订阅设置项的新增/删除/变更,搜索结果自动刷新。
|
package/lib/client.js
CHANGED
|
@@ -29,6 +29,9 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
const React = require('react')
|
|
30
30
|
|
|
31
31
|
const NS = 'settings-search-v5'
|
|
32
|
+
/** Single source of truth is package.json — scripts/check.mjs asserts this matches. */
|
|
33
|
+
const PKG_VERSION = '1.4.0'
|
|
34
|
+
const NPM_NAME = '@objectivex666/dsh-settings-search'
|
|
32
35
|
const zh = {
|
|
33
36
|
title: '设置',
|
|
34
37
|
placeholder: '搜索设置…',
|
|
@@ -38,6 +41,17 @@ window.__ModuleLoader__.load({
|
|
|
38
41
|
page: '页面',
|
|
39
42
|
option: '选项',
|
|
40
43
|
jumpFail: '未能自动跳转,请手动打开:{path}',
|
|
44
|
+
updn: '设置搜索',
|
|
45
|
+
updDesc: '设置面板顶部搜索框由本插件提供。在此检查插件的版本与更新。',
|
|
46
|
+
updCurrent: '当前版本',
|
|
47
|
+
updLatestKnown: 'npm 最新版',
|
|
48
|
+
updCheck: '检查更新',
|
|
49
|
+
updChecking: '正在检查…',
|
|
50
|
+
updNewest: '已是最新版本 ✓',
|
|
51
|
+
updFound: '发现新版本 {ver},可执行以下命令更新后重启 DSH:',
|
|
52
|
+
updErr: '检查失败(网络或注册表不可达),请稍后重试。',
|
|
53
|
+
updCopy: '复制命令',
|
|
54
|
+
updCopied: '已复制 ✓',
|
|
41
55
|
}
|
|
42
56
|
const en = {
|
|
43
57
|
title: 'Settings',
|
|
@@ -48,6 +62,17 @@ window.__ModuleLoader__.load({
|
|
|
48
62
|
page: 'Page',
|
|
49
63
|
option: 'Option',
|
|
50
64
|
jumpFail: 'Could not navigate automatically — open it here: {path}',
|
|
65
|
+
updn: 'Settings Search',
|
|
66
|
+
updDesc: "The search box atop the settings panel comes from this plugin. Check its version and updates here.",
|
|
67
|
+
updCurrent: 'Current version',
|
|
68
|
+
updLatestKnown: 'npm latest',
|
|
69
|
+
updCheck: 'Check for updates',
|
|
70
|
+
updChecking: 'Checking…',
|
|
71
|
+
updNewest: 'You are on the latest version ✓',
|
|
72
|
+
updFound: 'Version {ver} available. Run the command below, then restart DSH:',
|
|
73
|
+
updErr: 'Check failed (network or registry unreachable). Try again later.',
|
|
74
|
+
updCopy: 'Copy command',
|
|
75
|
+
updCopied: 'Copied ✓',
|
|
51
76
|
}
|
|
52
77
|
|
|
53
78
|
/** Inner-row slots that represent specific options inside one page. */
|
|
@@ -80,6 +105,19 @@ window.__ModuleLoader__.load({
|
|
|
80
105
|
.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; }
|
|
81
106
|
@keyframes sssFlashBg { 0% { background-color: var(--dsw-alias-interactive-bg-hover); } 100% { background-color: transparent; } }
|
|
82
107
|
.sss-flash { animation: sssFlashBg 1.8s ease-out; border-radius: 8px; }
|
|
108
|
+
.sss-upd { display: flex; flex-direction: column; gap: 12px; padding: 16px; max-width: 560px; }
|
|
109
|
+
.sss-upd-title { font-size: 15px; font-weight: 600; line-height: 22px; color: var(--dsw-alias-label-primary); margin: 0; }
|
|
110
|
+
.sss-upd-desc { margin: 0; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
|
|
111
|
+
.sss-upd-card { display: flex; flex-direction: column; gap: 10px; padding: 14px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; background: var(--dsw-alias-bg-layer-1); }
|
|
112
|
+
.sss-upd-row { display: flex; align-items: center; gap: 8px; font-size: 13px; line-height: 20px; }
|
|
113
|
+
.sss-upd-key { flex-shrink: 0; width: 88px; color: var(--dsw-alias-label-secondary); }
|
|
114
|
+
.sss-upd-val { color: var(--dsw-alias-label-primary); }
|
|
115
|
+
.sss-upd-badge { flex-shrink: 0; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 18px; }
|
|
116
|
+
.sss-btn { height: 28px; padding: 0 12px; border-radius: 7px; border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font-size: 12px; cursor: pointer; }
|
|
117
|
+
.sss-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
118
|
+
.sss-btn:disabled { opacity: .55; cursor: default; }
|
|
119
|
+
.sss-cmd { user-select: all; font-family: ui-monospace, Consolas, 'Courier New', monospace; font-size: 11.5px; line-height: 20px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; padding: 4px 8px; word-break: break-all; }
|
|
120
|
+
.sss-upd-note { margin: 0; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
|
|
83
121
|
`
|
|
84
122
|
let stylesInstalled = false
|
|
85
123
|
function installStyles() {
|
|
@@ -463,6 +501,94 @@ window.__ModuleLoader__.load({
|
|
|
463
501
|
])
|
|
464
502
|
}
|
|
465
503
|
|
|
504
|
+
const compareSemver = (a, b) => {
|
|
505
|
+
const pa = String(a).split('.').map((n) => parseInt(n, 10) || 0)
|
|
506
|
+
const pb = String(b).split('.').map((n) => parseInt(n, 10) || 0)
|
|
507
|
+
for (let i = 0; i < 3; i += 1) {
|
|
508
|
+
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0)
|
|
509
|
+
}
|
|
510
|
+
return 0
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function UpdatePanel() {
|
|
514
|
+
const [state, setState] = React.useState('idle') // idle | loading | newest | outdated | error
|
|
515
|
+
const [latest, setLatest] = React.useState('')
|
|
516
|
+
const [copied, setCopied] = React.useState(false)
|
|
517
|
+
|
|
518
|
+
React.useEffect(() => {
|
|
519
|
+
if (!copied) return
|
|
520
|
+
const off = setTimeout(() => setCopied(false), 1600)
|
|
521
|
+
return () => clearTimeout(off)
|
|
522
|
+
}, [copied])
|
|
523
|
+
|
|
524
|
+
const check = async () => {
|
|
525
|
+
setState('loading')
|
|
526
|
+
setCopied(false)
|
|
527
|
+
try {
|
|
528
|
+
const res = await fetch(`https://registry.npmjs.org/${NPM_NAME}/latest?t=${Date.now()}`)
|
|
529
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
530
|
+
const data = await res.json()
|
|
531
|
+
const ver = typeof data?.version === 'string' ? data.version : ''
|
|
532
|
+
if (ver === '') throw new Error('no version in registry response')
|
|
533
|
+
setLatest(ver)
|
|
534
|
+
setState(compareSemver(ver, PKG_VERSION) > 0 ? 'outdated' : 'newest')
|
|
535
|
+
} catch {
|
|
536
|
+
setState('error')
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const copyCmd = async () => {
|
|
541
|
+
const cmd = `dsh plugin update ${NPM_NAME}`
|
|
542
|
+
try { await navigator.clipboard.writeText(cmd); setCopied(true) } catch { /* user can copy manually */ }
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const statusNode = {
|
|
546
|
+
idle: React.createElement('span', { className: 'sss-upd-val' }, '—'),
|
|
547
|
+
loading: React.createElement('span', { className: 'sss-upd-val' }, t('updChecking')),
|
|
548
|
+
newest: React.createElement('span', { className: 'sss-upd-val' }, t('updNewest')),
|
|
549
|
+
error: React.createElement('span', { className: 'sss-upd-val' }, t('updErr')),
|
|
550
|
+
outdated: latest === '' ? null : React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } }, [
|
|
551
|
+
React.createElement('p', { className: 'sss-upd-note' }, t('updFound').replace('{ver}', `v${latest}`)),
|
|
552
|
+
React.createElement('code', { className: 'sss-cmd' }, `dsh plugin update ${NPM_NAME}`),
|
|
553
|
+
React.createElement('div', null,
|
|
554
|
+
React.createElement('button', { className: 'sss-btn', type: 'button', onClick: () => { void copyCmd() } },
|
|
555
|
+
copied ? t('updCopied') : t('updCopy'))),
|
|
556
|
+
]),
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return React.createElement('div', { className: 'sss-upd' }, [
|
|
560
|
+
React.createElement('h3', { className: 'sss-upd-title' }, `${t('updn')} v${PKG_VERSION}`),
|
|
561
|
+
React.createElement('p', { className: 'sss-upd-desc' }, t('updDesc')),
|
|
562
|
+
React.createElement('div', { className: 'sss-upd-card' }, [
|
|
563
|
+
React.createElement('div', { className: 'sss-upd-row' }, [
|
|
564
|
+
React.createElement('span', { className: 'sss-upd-key' }, t('updCurrent')),
|
|
565
|
+
React.createElement('code', { className: 'sss-cmd' }, PKG_VERSION),
|
|
566
|
+
state === 'newest' ? React.createElement('span', { className: 'sss-upd-badge' }, 'npm ✓') : null,
|
|
567
|
+
]),
|
|
568
|
+
state === 'outdated' || state === 'loading'
|
|
569
|
+
? React.createElement('div', { className: 'sss-upd-row' }, [
|
|
570
|
+
React.createElement('span', { className: 'sss-upd-key' }, t('updLatestKnown')),
|
|
571
|
+
state === 'loading' ? React.createElement('span', { className: 'sss-upd-val' }, '…') : React.createElement('code', { className: 'sss-cmd' }, latest),
|
|
572
|
+
])
|
|
573
|
+
: null,
|
|
574
|
+
state === 'outdated' || state === 'loading' || state === 'error' || state === 'idle' || state === 'newest'
|
|
575
|
+
? (state === 'outdated' ? statusNode.outdated : statusNode[state])
|
|
576
|
+
: null,
|
|
577
|
+
React.createElement('div', { className: 'sss-upd-row' },
|
|
578
|
+
React.createElement('button', { className: 'sss-btn', type: 'button', disabled: state === 'loading', onClick: () => { void check() } },
|
|
579
|
+
state === 'loading' ? t('updChecking') : t('updCheck'))),
|
|
580
|
+
]),
|
|
581
|
+
])
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
585
|
+
name: 'settings.section',
|
|
586
|
+
id: 'settings-search',
|
|
587
|
+
order: 1600,
|
|
588
|
+
locale: NS,
|
|
589
|
+
label: () => t('updn'),
|
|
590
|
+
}, UpdatePanel))
|
|
591
|
+
|
|
466
592
|
ctx.slots.inject('settings.header', () => ctx.slots.register({
|
|
467
593
|
name: 'settings.header',
|
|
468
594
|
priority: -1,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectivex666/dsh-settings-search",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|
package/scripts/check.mjs
CHANGED
|
@@ -54,6 +54,12 @@ check(typeof client.apply === 'function', 'client half must export apply(ctx)')
|
|
|
54
54
|
check(Array.isArray(client.inject) && client.inject.includes('slots') && client.inject.includes('locale'),
|
|
55
55
|
'client half inject must include "slots" and "locale"')
|
|
56
56
|
|
|
57
|
+
// 4. embedded version constant matches the manifest (update panel displays it)
|
|
58
|
+
const versionMatch = /const PKG_VERSION = '([^']+)'/.exec(source)
|
|
59
|
+
check(versionMatch !== null, 'lib/client.js must define const PKG_VERSION')
|
|
60
|
+
check(versionMatch?.[1] === pkg.version,
|
|
61
|
+
`PKG_VERSION (${versionMatch?.[1]}) must equal package.json version (${pkg.version})`)
|
|
62
|
+
|
|
57
63
|
if (failed > 0) {
|
|
58
64
|
console.error(`${failed} check(s) failed`)
|
|
59
65
|
process.exit(1)
|