fix(desktop): session Graph node order — sort by seq + repair turn/user reversal, node label previews
Also picks up two files the previous sync batch missed (test/artifact-compact-row.test.js, docs/qa-artifact-compact/).
This commit is contained in:
BIN
examples/desktop/docs/qa-artifact-compact/after-expanded.png
Normal file
BIN
examples/desktop/docs/qa-artifact-compact/after-expanded.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
examples/desktop/docs/qa-artifact-compact/after.png
Normal file
BIN
examples/desktop/docs/qa-artifact-compact/after.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
examples/desktop/docs/qa-artifact-compact/before.png
Normal file
BIN
examples/desktop/docs/qa-artifact-compact/before.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
105
examples/desktop/docs/qa-artifact-compact/fixture-before.html
Normal file
105
examples/desktop/docs/qa-artifact-compact/fixture-before.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- "Before" reproduction for docs/qa-artifact-compact/ — mirrors the
|
||||
pre-2026-07-18 hero-card artifact shape. Inline copy of the old
|
||||
builder + the pre-fix CSS block so the before/after diff is
|
||||
screenshot-comparable without git bisect. Not shipped. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>artifact-compact fixture (BEFORE)</title>
|
||||
<link rel="stylesheet" href="../../src/renderer/style.css">
|
||||
<style>
|
||||
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);
|
||||
}
|
||||
#stream { flex: 1; }
|
||||
.artifact-live-dot { animation: none !important; }
|
||||
/* --- BEFORE styling (pre-fix): wide flex-row with hero button.
|
||||
This is a verbatim replay of the pre-fix style.css block that the
|
||||
compact-row change replaced. Kept out of the shipped stylesheet
|
||||
— this override only applies inside this fixture. */
|
||||
.artifact-card {
|
||||
align-self: flex-start; max-width: 780px; width: 100%;
|
||||
background: var(--bg-elev); border: 1px solid var(--border); border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
transition: border-color 0.4s, background 0.4s;
|
||||
}
|
||||
.artifact-icon { font-size: 22px; line-height: 1; flex: 0 0 auto; }
|
||||
.artifact-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
|
||||
.artifact-name {
|
||||
font-family: var(--mono); font-size: 12.5px; color: var(--text);
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.artifact-meta {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 11px; color: var(--muted);
|
||||
}
|
||||
.artifact-kind {
|
||||
text-transform: uppercase; letter-spacing: 0.05em;
|
||||
padding: 1px 6px; border-radius: 3px;
|
||||
background: var(--bg-elev-2); color: var(--muted);
|
||||
}
|
||||
.artifact-version { color: var(--accent); font-family: var(--mono); }
|
||||
.artifact-live-dot {
|
||||
width: 6px; height: 6px; border-radius: 50%; background: var(--ok);
|
||||
}
|
||||
.artifact-open {
|
||||
flex: 0 0 auto; font-size: 12px; padding: 6px 12px;
|
||||
background: var(--accent); color: white; border: none; border-radius: 6px; cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<div class="main">
|
||||
<div class="header">BEFORE: 8 markdown artifacts (hero-card shape)</div>
|
||||
<div id="stream" class="stream"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const entries = [
|
||||
{ artifactId: 'session.md', kind: 'md', version: 3, path: '/w/.artifacts/session.md' },
|
||||
{ artifactId: 'tools.md', kind: 'md', version: 1, path: '/w/.artifacts/tools.md' },
|
||||
{ artifactId: 'AGENTS.md', kind: 'md', version: 2, path: '/w/.artifacts/AGENTS.md' },
|
||||
{ artifactId: 'plan.md', kind: 'md', version: 5, path: '/w/.artifacts/plan.md' },
|
||||
{ artifactId: 'trace.md', kind: 'md', version: 1, path: '/w/.artifacts/trace.md' },
|
||||
{ artifactId: 'growth.md', kind: 'md', version: 7, path: '/w/.artifacts/growth.md' },
|
||||
{ artifactId: 'recap.md', kind: 'md', version: 2, path: '/w/.artifacts/recap.md' },
|
||||
{ artifactId: 'notes.md', kind: 'md', version: 4, path: '/w/.artifacts/notes.md' },
|
||||
]
|
||||
// Pre-fix DOM builder (replayed verbatim).
|
||||
const stream = document.getElementById('stream')
|
||||
for (const entry of entries) {
|
||||
const el = document.createElement('div')
|
||||
el.className = 'artifact-card'
|
||||
el.innerHTML = ''
|
||||
const icon = document.createElement('span')
|
||||
icon.className = 'artifact-icon'
|
||||
icon.textContent = '📄'
|
||||
const body = document.createElement('div')
|
||||
body.className = 'artifact-body'
|
||||
const name = document.createElement('div')
|
||||
name.className = 'artifact-name'
|
||||
name.textContent = entry.artifactId
|
||||
const meta = document.createElement('div')
|
||||
meta.className = 'artifact-meta'
|
||||
const kind = document.createElement('span'); kind.className = 'artifact-kind'; kind.textContent = entry.kind
|
||||
const ver = document.createElement('span'); ver.className = 'artifact-version'; ver.textContent = `v${entry.version}`
|
||||
const dot = document.createElement('span'); dot.className = 'artifact-live-dot'
|
||||
meta.append(kind, ver, dot)
|
||||
body.append(name, meta)
|
||||
const btn = document.createElement('button')
|
||||
btn.className = 'artifact-open primary'
|
||||
btn.textContent = 'Open in browser'
|
||||
el.append(icon, body, btn)
|
||||
stream.appendChild(el)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Same 8-artifact fixture, but the third row is opened so the L1
|
||||
body (path + ghost "Open in browser" button) is visible for the
|
||||
inline-expand selfie. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>artifact-compact fixture (expanded)</title>
|
||||
<link rel="stylesheet" href="../../src/renderer/style.css">
|
||||
<style>
|
||||
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); }
|
||||
#stream { flex: 1; }
|
||||
.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">Fixture: L1 body expanded on row 3</div>
|
||||
<div id="stream" class="stream"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
window.dsh = { onArtifact: (cb) => { window.__cb = cb }, openArtifact: async () => ({ ok: true }), mockArtifact: async () => {} }
|
||||
</script>
|
||||
<script src="../../src/renderer/artifacts.js"></script>
|
||||
<script>
|
||||
const entries = [
|
||||
{ artifactId: 'session.md', kind: 'md', version: 3, path: '/w/.artifacts/session.md' },
|
||||
{ artifactId: 'tools.md', kind: 'md', version: 1, path: '/w/.artifacts/tools.md' },
|
||||
{ artifactId: 'AGENTS.md', kind: 'md', version: 2, path: '/w/harness/dsh-demo-worktrees/lane-artifact-compact/.artifacts/AGENTS.md' },
|
||||
{ artifactId: 'plan.md', kind: 'md', version: 5, path: '/w/.artifacts/plan.md' },
|
||||
{ artifactId: 'trace.md', kind: 'md', version: 1, path: '/w/.artifacts/trace.md' },
|
||||
{ artifactId: 'growth.md', kind: 'md', version: 7, path: '/w/.artifacts/growth.md' },
|
||||
{ artifactId: 'recap.md', kind: 'md', version: 2, path: '/w/.artifacts/recap.md' },
|
||||
{ artifactId: 'notes.md', kind: 'md', version: 4, path: '/w/.artifacts/notes.md' },
|
||||
]
|
||||
setTimeout(() => {
|
||||
for (const e of entries) window.__cb(e)
|
||||
// Open the third artifact so the L1 body is captured.
|
||||
const c = document.querySelectorAll('.artifact-card')[2]
|
||||
if (c) c.open = true
|
||||
}, 20)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
63
examples/desktop/docs/qa-artifact-compact/fixture.html
Normal file
63
examples/desktop/docs/qa-artifact-compact/fixture.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Reproducible fixture for the artifact-card compact-row change.
|
||||
Mocks 8 md artifact events so the auto-group + L0 row can be shot in
|
||||
one screenshot without booting the whole Electron shell. Used only
|
||||
for docs/qa-artifact-compact/ selfies; not shipped. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>artifact-compact fixture</title>
|
||||
<link rel="stylesheet" href="../../src/renderer/style.css">
|
||||
<style>
|
||||
/* Bare-minimum host chrome: give the .stream a viewport-height body
|
||||
so scroll behaviour matches the real shell. */
|
||||
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);
|
||||
}
|
||||
#stream { flex: 1; }
|
||||
/* Silence the pulse + the arrival flash for a stable selfie so the
|
||||
real resting-state layout (auto-group fusion, muted colors) shows. */
|
||||
.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">Fixture: 8 markdown artifacts arriving in a row</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.js"></script>
|
||||
<script>
|
||||
// Fire 8 md artifact events, then a divergent HTML one for variety.
|
||||
const entries = [
|
||||
{ artifactId: 'session.md', kind: 'md', version: 3, path: '/w/.artifacts/session.md' },
|
||||
{ artifactId: 'tools.md', kind: 'md', version: 1, path: '/w/.artifacts/tools.md' },
|
||||
{ artifactId: 'AGENTS.md', kind: 'md', version: 2, path: '/w/.artifacts/AGENTS.md' },
|
||||
{ artifactId: 'plan.md', kind: 'md', version: 5, path: '/w/.artifacts/plan.md' },
|
||||
{ artifactId: 'trace.md', kind: 'md', version: 1, path: '/w/.artifacts/trace.md' },
|
||||
{ artifactId: 'growth.md', kind: 'md', version: 7, path: '/w/.artifacts/growth.md' },
|
||||
{ artifactId: 'recap.md', kind: 'md', version: 2, path: '/w/.artifacts/recap.md' },
|
||||
{ artifactId: 'notes.md', kind: 'md', version: 4, path: '/w/.artifacts/notes.md' },
|
||||
]
|
||||
setTimeout(() => {
|
||||
for (const e of entries) window.__cb(e)
|
||||
}, 20)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user