Files
yuanshuai c91a64fddb
CI / Python 3.12 (push) Waiting to run
CI / Python 3.9 (push) Waiting to run
chore(repo): initialize team collaboration repository
2026-07-27 20:40:12 +08:00

273 lines
24 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="stylesheet" href="_ds/industry-22c874fd-9888-4e99-8234-d60d39b3d719/styles.css">
<script src="_ds/industry-22c874fd-9888-4e99-8234-d60d39b3d719/_ds_bundle.js"></script>
<style>
body{margin:0;background:#f2f2f3;min-width:1280px}
*{scrollbar-width:thin;scrollbar-color:var(--color-accent-300) transparent}
a{color:#5980a6}a:hover{color:#416180}
@keyframes omIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes omScan{to{background-position:28px 0}}
@keyframes omDraw{to{stroke-dashoffset:0}}
@keyframes omGrow{from{width:0}}
@keyframes omPulse{0%,100%{opacity:1}50%{opacity:.3}}
</style>
</helmet>
<dc-import name="ConsoleNav" active="evidence" hint-size="100%,92px"></dc-import>
<div data-screen-label="证据浏览器" style="max-width:1400px;margin:0 auto;padding:22px 24px 64px;font-family:var(--font-body);animation:omIn .5s cubic-bezier(.2,.7,.2,1) both">
<div style="display:flex;align-items:flex-end;gap:16px;margin-bottom:16px">
<div>
<div style="font-size:10px;letter-spacing:.18em;color:var(--color-accent-700);font-weight:500">04 · EVIDENCE</div>
<h2 style="margin:2px 0 0;font-size:30px">证据浏览器</h2>
<div class="text-muted" style="font-size:12.5px;margin-top:2px">失败时间线 · Evidence Bundle · 每个结论回链到原始证据</div>
</div>
<span style="margin-left:auto"></span>
<button class="btn btn-secondary" style="font-size:12.5px">创建复现任务</button>
<button class="btn btn-secondary" style="font-size:12.5px">生成缺陷草稿</button>
<button class="btn btn-primary" style="gap:7px">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline stroke-linejoin="round" stroke-linecap="round" points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
下载证据包 · 247 MB
</button>
</div>
<div style="display:grid;grid-template-columns:290px 1fr 330px;gap:18px;align-items:start">
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:12px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:8px">
<h6 style="margin:0;color:var(--color-neutral-700)">失败记录 · 近 7 天</h6>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-500)">3 / 214</span>
</div>
<input class="input" placeholder="搜索 run / 指纹 / 状态码…" style="font-size:12.5px;min-height:32px">
<div style="display:flex;flex-direction:column;gap:8px">
<sc-for list="{{ failures }}" as="f" hint-placeholder-count="3">
<div onClick="{{ f.pick }}" style="{{ f.itemStyle }}">
<div style="display:flex;align-items:center;gap:7px">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:11.5px;font-weight:700">{{ f.run }}</span>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:9.5px;color:var(--color-neutral-500)">{{ f.when }}</span>
</div>
<div style="margin:5px 0 4px"><span style="{{ f.tagStyle }}">{{ f.code }}</span></div>
<div style="font-size:12px;line-height:1.45;color:var(--color-neutral-800)">{{ f.summary }}</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-500);margin-top:4px">{{ f.meta }}</div>
</div>
</sc-for>
</div>
<div style="display:flex;gap:10px;font-size:10px;color:var(--color-neutral-600);border-top:1px solid var(--color-divider);padding-top:8px;flex-wrap:wrap">
<span><span style="display:inline-block;width:9px;height:8px;background:var(--color-accent-800);vertical-align:-1px"></span> DUT_*</span>
<span><span style="display:inline-block;width:9px;height:8px;background:repeating-linear-gradient(135deg,rgba(29,31,32,.35) 0 2px,transparent 2px 5px);border:1px solid var(--color-divider);vertical-align:-1px"></span> INFRA_*</span>
<span><span style="display:inline-block;width:9px;height:8px;border:1px dashed var(--color-neutral-500);vertical-align:-1px"></span> UNKNOWN</span>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:14px 16px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:10px">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:15px;font-weight:700">{{ selRun }}</span>
<span style="{{ selTagStyle }}">{{ selCode }}</span>
<span class="tag tag-outline" style="font-size:10px">指纹 {{ selFs }}</span>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10.5px;color:var(--color-neutral-600)">{{ selMeta }}</span>
</div>
<div style="font-size:13.5px;color:var(--color-neutral-800)">{{ selHeadline }}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-700)">
<span style="border:1px solid var(--color-divider);padding:2px 8px">HOST-L02 · Ubuntu 22.04 · 6.5.0-41</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">BIOS F26b</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">FW_3.3.0-rc2 · slot 2</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">nvme-cli 2.9.1 · fio 3.36</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">workflow v3 · sig 8e02…c4</span>
</div>
</div>
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:14px 16px;gap:2px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<h6 style="margin:0 0 10px;color:var(--color-neutral-700)">事件时间线 · TIMELINE</h6>
<div style="display:flex;flex-direction:column">
<sc-for list="{{ events }}" as="ev" hint-placeholder-count="8">
<div style="display:flex;gap:12px;position:relative;padding-bottom:14px">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:10.5px;color:var(--color-neutral-500);width:58px;text-align:right;padding-top:2px;flex:none">{{ ev.t }}</span>
<div style="display:flex;flex-direction:column;align-items:center;flex:none;width:14px">
<span style="{{ ev.dotStyle }}"></span>
<span style="{{ ev.lineStyle }}"></span>
</div>
<div style="flex:1;margin-top:-2px">
<div style="display:flex;align-items:center;gap:8px">
<span style="{{ ev.laneStyle }}">{{ ev.lane }}</span>
<span style="{{ ev.titleStyle }}">{{ ev.title }}</span>
</div>
<div style="font-size:12px;color:var(--color-neutral-700);margin-top:2px;line-height:1.5">{{ ev.detail }}</div>
<sc-if value="{{ ev.links }}" hint-placeholder-val="{{ false }}">
<div style="display:flex;gap:6px;margin-top:5px;flex-wrap:wrap">
<sc-for list="{{ ev.linkList }}" as="lk" hint-placeholder-count="2">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:10px;border:1px solid var(--color-accent-400);color:var(--color-accent-700);padding:2px 8px;cursor:pointer">{{ lk }}</span>
</sc-for>
</div>
</sc-if>
</div>
</div>
</sc-for>
</div>
</div>
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:14px 16px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:8px">
<h6 style="margin:0;color:var(--color-neutral-700)">AI 摘要 · 仅引用证据,不做最终判断</h6>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-600)">置信度 0.82 · 本地模型</span>
</div>
<div style="font-size:13px;line-height:1.7;color:var(--color-neutral-800)">S4 唤醒后 DUT 未完成链路训练即被访问:kernel 在唤醒后 3s 内未出现 nvme 重枚举日志 <span style="font-family:ui-monospace,Menlo,monospace;font-size:10.5px;border:1px solid var(--color-accent-400);color:var(--color-accent-700);padding:1px 6px">kernel.log:L1042-1067</span>,而基线固件 A 平均 1.8s 完成 <span style="font-family:ui-monospace,Menlo,monospace;font-size:10.5px;border:1px solid var(--color-accent-400);color:var(--color-accent-700);padding:1px 6px">metrics.json#resume_enum</span>。与 FS-0091 簇内 11 次历史失败特征一致(均为 S4 唤醒 + rc 系列固件)。</div>
<div style="font-size:12px;color:var(--color-neutral-700);border:1px dashed var(--color-divider);padding:8px 10px">尚不能排除:主板 BIOS F26b 的 S4 电源时序差异(建议单变量验证:同盘同固件换 HOST-L01 复跑 20 循环)。</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:12px;gap:6px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:8px">
<h6 style="margin:0;color:var(--color-neutral-700)">EVIDENCE BUNDLE</h6>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-500)">run-20260726-0007/</span>
</div>
<div style="display:flex;flex-direction:column;font-family:ui-monospace,Menlo,monospace;font-size:11px">
<sc-for list="{{ tree }}" as="n" hint-placeholder-count="12">
<div onClick="{{ n.toggle }}" style="{{ n.rowStyle }}">
<span style="{{ n.chevStyle }}">{{ n.chev }}</span>
<span>{{ n.name }}</span>
<sc-if value="{{ n.flag }}" hint-placeholder-val="{{ false }}">
<span style="font-size:8.5px;background:var(--color-accent-800);color:#f2f2f3;padding:1px 5px;letter-spacing:.06em">关键</span>
</sc-if>
<span style="margin-left:auto;color:var(--color-neutral-500);font-size:10px">{{ n.size }}</span>
</div>
</sc-for>
</div>
<div style="font-size:10.5px;color:var(--color-neutral-600);border-top:1px solid var(--color-divider);padding-top:8px;line-height:1.6">manifest 校验通过 · 42 文件 · SHA256 全部一致<br>生成组件 evidence-bus 0.9.4 · 不可覆盖</div>
</div>
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:12px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<h6 style="margin:0;color:var(--color-neutral-700)">恢复过程 · RECOVERY.JSON</h6>
<div style="display:flex;flex-direction:column;font-family:ui-monospace,Menlo,monospace;font-size:10.5px">
<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid rgba(29,31,32,.08)"><span style="color:var(--color-neutral-600)">L1 Agent 软恢复</span><span style="color:#8a2e22">FAIL · 60s</span></div>
<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid rgba(29,31,32,.08)"><span style="color:var(--color-neutral-600)">L2 OS 通道重启</span><span style="color:#8a2e22">FAIL · 120s</span></div>
<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid rgba(29,31,32,.08)"><span style="color:var(--color-neutral-600)">L3 带外 Reset</span><span style="color:var(--color-accent-700)">OK · 2m17s</span></div>
<div style="display:flex;justify-content:space-between;padding:4px 0"><span style="color:var(--color-neutral-600)">总耗时 / 介入</span><span>4m12s · 无人</span></div>
</div>
<a href="LiveRun.dc.html" style="font-size:11.5px">回放当时的实时视图 →</a>
</div>
</div>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props="{&quot;$preview&quot;:{&quot;width&quot;:1440,&quot;height&quot;:980}}">
class Component extends DCLogic {
state = { sel: 0, open: { environment: true, device: false, host: true, test: false, oob: true, recovery: false, summary: true } };
renderVals() {
const mono = { fontFamily: 'ui-monospace,Menlo,monospace' };
const tagOf = code => {
if (code.startsWith('DUT')) return { ...mono, fontSize: '10px', padding: '2px 8px', background: 'var(--color-accent-800)', color: '#f2f2f3', letterSpacing: '.03em' };
if (code.startsWith('INFRA')) return { ...mono, fontSize: '10px', padding: '2px 8px', background: 'repeating-linear-gradient(135deg,rgba(29,31,32,.12) 0 2px,transparent 2px 5px)', border: '1px solid var(--color-divider)', color: 'var(--color-neutral-800)', letterSpacing: '.03em' };
if (code === 'UNKNOWN') return { ...mono, fontSize: '10px', padding: '2px 8px', border: '1px dashed var(--color-neutral-500)', color: 'var(--color-neutral-700)', letterSpacing: '.03em' };
return { ...mono, fontSize: '10px', padding: '2px 8px', background: 'var(--color-neutral-200)', color: 'var(--color-neutral-800)', letterSpacing: '.03em' };
};
const F = [
{ run: 'run-20260726-0007', when: '昨日 02:29', code: 'DUT_ENUMERATION_FAILURE', fs: 'FS-0091', summary: 'S4 唤醒后 DUT 未枚举,链路未训练', meta: 'WS-03 · cycle 41/100 · FW_3.3.0-rc2', headline: '休眠唤醒后 nvme0n1 未出现;L1/L2 恢复失败,L3 带外 Reset 后设备恢复,任务在检查点冻结。' },
{ run: 'run-20260726-0011', when: '昨日 11:47', code: 'UNKNOWN', fs: 'FS-0102', summary: '校验超时且证据不足,等待工程师分类', meta: 'WS-02 · cycle 12/50 · FW_3.2.1', headline: 'verify_hash 超时,但主机、DUT 与网络信号均正常 — 证据不足,已标记待人工分类。' },
{ run: 'run-20260725-0018', when: '07-25 23:12', code: 'INFRA_HOST_OS_FAILURE', fs: 'FS-0088', summary: '蓝屏 DRIVER_POWER_STATE_FAILURE', meta: 'WS-01 · cycle 88/100 · FW_3.3.0-rc2', headline: 'Windows 蓝屏(电源状态驱动),判定为基础设施故障,不计入 DUT 结论;L4 AC 循环后恢复。' }
];
const failures = F.map((f, i) => ({
...f,
pick: () => this.setState({ sel: i }),
tagStyle: tagOf(f.code),
itemStyle: {
border: i === this.state.sel ? '1px solid var(--color-accent-600)' : '1px solid var(--color-divider)',
background: i === this.state.sel ? 'var(--color-accent-100)' : 'transparent',
padding: '10px 11px', cursor: 'pointer', transition: 'border-color .15s ease, background .15s ease'
}
}));
const sel = F[this.state.sel];
const EV = {
0: [
{ t: '02:28:19', lane: 'TEST', kind: 'n', title: 'cycle 41 · host.hibernate → S4', detail: '前序 40 循环全部 PASS · 检查点 cycle 40 已写入', links: null },
{ t: '02:29:44', lane: 'HOST', kind: 'n', title: '唤醒完成 · resume 11.8s', detail: '唤醒源 RTC · Agent 心跳恢复正常', links: null },
{ t: '02:29:47', lane: 'DUT', kind: 'f', title: 'dut.verify_enumeration 失败', detail: 'nvme0n1 未出现 · PCIe 链路未完成训练 · 重试 3 次无效', links: ['host/kernel.log', 'host/device-enumeration.json'] },
{ t: '02:29:48', lane: 'EVID', kind: 'e', title: '失败现场取证(恢复之前)', detail: 'HDMI 帧 ×3 · dmesg 尾部 · SMART 不可读已记录 · lspci 快照', links: ['oob/screenshots/', 'device/error-log.json'] },
{ t: '02:30:21', lane: 'RCVR', kind: 'f', title: 'L1 Agent 软恢复 → 失败', detail: 'PCIe rescan 无效 · 设备节点仍缺失 (60s)', links: null },
{ t: '02:31:07', lane: 'HB', kind: 'f', title: '主机心跳丢失', detail: 'Agent 停止上报 · ping 不可达 · 判定 HOST_UNREACHABLE', links: ['oob/heartbeat.ndjson'] },
{ t: '02:31:40', lane: 'RCVR', kind: 'f', title: 'L2 OS 通道重启 → 失败', detail: 'SSH 超时 120s · 按策略 esc-default-v2 升级', links: null },
{ t: '02:33:02', lane: 'OOB', kind: 'r', title: 'L3 带外 Reset 已执行', detail: '光耦 CH2 · 脉冲 250ms · 供电动作已留痕', links: ['oob/power-actions.ndjson'] },
{ t: '02:35:19', lane: 'HOST', kind: 'r', title: '主机上线 · 指纹一致', detail: 'Agent 自启 · BIOS/OS/驱动与任务登记一致', links: null },
{ t: '02:35:24', lane: 'DUT', kind: 'r', title: 'DUT 重新枚举成功', detail: 'Gen4 x4 · 1.8s · SMART 正常 · 数据区未写入', links: ['device/smart-health.json'] },
{ t: '02:35:31', lane: 'SYS', kind: 'c', title: '检查点对账 · 任务冻结', detail: '物理状态 = cycle 41 起点 · 等待复跑决策(不自动重试破坏性步骤)', links: null },
{ t: '02:36:02', lane: 'SYS', kind: 'n', title: '失败指纹 FS-0091 已生成', detail: '与 11 个历史运行聚类 · 均为 S4 唤醒 + rc 系列固件', links: ['summary/failure-signature.json'] }
],
1: [
{ t: '11:47:12', lane: 'TEST', kind: 'f', title: 'data.verify_hash 超时', detail: '300s 未返回 · fio 进程存活 · IO 无错误计数', links: ['test/stderr.log'] },
{ t: '11:47:13', lane: 'EVID', kind: 'e', title: '现场取证', detail: '进程树 · 资源占用 · SMART 正常 · 网络正常', links: ['host/system.log'] },
{ t: '11:48:40', lane: 'RCVR', kind: 'r', title: 'L1 终止校验进程并重跑 → 通过', detail: '重跑一次即 PASS · 无法稳定复现', links: null },
{ t: '11:50:02', lane: 'SYS', kind: 'c', title: '标记 UNKNOWN · 等待分类', detail: '证据不足 · 建议补充 blktrace 后纳入观察名单', links: ['summary/ai-summary.md'] }
],
2: [
{ t: '23:12:44', lane: 'HOST', kind: 'f', title: 'Windows 蓝屏', detail: 'DRIVER_POWER_STATE_FAILURE (0x9F) · 冷启动循环第 88 轮', links: ['oob/screenshots/'] },
{ t: '23:12:45', lane: 'EVID', kind: 'e', title: 'HDMI 蓝屏帧留证', detail: 'OS 不可达状态下由带外控制器抓取', links: ['oob/screenshots/'] },
{ t: '23:14:10', lane: 'OOB', kind: 'r', title: 'L4 AC 电源循环', detail: 'L3 Reset 无效 → 整机断电 · 安全间隔 30s', links: ['oob/power-actions.ndjson'] },
{ t: '23:18:02', lane: 'SYS', kind: 'c', title: '判定 INFRA_HOST_OS_FAILURE', detail: '不计入 DUT 结论 · 计入基础设施误报率监控', links: ['summary/failure-signature.json'] }
]
}[this.state.sel];
const laneColors = { HB: '#8a2e22', OOB: 'var(--color-accent-800)', RCVR: '#8a2e22', TEST: 'var(--color-accent-700)', SYS: 'var(--color-neutral-600)', EVID: 'var(--color-neutral-600)', DUT: 'var(--color-accent-800)', HOST: 'var(--color-neutral-700)' };
const events = EV.map((e, i) => {
let dot = { width: '9px', height: '9px', borderRadius: '50%', border: '1.5px solid var(--color-neutral-400)', background: 'var(--color-bg)', flex: 'none', marginTop: '4px' };
if (e.kind === 'f') dot = { ...dot, border: '1.5px solid #b03d2e', background: '#f7e9e6' };
if (e.kind === 'r') dot = { ...dot, border: 'none', background: 'var(--color-accent)' };
if (e.kind === 'c') dot = { ...dot, borderRadius: 0, border: 'none', background: 'var(--color-accent-800)' };
if (e.kind === 'e') dot = { ...dot, border: '1.5px dashed var(--color-neutral-500)' };
return {
...e,
linkList: e.links || [],
dotStyle: dot,
lineStyle: { width: '1px', flex: 1, background: i === EV.length - 1 ? 'transparent' : 'var(--color-divider)', marginTop: '3px' },
laneStyle: { ...mono, fontSize: '9.5px', color: laneColors[e.lane] || 'var(--color-neutral-600)', letterSpacing: '.06em', minWidth: '34px' },
titleStyle: { fontSize: '13px', fontWeight: e.kind === 'f' || e.kind === 'r' ? 600 : 500, color: e.kind === 'f' ? '#8a2e22' : 'var(--color-text)' }
};
});
const DIRS = [
{ id: 'root', name: 'manifest.json', size: '4 KB', file: true, depth: 0 },
{ id: 'environment', name: 'environment/', size: '4 项', kids: [['host.json', '2 KB'], ['dut.json', '2 KB'], ['firmware.json', '1 KB'], ['tool_versions.json', '3 KB']] },
{ id: 'timeline', name: 'timeline/events.ndjson', size: '1.2 MB', file: true, depth: 0 },
{ id: 'device', name: 'device/', size: '4 项', kids: [['identify.json', '18 KB'], ['smart-health.json', '9 KB'], ['error-log.json', '22 KB'], ['telemetry.bin', '64 MB']] },
{ id: 'host', name: 'host/', size: '3 项', kids: [['system.log', '8.4 MB'], ['kernel.log', '2.1 MB', true], ['device-enumeration.json', '11 KB', true]] },
{ id: 'test', name: 'test/', size: '4 项', kids: [['stdout.log', '96 MB'], ['stderr.log', '2 KB'], ['metrics.json', '340 KB'], ['hashes.json', '88 KB']] },
{ id: 'oob', name: 'oob/', size: '4 项', kids: [['heartbeat.ndjson', '420 KB', true], ['power-actions.ndjson', '2 KB', true], ['temperature.csv', '180 KB'], ['screenshots/ ×9', '38 MB', true]] },
{ id: 'recovery', name: 'recovery/recovery.json', size: '6 KB', file: true, depth: 0, flag: true },
{ id: 'summary', name: 'summary/', size: '3 项', kids: [['failure-signature.json', '3 KB', true], ['ai-summary.md', '5 KB'], ['issue-draft.md', '4 KB']] }
];
const tree = [];
for (const d of DIRS) {
const isOpen = !!this.state.open[d.id];
tree.push({
name: d.name, size: d.size, flag: !!d.flag,
chev: d.file ? '·' : (isOpen ? '▾' : '▸'),
chevStyle: { width: '12px', color: d.file ? 'var(--color-neutral-400)' : 'var(--color-accent-700)', flex: 'none' },
toggle: d.file ? null : () => this.setState(s => ({ open: { ...s.open, [d.id]: !s.open[d.id] } })),
rowStyle: { display: 'flex', alignItems: 'center', gap: '6px', padding: '4px 4px', cursor: d.file ? 'default' : 'pointer', borderBottom: '1px solid rgba(29,31,32,.06)' }
});
if (!d.file && isOpen) for (const [nm, sz, flag] of d.kids) tree.push({
name: nm, size: sz, flag: !!flag, chev: '', toggle: null,
chevStyle: { width: '12px', flex: 'none' },
rowStyle: { display: 'flex', alignItems: 'center', gap: '6px', padding: '3px 4px 3px 22px', color: 'var(--color-neutral-700)', borderBottom: '1px solid rgba(29,31,32,.04)' }
});
}
return {
failures, events, tree,
selRun: sel.run, selCode: sel.code, selFs: sel.fs, selMeta: sel.meta, selHeadline: sel.headline,
selTagStyle: tagOf(sel.code)
};
}
}
</script>
</body>
</html>