dsh-declutter 0.1.2 → 0.1.3
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/package.json +4 -4
- package/src/client.js +253 -175
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-declutter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
},
|
|
45
45
|
"repository": {
|
|
46
46
|
"type": "git",
|
|
47
|
-
"url": "https://github.com/DDDMUC/dsh-
|
|
47
|
+
"url": "https://github.com/DDDMUC/dsh-declutter.git"
|
|
48
48
|
},
|
|
49
|
-
"homepage": "https://github.com/DDDMUC/dsh-
|
|
49
|
+
"homepage": "https://github.com/DDDMUC/dsh-declutter#readme",
|
|
50
50
|
"bugs": {
|
|
51
|
-
"url": "https://github.com/DDDMUC/dsh-
|
|
51
|
+
"url": "https://github.com/DDDMUC/dsh-declutter/issues"
|
|
52
52
|
},
|
|
53
53
|
"description": "隐藏侧栏与会话菜单行,按 id 管理面板,可拖动排序。纯样式注入,卸载即还原。"
|
|
54
54
|
}
|
package/src/client.js
CHANGED
|
@@ -36,37 +36,37 @@ window.__ModuleLoader__.load({
|
|
|
36
36
|
const ENABLE_ROW_CONFIG = true
|
|
37
37
|
|
|
38
38
|
/** Row-config key: `<bundle package>#<row id from the bundle patch>`. */
|
|
39
|
-
/** Sidebar entry that opens this plugin's settings page. */
|
|
40
|
-
const PANEL_ENTRY_LABEL = '隐藏与整理'
|
|
41
|
-
/** Panel id: the URL segment (#/panel/<id>) and the main slot key. */
|
|
42
|
-
const PANEL_ENTRY_ID = 'hide-rows'
|
|
43
|
-
/** The sidebar draws the row; this slot contributes the icon in front of it. */
|
|
44
|
-
function PanelEntry() {
|
|
45
|
-
return h(
|
|
46
|
-
'svg',
|
|
47
|
-
{
|
|
48
|
-
width: 16,
|
|
49
|
-
height: 16,
|
|
50
|
-
viewBox: '0 0 16 16',
|
|
51
|
-
fill: 'none',
|
|
52
|
-
'aria-hidden': 'true',
|
|
53
|
-
},
|
|
54
|
-
h('path', {
|
|
55
|
-
d: 'M1.8 8c1.6-2.5 3.8-3.9 6.2-3.9S12.6 5.5 14.2 8c-1.6 2.5-3.8 3.9-6.2 3.9S3.4 10.5 1.8 8Z',
|
|
56
|
-
stroke: 'currentColor',
|
|
57
|
-
strokeWidth: 1.3,
|
|
58
|
-
strokeLinejoin: 'round',
|
|
59
|
-
}),
|
|
60
|
-
h('circle', { cx: 8, cy: 8, r: 1.9, stroke: 'currentColor', strokeWidth: 1.3 }),
|
|
61
|
-
h('path', {
|
|
62
|
-
d: 'M3 13 13 3',
|
|
63
|
-
stroke: 'currentColor',
|
|
64
|
-
strokeWidth: 1.3,
|
|
65
|
-
strokeLinecap: 'round',
|
|
66
|
-
}),
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
39
|
+
/** Sidebar entry that opens this plugin's settings page. */
|
|
40
|
+
const PANEL_ENTRY_LABEL = '隐藏与整理'
|
|
41
|
+
/** Panel id: the URL segment (#/panel/<id>) and the main slot key. */
|
|
42
|
+
const PANEL_ENTRY_ID = 'hide-rows'
|
|
43
|
+
/** The sidebar draws the row; this slot contributes the icon in front of it. */
|
|
44
|
+
function PanelEntry() {
|
|
45
|
+
return h(
|
|
46
|
+
'svg',
|
|
47
|
+
{
|
|
48
|
+
width: 16,
|
|
49
|
+
height: 16,
|
|
50
|
+
viewBox: '0 0 16 16',
|
|
51
|
+
fill: 'none',
|
|
52
|
+
'aria-hidden': 'true',
|
|
53
|
+
},
|
|
54
|
+
h('path', {
|
|
55
|
+
d: 'M1.8 8c1.6-2.5 3.8-3.9 6.2-3.9S12.6 5.5 14.2 8c-1.6 2.5-3.8 3.9-6.2 3.9S3.4 10.5 1.8 8Z',
|
|
56
|
+
stroke: 'currentColor',
|
|
57
|
+
strokeWidth: 1.3,
|
|
58
|
+
strokeLinejoin: 'round',
|
|
59
|
+
}),
|
|
60
|
+
h('circle', { cx: 8, cy: 8, r: 1.9, stroke: 'currentColor', strokeWidth: 1.3 }),
|
|
61
|
+
h('path', {
|
|
62
|
+
d: 'M3 13 13 3',
|
|
63
|
+
stroke: 'currentColor',
|
|
64
|
+
strokeWidth: 1.3,
|
|
65
|
+
strokeLinecap: 'round',
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
70
|
const ROW_CONFIG_KEY = 'dsh-declutter#hide-rows'
|
|
71
71
|
const STORE_KEY = 'dsh-sidebar-declutter'
|
|
72
72
|
/** Key this bundle stored its settings under before the rename. */
|
|
@@ -83,11 +83,11 @@ window.__ModuleLoader__.load({
|
|
|
83
83
|
kind: 'hide',
|
|
84
84
|
title: '左栏',
|
|
85
85
|
panels: true,
|
|
86
|
-
columns: 4,
|
|
86
|
+
columns: 4,
|
|
87
87
|
fields: [
|
|
88
88
|
{ key: 'brand', title: '顶部 logo' },
|
|
89
89
|
{ key: 'newSession', title: '「新会话」按钮' },
|
|
90
|
-
{ key: 'sidebarMore', title: '「更多」按钮' },
|
|
90
|
+
{ key: 'sidebarMore', title: '「更多」按钮' },
|
|
91
91
|
{ key: 'topGap', title: '顶部空隙' },
|
|
92
92
|
],
|
|
93
93
|
},
|
|
@@ -110,9 +110,9 @@ window.__ModuleLoader__.load({
|
|
|
110
110
|
inline: true,
|
|
111
111
|
options: [
|
|
112
112
|
{ value: 'stack', title: '一列' },
|
|
113
|
-
{ value: 'two', title: '两列' },
|
|
113
|
+
{ value: 'two', title: '两列' },
|
|
114
114
|
{ value: 'three', title: '三列' },
|
|
115
|
-
{ value: 'wrap', title: '自动紧凑' },
|
|
115
|
+
{ value: 'wrap', title: '自动紧凑' },
|
|
116
116
|
{ value: 'icon', title: '图标显示' },
|
|
117
117
|
],
|
|
118
118
|
},
|
|
@@ -233,8 +233,20 @@ window.__ModuleLoader__.load({
|
|
|
233
233
|
{ key: '取消归档', accel: '', rule: /^(取消归档|Unarchive)/, aria: [] },
|
|
234
234
|
{ key: '置顶会话', accel: '', rule: /^(置顶会话|Pin)/, aria: [] },
|
|
235
235
|
{ key: '取消置顶', accel: '', rule: /^(取消置顶|Unpin)/, aria: [] },
|
|
236
|
+
// Also shipped by the product: menu.fork / menu.sessionId / menu.openIn
|
|
237
|
+
// in the shell's own locale. They used to fall through to discovery and
|
|
238
|
+
// were mislabelled as community rows.
|
|
239
|
+
{ key: '分叉会话', accel: 'Alt+Meta+F', rule: /^(分叉会话|Fork)/, aria: ['Alt+Meta+F'] },
|
|
240
|
+
{ key: '复制 Session ID', accel: '', rule: /^(复制 Session ID|Copy Session ID)/, aria: [] },
|
|
241
|
+
{ key: '在应用中打开', accel: '', rule: /^(在应用中打开|Open in App)/, aria: [] },
|
|
236
242
|
]
|
|
237
243
|
|
|
244
|
+
/** Fold a stored/discovered label onto its official key when it matches. */
|
|
245
|
+
function canonicalMenuKey(key) {
|
|
246
|
+
const preset = MENU_PRESETS.find((one) => one.rule.test(key))
|
|
247
|
+
return preset === undefined ? key : preset.key
|
|
248
|
+
}
|
|
249
|
+
|
|
238
250
|
/**
|
|
239
251
|
* Rows that carry an accelerator can be hidden by CSS, before the menu is ever
|
|
240
252
|
* painted — no timing to lose. Rows without one still go through the DOM sweep.
|
|
@@ -258,12 +270,17 @@ window.__ModuleLoader__.load({
|
|
|
258
270
|
const parsed = JSON.parse(localStorage.getItem(MENU_STORE_KEY) || 'null')
|
|
259
271
|
if (!parsed) return
|
|
260
272
|
const hidden = Array.isArray(parsed.hidden)
|
|
261
|
-
? parsed.hidden.filter((one) => typeof one === 'string')
|
|
273
|
+
? [...new Set(parsed.hidden.filter((one) => typeof one === 'string').map(canonicalMenuKey))]
|
|
262
274
|
: []
|
|
263
275
|
// v1 recorded rows from every menu on the desktop — including the system
|
|
264
276
|
// "open with" list — so its `seen` is thrown away, keeping the switches.
|
|
265
277
|
const seen = parsed.version === MENU_STORE_VERSION && Array.isArray(parsed.seen)
|
|
266
|
-
? parsed.seen
|
|
278
|
+
? parsed.seen
|
|
279
|
+
.filter((one) => one && typeof one.key === 'string')
|
|
280
|
+
.reduce((list, one) => {
|
|
281
|
+
const key = canonicalMenuKey(one.key)
|
|
282
|
+
return list.some((entry) => entry.key === key) ? list : [...list, { ...one, key }]
|
|
283
|
+
}, [])
|
|
267
284
|
: []
|
|
268
285
|
menuState = { hidden, seen }
|
|
269
286
|
if (parsed.version !== MENU_STORE_VERSION) writeMenuState()
|
|
@@ -346,7 +363,26 @@ window.__ModuleLoader__.load({
|
|
|
346
363
|
let menuSweepQueued = false
|
|
347
364
|
|
|
348
365
|
/** Coalesce a burst of mutations into one sweep, before the next paint. */
|
|
349
|
-
function menuQueueSweep() {
|
|
366
|
+
function menuQueueSweep(records) {
|
|
367
|
+
// The 400ms watch overlaps the chat stream. Only a mutation that actually
|
|
368
|
+
// mounted a menu node deserves a document-wide sweep; per-token text and
|
|
369
|
+
// message-row updates do not. The timed sweeps below still cover menus
|
|
370
|
+
// that mount without adding a role-bearing node.
|
|
371
|
+
if (records && records.length) {
|
|
372
|
+
let menuish = false
|
|
373
|
+
for (const rec of records) {
|
|
374
|
+
for (const node of rec.addedNodes || []) {
|
|
375
|
+
if (node.nodeType !== 1) continue
|
|
376
|
+
if (node.matches?.('[role="menu"],[role="menuitem"]') ||
|
|
377
|
+
node.querySelector?.('[role="menu"],[role="menuitem"]')) {
|
|
378
|
+
menuish = true
|
|
379
|
+
break
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (menuish) break
|
|
383
|
+
}
|
|
384
|
+
if (!menuish) return
|
|
385
|
+
}
|
|
350
386
|
if (menuSweepQueued) return
|
|
351
387
|
menuSweepQueued = true
|
|
352
388
|
const run = () => {
|
|
@@ -382,6 +418,11 @@ window.__ModuleLoader__.load({
|
|
|
382
418
|
|
|
383
419
|
/** Menus mount a frame or two after the click that opens them. */
|
|
384
420
|
function menuScheduleSweep(fromSessionRow) {
|
|
421
|
+
// A click that cannot open a session menu and has no hidden rows to
|
|
422
|
+
// enforce makes menuSweep() a no-op: added stays false and every item
|
|
423
|
+
// hits the hidden guard. Skip the four document-wide scans, the 400ms
|
|
424
|
+
// watch and all timers -- this is the hot path on every ordinary click.
|
|
425
|
+
if (!fromSessionRow && !menuFromSessionRow && menuState.hidden.length === 0) return
|
|
385
426
|
if (fromSessionRow) menuFromSessionRow = true
|
|
386
427
|
menuSweep()
|
|
387
428
|
menuWatchForMount()
|
|
@@ -433,56 +474,96 @@ window.__ModuleLoader__.load({
|
|
|
433
474
|
|
|
434
475
|
function hiddenRules() {
|
|
435
476
|
const rules = []
|
|
477
|
+
/**
|
|
478
|
+
* Platform split. The desktop shell marks <html data-platform>: darwin on
|
|
479
|
+
* macOS, win32 on Windows; a plain browser carries no marker at all (see
|
|
480
|
+
* the shell's own macDesktopWindow / dsh-power-menu-mac). Linux desktop is
|
|
481
|
+
* neither darwin nor a browser and stays on the generic branch. macOS is
|
|
482
|
+
* the only build with native traffic lights floating over the sidebar and
|
|
483
|
+
* no in-page title bar strip.
|
|
484
|
+
*/
|
|
485
|
+
const isMacDesktop = () =>
|
|
486
|
+
typeof document !== 'undefined' &&
|
|
487
|
+
document.documentElement?.getAttribute('data-platform') === 'darwin'
|
|
436
488
|
if (values.brand) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
//
|
|
455
|
-
//
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
489
|
+
if (isMacDesktop()) {
|
|
490
|
+
// macOS has native traffic lights and no in-page title bar: the
|
|
491
|
+
// _topStrip is the only thing keeping sidebar content below them.
|
|
492
|
+
// Collapse the _logoRow (that is the space we reclaim) but leave the
|
|
493
|
+
// strip alone -- dropping both, as the Windows/browser branch does,
|
|
494
|
+
// is what pulled "New session" into the traffic lights. No global
|
|
495
|
+
// padding fallback here: [class*="_root"] is reused by many shell
|
|
496
|
+
// components and padding every one of them tears the column apart.
|
|
497
|
+
rules.push(
|
|
498
|
+
'[class*="_logoRow"] [class*="_brand"]{display:none!important}',
|
|
499
|
+
'[class*="_topStrip"] [class*="_brand"]{display:none!important}',
|
|
500
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_logoRow"]:not(:has([data-sidebar-toggle]))'
|
|
501
|
+
+ '{display:none!important}',
|
|
502
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_logoRow"]:has([data-sidebar-toggle])'
|
|
503
|
+
+ '{height:0!important;min-height:0!important;padding:0!important;overflow:visible!important}',
|
|
504
|
+
)
|
|
505
|
+
} else {
|
|
506
|
+
// The logo row is a flex child of the column, so dropping it closes the
|
|
507
|
+
// gap. It holds the collapse toggle on Windows and Linux, though, and that
|
|
508
|
+
// toggle must survive: hide the whole row only where it has no toggle, and
|
|
509
|
+
// otherwise hide just the brand inside it. `data-sidebar-toggle` is the
|
|
510
|
+
// sidebar's own attribute, stabler than any CSS-module class name.
|
|
511
|
+
rules.push(
|
|
512
|
+
'[class*="_logoRow"]:not(:has([data-sidebar-toggle])){display:none!important}',
|
|
513
|
+
'[class*="_logoRow"]:has([data-sidebar-toggle]) [class*="_brand"]{display:none!important}',
|
|
514
|
+
// Where the row does hold the toggle, drop its height instead of the
|
|
515
|
+
// row: zero height, no padding, and the button keeps the same place,
|
|
516
|
+
// drawn on top of whatever now starts where the row used to be. Pure
|
|
517
|
+
// CSS — no probe, no timer, no forced layout.
|
|
518
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_logoRow"]:has([data-sidebar-toggle])'
|
|
519
|
+
+ '{height:0!important;min-height:0!important;padding:0!important;overflow:visible!important'
|
|
520
|
+
+ ';align-items:flex-start!important;justify-content:flex-end!important}',
|
|
521
|
+
// Never macOS: its native traffic lights sit over the sidebar, so
|
|
522
|
+
// the toggle must stay in the shell's own layout. Shells that draw
|
|
523
|
+
// a Windows title bar are excluded by the first guard; everything
|
|
524
|
+
// else keeps the gutter behaviour.
|
|
525
|
+
'html:not([data-windows-titlebar]):not([data-platform="darwin"]) [class*="_root"]:not([class*="_collapsed"])'
|
|
526
|
+
+ ' [class*="_logoRow"]:has([data-sidebar-toggle]) [class*="_toggle"]'
|
|
527
|
+
+ '{position:fixed;top:6px;left:calc(var(--dsh-sidebar-width,280px) + 8px);z-index:30;border-radius:8px}',
|
|
528
|
+
// The collapsed rail keeps a normal row, or there is no way back out.
|
|
529
|
+
'[class*="_root"][class*="_collapsed"] [class*="_logoRow"]'
|
|
530
|
+
+ '{height:28px!important;min-height:0!important;padding-block:0!important;align-items:center!important}',
|
|
531
|
+
// Newer builds put the collapse toggle in a 52px `topStrip` above the
|
|
532
|
+
// logo row, so hiding the row alone leaves that strip showing as a band
|
|
533
|
+
// of empty space. Drop it too: on Windows the toggle is fixed inside the
|
|
534
|
+
// title bar anyway, and the browser build re-parks it in the gutter.
|
|
535
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_topStrip"]'
|
|
536
|
+
+ '{height:0!important;min-height:0!important;margin:0!important;padding:0!important'
|
|
537
|
+
+ ';overflow:visible!important;border:0!important}',
|
|
538
|
+
'html:not([data-windows-titlebar]):not([data-platform="darwin"]) [class*="_root"]:not([class*="_collapsed"])'
|
|
539
|
+
+ ' [class*="_topStrip"] [class*="_toggle"]'
|
|
540
|
+
+ '{position:fixed;top:6px;left:calc(var(--dsh-sidebar-width,280px) + 8px);z-index:30;border-radius:8px}',
|
|
541
|
+
)
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
// 顶部空隙: ONE checkbox on all three platforms, different behaviour.
|
|
545
|
+
// Windows / web keep the original three rules verbatim. On macOS the
|
|
546
|
+
// topStrip is 52px tall with a 28px toggle centred in it, so its lower
|
|
547
|
+
// part is empty: shrink it to 38px (reclaim 14px) and keep the toggle
|
|
548
|
+
// 11px from the top -- the same offset centring gave it -- so the button
|
|
549
|
+
// stays at exactly the same screen position while everything below moves
|
|
550
|
+
// up 14px. Zeroing _root padding is never done on macOS: that would
|
|
551
|
+
// shove the toggle into the native traffic lights.
|
|
552
|
+
if (values.topGap) {
|
|
553
|
+
if (isMacDesktop()) {
|
|
554
|
+
rules.push(
|
|
555
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_topStrip"]'
|
|
556
|
+
+ '{height:38px!important;min-height:38px!important;align-items:flex-start!important'
|
|
557
|
+
+ ';padding-top:11px!important;padding-bottom:0!important;box-sizing:border-box!important}',
|
|
558
|
+
)
|
|
559
|
+
} else {
|
|
560
|
+
rules.push(
|
|
561
|
+
'[class*="_root"]{padding-top:0!important}',
|
|
562
|
+
'[class*="_scrollRegion"]{margin-top:0!important}',
|
|
563
|
+
'[class*="_scrollArea"]{grid-template-rows:0 max-content 2px max-content!important}',
|
|
564
|
+
)
|
|
565
|
+
}
|
|
474
566
|
}
|
|
475
|
-
// Tighten the 18px that the sidebar itself adds above the panel list:
|
|
476
|
-
// root padding (6), the scroll region's top margin (4) and the first grid
|
|
477
|
-
// row of the scroll area (8). The 40px above them belongs to the native
|
|
478
|
-
// window menu bar, so it is left alone.
|
|
479
|
-
if (values.topGap) {
|
|
480
|
-
rules.push(
|
|
481
|
-
'[class*="_root"]{padding-top:0!important}',
|
|
482
|
-
'[class*="_scrollRegion"]{margin-top:0!important}',
|
|
483
|
-
'[class*="_scrollArea"]{grid-template-rows:0 max-content 2px max-content!important}',
|
|
484
|
-
)
|
|
485
|
-
}
|
|
486
567
|
if (values.newSession) {
|
|
487
568
|
rules.push('button[class*="_newSession"]{display:none!important}')
|
|
488
569
|
}
|
|
@@ -498,34 +579,34 @@ window.__ModuleLoader__.load({
|
|
|
498
579
|
+ '{display:grid!important;grid-template-columns:1fr 1fr;gap:2px}',
|
|
499
580
|
)
|
|
500
581
|
}
|
|
501
|
-
if (values.panelLayout === 'three') {
|
|
502
|
-
// Three fixed columns: same idea as the two-column grid, one more lane.
|
|
503
|
-
// Long labels keep the shipped ellipsis, and rows still line up.
|
|
504
|
-
rules.push(
|
|
505
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelList"]'
|
|
506
|
-
+ '{display:grid!important;grid-template-columns:repeat(3,minmax(0,1fr));gap:2px}',
|
|
507
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelEntry"]'
|
|
508
|
-
+ '{width:auto!important;min-width:0!important;max-width:100%}',
|
|
509
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelRow"]'
|
|
510
|
-
+ '{padding-left:6px!important;padding-right:6px!important}',
|
|
511
|
-
)
|
|
512
|
-
}
|
|
513
|
-
if (values.panelLayout === 'icon') {
|
|
514
|
-
// Icons only: the label is dropped, rows shrink to their glyph, and the
|
|
515
|
-
// list packs them side by side. The unpin control is hidden too, or it
|
|
516
|
-
// would show up as a second glyph per entry.
|
|
517
|
-
rules.push(
|
|
518
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelList"]'
|
|
519
|
-
+ '{flex-direction:row!important;flex-wrap:wrap!important;gap:4px!important}',
|
|
520
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelEntry"]'
|
|
521
|
-
+ '{flex:0 0 auto!important;width:auto!important;min-width:0!important;max-width:100%}',
|
|
522
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelTitle"]{display:none!important}',
|
|
523
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelRow"]'
|
|
524
|
-
+ '{width:auto!important;min-width:0!important;padding-left:8px!important;'
|
|
525
|
-
+ 'padding-right:8px!important;justify-content:center!important;gap:0!important}',
|
|
526
|
-
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelUnpin"]{display:none!important}',
|
|
527
|
-
)
|
|
528
|
-
}
|
|
582
|
+
if (values.panelLayout === 'three') {
|
|
583
|
+
// Three fixed columns: same idea as the two-column grid, one more lane.
|
|
584
|
+
// Long labels keep the shipped ellipsis, and rows still line up.
|
|
585
|
+
rules.push(
|
|
586
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelList"]'
|
|
587
|
+
+ '{display:grid!important;grid-template-columns:repeat(3,minmax(0,1fr));gap:2px}',
|
|
588
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelEntry"]'
|
|
589
|
+
+ '{width:auto!important;min-width:0!important;max-width:100%}',
|
|
590
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelRow"]'
|
|
591
|
+
+ '{padding-left:6px!important;padding-right:6px!important}',
|
|
592
|
+
)
|
|
593
|
+
}
|
|
594
|
+
if (values.panelLayout === 'icon') {
|
|
595
|
+
// Icons only: the label is dropped, rows shrink to their glyph, and the
|
|
596
|
+
// list packs them side by side. The unpin control is hidden too, or it
|
|
597
|
+
// would show up as a second glyph per entry.
|
|
598
|
+
rules.push(
|
|
599
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelList"]'
|
|
600
|
+
+ '{flex-direction:row!important;flex-wrap:wrap!important;gap:4px!important}',
|
|
601
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelEntry"]'
|
|
602
|
+
+ '{flex:0 0 auto!important;width:auto!important;min-width:0!important;max-width:100%}',
|
|
603
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelTitle"]{display:none!important}',
|
|
604
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelRow"]'
|
|
605
|
+
+ '{width:auto!important;min-width:0!important;padding-left:8px!important;'
|
|
606
|
+
+ 'padding-right:8px!important;justify-content:center!important;gap:0!important}',
|
|
607
|
+
'[class*="_root"]:not([class*="_collapsed"]) [class*="_panelUnpin"]{display:none!important}',
|
|
608
|
+
)
|
|
609
|
+
}
|
|
529
610
|
if (values.panelLayout === 'wrap') {
|
|
530
611
|
// Content-sized chips that wrap greedily, not fixed columns: two short
|
|
531
612
|
// names (插件 / 鱼塘) pack three or four to a line while a long one
|
|
@@ -578,7 +659,7 @@ window.__ModuleLoader__.load({
|
|
|
578
659
|
// The bulk layer only: every panel row plus the list's More launcher. The
|
|
579
660
|
// individual switches and the checkbox list stay untouched.
|
|
580
661
|
rules.push(
|
|
581
|
-
'[class*="_panelList"] [class*="_panelEntry"]{display:none!important}',
|
|
662
|
+
'[class*="_panelList"] [class*="_panelEntry"]{display:none!important}',
|
|
582
663
|
'[class*="_panelList"] > button[aria-label]{display:none!important}',
|
|
583
664
|
'[class*="_panelList"] [class*="_moreAnchor"]{display:none!important}',
|
|
584
665
|
'[class*="_panelList"] button[aria-haspopup="menu"]{display:none!important}',
|
|
@@ -590,13 +671,13 @@ window.__ModuleLoader__.load({
|
|
|
590
671
|
// Primary: the id stamped on the row, which keeps two same-named panels
|
|
591
672
|
// apart. Fallback: the name, used only before the first stamping pass and
|
|
592
673
|
// only when no other panel carries it.
|
|
593
|
-
// Newer builds wrap every row in a `_panelEntry` box; hiding only the
|
|
594
|
-
// button leaves that box occupying its grid cell, which is the empty slot
|
|
595
|
-
// seen when a two-column list loses one panel. Hide the box itself.
|
|
596
|
-
rules.push(
|
|
597
|
-
'[class*="_panelEntry"]:has(button[' + PANEL_ATTR + '="' + cssAttrValue(id) + '"])'
|
|
598
|
-
+ '{display:none!important}',
|
|
599
|
-
'button[' + PANEL_ATTR + '="' + cssAttrValue(id) + '"]{display:none!important}',
|
|
674
|
+
// Newer builds wrap every row in a `_panelEntry` box; hiding only the
|
|
675
|
+
// button leaves that box occupying its grid cell, which is the empty slot
|
|
676
|
+
// seen when a two-column list loses one panel. Hide the box itself.
|
|
677
|
+
rules.push(
|
|
678
|
+
'[class*="_panelEntry"]:has(button[' + PANEL_ATTR + '="' + cssAttrValue(id) + '"])'
|
|
679
|
+
+ '{display:none!important}',
|
|
680
|
+
'button[' + PANEL_ATTR + '="' + cssAttrValue(id) + '"]{display:none!important}',
|
|
600
681
|
)
|
|
601
682
|
const shared = panelEntries.some((one) => one.id !== id && one.label === entry.label)
|
|
602
683
|
if (!shared) {
|
|
@@ -624,7 +705,7 @@ window.__ModuleLoader__.load({
|
|
|
624
705
|
'.dhns-sectionHint{font-size:11px;line-height:1.4;color:var(--dsw-alias-label-tertiary)}',
|
|
625
706
|
'.dhns-rows{display:flex;flex-direction:column;gap:8px}',
|
|
626
707
|
'.dhns-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}',
|
|
627
|
-
'.dhns-grid-2{grid-template-columns:repeat(2,minmax(0,1fr))}',
|
|
708
|
+
'.dhns-grid-2{grid-template-columns:repeat(2,minmax(0,1fr))}',
|
|
628
709
|
'.dhns-grid-4{grid-template-columns:repeat(4,minmax(0,1fr))}',
|
|
629
710
|
'.dhns-grid .dhns-row{padding:6px 8px;align-items:center;border-radius:6px}',
|
|
630
711
|
'.dhns-grid .dhns-title{font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
|
|
@@ -685,8 +766,8 @@ window.__ModuleLoader__.load({
|
|
|
685
766
|
'border-radius:6px;border:1px solid var(--dsw-alias-border-secondary,rgba(128,128,128,.3));',
|
|
686
767
|
'background:transparent;color:var(--dsw-alias-label-secondary)}',
|
|
687
768
|
'.dhns-subheadBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}',
|
|
688
|
-
'.dhns-subheadBtn:disabled{opacity:.4;cursor:default}',
|
|
689
|
-
'.dhns-page{padding:24px 28px;max-width:820px}',
|
|
769
|
+
'.dhns-subheadBtn:disabled{opacity:.4;cursor:default}',
|
|
770
|
+
'.dhns-page{padding:24px 28px;max-width:820px}',
|
|
690
771
|
'.dhns-pageTitle{margin:0 0 16px;font-size:16px;font-weight:600}',
|
|
691
772
|
].join('')
|
|
692
773
|
|
|
@@ -1120,29 +1201,26 @@ window.__ModuleLoader__.load({
|
|
|
1120
1201
|
* row. `summary` is the one-liner the row shows when collapsed; `page` is the
|
|
1121
1202
|
* form the row's configure control opens.
|
|
1122
1203
|
*/
|
|
1123
|
-
/** The page #/panel/hide-rows renders: the same form as the plugin row card. */
|
|
1124
|
-
function SettingsPage() {
|
|
1125
|
-
return h(
|
|
1126
|
-
'div',
|
|
1127
|
-
{ className: 'dhns-page' },
|
|
1128
|
-
h('h2', { className: 'dhns-pageTitle' }, PANEL_ENTRY_LABEL),
|
|
1129
|
-
h(SettingsCard, { view: 'full' }),
|
|
1130
|
-
)
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1204
|
+
/** The page #/panel/hide-rows renders: the same form as the plugin row card. */
|
|
1205
|
+
function SettingsPage() {
|
|
1206
|
+
return h(
|
|
1207
|
+
'div',
|
|
1208
|
+
{ className: 'dhns-page' },
|
|
1209
|
+
h('h2', { className: 'dhns-pageTitle' }, PANEL_ENTRY_LABEL),
|
|
1210
|
+
h(SettingsCard, { view: 'full' }),
|
|
1211
|
+
)
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1133
1214
|
function SettingsCard(props) {
|
|
1134
1215
|
const view = props?.view
|
|
1135
1216
|
const [, forceRender] = React.useReducer((count) => count + 1, 0)
|
|
1136
1217
|
const [dragId, setDragId] = React.useState(null)
|
|
1137
1218
|
React.useEffect(() => subscribe(forceRender), [])
|
|
1138
1219
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
.map((field) => field.title)
|
|
1144
|
-
return h('span', null, on.length === 0 ? '未隐藏任何项' : '已隐藏:' + on.join('、'))
|
|
1145
|
-
}
|
|
1220
|
+
// The host renders this card with view="summary" as the one-line
|
|
1221
|
+
// description under the plugin row. Return nothing so no description
|
|
1222
|
+
// is drawn at all; the full form is unchanged.
|
|
1223
|
+
if (view === 'summary') return null
|
|
1146
1224
|
|
|
1147
1225
|
const rowFor = (field) =>
|
|
1148
1226
|
h(
|
|
@@ -1355,7 +1433,7 @@ window.__ModuleLoader__.load({
|
|
|
1355
1433
|
),
|
|
1356
1434
|
),
|
|
1357
1435
|
values.picked.length === 0
|
|
1358
|
-
?
|
|
1436
|
+
? null
|
|
1359
1437
|
: h(
|
|
1360
1438
|
'div',
|
|
1361
1439
|
{ className: 'dhns-rows', key: 'list' },
|
|
@@ -1452,13 +1530,13 @@ window.__ModuleLoader__.load({
|
|
|
1452
1530
|
: h(
|
|
1453
1531
|
'div',
|
|
1454
1532
|
{
|
|
1455
|
-
className: section.columns === 2
|
|
1456
|
-
? 'dhns-grid dhns-grid-2'
|
|
1457
|
-
: section.columns === 3
|
|
1458
|
-
? 'dhns-grid'
|
|
1459
|
-
: section.columns === 4
|
|
1460
|
-
? 'dhns-grid dhns-grid-4'
|
|
1461
|
-
: 'dhns-rows',
|
|
1533
|
+
className: section.columns === 2
|
|
1534
|
+
? 'dhns-grid dhns-grid-2'
|
|
1535
|
+
: section.columns === 3
|
|
1536
|
+
? 'dhns-grid'
|
|
1537
|
+
: section.columns === 4
|
|
1538
|
+
? 'dhns-grid dhns-grid-4'
|
|
1539
|
+
: 'dhns-rows',
|
|
1462
1540
|
},
|
|
1463
1541
|
section.fields.map(renderField),
|
|
1464
1542
|
),
|
|
@@ -1556,31 +1634,31 @@ window.__ModuleLoader__.load({
|
|
|
1556
1634
|
),
|
|
1557
1635
|
)
|
|
1558
1636
|
|
|
1559
|
-
// Same two-slot pattern the shipped panels use: the sidebar row only
|
|
1560
|
-
// carries the icon and label, and the main slot with the same key is the
|
|
1561
|
-
// page the shell routes #/panel/<id> to. Without the second registration
|
|
1562
|
-
// the click falls back to whatever panel was open before.
|
|
1563
|
-
if (ctx.slots && typeof ctx.slots.inject === 'function') {
|
|
1564
|
-
ctx.effect(() =>
|
|
1565
|
-
ctx.slots.inject('sidebar.panellist', () =>
|
|
1566
|
-
ctx.slots.register(
|
|
1567
|
-
{
|
|
1568
|
-
name: 'sidebar.panellist',
|
|
1569
|
-
id: PANEL_ENTRY_ID,
|
|
1570
|
-
order: 20,
|
|
1571
|
-
label: () => PANEL_ENTRY_LABEL,
|
|
1572
|
-
},
|
|
1573
|
-
PanelEntry,
|
|
1574
|
-
),
|
|
1575
|
-
),
|
|
1576
|
-
)
|
|
1577
|
-
ctx.effect(() =>
|
|
1578
|
-
ctx.slots.inject('main', () =>
|
|
1579
|
-
ctx.slots.register({ name: 'main', key: PANEL_ENTRY_ID }, SettingsPage),
|
|
1580
|
-
),
|
|
1581
|
-
)
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1637
|
+
// Same two-slot pattern the shipped panels use: the sidebar row only
|
|
1638
|
+
// carries the icon and label, and the main slot with the same key is the
|
|
1639
|
+
// page the shell routes #/panel/<id> to. Without the second registration
|
|
1640
|
+
// the click falls back to whatever panel was open before.
|
|
1641
|
+
if (ctx.slots && typeof ctx.slots.inject === 'function') {
|
|
1642
|
+
ctx.effect(() =>
|
|
1643
|
+
ctx.slots.inject('sidebar.panellist', () =>
|
|
1644
|
+
ctx.slots.register(
|
|
1645
|
+
{
|
|
1646
|
+
name: 'sidebar.panellist',
|
|
1647
|
+
id: PANEL_ENTRY_ID,
|
|
1648
|
+
order: 20,
|
|
1649
|
+
label: () => PANEL_ENTRY_LABEL,
|
|
1650
|
+
},
|
|
1651
|
+
PanelEntry,
|
|
1652
|
+
),
|
|
1653
|
+
),
|
|
1654
|
+
)
|
|
1655
|
+
ctx.effect(() =>
|
|
1656
|
+
ctx.slots.inject('main', () =>
|
|
1657
|
+
ctx.slots.register({ name: 'main', key: PANEL_ENTRY_ID }, SettingsPage),
|
|
1658
|
+
),
|
|
1659
|
+
)
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1584
1662
|
if (ENABLE_ROW_CONFIG) {
|
|
1585
1663
|
ctx.effect(() =>
|
|
1586
1664
|
ctx.slots.inject('plugins.row.config', () =>
|