dsh-showcase 0.1.2 → 0.1.4

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.
@@ -0,0 +1,161 @@
1
+ import { findComparisonPair, formatReportDuration, reportMarkdown, sanitizeReportExport, serializeInlineJson } from "./report-content.js";
2
+ import { dijiangConsoleCss } from "./dijiang-style.js";
3
+ import { renderDijiangLoader, dijiangMotionScript } from "./dijiang-motion.js";
4
+
5
+ const escape = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[character]);
6
+
7
+ const lucideLicense = "ISC License\n\nCopyright (c) 2026 Lucide Icons and Contributors\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n---\n\nThe following Lucide icons are derived from the Feather project:\n\nairplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out\n\nThe MIT License (MIT) (for the icons listed above)\n\nCopyright (c) 2013-present Cole Bemis\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.";
8
+
9
+ // Static Lucide icons keep exported reports self-contained without a React runtime.
10
+ const icons = {
11
+ "arrow": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-arrow-up-right\" aria-hidden=\"true\"><path d=\"M7 7h10v10\"></path><path d=\"M7 17 17 7\"></path></svg>",
12
+ "download": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-download\" aria-hidden=\"true\"><path d=\"M12 15V3\"></path><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"></path><path d=\"m7 10 5 5 5-5\"></path></svg>",
13
+ "copy": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-copy\" aria-hidden=\"true\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"></rect><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"></path></svg>"
14
+ };
15
+ const isBase64 = (value) => typeof value === "string" && value.length > 0 && value.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(value);
16
+
17
+ export function renderDijiangDocument(data, text, status, parts) {
18
+ const en = data.locale === "en";
19
+ const copy = en ? {
20
+ station: "DELIVERY RELAY", archive: "Evidence archive", changes: "Change register", checks: "Verification receipts", capture: "Visual evidence", output: "Local artifacts", overview: "Delivery report", sample: "DEMO DATA", snapshot: "REPORT SNAPSHOT", warnings: "Review queue", noWarnings: "No review warnings recorded", source: "Report", download: "Download HTML", privacy: "Privacy review", verified: "Retained verification output", count: "Recorded", emptyOutput: "No command output recorded.", full: "Download original capture", copyPath: "Copy path", copied: "Copied", copyFailed: "Copy unavailable; select the path below.", remaining: "More records are available in the source report.", local: "LOCAL ONLY", scope: "Verification scope", jump: "Delivery stages", skip: "Skip to evidence", untimed: "Time not recorded",
21
+ } : {
22
+ station: "交付中继", archive: "证据档案", changes: "变更档案", checks: "验证回执", capture: "界面证据", output: "本地产物", overview: "交付报告", sample: "演示数据", snapshot: "报告快照", warnings: "复核队列", noWarnings: "未记录额外复核事项", source: "采集报告", download: "下载 HTML", privacy: "隐私审查", verified: "已保留的验证输出", count: "记录", emptyOutput: "未记录命令输出。", full: "下载原始截图", copyPath: "复制路径", copied: "已复制", copyFailed: "复制不可用,请选择下方路径文本。", remaining: "其余记录见采集报告。", local: "仅本地生成", scope: "凭据覆盖", jump: "交付阶段", skip: "跳到证据", untimed: "未记录时间",
23
+ };
24
+ const files = Array.isArray(data.gitFiles) ? data.gitFiles : [];
25
+ const tests = Array.isArray(data.testReceipts) ? data.testReceipts : [];
26
+ const screenshots = Array.isArray(data.posterScreenshots) ? data.posterScreenshots.filter((shot) => isBase64(shot?.image) && /^image\/(png|jpeg|webp)$/.test(shot.mimeType)) : [];
27
+ const warnings = Array.isArray(data.freshnessWarnings) ? data.freshnessWarnings : [];
28
+ const gitState = data.gitState ?? (files.length ? "changed" : "clean");
29
+ const testState = data.testState ?? (tests.length ? "configured" : "unconfigured");
30
+ const detailCopy = en ? {
31
+ summary: "Delivery summary", duration: "Duration", delta: "Line changes", views: "Captures", diff: "Line diff",
32
+ missing: "No line diff retained in this report.", binary: "Binary file; no text diff.", untracked: "Untracked file; not included in the Git patch.",
33
+ omitted: "Patch omitted by capture limits.", truncated: "Only part of the output is retained.",
34
+ before: "Before", after: "After", compare: "Before / after", position: "Comparison position",
35
+ noPair: "No before and after captures of the same page and viewport.", privacyEmpty: "No redaction breakdown recorded.",
36
+ json: "Download JSON", markdown: "Download Markdown",
37
+ } : {
38
+ summary: "交付摘要", duration: "任务耗时", delta: "增删总量", views: "截图记录", diff: "逐行差异",
39
+ missing: "报告未保留逐行 Diff。", binary: "二进制文件,不展示文本 Diff。", untracked: "未跟踪文件,尚未纳入 Git 差异。",
40
+ omitted: "受采集大小或数量限制,此文件未保留 Diff。", truncated: "仅保留部分输出,内容已截断。",
41
+ before: "改版前", after: "改版后", compare: "前后对比", position: "对比位置",
42
+ noPair: "尚无相同页面、相同视口的改版前后截图。", privacyEmpty: "未记录脱敏分类明细。",
43
+ json: "下载 JSON", markdown: "下载 Markdown",
44
+ };
45
+ const redactionDetails = Array.isArray(data.redactionDetails) ? data.redactionDetails : [];
46
+ const exportReport = sanitizeReportExport(data.reportData ?? {
47
+ version: 1, generatedAt: data.generatedAt, demo: Boolean(data.demo), project: { name: data.projectName },
48
+ task: { goal: data.task, status: status.key, durationMs: data.durationMs },
49
+ git: { files, summary: { changedFiles: data.fileCount, additions: data.additions, deletions: data.deletions } },
50
+ tests, screenshots: screenshots.map(({ image, ...shot }) => shot),
51
+ redaction: { totalReplacements: data.redactionCount, replacements: Object.fromEntries(redactionDetails.map(item => [item.name, item.count])) },
52
+ warnings,
53
+ });
54
+ const exportPayload = serializeInlineJson({ report: exportReport, markdown: data.summaryMarkdown ?? reportMarkdown(exportReport, data.locale) });
55
+ const totals = { additions: data.additions ?? files.reduce((n, f) => n + (Number(f.additions) || 0), 0), deletions: data.deletions ?? files.reduce((n, f) => n + (Number(f.deletions) || 0), 0) };
56
+ const overview = '<section class="ed-section ed-overview" id="ed-overview"><header class="ed-section-head"><h2>' + detailCopy.summary + '</h2></header><dl><div><dt>' + detailCopy.duration + '</dt><dd>' + formatReportDuration(data.durationMs, data.locale) + '</dd></div><div><dt>' + detailCopy.delta + '</dt><dd class="ed-delta"><b>+' + totals.additions + '</b><i>−' + totals.deletions + '</i></dd></div><div><dt>' + detailCopy.views + '</dt><dd>' + screenshots.length + '</dd></div></dl></section>';
57
+ const diffPanels = files.map((file, index) => {
58
+ const reason = file.diffUnavailable === "binary" ? detailCopy.binary : file.diffUnavailable === "untracked" ? detailCopy.untracked : ["oversize", "limit"].includes(file.diffUnavailable) ? detailCopy.omitted : detailCopy.missing;
59
+ const lines = file.diff === undefined ? '<p class="ed-diff-missing">' + reason + '</p>' : '<pre class="ed-diff-code">' + String(file.diff).split(/\r?\n/).map(line => '<code class="ed-diff-line ' + (line.startsWith("@@") ? "hunk" : line.startsWith("+") && !line.startsWith("+++") ? "addition" : line.startsWith("-") && !line.startsWith("---") ? "deletion" : "") + '">' + escape(line) + '\n</code>').join("") + '</pre>';
60
+ return '<section class="ed-diff-panel" id="ed-diff-' + index + '"' + (index ? ' hidden' : '') + ' aria-label="' + escape(file.path) + ' Diff"><header><h3>' + detailCopy.diff + '</h3><code>' + escape(file.previousPath ? file.previousPath + " → " + file.path : file.path) + '</code></header>' + lines + (file.diffTruncated ? '<p class="ed-more">' + detailCopy.truncated + '</p>' : '') + '</section>';
61
+ }).join("");
62
+ const privacy = '<section class="ed-section ed-privacy" id="ed-privacy"><header class="ed-section-head"><h2>' + copy.privacy + '</h2><span>' + (Number(data.redactionCount) || 0) + ' ' + text.redactions + '</span></header>' + (redactionDetails.length ? '<dl>' + redactionDetails.map(item => '<div><dt>' + escape(item.name) + '</dt><dd>' + (Number(item.count) || 0) + '</dd></div>').join("") + '</dl>' : '<p class="ed-more">' + detailCopy.privacyEmpty + '</p>') + '</section>';
63
+ const pair = findComparisonPair(screenshots);
64
+ const comparison = '<div class="ed-comparison"><h3>' + detailCopy.compare + '</h3>' + (pair ? '<div class="ed-comparison-stage" data-ed-comparison data-before="ed-shot-' + screenshots.indexOf(pair.before) + '" data-after="ed-shot-' + screenshots.indexOf(pair.after) + '"><span class="ed-compare-label">' + detailCopy.before + '</span><span class="ed-compare-label ed-compare-label--after">' + detailCopy.after + '</span><div class="ed-compare-before" data-ed-derived></div><div class="ed-compare-after" data-ed-derived></div><i class="ed-compare-handle" aria-hidden="true"></i></div><label class="ed-compare-control">' + detailCopy.position + '<input type="range" min="0" max="100" value="50" step="1" data-ed-compare-range aria-label="' + detailCopy.position + '"><output>50%</output></label>' : '<p class="ed-more">' + detailCopy.noPair + '</p>') + '</div>';
65
+ const outputs = [[text.manifestReport, data.reportPath], [text.manifestSummary, data.summaryPath], [text.manifestPoster, data.posterPath]].filter(([, path]) => path);
66
+ const targets = ["ed-task", "ed-changes", "ed-tests", "ed-captures", "ed-outputs"];
67
+ const empty = (kind, title, detail) => `<div class="ed-empty" data-empty-state="${kind}"><strong>${escape(title)}</strong><p>${escape(detail)}</p></div>`;
68
+ const fileRows = gitState === "unavailable"
69
+ ? empty("git-unavailable", text.gitUnavailableTitle, text.gitUnavailableDetail)
70
+ : files.length ? `<ol class="ed-files">${files.map((file, index) => `<li><button type="button" data-ed-file="${index}" aria-pressed="${index === 0}" aria-controls="ed-diff-${index}"><span class="ed-index">${String(index + 1).padStart(2, "0")}</span><code>${escape(file.path)}</code><span class="ed-delta"><b>+${Number(file.additions) || 0}</b><i>−${Number(file.deletions) || 0}</i></span></button></li>`).join("")}</ol>`
71
+ : gitState === "clean" ? empty("git-clean", text.gitCleanTitle, text.gitCleanDetail) : empty("git-details-missing", text.gitChangedMissingTitle, text.gitChangedMissingDetail);
72
+ const testRows = tests.length ? `<ul class="ed-receipts">${tests.map((test, index) => {
73
+ const key = test.exitCode !== 0 ? "failed" : ["passed", "failed", "skipped"].includes(test.status) ? test.status : "passed";
74
+ const presentation = text.status[key];
75
+ return `<li data-status="${key}"><details ${index === 0 ? "open" : ""}><summary><span class="ed-receipt-mark" aria-label="${escape(presentation.receipt)}">${presentation.symbol}</span><code>${escape(test.command)}</code><span class="ed-receipt-result">${escape(presentation.receipt)}<small>${escape(test.duration || "")} · ${escape(text.exit)} ${Number(test.exitCode) || 0}</small></span></summary><pre>${escape(test.output || copy.emptyOutput)}</pre>${test.outputTruncated ? `<p class="ed-more">${detailCopy.truncated}</p>` : ""}</details></li>`;
76
+ }).join("")}</ul>` : empty(testState === "unconfigured" ? "tests-unconfigured" : "test-receipts-missing", testState === "unconfigured" ? text.testsUnconfiguredTitle : text.testsMissingTitle, testState === "unconfigured" ? text.testsUnconfiguredDetail : text.testsMissingDetail);
77
+ const excluded = (data.isolatedEvidenceViewports?.length ?? 0) + (data.unclassifiedEvidenceViewports?.length ?? 0) + (data.invalidEvidenceViewports?.length ?? 0);
78
+ const captures = screenshots.length ? `<div class="ed-captures">${screenshots.map((shot, index) => `<details ${shot.kind !== "before" || index === 0 ? "open" : ""}><summary><span>${String(index + 1).padStart(2, "0")}</span>${escape(shot.label)}</summary><figure><div class="ed-shot"><img id="ed-shot-${index}" src="data:${shot.mimeType};base64,${shot.image}" alt="${escape(shot.label)}" loading="${index === 0 ? "eager" : "lazy"}"></div><figcaption>${escape(shot.label)}<button type="button" data-ed-capture="ed-shot-${index}" title="${escape(copy.full)}">${icons.download}${escape(copy.full)}</button></figcaption></figure></details>`).join("")}</div>${excluded ? parts.integrity : ""}` : parts.integrity;
79
+ const captureSection = `<section class="ed-section ed-visual evidence-page" id="ed-captures" aria-label="${escape(copy.capture)}"><header class="ed-section-head"><h2>${escape(copy.capture)}</h2><span>${String(screenshots.length).padStart(2, "0")} / ${String(screenshots.length + excluded).padStart(2, "0")} ${escape(data.visualProject === false && !screenshots.length ? text.captureOptionalMeta : text.captureMatched)}</span></header>${captures}${screenshots.length ? comparison : ""}</section>`;
80
+ const statusAria = `${en ? "Status: " : "状态:"}${status.badge}`;
81
+ const settingsIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sliders-horizontal" aria-hidden="true"><path d="M21 4h-7M10 4H3M21 12h-9M8 12H3M21 20h-5M12 20H3M14 2v4M8 10v4M16 18v4"/></svg>';
82
+ const preferences = `<details class="ed-settings"><summary aria-label="${en ? "Display settings" : "显示设置"}" title="${en ? "Display settings" : "显示设置"}">${settingsIcon}</summary><div class="ed-preferences"><h2>${en ? "Display settings" : "显示设置"}</h2>
83
+ <fieldset><legend>${en ? "Appearance" : "明暗"}</legend><div class="ed-options"><label><input type="radio" name="ed-palette" value="light" data-ed-pref="palette" checked><span>${en ? "Light" : "亮色"}</span></label><label><input type="radio" name="ed-palette" value="dark" data-ed-pref="palette"><span>${en ? "Dark" : "暗色"}</span></label></div></fieldset>
84
+ <fieldset><legend>${en ? "Signal color" : "信号色"}</legend><div class="ed-options"><label><input type="radio" name="ed-accent" value="yellow" data-ed-pref="accent" checked><span><i class="ed-swatch ed-swatch--yellow"></i>${en ? "Valley yellow" : "谷地黄"}</span></label><label><input type="radio" name="ed-accent" value="cyan" data-ed-pref="accent"><span><i class="ed-swatch ed-swatch--cyan"></i>${en ? "Wuling cyan" : "武陵青"}</span></label></div></fieldset>
85
+ <label class="ed-option-row">${en ? "Full motion" : "完整动效"}<input type="checkbox" data-ed-pref="motion" checked></label>
86
+ <label class="ed-option-row">${en ? "Moving contours" : "动态等高线"}<input type="checkbox" data-ed-pref="background" checked></label>
87
+ <label class="ed-option-row">${en ? "Background frame cap" : "背景帧率上限"}<select data-ed-pref="fps"><option value="24">24 FPS</option><option value="60">60 FPS</option><option value="120">120 FPS</option></select></label>
88
+ <label class="ed-option-row">${en ? "Background speed" : "背景速度"}<input type="range" min="0.5" max="2" step="0.5" value="1" data-ed-pref="speed"><output data-ed-speed-output>1x</output></label>
89
+ </div></details>`;
90
+ const station = `<section class="ed-station field-readout" aria-label="${escape(copy.station)}"><header><span>${escape(copy.station)}</span><small>DIJIANG / LOCAL</small></header><p class="ed-station-code">${escape(status.fieldKicker)}</p><div class="ed-verdict" role="status" aria-label="${escape(statusAria)}"><span aria-hidden="true">${escape(status.symbol)}</span><strong>${escape(status.badge)}</strong></div><p class="ed-outcome" aria-label="${escape((en ? "Status: " : "状态:") + status.deliverable)}">${escape(status.deliverable)}</p><div class="ed-metrics">${parts.metrics}</div><footer><span>${escape(copy.privacy)}</span><b>${Number(data.redactionCount) || 0} ${escape(text.redactions)}</b></footer></section>`;
91
+ const reportMeta = `<dl class="ed-meta"><div><dt>${en ? "REVISION" : "比较范围"}</dt><dd>${escape(gitState === "unavailable" ? text.gitUnavailableMeta : data.gitRange || text.noRecords)}</dd></div><div><dt>${en ? "CAPTURED" : "报告时间"}</dt><dd>${escape(data.generatedAt || copy.untimed)}</dd></div></dl>`;
92
+ const body = `<a class="ed-skip" href="#ed-evidence">${escape(copy.skip)}</a>
93
+ <article class="poster dijiang-console" data-theme="frontier-signal" data-status="${status.key}">
94
+ <header class="ed-mast"><a class="ed-brand" href="#ed-task"><b>DS</b><span>${escape(text.fieldTheme)}<small>DSH / DIJIANG</small></span></a><div class="ed-mast-meta"><span>${escape(copy.local)}</span><span>${escape(data.demo ? copy.sample : copy.snapshot)}</span>${preferences}</div></header>
95
+ <nav class="ed-stages current-route" aria-label="${escape(copy.jump)}">${data.stages.map((stage, index) => `<a href="#${targets[index]}" ${index === 0 ? 'aria-current="location"' : ""}><b>${String(index + 1).padStart(2, "0")}</b><span>${escape(stage)}</span><i aria-hidden="true">${icons.arrow}</i></a>`).join("")}</nav>
96
+ <div class="ed-bridge"><canvas class="ed-contours" aria-hidden="true"></canvas><div class="ed-shell">
97
+ <header class="ed-heading" id="ed-task"><div><p class="ed-heading-code">${escape(copy.overview)} <span>// ${escape(status.fieldKicker)}</span></p><h1>${escape(data.projectName)}</h1><p class="ed-task">${escape(data.task)}</p></div>${station}</header>
98
+ </div></div>
99
+ <div class="ed-shell">
100
+ <div class="ed-workspace" id="ed-evidence">
101
+ <aside class="ed-side">
102
+ <section class="ed-review" aria-label="${escape(copy.warnings)}"><header><h2>${escape(copy.warnings)}</h2><span>${String(warnings.length).padStart(2, "0")}</span></header>${warnings.length ? `<ul>${warnings.map((warning) => `<li>${escape(warning)}</li>`).join("")}</ul>` : `<p>${escape(copy.noWarnings)}</p>`}</section>
103
+ ${reportMeta}
104
+ <div class="ed-scope"><span>${escape(copy.scope)}</span><p>${escape(text.scopeGit)} · ${escape(text.scopeTests)} · ${escape(data.visualProject === false ? text.scopeScreensOptional : text.scopeScreens)}</p></div>
105
+ </aside>
106
+ <div class="ed-main">
107
+ ${overview}
108
+ <section class="route-evidence delivery-manifest ed-ledger" data-status="${status.key}" data-git-state="${escape(gitState)}" data-test-state="${escape(testState)}" data-output-state="${outputs.length ? "available" : "empty"}" aria-label="${escape(copy.archive)}">
109
+ <div class="ed-section" id="ed-changes"><header class="ed-section-head"><h2>${escape(copy.changes)}</h2><span>${String(Number(data.fileCount) || files.length).padStart(2, "0")} ${escape(copy.count)}</span></header>${fileRows}${diffPanels}${data.fileCount > files.length ? `<p class="ed-more">${escape(copy.remaining)}</p>` : ""}</div>
110
+ <div class="ed-section" id="ed-tests"><header class="ed-section-head"><h2>${escape(copy.checks)}</h2><span>${Number(data.passedTests) || 0} / ${Number(data.testCount) || 0}</span></header>${testRows}${data.testCount > tests.length ? `<p class="ed-more">${escape(copy.remaining)}</p>` : ""}</div>
111
+ </section>
112
+ ${captureSection}
113
+ ${privacy}
114
+ <section class="ed-section ed-outputs" id="ed-outputs"><header class="ed-section-head"><h2>${escape(copy.output)}</h2><div class="ed-export-actions"><button type="button" data-ed-download title="${escape(copy.download)}">${icons.download}${escape(copy.download)}</button><button type="button" data-ed-export="json" aria-label="${detailCopy.json}" title="${detailCopy.json}">${icons.download}JSON</button><button type="button" data-ed-export="md" aria-label="${detailCopy.markdown}" title="${detailCopy.markdown}">${icons.download}Markdown</button></div></header>${outputs.length ? `<dl>${outputs.map(([label, path]) => `<div><dt>${escape(label)}</dt><dd><code>${escape(path)}</code><button type="button" data-ed-copy="${escape(path)}" aria-label="${escape(copy.copyPath)}: ${escape(path)}" title="${escape(copy.copyPath)}">${icons.copy}</button></dd></div>`).join("")}</dl>` : empty("outputs-missing", text.outputsMissingTitle, text.outputsMissingDetail)}<p class="ed-notice" role="status" aria-live="polite"></p></section>
115
+ </div>
116
+ </div><footer class="ed-footer"><span>${escape(text.footer)}</span><span>DSH / ${escape(copy.archive)}</span></footer>
117
+ </div>
118
+ </article>`;
119
+ return `<!doctype html><html lang="${data.locale}" data-dijiang-motion="full"><head><!--${lucideLicense}--><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escape(data.projectName)} · ${escape(text.fieldTheme)}</title><style>${dijiangConsoleCss}</style></head><body>${renderDijiangLoader(data.projectName, en, parts.instrument)}${body}<script id="ed-export-data" type="application/json">${exportPayload}</script><script>${dijiangMotionScript}</script><script>${dijiangConsoleScript}</script></body></html>`;
120
+ }
121
+
122
+ const dijiangConsoleScript = `(()=>{
123
+ const root=document.documentElement;
124
+ const links=[...document.querySelectorAll('.ed-stages a')];
125
+ const mark=()=>{links.forEach(a=>{if(a.hash===location.hash)a.setAttribute('aria-current','location');else a.removeAttribute('aria-current')})};
126
+ addEventListener('hashchange',mark);if(location.hash)mark();
127
+ const notice=document.querySelector('.ed-notice');
128
+ const en=root.lang==='en';
129
+ document.querySelectorAll('[data-ed-file]').forEach(button=>button.addEventListener('click',()=>{
130
+ document.querySelectorAll('[data-ed-file]').forEach(item=>item.setAttribute('aria-pressed',String(item===button)));
131
+ document.querySelectorAll('.ed-diff-panel').forEach(panel=>panel.hidden=panel.id!==button.getAttribute('aria-controls'));
132
+ }));
133
+ const comparison=document.querySelector('[data-ed-comparison]');
134
+ if(comparison){
135
+ for(const kind of ['before','after']){
136
+ const source=document.getElementById(comparison.dataset[kind]);
137
+ if(source){const image=document.createElement('img');image.src=source.src;image.alt=source.alt;comparison.querySelector('.ed-compare-'+kind).append(image)}
138
+ }
139
+ const slider=document.querySelector('[data-ed-compare-range]');
140
+ const update=()=>{comparison.querySelector('.ed-compare-after').style.clipPath='inset(0 0 0 '+slider.value+'%)';comparison.querySelector('.ed-compare-handle').style.left=slider.value+'%';slider.nextElementSibling.value=slider.value+'%'};
141
+ slider.addEventListener('input',update);update();
142
+ }
143
+ document.querySelectorAll('[data-ed-export]').forEach(button=>button.addEventListener('click',()=>{
144
+ const payload=JSON.parse(document.getElementById('ed-export-data').textContent);
145
+ const json=button.dataset.edExport==='json';
146
+ const url=URL.createObjectURL(new Blob([json?JSON.stringify(payload.report,null,2):payload.markdown],{type:json?'application/json':'text/markdown'}));
147
+ const anchor=document.createElement('a');anchor.href=url;anchor.download=(payload.report.demo?'delivery-demo-report':'delivery-report')+(json?'.json':'.md');anchor.click();setTimeout(()=>URL.revokeObjectURL(url),1000);
148
+ }));
149
+ document.querySelectorAll('[data-ed-copy]').forEach(button=>button.addEventListener('click',async()=>{
150
+ try{await navigator.clipboard.writeText(button.dataset.edCopy);notice.textContent=en?'Copied':'已复制'}catch{notice.textContent=en?'Copy unavailable; select the path text.':'复制不可用,请选择路径文本。'}
151
+ }));
152
+ document.querySelectorAll('[data-ed-capture]').forEach(button=>button.addEventListener('click',()=>{
153
+ const image=document.getElementById(button.dataset.edCapture);if(!image)return;
154
+ const anchor=document.createElement('a');anchor.href=image.src;anchor.download=button.dataset.edCapture+'.'+(image.src.match(/^data:image\\/(png|jpeg|webp)/)?.[1]||'png');anchor.click();
155
+ }));
156
+ document.querySelector('[data-ed-download]')?.addEventListener('click',()=>{
157
+ const clone=document.documentElement.cloneNode(true);clone.querySelector('.loader')?.remove();clone.dataset.loaderReady='true';clone.querySelectorAll('[data-ed-derived]').forEach(node=>node.replaceChildren());
158
+ const url=URL.createObjectURL(new Blob(['<!doctype html>'+clone.outerHTML],{type:'text/html'}));
159
+ const anchor=document.createElement('a');anchor.href=url;anchor.download='dijiang-delivery.html';anchor.click();setTimeout(()=>URL.revokeObjectURL(url),1000);
160
+ });
161
+ })()`;
@@ -0,0 +1,101 @@
1
+ const escape = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[character]);
2
+
3
+ export function renderDijiangLoader(projectName, en, instrument) {
4
+ return `<div class="loader loader--field" role="status" aria-label="${en ? "Opening local report" : "打开本地报告"}">
5
+ <section class="field-loader-sheet">
6
+ <canvas class="ed-contours" aria-hidden="true"></canvas>
7
+ <header class="field-loader-mast"><span class="field-loader-id">DSH / DIJIANG</span><span class="field-loader-project">${escape(projectName)}</span></header>
8
+ <div class="field-loader-center"><div class="field-loader-copy"><span class="field-loader-kicker">${en ? "LOCAL EVIDENCE / REPORT ACCESS" : "本地凭据 / 报告接入"}</span><strong>${en ? "EVIDENCE\nONLINE" : "证据接入"}</strong><p>${en ? "Dijiang / Opening the retained delivery record" : "终末地帝江号 / 正在打开交付档案"}</p></div>${instrument.replace("D-0017", "DIJIANG").replace("LINK / 04", "LOCAL")}</div>
9
+ <footer class="field-loader-statusbar"><div class="field-loader-readout"><span class="field-loader-status" data-loader-status>${en ? "Opening local report" : "打开本地报告"}</span><b data-loader-percent>00%</b></div><i class="field-loader-progress"><em data-loader-fill></em></i><div class="field-loader-stages" aria-hidden="true"><b>01 / LOCAL</b><b>02 / RECORDS</b><b>03 / DISPLAY</b></div></footer>
10
+ </section><div class="field-loader-topband" aria-hidden="true"></div>
11
+ </div>`;
12
+ }
13
+
14
+ export function readDijiangPreferences(embedded, readStored) {
15
+ const prefs = { palette: "light", accent: "yellow", background: true, motion: true, fps: 24, speed: 1 };
16
+ const sources = [embedded];
17
+ try { sources.push(readStored()); } catch { /* Offline files may not have access to storage. */ }
18
+ for (const source of sources) {
19
+ try {
20
+ const saved = JSON.parse(source || "{}");
21
+ if (!saved || typeof saved !== "object" || Array.isArray(saved)) continue;
22
+ if (["light", "dark"].includes(saved.palette)) prefs.palette = saved.palette;
23
+ if (["yellow", "cyan"].includes(saved.accent)) prefs.accent = saved.accent;
24
+ if (typeof saved.background === "boolean") prefs.background = saved.background;
25
+ if (typeof saved.motion === "boolean") prefs.motion = saved.motion;
26
+ if ([24,60,120].includes(saved.fps)) prefs.fps = saved.fps;
27
+ if ([.5,1,1.5,2].includes(saved.speed)) prefs.speed = saved.speed;
28
+ } catch { /* A corrupt preference does not invalidate the report. */ }
29
+ }
30
+ return prefs;
31
+ }
32
+
33
+ // Embedded in the exported HTML: no runtime, CDN or network access is required.
34
+ export const dijiangMotionScript = `(()=>{
35
+ const root=document.documentElement;
36
+ const key='dijiang-presentation-v1';
37
+ const prefs=(${readDijiangPreferences.toString()})(root.dataset.edPreferences,()=>localStorage.getItem(key));
38
+ if(new URLSearchParams(location.search).get('motion')==='accessible')prefs.motion=false;
39
+ let redraw=()=>{};
40
+ function apply(persist=false){
41
+ root.dataset.edPalette=prefs.palette;root.dataset.edAccent=prefs.accent;root.dataset.dijiangMotion=prefs.motion?'full':'accessible';root.dataset.edPreferences=JSON.stringify(prefs);
42
+ document.querySelectorAll('[data-ed-pref]').forEach(input=>{const name=input.dataset.edPref;if(input.type==='radio')input.checked=input.value===prefs[name];else if(input.type==='checkbox')input.checked=prefs[name];else input.value=String(prefs[name]);if(name==='fps'||name==='speed')input.disabled=!prefs.background||!prefs.motion});
43
+ const speed=document.querySelector('[data-ed-speed-output]');if(speed)speed.value=prefs.speed+'x';
44
+ if(persist)try{localStorage.setItem(key,JSON.stringify(prefs))}catch{}
45
+ redraw();
46
+ }
47
+ apply();
48
+ document.querySelectorAll('[data-ed-pref]').forEach(input=>input.addEventListener('input',()=>{const name=input.dataset.edPref;prefs[name]=input.type==='checkbox'?input.checked:['fps','speed'].includes(name)?Number(input.value):input.value;apply(true)}));
49
+ const settings=document.querySelector('.ed-settings');
50
+ document.addEventListener('click',event=>{if(settings&&!settings.contains(event.target))settings.open=false});
51
+ document.addEventListener('keydown',event=>{if(event.key==='Escape'&&settings?.open){settings.open=false;settings.querySelector('summary').focus()}});
52
+
53
+ // Trace terrain isolines once per size or palette; animate only the raster offset.
54
+ const fields=[...document.querySelectorAll('canvas.ed-contours')].map(canvas=>({canvas,buffer:document.createElement('canvas'),visible:true,width:0,height:0}));
55
+ function prepare(field){
56
+ const {canvas,buffer}=field,box=canvas.getBoundingClientRect();
57
+ const scale=Math.min(devicePixelRatio||1,1.5);field.width=Math.max(1,Math.round(box.width*scale));field.height=Math.max(1,Math.round(box.height*scale));
58
+ canvas.width=field.width;canvas.height=field.height;buffer.width=field.width+96;buffer.height=field.height+96;
59
+ const ctx=buffer.getContext('2d');if(!ctx)return;
60
+ ctx.strokeStyle=getComputedStyle(canvas).getPropertyValue('--contour').trim();
61
+ const step=6*scale,cols=Math.ceil(buffer.width/step),rows=Math.ceil(buffer.height/step);
62
+ const values=Array.from({length:rows+1},(_,j)=>Array.from({length:cols+1},(_,i)=>{
63
+ const x=i/cols*2,y=j/rows;
64
+ return .58*x-.72*y+.36*Math.sin(x*3.8+y*2.6)+.12*Math.cos(x*7.3-y*3.5)+.06*Math.sin(x*10.5+y*7.4);
65
+ }));
66
+ const paths=Array.from({length:97},()=>new Path2D());
67
+ for(let j=0;j<rows;j++)for(let i=0;i<cols;i++){
68
+ const corners=[[i*step,j*step,values[j][i]],[(i+1)*step,j*step,values[j][i+1]],[(i+1)*step,(j+1)*step,values[j+1][i+1]],[i*step,(j+1)*step,values[j+1][i]]];
69
+ const low=Math.max(-32,Math.ceil(Math.min(...corners.map(c=>c[2]))/.026)),high=Math.min(64,Math.floor(Math.max(...corners.map(c=>c[2]))/.026));
70
+ for(let level=low;level<=high;level++){
71
+ const height=level*.026,hits=[],path=paths[level+32];
72
+ for(let edge=0;edge<4;edge++){
73
+ const a=corners[edge],b=corners[(edge+1)%4];
74
+ if((a[2]<height)===(b[2]<height))continue;
75
+ const t=(height-a[2])/(b[2]-a[2]);hits.push([a[0]+(b[0]-a[0])*t,a[1]+(b[1]-a[1])*t]);
76
+ }
77
+ for(let n=0;n+1<hits.length;n+=2){path.moveTo(...hits[n]);path.lineTo(...hits[n+1])}
78
+ }
79
+ }
80
+ paths.forEach((path,index)=>{ctx.lineWidth=((index-32)%5===0?1:.5)*scale;ctx.stroke(path)});
81
+ paint(field,0);
82
+ }
83
+ function paint(field,phase){const ctx=field.canvas.getContext('2d');if(!ctx)return;ctx.clearRect(0,0,field.width,field.height);ctx.drawImage(field.buffer,-48+Math.sin(phase)*16,-48+Math.cos(phase*.7)*12)}
84
+ let frame=0,last=0,phase=0;
85
+ function tick(now){frame=0;if(document.hidden||!prefs.background||!prefs.motion)return;const interval=1000/prefs.fps;if(now-last>=interval){phase+=Math.min(100,now-(last||now))*.00004*prefs.speed;last=now;fields.forEach(field=>{if(field.visible&&field.canvas.isConnected)paint(field,phase)})}if(fields.some(field=>field.visible&&field.canvas.isConnected))frame=requestAnimationFrame(tick)}
86
+ function start(){if(frame)cancelAnimationFrame(frame);frame=0;last=0;if(!document.hidden&&prefs.background&&prefs.motion&&fields.some(field=>field.visible&&field.canvas.isConnected))frame=requestAnimationFrame(tick)}
87
+ redraw=()=>{fields.forEach(field=>{if(field.canvas.isConnected)prepare(field)});start()};
88
+ const observer=typeof IntersectionObserver==='function'?new IntersectionObserver(entries=>{for(const entry of entries){const field=fields.find(item=>item.canvas===entry.target);if(field)field.visible=entry.isIntersecting}start()}):null;
89
+ fields.forEach(field=>observer?.observe(field.canvas));
90
+ const resize=typeof ResizeObserver==='function'?new ResizeObserver(()=>redraw()):null;fields.forEach(field=>resize?.observe(field.canvas));if(!resize)addEventListener('resize',redraw);
91
+ document.addEventListener('visibilitychange',start);addEventListener('pagehide',()=>{cancelAnimationFrame(frame);observer?.disconnect();resize?.disconnect()});addEventListener('pageshow',event=>{if(event.persisted){fields.forEach(field=>{if(field.canvas.isConnected){observer?.observe(field.canvas);resize?.observe(field.canvas)}});redraw()}else start()});redraw();
92
+
93
+ const loader=document.querySelector('.loader');
94
+ if(!loader){root.dataset.loaderReady='true';return}
95
+ root.dataset.loaderReady='false';loader.dataset.dijiangMotion=root.dataset.dijiangMotion;loader.setAttribute('aria-busy','true');
96
+ const en=root.lang==='en',began=performance.now();let finished=false,progressFrame=0;
97
+ function progress(now){if(finished)return;const value=Math.min(1,(now-began)/1550);loader.querySelector('[data-loader-fill]').style.transform='scaleX('+value+')';loader.querySelector('[data-loader-percent]').textContent=String(Math.round(value*100)).padStart(2,'0')+'%';loader.querySelector('[data-loader-status]').textContent=(en?['Opening local report','Preparing retained records','Preparing the display','Report available']:['打开本地报告','整理已保存记录','准备显示界面','报告可供查看'])[Math.min(3,Math.floor(value*4))];if(value<1)progressFrame=requestAnimationFrame(progress)}
98
+ function remove(){if(finished)return;finished=true;cancelAnimationFrame(progressFrame);loader.setAttribute('aria-busy','false');root.dataset.loaderReady='true';fields.filter(field=>loader.contains(field.canvas)).forEach(field=>{observer?.unobserve(field.canvas);resize?.unobserve(field.canvas);field.visible=false});loader.remove();start()}
99
+ function onExit(event){if(event.target===loader){loader.removeEventListener('animationend',onExit);remove()}}
100
+ loader.addEventListener('animationend',onExit);progressFrame=requestAnimationFrame(progress);setTimeout(remove,prefs.motion?2900:180);
101
+ })()`;
@@ -0,0 +1,37 @@
1
+ export const dijiangConsoleCss = `
2
+ :root{color-scheme:light;--paper:#fff;--ink:#191919;--muted:#666;--line:#d9d9d9;--surface:#f2f2f2;--inset:#e6e6e6;--signal:#fffa00;--contour:#191919;--positive:#191919;--negative:#666;--mono:'Cascadia Mono',Consolas,'PingFang SC','Microsoft YaHei',monospace;--space-xs:4px;--space-sm:8px;--space-md:16px;--space-lg:24px;--space-xl:32px;--topbar:64px;--rail:80px}
3
+ :root[data-ed-palette=dark]{color-scheme:dark;--paper:#191919;--ink:#fff;--muted:#b3b3b3;--line:#424242;--surface:#252525;--inset:#353535;--contour:#fff;--positive:#fff;--negative:#b3b3b3}
4
+ :root[data-ed-accent=cyan]{--signal:#14d0d0}
5
+ *{box-sizing:border-box;min-width:0;letter-spacing:0}html{font-size:16px;scroll-behavior:smooth;scroll-padding-top:88px}body{margin:0;background:var(--paper);color:var(--ink);font-family:Arial,'Helvetica Neue','PingFang SC','Microsoft YaHei',sans-serif;font-variant-numeric:tabular-nums;line-height:1.5}button,input,select{font:inherit}button,a,summary{-webkit-tap-highlight-color:transparent}button{cursor:pointer}button,a,summary,input,select{border-radius:0}button:focus-visible,a:focus-visible,summary:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--ink);outline-offset:4px}a{color:inherit}button{color:inherit}svg{display:block}.lucide{flex:0 0 auto}button{display:inline-flex;align-items:center;justify-content:center;gap:8px}button:active{transform:translateX(1px)}button:hover{background:var(--inset)}[hidden]{display:none!important}
6
+ .poster.dijiang-console{width:100%;height:auto;min-height:100svh;padding:var(--topbar) 0 0 var(--rail);background:var(--paper);color:var(--ink);overflow:visible}.ed-shell{width:100%;max-width:1440px;padding:0 56px;margin:auto}.ed-skip{position:fixed;top:-80px;left:16px;z-index:60;background:var(--signal);color:#191919;padding:12px 16px}.ed-skip:focus{top:8px}
7
+ .ed-mast{position:fixed;z-index:30;inset:0 0 auto;height:var(--topbar);display:flex;align-items:center;justify-content:space-between;gap:16px;padding:0 24px;background:var(--paper);border-bottom:1px solid var(--line)}.ed-brand{display:flex;align-items:center;gap:16px;text-decoration:none}.ed-brand>b{display:grid;place-items:center;width:40px;height:40px;background:var(--ink);color:var(--paper);font-size:20px;font-weight:900}.ed-brand>span{font-size:14px;font-weight:700}.ed-brand small{display:block;font:10px/1.6 var(--mono);color:var(--muted)}.ed-mast-meta{display:flex;align-items:center;gap:24px;font:11px var(--mono)}.ed-mast-meta>span:last-of-type{border:1px solid var(--line);padding:4px 8px}.ed-settings{position:relative}.ed-settings>summary{width:40px;height:40px;display:grid;place-items:center;cursor:pointer;list-style:none;border:1px solid var(--line)}.ed-settings summary::-webkit-details-marker{display:none}.ed-settings[open]>summary{background:var(--signal);color:#191919;border-color:#191919}.ed-preferences{position:absolute;z-index:40;right:0;top:48px;width:288px;max-width:calc(100vw - 32px);padding:20px;background:var(--paper);border:1px solid var(--ink);box-shadow:6px 6px 0 color-mix(in srgb,var(--ink) 12%,transparent)}.ed-preferences h2{font-size:16px;margin:0 0 16px}.ed-preferences fieldset{border:0;margin:0 0 16px;padding:0}.ed-preferences legend{font-size:12px;margin-bottom:8px;color:var(--muted)}.ed-options{display:flex;gap:0}.ed-options label{flex:1;position:relative}.ed-options input{position:absolute;opacity:0;width:1px;height:1px}.ed-options span{min-height:40px;display:flex;align-items:center;justify-content:center;gap:8px;border:1px solid var(--line);font-size:12px;cursor:pointer}.ed-options input:checked+span{background:var(--ink);color:var(--paper);border-color:var(--ink)}.ed-options input:focus-visible+span{outline:2px solid var(--ink);outline-offset:3px}.ed-swatch{display:block;width:12px;height:12px;border:1px solid #191919}.ed-swatch--yellow{background:#fffa00}.ed-swatch--cyan{background:#14d0d0}.ed-option-row{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;margin-bottom:12px}.ed-option-row input[type=checkbox]{width:18px;height:18px;accent-color:var(--ink)}.ed-option-row select{background:var(--surface);color:var(--ink);border:1px solid var(--line);min-height:36px;padding:4px 8px}.ed-option-row input[type=range]{width:128px;accent-color:var(--ink)}.ed-option-row output{width:3ch;text-align:right}.ed-preferences :disabled{opacity:.45;cursor:not-allowed}
8
+ .ed-preferences{max-height:calc(100svh - var(--topbar) - 88px);overflow:auto;overscroll-behavior:contain}
9
+ .ed-stages.current-route{position:fixed;z-index:20;inset:var(--topbar) auto 0 0;width:var(--rail);display:flex;flex-direction:column;gap:0;padding:16px 0;background:var(--paper);border-right:1px solid var(--line);overflow-y:auto}.ed-stages a{min-height:72px;flex:0 0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;text-decoration:none;border-left:4px solid transparent;border-bottom:0;transition:background .15s,color .15s}.ed-stages b{font:18px var(--mono)}.ed-stages span{font-size:12px;font-weight:700;overflow-wrap:anywhere}.ed-stages i{display:none}.ed-stages a[aria-current]{background:var(--inset);color:var(--ink);border-left-color:var(--ink)}.ed-stages a:hover{background:var(--surface);color:var(--ink)}.ed-stages:after{content:'DSH / DIJIANG';writing-mode:vertical-rl;margin:24px auto 0;font:10px var(--mono);color:var(--muted)}
10
+ .ed-bridge{position:relative;isolation:isolate;background:var(--surface);border-bottom:1px solid var(--line);overflow:hidden}.ed-bridge:before{content:'';position:absolute;z-index:-1;inset:0 0 0 45%;background-image:radial-gradient(var(--muted) .65px,transparent .65px);background-size:12px 12px;opacity:.14;pointer-events:none}.ed-contours{position:absolute;inset:0 0 0 auto;width:58%;height:100%;z-index:-1;pointer-events:none;opacity:.14;mask-image:linear-gradient(90deg,transparent,#000 30%)}.ed-heading{display:grid;grid-template-columns:minmax(0,1fr) 288px;gap:64px;align-items:center;padding:56px 0 64px}.ed-heading-code{display:flex;align-items:center;flex-wrap:wrap;gap:12px;margin:0 0 28px;font-size:12px;font-weight:700}.ed-heading-code>span{font-size:11px;font-weight:400;color:var(--muted)}.ed-heading-code:before{content:'';width:24px;height:12px;background:var(--signal);border-left:4px solid var(--ink);transform:skewX(-18deg)}.ed-heading h1{max-width:26ch;font-size:64px;line-height:1.04;font-weight:900;overflow-wrap:anywhere;margin:0 0 24px;text-wrap:balance}.ed-task{font-size:15px;line-height:1.8;margin:0;max-width:70ch;overflow-wrap:anywhere}.ed-station{--ink:#fff;--paper:#191919;--muted:#b3b3b3;--line:#424242;border:1px solid var(--line);border-top:3px solid var(--signal);padding:24px;background:var(--paper);color:var(--ink)}.ed-station>header{display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:12px;font-weight:700}.ed-station small{font:9px var(--mono);color:var(--muted)}.ed-station-code,.ed-outcome{display:none}.ed-verdict{display:flex;gap:12px;align-items:center;margin:24px 0}.ed-verdict>span{display:grid;place-items:center;min-width:24px;height:32px;background:var(--signal);color:#191919;font-size:20px;font-weight:700}.ed-verdict strong{font-size:30px;line-height:1.2;overflow-wrap:anywhere}.ed-metrics{display:grid;grid-template-columns:1fr 1fr;gap:24px}.ed-metrics>div{display:flex;flex-direction:column;align-items:start;border-top:1px solid var(--line);padding:16px 0;gap:6px}.ed-metrics small{font:11px Arial,'Microsoft YaHei',sans-serif;color:var(--muted)}.ed-metrics b{font:24px var(--mono);order:2;overflow-wrap:anywhere}.ed-metrics>div:last-child{display:none}.ed-station footer{display:flex;justify-content:space-between;gap:12px;border-top:1px solid var(--line);padding-top:12px;font-size:10px}.ed-station footer b{font-weight:400}.dijiang-console[data-status=failed] .ed-verdict>span{background:var(--signal);color:#191919;border:1px solid var(--ink)}
11
+ .ed-workspace{padding:0 0 24px;display:flex;flex-direction:column}.ed-side{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr) minmax(0,.8fr);gap:24px;padding:20px 0;border-bottom:1px solid var(--line);margin-bottom:24px}.ed-review header{display:flex;align-items:center;justify-content:space-between;gap:8px}.ed-review h2{font-size:12px;margin:0}.ed-review header>span{font:11px var(--mono)}.ed-review ul{margin:8px 0 0;padding-left:16px}.ed-review p,.ed-review li{font-size:11px;line-height:1.7;color:var(--muted);overflow-wrap:anywhere}.ed-review p{margin:8px 0 0}.ed-meta{display:grid;gap:8px;margin:0;padding-left:24px;border-left:1px solid var(--line);font-size:10px}.ed-meta dt{color:var(--muted)}.ed-meta dd{margin:4px 0 0;font:11px/1.5 var(--mono);overflow-wrap:anywhere}.ed-scope{border-left:1px solid var(--line);padding-left:24px;font-size:11px;color:var(--muted)}.ed-scope p{line-height:1.7;margin:8px 0 0}.ed-main{width:100%}.ed-section{margin:0 0 32px;scroll-margin-top:24px}.ed-section-head{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;border-bottom:1px solid var(--ink);padding:0 0 12px;margin:0}.ed-section-head h2{display:flex;align-items:center;gap:10px;font-size:16px;line-height:1.3;margin:0;font-weight:700}.ed-section-head h2:before{content:'';width:4px;height:14px;flex:0 0 4px;background:var(--ink)}.ed-section-head>span{font:11px var(--mono);color:var(--muted);text-align:right}.ed-overview dl{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));margin:0;border-bottom:1px solid var(--line)}.ed-overview dl>div{padding:16px 24px;border-right:1px solid var(--line)}.ed-overview dl>div:first-child{padding-left:0}.ed-overview dl>div:last-child{border-right:0}.ed-overview dt{font-size:11px;color:var(--muted)}.ed-overview dd{font:22px/1.4 var(--mono);margin:8px 0 0;overflow-wrap:anywhere}.ed-overview .ed-delta{font-size:22px}.ed-delta{display:flex;flex-wrap:wrap;gap:8px;font:12px var(--mono)}.ed-delta b{font-weight:600;color:var(--positive)}.ed-delta i{font-style:normal;color:var(--negative)}
12
+ .ed-ledger.delivery-manifest{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:32px;position:static;background:none;border:0;padding:0;margin:0;overflow:visible}.ed-files,.ed-receipts{list-style:none;padding:0;margin:0}.ed-files li{border-bottom:1px solid var(--line)}.ed-files button{display:grid;grid-template-columns:24px minmax(0,1fr) auto;align-items:baseline;gap:8px;width:100%;padding:12px 8px;text-align:left;border:0;background:none}.ed-files button[aria-pressed=true]{background:var(--inset);box-shadow:inset 3px 0 var(--signal)}.ed-files button:hover{background:var(--surface)}.ed-index{font:10px var(--mono);color:var(--muted)}.ed-files code,.ed-receipts code{font:12px/1.6 var(--mono);overflow-wrap:anywhere}.ed-diff-panel{margin-top:16px}.ed-diff-panel header{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:8px;padding:12px;background:var(--ink);color:var(--paper)}.ed-diff-panel h3{font-size:12px;margin:0}.ed-diff-panel header code{font:10px var(--mono);overflow-wrap:anywhere}.ed-diff-code{margin:0;padding:12px;background:var(--surface);border:1px solid var(--line);border-top:0;color:var(--ink);max-height:256px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font:12px/1.7 var(--mono)}.ed-diff-line{font:inherit}.ed-diff-line.addition{color:var(--positive)}.ed-diff-line.deletion{color:var(--negative)}.ed-diff-line.hunk{color:var(--ink);background:color-mix(in srgb,var(--signal) 32%,var(--paper))}.ed-diff-missing{margin:0;padding:16px;background:var(--inset);color:var(--muted);font-size:12px}.ed-more{font-size:11px;color:var(--muted);margin:8px 0;overflow-wrap:anywhere}.ed-receipts>li{border-bottom:1px solid var(--line)}.ed-receipts summary{display:grid;grid-template-columns:16px minmax(0,1fr) auto 12px;gap:8px;align-items:center;padding:12px 8px;cursor:pointer;list-style:none}.ed-receipts summary::-webkit-details-marker{display:none}.ed-receipts summary:after{content:'+';font:16px var(--mono)}.ed-receipts details[open]>summary:after{content:'−'}.ed-receipts summary:hover,.ed-receipts details[open]>summary{background:var(--inset)}.ed-receipt-mark{font-size:16px}.ed-receipt-result{font-size:11px;text-align:right}.ed-receipt-result small{display:block;font:9px/1.7 var(--mono);color:var(--muted)}.ed-receipts [data-status=failed] .ed-receipt-result,.ed-receipts [data-status=failed] .ed-receipt-mark{color:var(--negative)}.ed-receipts pre{background:var(--surface);color:var(--ink);margin:0;padding:16px;max-height:240px;overflow:auto;font:12px/1.8 var(--mono);white-space:pre-wrap;overflow-wrap:anywhere}.ed-empty{padding:24px 0}.ed-empty strong{font-size:13px}.ed-empty p{font-size:12px;line-height:1.7;color:var(--muted);max-width:65ch;margin:8px 0 0}
13
+ .ed-visual.evidence-page{position:relative;background:none;min-height:0;overflow:visible;padding:0}.ed-captures{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px}.ed-captures details{align-self:start;border-bottom:1px solid var(--line)}.ed-captures summary{display:flex;align-items:baseline;gap:12px;padding:12px 0;font-size:12px;cursor:pointer;list-style:none;overflow-wrap:anywhere}.ed-captures summary::-webkit-details-marker{display:none}.ed-captures summary>span{font:10px var(--mono);color:var(--muted)}.ed-captures summary:after{content:'+';margin-left:auto}.ed-captures details[open]>summary:after{content:'−'}.ed-captures figure{margin:0}.ed-captures .ed-shot{display:block;height:240px;background:var(--inset);overflow:auto;border:1px solid var(--line)}.ed-captures img{display:block;width:100%;height:100%;object-fit:contain}.ed-captures figcaption{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;margin:8px 0;font-size:10px;color:var(--muted)}.ed-captures button,.ed-outputs button{min-height:36px;padding:8px 12px;border:1px solid var(--line);background:none;font-size:11px}.ed-captures button:hover,.ed-outputs button:hover{background:var(--inset);border-color:var(--ink)}.ed-visual .evidence-page__guard{display:block;min-height:0;padding:24px 0;border:0;background:none;color:var(--ink)}.ed-visual .evidence-page__guard:before,.ed-visual .evidence-page__guard:after,.ed-visual .evidence-page__guard-meta,.ed-visual .evidence-page__guard-copy>small,.ed-visual .evidence-page__guard-empty,.ed-visual .evidence-page__guard-action{display:none}.ed-visual .evidence-page__guard-copy b{font-size:13px}.ed-visual .evidence-page__guard-copy p{font-size:12px;color:var(--muted);line-height:1.7;margin:8px 0}.ed-visual .evidence-page__guard-list{list-style:none;padding:0;margin:12px 0}.ed-visual .evidence-page__guard-list li{display:flex;flex-wrap:wrap;gap:8px;border-top:1px solid var(--line);padding:8px 0;font-size:11px}.ed-visual .evidence-page__guard-list small{margin-left:auto}
14
+ .ed-comparison{margin-top:24px}.ed-comparison h3{font-size:13px;margin:0 0 12px}.ed-comparison-stage{position:relative;width:100%;aspect-ratio:16/7;max-height:420px;overflow:hidden;background:var(--inset);border:1px solid var(--line)}.ed-compare-before,.ed-compare-after{position:absolute;inset:0}.ed-compare-after{clip-path:inset(0 0 0 50%)}.ed-comparison-stage img{display:block;width:100%;height:100%;object-fit:contain}.ed-compare-label{position:absolute;z-index:2;top:12px;left:12px;background:var(--ink);color:var(--paper);padding:4px 8px;font-size:10px}.ed-compare-label--after{left:auto;right:12px;background:var(--signal);color:#191919}.ed-compare-handle{position:absolute;top:0;bottom:0;left:50%;width:2px;background:var(--signal);box-shadow:0 0 0 1px #191919;pointer-events:none}.ed-compare-control{display:flex;align-items:center;gap:12px;font-size:11px;margin-top:12px}.ed-compare-control input{flex:1;accent-color:var(--ink)}.ed-compare-control output{min-width:3ch;text-align:right}
15
+ .ed-privacy dl{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 24px;margin:0}.ed-privacy dl>div{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:12px 0;border-bottom:1px solid var(--line);font-size:12px}.ed-privacy dt{flex:1;overflow-wrap:anywhere}.ed-privacy dd{flex:0 0 auto;font:16px var(--mono);margin:0}.ed-export-actions{display:flex;flex-wrap:wrap;gap:8px}.ed-outputs dl{margin:0}.ed-outputs dl>div{display:grid;grid-template-columns:112px minmax(0,1fr);gap:16px;border-bottom:1px solid var(--line);padding:12px 0;font-size:12px}.ed-outputs dd{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0}.ed-outputs code{font:12px/1.5 var(--mono);overflow-wrap:anywhere;user-select:all}.ed-outputs dd button{border:0;flex:0 0 40px}.ed-notice{min-height:20px;font-size:12px;margin:8px 0 0}.ed-footer{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;border-top:1px solid var(--ink);padding:16px 0 24px;font:10px/1.6 var(--mono);color:var(--muted)}
16
+ html:not([data-loader-ready=true]) .dijiang-console{visibility:hidden}html[data-loader-ready=true] .dijiang-console{visibility:visible}html[data-loader-ready=true] .ed-heading,html[data-loader-ready=true] .ed-main{animation:ed-lateral-reveal .5s cubic-bezier(.16,1,.3,1) both}@keyframes ed-lateral-reveal{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0)}}
17
+ .loader.loader--field{--paper:#141414;--ink:#fff;--muted:#b3b3b3;--line:#424242;--inset:#353535;--contour:#fff;position:fixed;z-index:50;inset:0;background:transparent;color:var(--ink);overflow:hidden;animation:dijiang-reference-exit 2.4s step-end both}.field-loader-sheet{position:absolute;inset:0;display:grid;grid-template-rows:auto minmax(0,1fr) auto;padding:24px 48px;background:var(--paper);overflow:hidden;animation:ed-loader-sheet-exit 2.4s step-end both}.field-loader-sheet:before{content:'';position:absolute;inset:0;background-image:radial-gradient(var(--muted) .65px,transparent .65px);background-size:16px 16px;opacity:.15;pointer-events:none}.field-loader-sheet>.ed-contours{width:75%;opacity:.12;z-index:0}.field-loader-mast,.field-loader-center,.field-loader-statusbar{position:relative;z-index:1}.field-loader-mast{display:flex;align-items:center;justify-content:space-between;gap:24px;border-bottom:1px solid var(--line);padding-bottom:16px;font:11px var(--mono)}.field-loader-id{font-weight:700}.field-loader-project{max-width:60%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.field-loader-center{display:grid;grid-template-columns:minmax(0,1fr) minmax(240px,400px);gap:48px;align-items:center;width:min(100%,1200px);margin:auto}.field-loader-copy{min-width:0}.field-loader-kicker{display:block;color:var(--signal);font:12px var(--mono);margin-bottom:24px}.field-loader-copy strong{display:block;font-size:96px;font-weight:900;line-height:.98;overflow-wrap:anywhere;max-width:10ch;animation:ed-lateral-reveal .65s cubic-bezier(.16,1,.3,1) both}.field-loader-copy p{color:var(--muted);font-size:13px;margin:24px 0 0;max-width:60ch;overflow-wrap:anywhere}.field-loader-instrument{width:100%;height:auto;max-height:400px;overflow:visible;fill:none;stroke:var(--ink);stroke-width:1}.field-loader-instrument .instrument-halo,.field-loader-instrument .instrument-ring,.field-loader-instrument .instrument-ticks,.field-loader-instrument .instrument-sweep,.field-loader-instrument .instrument-cross{transform-box:view-box;transform-origin:160px 160px}.instrument-halo{stroke-dasharray:1 8;animation:dijiang-instrument-spin 36s linear infinite}.instrument-ring--outer{stroke-dasharray:220 50 40 40;stroke-width:2;animation:dijiang-instrument-spin 12s linear infinite}.instrument-ring--inner{stroke-dasharray:120 35;animation:dijiang-instrument-spin-reverse 8s linear infinite}.instrument-ring--core{stroke-width:8;stroke-dasharray:50 35;stroke:var(--signal);animation:dijiang-instrument-core-spin 3s linear infinite}.instrument-ticks{stroke-width:1}.instrument-tick--major{stroke-width:3}.instrument-cross{stroke:var(--line);stroke-dasharray:2 5}.instrument-sweep{stroke:var(--signal);stroke-width:2;animation:dijiang-loader-sweep-rotate 3s linear infinite}.instrument-node{fill:var(--signal);stroke:var(--ink)}.instrument-label{fill:var(--ink);stroke:none;text-anchor:middle;font:8px Arial,sans-serif}.field-loader-statusbar{border-top:1px solid var(--line);padding-top:16px}.field-loader-readout{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:16px}.field-loader-status{font-size:12px}.field-loader-readout>b{font:48px var(--mono);color:var(--signal)}.field-loader-progress{position:relative;display:block;width:100%;height:20px;background:var(--inset);border:1px solid var(--line);overflow:hidden}.field-loader-progress em{position:absolute;inset:0;background:var(--signal);transform:scaleX(0);transform-origin:left}.field-loader-progress:after{content:'';position:absolute;inset:0;background:repeating-linear-gradient(90deg,transparent 0 39px,#141414 39px 40px);opacity:.2}.field-loader-stages{display:flex;justify-content:space-between;gap:16px;margin-top:12px;font:10px var(--mono);color:var(--muted)}.field-loader-stages b{font-weight:400}.field-loader-topband{position:absolute;z-index:3;top:-8%;left:-25%;width:150%;height:116%;bottom:auto;border:0;background:var(--signal);transform:translateX(-120%) skewX(-14deg);animation:dijiang-yellow-transfer 2.4s cubic-bezier(.65,0,.25,1) both;pointer-events:none}@keyframes dijiang-yellow-transfer{0%,64%{transform:translateX(-120%) skewX(-14deg)}79%{transform:translateX(0) skewX(-14deg)}100%{transform:translateX(120%) skewX(-14deg)}}@keyframes ed-loader-sheet-exit{0%,79%{visibility:visible}79.1%,100%{visibility:hidden}}@keyframes dijiang-reference-exit{0%,99.9%{visibility:visible}100%{visibility:hidden;pointer-events:none}}@keyframes dijiang-instrument-spin{to{transform:rotate(360deg)}}@keyframes dijiang-instrument-spin-reverse{to{transform:rotate(-360deg)}}@keyframes dijiang-instrument-core-spin{to{transform:rotate(360deg)}}@keyframes dijiang-loader-sweep-rotate{to{transform:rotate(360deg)}}
18
+ html[data-dijiang-motion=accessible]{scroll-behavior:auto}html[data-dijiang-motion=accessible] .loader.loader--field{animation:dijiang-reference-reduced-exit .15s linear both}html[data-dijiang-motion=accessible] .loader.loader--field *{animation:none!important}html[data-dijiang-motion=accessible] .field-loader-topband{display:none}html[data-dijiang-motion=accessible] .dijiang-console *{animation:none!important;transition:none!important}@keyframes dijiang-reference-reduced-exit{to{opacity:0;visibility:hidden}}
19
+ @media(min-width:761px){.ed-captures:has(>details:only-child){grid-template-columns:1fr;max-width:960px}.ed-captures:has(>details:only-child) .ed-shot{height:auto;aspect-ratio:16/10;max-height:480px}.ed-captures:has(>details:nth-child(2):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}}
20
+ @media(min-width:1700px){.ed-heading h1{font-size:72px}.ed-heading{padding:40px 0}.ed-ledger.delivery-manifest{gap:48px}}
21
+ @media(max-width:1100px){.ed-heading h1{font-size:48px}.ed-heading{grid-template-columns:minmax(0,1fr) 240px;gap:24px}.ed-shell{padding:0 24px}.ed-ledger.delivery-manifest{gap:24px}.ed-receipts summary{grid-template-columns:16px minmax(0,1fr) 12px}.ed-receipt-result{grid-column:2;text-align:left;display:flex;align-items:baseline;gap:8px}.ed-receipts summary:after{grid-column:3;grid-row:1}.ed-side{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr)}.ed-scope{display:none}.field-loader-copy strong{font-size:72px}.field-loader-center{gap:24px}.ed-files .ed-delta{font-size:11px}}
22
+ @media(max-width:760px){:root{--topbar:56px;--rail:0px}html{scroll-padding-top:76px}.poster.dijiang-console{padding-bottom:72px}.ed-mast{padding:0 16px}.ed-brand{gap:8px}.ed-brand>b{width:32px;height:32px;font-size:16px}.ed-brand>span{font-size:12px}.ed-brand small{font-size:8px}.ed-mast-meta{gap:8px}.ed-mast-meta>span:first-child{display:none}.ed-mast-meta>span:last-of-type{font-size:9px}.ed-stages.current-route{inset:auto 0 0;width:100%;height:64px;display:grid;grid-template-columns:repeat(5,minmax(0,1fr));padding:0;border-right:0;border-top:1px solid var(--ink);background:var(--paper);overflow:visible}.ed-stages a{min-height:0;border-left:0;border-top:3px solid transparent;border-bottom:0;border-right:1px solid var(--line);gap:2px}.ed-stages a[aria-current]{border-top-color:var(--ink)}.ed-stages a:last-child{border-right:0}.ed-stages b{font-size:12px}.ed-stages span{font-size:11px}.ed-stages:after{display:none}.ed-shell{padding:0 16px}.ed-heading{grid-template-columns:1fr;gap:24px;padding:24px 0}.ed-heading h1{font-size:36px;margin-bottom:16px}.ed-heading-code{margin-bottom:16px;gap:8px;font-size:11px}.ed-heading-code>span{font-size:10px}.ed-task{font-size:13px}.ed-contours{width:100%;opacity:.1}.ed-station{padding:16px;border-top:3px solid var(--signal)}.ed-station>header{font-size:11px}.ed-verdict{margin:12px 0}.ed-verdict strong{font-size:26px}.ed-metrics>div{flex-direction:row;align-items:baseline;justify-content:space-between;padding:8px 0;gap:12px}.ed-metrics b{font-size:20px}.ed-station footer{padding-top:8px}.ed-side{gap:16px;padding:16px 0;margin-bottom:24px;grid-template-columns:1fr}.ed-meta{grid-template-columns:1fr 1fr;padding:12px 0 0;border-left:0;border-top:1px solid var(--line)}.ed-review ul{margin-top:4px}.ed-overview dd,.ed-overview .ed-delta{font-size:16px}.ed-overview dl>div{padding:12px 8px 12px 0}.ed-section{margin-bottom:24px}.ed-section-head h2{font-size:15px}.ed-ledger.delivery-manifest{display:block}.ed-captures{grid-template-columns:1fr;gap:16px}.ed-captures .ed-shot{height:256px}.ed-comparison-stage{aspect-ratio:1.2}.ed-captures button,.ed-outputs button{min-height:44px}.ed-privacy dl{grid-template-columns:1fr}.ed-outputs dl>div{grid-template-columns:1fr;gap:4px}.ed-outputs code{font-size:11px}.ed-section-head>span{font-size:10px}.ed-receipts summary{grid-template-columns:16px minmax(0,1fr) 12px}.ed-preferences{right:0}.field-loader-sheet{padding:16px}.field-loader-mast{font-size:9px;gap:12px;padding-bottom:12px}.field-loader-center{grid-template-columns:1fr;gap:16px;padding:24px 0}.field-loader-copy strong{font-size:56px}.field-loader-kicker{font-size:10px;margin-bottom:16px}.field-loader-copy p{font-size:11px;margin-top:16px}.field-loader-instrument{max-height:240px}.field-loader-readout>b{font-size:28px}.field-loader-stages{font-size:9px;gap:8px}.field-loader-status{font-size:11px}.field-loader-project{max-width:45%}}
23
+ @media(max-width:760px){.ed-option-row{min-height:44px}.ed-options span{min-height:44px}.ed-settings>summary{width:44px;height:44px}}
24
+ @media(max-width:760px) and (max-height:700px){.field-loader-center{grid-template-columns:minmax(0,1fr) 112px;gap:16px}.field-loader-copy strong{font-size:40px}.field-loader-instrument{max-height:112px}.field-loader-copy p{max-height:48px;overflow:hidden}}
25
+ @media(min-width:761px) and (max-height:750px){.ed-heading{padding:20px 0}.ed-heading h1{margin-bottom:12px}.ed-station{padding:16px}.ed-verdict{margin:12px 0}.ed-verdict strong{font-size:26px}.ed-metrics>div{padding:8px 0}.ed-metrics b{font-size:20px}.ed-side{padding:12px 0;margin-bottom:20px}.ed-overview dl>div{padding-top:12px;padding-bottom:12px}.ed-overview{margin-bottom:24px}}
26
+ @media(min-width:761px) and (max-height:650px){.field-loader-sheet{padding:16px 32px}.field-loader-instrument{max-height:260px}.field-loader-copy strong{font-size:64px}.field-loader-center{grid-template-columns:minmax(0,1fr) 260px}.ed-heading{padding:20px 0}}
27
+ @media(prefers-reduced-motion:reduce){html:not([data-dijiang-motion=full]){scroll-behavior:auto}html:not([data-dijiang-motion=full]) *{animation:none!important;transition:none!important}}
28
+ /* Industrial editorial override: keep status semantics monochrome and give the report a quieter reading rhythm. */
29
+ .ed-receipt-mark{display:grid;place-items:center;width:16px;height:16px;color:#191919;background:var(--signal);font-size:11px;font-weight:900}.ed-receipts [data-status=failed] .ed-receipt-result{color:var(--ink);text-decoration:underline;text-decoration-color:var(--signal);text-decoration-thickness:2px;text-underline-offset:3px}.ed-receipts [data-status=failed] .ed-receipt-mark{color:var(--ink);background:var(--surface);border:1px solid var(--ink)}
30
+ @media(min-width:1101px){.ed-shell{max-width:1440px;padding-inline:56px}.ed-heading{gap:64px;padding:56px 0 64px}.ed-heading-code{margin-bottom:28px}.ed-heading h1{margin-bottom:24px}.ed-task{line-height:1.8}.ed-station{padding:24px}.ed-verdict{margin:24px 0}.ed-metrics{gap:24px}.ed-metrics>div{padding:16px 0;gap:6px}.ed-workspace{padding-bottom:48px}.ed-side{gap:40px;padding:32px 0;margin-bottom:48px}.ed-review ul{margin-top:12px}.ed-review p,.ed-review li{line-height:1.8}.ed-review p{margin-top:12px}.ed-meta{gap:12px;padding-left:32px}.ed-meta dd{line-height:1.6}.ed-scope{padding-left:32px}.ed-scope p{line-height:1.8;margin-top:12px}.ed-section{margin-bottom:48px}.ed-section-head{padding-bottom:16px}.ed-overview dl>div{padding:24px 32px}.ed-overview dd{margin-top:12px}}
31
+ @media(max-width:760px){.poster.dijiang-console{padding-bottom:0}.ed-stages.current-route{position:sticky;top:var(--topbar);bottom:auto;z-index:25}}
32
+ /* Bold industrial hero: a single structural peak carries the extra energy. */
33
+ .ed-bridge>.ed-shell{position:relative;z-index:1}.ed-bridge:after{content:'';position:absolute;z-index:0;top:0;right:0;width:34%;height:100%;background:var(--ink);opacity:.055;clip-path:polygon(18% 0,100% 0,100% 100%,0 100%,12% 54%);pointer-events:none}.ed-heading{position:relative}.ed-heading:after{content:'';position:absolute;z-index:0;top:0;right:31%;width:12px;height:100%;background:var(--signal);clip-path:polygon(28% 0,100% 0,72% 100%,0 100%);opacity:.92;pointer-events:none}.ed-heading>div,.ed-heading>.ed-station{position:relative;z-index:1}.ed-heading h1{font-size:clamp(4rem,5.7vw,6rem);letter-spacing:-.025em}.ed-contours{width:68%;opacity:.2}.ed-station{width:100%;box-shadow:inset 0 0 0 1px rgba(255,255,255,.08)}.ed-overview{position:relative;padding-top:16px;border-top:4px solid var(--signal)}
34
+ @media(max-width:1100px){.ed-heading:after{right:29%;width:8px}.ed-heading h1{font-size:48px}.ed-contours{width:64%;opacity:.17}}
35
+ @media(max-width:760px){.ed-bridge:after{width:30%;opacity:.045}.ed-heading:after{right:10%;top:0;width:7px;height:100%;opacity:.72}.ed-heading h1{font-size:40px;letter-spacing:-.015em}.ed-contours{width:100%;opacity:.14}.ed-overview{padding-top:10px;border-top-width:3px}}
36
+ @media print{.loader,.ed-mast,.ed-stages,.ed-settings,.ed-skip,.ed-export-actions,.ed-outputs button,.ed-captures button,.ed-contours{display:none!important}body{background:#fff;color:#191919}.poster.dijiang-console{padding:0;min-height:0}.ed-shell{padding:0}.ed-ledger.delivery-manifest{display:block}.ed-diff-panel[hidden]{display:block!important}.ed-diff-code,.ed-receipts pre{max-height:none}.ed-side{display:block}.ed-meta{border:0;padding:0}.ed-comparison{display:none}.ed-captures{grid-template-columns:1fr}*{animation:none!important;transition:none!important}}
37
+ `;
package/plugin/index.d.ts CHANGED
@@ -1,18 +1,31 @@
1
1
  export const name: string;
2
2
  export const inject: string[];
3
3
  export type ShowcaseTheme = "frontier-signal" | "blue-big-fish";
4
+ export interface VolatileRef<T> {
5
+ get(): T;
6
+ }
4
7
  export type ShowcaseSettings = {
5
- theme: ShowcaseTheme;
6
- generatePoster: boolean;
8
+ theme: VolatileRef<ShowcaseTheme>;
9
+ generatePoster: VolatileRef<boolean>;
10
+ };
11
+ export type ShowcaseSettingsInput = {
12
+ theme?: ShowcaseTheme;
13
+ generatePoster?: boolean;
7
14
  };
8
15
  export const SETTINGS_NAMESPACE: string;
9
- export const SETTINGS_RPC_CHANNEL: string;
10
16
  export const Config: {
11
- (value?: { theme?: ShowcaseTheme; generatePoster?: boolean }): ShowcaseSettings;
17
+ (value?: ShowcaseSettingsInput): ShowcaseSettings;
12
18
  toJSON(): unknown;
13
19
  };
14
20
  export function apply(ctx: {
15
- connection: { rpc: { handle(channel: string, handler: (endpoint: string, payload: unknown) => Promise<unknown>, options: { authority: "loopback" }): unknown } };
16
- settings: { register(namespace: string, schema: unknown, options: { base: ShowcaseSettings }): { get(): ShowcaseSettings; update(patch: Partial<ShowcaseSettings>): Promise<void> } };
21
+ connection?: unknown;
22
+ settings: {
23
+ describe?(): Array<{ ns: string; value?: Partial<Record<"theme" | "generatePoster", unknown>>; revision?: number }>;
24
+ update?(namespace: string, patch: ShowcaseSettingsInput, expectedRevision?: number): Promise<void>;
25
+ register?(namespace: string, schema: unknown, options: { base: ShowcaseSettingsInput }): { get(): ShowcaseSettingsInput; update(patch: ShowcaseSettingsInput): Promise<void> };
26
+ };
17
27
  tools: { register(definition: unknown): void };
18
- }, config?: { theme?: ShowcaseTheme; generatePoster?: boolean }): void;
28
+ inject?: (services: string[], apply: (ctx: { settings: { configure(options: { auto?: boolean }, owner?: unknown): () => void } }) => unknown) => unknown;
29
+ fiber?: unknown;
30
+ effect?: (callback: () => unknown, label?: string) => unknown;
31
+ }, config?: ShowcaseSettingsInput | ShowcaseSettings): void;