@nelsonlongxiang/dsh-open-a2a-net 0.6.33 → 0.6.34
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 +5 -2
- package/README_zh.md +5 -2
- package/assets/nexusDist/assets/index-D2BGWOeU.js +9 -0
- package/assets/nexusDist/assets/index-z33lHtLw.css +1 -0
- package/assets/nexusDist/index.html +2 -2
- package/lib/a2a-client.js +9 -2
- package/lib/auth-observer.js +1 -1
- package/lib/canvas-store.js +44 -6
- package/lib/card.js +2 -1
- package/lib/cli-home.js +44 -0
- package/lib/client/A2aControl.js +1 -1
- package/lib/client.js +65 -65
- package/lib/client.js.map +1 -1
- package/lib/direct-forward.js +61 -0
- package/lib/faces/admin.js +41 -5
- package/lib/faces/wire.js +55 -16
- package/lib/index.js +110 -64
- package/lib/outbound-scope.js +25 -0
- package/lib/queue-guard.js +37 -0
- package/lib/skill/references/cli.md +5 -4
- package/lib/skill/references/pitfalls.md +1 -4
- package/lib/task-auth.js +52 -3
- package/lib/task-ledger.js +4 -1
- package/lib/tools/info.js +19 -3
- package/lib/types/a2a-client.d.ts +1 -1
- package/lib/types/auth-observer.d.ts +2 -2
- package/lib/types/canvas-store.d.ts +10 -1
- package/lib/types/cli-home.d.ts +24 -0
- package/lib/types/client/A2aControl.d.ts +1 -1
- package/lib/types/direct-forward.d.ts +32 -0
- package/lib/types/faces/admin.d.ts +8 -1
- package/lib/types/faces/wire.d.ts +12 -4
- package/lib/types/index.d.ts +4 -0
- package/lib/types/outbound-scope.d.ts +21 -0
- package/lib/types/queue-guard.d.ts +32 -0
- package/lib/types/runtime.d.ts +1 -1
- package/lib/types/task-auth.d.ts +17 -2
- package/lib/types/task-ledger.d.ts +6 -1
- package/lib/types/types.d.ts +8 -0
- package/nexus-stage/src/cli-home-picker.ts +42 -0
- package/nexus-stage/src/main.ts +9 -2
- package/nexus-stage/src/planning-card.ts +61 -0
- package/nexus-stage/src/planning-menus.ts +42 -7
- package/nexus-stage/src/planning-view.ts +14 -32
- package/nexus-stage/src/planning.css +17 -0
- package/nexus-stage/src/workbench.css +4 -0
- package/nexus-stage/src/workbench.ts +22 -5
- package/nexus-stage/src/world.ts +5 -1
- package/package.json +2 -1
- package/assets/nexusDist/assets/index-BACVZe0j.css +0 -1
- package/assets/nexusDist/assets/index-Bbxnpf2p.js +0 -9
|
@@ -91,12 +91,46 @@ export function createPlanningMenus(d: MenusDeps): PlanningMenus {
|
|
|
91
91
|
title.id = 'p-dialog-title'
|
|
92
92
|
title.className = 'p-dialog-title'
|
|
93
93
|
title.textContent = `组建团队(${ids.length} 个节点,自上而下即优先级)`
|
|
94
|
-
|
|
95
|
-
kind
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
// Team type: a visible two-way toggle (a bare <select> read as a label and roster went unnoticed).
|
|
95
|
+
const kind = { value: 'canvas' as 'canvas' | 'roster' }
|
|
96
|
+
const kindGroup = document.createElement('div')
|
|
97
|
+
kindGroup.className = 'p-dialog-kind'
|
|
98
|
+
kindGroup.setAttribute('role', 'radiogroup')
|
|
99
|
+
kindGroup.setAttribute('aria-label', '团队类型')
|
|
100
|
+
const kindHint = document.createElement('div')
|
|
101
|
+
kindHint.className = 'p-dialog-hint'
|
|
102
|
+
const KIND_HINT = {
|
|
103
|
+
canvas: 'canvas 队:画布上的协作组,可以加入远端成员。',
|
|
104
|
+
roster: 'roster 网络队:只收本机已入网的会话;队名会写进属主的精确允许名单。',
|
|
105
|
+
} as const
|
|
106
|
+
const kindButtons = (['canvas', 'roster'] as const).map(value => {
|
|
107
|
+
const b = document.createElement('button')
|
|
108
|
+
b.type = 'button'
|
|
109
|
+
b.dataset.kind = value
|
|
110
|
+
b.setAttribute('role', 'radio')
|
|
111
|
+
b.textContent = value === 'canvas' ? 'canvas 队' : 'roster 网络队'
|
|
112
|
+
b.addEventListener('click', () => setKind(value))
|
|
113
|
+
kindGroup.appendChild(b)
|
|
114
|
+
return b
|
|
115
|
+
})
|
|
116
|
+
function setKind(value: 'canvas' | 'roster'): void {
|
|
117
|
+
kind.value = value
|
|
118
|
+
for (const b of kindButtons) {
|
|
119
|
+
const on = b.dataset.kind === value
|
|
120
|
+
b.setAttribute('aria-checked', String(on))
|
|
121
|
+
b.tabIndex = on ? 0 : -1
|
|
122
|
+
}
|
|
123
|
+
kindHint.textContent = KIND_HINT[value]
|
|
124
|
+
err.textContent = ''
|
|
99
125
|
}
|
|
126
|
+
// Arrow keys move between the two options (radiogroup pattern).
|
|
127
|
+
kindGroup.addEventListener('keydown', (ev) => {
|
|
128
|
+
const e = ev as KeyboardEvent
|
|
129
|
+
if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) return
|
|
130
|
+
e.preventDefault()
|
|
131
|
+
setKind(kind.value === 'canvas' ? 'roster' : 'canvas')
|
|
132
|
+
kindButtons.find(b => b.dataset.kind === kind.value)?.focus()
|
|
133
|
+
})
|
|
100
134
|
const input = document.createElement('input')
|
|
101
135
|
input.className = 'p-dialog-input'
|
|
102
136
|
input.maxLength = 40
|
|
@@ -113,7 +147,8 @@ export function createPlanningMenus(d: MenusDeps): PlanningMenus {
|
|
|
113
147
|
cancelBtn.type = 'button'
|
|
114
148
|
cancelBtn.textContent = '取消'
|
|
115
149
|
row.append(okBtn, cancelBtn)
|
|
116
|
-
|
|
150
|
+
setKind('canvas')
|
|
151
|
+
panel.append(title, kindGroup, kindHint, input, err, row)
|
|
117
152
|
wrap.appendChild(panel)
|
|
118
153
|
root.appendChild(wrap)
|
|
119
154
|
|
|
@@ -161,7 +196,7 @@ export function createPlanningMenus(d: MenusDeps): PlanningMenus {
|
|
|
161
196
|
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); close(); return }
|
|
162
197
|
if (e.key !== 'Tab') return
|
|
163
198
|
e.preventDefault()
|
|
164
|
-
const focusables = Array.from(panel.querySelectorAll<HTMLElement>('input, select, button'))
|
|
199
|
+
const focusables = Array.from(panel.querySelectorAll<HTMLElement>('input, select, button:not([tabindex="-1"])'))
|
|
165
200
|
const idx = focusables.indexOf(document.activeElement as HTMLElement)
|
|
166
201
|
const next = e.shiftKey
|
|
167
202
|
? focusables[(idx - 1 + focusables.length) % focusables.length]
|
|
@@ -51,6 +51,7 @@ import { settleBoard, tidyMembers } from './planning-overlap'
|
|
|
51
51
|
import { createLayoutUndo, type LayoutSnap } from './planning-undo'
|
|
52
52
|
import { createNodeList } from './planning-nodelist'
|
|
53
53
|
import { cardText } from './planning-card-text'
|
|
54
|
+
import { createCardElement, paintPrio, workLabel } from './planning-card'
|
|
54
55
|
import { createLegend } from './planning-legend'
|
|
55
56
|
import { UNREACHABLE_HINT, showFrameReachability, unreachableRefs } from './planning-reachability'
|
|
56
57
|
import { viewportBucket, type PlanningDeps, type PlanningInput, type PlanningSession, type PlanningTeam, type PlanningView, type SeamKey, type SeamPointer, type ViewportBucket } from './planning-types'
|
|
@@ -68,6 +69,9 @@ type Gesture =
|
|
|
68
69
|
| { readonly kind: 'pan'; last: { x: number; y: number }; moved: boolean }
|
|
69
70
|
| { readonly kind: 'marquee'; origin: { x: number; y: number }; last: { x: number; y: number }; additive: boolean }
|
|
70
71
|
|
|
72
|
+
// The card skeleton, its 工作中/空闲 chip and the right-hand chip live in planning-card (module-size cap).
|
|
73
|
+
export { workLabel } from './planning-card'
|
|
74
|
+
|
|
71
75
|
export function createPlanningView(deps: PlanningDeps): PlanningView {
|
|
72
76
|
const model = new WorldModel()
|
|
73
77
|
let layoutDoc: LayoutDoc | undefined
|
|
@@ -398,22 +402,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
|
|
|
398
402
|
seen.add(n.id)
|
|
399
403
|
let el = nodeEls.get(n.id)
|
|
400
404
|
if (el === undefined) {
|
|
401
|
-
el =
|
|
402
|
-
el.className = 'p-node'
|
|
403
|
-
el.dataset.id = n.id
|
|
404
|
-
el.tabIndex = 0
|
|
405
|
-
const nm = document.createElement('div')
|
|
406
|
-
nm.className = 'nm'
|
|
407
|
-
const dot = document.createElement('span')
|
|
408
|
-
dot.className = 'dot'
|
|
409
|
-
const name = document.createElement('span')
|
|
410
|
-
name.className = 'nm-text'
|
|
411
|
-
nm.append(dot, name)
|
|
412
|
-
const sub = document.createElement('div')
|
|
413
|
-
sub.className = 'sub mono'
|
|
414
|
-
const prio = document.createElement('span')
|
|
415
|
-
prio.className = 'prio mono'
|
|
416
|
-
el.append(nm, sub, prio)
|
|
405
|
+
el = createCardElement(n.id)
|
|
417
406
|
nodeLayer.appendChild(el)
|
|
418
407
|
nodeEls.set(n.id, el)
|
|
419
408
|
}
|
|
@@ -422,6 +411,12 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
|
|
|
422
411
|
el.style.top = `${r.y}px`
|
|
423
412
|
el.classList.toggle('cold', !n.live)
|
|
424
413
|
el.classList.toggle('remote', n.remote === true)
|
|
414
|
+
// 工作中/空闲: live local sessions only; the host reports nothing for cold or remote nodes.
|
|
415
|
+
const work = n.live && n.remote !== true ? lastWork.get(n.id) : undefined
|
|
416
|
+
el.classList.toggle('is-working', work?.status === 'running')
|
|
417
|
+
const workText = workLabel(work)
|
|
418
|
+
const workEl = el.querySelector<HTMLElement>('.work')!
|
|
419
|
+
if (workEl.textContent !== workText) workEl.textContent = workText
|
|
425
420
|
el.classList.toggle('selected', model.isSelected(n.id))
|
|
426
421
|
el.setAttribute('aria-selected', String(model.isSelected(n.id)))
|
|
427
422
|
const host = n.id.slice('peer-'.length)
|
|
@@ -436,22 +431,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
|
|
|
436
431
|
if (subEl.textContent !== subText) subEl.textContent = subText
|
|
437
432
|
const tip = unavailable ? `${txt.tip} · ${UNREACHABLE_HINT}` : txt.tip
|
|
438
433
|
if (el.title !== tip) el.title = tip
|
|
439
|
-
|
|
440
|
-
if (n.remote === true) {
|
|
441
|
-
// gh#20 W-A: the peer badge — link class colors the chip (linked
|
|
442
|
-
// green / stranger gray), the verified tick/cross rides after it.
|
|
443
|
-
// An absent link field (older host) renders the neutral 'peer'.
|
|
444
|
-
const info = peerInfoByHost.get(host)
|
|
445
|
-
const cls = info?.link === 'linked' ? 'lnk-linked' : info?.link === 'stranger' ? 'lnk-stranger' : ''
|
|
446
|
-
if (prioEl.className !== `prio mono ${cls}`.trim()) prioEl.className = `prio mono ${cls}`.trim()
|
|
447
|
-
const mark = info === undefined ? '' : info.verified === true ? '✓' : '✗'
|
|
448
|
-
const badge = (info?.link ?? 'peer') + mark
|
|
449
|
-
if (prioEl.textContent !== badge) prioEl.textContent = badge
|
|
450
|
-
} else {
|
|
451
|
-
if (prioEl.className !== 'prio mono') prioEl.className = 'prio mono'
|
|
452
|
-
const prioText = n.memberships[0] !== undefined ? `P${n.memberships[0]!.index}` : ''
|
|
453
|
-
if (prioEl.textContent !== prioText) prioEl.textContent = prioText
|
|
454
|
-
}
|
|
434
|
+
paintPrio(el.querySelector<HTMLElement>('.prio')!, n, n.remote === true ? peerInfoByHost.get(host) : undefined)
|
|
455
435
|
}
|
|
456
436
|
for (const [id, el] of [...nodeEls]) {
|
|
457
437
|
if (!seen.has(id)) { el.remove(); nodeEls.delete(id) }
|
|
@@ -470,6 +450,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
|
|
|
470
450
|
const federation = createFederationOverlay(svg, model)
|
|
471
451
|
let lastPeers: ReadonlyArray<PeerRow> = []
|
|
472
452
|
let lastRemoteTeams: ReadonlyMap<string, RemoteTeamRow[]> = new Map()
|
|
453
|
+
let lastWork: ReadonlyMap<string, Pick<PlanningSession, 'status' | 'queued'>> = new Map()
|
|
473
454
|
const peerScoreByText = new Map<string, number | undefined>()
|
|
474
455
|
/** gh#20 W-A: per-host link/verified info for the peer badges. */
|
|
475
456
|
const peerInfoByHost = new Map<string, { link?: 'linked' | 'stranger'; verified?: boolean }>()
|
|
@@ -1221,6 +1202,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
|
|
|
1221
1202
|
// excluded from bounds, so the column cannot feed back into itself).
|
|
1222
1203
|
const peerSeen = new Set<string>()
|
|
1223
1204
|
lastRemoteTeams = groupRemoteTeams(input.remoteTeams ?? [])
|
|
1205
|
+
lastWork = new Map(input.sessions.map(s => [s.id, { status: s.status, queued: s.queued }]))
|
|
1224
1206
|
reachability = unreachableRefs(input)
|
|
1225
1207
|
peerScoreByText.clear()
|
|
1226
1208
|
for (const p of input.peers ?? []) {
|
|
@@ -116,6 +116,14 @@
|
|
|
116
116
|
#dsh-plan .p-node.remote .nm-text{overflow-wrap:anywhere}
|
|
117
117
|
#dsh-plan .p-node .dot{width:8px; height:8px; border-radius:50%; background:var(--edge-live); flex:none}
|
|
118
118
|
#dsh-plan .p-node.cold .dot{background:transparent; border:1.5px solid var(--edge-cold)}
|
|
119
|
+
#dsh-plan .p-node .sub-row{display:flex; align-items:baseline; gap:6px; margin-top:2px; min-width:0}
|
|
120
|
+
#dsh-plan .p-node .sub-row .sub{flex:1 1 auto; min-width:0; margin-top:0}
|
|
121
|
+
#dsh-plan .p-node .work{flex:none; font-size:10.5px; font-weight:500; color:var(--ink-muted); white-space:nowrap}
|
|
122
|
+
#dsh-plan .p-node .work:empty{display:none}
|
|
123
|
+
#dsh-plan .p-node.is-working .work{color:var(--edge-live)}
|
|
124
|
+
#dsh-plan .p-node.is-working .dot{animation:p-working 1.4s ease-in-out infinite}
|
|
125
|
+
@keyframes p-working{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--edge-live) 55%,transparent)}50%{box-shadow:0 0 0 4px transparent}}
|
|
126
|
+
@media (prefers-reduced-motion: reduce){#dsh-plan .p-node.is-working .dot{animation:none}}
|
|
119
127
|
#dsh-plan .p-node .sub{font-size:11px; color:var(--ink-muted); margin-top:2px;
|
|
120
128
|
overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
|
|
121
129
|
/* W1: a picked search hit pulses its card for 1.2s (class removed on timer). */
|
|
@@ -252,6 +260,15 @@
|
|
|
252
260
|
#dsh-plan .p-dialog-input:hover{border-color:color-mix(in srgb,var(--uupm-ink-1) 55%,transparent)}
|
|
253
261
|
#dsh-plan .p-dialog-input:focus-visible{outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent)}
|
|
254
262
|
#dsh-plan .p-dialog-err{min-height:1.2em; font-size:11px; color:var(--uupm-error)}
|
|
263
|
+
#dsh-plan .p-dialog-kind{display:grid; grid-template-columns:1fr 1fr; gap:4px; padding:3px;
|
|
264
|
+
background:var(--uupm-bg-0); border:1px solid var(--card-line); border-radius:8px}
|
|
265
|
+
#dsh-plan .p-dialog-kind button{font:inherit; color:var(--ink-muted); background:transparent; border:1px solid transparent;
|
|
266
|
+
border-radius:6px; padding:6px 10px; cursor:pointer}
|
|
267
|
+
#dsh-plan .p-dialog-kind button:hover{color:var(--ink); background:var(--uupm-wash-8)}
|
|
268
|
+
#dsh-plan .p-dialog-kind button[aria-checked=true]{color:var(--ink); font-weight:600; background:var(--uupm-wash-15);
|
|
269
|
+
border-color:var(--accent)}
|
|
270
|
+
#dsh-plan .p-dialog-kind button:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
|
|
271
|
+
#dsh-plan .p-dialog-hint{font-size:11px; color:var(--ink-muted); max-width:340px}
|
|
255
272
|
#dsh-plan .p-dialog-row{display:flex; gap:8px; justify-content:flex-end}
|
|
256
273
|
#dsh-plan .p-dialog-row button{background:transparent; color:var(--ink); border:1px solid var(--card-line);
|
|
257
274
|
border-radius:6px; padding:5px 12px; font:inherit; cursor:pointer}
|
|
@@ -93,6 +93,10 @@ body{background:var(--n-bg);color:var(--n-ink);font:13px/1.45 system-ui,"Segoe U
|
|
|
93
93
|
.wb-member-remove:hover:not(:disabled),.wb-member-submit:hover:not(:disabled){border-color:var(--n-danger)}
|
|
94
94
|
.wb-member-submit:hover:not(:disabled){border-color:var(--n-accent)}
|
|
95
95
|
.wb-member-add{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
|
|
96
|
+
.wb-cli-home{display:flex;flex-wrap:wrap;gap:6px;padding:8px;margin:4px 0 10px;border:1px solid var(--n-border);border-radius:6px}
|
|
97
|
+
.wb-cli-home [hidden]{display:none}
|
|
98
|
+
.wb-cli-home .wb-hint{flex-basis:100%;margin-top:0}
|
|
99
|
+
.wb-cli-home .wb-member-pick{min-width:0;max-width:100%}
|
|
96
100
|
.wb-member-pick,.wb-member-manual{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);
|
|
97
101
|
border-radius:6px;padding:3px 8px;font:inherit;font-size:12px;min-width:0}
|
|
98
102
|
.wb-member-pick{flex:1 1 150px}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* @module nexus-stage/workbench
|
|
20
20
|
*/
|
|
21
21
|
import { createChatDrawer, type ChatTarget } from './wb-chat'
|
|
22
|
+
import { createCliHomePicker, type CliHomeOptions } from './cli-home-picker'
|
|
22
23
|
import { createRailTabs } from './wb-rail'
|
|
23
24
|
import { DIRECTORY_FILTERS, learnLogTitle, loadLogTitles, renderDirectory, type DirFilter } from './wb-directory'
|
|
24
25
|
import { ledgerFacts, renderLedger as paintLedger } from './wb-ledger'
|
|
@@ -130,6 +131,9 @@ export interface CanvasMemberRow {
|
|
|
130
131
|
readonly joined?: boolean
|
|
131
132
|
readonly live?: boolean
|
|
132
133
|
readonly remote?: boolean
|
|
134
|
+
/** Present only for CLI refs: null means the linked home is ambiguous or unknown. */
|
|
135
|
+
readonly cliHome?: string | null
|
|
136
|
+
readonly cliHeld?: boolean
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
/** One canvas team from /state `canvas.teams`. */
|
|
@@ -142,7 +146,7 @@ export interface CanvasTeamRow {
|
|
|
142
146
|
/** One activity-ring entry. */
|
|
143
147
|
export interface ActivityRow {
|
|
144
148
|
readonly ts: number
|
|
145
|
-
readonly dir: 'in' | 'out'
|
|
149
|
+
readonly dir: 'in' | 'out' | 'fwd'
|
|
146
150
|
readonly team: string
|
|
147
151
|
readonly peer: string
|
|
148
152
|
readonly ok: boolean
|
|
@@ -212,7 +216,8 @@ export interface WorkbenchDeps {
|
|
|
212
216
|
abandon(taskId: string, reason: string): Promise<boolean>
|
|
213
217
|
/** Canvas team member change via the control route (0.6.6): `id` is a
|
|
214
218
|
* local session id or a zone-qualified remote handle. True on success. */
|
|
215
|
-
canvasOp(action: 'add-member' | 'remove-member', name: string, id: string): Promise<boolean>
|
|
219
|
+
canvasOp(action: 'add-member' | 'remove-member', name: string, id: string, home?: string): Promise<boolean>
|
|
220
|
+
cliHomeOptions?(name: string, id: string): Promise<CliHomeOptions>
|
|
216
221
|
/** Quick dispatch (0.6.9): POST /__dsh_a2a/dispatch as a human gesture —
|
|
217
222
|
* one message to a routing team, async delivery, receipt lands in the
|
|
218
223
|
* ledger. Optional — without it the 快派 form hides. */
|
|
@@ -333,6 +338,7 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
|
|
|
333
338
|
// Canvas add-member form cache (static DOM re-mounted across polls; keyed
|
|
334
339
|
// by team name — same trick as draftForm so inputs survive the 3s poll).
|
|
335
340
|
let canvasForm: { name: string; form: HTMLFormElement; pick: HTMLSelectElement; manual: HTMLInputElement } | null = null
|
|
341
|
+
const cliHomeForms = new Map<string, HTMLFormElement>()
|
|
336
342
|
// Quick-dispatch form (0.6.9), cached per target routing team the same
|
|
337
343
|
// draftForm way: a textarea's draft must survive the 3s poll remount.
|
|
338
344
|
let dispatchForm: { team: string; form: HTMLFormElement; text: HTMLTextAreaElement; note: HTMLElement } | null = null
|
|
@@ -690,7 +696,7 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
|
|
|
690
696
|
for (const row of ring) {
|
|
691
697
|
const line = el('div', 'wb-row')
|
|
692
698
|
const dir = el('span', 'wb-row-dir')
|
|
693
|
-
dir.textContent = row.dir === 'out' ? '→' : '←'
|
|
699
|
+
dir.textContent = row.dir === 'fwd' ? '⇢' : row.dir === 'out' ? '→' : '←'
|
|
694
700
|
if (row.ok !== true) dir.classList.add('is-fail')
|
|
695
701
|
line.appendChild(dir)
|
|
696
702
|
teamCell(line, last, row.team)
|
|
@@ -962,12 +968,12 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
|
|
|
962
968
|
}
|
|
963
969
|
for (const member of fresh.members) {
|
|
964
970
|
const row = el('div', 'wb-member-row')
|
|
965
|
-
const dot = member.remote === true ? '◇ 远端' : member.live === true ? '● 现场' : member.joined === true ? '○ 冷启' : '· 未入网'
|
|
971
|
+
const dot = member.cliHeld === true ? '本机持有,无需指定' : member.remote === true ? (member.cliHome === null ? '◇ 远端 · home 未确定' : '◇ 远端') : member.live === true ? '● 现场' : member.joined === true ? '○ 冷启' : '· 未入网'
|
|
966
972
|
row.appendChild(el('span', 'wb-member-dot')).textContent = dot
|
|
967
973
|
const label = el('span', 'wb-member-label')
|
|
968
974
|
const session = (last?.sessions ?? []).find(s => s.id === member.id)
|
|
969
975
|
label.textContent = member.remote === true ? member.id : (session !== undefined ? displayName(session) : member.id)
|
|
970
|
-
label.title = member.team ?? member.id
|
|
976
|
+
label.title = member.cliHeld === true ? '本机持有,无需指定' : member.cliHome !== undefined ? (member.cliHome ?? 'home 未确定') : (member.team ?? member.id)
|
|
971
977
|
row.appendChild(label)
|
|
972
978
|
const remove = el('button', 'wb-member-remove')
|
|
973
979
|
remove.type = 'button'
|
|
@@ -978,6 +984,15 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
|
|
|
978
984
|
})
|
|
979
985
|
row.appendChild(remove)
|
|
980
986
|
card.appendChild(row)
|
|
987
|
+
if (member.cliHome === null && member.cliHeld !== true && deps.cliHomeOptions !== undefined) {
|
|
988
|
+
const key = JSON.stringify([fresh.name, member.id])
|
|
989
|
+
let form = cliHomeForms.get(key)
|
|
990
|
+
if (form === undefined) {
|
|
991
|
+
form = createCliHomePicker(() => deps.cliHomeOptions!(fresh.name, member.id), home => deps.canvasOp('add-member', fresh.name, member.id, home))
|
|
992
|
+
cliHomeForms.set(key, form)
|
|
993
|
+
}
|
|
994
|
+
card.appendChild(form)
|
|
995
|
+
}
|
|
981
996
|
}
|
|
982
997
|
// Add-member form: static DOM re-mounted per poll (draftForm trick) —
|
|
983
998
|
// rebuilt only when the inspected team changes.
|
|
@@ -1196,6 +1211,8 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
|
|
|
1196
1211
|
}
|
|
1197
1212
|
|
|
1198
1213
|
function update(state: StateBody | null): void {
|
|
1214
|
+
const unknown = new Set((state?.canvas?.teams ?? []).flatMap(team => team.members.filter(member => member.cliHome === null && member.cliHeld !== true).map(member => JSON.stringify([team.name, member.id]))))
|
|
1215
|
+
for (const key of cliHomeForms.keys()) if (!unknown.has(key)) cliHomeForms.delete(key)
|
|
1199
1216
|
last = state
|
|
1200
1217
|
// 0.6.12: refresh the readable-log list opportunistically (single flight;
|
|
1201
1218
|
// the sidebar re-renders when rows land, mid-poll remount is idempotent).
|
package/nexus-stage/src/world.ts
CHANGED
|
@@ -153,7 +153,11 @@ export function planSeatClear(sid: string, occupied: ReadonlyArray<{ readonly x:
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/** Minimal session row shape the model needs (topology.SessionRow satisfies it). */
|
|
156
|
-
export interface SessionLite {
|
|
156
|
+
export interface SessionLite {
|
|
157
|
+
id: string; label: string; team: string; name?: string; description?: string; joined: boolean; live?: boolean; teams?: readonly string[]
|
|
158
|
+
/** Live local sessions only: mid-turn ('running') or idle, and how many receipt-owing tasks wait on it. */
|
|
159
|
+
status?: 'running' | 'idle'; queued?: number
|
|
160
|
+
}
|
|
157
161
|
|
|
158
162
|
/** Minimal team shape (topology.CanvasTeam satisfies it). `team` is the
|
|
159
163
|
* mono route alias `<host>/canvas/<name>` when the payload carries it. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nelsonlongxiang/dsh-open-a2a-net",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.34",
|
|
4
4
|
"description": "Open A2A network plugin for DeepSeek Harness: signed agent cards, decentralized peer/zone discovery, direct routing model tools, and joinable session nodes with a sidebar control",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
"test:live": "vitest run tests/live",
|
|
86
86
|
"test:build-layout": "vitest run tests/gates/build-layout.spec.ts",
|
|
87
87
|
"verify:docs": "node scripts/doc-drift.mjs",
|
|
88
|
+
"reanchor:arch": "node scripts/reanchor-architecture.mjs",
|
|
88
89
|
"verify:nexus": "node scripts/verify-nexus-ci.mjs && npm --prefix nexus-stage run build && npm --prefix nexus-stage run test && git diff --exit-code -- assets/nexusDist/assets && git diff --ignore-cr-at-eol --exit-code -- assets/nexusDist/index.html",
|
|
89
90
|
"verify": "pnpm typecheck && pnpm verify:docs && pnpm build && pnpm test && pnpm test:build-layout && pnpm verify:nexus"
|
|
90
91
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#dsh-plan *,#dsh-plan *:before,#dsh-plan *:after{box-sizing:border-box}#dsh-plan{--uupm-ink-0:var(--n-ink,#dde3f0);--uupm-ink-1:var(--n-ink-muted,#7a86a0);--uupm-bg-0:var(--n-bg,#060a12);--uupm-bg-1:var(--n-panel,#0d1322);--uupm-bg-2:var(--n-raised,#131b2e);--uupm-accent:var(--n-accent,#9673f7);--uupm-live:var(--n-live,#4ade80);--uupm-cold:var(--n-cold,#7a86a0);--uupm-edge-member:var(--n-canvas,#22d3ee);--uupm-warn:var(--n-act,#f59e0b);--uupm-error:var(--n-danger,#f87171);--uupm-indigo:var(--n-federal,#6366f1);--uupm-wash-5:color-mix(in srgb,var(--uupm-accent) 5%,transparent);--uupm-wash-8:color-mix(in srgb,var(--uupm-accent) 8%,transparent);--uupm-wash-12:color-mix(in srgb,var(--uupm-accent) 12%,transparent);--uupm-wash-15:color-mix(in srgb,var(--uupm-accent) 15%,transparent);--uupm-wash-18:color-mix(in srgb,var(--uupm-accent) 18%,transparent);--surface:var(--uupm-bg-1);--ink:var(--uupm-ink-0);--ink-muted:var(--uupm-ink-1);--accent:var(--uupm-accent);--edge-live:var(--uupm-live);--edge-cold:var(--uupm-cold);--federal:var(--uupm-indigo);--card-bg:var(--surface);--card-line:var(--n-card-line,#5c6680);--card-line-hover:var(--uupm-ink-1);--toolbar-bg:color-mix(in srgb,var(--uupm-bg-1) 92%,transparent);--grid-dot:color-mix(in srgb,var(--uupm-ink-1) 22%,transparent);--scrim:color-mix(in srgb,var(--uupm-bg-0) 60%,transparent);--error-ink:var(--n-error-ink,#fecaca);--error-panel:var(--n-error-panel,#2a0d10f2)}#dsh-plan{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden;background:var(--uupm-bg-0);color:var(--ink);font-size:13px;line-height:1.5;outline:none}#dsh-plan .mono{font-family:ui-monospace,Cascadia,Consolas,monospace}#dsh-plan:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}#dsh-plan .p-grid{position:absolute;top:0;right:0;bottom:0;left:0;z-index:0;pointer-events:none;background-image:radial-gradient(circle,var(--grid-dot) 1px,transparent 1px);background-size:24px 24px;background-position:0 0}#dsh-plan .p-world{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;transform-origin:0 0}#dsh-plan .p-layer{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none}#dsh-plan .p-edges{position:absolute;overflow:visible;width:100%;height:100%;pointer-events:none}#dsh-plan .p-frame{pointer-events:none}#dsh-plan .p-frame-head,#dsh-plan .p-node{pointer-events:auto}#dsh-plan .e-member{stroke:var(--uupm-edge-member);stroke-opacity:.38;stroke-width:1.5}#dsh-plan .e-member.dashed{stroke-dasharray:3 4}#dsh-plan .p-frame{--frame-ink:var(--frame-line,#8B5CF6)}@media (prefers-color-scheme: light){#dsh-plan .p-frame{--frame-ink:color-mix(in srgb,var(--frame-line,#8B5CF6) 72%,black)}}#dsh-plan .p-frame{position:absolute;border:1px solid var(--frame-ink);border-radius:10px;background:var(--frame-bg,var(--uupm-wash-5));min-width:120px;min-height:60px}#dsh-plan .p-frame-head{position:absolute;top:-13px;left:50%;transform:translate(-50%);height:24px;padding:0 10px;border-radius:6px;display:flex;gap:8px;align-items:center;background:var(--uupm-bg-2);border:1px solid var(--frame-ink);font-size:12px;font-weight:600;letter-spacing:.02em;white-space:nowrap;cursor:grab}#dsh-plan .p-frame-head:active{cursor:grabbing}#dsh-plan .p-frame-head:hover{border-color:var(--card-line-hover)}#dsh-plan .p-frame-head .cnt{color:var(--ink-muted);font-weight:400}#dsh-plan .p-frame-head .route{font-size:10.5px;color:var(--ink-muted)}#dsh-plan .p-frame.is-mirror{border-style:dashed}#dsh-plan .p-frame.is-roster{border-style:double;border-width:3px}#dsh-plan .p-frame.is-roster .p-frame-head{border-style:dashed}#dsh-plan .p-frame.is-syncing .p-frame-head .route{color:var(--accent)}#dsh-plan .p-frame.is-unreachable{opacity:.76;border-style:dashed}#dsh-plan .p-frame-unreachable{position:absolute;left:8px;top:20px;max-width:calc(100% - 16px);color:var(--uupm-error);font-size:11px;pointer-events:none}#dsh-plan .p-node.remote.is-unreachable{opacity:.72;border-left-color:var(--uupm-error)}#dsh-plan .p-node.remote.is-unreachable .sub{color:var(--uupm-error)}#dsh-plan .p-node{position:absolute;width:172px;height:56px;padding:8px 26px 8px 14px;background:var(--card-bg);border:1px solid var(--card-line);border-left:3px solid var(--edge-live);border-radius:8px;cursor:grab;-webkit-user-select:none;user-select:none;transition:border-color .12s cubic-bezier(.2,0,0,1),transform .12s cubic-bezier(.2,0,0,1)}#dsh-plan .p-node:hover{border-color:var(--card-line-hover)}#dsh-plan .p-node:active{cursor:grabbing}#dsh-plan .p-node:focus-visible{outline:2px solid var(--accent);outline-offset:2px}#dsh-plan .p-node.selected{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-node.dragging{transform:scale(1.02);border-color:var(--accent)}#dsh-plan .p-node.cold{border-left-color:var(--edge-cold)}#dsh-plan .p-node .nm{font-weight:600;display:flex;align-items:center;gap:7px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#dsh-plan .p-node .nm-text{min-width:0;overflow:hidden;white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:1.25}#dsh-plan .p-node.remote .nm-text{overflow-wrap:anywhere}#dsh-plan .p-node .dot{width:8px;height:8px;border-radius:50%;background:var(--edge-live);flex:none}#dsh-plan .p-node.cold .dot{background:transparent;border:1.5px solid var(--edge-cold)}#dsh-plan .p-node .sub{font-size:11px;color:var(--ink-muted);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#dsh-plan .p-node.pulse{box-shadow:0 0 0 2px var(--accent),0 0 14px 4px var(--uupm-wash-15);border-color:var(--accent)}#dsh-plan .p-search{position:relative}#dsh-plan .p-search-input{width:170px;min-height:28px;padding:2px 8px;font-size:12px;background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:6px}#dsh-plan .p-search-input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-search-list{position:absolute;top:100%;left:0;margin-top:4px;display:none;min-width:240px;max-height:220px;overflow:auto;z-index:9;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:6px}#dsh-plan .p-search-hit{padding:5px 10px;font-size:12px;color:var(--ink);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#dsh-plan .p-search-hit:hover,#dsh-plan .p-search-hit.active{background:var(--uupm-wash-15)}#dsh-plan .p-node .prio{position:absolute;top:6px;right:8px;font-size:10px;color:var(--ink-muted);border:1px solid var(--card-line);border-radius:4px;padding:0 5px}#dsh-plan .p-marquee{position:absolute;z-index:2;display:none;pointer-events:none;border:1px solid var(--accent);background:var(--uupm-wash-8)}#dsh-plan .p-toolbar{position:absolute;top:16px;left:16px;z-index:3;display:flex;gap:6px;align-items:center;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:6px 8px}#dsh-plan .p-tabs{position:absolute;top:16px;left:50%;transform:translate(-50%);z-index:3;display:flex;gap:4px;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:4px}#dsh-plan .p-tabs [role=tab]{background:transparent;color:var(--ink-muted);border:1px solid transparent;border-radius:6px;padding:4px 12px;font:inherit;cursor:pointer}#dsh-plan .p-tabs [role=tab]:hover{color:var(--ink);border-color:var(--card-line)}#dsh-plan .p-tabs [role=tab][aria-selected=true]{color:var(--ink);border-color:var(--accent);background:var(--uupm-wash-12)}#dsh-plan .p-tabs [role=tab]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-toolbar button{background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:6px;padding:4px 10px;font:inherit;cursor:pointer;white-space:nowrap}#dsh-plan .p-toolbar button:hover{border-color:var(--accent)}#dsh-plan .p-toolbar button:active{background:var(--uupm-wash-15)}#dsh-plan .p-toolbar button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-zoom{min-width:44px;text-align:center;font-size:11px;color:var(--ink-muted)}#dsh-plan .p-sep{align-self:stretch;width:1px;margin:2px 4px;background:var(--card-line)}#dsh-plan .p-lamp{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--ink-muted);padding:0 6px}#dsh-plan .p-lamp i{width:7px;height:7px;border-radius:50%;background:var(--ink-muted);transition:background .14s ease}#dsh-plan .p-lamp.saved i{background:var(--edge-live)}#dsh-plan .p-lamp.pending i{background:var(--uupm-warn)}#dsh-plan .p-lamp.error{color:var(--uupm-error);cursor:pointer}#dsh-plan .p-lamp.error i{background:var(--uupm-error)}#dsh-plan .p-lamp.error:hover{border:1px solid var(--uupm-error);border-radius:4px}#dsh-plan .p-status{position:absolute;top:16px;right:16px;z-index:3;display:flex;gap:14px;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:8px 14px;font-size:12px}#dsh-plan .p-status b{font-weight:600}#dsh-plan .ok{color:var(--edge-live)}#dsh-plan .warn{color:var(--uupm-warn)}#dsh-plan .mut{color:var(--ink-muted)}#dsh-plan .p-legend{position:absolute;bottom:16px;left:16px;z-index:3;font-size:11px;color:var(--ink-muted);background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:4px 10px}#dsh-plan .p-legend summary{cursor:pointer;padding:2px 0;-webkit-user-select:none;user-select:none}#dsh-plan .p-legend summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}#dsh-plan .p-legend[open]{padding-bottom:8px}#dsh-plan .p-legend-rows{display:grid;gap:4px;margin-top:4px}#dsh-plan .p-legend i{display:inline-block;width:18px;border-top:2px solid var(--uupm-edge-member);vertical-align:middle;margin-right:6px}#dsh-plan .p-legend i.dashed{border-top-style:dashed}@media (max-width:680px){#dsh-plan .p-toolbar{position:relative;top:auto;left:auto;margin:8px 8px 0;flex-wrap:wrap;row-gap:6px}#dsh-plan .p-toolbar>*{flex:none}#dsh-plan .p-toolbar .p-search{order:-1;flex:1 1 100%}#dsh-plan .p-toolbar .p-sep{display:none}#dsh-plan .p-toolbar .p-search-input{width:100%;box-sizing:border-box}#dsh-plan .p-status{position:relative;top:auto;right:auto;left:auto;width:fit-content;margin:6px 8px 0;padding:6px 10px;gap:10px;flex-wrap:wrap}#dsh-plan .p-legend{display:none}}@media (pointer:coarse){#dsh-plan .p-toolbar button{min-height:36px;min-width:36px}}@media (prefers-reduced-motion: reduce){#dsh-plan *{transition:none!important;animation:none!important}}#dsh-plan .p-frame.drop-target{border-color:var(--accent);border-width:2px;background:var(--uupm-wash-12)}#dsh-plan .p-frame-head:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-node.remote{border-left-color:var(--federal);padding-right:46px}#dsh-plan .p-node.remote .dot{background:var(--federal)}#dsh-plan .p-node.remote .prio{color:var(--ink)}#dsh-plan .p-node .prio.lnk-linked{color:var(--live, #2e9e5b);border-color:var(--live, #2e9e5b)}#dsh-plan .p-node .prio.lnk-stranger{color:var(--ink-muted);border-color:var(--card-line)}#dsh-plan .e-activity{stroke:var(--accent);stroke-width:2;stroke-dasharray:8 6;stroke-opacity:.9}#dsh-plan .e-federal{stroke:var(--federal);stroke-width:1.5;stroke-dasharray:6 5;stroke-opacity:.5}#dsh-plan .edge-label{font-size:10.5px;fill:var(--ink-muted)}@media (prefers-reduced-motion: no-preference){#dsh-plan .e-activity{animation:p-activity-flow 1.1s linear infinite}}@keyframes p-activity-flow{to{stroke-dashoffset:-28}}#dsh-plan .p-status{cursor:pointer}#dsh-plan .p-status:hover{border-color:var(--accent)}#dsh-plan .p-status:active{background:var(--uupm-wash-12)}#dsh-plan .p-status:focus-visible{outline:2px solid var(--accent);outline-offset:2px}#dsh-plan .p-menu{position:fixed;z-index:5;min-width:170px;display:grid;padding:4px;gap:2px;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px}#dsh-plan .p-menu [role=menuitem]{background:transparent;color:var(--ink);border:1px solid transparent;border-radius:6px;padding:6px 10px;font:inherit;text-align:left;cursor:pointer;white-space:nowrap}#dsh-plan .p-menu [role=menuitem]:hover{border-color:var(--card-line);background:var(--uupm-wash-8)}#dsh-plan .p-menu [role=menuitem]:active{background:var(--uupm-wash-18)}#dsh-plan .p-menu [role=menuitem]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-menu [role=menuitem][aria-disabled=true]{color:var(--ink-muted);cursor:not-allowed}#dsh-plan .p-dialog{position:absolute;top:0;right:0;bottom:0;left:0;z-index:6;display:grid;place-items:center;background:var(--scrim)}#dsh-plan .p-dialog-panel{min-width:300px;padding:14px 16px;display:grid;gap:10px;background:var(--uupm-bg-2);border:1px solid var(--card-line);border-radius:10px}#dsh-plan .p-dialog-title{font-weight:600;font-size:12.5px;color:var(--ink)}#dsh-plan .p-dialog-input{font:inherit;color:var(--ink);background:var(--uupm-bg-0);border:1px solid var(--card-line);border-radius:6px;padding:7px 10px}#dsh-plan .p-dialog-input:hover{border-color:color-mix(in srgb,var(--uupm-ink-1) 55%,transparent)}#dsh-plan .p-dialog-input:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}#dsh-plan .p-dialog-err{min-height:1.2em;font-size:11px;color:var(--uupm-error)}#dsh-plan .p-dialog-row{display:flex;gap:8px;justify-content:flex-end}#dsh-plan .p-dialog-row button{background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:6px;padding:5px 12px;font:inherit;cursor:pointer}#dsh-plan .p-dialog-row button:hover{border-color:var(--accent)}#dsh-plan .p-dialog-row button:active{background:var(--uupm-wash-15)}#dsh-plan .p-dialog-row button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-notice-stack{position:absolute;bottom:52px;left:50%;transform:translate(-50%);z-index:5;display:grid;gap:6px;justify-items:center;pointer-events:none}#dsh-plan .p-notice{max-width:60ch;padding:7px 12px;font-size:12px;color:var(--ink);background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#dsh-plan .p-notice.error{color:var(--error-ink);border-color:var(--uupm-error);background:var(--error-panel)}#dsh-plan .p-netbtn{color:var(--ink-muted)}#dsh-plan .p-netbtn:not([disabled]){color:var(--ink)}#dsh-plan .p-netmenu{max-height:240px;overflow:auto}#dsh-plan .p-nodebtn{color:var(--ink)}#dsh-plan .p-nodelist{min-width:300px;max-width:340px;max-height:420px;overflow:auto;padding:6px}#dsh-plan .p-nodelist-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:2px 4px 6px;border-bottom:1px solid var(--card-line)}#dsh-plan .p-nodelist-title{font-size:12px;font-weight:600;color:var(--ink)}#dsh-plan .p-nodesel{font:inherit;font-size:11.5px;color:var(--ink);background:var(--uupm-bg-0);border:1px solid var(--card-line);border-radius:6px;padding:2px 6px;max-width:220px}#dsh-plan .p-nodesel:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-nodelist-group{font-size:11px;color:var(--ink-muted);padding:8px 4px 3px;display:flex;align-items:baseline;gap:8px}#dsh-plan .p-nodelist-note{font-size:10px;color:var(--ink-muted);opacity:.8}#dsh-plan .p-noderow{display:flex;align-items:center;gap:6px;padding:3px 4px;border-radius:6px;font-size:12px;color:var(--ink)}#dsh-plan .p-noderow:hover{background:var(--uupm-wash-8)}#dsh-plan .p-nodedot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--edge-live)}#dsh-plan .p-nodedot.cold{background:var(--ink-muted)}#dsh-plan .p-nodedot.off{background:transparent;border:1px solid var(--ink-muted)}#dsh-plan .p-nodename{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#dsh-plan .p-nonet{flex:none;font-size:10px;color:var(--uupm-warn);border:1px solid var(--uupm-warn);border-radius:4px;padding:0 4px}#dsh-plan .p-teamcount{flex:none;font-size:10px;color:var(--ink-muted);border:1px solid var(--card-line);border-radius:4px;padding:0 4px}#dsh-plan .p-listbtn{flex:none;background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:5px;padding:1px 7px;font:inherit;font-size:11px;cursor:pointer}#dsh-plan .p-listbtn:hover{border-color:var(--card-line-hover);background:var(--uupm-wash-8)}#dsh-plan .p-listbtn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-nodelist-empty{padding:10px 4px;font-size:11.5px;color:var(--ink-muted)}#dsh-plan .p-frame{position:absolute}#dsh-plan .p-frame-handle{position:absolute;pointer-events:auto;width:10px;height:10px;z-index:2;opacity:0;transition:opacity .12s ease}#dsh-plan .p-frame:hover .p-frame-handle{opacity:1}#dsh-plan .p-frame-handle:after{content:"";position:absolute;top:2px;right:2px;bottom:2px;left:2px;background:var(--frame-ink);border-radius:2px}#dsh-plan .p-h-n{top:-5px;left:calc(50% - 12px);width:24px;height:8px;cursor:ns-resize}#dsh-plan .p-h-s{bottom:-5px;left:calc(50% - 12px);width:24px;height:8px;cursor:ns-resize}#dsh-plan .p-h-e{right:-5px;top:calc(50% - 12px);width:8px;height:24px;cursor:ew-resize}#dsh-plan .p-h-w{left:-5px;top:calc(50% - 12px);width:8px;height:24px;cursor:ew-resize}#dsh-plan .p-h-ne{top:-5px;right:-5px;cursor:nesw-resize}#dsh-plan .p-h-nw{top:-5px;left:-5px;cursor:nwse-resize}#dsh-plan .p-h-se{bottom:-5px;right:-5px;cursor:nwse-resize}#dsh-plan .p-h-sw{bottom:-5px;left:-5px;cursor:nesw-resize}#dsh-plan .p-h-ne:after,#dsh-plan .p-h-sw:after{transform:rotate(45deg)}#dsh-plan .p-h-nw:after,#dsh-plan .p-h-se:after{transform:rotate(-45deg)}#dsh-plan .p-frame.selected .p-frame-handle{opacity:1}#dsh-plan .p-frame.selected{outline:1px solid var(--frame-ink)}:root{color-scheme:dark;--n-bg:#060a12;--n-ink:#dde3f0;--n-ink-muted:#7a86a0;--n-panel:#0d1322;--n-live:#4ade80;--n-cold:#7a86a0;--n-border:#223052;--n-act:#f59e0b;--n-danger:#f87171;--n-accent:#9673f7;--n-rail-w:300px;--n-canvas:#22d3ee;--n-line:var(--n-border);--n-raised:#131b2e;--n-federal:#6366f1;--n-card-line:#5c6680;--n-error-ink:#fecaca;--n-error-panel:#2a0d10f2;--n-shadow:rgba(0,0,0,.35)}@media (prefers-color-scheme: light){:root{--n-bg:#eef2fa;--n-ink:#1a2438;--n-ink-muted:#5a6580;--n-panel:#ffffff;--n-live:#157a37;--n-border:#c6d0e6;--n-act:#a84d08;--n-danger:#b91c1c;--n-accent:#6d28d9;--n-cold:#636c81;--n-canvas:#0e7490;--n-raised:#ffffff;--n-federal:#4f46e5;--n-card-line:#8a95ae;--n-error-ink:#b91c1c;--n-error-panel:#fef2f2f2;--n-shadow:rgba(26,36,56,.16)}}@media (min-width:1440px){:root{--n-rail-w:340px }}html,body,#app{height:100%;margin:0}body{background:var(--n-bg);color:var(--n-ink);font:13px/1.45 system-ui,Segoe UI,Roboto,PingFang SC,Microsoft YaHei,sans-serif}.wb{display:flex;flex-direction:column;height:100%}.wb-header{display:flex;align-items:center;gap:12px;padding:8px 14px;border-bottom:1px solid var(--n-border);background:var(--n-panel)}.wb-title{display:flex;align-items:center;gap:10px;font-weight:600;font-size:14px;white-space:nowrap}.wb-badge{font-size:11px;font-weight:400;color:var(--n-ink-muted);border:1px solid var(--n-border);border-radius:99px;padding:1px 8px}.wb-badge[hidden]{display:none}.wb-row-remote{margin-left:6px;padding:0 6px;border:1px solid currentColor;border-radius:99px;font-size:10.5px;color:var(--n-ink-muted);vertical-align:1px}.wb-header-stats{display:flex;gap:16px;font-size:12px;color:var(--n-ink-muted);flex-wrap:wrap;min-width:0}.wb-header-stats .wb-kv{display:flex;gap:6px}.wb-header-stats .wb-kv-k{color:var(--n-ink-muted)}.wb-header-stats .wb-kv-v{color:var(--n-ink)}.wb-header-ok{color:var(--n-ink-muted)}.wb-alert{border:1px solid currentColor;border-radius:99px;padding:0 9px;line-height:20px;white-space:nowrap;cursor:pointer;background:transparent;font:inherit}.wb-alert:hover{background:color-mix(in srgb,currentColor 12%,transparent)}.wb-alert:focus-visible{outline:2px solid currentColor;outline-offset:2px}.wb-alert-danger{color:var(--n-danger)}.wb-alert-warn{color:var(--n-act)}.wb-forensics{font-family:ui-monospace,Consolas,monospace;font-size:11px}.wb-icon-btn{background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:8px;width:30px;height:30px;display:none;align-items:center;justify-content:center;font:inherit;font-size:14px;cursor:pointer;flex:none}.wb-icon-btn:hover{border-color:var(--n-accent)}.wb-menu{margin-left:0}.wb-rail-toggle{margin-left:auto}.wb.is-rail-collapsed .wb-rail{display:none}.wb-body{position:relative;display:grid;grid-template-columns:minmax(0,var(--n-rail-w)) minmax(0,1fr);flex:1;min-height:0}.wb-rail{border-right:1px solid var(--n-border);min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;background:color-mix(in srgb,var(--n-panel) 60%,var(--n-bg))}.wb-side-head{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--n-ink-muted);margin:10px 2px 4px}.wb-item{display:flex;gap:7px;align-items:center;width:100%;text-align:left;background:none;border:none;color:var(--n-ink);font:inherit;padding:3px 6px;border-radius:6px;cursor:pointer}.wb-item:hover{background:color-mix(in srgb,var(--n-accent) 14%,transparent)}.wb-dot{font-size:10px}.wb-dot-live{color:var(--n-live)}.wb-dot-cold{color:var(--n-cold)}.wb-dot-canvas{color:var(--n-canvas)}.wb-dot-remote{color:var(--n-act)}.wb-dir-tools{display:flex;flex-direction:column;gap:6px;padding:8px 6px 4px}.wb-search{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:4px 8px;font:inherit;font-size:12.5px;min-width:0}.wb-search:focus{outline:1px solid var(--n-accent)}.wb-filters{display:flex;flex-wrap:wrap;gap:4px}.wb-filter{background:none;border:1px solid var(--n-border);color:var(--n-ink-muted);border-radius:99px;padding:0 10px;min-height:24px;font:inherit;font-size:11.5px;cursor:pointer}.wb-filter:hover{border-color:var(--n-accent);color:var(--n-ink)}.wb-filter.is-active{border-color:var(--n-accent);color:var(--n-ink);background:color-mix(in srgb,var(--n-accent) 16%,transparent)}.wb-item-badge{flex:none;font-size:10.5px;color:var(--n-canvas);border:1px solid color-mix(in srgb,var(--n-canvas) 45%,transparent);border-radius:99px;padding:0 6px;line-height:17px;white-space:nowrap}.wb-item-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-member-row{display:flex;align-items:center;gap:8px;padding:3px 2px;border-radius:6px}.wb-member-row:hover{background:color-mix(in srgb,var(--n-accent) 10%,transparent)}.wb-member-dot{flex:none;font-size:11px;color:var(--n-ink-muted);min-width:44px}.wb-member-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-member-remove,.wb-member-submit{background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:1px 8px;font:inherit;font-size:11.5px;cursor:pointer;flex:none}.wb-member-remove:hover:not(:disabled),.wb-member-submit:hover:not(:disabled){border-color:var(--n-danger)}.wb-member-submit:hover:not(:disabled){border-color:var(--n-accent)}.wb-member-add{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}.wb-member-pick,.wb-member-manual{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:3px 8px;font:inherit;font-size:12px;min-width:0}.wb-member-pick{flex:1 1 150px}.wb-member-manual{flex:1 1 130px}.wb-member-pick:focus,.wb-member-manual:focus{outline:1px solid var(--n-accent)}.wb-hint{margin-top:10px;font-size:11px;color:var(--n-ink-muted);line-height:1.5}.wb-dispatch{margin-top:12px;border-top:1px dashed var(--n-border);padding-top:8px}.wb-dispatch-form{display:flex;flex-direction:column;gap:6px;margin-top:6px}.wb-dispatch-text{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:5px 8px;font:inherit;font-size:12px;min-height:52px;resize:vertical}.wb-dispatch-text:focus{outline:1px solid var(--n-accent)}.wb-dispatch-submit{align-self:flex-end;background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:2px 14px;font:inherit;font-size:11.5px;cursor:pointer}.wb-dispatch-submit:hover:not(:disabled){border-color:var(--n-accent)}.wb-dispatch-note{font-size:11px;color:var(--n-ink-muted);line-height:1.4;min-height:0}.wb-stage{position:relative;min-width:0;min-height:0;display:flex;flex-direction:column}.wb-stage-board{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden}.wb-stage-board.is-hidden{display:none}.wb-data{display:flex;flex-direction:column;gap:14px;padding:10px 14px;overflow-y:auto;min-height:0}.wb-data.is-hidden{display:none}.wb-pane-head{font-size:12px;color:var(--n-ink-muted);margin:4px 0 8px}.wb-pane-subhead{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--n-ink-muted);margin:12px 0 6px}.wb-table,.wb-row{display:flex;gap:10px;align-items:baseline}.wb-row{font-family:ui-monospace,Consolas,monospace;font-size:12px;background:color-mix(in srgb,var(--n-panel) 55%,transparent);border:1px solid var(--n-border);border-radius:6px;padding:3px 8px;flex-wrap:wrap}.wb-row-team{color:var(--n-accent);min-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-row-meta{color:var(--n-ink-muted)}.wb-row-badges{color:var(--n-live)}.wb-row-dir{min-width:16px}.wb-row-dir.is-fail{color:var(--n-danger)}.wb-btn{background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:2px 10px;font:inherit;font-size:12px;cursor:pointer;margin-left:auto}.wb-btn:hover:not(:disabled){border-color:var(--n-accent)}.wb-btn:disabled{opacity:.5;cursor:default}.wb-btn-primary{border-color:var(--n-accent);color:var(--n-ink)}.wb-action{display:flex;gap:10px;align-items:baseline;font-size:12.5px;padding:5px 8px;border-radius:6px;border:1px solid var(--n-border);margin-bottom:4px}.wb-action-high{border-color:var(--n-danger)}.wb-action-text{flex:1}.wb-draft{display:flex;flex-direction:column;gap:8px;max-width:560px;margin-top:6px}.wb-select,.wb-textarea{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:8px;padding:6px 9px;font:inherit}.wb-textarea{resize:vertical}.wb-select:focus,.wb-textarea:focus{outline:1px solid var(--n-accent)}.wb-inspector{position:absolute;top:8px;right:8px;width:280px;max-height:calc(100% - 16px);overflow-y:auto;background:var(--n-panel);border:1px solid var(--n-border);border-radius:10px;padding:10px;z-index:20;box-shadow:0 8px 24px var(--n-shadow);display:none}.wb-inspector.is-open{display:block}.wb-inspector-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.wb-inspector-title{font-weight:600;font-size:12.5px}.wb-inspector .wb-icon-btn{display:inline-flex;width:24px;height:24px;font-size:12px}.wb-card{display:flex;flex-direction:column;gap:6px}.wb-kv{display:flex;gap:8px;font-size:12px}.wb-kv-k{color:var(--n-ink-muted);min-width:58px;flex:none}.wb-kv-v{word-break:break-all}.wb-muted{color:var(--n-ink-muted)}.wb-chat{position:absolute;top:8px;right:8px;bottom:8px;width:400px;max-width:calc(100% - 16px);display:flex;flex-direction:column;background:var(--n-panel);border:1px solid var(--n-border);border-radius:12px;z-index:22;box-shadow:0 8px 24px var(--n-shadow);overflow:hidden;transform:translate(calc(100% + 12px));transition:transform .18s ease;pointer-events:none}.wb-chat.is-open{transform:translate(0);pointer-events:auto}.wb-chat-head{display:flex;align-items:center;gap:8px;padding:10px 10px 10px 12px;border-bottom:1px solid var(--n-border);flex:none}.wb-chat-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--n-ink-muted)}.wb-chat-dot.is-live{background:var(--n-live)}.wb-chat-dot.is-cold{background:transparent;border:1.5px solid var(--n-ink-muted)}.wb-chat-dot.is-peer{background:var(--n-federal)}.wb-chat-titles{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;line-height:1.3}.wb-chat-title{font-weight:600;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-chat-sub{font:11px/1.4 ui-monospace,Consolas,monospace;color:var(--n-ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-chat .wb-icon-btn{display:inline-flex;width:28px;height:28px;font-size:13px;flex:none;border-color:transparent}.wb-chat .wb-icon-btn:hover{border-color:var(--n-border)}.wb-chat .wb-icon-btn:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-chat .wb-chat-back[hidden]{display:none}.wb-chat-info-btn[aria-expanded=true]{border-color:var(--n-accent);color:var(--n-accent)}.wb-chat-info{flex:none;display:flex;flex-direction:column;gap:4px;padding:8px 12px;font-size:12px;border-bottom:1px solid var(--n-border);background:color-mix(in srgb,var(--n-bg) 55%,var(--n-panel))}.wb-chat-info[hidden]{display:none}.wb-chat-info .wb-kv-v{font-family:ui-monospace,Consolas,monospace;font-size:11.5px}.wb-chat-thread{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;gap:10px;padding:12px}.wb-chat-thread:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px}.wb-chat-log,.wb-chat-sent{display:flex;flex-direction:column;gap:10px}.wb-chat-notice{align-self:center;max-width:92%;text-align:center;font-size:11.5px;line-height:1.5;color:var(--n-ink-muted);border:1px dashed var(--n-border);border-radius:8px;padding:6px 10px}.wb-chat-notice[hidden]{display:none}.wb-chat-empty{align-self:center;padding:24px 8px;font-size:12px;color:var(--n-ink-muted);text-align:center}.wb-chat-empty.is-error{color:var(--n-danger)}.wb-msg{display:flex;flex-direction:column;gap:4px;max-width:86%;padding:8px 11px;border-radius:12px;font-size:13px;line-height:1.55}.wb-msg-assistant{align-self:flex-start;background:var(--n-raised);border:1px solid var(--n-border);border-bottom-left-radius:4px}.wb-msg-user{align-self:flex-end;background:color-mix(in srgb,var(--n-accent) 16%,var(--n-panel));border:1px solid color-mix(in srgb,var(--n-accent) 35%,transparent);border-bottom-right-radius:4px}.wb-msg-ledger{align-self:center;max-width:92%;background:transparent;border:1px dashed var(--n-border);font-size:12px}.wb-msg-ledger .wb-msg-text{color:var(--n-ink-muted)}.wb-msg-ledger .wb-msg-meta{justify-content:center}.wb-msg-in .wb-msg-meta{justify-content:flex-start}.wb-msg-who{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.wb-msg-text{color:var(--n-ink);white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}.wb-msg-meta{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px;font-size:11px}.wb-msg-task{font-family:ui-monospace,Consolas,monospace;color:var(--n-ink-muted)}.wb-msg-status{border:1px solid currentColor;border-radius:99px;padding:0 7px;line-height:18px;white-space:nowrap}.wb-msg-status.is-wait,.wb-msg-status.is-muted{color:var(--n-ink-muted)}.wb-msg-status.is-ok{color:var(--n-live)}.wb-msg-status.is-warn{color:var(--n-act)}.wb-msg-status.is-danger{color:var(--n-danger)}.wb-chat-divider{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--n-ink-muted);margin-top:4px}.wb-chat-divider:before,.wb-chat-divider:after{content:"";flex:1;border-top:1px solid var(--n-border)}.wb-chat-sessions{display:flex;flex-direction:column;gap:6px}.wb-chat-session{display:flex;flex-direction:column;align-items:flex-start;gap:2px;width:100%;text-align:left;background:var(--n-raised);border:1px solid var(--n-border);border-radius:8px;padding:7px 10px;color:var(--n-ink);font:inherit;cursor:pointer}.wb-chat-session:hover{border-color:var(--n-accent)}.wb-chat-session:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-chat-session-id{font:12px ui-monospace,Consolas,monospace}.wb-chat-session-meta{font-size:11px;color:var(--n-ink-muted)}.wb-chat-foot{flex:none;border-top:1px solid var(--n-border);padding:8px 10px 10px;background:var(--n-panel)}.wb-chat-composer{display:flex;flex-direction:column;gap:4px}.wb-chat-composer-row{display:flex;align-items:flex-end;gap:8px}.wb-chat-composer .wb-dispatch-text{flex:1 1 auto;min-height:38px;max-height:160px;resize:none;border-radius:10px;padding:8px 10px;font-size:13px;line-height:1.5}.wb-chat-composer .wb-dispatch-submit{align-self:auto;height:38px;padding:0 16px;border-radius:10px;background:var(--n-accent);border-color:var(--n-accent);color:var(--n-bg);font-size:13px;font-weight:600}.wb-chat-composer .wb-dispatch-submit:hover:not(:disabled){border-color:var(--n-accent);filter:brightness(1.08)}.wb-chat-composer .wb-dispatch-submit:disabled{opacity:.45;cursor:default}.wb-chat-composer .wb-dispatch-note{font-size:11px;padding-left:2px}.wb-chat-composer .wb-dispatch-note.is-error{color:var(--n-danger)}.wb-chat-readonly{font-size:12px;color:var(--n-ink-muted);text-align:center;padding:4px 0}.nexus-fault{display:none;position:fixed;top:10px;right:12px;z-index:50;max-width:420px;font:12px/1.5 ui-monospace,Consolas,monospace;color:var(--n-error-ink);background:var(--n-error-panel);border:1px solid var(--n-danger);border-radius:8px;padding:6px 10px;pointer-events:none}@media (max-width:1023px){.wb-menu{display:inline-flex}.wb-rail-toggle{display:none}.wb-body{grid-template-columns:minmax(0,1fr)}.wb-rail{position:fixed;left:0;top:0;bottom:0;width:min(86vw,320px);z-index:40;background:var(--n-panel);border-right:1px solid var(--n-border);transform:translate(-102%);transition:transform .18s ease;box-shadow:8px 0 24px var(--n-shadow)}.wb.is-rail-open .wb-rail{transform:translate(0)}.wb-inspector{left:8px;right:8px;width:auto;top:auto;bottom:8px;max-height:45%}.wb-chat{left:8px;right:8px;width:auto;top:auto;bottom:8px;height:85%;max-height:85%;transform:translateY(102%)}.wb-chat.is-open{transform:translateY(0)}}@media (pointer:coarse){.wb-filter,.wb-item,.wb-tab{min-height:36px}.wb-icon-btn,.wb-inspector .wb-icon-btn,.wb-chat .wb-icon-btn{width:36px;height:36px}}@media (max-width:680px){.wb-header{flex-wrap:wrap;row-gap:4px}.wb-header-stats{order:3;width:100%}.wb-row-team{min-width:0;flex:1 1 auto}.wb-data{padding:8px}}.wb-host-row{display:flex;align-items:center;gap:7px;padding:4px 6px;border:1px solid var(--n-border);border-radius:7px;margin-bottom:4px;cursor:pointer;font-size:12px}.wb-host-row:hover{border-color:var(--n-accent);background:color-mix(in srgb,var(--n-accent) 8%,transparent)}.wb-host-row:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-host-lamp{flex:none;width:8px;height:8px;border-radius:50%;background:var(--n-ink-muted)}.wb-host-lamp.is-live{background:var(--n-live)}.wb-host-lamp.is-warm{background:var(--n-act)}.wb-host-lamp.is-cold{background:var(--n-ink-muted)}.wb-host-lamp.is-fail{background:var(--n-danger)}.wb-host-lamp.is-unknown{background:transparent;border:1px solid var(--n-ink-muted)}.wb-host-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--n-ink)}.wb-host-badge{flex:none;font-size:10.5px;border-radius:99px;padding:0 7px;line-height:17px;border:1px solid var(--n-border)}.wb-host-badge.is-linked{color:var(--n-live);border-color:var(--n-live)}.wb-host-badge.is-stranger{color:var(--n-ink-muted);border-color:var(--n-border)}.wb-host-ver{flex:none;font-size:10px;color:var(--n-ink-muted);border:1px solid var(--n-border);border-radius:4px;padding:0 4px}.wb-host-url{flex:none;max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--n-ink-muted);font-size:10.5px}.wb-tabs{flex:none;display:flex;gap:2px;padding:6px 6px 0;border-bottom:1px solid var(--n-border);overflow-x:auto;scrollbar-width:none}.wb-tab{flex:1 1 auto;display:inline-flex;align-items:center;justify-content:center;gap:5px;min-width:0;background:none;border:none;border-bottom:2px solid transparent;color:var(--n-ink-muted);font:inherit;font-size:12.5px;padding:6px 6px 7px;cursor:pointer;white-space:nowrap}.wb-tab:hover{color:var(--n-ink)}.wb-tab.is-active{color:var(--n-ink);border-bottom-color:var(--n-accent);font-weight:600}.wb-tab:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px;border-radius:6px 6px 0 0}.wb-tab[hidden]{display:none}.wb-tab-badge{font:10.5px/16px ui-monospace,Consolas,monospace;color:var(--n-ink-muted);font-weight:400}.wb-tab-badge:empty{display:none}.wb-tab-badge.is-danger,.wb-tab-badge.is-warn,.wb-tab-badge.is-accent{padding:0 6px;border-radius:99px;border:1px solid currentColor}.wb-tab-badge.is-danger{color:var(--n-danger)}.wb-tab-badge.is-warn{color:var(--n-act)}.wb-tab-badge.is-accent{color:var(--n-accent)}.wb-tabpanels{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}.wb-tabpanel{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:8px 10px 16px}.wb-tabpanel[hidden]{display:none}.wb-tabpanel:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px}.wb-tabpanel .wb-pane{display:flex;flex-direction:column;gap:8px}.wb-tabpanel .wb-dir-tools{padding:0 0 6px}.wb-dir-group{display:flex;flex-direction:column;gap:1px;margin-top:10px}.wb-dir-group:first-child{margin-top:2px}.wb-dir-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;padding:0 6px 4px;font-size:11px;letter-spacing:.04em;color:var(--n-ink-muted)}.wb-dir-head-label{font-weight:600;color:var(--n-ink)}.wb-dir-head-note{font-size:10.5px}.wb-dir-day{padding:6px 6px 2px;font-size:10.5px;color:var(--n-ink-muted)}.wb-dir-empty{padding:24px 8px;text-align:center;font-size:12px;color:var(--n-ink-muted)}.wb-directory .wb-item{gap:9px;padding:5px 6px;border-radius:8px}.wb-directory .wb-item:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px}.wb-item-text{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;line-height:1.35}.wb-item-sub{font:11px/1.35 ui-monospace,Consolas,monospace;color:var(--n-ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-item.is-untitled .wb-item-label{color:var(--n-ink-muted);font-family:ui-monospace,Consolas,monospace;font-size:12px}.wb-directory .wb-item.is-unreachable{opacity:.78}.wb-unreachable-note,.wb-node-warning{font-size:11px;line-height:1.35;color:var(--n-danger);white-space:normal;overflow-wrap:anywhere}.wb-dot-log{color:var(--n-ink-muted)}.wb-led-tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.wb-led-tile{display:flex;flex-direction:column;align-items:flex-start;gap:1px;padding:7px 8px;border-radius:8px;border:1px solid var(--n-border);background:var(--n-raised);cursor:help;min-width:0}.wb-led-tile-v{font:600 17px/1.15 ui-monospace,Consolas,monospace;color:var(--n-ink)}.wb-led-tile-k{font-size:11px;color:var(--n-ink-muted)}.wb-led-tile.is-warn{border-color:var(--n-act)}.wb-led-tile.is-warn .wb-led-tile-v{color:var(--n-act)}.wb-led-tile.is-danger{border-color:var(--n-danger)}.wb-led-tile.is-danger .wb-led-tile-v{color:var(--n-danger)}.wb-led-split{display:flex;flex-direction:column;gap:4px}.wb-led-split-bar{display:flex;height:6px;border-radius:99px;overflow:hidden;background:var(--n-border)}.wb-led-seg{display:block;height:100%}.wb-led-split-legend{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:11px;color:var(--n-ink-muted)}.wb-led-split-item{display:inline-flex;align-items:center;gap:4px}.wb-led-swatch{display:inline-block;width:8px;height:8px;border-radius:2px}.wb-led-seg.is-completed,.wb-led-swatch.is-completed{background:var(--n-live)}.wb-led-seg.is-rejected,.wb-led-swatch.is-rejected{background:var(--n-act)}.wb-led-seg.is-failed,.wb-led-swatch.is-failed{background:var(--n-danger)}.wb-led-seg.is-abandoned,.wb-led-swatch.is-abandoned{background:var(--n-ink-muted)}.wb-led-seg.is-unclassified,.wb-led-swatch.is-unclassified,.wb-led-seg.is-other,.wb-led-swatch.is-other{background:color-mix(in srgb,var(--n-ink-muted) 45%,var(--n-border))}.wb-led-group{display:flex;flex-direction:column;gap:6px;margin-top:4px}.wb-led-group-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;font-size:11.5px;font-weight:600;color:var(--n-ink)}.wb-led-group-hint{font-size:10.5px;font-weight:400;color:var(--n-ink-muted)}.wb-led-group.is-danger .wb-led-group-head{color:var(--n-danger)}.wb-led-group.is-warn .wb-led-group-head{color:var(--n-act)}.wb-led-card{position:relative;display:flex;flex-direction:column;gap:3px;padding:7px 9px;border-radius:8px;border:1px solid var(--n-border);background:var(--n-raised);min-width:0}.wb-led-card.is-danger{border-color:var(--n-danger)}.wb-led-card.is-warn{border-left:3px solid var(--n-act)}.wb-led-card.is-muted,.wb-led-card.is-quiet{background:transparent}.wb-led-card.is-link{cursor:pointer}.wb-led-card.is-link:hover{border-color:var(--n-accent)}.wb-led-card.is-link:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-led-card-head{display:flex;align-items:flex-start;gap:6px;min-width:0}.wb-led-dir{flex:none;font-size:12px;color:var(--n-ink-muted);line-height:1.45}.wb-led-dir.is-in{color:var(--n-federal)}.wb-led-brief{flex:1 1 auto;min-width:0;font-size:12.5px;line-height:1.45;color:var(--n-ink);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}.wb-led-card.is-muted .wb-led-brief,.wb-led-card.is-quiet .wb-led-brief{color:var(--n-ink-muted)}.wb-led-card.is-danger .wb-led-brief{color:var(--n-danger)}.wb-led-brief.is-missing{font-style:italic}.wb-led-meta{display:flex;flex-wrap:wrap;gap:2px 8px;font:11px/1.45 ui-monospace,Consolas,monospace;color:var(--n-ink-muted);min-width:0}.wb-led-meta>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.wb-led-chip{border:1px solid var(--n-border);border-radius:99px;padding:0 6px;font-family:inherit}.wb-led-abandon{position:absolute;top:6px;right:6px;margin-left:0;padding:1px 9px;font-size:11.5px}.wb-led-abandon.is-quiet{border-color:transparent;background:transparent;color:var(--n-ink-muted)}.wb-led-abandon.is-quiet:hover{border-color:var(--n-border);color:var(--n-ink)}.wb-led-card:has(.wb-led-abandon) .wb-led-card-head{padding-right:52px}.wb-led-empty{padding:24px 8px;text-align:center;font-size:12px;color:var(--n-ink-muted)}.wb-led-forensics{font-size:11px;color:var(--n-ink-muted);border-top:1px dashed var(--n-border);padding-top:6px;margin-top:4px}.wb-led-forensics summary{cursor:pointer}.wb-led-forensics summary:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-led-forensics-body{display:flex;flex-direction:column;gap:2px;padding:4px 0 0 12px;font-family:ui-monospace,Consolas,monospace}
|