@mzzsfy/dsh-settings-nav-icons 0.3.0 → 0.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.md +3 -3
- package/package.json +1 -1
- package/src/client.js +18 -0
- package/src/logic.mjs +10 -0
- package/test/orchestration.test.mjs +10 -4
- package/test/parity.test.mjs +10 -3
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ DeepSeek Harness 纯前端插件:设置弹窗左侧导航与 dsh-market 插件
|
|
|
38
38
|
|
|
39
39
|
- **外部声明**:其他插件运行时注册自己的 label/插件名 → 图标,优先级最高(见下节)。
|
|
40
40
|
- **内置映射**:官方分区与无法改源的第三方分区。
|
|
41
|
-
- **关键词**:插件名/分区名整词命中语义关键词得主题图(git→分支、im→机器人、search→放大镜、usage
|
|
41
|
+
- **关键词**:插件名/分区名整词命中语义关键词得主题图(git→分支、im→机器人、search→放大镜、usage→图表、wallet→钱包、dash→表盘等 21 组规则);整词边界匹配,`im` 不误伤 `important`。
|
|
42
42
|
- **备用池**:星芒/层叠/标签/网格四个中性图形,按名称稳定哈希取一个——同一名称永远同一图形,重渲染不闪动。dsh-market 目录内全部插件(含下载量前 1000)至此都有图标。
|
|
43
43
|
|
|
44
44
|
## 图标声明机制(其他插件接入)
|
|
@@ -54,12 +54,12 @@ window.__navicIcons.register({ '消息通知': 'bell' })
|
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
- 键:分区显示文本(设置导航)或插件名(dsh-market 卡片)。两域共用一张表,同名时以先命中者生效,键请取不易与官方分区撞名的插件名。官方双语分区按等价组解析:任一语言 label 作键,两种语言下均命中(消除语言切换的声明键漂移)。
|
|
57
|
-
- 值:内置 glyph 名(`tune/theme/bot/market/cube/mcp/shield/cards/plan/bell/wrench/archive/spark/layers/tag/grid/git/search/term/chart/code/doc/db/flow/globe/lock/image/zap`)或完整 16×16 `<svg>` 字符串。svg 字符串过安全门:完整开标签(大小写不敏感,拒绝 `<svgx` 残串)且单根闭合(首个 `</svg>` 后不得再有内容,堵尾缀活动 HTML);不带 `on*` 事件属性(`\b` 前界堵斜杠分隔绕过)、不带 `<script>`/`<style>`(内联样式全文档生效且 @import 可外联)/`<foreignObject>`/SMIL 动画(`<animate>`/`<set>` 等)载体;不带 `href`/`xlink:href` 及 `attributeName="href"` 注入(16×16 静态图标无合法引用/动画场景,外联请求一并封死);不带 `javascript:`(纵深);长度 ≤4096 字符;glyph 名查表经 `typeof` 收口,原型链成员不可能被注入。
|
|
57
|
+
- 值:内置 glyph 名(`tune/theme/bot/market/cube/mcp/shield/cards/plan/bell/wrench/archive/spark/layers/tag/grid/git/search/term/chart/code/doc/db/flow/globe/lock/image/zap/wallet/gauge`)或完整 16×16 `<svg>` 字符串。svg 字符串过安全门:完整开标签(大小写不敏感,拒绝 `<svgx` 残串)且单根闭合(首个 `</svg>` 后不得再有内容,堵尾缀活动 HTML);不带 `on*` 事件属性(`\b` 前界堵斜杠分隔绕过)、不带 `<script>`/`<style>`(内联样式全文档生效且 @import 可外联)/`<foreignObject>`/SMIL 动画(`<animate>`/`<set>` 等)载体;不带 `href`/`xlink:href` 及 `attributeName="href"` 注入(16×16 静态图标无合法引用/动画场景,外联请求一并封死);不带 `javascript:`(纵深);长度 ≤4096 字符;glyph 名查表经 `typeof` 收口,原型链成员不可能被注入。
|
|
58
58
|
- 声明值经归一化后写入注册表;同值重复注册幂等短路;非法值(未知 glyph/被安全门拒绝/非字符串)撤销该键声明,该分区回到内置映射或关键词/哈希默认管线,已改写的 nav svg 内容就地重写。
|
|
59
59
|
- 声明持久化在 `window.__navicIconDeclarations`:本插件 client 半区热重载会重跑工厂而生产者不重发注册,持久层让重装实例恢复声明,页面刷新随 window 释放。
|
|
60
60
|
- 污染面收敛在 `window.__navicIcons` 单一命名空间,插件卸载时移除 API、取消已排定的重绘、队列恢复数组形态——卸载后生产者按上方「方式二」入队等待下一实例,不再驱动 DOM 改写;重载页面后全部还原为官方图标。
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
本仓库全部插件包(含本包自画像与仅 host 半区的包)都走此机制显式声明,不依赖关键词/哈希推导;键为「分区 label + 市场短名」双形态:usage-panel(账号余额、dsh-usage-panel → wallet)、usage-dash(使用统计/Usage、dsh-usage-dash → gauge)、turn-notify(消息通知、dsh-turn-notify → bell)、maintain(版本与运维、dsh-maintain → wrench)、session-manager(会话归档、dsh-session-manager → archive)、rs-workflow(若水工作流、dsh-rs-workflow → flow)、cron-board(dsh-cron-board → plan)、think-expand(dsh-think-expand → spark)、model-capability-editor(dsh-model-capability-editor → cube)、auto-trust-all(dsh-auto-trust-all → shield)、llm-pi-gateway(dsh-llm-pi-gateway → globe)、settings-nav-icons(dsh-settings-nav-icons → tune);仅 host 半区的 auto-trust-all 与 llm-pi-gateway 为声明专设最小 client 半区。注册样板由本包测试做契约锁定。
|
|
63
63
|
|
|
64
64
|
## 内置映射表
|
|
65
65
|
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
// 背景见 README「实现边界」:settings.* 子槽声明权被原版条目占用,
|
|
4
4
|
// 接管 Shell 不可行,DOM 观察是唯一不依赖官方契约变更的路径。
|
|
5
5
|
|
|
6
|
+
// 本插件市场短名自画像:经同一套声明机制注册(先于 load,渲染端同文件就绪)
|
|
7
|
+
if (typeof window !== 'undefined') {
|
|
8
|
+
const NAV_ICON = { 'dsh-settings-nav-icons': 'tune' }
|
|
9
|
+
if (window.__navicIcons !== undefined) window.__navicIcons.register(NAV_ICON)
|
|
10
|
+
else if (Array.isArray(window.__navicIconQueue)) window.__navicIconQueue.push(NAV_ICON)
|
|
11
|
+
else window.__navicIconQueue = [NAV_ICON]
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
window.__ModuleLoader__.load({
|
|
7
15
|
id: '@mzzsfy/dsh-settings-nav-icons',
|
|
8
16
|
factory(require) {
|
|
@@ -387,6 +395,13 @@ window.__ModuleLoader__.load({
|
|
|
387
395
|
rectOf('2.5', '3', '11', '10', '1.2') +
|
|
388
396
|
circleOf('5.9', '6.1', '1') + pathOf('m3.6 12 3-3 2.3 2.3 1.7-1.7 1.9 1.9'))
|
|
389
397
|
const ZAP = svgOf(pathOf('M8.9 1.8 3.8 8.9h3.5L7 14.2l5.2-7.1H8.6z'))
|
|
398
|
+
const WALLET = svgOf(
|
|
399
|
+
rectOf('2.5', '3.5', '11', '9.5', '1.5') +
|
|
400
|
+
pathOf('M13.5 7.4h-2.1a1.6 1.6 0 0 0 0 3.2h2.1'))
|
|
401
|
+
const GAUGE = svgOf(
|
|
402
|
+
pathOf('M2.5 12a5.5 5.5 0 1 1 11 0') +
|
|
403
|
+
pathOf('M8 12 10.8 9.2') +
|
|
404
|
+
pathOf('M2.5 12h11'))
|
|
390
405
|
const FALLBACK = [SPARK, LAYERS, TAG, GRID]
|
|
391
406
|
|
|
392
407
|
// 内置 glyph 名称表:声明值可用名称引用内置图形,避免跨插件复制 svg。
|
|
@@ -396,6 +411,7 @@ window.__ModuleLoader__.load({
|
|
|
396
411
|
archive: ARCHIVE, spark: SPARK, layers: LAYERS, tag: TAG, grid: GRID,
|
|
397
412
|
git: GIT, search: SEARCH, term: TERM, chart: CHART, code: CODE, doc: DOC,
|
|
398
413
|
db: DB, flow: FLOW, globe: GLOBE, lock: LOCK, image: IMAGE, zap: ZAP,
|
|
414
|
+
wallet: WALLET, gauge: GAUGE,
|
|
399
415
|
}
|
|
400
416
|
|
|
401
417
|
// 声明值解析与安全门:svg 须完整开标签(大小写不敏感)且单根闭合(首个 </svg>
|
|
@@ -437,7 +453,9 @@ window.__ModuleLoader__.load({
|
|
|
437
453
|
['doc|note|file|markdown|wiki', DOC],
|
|
438
454
|
['database|sqlite|redis|cache|db', DB],
|
|
439
455
|
['session|archive|history', ARCHIVE],
|
|
456
|
+
['dash|dashboard|gauge', GAUGE],
|
|
440
457
|
['usage|stat|meter|monitor|radar|metric', CHART],
|
|
458
|
+
['wallet|balance|money|coin|fund', WALLET],
|
|
441
459
|
['flow|workflow|pipeline|task|job|queue', FLOW],
|
|
442
460
|
['auth|login|pass|secret|token|lock|guard|crypt', SHIELD],
|
|
443
461
|
['mcp', MCP],
|
package/src/logic.mjs
CHANGED
|
@@ -109,6 +109,13 @@ export const IMAGE = svgOf(
|
|
|
109
109
|
rectOf('2.5', '3', '11', '10', '1.2') +
|
|
110
110
|
circleOf('5.9', '6.1', '1') + pathOf('m3.6 12 3-3 2.3 2.3 1.7-1.7 1.9 1.9'))
|
|
111
111
|
export const ZAP = svgOf(pathOf('M8.9 1.8 3.8 8.9h3.5L7 14.2l5.2-7.1H8.6z'))
|
|
112
|
+
export const WALLET = svgOf(
|
|
113
|
+
rectOf('2.5', '3.5', '11', '9.5', '1.5') +
|
|
114
|
+
pathOf('M13.5 7.4h-2.1a1.6 1.6 0 0 0 0 3.2h2.1'))
|
|
115
|
+
export const GAUGE = svgOf(
|
|
116
|
+
pathOf('M2.5 12a5.5 5.5 0 1 1 11 0') +
|
|
117
|
+
pathOf('M8 12 10.8 9.2') +
|
|
118
|
+
pathOf('M2.5 12h11'))
|
|
112
119
|
|
|
113
120
|
export const FALLBACK = [SPARK, LAYERS, TAG, GRID]
|
|
114
121
|
|
|
@@ -119,6 +126,7 @@ export const GLYPHS = {
|
|
|
119
126
|
archive: ARCHIVE, spark: SPARK, layers: LAYERS, tag: TAG, grid: GRID,
|
|
120
127
|
git: GIT, search: SEARCH, term: TERM, chart: CHART, code: CODE, doc: DOC,
|
|
121
128
|
db: DB, flow: FLOW, globe: GLOBE, lock: LOCK, image: IMAGE, zap: ZAP,
|
|
129
|
+
wallet: WALLET, gauge: GAUGE,
|
|
122
130
|
}
|
|
123
131
|
|
|
124
132
|
// 声明值解析与安全门:svg 须完整开标签(大小写不敏感)且单根闭合(首个 </svg>
|
|
@@ -183,7 +191,9 @@ export const NAME_RULES = [
|
|
|
183
191
|
['doc|note|file|markdown|wiki', DOC],
|
|
184
192
|
['database|sqlite|redis|cache|db', DB],
|
|
185
193
|
['session|archive|history', ARCHIVE],
|
|
194
|
+
['dash|dashboard|gauge', GAUGE],
|
|
186
195
|
['usage|stat|meter|monitor|radar|metric', CHART],
|
|
196
|
+
['wallet|balance|money|coin|fund', WALLET],
|
|
187
197
|
['flow|workflow|pipeline|task|job|queue', FLOW],
|
|
188
198
|
['auth|login|pass|secret|token|lock|guard|crypt', SHIELD],
|
|
189
199
|
['mcp', MCP],
|
|
@@ -300,11 +300,12 @@ test('启动样式代际:HMR 不卸载重评估由新实例代拆,旧形槽跨
|
|
|
300
300
|
test('registerIcons 输入校验:数组与空值拒收', () => {
|
|
301
301
|
const { win, doc, raf } = loadClient()
|
|
302
302
|
const unload = boot(win.__loaded, doc)
|
|
303
|
+
raf.flush() // 排空启动期自声明的重贴,后续断言只看本次入参的增量
|
|
303
304
|
try {
|
|
304
305
|
win.__navicIcons.register(['bell'])
|
|
305
306
|
win.__navicIcons.register(null)
|
|
306
307
|
win.__navicIcons.register('bell')
|
|
307
|
-
assert.deepEqual(Object.keys(win.__navicIconDeclarations ?? {}), [], '非法入参不写入注册表')
|
|
308
|
+
assert.deepEqual(Object.keys(win.__navicIconDeclarations ?? {}), ['dsh-settings-nav-icons'], '非法入参不写入注册表(仅启动期自声明在场)')
|
|
308
309
|
assert.equal(raf.size(), 0, '非法入参不触发重贴')
|
|
309
310
|
assert.equal(doc.marked.length, 0)
|
|
310
311
|
} finally {
|
|
@@ -402,6 +403,7 @@ test('队列时序:畸形条目不中断排空,同键后者覆盖,排空先于
|
|
|
402
403
|
test('卸载:__navicIcons 删除,rAF 取消,异常不外抛', () => {
|
|
403
404
|
const { win, doc, raf } = loadClient()
|
|
404
405
|
const unload = boot(win.__loaded, doc)
|
|
406
|
+
raf.flush() // 排空启动期自声明的重贴,断言只看本次注册的增量
|
|
405
407
|
win.__navicIcons.register({ bell: 'bell' })
|
|
406
408
|
assert.equal(raf.size(), 1, '重贴已排定')
|
|
407
409
|
unload()
|
|
@@ -536,6 +538,7 @@ test('头像槽换名:重贴清行内旧注入(父容器范围),外来兄弟不
|
|
|
536
538
|
test('rAF 合批:同一帧内多次 register 只排一帧,flush 后重新排定', () => {
|
|
537
539
|
const { win, doc, raf } = loadClient()
|
|
538
540
|
const unload = boot(win.__loaded, doc)
|
|
541
|
+
raf.flush() // 排空启动期自声明的重贴,断言只看本次注册的增量
|
|
539
542
|
try {
|
|
540
543
|
win.__navicIcons.register({ a: 'bell' })
|
|
541
544
|
win.__navicIcons.register({ b: 'wrench' })
|
|
@@ -567,6 +570,7 @@ test('replacePass 逐项异常隔离:单项失败不中断其余', () => {
|
|
|
567
570
|
test('onMutations 过滤:元素级变更唤醒,文本节点与域外文本不唤醒', () => {
|
|
568
571
|
const { win, doc, raf, observers } = loadClient()
|
|
569
572
|
const unload = boot(win.__loaded, doc)
|
|
573
|
+
raf.flush() // 排空启动期自声明的重贴,断言只看本次变更的增量
|
|
570
574
|
try {
|
|
571
575
|
const observer = observers[observers.length - 1]
|
|
572
576
|
const inCellText = { parentElement: { closest(sel) { return sel.includes('navCell') ? {} : null } } }
|
|
@@ -740,10 +744,12 @@ test('用户覆盖:非 nav 单元格右键不弹浮层', () => {
|
|
|
740
744
|
}
|
|
741
745
|
})
|
|
742
746
|
|
|
743
|
-
//
|
|
747
|
+
// 声明侧契约:全部生产者包的注册样板(先探测入口、数组态入队),改契约须同步全部生产者。
|
|
744
748
|
// 包脱离 monorepo 布局(发布态)时跳过:无兄弟包源码可读。
|
|
745
|
-
test('
|
|
746
|
-
const producers = ['dsh-session-manager', 'dsh-usage-panel', 'dsh-turn-notify', 'dsh-maintain'
|
|
749
|
+
test('生产者样板契约:全包注册走 __navicIcons/__navicIconQueue', () => {
|
|
750
|
+
const producers = ['dsh-session-manager', 'dsh-usage-panel', 'dsh-turn-notify', 'dsh-maintain', 'dsh-rs-workflow',
|
|
751
|
+
'dsh-usage-dash', 'dsh-cron-board', 'dsh-think-expand', 'dsh-model-capability-editor', 'dsh-settings-nav-icons',
|
|
752
|
+
'dsh-auto-trust-all', 'dsh-llm-pi-gateway']
|
|
747
753
|
let checked = 0
|
|
748
754
|
for (const pkg of producers) {
|
|
749
755
|
const clientPath = join(PKG_ROOT, '..', pkg, 'src', 'client.js')
|
package/test/parity.test.mjs
CHANGED
|
@@ -274,7 +274,7 @@ function defineScenarios(prefix, L) {
|
|
|
274
274
|
})
|
|
275
275
|
|
|
276
276
|
test(prefix + '关键词规则整词边界与跨规则优先级', () => {
|
|
277
|
-
const { themedIcon, FALLBACK } = L
|
|
277
|
+
const { themedIcon, FALLBACK, GLYPHS } = L
|
|
278
278
|
assert.equal(themedIcon('important'), FALLBACK[poolIndexOf('important')], 'im 不命中 important')
|
|
279
279
|
assert.equal(themedIcon('dbtool'), FALLBACK[poolIndexOf('dbtool')], 'db 不命中 dbtool')
|
|
280
280
|
assert.equal(themedIcon('my db tool'), themedIcon('db'), '独立词 db 命中')
|
|
@@ -283,6 +283,13 @@ function defineScenarios(prefix, L) {
|
|
|
283
283
|
// 顺序即优先级,具体语义在前:双规则同打名取靠前者
|
|
284
284
|
assert.equal(themedIcon('search-bot'), themedIcon('search'), 'search 先于 bot')
|
|
285
285
|
assert.equal(themedIcon('git-notify'), themedIcon('git'), 'git 先于 notify')
|
|
286
|
+
// 余额语义:wallet 系关键词命中钱包图,plan 系关键词仍命中时钟
|
|
287
|
+
assert.equal(themedIcon('account-balance'), GLYPHS.wallet, 'balance 命中钱包')
|
|
288
|
+
assert.equal(themedIcon('my-wallet-tool'), GLYPHS.wallet, 'wallet 命中钱包')
|
|
289
|
+
assert.equal(themedIcon('cron-board'), GLYPHS.plan, 'cron 仍命中时钟')
|
|
290
|
+
// 仪表盘语义:dash 系关键词命中表盘图
|
|
291
|
+
assert.equal(themedIcon('usage-dash'), GLYPHS.gauge, 'dash 命中表盘')
|
|
292
|
+
assert.equal(themedIcon('my-dashboard'), GLYPHS.gauge, 'dashboard 命中表盘')
|
|
286
293
|
})
|
|
287
294
|
|
|
288
295
|
test(prefix + '别名等价:双语分区任一语言键命中,组外键不受影响', () => {
|
|
@@ -419,8 +426,8 @@ test('两份实现 NAME_RULES 全量同源(含标志位)', () => {
|
|
|
419
426
|
|
|
420
427
|
test('README 契约数量锁定', () => {
|
|
421
428
|
// README 声明的内置表规模,扩表须同步改 README
|
|
422
|
-
assert.equal(Object.keys(logic.GLYPHS).length,
|
|
423
|
-
assert.equal(logic.NAME_RULES.length,
|
|
429
|
+
assert.equal(Object.keys(logic.GLYPHS).length, 30)
|
|
430
|
+
assert.equal(logic.NAME_RULES.length, 21)
|
|
424
431
|
})
|
|
425
432
|
|
|
426
433
|
test('LOGIC 段与 logic.mjs 决策函数逐函数源码一致(归一化注释与空白)', () => {
|