mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
feat(desktop): artifact evolution chain + Board/Timeline views
Adds a version-evolution capability to the Artifacts pane:
- Evolution chain: LCS-based diff across successive versions of the
same logical artifact; when a blob is missing, the chain honestly
surfaces a placeholder rather than fabricating diff content.
- Board / Timeline tab switcher on the Artifacts page.
- Auto-grouping into 5 kinds so the Board reads at a glance.
Files:
src/renderer/artifacts-board.js — new Board / Timeline / Evolution views
src/renderer/artifacts.js — wire Board tab + evolution rail
src/renderer/index.html — script tag + tab markup
src/renderer/style.css — tail append (~200 lines)
test/artifact-evolution-board.test.js — 23 new tests
docs/artifact-board-fixture.json — fixture seed
docs/qa-artifact-evolution/ — 3 QA screenshots + fixture.html
scripts/qa-cdp-shoot-artifact-v2.mjs — QA capture harness
Merged as source-repo test-real @ 7279870 (with lane-side conflict
resolution against test-real @ c1d93aa: style.css tail-append union;
independent of Lanes C/A/D CSS sections).
Test suite: 1727/1727 pass (+23 over Lane C+A+D baseline of 1704).
Follow-up L-3 (in upstream ledger): runtime ArtifactServer to grow
a snapshot store so the "missing blob" fallback becomes rare.
This commit is contained in:
BIN
examples/desktop/docs/qa-artifact-evolution/01-list.png
Normal file
BIN
examples/desktop/docs/qa-artifact-evolution/01-list.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
examples/desktop/docs/qa-artifact-evolution/02-board.png
Normal file
BIN
examples/desktop/docs/qa-artifact-evolution/02-board.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
BIN
examples/desktop/docs/qa-artifact-evolution/03-evolution.png
Normal file
BIN
examples/desktop/docs/qa-artifact-evolution/03-evolution.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 132 KiB |
99
examples/desktop/docs/qa-artifact-evolution/fixture.html
Normal file
99
examples/desktop/docs/qa-artifact-evolution/fixture.html
Normal file
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Reproducible fixture for the lane-artifact-v2 change (version
|
||||
evolution strip + Board / Timeline views). Loads the fixture data
|
||||
from docs/artifact-board-fixture.json and replays every entry
|
||||
through window.__cb (the onArtifact bridge) in order, so:
|
||||
* Auto-group + L0 rows populate the List view.
|
||||
* Multi-version entries (session.md v1→v3, landing.html v1→v2,
|
||||
config.json v1→v2) exercise the evolution strip diffs.
|
||||
* Every kind bucket (md/html/svg/json/code/other) surfaces in the
|
||||
Board view.
|
||||
Screenshots for docs/qa-artifact-evolution/ are taken with the
|
||||
panel in each of the three views + the evolution strip opened. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>lane-artifact-v2 fixture — Board + evolution</title>
|
||||
<link rel="stylesheet" href="../../src/renderer/style.css">
|
||||
<style>
|
||||
/* Bare-minimum host chrome. */
|
||||
html, body { height: 100%; margin: 0; }
|
||||
body { background: var(--bg); color: var(--text); font-family: system-ui, sans-serif; }
|
||||
.app { display: flex; height: 100vh; }
|
||||
.main { display: flex; flex-direction: column; flex: 1; min-width: 0; }
|
||||
.header {
|
||||
padding: 10px 16px; border-bottom: 1px solid var(--border);
|
||||
font-size: 13px; color: var(--muted);
|
||||
display: flex; gap: 8px; align-items: center;
|
||||
}
|
||||
.header .fixture-controls button {
|
||||
background: var(--bg-elev-2); color: var(--text);
|
||||
border: 1px solid var(--border); padding: 3px 10px;
|
||||
border-radius: 4px; font-size: 11px; cursor: pointer;
|
||||
}
|
||||
.header .fixture-controls button:hover { border-color: var(--accent); }
|
||||
#stream {
|
||||
flex: 1; padding: 12px; overflow-y: auto;
|
||||
display: flex; flex-direction: column; gap: 12px;
|
||||
}
|
||||
/* Silence the pulse + arrival flash so screenshots capture the
|
||||
resting state, matching the qa-artifact-compact convention. */
|
||||
.artifact-live-dot { animation: none !important; }
|
||||
.artifact-card.artifact-flash { border-color: var(--border) !important; background: var(--bg-elev) !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<span>Fixture: 3 multi-version + 4 single-version artifacts across 5 kinds</span>
|
||||
<span class="fixture-controls" style="margin-left: auto; display: flex; gap: 6px;">
|
||||
<button data-view="list">List</button>
|
||||
<button data-view="board">Board</button>
|
||||
<button data-view="timeline">Timeline</button>
|
||||
<button id="open-evolution">Open evolution: session.md</button>
|
||||
</span>
|
||||
</div>
|
||||
<div id="stream" class="stream"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Fake bridge — artifacts.js only touches window.dsh.onArtifact +
|
||||
window.dsh.openArtifact + window.dsh.mockArtifact. -->
|
||||
<script>
|
||||
window.dsh = {
|
||||
onArtifact: (cb) => { window.__cb = cb },
|
||||
openArtifact: async () => ({ ok: true }),
|
||||
mockArtifact: async () => {},
|
||||
}
|
||||
</script>
|
||||
<script src="../../src/renderer/artifacts-board.js"></script>
|
||||
<script src="../../src/renderer/artifacts.js"></script>
|
||||
<script>
|
||||
// Replay the fixture through the same onArtifact seam the real IPC
|
||||
// event uses. Because the artifacts.js history tracker de-dups on
|
||||
// version, feeding multi-version entries in order produces exactly
|
||||
// the same chain a real session would.
|
||||
async function main() {
|
||||
const res = await fetch('../artifact-board-fixture.json')
|
||||
const data = await res.json()
|
||||
for (const entry of data.artifacts) {
|
||||
window.__cb(entry)
|
||||
}
|
||||
// Wire the header buttons to the exposed switchView.
|
||||
const api = window.__dshArtifacts
|
||||
for (const btn of document.querySelectorAll('.fixture-controls button[data-view]')) {
|
||||
btn.addEventListener('click', () => api.switchView(btn.dataset.view))
|
||||
}
|
||||
// Convenience: pop the version chip on session.md so QA can
|
||||
// snap the evolution strip without a manual click search.
|
||||
document.getElementById('open-evolution').addEventListener('click', () => {
|
||||
const card = document.querySelector('.artifact-card[data-artifact-id="session.md"]')
|
||||
if (!card) return
|
||||
const chip = card.querySelector('.artifact-version')
|
||||
if (chip) chip.click()
|
||||
})
|
||||
}
|
||||
main()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user