dsh-mesh-ui 0.0.12 → 0.0.13
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 +177 -18
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1457,6 +1457,36 @@ function deviceLinkState(data, peer, lanMap) {
|
|
|
1457
1457
|
var verdictState = useState(''); var verdictShown = verdictState[0]; var setVerdictShown = verdictState[1]
|
|
1458
1458
|
var verdictRef = useRef({ raw: '', streak: 0 })
|
|
1459
1459
|
var s5 = useState('local'); var selectedKey = s5[0]; var setSelectedKey = s5[1]
|
|
1460
|
+
// 双栏宽度:拖动分隔条调整并记忆,窄屏由 CSS 媒体查询自动堆叠。
|
|
1461
|
+
var dragState = useState(function () {
|
|
1462
|
+
try { var saved = Number(window.localStorage.getItem('dsh-mesh-split-w')); return Number.isFinite(saved) && saved > 0 ? saved : 0 } catch (error) { return 0 }
|
|
1463
|
+
})
|
|
1464
|
+
var splitW = dragState[0]; var setSplitW = dragState[1]
|
|
1465
|
+
|
|
1466
|
+
function startSplit(event) {
|
|
1467
|
+
event.preventDefault()
|
|
1468
|
+
var body = event.currentTarget.parentElement
|
|
1469
|
+
if (!body) return
|
|
1470
|
+
var rect = body.getBoundingClientRect()
|
|
1471
|
+
var startW = splitW > 0 ? splitW : Math.round(rect.width * 0.62)
|
|
1472
|
+
var currentW = startW
|
|
1473
|
+
function onMove(moveEvent) {
|
|
1474
|
+
var width = Math.max(300, Math.min(moveEvent.clientX - rect.left, Math.max(300, rect.width - 360)))
|
|
1475
|
+
currentW = width
|
|
1476
|
+
setSplitW(width)
|
|
1477
|
+
}
|
|
1478
|
+
function onUp() {
|
|
1479
|
+
window.removeEventListener('pointermove', onMove)
|
|
1480
|
+
window.removeEventListener('pointerup', onUp)
|
|
1481
|
+
document.body.style.cursor = ''
|
|
1482
|
+
document.body.style.userSelect = ''
|
|
1483
|
+
try { window.localStorage.setItem('dsh-mesh-split-w', String(currentW)) } catch (error) { /* ignore */ }
|
|
1484
|
+
}
|
|
1485
|
+
document.body.style.cursor = 'col-resize'
|
|
1486
|
+
document.body.style.userSelect = 'none'
|
|
1487
|
+
window.addEventListener('pointermove', onMove)
|
|
1488
|
+
window.addEventListener('pointerup', onUp)
|
|
1489
|
+
}
|
|
1460
1490
|
|
|
1461
1491
|
function load(force) {
|
|
1462
1492
|
if (!force) {
|
|
@@ -1575,23 +1605,30 @@ function deviceLinkState(data, peer, lanMap) {
|
|
|
1575
1605
|
),
|
|
1576
1606
|
error ? h('div', { className: 'dm-err' }, error) : null,
|
|
1577
1607
|
|
|
1578
|
-
h(
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1608
|
+
h('div', { className: 'dm-body', style: splitW > 0 ? { '--dm-main-w': splitW + 'px' } : undefined },
|
|
1609
|
+
h('div', { className: 'dm-pane' },
|
|
1610
|
+
h(Viewport, { data: data, layout: computeLayout(1 + filterSelfPeers(dedupePeers(data.peers || []), data.nodeName).length), selectedKey: selectedKey, onSelect: setSelectedKey }),
|
|
1611
|
+
h(InfraPanels, { data: data }),
|
|
1612
|
+
h(DeviceDetails, { data: data, selectedKey: selectedKey })
|
|
1613
|
+
),
|
|
1614
|
+
h('div', { className: 'dm-divider', onPointerDown: startSplit, title: '↔ 拖拽调整左右宽度(双击恢复默认)', onDoubleClick: function () { setSplitW(0); try { window.localStorage.removeItem('dsh-mesh-split-w') } catch (error) { /* ignore */ } } }),
|
|
1615
|
+
h('div', { className: 'dm-pane' },
|
|
1616
|
+
h(Details, { title: '本机实例', badge: ((data.local && data.local.instances) || []).length, open: true }, h(InstanceList, { data: data })),
|
|
1617
|
+
h(Details, { title: '版本一致性', badge: versions.length ? versions.length + ' 处升级' : '一致' },
|
|
1618
|
+
versions.length
|
|
1619
|
+
? versions.map(function (item, i) { return h('div', { key: i, className: 'dm-sub', style: { marginTop: 4 } }, '· ' + (typeof item === 'string' ? item : JSON.stringify(item))) })
|
|
1620
|
+
: h('div', { className: 'dm-empty' }, '所有节点/实例版本一致')
|
|
1621
|
+
),
|
|
1622
|
+
h(Details, { title: '委托台账', badge: (data.delegations && data.delegations.total) || 0 }, h(DelegationTable, { data: data })),
|
|
1623
|
+
h(Details, { title: '告警历史', badge: ((data.alerts && data.alerts.history) || []).length }, h(AlertList, { data: data })),
|
|
1624
|
+
(issues.length || hints.length) ? h(Details, { title: '诊断建议', badge: issues.length + hints.length },
|
|
1625
|
+
h('div', null,
|
|
1626
|
+
issues.slice(0, 6).map(function (issue, i) { return h('div', { key: 'i' + i, className: 'dm-err', style: { marginTop: 3 } }, '· ' + issue) }),
|
|
1627
|
+
hints.slice(0, 6).map(function (hint, i) { return h('div', { key: 'h' + i, className: 'dm-sub', style: { marginTop: 3 } }, '· ' + hint) })
|
|
1628
|
+
)
|
|
1629
|
+
) : null
|
|
1593
1630
|
)
|
|
1594
|
-
)
|
|
1631
|
+
),
|
|
1595
1632
|
h('div', { className: 'dm-foot' }, '数据源:mesh-health / host status / peer host_self / 委托台账 / 告警历史 · ' + (data.repo || '') + ' · node ' + (data.nodeVersion || '') + ' · ' + REFRESH_MS / 1000 + 's 自动刷新(只读)')
|
|
1596
1633
|
)
|
|
1597
1634
|
}
|
|
@@ -1810,6 +1847,23 @@ body[data-ds-dark-theme] .dm-dg-fs-item.active { background:rgba(88,166,255,.28)
|
|
|
1810
1847
|
)
|
|
1811
1848
|
}
|
|
1812
1849
|
|
|
1850
|
+
/** 左侧栏「AI 网络」专用图标:中心节点 + 四向设备,和「委托」区分开。 */
|
|
1851
|
+
function networkPanelIcon(props) {
|
|
1852
|
+
var size = (props && props.size) || 16
|
|
1853
|
+
return h('svg', {
|
|
1854
|
+
'data-dsh-panel-entry': 'mesh-network', viewBox: '0 0 16 16', width: size, height: size,
|
|
1855
|
+
fill: 'none', stroke: 'currentColor', strokeWidth: '1.3', strokeLinecap: 'round', 'aria-hidden': 'true',
|
|
1856
|
+
style: { color: 'var(--dsh-brand-color, currentColor)' }
|
|
1857
|
+
},
|
|
1858
|
+
h('path', { d: 'M8 8 L8 2.6 M8 8 L13.4 8 M8 8 L8 13.4 M8 8 L2.6 8', opacity: '.55' }),
|
|
1859
|
+
h('circle', { cx: 8, cy: 8, r: 2.1, fill: 'none', strokeWidth: '1.4' }),
|
|
1860
|
+
h('circle', { cx: 8, cy: 2.6, r: 1.25, fill: 'currentColor', stroke: 'none' }),
|
|
1861
|
+
h('circle', { cx: 13.4, cy: 8, r: 1.25, fill: 'currentColor', stroke: 'none' }),
|
|
1862
|
+
h('circle', { cx: 8, cy: 13.4, r: 1.25, fill: 'currentColor', stroke: 'none' }),
|
|
1863
|
+
h('circle', { cx: 2.6, cy: 8, r: 1.25, fill: 'currentColor', stroke: 'none' })
|
|
1864
|
+
)
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1813
1867
|
function MeshDelegationPanel() {
|
|
1814
1868
|
var s0 = useState(null); var list = s0[0]; var setList = s0[1]
|
|
1815
1869
|
var s1 = useState(true); var loading = s1[0]; var setLoading = s1[1]
|
|
@@ -2249,6 +2303,111 @@ body[data-ds-dark-theme] .dm-dg-fs-item.active { background:rgba(88,166,255,.28)
|
|
|
2249
2303
|
)
|
|
2250
2304
|
}
|
|
2251
2305
|
|
|
2306
|
+
// 2026-11-10:AI 网络现在是左侧栏主页面,不再受设置面板的窄容器约束。
|
|
2307
|
+
// 主区域自身没有内边距,这里给页面统一留白并限制最大阅读宽度。
|
|
2308
|
+
var PAGE_CSS = `
|
|
2309
|
+
.dm-page { box-sizing:border-box; width:100%; height:100%; overflow:hidden; padding:clamp(16px, 2.2vw, 30px) clamp(18px, 2.6vw, 42px) 0; }
|
|
2310
|
+
.dm-content-shell { width:100%; height:100%; position:relative; --dm-content-w:920px; }
|
|
2311
|
+
.dm-content-scroll { height:100%; overflow:auto; padding-bottom:36px; scrollbar-gutter:stable; }
|
|
2312
|
+
.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; }
|
|
2313
|
+
.dm-content > .dm-root { width:100%; max-width:100%; margin:0 auto; }
|
|
2314
|
+
/* 与 DSH 对话区一致的宽度手柄:位于内容两侧外 24px,平时隐藏,hover/拖拽显示 2px 渐变线 */
|
|
2315
|
+
.dm-width-handle { z-index:0; position:absolute; top:0; bottom:0; cursor:col-resize; width:clamp(0px, calc((100% - var(--dm-content-w)) / 2 - 48px), 10px); }
|
|
2316
|
+
.dm-width-handle[data-side="left"] { right:calc(50% + var(--dm-content-w) / 2 + 24px); }
|
|
2317
|
+
.dm-width-handle[data-side="right"] { left:calc(50% + var(--dm-content-w) / 2 + 24px); }
|
|
2318
|
+
.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)); }
|
|
2319
|
+
.dm-width-handle[data-side="left"]::after { right:4px; }
|
|
2320
|
+
.dm-width-handle[data-side="right"]::after { left:4px; }
|
|
2321
|
+
.dm-width-handle[data-dragging]::after { opacity:1; }
|
|
2322
|
+
@media (hover:hover) { .dm-width-handle:hover::after { opacity:1; } }
|
|
2323
|
+
@media (max-width: 1000px) { .dm-width-handle { display:none; } }
|
|
2324
|
+
.dm-body { display:grid; grid-template-columns:minmax(300px, var(--dm-main-w, 62%)) 8px minmax(0,1fr); gap:10px; align-items:start; }
|
|
2325
|
+
.dm-pane { min-width:0; display:flex; flex-direction:column; gap:12px; }
|
|
2326
|
+
.dm-divider { cursor:col-resize; align-self:stretch; min-height:120px; border-radius:4px; position:relative; }
|
|
2327
|
+
.dm-divider::before { content:''; position:absolute; top:0; bottom:0; left:3px; width:1px; background:rgba(128,128,128,.28); }
|
|
2328
|
+
.dm-divider:hover::before, .dm-divider:active::before { background:rgba(88,166,255,.7); width:2px; left:2.5px; }
|
|
2329
|
+
@container dm-content (max-width: 860px) { .dm-body { grid-template-columns:1fr; } .dm-divider { display:none; } }
|
|
2330
|
+
`
|
|
2331
|
+
function MeshNetworkPage() {
|
|
2332
|
+
// 宽度偏好:与 DSH 对话区相同语义——空 = 默认宽度,数字 = 用户拖拽固定值。
|
|
2333
|
+
var prefState = useState(function () {
|
|
2334
|
+
try { var saved = Number(window.localStorage.getItem('dsh-mesh.contentWidth')); return Number.isFinite(saved) && saved > 0 ? saved : 0 } catch (error) { return 0 }
|
|
2335
|
+
})
|
|
2336
|
+
var pref = prefState[0]; var setPref = prefState[1]
|
|
2337
|
+
var shellState = useState(0); var containerW = shellState[0]; var setContainerW = shellState[1]
|
|
2338
|
+
var shellRef = useRef(null)
|
|
2339
|
+
|
|
2340
|
+
useEffect(function () {
|
|
2341
|
+
var el = shellRef.current
|
|
2342
|
+
if (!el) return
|
|
2343
|
+
function measure() { setContainerW(el.clientWidth || 0) }
|
|
2344
|
+
measure()
|
|
2345
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
2346
|
+
var observer = new ResizeObserver(measure)
|
|
2347
|
+
observer.observe(el)
|
|
2348
|
+
return function () { observer.disconnect() }
|
|
2349
|
+
}
|
|
2350
|
+
window.addEventListener('resize', measure)
|
|
2351
|
+
return function () { window.removeEventListener('resize', measure) }
|
|
2352
|
+
}, [])
|
|
2353
|
+
|
|
2354
|
+
// DSH 对话区公式:默认 clamp(680, 64% 容器, 920);用户值 [640, 容器-176]。
|
|
2355
|
+
var widthNow = containerW || 920
|
|
2356
|
+
var maxW = Math.max(640, widthNow - 176)
|
|
2357
|
+
var contentW = pref > 0 ? Math.min(Math.max(pref, 640), maxW) : Math.max(680, Math.min(widthNow * 0.64, 920))
|
|
2358
|
+
|
|
2359
|
+
function beginWidthDrag(side, event) {
|
|
2360
|
+
event.preventDefault()
|
|
2361
|
+
var handle = event.currentTarget
|
|
2362
|
+
var shell = handle.closest ? handle.closest('.dm-content-shell') : (handle.parentElement && handle.parentElement.parentElement)
|
|
2363
|
+
if (!shell) return
|
|
2364
|
+
var max = Math.max(640, (shell.clientWidth || 920) - 176)
|
|
2365
|
+
var base = contentW
|
|
2366
|
+
var originX = event.clientX
|
|
2367
|
+
var latest = contentW
|
|
2368
|
+
var firstBox = handle.getBoundingClientRect()
|
|
2369
|
+
handle.style.setProperty('--dm-width-handle-pointer-y', (event.clientY - firstBox.top) + 'px')
|
|
2370
|
+
handle.toggleAttribute('data-dragging', true)
|
|
2371
|
+
document.body.style.cursor = 'col-resize'
|
|
2372
|
+
document.body.style.userSelect = 'none'
|
|
2373
|
+
function onMove(moveEvent) {
|
|
2374
|
+
var dx = moveEvent.clientX - originX
|
|
2375
|
+
var outward = side === 'right' ? dx : -dx
|
|
2376
|
+
// 只有拖拽时才更新高亮位置(与 DSH 一致);hover 时保持手柄高度的 50%。
|
|
2377
|
+
var box = handle.getBoundingClientRect()
|
|
2378
|
+
handle.style.setProperty('--dm-width-handle-pointer-y', (moveEvent.clientY - box.top) + 'px')
|
|
2379
|
+
// 两侧同时外扩/内收,所以内容宽度变化是手柄位移的 2 倍(与 DSH 一致)。
|
|
2380
|
+
latest = Math.min(Math.max(base + outward * 2, 640), max)
|
|
2381
|
+
setPref(latest)
|
|
2382
|
+
}
|
|
2383
|
+
function onUp() {
|
|
2384
|
+
handle.toggleAttribute('data-dragging', false)
|
|
2385
|
+
window.removeEventListener('pointermove', onMove)
|
|
2386
|
+
window.removeEventListener('pointerup', onUp)
|
|
2387
|
+
document.body.style.cursor = ''
|
|
2388
|
+
document.body.style.userSelect = ''
|
|
2389
|
+
try { window.localStorage.setItem('dsh-mesh.contentWidth', String(latest)) } catch (error) { /* ignore */ }
|
|
2390
|
+
}
|
|
2391
|
+
window.addEventListener('pointermove', onMove)
|
|
2392
|
+
window.addEventListener('pointerup', onUp)
|
|
2393
|
+
}
|
|
2394
|
+
function resetContentWidth() {
|
|
2395
|
+
setPref(0)
|
|
2396
|
+
try { window.localStorage.removeItem('dsh-mesh.contentWidth') } catch (error) { /* ignore */ }
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
return h('div', { className: 'dm-page' },
|
|
2400
|
+
h('style', null, PAGE_CSS),
|
|
2401
|
+
h('div', { className: 'dm-content-shell', ref: shellRef, style: { '--dm-content-w': contentW + 'px' } },
|
|
2402
|
+
h('div', { className: 'dm-width-handle', 'data-side': 'left', title: '拖拽调整内容宽度(双击恢复默认)', onPointerDown: function (event) { beginWidthDrag('left', event) }, onDoubleClick: resetContentWidth }),
|
|
2403
|
+
h('div', { className: 'dm-width-handle', 'data-side': 'right', title: '拖拽调整内容宽度(双击恢复默认)', onPointerDown: function (event) { beginWidthDrag('right', event) }, onDoubleClick: resetContentWidth }),
|
|
2404
|
+
h('div', { className: 'dm-content-scroll' },
|
|
2405
|
+
h('div', { className: 'dm-content', style: { width: 'min(100%, ' + contentW + 'px)' } }, h(MeshStatusSection))
|
|
2406
|
+
)
|
|
2407
|
+
)
|
|
2408
|
+
)
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2252
2411
|
var SECTION = { name: 'settings.section', id: 'dsh-mesh', order: 90, label: 'AI 网络' }
|
|
2253
2412
|
var inject = ['slots']
|
|
2254
2413
|
|
|
@@ -2317,10 +2476,10 @@ body[data-ds-dark-theme] .dm-dg-fs-item.active { background:rgba(88,166,255,.28)
|
|
|
2317
2476
|
// 2026-11-10:AI 网络从「设置」迁到左侧栏独立页面,使用更大的主区域。
|
|
2318
2477
|
try {
|
|
2319
2478
|
disposers.push(ctx.slots.inject('sidebar.panellist', function () {
|
|
2320
|
-
return ctx.slots.register({ name: 'sidebar.panellist', id: 'mesh-network', order: 54, label: function () { return 'AI 网络' } },
|
|
2479
|
+
return ctx.slots.register({ name: 'sidebar.panellist', id: 'mesh-network', order: 54, label: function () { return 'AI 网络' } }, networkPanelIcon)
|
|
2321
2480
|
}))
|
|
2322
2481
|
disposers.push(ctx.slots.inject('main', function () {
|
|
2323
|
-
return ctx.slots.register({ name: 'main', key: 'mesh-network', inject: function () { return {} } },
|
|
2482
|
+
return ctx.slots.register({ name: 'main', key: 'mesh-network', inject: function () { return {} } }, MeshNetworkPage)
|
|
2324
2483
|
}))
|
|
2325
2484
|
} catch (error) {
|
|
2326
2485
|
console.warn('[dsh-mesh-ui] sidebar network page registration failed:', error)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mesh-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
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",
|