dsh-mesh-ui 0.0.17 → 0.0.18
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/lib/client.js +126 -28
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -313,12 +313,22 @@ body.light .dm-edge-label { stroke: rgba(255,255,255,.92); }
|
|
|
313
313
|
.dm-details > summary::-webkit-details-marker { display:none; }
|
|
314
314
|
.dm-details > summary::before { content:'▸'; font-size:10px; opacity:.5; transition:transform .12s; }
|
|
315
315
|
.dm-details[open] > summary::before { transform:rotate(90deg); }
|
|
316
|
-
.dm-details > .dm-body { padding:0 12px 11px; }
|
|
316
|
+
.dm-details > .dm-details-body { padding:0 12px 11px; }
|
|
317
317
|
.dm-table { width:100%; border-collapse:collapse; }
|
|
318
318
|
.dm-table th { text-align:left; font-weight:600; font-size:11px; opacity:.5; padding:2px 8px 5px 0; border-bottom:1px solid rgba(128,128,128,.2); white-space:nowrap; }
|
|
319
319
|
.dm-table td { padding:5px 8px 5px 0; border-bottom:1px solid rgba(128,128,128,.1); font-size:12px; }
|
|
320
320
|
.dm-table tr:last-child td { border-bottom:none; }
|
|
321
|
-
|
|
321
|
+
/* 2026-11-10:委托台账表改为固定列宽 + 单行省略;非固定布局下 5 列会把「节点/任务」压到
|
|
322
|
+
每行一个字,就是用户看到的竖排。完整内容看 title 悬浮提示。 */
|
|
323
|
+
.dm-details .dm-table { table-layout:fixed; min-width:420px; }
|
|
324
|
+
.dm-table-wrap { overflow-x:auto; }
|
|
325
|
+
.dm-details .dm-table th, .dm-details .dm-table td { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
326
|
+
.dm-details .dm-table th:nth-child(1), .dm-details .dm-table td:nth-child(1) { width:16%; }
|
|
327
|
+
.dm-details .dm-table th:nth-child(2), .dm-details .dm-table td:nth-child(2) { width:18%; }
|
|
328
|
+
.dm-details .dm-table th:nth-child(3), .dm-details .dm-table td:nth-child(3) { width:22%; }
|
|
329
|
+
.dm-details .dm-table th:nth-child(4), .dm-details .dm-table td:nth-child(4) { width:10%; }
|
|
330
|
+
/* 目标列不写死百分比:让它吃掉剩余空间(单元格右内边距不参与百分比,写死 34% 会溢出 1 条横向滚动条) */
|
|
331
|
+
.dm-trunc { max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:inline-block; vertical-align:bottom; }
|
|
322
332
|
.dm-instrow { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:6px 0; border-bottom:1px solid rgba(128,128,128,.1); }
|
|
323
333
|
.dm-instrow:last-child { border-bottom:none; }
|
|
324
334
|
.dm-alertrow { display:flex; gap:8px; align-items:baseline; padding:5px 0; border-bottom:1px dashed rgba(128,128,128,.16); font-size:12px; }
|
|
@@ -1441,7 +1451,9 @@ function deviceLinkState(data, peer, lanMap) {
|
|
|
1441
1451
|
}
|
|
1442
1452
|
return h('details', { className: 'dm-details', open: open, onToggle: onToggle },
|
|
1443
1453
|
h('summary', null, props.title, props.badge != null ? h('span', { className: 'dm-pill muted' }, String(props.badge)) : null),
|
|
1444
|
-
|
|
1454
|
+
// 2026-11-10:折叠面板正文用独立容器整栏铺满;原先借用 .dm-body(62% 双栏 grid)
|
|
1455
|
+
// 会把「委托台账」这类单列表格压到 300px,中文被逐字折成竖排。
|
|
1456
|
+
h('div', { className: 'dm-details-body' }, props.children)
|
|
1445
1457
|
)
|
|
1446
1458
|
}
|
|
1447
1459
|
|
|
@@ -1472,17 +1484,20 @@ function deviceLinkState(data, peer, lanMap) {
|
|
|
1472
1484
|
if (!list.length) return h('div', { className: 'dm-empty' }, '暂无委托记录')
|
|
1473
1485
|
return h('div', null,
|
|
1474
1486
|
h('div', { className: 'dm-sub', style: { marginBottom: 5 } }, '共 ' + (d.total || 0) + ' 条 · ' + Object.keys(stats).map(function (k) { return k + ' ' + stats[k] }).join(' · ')),
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
h('
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
h('
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1487
|
+
// 2026-11-10:外层给横向滚动兜底——栏窄到连固定列宽都放不下时滚动,而不是压缩成竖排。
|
|
1488
|
+
h('div', { className: 'dm-table-wrap' },
|
|
1489
|
+
h('table', { className: 'dm-table' },
|
|
1490
|
+
h('thead', null, h('tr', null, h('th', null, '状态'), h('th', null, '节点'), h('th', null, '任务'), h('th', null, '耗时'), h('th', null, '目标'))),
|
|
1491
|
+
h('tbody', null, list.map(function (task) {
|
|
1492
|
+
return h('tr', { key: task.taskId },
|
|
1493
|
+
h('td', null, stateChip(task.state)),
|
|
1494
|
+
h('td', { title: nodeLabel(task.node) }, nodeLabel(task.node)),
|
|
1495
|
+
h('td', { className: 'dm-mono', style: { fontSize: 11 }, title: String(task.taskId || '') }, String(task.taskId || '').replace(/^T-\d{8}-/, 'T-…')),
|
|
1496
|
+
h('td', { className: 'dm-mono' }, dur(task.durationSec)),
|
|
1497
|
+
h('td', null, h('span', { className: 'dm-trunc', title: task.goal }, task.goal || ''))
|
|
1498
|
+
)
|
|
1499
|
+
}))
|
|
1500
|
+
)
|
|
1486
1501
|
)
|
|
1487
1502
|
)
|
|
1488
1503
|
}
|
|
@@ -2441,17 +2456,26 @@ body[data-ds-dark-theme] .dm-dg-fs-item.active { background:rgba(88,166,255,.28)
|
|
|
2441
2456
|
}
|
|
2442
2457
|
|
|
2443
2458
|
// 2026-11-10:AI 网络现在是左侧栏主页面,不再受设置面板的窄容器约束。
|
|
2444
|
-
//
|
|
2459
|
+
// 2026-11-10(二次):内边距从 .dm-page 挪到滚动容器上——滚动条属于滚动容器,只有在滚动容器
|
|
2460
|
+
// 铺满整页时才会像 DSH 对话区那样贴到最右边;内容留白靠滚动容器自己的 padding 提供。
|
|
2445
2461
|
var PAGE_CSS = `
|
|
2446
|
-
.dm-page { box-sizing:border-box; width:100%; height:100%; overflow:hidden; padding:clamp(
|
|
2462
|
+
.dm-page { box-sizing:border-box; width:100%; height:100%; overflow:hidden; padding:0; --dm-pad-x:clamp(18px, 2.6vw, 42px); --dm-pad-y:clamp(16px, 2.2vw, 30px); --dm-scroll-inset:2px; }
|
|
2447
2463
|
.dm-content-shell { width:100%; height:100%; position:relative; --dm-content-w:920px; }
|
|
2448
|
-
|
|
2464
|
+
/* 纵向滚动条:对齐 DSH 对话区 wSkVaW_scrollBody 的观感——滚动容器 margin-right:2px
|
|
2465
|
+
(滚动条离面板边 2px,不贴死),滑块 track 留 2px(滑块上下不顶到边)。
|
|
2466
|
+
滚动条只占右侧会把内容挤得偏左,所以左侧补等宽内边距(--dm-sb-pad)。 */
|
|
2467
|
+
/* border-box 必须加:height:100% + 上下 padding 否则把滚动容器撑出面板高度,
|
|
2468
|
+
底部 padding 和页脚会被 .dm-page 的 overflow:hidden 裁掉(2026-11-10 实测 1051 > 985)。 */
|
|
2469
|
+
.dm-content-scroll { box-sizing:border-box; height:100%; overflow:auto; scrollbar-gutter:stable; margin-right:var(--dm-scroll-inset); padding:var(--dm-pad-y) var(--dm-pad-x) 36px; padding-left:calc(var(--dm-pad-x) + var(--dm-sb-pad, 0px) + var(--dm-scroll-inset)); }
|
|
2470
|
+
.dm-content-scroll::-webkit-scrollbar-track { margin:2px; }
|
|
2449
2471
|
.dm-content { width:min(100%, var(--dm-content-w)); max-width:100%; margin:0 auto; box-sizing:border-box; container-type:inline-size; container-name:dm-content; }
|
|
2450
2472
|
.dm-content > .dm-root { width:100%; max-width:100%; margin:0 auto; }
|
|
2451
|
-
/*
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
.dm-width-handle
|
|
2473
|
+
/* 宽度手柄:位置由 JS 实测的「内容边距 + 页面留白」算出高亮线中心点,写进 --dm-bar-l / --dm-bar-r。
|
|
2474
|
+
高亮线在 10px 手柄内偏内侧 5px(::after 4px + 2px 线宽的一半),所以手柄 = 中心点 ∓ 5px。
|
|
2475
|
+
这样留白窄时(拖到最大)高亮线自动居中在「内容边 ↔ 页面边」中间,留白宽时保持 24px 间隙。 */
|
|
2476
|
+
.dm-width-handle { z-index:1; position:absolute; top:0; bottom:0; cursor:col-resize; width:10px; }
|
|
2477
|
+
.dm-width-handle[data-side="left"] { left:calc(var(--dm-bar-l, -100px) - 5px); }
|
|
2478
|
+
.dm-width-handle[data-side="right"] { right:calc(var(--dm-bar-r, -100px) - 5px); }
|
|
2455
2479
|
.dm-width-handle::after { content:''; pointer-events:none; opacity:0; position:absolute; top:0; bottom:0; width:2px; border-radius:2px; background:linear-gradient(to bottom, transparent calc(var(--dm-width-handle-pointer-y, 50%) - 36px), var(--dsw-alias-scrollbar-bg-l1, rgba(128,128,128,.55)) calc(var(--dm-width-handle-pointer-y, 50%) - 8px), var(--dsw-alias-scrollbar-bg-l1, rgba(128,128,128,.55)) calc(var(--dm-width-handle-pointer-y, 50%) + 8px), transparent calc(var(--dm-width-handle-pointer-y, 50%) + 36px)); }
|
|
2456
2480
|
.dm-width-handle[data-side="left"]::after { right:4px; }
|
|
2457
2481
|
.dm-width-handle[data-side="right"]::after { left:4px; }
|
|
@@ -2481,12 +2505,29 @@ body[data-ds-dark-theme] .dm-dg-fs-item.active { background:rgba(88,166,255,.28)
|
|
|
2481
2505
|
})
|
|
2482
2506
|
var pref = prefState[0]; var setPref = prefState[1]
|
|
2483
2507
|
var shellState = useState(0); var containerW = shellState[0]; var setContainerW = shellState[1]
|
|
2508
|
+
// 滚动条实际占位宽度(px):左侧补等宽内边距,让内容在 shell 上真正居中。
|
|
2509
|
+
var sbState = useState(0); var sbPad = sbState[0]; var setSbPad = sbState[1]
|
|
2510
|
+
// 手柄几何:内容元素到 shell 左右边的实测 px(null = 还没量到)。
|
|
2511
|
+
var geoState = useState(null); var geo = geoState[0]; var setGeo = geoState[1]
|
|
2484
2512
|
var shellRef = useRef(null)
|
|
2485
2513
|
|
|
2514
|
+
// 2026-11-10:宽度基准 =「内容可用宽度」= 滚动容器内容盒宽(clientWidth - 左右 padding);
|
|
2515
|
+
// 滚动条贴页面右边,其占位由 CSS 在左侧补等宽 padding 抵消(--dm-sb-pad)。
|
|
2486
2516
|
useEffect(function () {
|
|
2487
2517
|
var el = shellRef.current
|
|
2488
2518
|
if (!el) return
|
|
2489
|
-
function measure() {
|
|
2519
|
+
function measure() {
|
|
2520
|
+
var scroller = el.querySelector('.dm-content-scroll')
|
|
2521
|
+
if (!scroller) { setContainerW(el.clientWidth || 0); return }
|
|
2522
|
+
var sb = Math.max(0, (scroller.offsetWidth || 0) - (scroller.clientWidth || 0))
|
|
2523
|
+
var scs = getComputedStyle(scroller)
|
|
2524
|
+
var padL = parseFloat(scs.paddingLeft) || 0
|
|
2525
|
+
var padR = parseFloat(scs.paddingRight) || 0
|
|
2526
|
+
// 内容盒宽 = clientWidth - 实际左右 padding(左侧含滚动条补偿与 2px 滚动条外留白)
|
|
2527
|
+
var avail = Math.max(0, (scroller.clientWidth || 0) - padL - padR)
|
|
2528
|
+
setContainerW(avail)
|
|
2529
|
+
setSbPad(function (prev) { return Math.abs(prev - sb) >= 0.5 ? sb : prev })
|
|
2530
|
+
}
|
|
2490
2531
|
measure()
|
|
2491
2532
|
if (typeof ResizeObserver !== 'undefined') {
|
|
2492
2533
|
var observer = new ResizeObserver(measure)
|
|
@@ -2495,19 +2536,67 @@ body[data-ds-dark-theme] .dm-dg-fs-item.active { background:rgba(88,166,255,.28)
|
|
|
2495
2536
|
}
|
|
2496
2537
|
window.addEventListener('resize', measure)
|
|
2497
2538
|
return function () { window.removeEventListener('resize', measure) }
|
|
2498
|
-
}, [])
|
|
2539
|
+
}, [sbPad])
|
|
2499
2540
|
|
|
2500
|
-
//
|
|
2541
|
+
// 手柄定位:高亮线在「内容边 ↔ 页面边」的留白里自动居中(留白宽时退回 24px 间隙)。
|
|
2542
|
+
// 滚动容器已铺满整页,所以内容到 shell 左右边的距离就是完整留白(右侧含滚动条)。 */
|
|
2543
|
+
useEffect(function () {
|
|
2544
|
+
var el = shellRef.current
|
|
2545
|
+
if (!el) return
|
|
2546
|
+
var raf = 0
|
|
2547
|
+
function sync() {
|
|
2548
|
+
var content = el.querySelector('.dm-content')
|
|
2549
|
+
if (!content) return
|
|
2550
|
+
var sr = el.getBoundingClientRect()
|
|
2551
|
+
var cr = content.getBoundingClientRect()
|
|
2552
|
+
// 留白量到 shell(页面)边:滚动条外那 2px 也算留白,高亮线才居中在视觉留白里
|
|
2553
|
+
var offL = cr.left - sr.left
|
|
2554
|
+
var offR = sr.right - cr.right
|
|
2555
|
+
// 高亮线中心到内容边的距离:留白的一半,但夹在 [12, 29](29 = 24px 间隙 + 5px 线中心偏移)
|
|
2556
|
+
var gapL = Math.max(12, Math.min(29, offL / 2))
|
|
2557
|
+
var gapR = Math.max(12, Math.min(29, offR / 2))
|
|
2558
|
+
var bl = Math.round((offL - gapL) * 10) / 10
|
|
2559
|
+
var br = Math.round((offR - gapR) * 10) / 10
|
|
2560
|
+
setGeo(function (prev) {
|
|
2561
|
+
if (prev && Math.abs(prev.l - bl) < 0.5 && Math.abs(prev.r - br) < 0.5) return prev
|
|
2562
|
+
return { l: bl, r: br }
|
|
2563
|
+
})
|
|
2564
|
+
}
|
|
2565
|
+
sync()
|
|
2566
|
+
raf = window.requestAnimationFrame(sync)
|
|
2567
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
2568
|
+
var observer = new ResizeObserver(sync)
|
|
2569
|
+
var content = el.querySelector('.dm-content')
|
|
2570
|
+
if (content) observer.observe(content)
|
|
2571
|
+
observer.observe(el)
|
|
2572
|
+
return function () { window.cancelAnimationFrame(raf); observer.disconnect() }
|
|
2573
|
+
}
|
|
2574
|
+
return function () { window.cancelAnimationFrame(raf) }
|
|
2575
|
+
}, [contentW, sbPad])
|
|
2576
|
+
|
|
2577
|
+
// 默认宽度 = 内容可用宽度 x DEFAULT_W_RATIO;拖拽限制在 [640, 可用宽度],
|
|
2578
|
+
// 双击手柄清掉偏好后回到默认。想留一点内边距就把 DEFAULT_W_RATIO 调到 0.98/0.96。
|
|
2579
|
+
var DEFAULT_W_RATIO = 1
|
|
2501
2580
|
var widthNow = containerW || 920
|
|
2502
|
-
var maxW = Math.max(640, widthNow
|
|
2503
|
-
var contentW = pref > 0 ? Math.min(Math.max(pref, 640), maxW) : Math.
|
|
2581
|
+
var maxW = Math.max(640, widthNow)
|
|
2582
|
+
var contentW = pref > 0 ? Math.min(Math.max(pref, 640), maxW) : Math.round(widthNow * DEFAULT_W_RATIO)
|
|
2504
2583
|
|
|
2505
2584
|
function beginWidthDrag(side, event) {
|
|
2506
2585
|
event.preventDefault()
|
|
2507
2586
|
var handle = event.currentTarget
|
|
2508
2587
|
var shell = handle.closest ? handle.closest('.dm-content-shell') : (handle.parentElement && handle.parentElement.parentElement)
|
|
2509
2588
|
if (!shell) return
|
|
2510
|
-
var
|
|
2589
|
+
var scroller = shell.querySelector('.dm-content-scroll')
|
|
2590
|
+
var max = 920
|
|
2591
|
+
if (scroller) {
|
|
2592
|
+
var scs2 = getComputedStyle(scroller)
|
|
2593
|
+
var padL2 = parseFloat(scs2.paddingLeft) || 0
|
|
2594
|
+
var padR2 = parseFloat(scs2.paddingRight) || 0
|
|
2595
|
+
max = Math.max(0, (scroller.clientWidth || 0) - padL2 - padR2)
|
|
2596
|
+
} else if (shell.clientWidth) {
|
|
2597
|
+
max = shell.clientWidth
|
|
2598
|
+
}
|
|
2599
|
+
max = Math.max(640, max || 920)
|
|
2511
2600
|
var base = contentW
|
|
2512
2601
|
var originX = event.clientX
|
|
2513
2602
|
var latest = contentW
|
|
@@ -2544,7 +2633,16 @@ body[data-ds-dark-theme] .dm-dg-fs-item.active { background:rgba(88,166,255,.28)
|
|
|
2544
2633
|
|
|
2545
2634
|
return h('div', { className: 'dm-page' },
|
|
2546
2635
|
h('style', null, PAGE_CSS),
|
|
2547
|
-
h('div', {
|
|
2636
|
+
h('div', {
|
|
2637
|
+
className: 'dm-content-shell',
|
|
2638
|
+
ref: shellRef,
|
|
2639
|
+
style: {
|
|
2640
|
+
'--dm-content-w': contentW + 'px',
|
|
2641
|
+
'--dm-sb-pad': sbPad + 'px',
|
|
2642
|
+
'--dm-bar-l': (geo ? geo.l : -100) + 'px',
|
|
2643
|
+
'--dm-bar-r': (geo ? geo.r : -100) + 'px'
|
|
2644
|
+
}
|
|
2645
|
+
},
|
|
2548
2646
|
h('div', { className: 'dm-width-handle', 'data-side': 'left', title: '拖拽调整内容宽度(双击恢复默认)', onPointerDown: function (event) { beginWidthDrag('left', event) }, onDoubleClick: resetContentWidth }),
|
|
2549
2647
|
h('div', { className: 'dm-width-handle', 'data-side': 'right', title: '拖拽调整内容宽度(双击恢复默认)', onPointerDown: function (event) { beginWidthDrag('right', event) }, onDoubleClick: resetContentWidth }),
|
|
2550
2648
|
h('div', { className: 'dm-content-scroll' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mesh-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "DSH sidebar page for dsh-mesh: AI network topology, peer health, and delegation panel. Read-only; data is collected from the installed dsh-mesh-node package.",
|
|
5
5
|
"icon": "icon.svg",
|
|
6
6
|
"type": "module",
|