From fb78fddb47dd4250db202cc7781d34c97d37e488 Mon Sep 17 00:00:00 2001 From: colwill <62014134+colwill@users.noreply.github.com> Date: Sun, 4 Oct 2026 01:36:15 +0100 Subject: [PATCH 1/2] chore: qol fixes for visualiser --- README.md | 13 +- assets/vis/vis.css | 279 +++++++-- assets/vis/vis.html | 17 +- assets/vis/vis.js | 1322 ++++++++++++++++++++++++++++++++++++++----- docs/EXTENSION.md | 3 +- docs/MCP.md | 4 +- src/inspect.rs | 471 +++++++++++++++ src/lib.rs | 1 + src/prompts.rs | 66 ++- src/replay.rs | 51 +- src/scan.rs | 20 +- src/serve.rs | 531 ++++++++++++----- src/vis.rs | 10 +- 13 files changed, 2442 insertions(+), 346 deletions(-) create mode 100644 src/inspect.rs diff --git a/README.md b/README.md index 1a9050b..2df2445 100644 --- a/README.md +++ b/README.md @@ -189,11 +189,18 @@ as in the editor: - **A definition at a time.** An edit is split into the definitions it touches - imports first, then what it adds, then what it changes, then what it removes - so a replay builds the change up one function, type or constant at a time, in the order the agent staged it. -- **Looks too.** `find`, `references`, `file` and `dependencies` calls land as looks at what they - named, so you can see what an agent read before it wrote anything. +- **Inspects too.** What an agent reads lands as an inspect of what it read, so you can see what + it looked at before it wrote anything: `find`, `references`, `file` and `dependencies` calls as + ccc answers them, and its own reads - Claude Code's `Read`, a `cat`, `sed -n` or `grep` of a + file in its shell, a subagent's too - from its transcript, as it writes them. +- **Follow live changes.** The switch on the right is green while each change is shown as it lands + (`L` turns it on and off). While it is on, the step controls fold away to the left behind `»`, + and they open again by themselves once it is off. - **At your pace.** Live keeps the time between steps as it was; ½×, ¼×, ⅛× and ⅒× stretch it, and following runs behind by however many steps are still waiting. -- **One step on its own.** While a step is on show (◎), what it did not touch fades back, and a big +- **Big views in groups.** A view of 50 or more components or functions is laid out a group at a + time +- **Granular Changes.** While a step is on show (◎), what it did not touch fades back, and a big scene draws only what is wired to it, laid out together to fit the screen. Click the canvas to bring the rest back. diff --git a/assets/vis/vis.css b/assets/vis/vis.css index 2ba7ddd..d38bfea 100644 --- a/assets/vis/vis.css +++ b/assets/vis/vis.css @@ -3,6 +3,7 @@ purple for selection, slate lines. Dark is derived from the same roles. */ :root { + --foot: 12px; --paper: #fbfcfd; --grid: rgba(0, 0, 0, .06); --surface: #ffffff; @@ -254,8 +255,8 @@ button, input { font: inherit; color: inherit; } background-image: linear-gradient(to right, var(--grid) 1px, transparent 1px), linear-gradient(to bottom, var(--grid) 1px, transparent 1px); - background-size: calc(20px * var(--zoom, 1)) calc(20px * var(--zoom, 1)); - background-position: calc(var(--pan-x, 0) * 1px) calc(var(--pan-y, 0) * 1px); + /* the grid's size and position follow the camera - see `applyView` */ + background-size: 20px 20px; touch-action: none; } .viewport.panning { cursor: grabbing; } @@ -279,7 +280,8 @@ button, input { font: inherit; color: inherit; } left: 0; top: 0; transform-origin: 0 0; - transform: translate(calc(var(--pan-x, 0) * 1px), calc(var(--pan-y, 0) * 1px)) scale(var(--zoom, 1)); + /* moved as one layer, so a pan or zoom repaints none of what it holds */ + will-change: transform; } .surface > * { position: absolute; left: 0; top: 0; } .wires { overflow: visible; width: 1px; height: 1px; pointer-events: none; } @@ -304,6 +306,44 @@ button, input { font: inherit; color: inherit; } .frame.group { border-style: solid; border-width: 1px; border-radius: 10px; } .frame.group > .title { top: 8px; bottom: auto; font-size: 12px; font-weight: 600; } +.group-card { + position: absolute; + box-sizing: border-box; + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + gap: .35em; + padding: 1em 1.4em; + font-size: min(calc(13px * var(--gz, 1)), calc(var(--w, 400px) / 16), calc(var(--h, 300px) / 7)); + text-align: center; + background: var(--surface); + border: calc(2px * var(--gz, 1)) solid var(--border); + border-radius: calc(12px * var(--gz, 1)); + box-shadow: var(--shadow); + cursor: zoom-in; + overflow: hidden; + transition: border-color .15s; +} +.group-card:hover { border-color: var(--primary); } +.group-card .g-what { font-size: .8em; letter-spacing: .06em; color: var(--muted); overflow-wrap: anywhere; } +.group-card .g-title { font-size: 1.8em; font-weight: 650; line-height: 1.15; color: var(--text); overflow-wrap: anywhere; } +.group-card .g-sub { color: var(--muted); } +.group-card .g-mark { margin-top: .3em; padding: .15em .8em; border-radius: 1em; background: var(--chg); color: #fff; font-size: .9em; } +.group-card .g-mark[hidden] { display: none; } +.group-card.chg { + --chg: var(--primary); + border-color: var(--chg); + box-shadow: var(--shadow), 0 0 0 calc(5px * var(--gz, 1)) color-mix(in srgb, var(--chg) 28%, transparent); +} +.group-card.chg-added { --chg: var(--green); } +.group-card.chg-removed { --chg: var(--red); } +.group-card.chg-inspect { --chg: var(--blue); } +.group-wires path { pointer-events: none; } +.group-wires .gwire { fill: none; stroke: var(--edge); } +.group-wires .ghead { fill: var(--edge); } +.group-wires text { fill: var(--muted); stroke: var(--paper); paint-order: stroke; text-anchor: middle; dominant-baseline: central; font-weight: 600; } + /* ---------- C4 nodes (doc canvas cards) ---------- */ .node { @@ -324,10 +364,12 @@ button, input { font: inherit; color: inherit; } .node.dragging { box-shadow: var(--shadow-lift); cursor: grabbing; z-index: 5; transition: none; } .node.dim { opacity: .35; } .node.hit { box-shadow: var(--shadow), 0 0 0 4px var(--primary-glow); border-color: var(--primary); } +/* a node's insides never move the layout around it */ +.node { contain: layout style; } -.card { width: 240px; padding: 10px 12px 10px 16px; } +.card { width: 240px; padding: 10px 12px calc(10px + var(--foot)) 16px; } .card.wide { width: 300px; } -.card.system { width: 340px; padding: 16px 18px 16px 22px; } +.card.system { width: 340px; padding: 16px 18px calc(16px + var(--foot)) 22px; } .card .stripe { position: absolute; left: -2px; @@ -389,7 +431,7 @@ button, input { font: inherit; color: inherit; } pointer-events: auto; cursor: pointer; } -.elabel.dim { opacity: .3; } +.dimming .elabel:not(.hot) { opacity: .3; } .elabel.declared { border-style: dashed; } /* ---------- wires ---------- */ @@ -399,7 +441,12 @@ button, input { font: inherit; color: inherit; } .wire.data { stroke-width: 1.5; stroke-dasharray: 4 3; } .wire.back { stroke-dasharray: 6 4; opacity: .6; } .wire.declared { stroke-dasharray: 6 4; } -.wire.dim { opacity: .2; } +/* the scene's wires dimmed as one drawing, the lit ones copied above it - see `highlight` */ +#wires { transition: opacity .12s; } +#wires.dimming { opacity: .2; } +.wires.lit path { pointer-events: none; } +/* what the collection on show added stays green while it is on show - see `showKept` */ +.wire.kept-new { stroke: var(--green); } .wire.hot { stroke: var(--primary); stroke-width: 2.75; opacity: 1; } .arrowhead { fill: var(--edge); } .arrowhead.hot { fill: var(--primary); } @@ -409,7 +456,7 @@ button, input { font: inherit; color: inherit; } .bp { min-width: 170px; max-width: 340px; - padding: 0 0 6px; + padding: 0 0 calc(6px + var(--foot)); overflow: visible; } .bp .head { @@ -449,6 +496,8 @@ button, input { font: inherit; color: inherit; } white-space: nowrap; } .bp .pin.in { grid-column: 1; } +/* rules what a function returns off from what it takes */ +.bp .rows .sep { grid-column: 1 / -1; border-top: 1px solid var(--edge); margin: 4px 0; } .bp .pin.out { grid-column: 2; justify-content: flex-end; text-align: right; } .bp .pin .lbl { overflow: hidden; text-overflow: ellipsis; max-width: 170px; } .bp .pin.data .lbl { font-family: var(--mono); font-size: 11px; color: var(--muted); } @@ -472,7 +521,7 @@ button, input { font: inherit; color: inherit; } .bp .pin.in i { margin-left: -18px; } .bp .pin.out i { margin-right: -18px; order: 2; } .bp .body { padding: 2px 12px 2px; font: 12px var(--mono); color: var(--muted); overflow-wrap: anywhere; max-width: 320px; } -.bp.compact { min-width: 0; border-radius: 16px; padding: 0; } +.bp.compact { min-width: 0; border-radius: 16px; padding: 0 0 var(--foot); } .bp.compact .rows { display: flex; align-items: center; } .bp.compact .pin { padding: 0 10px; min-height: 28px; } .bp.compact .chain { font: 11px var(--mono); color: var(--muted); padding: 5px 0; max-width: 260px; overflow-wrap: anywhere; } @@ -642,13 +691,22 @@ button, input { font: inherit; color: inherit; } } .message[hidden] { display: none; } .message b { color: var(--text); display: block; margin-bottom: 4px; } -.note-bar { +/* the view's note, and under it the step on show's, centred at the top */ +.top-notes { position: fixed; left: 50%; top: 64px; z-index: 12; transform: translateX(-50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; max-width: 70vw; + pointer-events: none; +} +.top-notes > * { pointer-events: auto; max-width: 100%; } +.note-bar { padding: 6px 12px; background: var(--surface); border: 1px solid var(--border); @@ -665,15 +723,36 @@ button, input { font: inherit; color: inherit; } written yet is dashed amber */ .node.chg { --chg: var(--primary); + --chg-label: 'Modified'; border-color: var(--chg); box-shadow: var(--shadow), 0 0 0 3px color-mix(in srgb, var(--chg) 30%, transparent); animation: chg-pulse 1.4s ease-out 2; } -.node.chg-added { --chg: var(--green); } -.node.chg-removed { --chg: var(--red); opacity: .85; } -.node.chg-pending { --chg: var(--orange); border-style: dashed; } +.node.chg-added { --chg: var(--green); --chg-label: 'Added'; } +.node.chg-removed { --chg: var(--red); --chg-label: 'Removed'; opacity: .85; } +.node.chg-pending { --chg: var(--orange); --chg-label: 'Proposed'; border-style: dashed; } /* what an agent looked at, rather than changed */ -.node.chg-read { --chg: var(--blue); } +.node.chg-inspect { --chg: var(--blue); --chg-label: 'Inspected'; } +/* what happened to a node, in a word on a label across its foot */ +.node.chg::after, +.node.kept-new::after, +.node.leaving::after { + content: var(--chg-label, ''); + position: absolute; + left: 50%; + bottom: 0; + transform: translate(-50%, 50%); + padding: 1px 12px 2px; + border: 1.5px solid var(--chg); + border-radius: 7px; + background: color-mix(in srgb, var(--chg) 16%, var(--surface)); + color: color-mix(in srgb, var(--chg) 85%, var(--text)); + font: 600 12px/1.35 var(--font); + letter-spacing: .02em; + white-space: nowrap; + pointer-events: none; + z-index: 3; +} @keyframes chg-pulse { 0% { box-shadow: var(--shadow), 0 0 0 0 color-mix(in srgb, var(--chg) 60%, transparent); } 70% { box-shadow: var(--shadow), 0 0 0 16px color-mix(in srgb, var(--chg) 0%, transparent); } @@ -682,6 +761,78 @@ button, input { font: inherit; color: inherit; } @media (prefers-reduced-motion: reduce) { .node.chg { animation: none; } .viewport.glide .surface { transition: none !important; } + .node.arrived, .pin.stamp, .pin.stamp::after, .pin.stamp .lbl, .pin.stamp i, .txt-change, .wire.wire-new { animation: none !important; } +} + +/* ---------- motion: see `morph` and `travel` ---------- */ +/* the view left behind, lifted off as one picture */ +.ghost { position: absolute; left: 0; top: 0; transform-origin: 0 0; pointer-events: none; will-change: transform, opacity; } +.ghost > * { position: absolute; left: 0; top: 0; } +.node.leaving { --chg: var(--red); --chg-label: 'Removed'; pointer-events: none; border-color: var(--red); } +/* a node that came into a view drawn again */ +.node.arrived:not(.chg) { animation: arrived 1.2s ease-out 1; } +@keyframes arrived { + 0%, 30% { box-shadow: var(--shadow), 0 0 0 3px color-mix(in srgb, var(--green) 55%, transparent); border-color: var(--green); } +} +.node.kept-new:not(.chg) { --chg: var(--green); --chg-label: 'Added'; border-color: var(--green); } +/* a node the ask added, marked by a step that reached it, is an addition still */ +.node.kept-new.chg-modified:not(.chg-pending) { --chg: var(--green); --chg-label: 'Added'; } +.bp .pin.kept-new { background: color-mix(in srgb, var(--green) 14%, transparent); } +.bp .pin.kept-new .lbl { color: color-mix(in srgb, var(--green) 75%, var(--text)); } +/* A pin that came, or whose words changed, stamped onto its node - see + `stamp`: the words land from above and press in, the pin's dot pops on, + and a ring of ink spreads from where it struck - green for one that came, + amber for one that changed - as the mark it leaves fades. */ +.bp .pin.stamp { --ink: var(--green); animation: stamp-mark 1.5s ease-out var(--stamp-at, 0s) 1 backwards; } +.bp .pin.stamp.changed { --ink: var(--orange); } +.bp .pin.stamp .lbl { animation: stamp-press .5s cubic-bezier(.3, 1.5, .55, 1) var(--stamp-at, 0s) 1 backwards; } +.bp .pin.stamp i { animation: stamp-dot .35s cubic-bezier(.3, 1.6, .55, 1) var(--stamp-at, 0s) 1 backwards; } +.bp .pin.stamp::after { + content: ''; + position: absolute; + inset: 1px 8px; + border: 2px solid var(--ink); + border-radius: 5px; + opacity: 0; + pointer-events: none; + animation: stamp-ink .75s ease-out calc(var(--stamp-at, 0s) + .2s) 1; +} +@keyframes stamp-press { + 0% { opacity: 0; transform: scale(1.9) translateY(-3px); filter: blur(1.5px); } + 55% { opacity: 1; transform: scale(.92); filter: none; } + 80% { transform: scale(1.03); } +} +@keyframes stamp-dot { + 0% { transform: scale(0); } +} +@keyframes stamp-ink { + 0% { opacity: .95; transform: scale(.94); } + 100% { opacity: 0; transform: scale(1.16, 1.5); } +} +@keyframes stamp-mark { + 0% { background: transparent; } + 20%, 45% { background: color-mix(in srgb, var(--ink) 24%, transparent); } +} +@keyframes words-change { + 0%, 35% { background: color-mix(in srgb, var(--orange) 28%, transparent); } +} +.bp .pin .lbl-was { + position: absolute; + font: 11px var(--mono); + color: var(--red); + text-decoration: line-through; + white-space: nowrap; + pointer-events: none; +} +.bp .pin.pin-out { position: absolute; pointer-events: none; } +.bp .pin.pin-out .lbl { color: var(--red); text-decoration: line-through; } +.txt-change { animation: words-change 1.2s ease-out 1; border-radius: 3px; } +/* the wires of a view drawn again: the old fade, a gone one red, a new one green */ +.ghost-wires .wire { opacity: .7; } +.ghost-wires .wire.gone { stroke: var(--red); opacity: 1; } +.wire.wire-new { animation: wire-new 1.6s ease-out 1; } +@keyframes wire-new { + 0%, 45% { stroke: var(--green); } } .viewport.glide .surface { transition: transform .6s cubic-bezier(.2, .7, .2, 1); } .viewport.glide { transition: background-size .6s, background-position .6s; } @@ -707,11 +858,17 @@ button, input { font: inherit; color: inherit; } /* no edits yet: only the switches for what comes stay, in the corner */ .timeline.empty { left: auto; } .timeline.empty > :not(.tl-ws):not(.tl-follow):not(.tl-speed):not(.tl-focus) { display: none; } -body.with-tl-switches .zoom { bottom: 62px; } -body.with-timeline .legend { bottom: 62px; } -body.with-timeline .zoom { bottom: 62px; } -body.with-timeline .panel { bottom: 62px; } -.tl-controls { display: flex; gap: 2px; } +/* what sits above the timeline keeps clear of it, however many rows it wraps + to - `--tl-top` is how far its top is from the window's foot */ +body.with-tl-switches .zoom, +body.with-timeline .legend, +body.with-timeline .zoom, +body.with-timeline .panel { bottom: calc(var(--tl-top, 52px) + 10px); } +.tl-controls { display: flex; flex: none; gap: 2px; max-width: 120px; overflow: hidden; transition: max-width .2s ease, opacity .2s ease; } +/* following, folded away to the left - the flex gap after them goes too */ +.tl-controls.folded { max-width: 0; opacity: 0; margin-right: -8px; } +.timeline .tl-unfold { min-width: 22px; padding: 0 4px; color: var(--muted); font-weight: 400; } +.timeline .tl-unfold[hidden] { display: none; } .timeline button { height: 28px; min-width: 28px; @@ -723,6 +880,8 @@ body.with-timeline .panel { bottom: 62px; } font-weight: 600; cursor: pointer; white-space: nowrap; + /* a switch keeps the width its words need - the track, the ask and the step give way instead */ + flex: none; } .timeline button:hover:not(:disabled) { background: var(--hover); color: var(--tool-active); } .timeline button:disabled { opacity: .4; cursor: default; } @@ -745,7 +904,7 @@ body.with-timeline .panel { bottom: 62px; } .tl-ticks .tick.reverted { background: var(--red); } .tl-ticks .tick.discarded { background: var(--grey); opacity: .45; } .tl-ticks .tick.edited { background: var(--blue); } -.tl-ticks .tick.read { background: var(--primary); opacity: .4; height: 30%; } +.tl-ticks .tick.inspect { background: var(--primary); opacity: .4; height: 30%; } .tl-ticks .tick.at { height: 100%; opacity: 1; box-shadow: 0 0 0 2px var(--primary); } /* a change made by hand is a little person: the tick its body, this its head */ .tl-ticks .tick.human { position: relative; height: 40%; } @@ -771,7 +930,6 @@ body.with-timeline .panel { bottom: 62px; } .tl-step .person, .replay-badge .person, .tl-ask .person, .ask-row .person { color: var(--blue); } .tl-ask .glyph, .ask-row .glyph { color: var(--primary); } /* where one ask's steps end and the next one's begin */ -.tl-ticks .tl-sep { flex: none; align-self: stretch; width: 2px; margin: 0 2px; border-radius: 1px; background: var(--border); } .timeline .tl-ask { flex: 0 1 auto; min-width: 0; @@ -783,6 +941,8 @@ body.with-timeline .panel { bottom: 62px; } font-weight: 400; font-size: 12px; } +.timeline .tl-ask:not([hidden]) { display: inline-flex; align-items: center; gap: 6px; } +.timeline .tl-ask .ask-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .timeline .tl-ask[hidden] { display: none; } .panel .ask-session { display: flex; @@ -822,6 +982,7 @@ body.with-timeline .panel { bottom: 62px; } overflow: hidden; } .panel .ask-n { flex: none; color: var(--muted); font-size: 11px; } +.timeline .tl-ask .ask-n { flex: none; color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; } .panel .ask-play { flex: none; border: 0; background: none; color: var(--tool); cursor: pointer; } .panel .ask-play:hover { color: var(--primary); } .timeline .tl-step { @@ -835,18 +996,56 @@ body.with-timeline .panel { bottom: 62px; } color: var(--muted); } .timeline .tl-step:disabled { opacity: 1; } -.tl-side { display: flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; } +.tl-side { display: flex; flex: none; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; } .tl-side[hidden] { display: none; } .timeline .tl-side button { border-radius: 0; font-weight: 400; font-size: 12px; } .timeline .tl-side button[aria-pressed="true"] { background: var(--primary-soft); color: var(--primary); } -.timeline .tl-follow { border: 1px solid var(--border); border-radius: 14px; font-weight: 400; } -.timeline .tl-follow[aria-pressed="true"] { border-color: var(--primary); color: var(--primary); background: var(--primary-soft); } +/* the follow switch: green while it follows live changes, grey while it does not */ +.timeline .tl-follow { + display: inline-flex; + align-items: center; + gap: 7px; + padding: 0 10px 0 6px; + border: 1px solid var(--border); + border-radius: 14px; + font-weight: 400; + color: var(--muted); +} +.tl-follow .switch { + position: relative; + flex: none; + width: 26px; + height: 15px; + border-radius: 8px; + background: var(--grey); + opacity: .6; + transition: background .15s, opacity .15s; +} +.tl-follow .switch::after { + content: ''; + position: absolute; + top: 2px; + left: 2px; + width: 11px; + height: 11px; + border-radius: 50%; + background: #fff; + box-shadow: 0 1px 2px rgb(0 0 0 / .25); + transition: transform .15s; +} +.timeline .tl-follow[aria-checked="true"] { + border-color: var(--green); + color: color-mix(in srgb, var(--green) 80%, var(--text)); + background: color-mix(in srgb, var(--green) 12%, transparent); +} +.timeline .tl-follow[aria-checked="true"]:hover:not(:disabled) { + color: color-mix(in srgb, var(--green) 80%, var(--text)); + background: color-mix(in srgb, var(--green) 20%, transparent); +} +.tl-follow[aria-checked="true"] .switch { background: var(--green); opacity: 1; } +.tl-follow[aria-checked="true"] .switch::after { transform: translateX(11px); } .replay-badge { - position: fixed; - left: 12px; - top: 64px; - z-index: 12; padding: 4px 10px; border: 1px solid var(--orange); border-radius: 14px; @@ -854,12 +1053,12 @@ body.with-timeline .panel { bottom: 62px; } color: var(--text); font-size: 12px; box-shadow: var(--shadow-bar); - max-width: calc(100vw - 24px); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .replay-badge[hidden] { display: none; } +.replay-badge .off-map { color: var(--muted); } .panel .chg-row { display: flex; align-items: baseline; gap: 8px; } .panel .chg-tag { @@ -872,7 +1071,7 @@ body.with-timeline .panel { bottom: 62px; } } .panel .chg-tag.added { background: var(--green); } .panel .chg-tag.removed { background: var(--red); } -.panel .chg-tag.read { background: var(--blue); } +.panel .chg-tag.inspect { background: var(--blue); } .panel .diff-file { margin: 8px 0 2px; font: 12px var(--mono); } .panel .diff-file span { color: var(--muted); } .panel .diff pre { @@ -894,8 +1093,7 @@ body.with-timeline .panel { bottom: 62px; } .search input { width: 160px; } .panel { width: auto; left: 12px; top: auto; height: 45vh; } .panel:not([hidden]) ~ .zoom { right: 12px; bottom: calc(45vh + 24px); } - body.with-timeline .panel { bottom: 62px; } - body.with-timeline .panel:not([hidden]) ~ .zoom { bottom: calc(45vh + 74px); } + body.with-timeline .panel:not([hidden]) ~ .zoom { bottom: calc(45vh + var(--tl-top, 52px) + 22px); } } /* a VS Code side bar: one column - the crumbs get a row of their own above @@ -910,11 +1108,22 @@ body.with-timeline .panel { bottom: 62px; } .search svg { top: 8px; } .tools .chip { padding: 0 6px; font-size: 11px; } .viewport { top: 84px; } - .panel, .note-bar, .replay-badge { top: 94px; } + .panel, .top-notes { top: 94px; } + .panel { left: 6px; right: 6px; } +} + +/* too narrow for the track beside every switch: it takes a row of its own, + rather than a switch being pushed out of the bar */ +@media (max-width: 720px) { .timeline { flex-wrap: wrap; left: 6px; right: 6px; bottom: 6px; } .tl-track { order: 5; flex-basis: 100%; } .timeline .tl-step { flex: 1 1 0; max-width: none; } .timeline .tl-ask { max-width: 12ch; } - body.with-timeline .zoom, body.with-timeline .panel { bottom: 88px; } - .panel { left: 6px; right: 6px; } + /* `Following · 12`, the word left to the tooltip */ + .tl-follow .tl-behind { display: none; } +} + +@media (max-width: 560px) { + /* `Follow`, the rest of its name left to the tooltip */ + .tl-follow .tl-follow-more { display: none; } } diff --git a/assets/vis/vis.html b/assets/vis/vis.html index d43e3aa..4d2db14 100644 --- a/assets/vis/vis.html +++ b/assets/vis/vis.html @@ -37,8 +37,11 @@ +
+
+ @@ -58,12 +61,16 @@
- + +
+ + +
- diff --git a/assets/vis/vis.js b/assets/vis/vis.js index e1d0b68..fbbaee2 100644 --- a/assets/vis/vis.js +++ b/assets/vis/vis.js @@ -133,7 +133,7 @@ const timer = setTimeout(() => { waiting.delete(id); reject(new Error('the extension did not answer')); - }, 30000); + }, 65000); waiting.set(id, m => { clearTimeout(timer); resolve(m); @@ -270,8 +270,12 @@ } } - // the live map at the route in the address - any replay ends here - async function render(keepView) { + // a level that timed out, rather than one that could not be drawn + const busy = e => /did not answer|timed out/.test(e.message || ''); + + // the live map at the route in the address - any replay ends here; a level + // the analyser was too busy to answer for is asked for once more + async function render(keepView, again) { const seq = ++renderSeq; endReplay(); const route = parseRoute(); @@ -303,6 +307,11 @@ } catch (e) { if (seq !== renderSeq) return; clearCanvas(); + if (!again && busy(e)) { + showMessage('Still drawing this level', 'the analyser is busy - asking again'); + setTimeout(() => seq === renderSeq && render(keepView, true), 1500); + return; + } showMessage('Could not draw this level', e.message); } crumbs(); @@ -353,7 +362,7 @@ gap.style.visibility = 'hidden'; return gap; } - const e = h('div', `pin ${side} ${p.data ? 'data' : p.more ? 'more' : 'exec'}`); + const e = h('div', `pin ${side} ${p.data ? 'data' : p.more ? 'more' : 'exec'}${p.ret ? ' ret' : ''}`); if (p.pin) e.dataset.pin = p.pin; if (p.colour) e.style.setProperty('--pin', p.colour); if (p.title) e.title = p.title; @@ -361,8 +370,12 @@ return e; } + // the pin for what a function returns + const returnsPin = (type, pin) => ({ ret: true, data: true, label: 'Returns ' + type, title: 'returns ' + type, colour: typeColour(type), pin }); + // a node in the blueprint style: a header in the node's colour, then rows of - // input pins on the left and output pins on the right + // input pins on the left and output pins on the right - what it returns + // last, ruled off from the rest function bp({ accent, k, t, q, badges, ins, outs, body, cls }) { const n = h('div', 'node bp' + (cls ? ' ' + cls : '')); n.style.setProperty('--accent', accent || 'var(--grey)'); @@ -379,8 +392,13 @@ if (body) n.append(h('div', 'body', body)); const rows = h('div', 'rows'); const left = ins || []; - const right = outs || []; + const right = (outs || []).filter(p => !p.ret); + const returns = (outs || []).filter(p => p.ret); for (let i = 0; i < Math.max(left.length, right.length); i++) rows.append(pinEl(left[i], 'in'), pinEl(right[i], 'out')); + if (returns.length) { + rows.append(h('div', 'sep')); + for (const p of returns) rows.append(pinEl(null, 'in'), pinEl(p, 'out')); + } n.append(rows); return n; } @@ -660,7 +678,7 @@ return { pin: 'c' + c.to, label: target ? target.name : '?', title: `line ${c.line}` }; }); if (f.calls_total > calls.length) outs.push({ more: true, label: `+${f.calls_total - calls.length} more` }); - if (f.ret) outs.push({ data: true, label: f.ret, colour: typeColour(f.ret), pin: 'ret' }); + if (f.ret) outs.push(returnsPin(f.ret, 'ret')); const badges = []; if (f.entry && !f.module && !f.test) badges.push(badge('mode entry', 'entry', 'nothing else in the map calls it')); if (f.recursive) badges.push(badge('mode', '↻', 'calls itself')); @@ -737,8 +755,15 @@ const f = d.function; const nodes = []; let seq = 0; + // a step is known again in the next drawing by what it is, not where it + // falls - its kind and words, and which of the same it is + const seen = new Map(); const add = (el, kind, data, drill) => { - const n = { id: 'n' + seq++, kind, data, el, drill }; + const what = data ? `${data.qual ? data.qual + '.' : ''}${data.name || data.label || data.kind || ''}` : ''; + const base = `${kind}:${what}`; + const k = seen.get(base) || 0; + seen.set(base, k + 1); + const n = { id: 'n' + seq++, mkey: `${base}#${k}`, kind, data, el, drill }; nodes.push(n); return n; }; @@ -753,7 +778,7 @@ q: `${f.file}:${f.line}`, badges: entryBadges, body: d.callers_total ? `called by ${d.callers.slice(0, 3).map(c => c.name).join(', ')}${d.callers_total > 3 ? ` +${d.callers_total - 3}` : ''}` : f.module ? null : 'nothing in the map calls it', - outs: [{ pin: 'out', label: 'then' }, ...d.params.map((p, i) => ({ pin: 'p' + i, data: true, label: p, colour: typeColour(p.split(/[:\s]/).pop()) }))].concat(f.ret ? [{ data: true, label: '→ ' + f.ret, colour: typeColour(f.ret) }] : []), + outs: [{ pin: 'out', label: 'then' }, ...d.params.map((p, i) => ({ pin: 'p' + i, data: true, label: p, colour: typeColour(p.split(/[:\s]/).pop()) }))].concat(f.ret ? [returnsPin(f.ret)] : []), }), 'entry', { ...d, step: null } @@ -838,7 +863,7 @@ } const tree = blocks(d.steps); - const end = add(bp({ cls: 'terminal', accent: 'var(--faint)', k: 'end', t: f.ret ? 'Falls off the end' : 'End', ins: [{ pin: 'in', label: '' }] }), 'end', { t: 'end' }); + const end = add(bp({ cls: 'terminal', accent: 'var(--faint)', k: 'end', t: 'End of scope', ins: [{ pin: 'in', label: '' }] }), 'end', { t: 'end' }); return { key: 'flow', kind: 'bp', @@ -1024,6 +1049,401 @@ return result; } + // ------------------------------------------------------------- groups + + // a group of this many nodes or more splits into the groups within it, + // and a scene this big reads, zoomed out, as its groups + const GROUP_MIN = 50; + // zoomed out to this a group's nodes fold away into its card, and back in + // to this they return - a gap between, so it never flickers + const GROUP_HIDE = 0.4; + const GROUP_SHOW = 0.5; + // a group that holds groups opens into them once it covers this much of + // the screen, and folds again below this - and one opened by a click stays + // open until zoomed back out past where it fit + const OPEN_AT = 0.75; + const FOLD_AT = 0.6; + const HOLD_AT = 0.85; + // directories with fewer components than this, side by side, are one group + const GROUP_SMALL = 5; + // functions to a run of a file, about, when nothing else groups them + const FN_RUN = 30; + // room round a group's nodes for its outline - its title along the top + const GROUP_PAD = { left: 28, top: 40, right: 28, bottom: 28 }; + const MARKS = ['added', 'removed', 'modified', 'inspect']; + // the shape groups are packed into, about the screen's + const SHELF_ASPECT = 1.6; + // a bundle of wires is counted from this many calls + const BUNDLE_COUNTED = 5; + + // The groups a big scene's nodes fall into, each the context they share - + // a container's components by directory, a directory with too many in it + // split into the directories within it; a file's functions by the type + // that owns them - each before the groups within it. None for a scene + // small enough to read whole. + function groupScene(scene) { + const comps = scene.nodes.filter(n => n.kind === 'component'); + const fns = scene.nodes.filter(n => n.kind === 'function'); + let root; + let unit; + if (comps.length >= GROUP_MIN) [root, unit] = [dirTree(comps), 'component']; + else if (fns.length >= GROUP_MIN) { + const owner = n => (n.data.test ? 'tests' : n.data.module ? 'top level' : n.data.owner || 'free functions'); + const by = new Map(); + for (const n of fns) by.set(owner(n), [...(by.get(owner(n)) || []), n.id]); + const children = [...by].sort((a, b) => a[0].localeCompare(b[0])).map(([key, members]) => ({ key, title: key, path: '', members, children: [] })); + // A group of many with nothing more to tell them apart - a script's free + // functions - splits into runs of the file, a few dozen at a time, each + // called by the functions it runs from and to. + const fn = new Map(fns.map(n => [n.id, n])); + for (const c of children) { + if (c.members.length < GROUP_MIN) continue; + const inOrder = c.members.map(id => fn.get(id)).sort((a, b) => a.data.line - b.data.line); + const size = Math.ceil(inOrder.length / Math.ceil(inOrder.length / FN_RUN)); + for (let at = 0; at < inOrder.length; at += size) { + const run = inOrder.slice(at, at + size); + const [a, b] = [run[0].data, run[run.length - 1].data]; + c.children.push({ key: `${c.key} ${a.line}`, title: `${a.name} … ${b.name}`, path: `lines ${a.line}–${b.end || b.line}`, members: run.map(n => n.id), children: [] }); + } + } + [root, unit] = [{ key: 'functions', title: 'functions', path: '', members: fns.map(n => n.id), children }, 'function']; + } else return null; + // the whole scene one card only when nothing splits it + const tops = root.children.length ? root.children : [root]; + const all = []; + const chains = new Map(); + const walk = (c, up) => { + Object.assign(c, { i: all.length, parent: up[up.length - 1] || null, unit }); + all.push(c); + const chain = [...up, c]; + if (!c.children.length) for (const id of c.members) chains.set(id, chain); + for (const k of c.children) walk(k, chain); + }; + for (const t of tops) walk(t, []); + scene.tops = tops; + scene.chains = chains; + return all; + } + + // A container's components as a tree of their directories: one that holds + // only one directory is passed through, and one with too many components + // splits into the directories in it - its own files a group of their own. + function dirTree(nodes) { + const segs = new Map(nodes.map(n => [n.id, (n.data.dir || '').split('/').filter(Boolean)])); + const named = key => { + const cut = key.lastIndexOf('/'); + return { title: key.slice(cut + 1) || key, path: cut > 0 ? key.slice(0, cut + 1) : '' }; + }; + // `name` - the directory that sets the group apart from the ones beside + // it, which it is called by, however deep its own files sit below it + const build = (items, depth, name) => { + let d = depth; + while (items.every(n => segs.get(n.id).length > d) && new Set(items.map(n => segs.get(n.id)[d])).size === 1) d++; + const key = segs.get(items[0].id).slice(0, d).join('/') || '.'; + const c = { key, ...named(key), members: items.map(n => n.id), children: [] }; + if (name && name !== c.title) Object.assign(c, { title: name, path: key }); + if (items.length < GROUP_MIN) return c; + const by = new Map(); + for (const n of items) { + const s = segs.get(n.id)[d] ?? ''; + if (!by.has(s)) by.set(s, []); + by.get(s).push(n); + } + if (by.size < 2) return c; + const subs = [...by].sort((a, b) => a[0].localeCompare(b[0])); + // a run of little directories reads better as one group than as a card each + const small = subs.filter(([s, sub]) => s && sub.length < GROUP_SMALL); + const merged = small.length >= 2 ? new Set(small.map(([s]) => s)) : new Set(); + for (const [s, sub] of subs) { + if (merged.has(s)) continue; + c.children.push(s ? build(sub, d + 1, s) : { key: key + '/', title: c.title + ' files', path: key + '/', members: sub.map(n => n.id), children: [] }); + } + if (merged.size) { + const members = small.flatMap(([, sub]) => sub.map(n => n.id)); + c.children.push({ key: key + '/…', title: `${merged.size} smaller directories`, path: key + '/', members, children: [] }); + } + return c; + }; + return build(nodes, 0); + } + + // a group and the groups it is in, outermost first + function chainOf(g) { + const out = []; + for (let x = g; x; x = x.parent) out.unshift(x); + return out; + } + + // the classes a node or wire takes from the groups it is in - see `regroup` + const groupClasses = (scene, id) => ((scene.chains && scene.chains.get(id)) || []).map(g => 'cg-' + g.i); + + // is a node out of sight in a folded group + const hiddenByGroup = id => !!S.collapsed && ((S.scene.chains && S.scene.chains.get(id)) || []).some(g => S.collapsed.has(g.i)); + + // A big scene laid out a group at a time: each group's nodes together, the + // groups in a group laid out as nodes of their own, and so on up - so each + // group holds a region of its own, which folded is one overview card. + function layeredGroups(scene) { + const byId = new Map(scene.nodes.map(n => [n.id, n])); + const opts = { gapX: scene.gapX, gapY: scene.gapY, order: scene.order, maxCol: 8 }; + const padW = GROUP_PAD.left + GROUP_PAD.right; + const padH = GROUP_PAD.top + GROUP_PAD.bottom; + // Parts packed in rows, the biggest first, into a block the shape of the + // screen - the calls between groups are their bundled wires, so where a + // group sits needs to follow them less than it needs to be found - and + // where each part's own nodes then sit. + const arrange = (parts, gap) => { + const size = p => (p.k ? p.k.members.length : 0); + const sorted = [...parts].sort((a, b) => size(b) - size(a) || b.h - a.h || String(a.k ? a.k.key : a.id).localeCompare(String(b.k ? b.k.key : b.id))); + const area = parts.reduce((a, p) => a + (p.w + gap) * (p.h + gap), 0); + const row = Math.max(...parts.map(p => p.w), Math.sqrt(area * SHELF_ASPECT)); + const where = new Map(); + let [x, y, tall] = [0, 0, 0]; + for (const p of sorted) { + if (x > 0 && x + p.w > row) [x, y, tall] = [0, y + tall + gap, 0]; + where.set(p.id, { x, y }); + x += p.w + gap; + tall = Math.max(tall, p.h); + } + const pos = new Map(); + let w = 0; + let h = 0; + for (const p of parts) { + const o = where.get(p.id) || { x: 0, y: 0 }; + if (p.k) for (const [id, q] of p.pos) pos.set(id, { x: o.x + q.x, y: o.y + q.y }); + else pos.set(p.id, o); + w = Math.max(w, o.x + p.w); + h = Math.max(h, o.y + p.h); + } + return { pos, w, h }; + }; + // one group laid out: where each of its nodes sits within its box, and the box's size + const lay = c => { + let inner; + if (c.children.length) inner = arrange(c.children.map(k => ({ id: 'g:' + k.i, k, ...lay(k) })), 50); + else { + const members = c.members.map(id => byId.get(id)).filter(Boolean); + const ids = new Set(c.members); + const at = layered(members, scene.edges.filter(e => ids.has(e.from) && ids.has(e.to)), opts); + inner = { pos: new Map(), w: 0, h: 0 }; + for (const n of members) { + const p = at.get(n.id) || { x: 0, y: 0 }; + inner.pos.set(n.id, p); + inner.w = Math.max(inner.w, p.x + n.w); + inner.h = Math.max(inner.h, p.y + n.h); + } + } + const pos = new Map(); + for (const [id, q] of inner.pos) pos.set(id, { x: GROUP_PAD.left + q.x, y: GROUP_PAD.top + q.y }); + return { pos, w: inner.w + padW, h: inner.h + padH }; + }; + const parts = scene.tops.map(t => ({ id: 'g:' + t.i, k: t, ...lay(t) })); + const grouped = new Set(scene.tops.flatMap(t => t.members)); + for (const n of scene.nodes) if (!grouped.has(n.id)) parts.push(n); + return arrange(parts, 90).pos; + } + + // a group's box: round its nodes, or round the boxes of the groups in it, + // with room for its outline - worked out afresh, so it follows a dragged node + function clusterBox(c) { + let [x0, y0, x1, y1] = [Infinity, Infinity, -Infinity, -Infinity]; + const take = (a, b, c2, d) => ([x0, y0, x1, y1] = [Math.min(x0, a), Math.min(y0, b), Math.max(x1, c2), Math.max(y1, d)]); + if (c.children.length) { + for (const k of c.children) { + const b = clusterBox(k); + if (b) take(b.x, b.y, b.x + b.w, b.y + b.h); + } + } else { + for (const id of c.members) { + const n = S.byId.get(id); + if (n) take(n.x, n.y, n.x + n.w, n.y + n.h); + } + } + c.box = x0 === Infinity ? null : { x: x0 - GROUP_PAD.left, y: y0 - GROUP_PAD.top, w: x1 - x0 + GROUP_PAD.left + GROUP_PAD.right, h: y1 - y0 + GROUP_PAD.top + GROUP_PAD.bottom }; + return c.box; + } + + // a group as one line: how much is in it + function groupSummary(g) { + const ms = g.members.map(id => S.byId.get(id)).filter(Boolean); + const within = g.children.length ? ` in ${plural(g.children.length, 'group')}` : ''; + if (g.unit === 'component') { + const lines = ms.reduce((a, n) => a + (n.data.lines || 0), 0); + const langs = [...new Set(ms.map(n => langName(n.data.language)))].slice(0, 3).join(', '); + return `${plural(ms.length, 'component')}${within} · ${plural(lines, 'line')}${langs ? ' · ' + langs : ''}`; + } + const entries = ms.filter(n => n.data.entry).length; + return plural(ms.length, 'function') + within + (entries ? ` · ${plural(entries, 'entry point')}` : ''); + } + + // the rules that fold groups away, written as the zoom changes + function groupStyle() { + let el = document.getElementById('group-style'); + if (!el) { + el = document.createElement('style'); + el.id = 'group-style'; + document.head.append(el); + } + return el; + } + + // Which groups are folded at this zoom: one with nodes of its own, until + // zoomed in far enough to read them; one holding groups, until zoomed in + // near to where it alone would fill the screen. A folded group's nodes, + // wires and inner groups go, and its card shows - by a rule each, so + // folding a group of hundreds touches one style. + function regroup() { + const sc = S.scene; + if (!sc || !sc.groups) return; + const vp = $('viewport'); + const z = S.zoom; + // the first look starts from everything folded + const was = S.collapsed || new Set(sc.groups.map(g => g.i)); + const now = new Set(); + const [vw, vh] = [vp.clientWidth, vp.clientHeight]; + // the zoom the whole scene fits at - nothing opens by zoom until past it, so + // the scene fitted reads as its top groups however big a share one takes + if (S.sceneFit == null) { + const b = bounds(); + S.sceneFit = clamp(Math.min((vw - 96) / b.w, (vh - 150) / b.h), minZoom(), 1.15); + } + const past = z > S.sceneFit * 1.15; + const visit = c => { + const folded = was.has(c.i); + let open = z >= (folded ? GROUP_SHOW : GROUP_HIDE); + if (!open && c.children.length && c.box) { + // how much of the screen it would take, wherever it is panned to + const cover = (Math.min(c.box.w * z, vw) * Math.min(c.box.h * z, vh)) / (vw * vh); + const fit = Math.min((vw - 96) / c.box.w, (vh - 150) / c.box.h); + open = (past && cover >= (folded ? OPEN_AT : FOLD_AT)) || (S.opened.has(c.i) && z >= fit * HOLD_AT); + } + if (open) return c.children.forEach(visit); + now.add(c.i); + S.opened.delete(c.i); + }; + sc.tops.forEach(visit); + const same = !!S.collapsed && now.size === S.collapsed.size && [...now].every(i => S.collapsed.has(i)); + S.collapsed = now; + if (!same) { + groupStyle().textContent = [...now].map(i => `.cg-${i}, .gframe-${i} { display: none !important; } .gcard-${i} { display: flex !important; }`).join('\n'); + for (const g of sc.groups) if (now.has(g.i) && g.el && g.box) placeCard(g); + } + if (!same || (now.size && z !== S.groupZoom)) drawGroupWires(); + } + + function placeCard(g) { + Object.assign(g.el.style, { left: g.box.x + 'px', top: g.box.y + 'px', width: g.box.w + 'px', height: g.box.h + 'px' }); + g.el.style.setProperty('--w', g.box.w + 'px'); + g.el.style.setProperty('--h', g.box.h + 'px'); + } + + // Each group as one overview card over the region its nodes hold - shown + // while it is folded: what it is, how much is in it, and how much of it + // the step on show touched. A click opens it. + function drawGroups() { + const host = $('groups'); + host.textContent = ''; + for (const g of S.scene.groups || []) { + if (!g.box) continue; + const el = h('div', 'group-card gcard-' + g.i); + el.classList.add(...chainOf(g).slice(0, -1).map(x => 'cg-' + x.i)); + g.mark = h('div', 'g-mark'); + g.mark.hidden = true; + const what = g.path || (g.unit === 'component' ? 'components' : ['tests', 'top level', 'free functions'].includes(g.key) ? 'functions' : 'type'); + el.append(h('div', 'g-what', what), h('div', 'g-title', g.title), h('div', 'g-sub', groupSummary(g)), g.mark); + el.title = `${g.key} - click, or zoom in, to open it`; + el.addEventListener('click', ev => { + ev.stopPropagation(); + openGroup(g); + }); + g.el = el; + placeCard(g); + host.append(el); + } + markGroups(); + } + + // a group opened: one of nodes zoomed in until they can be read, one of + // groups until it fills the screen and opens into them + function openGroup(g) { + if (!g.children.length) return flyTo(g.members.map(id => S.byId.get(id)).filter(Boolean)); + S.opened.add(g.i); + const vp = $('viewport'); + const b = g.box; + S.zoom = clamp(Math.min((vp.clientWidth - 96) / b.w, (vp.clientHeight - 150) / b.h), minZoom(), 1.15); + S.panX = vp.clientWidth / 2 - (b.x + b.w / 2) * S.zoom; + S.panY = vp.clientHeight / 2 - (b.y + b.h / 2) * S.zoom; + glide(); + applyView(); + } + + // a group whose nodes the step on show touched is marked as most of them are, and counts them + function markGroups() { + for (const g of (S.scene && S.scene.groups) || []) { + if (!g.el) continue; + const hows = []; + for (const id of g.members) { + const n = S.byId.get(id); + const how = n && MARKS.find(m => n.el.classList.contains('chg-' + m)); + if (how) hows.push(how); + } + g.el.classList.remove('chg', ...MARKS.map(m => 'chg-' + m)); + g.mark.hidden = !hows.length; + if (!hows.length) continue; + const most = MARKS.map(m => [m, hows.filter(x => x === m).length]).sort((p, q) => q[1] - p[1])[0][0]; + g.el.classList.add('chg', 'chg-' + most); + g.mark.textContent = `${hows.length} ${most === 'inspect' ? 'inspected' : 'changed'}`; + } + } + + // The wires to and between folded groups: one for each pair of what is in + // sight, as thick as the calls it carries and counted - drawn for the zoom, + // so they read the same however far out. + function drawGroupWires() { + const svg = $('group-wires'); + svg.textContent = ''; + const sc = S.scene; + if (!sc || !sc.groups || !S.collapsed || !S.collapsed.size) return; + const z = 1 / S.zoom; + S.groupZoom = S.zoom; + $('groups').style.setProperty('--gz', String(z)); + // what stands for a node: the outermost folded group it is in, if any + const stand = id => ((sc.chains && sc.chains.get(id)) || []).find(g => S.collapsed.has(g.i)) || null; + const all = new Map(); + for (const e of sc.edges) { + const a = stand(e.from); + const b = stand(e.to); + if ((!a && !b) || a === b) continue; + const k = `${a ? 'g' + a.i : e.from}>${b ? 'g' + b.i : e.to}`; + if (!all.has(k)) all.set(k, { a: a ? a.box : S.byId.get(e.from), b: b ? b.box : S.byId.get(e.to), count: 0 }); + all.get(k).count += (e.data && e.data.count) || 1; + } + const ns = 'http://www.w3.org/2000/svg'; + const make = (tag, attrs) => { + const x = document.createElementNS(ns, tag); + for (const [k, v] of Object.entries(attrs)) x.setAttribute(k, v); + return x; + }; + for (const { a, b, count } of all.values()) { + if (!a || !b || a.w == null || b.w == null) continue; + const [p1, p2] = sideAnchors(a, b); + const len = Math.hypot(p2.x - p1.x, p2.y - p1.y) || 1; + const ux = (p2.x - p1.x) / len; + const uy = (p2.y - p1.y) / len; + const s = 9 * z; + const bx = p2.x - ux * s; + const by = p2.y - uy * s; + // a thin bundle is told by its thickness alone - counting every one crowds the cards + const label = make('text', { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2, 'font-size': 12 * z, style: `stroke-width: ${4 * z}` }); + label.textContent = count >= BUNDLE_COUNTED ? `×${count}` : ''; + svg.append( + make('path', { d: `M${p1.x} ${p1.y} L${bx} ${by}`, class: 'gwire', 'vector-effect': 'non-scaling-stroke', style: `stroke-width: ${1.5 + Math.min(4, Math.log2(count))}` }), + make('path', { d: `M${p2.x} ${p2.y} L${bx - uy * s * 0.55} ${by + ux * s * 0.55} L${bx + uy * s * 0.55} ${by - ux * s * 0.55} z`, class: 'ghead' }), + label + ); + } + } + // the context level: the system in the middle, what calls in on its left, // what it reaches on its right function layoutContext(scene) { @@ -1129,25 +1549,39 @@ $('note').hidden = true; S.scene = null; S.byId = new Map(); - } - + S.lit = null; + $('lit-wires').textContent = ''; + $('wires').classList.remove('dimming'); + $('groups').textContent = ''; + $('group-wires').textContent = ''; + groupStyle().textContent = ''; + S.collapsed = null; + S.opened = new Set(); + S.sceneFit = null; + S.groupZoom = null; + S.alsoLit = new Set(); + } + + // Each pin's dot centre, from the node's outer corner - as drawn, to the + // fraction of a pixel, and back out of the scale it is drawn at, whatever + // the camera or a running animation holds. function measurePins(n) { n.pins = new Map(); + const box = n.el.getBoundingClientRect(); + const k = box.width / (n.el.offsetWidth || 1) || 1; for (const p of n.el.querySelectorAll('[data-pin]')) { - const dot = p.querySelector('i') || p; - let x = dot.offsetWidth / 2; - let y = dot.offsetHeight / 2; - let e = dot; - while (e && e !== n.el) { - x += e.offsetLeft; - y += e.offsetTop; - e = e.offsetParent; - } - n.pins.set(p.dataset.pin, { x, y }); + const dot = (p.querySelector('i') || p).getBoundingClientRect(); + n.pins.set(p.dataset.pin, { x: (dot.left + dot.width / 2 - box.left) / k, y: (dot.top + dot.height / 2 - box.top) / k }); } } function mount(scene, keepView) { + const was = remember(); + const same = !!was && sameView(was.route, S.route); + const via = S.via; + S.via = null; + // another view: the one on show lifted off as a picture, to move away as this one comes in + const ghost = was && !same ? lift(was) : null; clearCanvas(); // the list of asks belongs to no one level, so it stays while they are walked if (!S.asksPanel) hidePanel(); @@ -1167,9 +1601,23 @@ n.h = n.el.offsetHeight; measurePins(n); } + // a big scene is laid out a group at a time, and zoomed out reads as its groups + if (scene.groups === undefined) { + scene.groups = scene.layout === 'layered' ? groupScene(scene) : null; + if (scene.groups) { + // laid out afresh, apart from where its nodes sat ungrouped + scene.key += ':grouped'; + scene.frames = [ + ...(scene.frames || []), + ...scene.groups.map(g => ({ title: g.key, sub: plural(g.members.length, g.unit), members: g.members, cls: 'group', group: g })), + ]; + } + } + for (const n of scene.nodes) n.el.classList.add(...groupClasses(scene, n.id)); let pos; if (scene.layout === 'flow') pos = layoutFlow(scene); else if (scene.layout === 'context') pos = layoutContext(scene); + else if (scene.groups) pos = layeredGroups(scene); else pos = layered(scene.nodes, scene.edges, scene); const saved = scene.persist ? store.get('pos:' + scene.key, {}) : {}; for (const n of scene.nodes) { @@ -1181,6 +1629,7 @@ } drawFrames(); drawEdges(); + drawGroups(); if (scene.note) { $('note').textContent = scene.note; $('note').hidden = false; @@ -1190,8 +1639,375 @@ if (keepView) applyView(); else fit(); const changed = applyMarks(); - if (S.fly && changed.length) flyTo(changed); + for (const n of changed) if (n.el.classList.contains('chg-added')) keepNew('node:' + morphKey(n)); + // the camera goes straight to where it ends, and the motion carries it there + const moving = same || !!ghost; + if (S.fly && changed.length) flyTo(changed, moving); S.fly = false; + if (same) morph(was); + else if (ghost) travel(was, ghost, via); + showKept(); + showOffMap(); + S.shownRoute = S.route && { ...S.route }; + } + + // ------------------------------------------------------------- additions kept + + // What each step added - nodes, the pins of a parameter, a return or a + // call, and wires - by the view it was seen in. Those of the collection on + // show, up to the step on show, stay green: a flash fades to grey long + // before the ask is done. Another collection on show, they rest. + const KEPT = new Map(); + // steps whose additions are kept, at most - the timeline holds fewer + const KEPT_MAX = 400; + + // a view as its additions are kept: a function by its name, so a step that + // moves it keeps them + const keptView = r => (!r ? '' : r.level === 'flow' ? `flow:${r.file}@${r.name || r.line}` : href(r)); + + function keepNew(key) { + const ev = T.at != null ? T.steps[T.at] : null; + if (!ev) return; + const v = keptView(S.route); + if (!KEPT.has(ev.seq)) KEPT.set(ev.seq, new Map()); + const views = KEPT.get(ev.seq); + if (!views.has(v)) views.set(v, new Set()); + views.get(v).add(key); + if (KEPT.size > KEPT_MAX) { + const held = new Set(T.steps.map(s => s.seq)); + for (const seq of KEPT.keys()) if (!held.has(seq)) KEPT.delete(seq); + } + } + + // the additions of the collection on show, up to the step on show, green on the scene + function showKept() { + for (const el of document.querySelectorAll('.kept-new')) el.classList.remove('kept-new'); + if (!S.scene || T.at == null) return; + const v = keptView(S.route); + const kept = new Set(); + for (const i of shownSteps()) { + if (i > T.at) break; + for (const k of KEPT.get(T.steps[i].seq)?.get(v) || []) kept.add(k); + } + if (!kept.size) return; + const small = S.scene.nodes.length <= MOTION_MAX; + for (const n of S.scene.nodes) { + const mk = morphKey(n); + if (kept.has('node:' + mk)) n.el.classList.add('kept-new'); + if (small && n.el.classList.contains('bp')) for (const p of pinsOf(n)) if (kept.has(`pin:${mk}|${p.key}`)) p.el.classList.add('kept-new'); + } + const keyOf = id => (S.byId.has(id) ? morphKey(S.byId.get(id)) : String(id)); + for (const e of S.scene.edges) if (e.path && kept.has('wire:' + wireKey(e, keyOf))) e.path.classList.add('kept-new'); + } + + // ------------------------------------------------------------- motion + + // motion is for those who want it + const calm = () => matchMedia('(prefers-reduced-motion: reduce)').matches; + // a scene past this many nodes changes in a cut - moving thousands of + // elements at once is a stall, not an animation + const MOTION_MAX = 600; + // and past this many coming and going, they simply appear + const MOTION_CROWD = 150; + const EASE = 'cubic-bezier(.2, .7, .2, 1)'; + const MOVE_MS = 480; + const DEPTH = { system: 0, containers: 1, components: 2, code: 3, flow: 4 }; + // words on a node that, changed, are pointed out + const WORDS = ['.head .t', '.head .q', '.body', '.chain', '.name', '.tech']; + + // a node as the next scene knows it again + const morphKey = n => n.mkey || n.key || n.id; + + // The same view drawn again - a step's other side, a function after an + // edit, the map after a save - rather than a move to another. + function sameView(a, b) { + if (!a || !b || a.level !== b.level) return false; + if (a.level === 'flow') return a.file === b.file && (a.name ? a.name === b.name : a.line === b.line); + return href(a) === href(b); + } + + // a parameter is its name - `n: i32` is still `n` whatever its type becomes + const pinName = label => label.split(':')[0].trim(); + + // a node's pins as the eye reads them: the side, what each says, how far down + function pinsOf(n) { + const out = []; + const seen = new Map(); + const top = n.el.getBoundingClientRect().top; + for (const p of n.el.querySelectorAll('.rows > .pin')) { + const label = p.querySelector('.lbl')?.textContent || ''; + if (p.style.visibility === 'hidden' || (!label && !p.dataset.pin)) continue; + const side = p.classList.contains('in') ? 'in' : 'out'; + const base = `${side}:${p.classList.contains('ret') ? 'ret' : pinName(label) || p.dataset.pin}`; + const k = seen.get(base) || 0; + seen.set(base, k + 1); + out.push({ key: `${base}#${k}`, label, side, el: p, top: (p.getBoundingClientRect().top - top) / S.zoom }); + } + return out; + } + + const wordsOf = el => new Map(WORDS.map(s => [s, el.querySelector(s)?.textContent ?? null])); + + // a wire by the nodes it joins, as the next scene knows them again + function wireKey(e, keyOf) { + const pin = S.scene.layout === 'flow' ? e.fromPin || '' : ''; + return `${keyOf(e.from)}:${pin}>${keyOf(e.to)}|${e.cls || ''}`; + } + + // what the canvas shows now, for the scene that follows to move on from + function remember() { + if (!S.scene || !S.shownRoute || calm()) return null; + const size = S.scene.nodes.length; + const nodes = new Map(); + const small = size <= MOTION_MAX; + for (const n of S.scene.nodes) { + nodes.set(morphKey(n), { n, x: n.x, y: n.y, pins: small && n.el.classList.contains('bp') ? pinsOf(n) : [], words: small ? wordsOf(n.el) : null }); + } + const keyOf = id => (S.byId.has(id) ? morphKey(S.byId.get(id)) : String(id)); + const wires = new Map(); + if (small) for (const e of S.scene.edges) if (e.path) wires.set(wireKey(e, keyOf), { d: e.path.getAttribute('d'), from: keyOf(e.from), to: keyOf(e.to) }); + return { route: S.shownRoute, size, nodes, wires, view: { zoom: S.zoom, panX: S.panX, panY: S.panY } }; + } + + // an animation that tidies up after itself, and is no promise of one + function animate(el, frames, opts, after) { + const a = el.animate(frames, opts); + if (after) a.finished.then(after, after); + return a; + } + + // a class put on for the length of its animation, again if it was already + function flash(el, cls) { + el.classList.remove(cls); + void el.offsetWidth; + el.classList.add(cls); + el.addEventListener('animationend', () => el.classList.remove(cls), { once: true }); + } + + const cameraOf = v => `translate(${v.panX}px, ${v.panY}px) scale(${v.zoom})`; + const gridOf = v => ({ backgroundSize: `${20 * v.zoom}px ${20 * v.zoom}px`, backgroundPosition: `${v.panX}px ${v.panY}px` }); + + // the camera carried from where it was to where it is now + function carryCamera(from) { + const to = { zoom: S.zoom, panX: S.panX, panY: S.panY }; + if (Math.abs(from.zoom - to.zoom) < 1e-3 && Math.abs(from.panX - to.panX) < 0.5 && Math.abs(from.panY - to.panY) < 0.5) return; + $('viewport').classList.remove('glide'); + for (const a of $('surface').getAnimations()) a.cancel(); + animate($('surface'), [{ transform: cameraOf(from) }, { transform: cameraOf(to) }], { duration: MOVE_MS, easing: EASE }); + animate($('viewport'), [gridOf(from), gridOf(to)], { duration: MOVE_MS, easing: EASE }); + } + + // The same view drawn again: what stayed glides to where it sits now, what + // came grows in, what went fades where it was, and the pins and words of a + // node that changed say so - a parameter, a return type, a call. + function morph(was) { + carryCamera(was.view); + const now = S.scene.nodes; + if (now.length > MOTION_MAX || was.size > MOTION_MAX) return; + const kept = new Set(now.map(morphKey).filter(k => was.nodes.has(k))); + const crowd = now.length - kept.size + was.nodes.size - kept.size > MOTION_CROWD; + // pins stamped so far, so each lands after the one before + let stamped = 0; + for (const n of now) { + const old = was.nodes.get(morphKey(n)); + if (!old) { + keepNew('node:' + morphKey(n)); + if (!crowd) enter(n.el); + continue; + } + const dx = old.x - n.x; + const dy = old.y - n.y; + if (Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5) animate(n.el, [{ transform: `translate(${dx}px, ${dy}px)` }, { transform: 'none' }], { duration: MOVE_MS, easing: EASE }); + if (old.pins.length || n.el.classList.contains('bp')) stamped = morphPins(n, old.pins, stamped); + if (old.words) morphWords(n.el, old.words); + } + if (!crowd) for (const [k, old] of was.nodes) if (!kept.has(k)) leave(old.n.el); + morphWires(was.wires); + } + + // a node that was not there before grows in, and glows a moment + function enter(el) { + animate(el, [{ opacity: 0, transform: 'scale(.86)' }, { opacity: 1, transform: 'none' }], { duration: 420, delay: 140, easing: EASE, fill: 'backwards' }); + flash(el, 'arrived'); + } + + // a node gone from the scene fades out where it was + function leave(el) { + if (el.isConnected) return; + el.classList.add('leaving'); + el.classList.remove('selected', 'hit', 'faded'); + delete el.dataset.id; + $('nodes').append(el); + animate(el, [{ opacity: 1, transform: 'none' }, { opacity: 0, transform: 'scale(.86)' }], { duration: 520, easing: EASE, fill: 'forwards' }, () => el.remove()); + } + + // when the first pin is stamped - once the nodes have settled - the gap + // to the next, how many land one by one before the rest land together, + // and how long a stamp's ink takes to dry + const STAMP_AT = 340; + const STAMP_GAP = 170; + const STAMP_RUN = 10; + const STAMP_MS = 1700; + + // A node's pins against what they were. One that came, or whose words + // changed, is stamped onto the node, one after another; one that went + // slides off the node struck through. `stamped` is how many landed before + // this node's, and the count is handed on. + function morphPins(n, before, stamped) { + const was = new Map(before.map(p => [p.key, p])); + const now = new Set(); + for (const p of pinsOf(n)) { + now.add(p.key); + const old = was.get(p.key); + if (old && old.label === p.label) continue; + if (!old) keepNew(`pin:${morphKey(n)}|${p.key}`); + stamp(p.el, STAMP_AT + Math.min(stamped++, STAMP_RUN) * STAMP_GAP, old ? old.label : null); + } + for (const p of before) if (!now.has(p.key)) pinLeave(n, p); + return stamped; + } + + // A pin pressed onto its node `at` ms from now: it lands from above, presses + // in and leaves a ring of ink - green for one that came, amber for one + // whose words changed. Those old words stay put until the stamp comes + // down, and lift off as it lands. + function stamp(pin, at, old) { + pin.style.setProperty('--stamp-at', at + 'ms'); + pin.classList.toggle('changed', old != null); + pin.classList.add('stamp'); + setTimeout(() => pin.classList.remove('stamp', 'changed'), at + STAMP_MS); + const lbl = old != null && pin.querySelector('.lbl'); + if (!lbl) return; + const ghost = h('span', 'lbl-was', old); + ghost.style.left = lbl.offsetLeft + 'px'; + ghost.style.top = lbl.offsetTop + 'px'; + pin.append(ghost); + animate(ghost, [{ opacity: 1, transform: 'none' }, { opacity: 0, transform: 'translateY(7px) scale(.9)' }], { duration: 260, delay: Math.max(0, at - 120), easing: 'ease-in', fill: 'both' }, () => ghost.remove()); + } + + function pinLeave(n, p) { + const g = p.el; + if (g.isConnected) return; + g.classList.add('pin-out'); + g.removeAttribute('data-pin'); + g.style.top = Math.max(0, Math.min(p.top, n.el.offsetHeight - 22)) + 'px'; + g.style[p.side === 'in' ? 'left' : 'right'] = '0'; + n.el.append(g); + const off = p.side === 'in' ? -28 : 28; + animate(g, [{ opacity: 1, transform: 'none' }, { opacity: 1, transform: 'none', offset: 0.35 }, { opacity: 0, transform: `translateX(${off}px)` }], { duration: 900, easing: EASE, fill: 'forwards' }, () => g.remove()); + } + + function morphWords(el, before) { + for (const [sel, old] of before) { + const x = el.querySelector(sel); + if (x && old != null && x.textContent !== old) flash(x, 'txt-change'); + } + } + + // The wires settle in once the nodes have, the old fading as they go. A + // wire that came draws itself in, with both its ends kept in sight; one + // that went fades red, and a node it leaves that nothing on show still + // reaches fades back after it. + function morphWires(before) { + const keyOf = id => (S.byId.has(id) ? morphKey(S.byId.get(id)) : String(id)); + const now = new Map(); + for (const e of S.scene.edges) if (e.path) now.set(wireKey(e, keyOf), e); + const svg = $('wires'); + const old = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + old.setAttribute('class', 'ghost-wires'); + const byKey = new Map(S.scene.nodes.map(n => [morphKey(n), n])); + for (const [k, w] of before) { + const p = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + p.setAttribute('d', w.d); + p.setAttribute('class', 'wire' + (now.has(k) ? '' : ' gone')); + old.append(p); + if (now.has(k)) continue; + for (const end of [w.from, w.to]) { + const n = byKey.get(end); + if (n && n.el.classList.contains('faded')) animate(n.el, [{ opacity: 1 }, { opacity: 1, offset: 0.4 }, { opacity: 0.2 }], { duration: 900, easing: 'ease-out' }); + } + } + svg.insertBefore(old, $('wire-layer')); + animate(old, [{ opacity: 1 }, { opacity: 0 }], { duration: 360, easing: 'ease-out', fill: 'forwards' }, () => old.remove()); + animate($('wire-layer'), [{ opacity: 0 }, { opacity: 0, offset: 0.55 }, { opacity: 1 }], { duration: MOVE_MS + 260 }); + const fresh = [...now].filter(([k]) => !before.has(k)); + for (const [k, e] of fresh) { + keepNew('wire:' + k); + // both ends of a new connection stay in sight, however the step is seen + e.path.classList.remove('faded'); + for (const id of [e.from, e.to]) { + const n = S.byId.get(id); + if (!n) continue; + S.alsoLit.add(id); + n.el.classList.remove('faded'); + } + } + if (!before.size || fresh.length > MOTION_CROWD) return; + for (const [, e] of fresh) { + const len = e.path.getTotalLength(); + animate(e.path, [{ strokeDasharray: `${len}`, strokeDashoffset: `${len}` }, { strokeDasharray: `${len}`, strokeDashoffset: '0' }], { duration: 520, delay: MOVE_MS, easing: EASE, fill: 'backwards' }); + flash(e.path, 'wire-new'); + } + } + + // the view on show lifted off the canvas as one picture - one move at a + // time, so a move made before the last one finished cuts it short + function lift(was) { + for (const g of document.querySelectorAll('.ghost')) g.remove(); + for (const a of $('surface').getAnimations()) a.cancel(); + if (was.size > MOTION_MAX) return null; + const ghost = h('div', 'ghost'); + ghost.style.transform = $('surface').style.transform; + for (const id of ['frames', 'wires', 'labels', 'nodes', 'groups', 'group-wires']) { + const c = $(id).cloneNode(true); + c.removeAttribute('id'); + for (const x of c.querySelectorAll('[id]')) x.removeAttribute('id'); + ghost.append(c); + } + // folded as it was - the rules that fold its groups go with the next scene + for (const i of S.collapsed || []) { + for (const x of ghost.querySelectorAll(`.cg-${i}, .gframe-${i}`)) x.remove(); + for (const x of ghost.querySelectorAll(`.gcard-${i}`)) x.style.display = 'flex'; + } + $('viewport').append(ghost); + return ghost; + } + + // From one view to another. Going in, the old view swells through what was + // opened as the new one grows out of it; coming back up, the old view + // shrinks into where it sits in the new. Elsewhere, one fades into the other. + function travel(was, ghost, via) { + const vp = $('viewport').getBoundingClientRect(); + const surface = $('surface'); + const final = surface.style.transform; + const from = ghost.style.transform; + const done = () => ghost.remove(); + const deeper = (DEPTH[S.route.level] ?? 0) > (DEPTH[was.route.level] ?? 0); + let at = deeper ? via : null; + if (!deeper && (DEPTH[S.route.level] ?? 0) < (DEPTH[was.route.level] ?? 0)) { + // the node in this view that opens the one left + const back = S.scene.nodes.find(n => n.drill && sameView(n.drill, was.route)); + if (back) at = { left: vp.left + S.panX + back.x * S.zoom, top: vp.top + S.panY + back.y * S.zoom, width: back.w * S.zoom, height: back.h * S.zoom }; + } + $('viewport').classList.remove('glide'); + if (!at || !at.width || !at.height) { + animate(ghost, [{ opacity: 1 }, { opacity: 0 }], { duration: 260, easing: 'ease-out', fill: 'forwards' }, done); + animate(surface, [{ opacity: 0, transform: `translateY(8px) ${final}` }, { opacity: 1, transform: final }], { duration: 360, delay: 80, easing: EASE, fill: 'backwards' }); + return; + } + const cx = at.left + at.width / 2 - vp.left; + const cy = at.top + at.height / 2 - vp.top; + // how small the whole view is, held within the node + const k = clamp(Math.max(at.width / vp.width, at.height / vp.height), 0.12, 0.9); + const about = (s, t) => `translate(${cx}px, ${cy}px) scale(${s}) translate(${-cx}px, ${-cy}px) ${t}`; + const big = Math.min(1 / k, 4); + if (deeper) { + animate(ghost, [{ opacity: 1, transform: from }, { opacity: 0, transform: about(big, from) }], { duration: MOVE_MS, easing: EASE, fill: 'forwards' }, done); + animate(surface, [{ opacity: 0, transform: about(k, final) }, { opacity: 1, transform: final }], { duration: MOVE_MS, easing: EASE }); + } else { + animate(ghost, [{ opacity: 1, transform: from }, { opacity: 0, transform: about(k, from) }], { duration: MOVE_MS, easing: EASE, fill: 'forwards' }, done); + animate(surface, [{ opacity: 0, transform: about(Math.min(big, 1.6), final) }, { opacity: 1, transform: final }], { duration: MOVE_MS, easing: EASE }); + } } // a step's scene past this many nodes keeps only what it touched and what @@ -1235,14 +2051,27 @@ function drawFrames() { const host = $('frames'); host.textContent = ''; + // each group's outline holds the outlines of the groups in it + for (const t of S.scene.tops || []) clusterBox(t); for (const f of S.scene.frames || []) { const members = f.members.map(id => S.byId.get(id)).filter(Boolean); if (!members.length) continue; - const x0 = Math.min(...members.map(n => n.x)) - 28; - const y0 = Math.min(...members.map(n => n.y)) - 28; - const x1 = Math.max(...members.map(n => n.x + n.w)) + 28; - const y1 = Math.max(...members.map(n => n.y + n.h)) + 56; + let x0 = Math.min(...members.map(n => n.x)) - 28; + let y0 = Math.min(...members.map(n => n.y)) - 28; + let x1 = Math.max(...members.map(n => n.x + n.w)) + 28; + let y1 = Math.max(...members.map(n => n.y + n.h)) + 56; + const g = f.group && f.group.box; + if (g) [x0, y0, x1, y1] = [g.x, g.y, g.x + g.w, g.y + g.h]; + else { + // and an outline round groups holds them whole + const mine = new Set(f.members); + for (const t of S.scene.tops || []) { + if (!t.box || !t.members.some(id => mine.has(id))) continue; + [x0, y0, x1, y1] = [Math.min(x0, t.box.x - 28), Math.min(y0, t.box.y - 28), Math.max(x1, t.box.x + t.box.w + 28), Math.max(y1, t.box.y + t.box.h + 56)]; + } + } const box = h('div', 'frame' + (f.cls ? ' ' + f.cls : '')); + if (f.group) box.classList.add('gframe-' + f.group.i, ...chainOf(f.group).slice(0, -1).map(x => 'cg-' + x.i)); Object.assign(box.style, { left: x0 + 'px', top: y0 + 'px', width: x1 - x0 + 'px', height: y1 - y0 + 'px' }); const title = h('div', 'title', f.title); if (f.sub) title.append(h('small', null, `[${f.sub}]`)); @@ -1287,8 +2116,25 @@ return curve(p1, p2); } + // the reach of each bend of a wire that runs along a floor, and the least run worth drawing between them + const BEND = 64; + const FLOOR_RUN = 48; + + // Down to a floor below what lies between, along it, and up into the + // target. Too close for a run along the floor, the two bends would fold + // back over each other - so they meet level at the lowest point instead, + // one smooth dip. function below(p1, p2, yb) { - return `M${p1.x} ${p1.y} C${p1.x + 34} ${p1.y} ${p1.x + 34} ${yb} ${p1.x + 64} ${yb} L${p2.x - 64} ${yb} C${p2.x - 34} ${yb} ${p2.x - 34} ${p2.y} ${p2.x} ${p2.y}`; + const span = p2.x - p1.x; + if (span >= 2 * BEND + FLOOR_RUN) { + return `M${p1.x} ${p1.y} C${p1.x + 34} ${p1.y} ${p1.x + 34} ${yb} ${p1.x + BEND} ${yb} L${p2.x - BEND} ${yb} C${p2.x - 34} ${yb} ${p2.x - 34} ${p2.y} ${p2.x} ${p2.y}`; + } + const mx = (p1.x + p2.x) / 2; + // which way the dip runs along its floor, and how level it lies there + const dir = span < 0 ? -1 : 1; + const level = Math.max(12, Math.abs(span) / 4); + const lead = clamp(Math.abs(span) / 2, 24, 40); + return `M${p1.x} ${p1.y} C${p1.x + lead} ${p1.y} ${mx - dir * level} ${yb} ${mx} ${yb} C${mx + dir * level} ${yb} ${p2.x - lead} ${p2.y} ${p2.x} ${p2.y}`; } function backTo(p1, n, yb) { @@ -1335,6 +2181,10 @@ wires.append(path, hit); e.path = path; e.hit = hit; + // a wire at a grouped node goes while a group of its stands in for it + e.groups = [...new Set([...groupClasses(S.scene, e.from), ...groupClasses(S.scene, e.to)])]; + path.classList.add(...e.groups); + hit.classList.add(...e.groups); if (c4 && e.label && e.mid) { const lab = h('div', 'elabel' + (e.cls === 'declared' ? ' declared' : ''), e.label); lab.style.left = e.mid.x + 'px'; @@ -1345,6 +2195,7 @@ ev.stopPropagation(); selectEdge(e); }); + lab.classList.add(...e.groups); labels.append(lab); e.labelEl = lab; } @@ -1365,40 +2216,99 @@ e.labelEl.style.top = e.mid.y + 'px'; } } + // the lit copies follow the wires they copy + if (S.lit && S.lit.edges.some(e => e.from === id || e.to === id)) highlight(S.lit.id); + } + + // the wires at each node of the scene, gathered the first time they are asked for + function touching(id) { + const sc = S.scene; + if (!sc.touching) { + sc.touching = new Map(); + for (const e of sc.edges) { + for (const end of new Set([e.from, e.to])) { + if (!sc.touching.has(end)) sc.touching.set(end, []); + sc.touching.get(end).push(e); + } + } + } + return (sc.touching.get(id) || []).filter(e => e.path); } + // A node's wires lit and the rest dimmed. The scene's wires are one drawing, + // dimmed whole and never redrawn; the lit ones are copies on a layer of + // their own - so a pointer moving over thousands of wires redraws a handful. function highlight(id) { if (!S.scene) return; const c4 = S.scene.kind === 'c4'; + const quiet = S.scene.quietLabels; + const was = S.lit || { edges: [], nodes: [] }; + for (const e of was.edges) { + if (!e.labelEl) continue; + e.labelEl.classList.remove('hot'); + if (quiet) e.labelEl.style.display = 'none'; + } + for (const n of was.nodes) n.el.classList.remove('hit'); + const edges = id == null ? [] : touching(id); + const lit = $('lit-wires'); + lit.textContent = ''; const near = new Set(); - for (const e of S.scene.edges) { - if (!e.path) continue; - const hot = id != null && (e.from === id || e.to === id); - e.path.classList.toggle('hot', hot); - e.path.classList.toggle('dim', id != null && !hot); - if (c4) e.path.setAttribute('marker-end', hot ? 'url(#arrow-hot)' : 'url(#arrow)'); + for (const e of edges) { + const copy = e.path.cloneNode(false); + copy.classList.add('hot'); + if (c4) copy.setAttribute('marker-end', 'url(#arrow-hot)'); + lit.append(copy); if (e.labelEl) { - e.labelEl.classList.toggle('dim', id != null && !hot); - if (S.scene.quietLabels) e.labelEl.style.display = hot ? '' : 'none'; - } - if (hot) { - near.add(e.from); - near.add(e.to); + e.labelEl.classList.add('hot'); + if (quiet) e.labelEl.style.display = ''; } + near.add(e.from); + near.add(e.to); } - for (const n of S.scene.nodes) n.el.classList.toggle('hit', id != null && n.id !== id && near.has(n.id)); + near.delete(id); + const nodes = [...near].map(n => S.byId.get(n)).filter(Boolean); + for (const n of nodes) n.el.classList.add('hit'); + S.lit = { id, edges, nodes }; + $('wires').classList.toggle('dimming', id != null); + $('labels').classList.toggle('dimming', id != null); + } + + // Hover lights a node's wires a moment after the pointer reaches it, and + // lets them go a little later, so a pointer crossing the gaps between nodes + // never flashes the whole scene. + let hoverTimer = 0; + function hoverSoon() { + clearTimeout(hoverTimer); + hoverTimer = setTimeout(() => S.selected == null && highlight(S.hover), S.hover == null ? 150 : 30); } // ------------------------------------------------------------- view + // The camera: the surface moved and scaled as one layer, and the grid with + // it - set on those two alone, never as a property the thousands of nodes + // below would inherit and each be styled again for. function applyView() { const vp = $('viewport'); - vp.style.setProperty('--zoom', S.zoom); - vp.style.setProperty('--pan-x', S.panX); - vp.style.setProperty('--pan-y', S.panY); + $('surface').style.transform = `translate(${S.panX}px, ${S.panY}px) scale(${S.zoom})`; + vp.style.backgroundSize = `${20 * S.zoom}px ${20 * S.zoom}px`; + vp.style.backgroundPosition = `${S.panX}px ${S.panY}px`; // at 30% and below the grid goes - see `.viewport.far` vp.classList.toggle('far', S.zoom <= 0.3); - $('zoom-level').textContent = Math.round(S.zoom * 100) + '%'; + // far out, a whole percent would read 0 + const pct = S.zoom * 100; + $('zoom-level').textContent = (pct >= 10 ? Math.round(pct) : pct >= 1 ? pct.toFixed(1) : pct.toPrecision(2)) + '%'; + // a big scene zoomed out reads as its groups, and zoomed back in as its nodes + regroup(); + } + + // the camera once a frame, however many pointer or wheel events the frame brings + let viewFrame = 0; + function viewSoon() { + if (viewFrame) return; + viewFrame = requestAnimationFrame(() => { + viewFrame = 0; + applyView(); + }); } function bounds() { @@ -1421,12 +2331,19 @@ // and is often far wider than the screen: past the point where its text // stops being legible it opens on the entry node instead. `whole` forces // the full view. + // as far out as the camera goes: as far as wanted - the floor only keeps + // the zoom from reaching nothing + const MIN_ZOOM = 1e-6; + const minZoom = () => MIN_ZOOM; + function fit(whole) { + // fitted whole, a big scene reads as its top groups again + if (S.opened) S.opened.clear(); const vp = $('viewport'); const b = bounds(); const vw = vp.clientWidth; const vh = vp.clientHeight; - const z = clamp(Math.min((vw - 96) / b.w, (vh - 150) / b.h), 0.08, 1.15); + const z = clamp(Math.min((vw - 96) / b.w, (vh - 150) / b.h), minZoom(), 1.15); const entry = S.scene && S.scene.entry; if (whole !== true && entry && z < 0.55) { S.zoom = clamp((vh - 150) / b.h, 0.55, 1); @@ -1441,11 +2358,11 @@ } function zoomAt(mx, my, factor) { - const z = clamp(S.zoom * factor, 0.08, 3); + const z = clamp(S.zoom * factor, minZoom(), 3); S.panX = mx - ((mx - S.panX) * z) / S.zoom; S.panY = my - ((my - S.panY) * z) / S.zoom; S.zoom = z; - applyView(); + viewSoon(); } function zoomBy(factor) { @@ -1460,6 +2377,8 @@ S.selected = id; highlight(id); if (id == null) return hidePanel(); + // a node a folded group stands in for is gone to, near enough to see it + if (hiddenByGroup(id)) flyTo([S.byId.get(id)]); showPanelFor(S.byId.get(id)); clearOfPanel(S.byId.get(id)); } @@ -1485,7 +2404,10 @@ } function drill(n) { - if (n && n.drill) go(n.drill); + if (!n || !n.drill) return; + // where the view opened from, for it to grow out of + S.via = n.el.getBoundingClientRect(); + go(n.drill); } function hidePanel() { @@ -1836,7 +2758,7 @@ const id = nodeEl ? nodeEl.dataset.id : null; if (id !== S.hover) { S.hover = id; - highlight(id); + hoverSoon(); } } return; @@ -1854,7 +2776,7 @@ if (drag.mode === 'pan') { S.panX = drag.px + dx; S.panY = drag.py + dy; - applyView(); + viewSoon(); return; } const n = drag.n; @@ -1910,7 +2832,7 @@ } else { S.panX -= e.deltaX; S.panY -= e.deltaY; - applyView(); + viewSoon(); } }, { passive: false } @@ -1984,7 +2906,7 @@ else if (e.key === 'Enter' && S.selected) drill(S.byId.get(S.selected)); else if (e.key === ',' || e.key === '<') stepBy(-1); else if (e.key === '.' || e.key === '>') stepBy(1); - else if (e.key === 'l' || e.key === 'L') followLive(); + else if (e.key === 'l' || e.key === 'L') toggleFollow(); else if (e.key === '[') groupBy(-1); else if (e.key === ']') groupBy(1); }); @@ -2032,10 +2954,10 @@ pumping: false, // the step shown last while following, and when shown: null, + // the step controls opened by hand while following + unfolded: false, // each step on show is seen alone: what it touched in full, the rest faded focus: store.get('focus', true), - // the agent session on show - one at a time, and undefined for the latest - session: undefined, }; const MAX_TIMELINE = 200; const STATUS = { @@ -2044,7 +2966,7 @@ reverted: 'reverted - put back', discarded: 'discarded - dropped unwritten', edited: 'edited by hand - saved outside ccc’s edit tools', - read: 'read - an agent looked at it through ccc’s tools', + inspect: 'inspect - an agent read it, through ccc’s read tools or its own', }; const sameFn = (t, f) => t.file === f.file && t.name === f.name && (t.owner || null) === (f.owner || null); @@ -2096,6 +3018,8 @@ A.of = new Map(); A.groups.forEach((g, i) => g.steps.forEach(seq => A.of.set(seq, i))); drawTimeline(); + // which steps make up the collection on show may have just been learned + showKept(); if (S.asksPanel) panelAsks(); } @@ -2109,7 +3033,7 @@ function summary(ev) { const fn = ev.focus && ev.focus.level === 'flow' ? ev.focus.name : null; const files = ev.files.length === 1 ? base(ev.files[0].path) : plural(ev.files_total, 'file'); - if (ev.status === 'read') return `looked at ${fn || files}`; + if (ev.status === 'inspect') return `inspected ${fn || files}`; const added = ev.files.reduce((n, f) => n + f.added, 0); const removed = ev.files.reduce((n, f) => n + f.removed, 0); // one part of a step: the definition it is about, and which part of how many @@ -2151,10 +3075,11 @@ const fn = S.scene.entry && S.scene.entry.data.function; const t = fn && ev.functions.find(x => sameFn(x, fn)); if (!t || layoutOnly(t)) return null; - if (n.kind === 'entry') return t.change === 'modified' ? null : t.change; const lines = t['lines_' + side] || []; + // the entry stands for the signature: marked when a change reaches its first line + if (n.kind === 'entry') return t.change !== 'modified' ? t.change : t[side] && within(t[side].start, lines) ? 'modified' : null; const hit = n.kind === 'calls' ? d.calls.some(c => within(c.line, lines)) : within(d.line, lines); - return hit ? (t.change === 'added' || t.change === 'read' ? t.change : 'modified') : null; + return hit ? (t.change === 'added' || t.change === 'inspect' ? t.change : 'modified') : null; } case 'code': { if (n.kind !== 'function' && n.kind !== 'proxy') return null; @@ -2174,8 +3099,8 @@ } } - // how a level above the code took part: looked at, or changed - const touched = ev => (ev.status === 'read' ? 'read' : 'modified'); + // how a level above the code took part: inspected, or changed + const touched = ev => (ev.status === 'inspect' ? 'inspect' : 'modified'); // A change with no step of its own - lines removed, a statement that calls // nothing - is marked on the step it lands after, or on the entry. @@ -2206,7 +3131,7 @@ hit.push(n); }; for (const n of S.scene.nodes) { - n.el.classList.remove('chg', 'chg-added', 'chg-modified', 'chg-removed', 'chg-pending', 'chg-read'); + n.el.classList.remove('chg', 'chg-added', 'chg-modified', 'chg-removed', 'chg-pending', 'chg-inspect'); const how = m ? changeOf(n, m.ev, m.side) : null; if (how) mark(n, how); } @@ -2215,6 +3140,7 @@ if (near) mark(near, 'modified'); } fade(hit); + markGroups(); return hit; } @@ -2223,7 +3149,8 @@ // into the canvas. function fade(hit) { const on = !!(S.scene && S.focusing && hit.length); - const keep = new Set(hit.map(n => n.id)); + // what the step touched, and what a connection it made reaches + const keep = new Set([...hit.map(n => n.id), ...(S.alsoLit || [])]); for (const n of S.scene ? S.scene.nodes : []) n.el.classList.toggle('faded', on && !keep.has(n.id)); for (const e of S.scene ? S.scene.edges : []) { const lit = keep.has(e.from) || keep.has(e.to); @@ -2242,7 +3169,8 @@ } // glide the camera onto the marked nodes, near enough to read them - function flyTo(nodes) { + // `now` - straight there, as the motion of a new scene carries the camera + function flyTo(nodes, now) { const vp = $('viewport'); const bar = $('timeline'); const x0 = Math.min(...nodes.map(n => n.x)); @@ -2251,11 +3179,12 @@ const y1 = Math.max(...nodes.map(n => n.y + n.h)); const vw = vp.clientWidth - ($('panel').hidden ? 0 : 352); const vh = vp.clientHeight - (bar.hidden ? 0 : bar.offsetHeight + 12); - const floor = S.route && S.route.level === 'flow' ? 0.6 : 0.3; + // near enough a grouped scene that its nodes are back + const floor = S.route && S.route.level === 'flow' ? 0.6 : S.scene && S.scene.groups ? GROUP_SHOW + 0.05 : 0.3; S.zoom = clamp(Math.min((vw - 120) / Math.max(1, x1 - x0), (vh - 120) / Math.max(1, y1 - y0)), floor, 1.1); S.panX = vw / 2 - ((x0 + x1) / 2) * S.zoom; S.panY = vh / 2 - ((y0 + y1) / 2) * S.zoom; - glide(); + if (!now) glide(); applyView(); } @@ -2267,6 +3196,9 @@ glideTimer = setTimeout(() => vp.classList.remove('glide'), 700); } + // how long a write is shown as it was before it becomes what it is + const BEFORE_MS = 650; + // a step just landed: show it where it happened, as the map holds it now async function arrive(ev) { const i = T.steps.indexOf(ev); @@ -2291,8 +3223,18 @@ } // a step not written yet: draw the map as it would leave it, to look over if (ev.status === 'staged' && ev.snapshot && ev.focus) return replay(T.at, 'after', true); - // a look moved nothing, so the overview on show still holds - if (ev.status !== 'staged' && ev.status !== 'read') S.overview = null; + // A write to one function or file seen from elsewhere: shown first as it + // was, a moment, then becoming what it is - its pins, calls and words + // moving to their new places. + const near = ev.focus && (ev.focus.level === 'flow' || ev.focus.level === 'code'); + const was = focusRoute(ev, 'before'); + if (near && was && ev.snapshot && ['applied', 'reverted', 'edited'].includes(ev.status) && !calm() && !sameView(S.shownRoute, was)) { + await replay(T.at, 'before', true); + await sleep(BEFORE_MS / T.speed); + if (T.steps[T.at] !== ev || !T.follow) return; + } + // an inspect moved nothing, so the overview on show still holds + if (ev.status !== 'staged' && ev.status !== 'inspect') S.overview = null; try { await ensureOverview(); } catch { @@ -2300,20 +3242,53 @@ } S.marks = { ev, side: ev.on_disk }; S.fly = true; - const route = focusRoute(ev, ev.on_disk); + // A step in a file the map does not hold - a stylesheet, a README - is seen + // where the latest step before it that the map holds happened, never + // wherever the view was left: following goes on from the latest point. + if (!focusRoute(ev, ev.on_disk)) S.offMapStep = ev; + const route = focusRoute(ev, ev.on_disk) || placeBefore(T.at); if (route) go(route); else render(true); } + // A step in files the map does not draw - a stylesheet, a README - shown + // by what it changed, in the panel, once the view it is seen from is up; + // never over the list of asks. + function showOffMap() { + const ev = S.offMapStep; + S.offMapStep = null; + if (ev && !S.asksPanel) panelEvent(ev); + } + + // where on the map the latest step before `i` happened, if any did + function placeBefore(i) { + for (let j = Math.min(i, T.steps.length) - 1; j >= 0; j--) { + const r = focusRoute(T.steps[j], T.steps[j].on_disk); + if (r) return r; + } + return null; + } + + // The follow switch: on, each live change is shown as it lands; off, the + // step on show stays. + function toggleFollow() { + if (!(T.follow && (!T.replay || T.replay.live))) return followLive(); + stopPlay(); + T.follow = false; + T.queue = []; + drawTimeline(); + } + // the live map again, the newest step shown as if it had just landed, and // each step after it followed function followLive() { stopPlay(); T.follow = true; - // live is now, whatever was still waiting its turn, in the latest session + // the step controls fold away again + T.unfolded = false; + // live is now, whatever was still waiting its turn, in the latest ask T.queue = []; T.shown = null; - T.session = undefined; endReplay(); const last = T.steps[T.steps.length - 1]; if (!last || last.status === 'discarded') { @@ -2351,8 +3326,6 @@ T.queue.shift(); if (!T.steps.includes(ev)) continue; T.shown = { ev, at: Date.now() }; - // following is the latest session - T.session = undefined; await arrive(ev); } } finally { @@ -2412,9 +3385,14 @@ } catch { return; } - const route = focusRoute(ev, ev.on_disk); + // a step the map has no place for shows what it changed in the panel + if (!focusRoute(ev, ev.on_disk)) S.offMapStep = ev; + const route = focusRoute(ev, ev.on_disk) || placeBefore(i); if (route) go(route); - else drawTimeline(); + else { + drawTimeline(); + showOffMap(); + } return; } const snap = full[side]; @@ -2463,7 +3441,7 @@ drawTimeline(); } - // the step before or after the one on show, within the session on show + // the step before or after the one on show, within the ask on show function stepBy(d) { const shown = shownSteps(); if (!shown.length) return; @@ -2501,39 +3479,45 @@ drawTimeline(); } - // Runs of asks by the agent session they came from. Changes made by hand, - // and steps no transcript explains, go with the session they fall in. - function sessionSpans() { - const spans = []; - for (const [gi, g] of A.groups.entries()) { - const s = g.ask ? g.ask.session : null; - let cur = spans[spans.length - 1]; - if (!cur || (s && cur.session && cur.session !== s)) spans.push((cur = { session: s, groups: new Set() })); - else if (s && !cur.session) cur.session = s; - cur.groups.add(gi); - } - return spans; + // The collection a group of steps belongs to: every step of one ask, + // wherever they fall, or one run of changes made by hand, or of steps no + // transcript explains. The timeline holds one collection at a time. + const collectionOf = gi => (A.groups[gi]?.ask ? 'ask:' + A.groups[gi].ask.id : 'run:' + gi); + + // the collection of the step on show - undefined while its ask is not known yet + const collectionAt = () => { + const gi = T.at != null ? A.of.get(T.steps[T.at]?.seq) : undefined; + return gi === undefined ? undefined : collectionOf(gi); + }; + + // The collections on the timeline in the order they began, each with the + // numbers of its steps. + function collections() { + const out = new Map(); + A.groups.forEach((g, gi) => { + const key = collectionOf(gi); + if (!out.has(key)) out.set(key, { key, group: g, steps: [] }); + out.get(key).steps.push(...g.steps); + }); + return [...out.values()]; } - // The steps of the session on show, as indices into `steps` - the latest - // unless one was picked. A step no ask is known for yet is the newest, so - // it goes with the latest. + // The steps of the collection on show, as indices into `steps`: that of the + // step on show, else the latest - so following, each new ask starts the + // timeline afresh. A step no ask is known for yet is the newest, so it goes + // with the latest. function shownSteps() { - const spans = sessionSpans(); - if (spans.length < 2) return T.steps.map((_, i) => i); - const latest = spans[spans.length - 1]; - const want = (T.session !== undefined && spans.find(s => s.session === T.session)) || latest; + if (!A.groups.length) return T.steps.map((_, i) => i); + const latest = collectionOf(A.groups.length - 1); + const want = collectionAt() || latest; const out = []; T.steps.forEach((ev, i) => { const gi = A.of.get(ev.seq); - if (gi === undefined ? want === latest : want.groups.has(gi)) out.push(i); + if (gi === undefined ? want === latest : collectionOf(gi) === want) out.push(i); }); return out; } - // the session a group of steps belongs to - const sessionOf = gi => (sessionSpans().find(s => s.groups.has(gi)) || {}).session; - function drawTimeline() { const bar = $('timeline'); const n = T.steps.length; @@ -2544,16 +3528,11 @@ document.body.classList.toggle('with-tl-switches', !n); const ticks = $('tl-ticks'); ticks.textContent = ''; - let run; - // one session at a time - a long history drawn whole is what lags + // one collection at a time - scrubbing stays inside it, and a long history drawn whole is what lags const shown = shownSteps(); - shown.forEach((i, k) => { + shown.forEach(i => { const ev = T.steps[i]; - const gi = A.of.get(ev.seq); - // each ask's steps sit together, set off from the next - if (k > 0 && gi !== run) ticks.append(h('b', 'tl-sep')); - run = gi; - const g = A.groups[gi]; + const g = A.groups[A.of.get(ev.seq)]; const t = h('i', `tick ${ev.status}${byHand(ev) ? ' human' : ''}${layoutOnly(ev) ? ' ws' : ''}${i === T.at ? ' at' : ''}`); t.title = `${g ? groupText(g) + '\n' : ''}#${ev.seq} ${ev.status} · ${ev.tool} · ${summary(ev)}${why(ev) ? ' · ' + why(ev) : ''}`; t.dataset.i = i; @@ -2570,11 +3549,15 @@ const g = groupOf(ev); const chip = $('tl-ask'); chip.hidden = !A.groups.length; - chip.replaceChildren(...(g ? [groupIcon(g)] : []), h('span', 'ask-label', g ? groupText(g) : 'Asks')); - chip.title = `${g ? groupText(g) + '\n\n' : ''}Every ask and what it changed - [ and ] step between them`; - const cur = ev ? A.of.get(ev.seq) : undefined; - for (const row of document.querySelectorAll('#panel .ask-row')) row.classList.toggle('at', +row.dataset.g === cur); const k = shown.indexOf(T.at); + chip.replaceChildren( + ...(g ? [groupIcon(g)] : []), + h('span', 'ask-label', g ? groupText(g) : 'Asks'), + ...(g && k >= 0 ? [h('span', 'ask-n', `${k + 1} / ${shown.length}`)] : []) + ); + chip.title = `${g ? groupText(g) + '\n\n' : ''}Every ask and what it changed - [ and ] step between them`; + const cur = collectionAt(); + for (const row of document.querySelectorAll('#panel .ask-row')) row.classList.toggle('at', row.dataset.c === cur); $('tl-prev').disabled = !shown.length || k === 0; $('tl-next').disabled = !shown.length || T.at == null || k >= shown.length - 1; $('tl-play').disabled = !n; @@ -2587,20 +3570,38 @@ b.disabled = !(full && full[b.dataset.side]); } const live = T.follow && (!T.replay || T.replay.live); - $('tl-follow').setAttribute('aria-pressed', String(live)); + $('tl-follow').setAttribute('aria-checked', String(live)); // slowed, following runs behind by the steps still waiting their turn - $('tl-follow').textContent = live ? (T.queue.length ? `Following · ${T.queue.length} behind` : 'Following') : 'Follow'; + $('tl-behind').replaceChildren(...(live && T.queue.length ? [` · ${T.queue.length}`, h('span', 'tl-behind', ' behind')] : [])); + // Following, the step controls fold away to the left behind an arrow + // that opens them - and stopping opens them too. + const folded = live && !T.unfolded; + const controls = $('tl-controls'); + controls.classList.toggle('folded', folded); + controls.inert = folded; + const unfold = $('tl-unfold'); + unfold.hidden = !live; + unfold.textContent = folded ? '»' : '«'; + unfold.title = folded ? 'Show the step controls' : 'Fold the step controls away'; + unfold.setAttribute('aria-label', unfold.title.toLowerCase()); + unfold.setAttribute('aria-expanded', String(!folded)); $('tl-ws').setAttribute('aria-pressed', String(T.ws)); $('tl-speed').textContent = SPEED_LABEL[T.speed]; $('tl-focus').setAttribute('aria-pressed', String(T.focus)); const badge = $('replay-badge'); - badge.hidden = !T.replay || !ev; + // a step only in files the map does not draw - a stylesheet, a README - + // moves nothing on it, so it says so whether replayed or just landed + const offMap = !!ev && !!S.overview && ev.status !== 'inspect' && ev.files.length > 0 && !focusRoute(ev, ev.on_disk); + badge.hidden = !ev || !(T.replay || offMap); if (!badge.hidden) { badge.replaceChildren( ...(byHand(ev) ? [person()] : []), - T.replay.live - ? `Proposed · step #${ev.seq}${why(ev) ? ' · ' + why(ev) : ' - staged, not written yet'}` - : `Replay · step #${ev.seq} ${ev.status} · ${T.side} it${why(ev) ? ' · ' + why(ev) : ''}`, + !T.replay + ? `Step #${ev.seq} ${ev.status}${why(ev) ? ' · ' + why(ev) : ''}` + : T.replay.live + ? `Proposed · step #${ev.seq}${why(ev) ? ' · ' + why(ev) : ' - staged, not written yet'}` + : `Replay · step #${ev.seq} ${ev.status} · ${T.side} it${why(ev) ? ' · ' + why(ev) : ''}`, + ...(offMap ? [h('span', 'off-map', ` · ${ev.files.map(f => base(f.path)).join(', ')} not on the map`)] : []), ); badge.title = (ev.intent || []).join('\n'); } @@ -2625,11 +3626,19 @@ function panelEvent(ev) { select(null); - const look = ev.status === 'read'; - panel((look ? 'Look #' : 'Edit step #') + ev.seq, `${ev.status} · ${ev.tool}`, body => { - const by = byHand(ev) ? 'a person, by hand' : look ? 'an agent, through ccc’s read tools' : 'an agent, through ccc’s edit tools'; + const look = ev.status === 'inspect'; + // a read an agent took with its own tool names the call, found in its transcript + const own = look && !!ev.call; + panel((look ? 'Inspect #' : 'Edit step #') + ev.seq, `${ev.status} · ${ev.tool}`, body => { + const by = byHand(ev) + ? 'a person, by hand' + : own + ? `an agent, with its own ${ev.tool} tool` + : look + ? 'an agent, through ccc’s read tools' + : 'an agent, through ccc’s edit tools'; const rows = [['status', STATUS[ev.status]], ['by', by]]; - if (ev.intent && ev.intent.length) rows.push([look ? 'asked' : 'why', ev.intent.join(' · ')]); + if (ev.intent && ev.intent.length) rows.push([own ? 'read' : look ? 'asked' : 'why', ev.intent.join(' · ')]); if (ev.part) rows.push(['part', `${ev.part.index + 1} of ${ev.part.of} - ${ev.part.kind} ${ev.part.change}`]); const g = groupOf(ev); if (g && g.ask) rows.push(['ask', g.ask.prompt]); @@ -2644,7 +3653,7 @@ row.append(h('span', 'chg-tag ' + (t.whitespace ? 'ws' : t.change), t.whitespace ? 'layout' : t.change), goBtn(`${t.owner ? t.owner + '::' : ''}${t.name} ${base(t.file)}`, route)); return row; }); - section(body, look ? 'Looked at' : 'Functions', list(rows)); + section(body, look ? 'Inspected' : 'Functions', list(rows)); } if (ev.ops.length) section(body, 'Operations', list(ev.ops.map(o => h('code', null, o)))); if (look) section(body, 'Files', list(ev.files.map(f => goBtn(f.path, { level: 'code', file: f.path })))); @@ -2653,17 +3662,20 @@ S.stepPanel = true; } - // Every ask the timeline holds, in order, under the session it was made in: - // pick one to walk through its steps, or play a whole session through. + // Every collection the timeline holds - each ask, each run of changes by + // hand - in order, under the session it was made in: pick one and the + // timeline holds its steps alone, or play a whole session through. function panelAsks() { const keep = S.asksPanel ? $('panel-body').scrollTop : 0; select(null); - const asks = new Set(A.groups.filter(g => g.ask).map(g => g.ask.id)).size; + const all = collections(); + const asks = all.filter(c => c.group.ask).length; panel('Asks', `${plural(asks, 'ask')} · ${plural(T.steps.length, 'step')}`, body => { - if (!A.groups.length) body.append(h('p', 'hint', 'No steps yet - each ask an agent works on lands here, and every change made by hand')); - const cur = A.of.get(T.steps[T.at]?.seq); + if (!all.length) body.append(h('p', 'hint', 'No steps yet - each ask an agent works on lands here, and every change made by hand')); + const cur = collectionAt(); let session; - A.groups.forEach((g, gi) => { + for (const c of all) { + const g = c.group; if (g.ask && g.ask.session !== session) { session = g.ask.session; const s = session; @@ -2675,18 +3687,18 @@ head.append(all); body.append(head); } - const row = h('div', 'ask-row' + (gi === cur ? ' at' : '')); - row.dataset.g = gi; + const row = h('div', 'ask-row' + (c.key === cur ? ' at' : '')); + row.dataset.c = c.key; const go = h('button', 'ask-go'); - go.append(groupIcon(g), h('span', 'ask-text', groupText(g)), h('span', 'ask-n', plural(g.steps.length, 'step'))); + go.append(groupIcon(g), h('span', 'ask-text', groupText(g)), h('span', 'ask-n', plural(c.steps.length, 'step'))); go.title = groupText(g); - go.addEventListener('click', () => goGroup(gi)); + go.addEventListener('click', () => goCollection(c)); const one = h('button', 'ask-play', '▶'); one.title = 'Replay this ask’s steps'; - one.addEventListener('click', () => play(indicesOf(g.steps))); + one.addEventListener('click', () => play(indicesOf(c.steps))); row.append(go, one); body.append(row); - }); + } }); S.asksPanel = true; $('panel-body').scrollTop = keep; @@ -2695,22 +3707,21 @@ // where steps sit on the timeline, by their numbers const indicesOf = seqs => seqs.map(seq => T.steps.findIndex(e => e.seq === seq)).filter(i => i >= 0); - // a group, from its first step - function goGroup(gi) { - const g = A.groups[gi]; - const [i] = g ? indicesOf(g.steps) : []; + // a collection, from its first step - the timeline then holds it alone + function goCollection(c) { + const [i] = c ? indicesOf(c.steps) : []; if (i == null) return; stopPlay(); - T.session = sessionOf(gi); replay(i, 'after'); } - // the ask before or after the one on show + // the collection before or after the one on show function groupBy(d) { - if (!A.groups.length) return; - const cur = A.of.get(T.steps[T.at]?.seq); - const from = cur == null ? (d > 0 ? -1 : A.groups.length) : cur; - goGroup(clamp(from + d, 0, A.groups.length - 1)); + const all = collections(); + if (!all.length) return; + const k = all.findIndex(c => c.key === collectionAt()); + const from = k < 0 ? (d > 0 ? -1 : all.length) : k; + goCollection(all[clamp(from + d, 0, all.length - 1)]); } // every step from a session's first ask to its last, changes by hand between them included @@ -2718,11 +3729,22 @@ const at = A.groups.map((g, i) => (g.ask && g.ask.session === session ? i : -1)).filter(i => i >= 0); if (!at.length) return; stopPlay(); - T.session = session; play(indicesOf(A.groups.slice(at[0], at[at.length - 1] + 1).flatMap(g => g.steps))); } + // how far the timeline's top sits from the window's foot, for what keeps clear of it + function clearOfTimeline() { + const bar = $('timeline'); + const set = () => { + const top = bar.hidden ? 0 : Math.ceil(window.innerHeight - bar.getBoundingClientRect().top); + document.body.style.setProperty('--tl-top', top + 'px'); + }; + new ResizeObserver(set).observe(bar); + window.addEventListener('resize', set); + } + function bindTimeline() { + clearOfTimeline(); const track = $('tl-track'); let scrubbing = false; let timer = 0; @@ -2764,7 +3786,11 @@ $('tl-prev').addEventListener('click', () => stepBy(-1)); $('tl-next').addEventListener('click', () => stepBy(1)); $('tl-play').addEventListener('click', () => play()); - $('tl-follow').addEventListener('click', () => followLive()); + $('tl-follow').addEventListener('click', () => toggleFollow()); + $('tl-unfold').addEventListener('click', () => { + T.unfolded = !T.unfolded; + drawTimeline(); + }); $('tl-speed').addEventListener('click', e => { // shift steps back up through the speeds const i = SPEEDS.indexOf(T.speed); @@ -2814,9 +3840,9 @@ } const fresh = r.events || []; T.steps.push(...fresh); - // past the cap a look goes before an edit does, as on the server + // past the cap an inspect goes before an edit does, as on the server while (T.steps.length > MAX_TIMELINE) { - const look = T.steps.slice(0, T.steps.length >> 1).findIndex(e => e.status === 'read'); + const look = T.steps.slice(0, T.steps.length >> 1).findIndex(e => e.status === 'inspect'); const cut = look < 0 ? 0 : look; T.steps.splice(cut, 1); if (T.at != null && T.at > cut) T.at--; diff --git a/docs/EXTENSION.md b/docs/EXTENSION.md index 6022aca..cb9dac5 100644 --- a/docs/EXTENSION.md +++ b/docs/EXTENSION.md @@ -63,7 +63,8 @@ steps along its foot. While an agent edits through ccc's tools, it flies to each and marks it, and any step can be replayed before and after. A staged change is drawn as **Proposed** - the code as it would be - before anything is written, beside the one-line intent the agent gave for it. Changes saved by hand land on the timeline too, marked with a person. Each edit -plays a definition at a time and an agent's reads land as looks, at the pace the speed button sets - +plays a definition at a time and an agent's reads land as inspects - through ccc or with its own +tools - one ask at a time, at the pace the speed button sets - it is the same page as in a browser, so it behaves the same there (see the [README](../README.md#architecture-visualiser)). A change that only moves whitespace or wraps lines is not marked unless ¶ is on. Its title-bar buttons open the same diff --git a/docs/MCP.md b/docs/MCP.md index 4d937ec..79baa19 100644 --- a/docs/MCP.md +++ b/docs/MCP.md @@ -51,6 +51,8 @@ What every write is held to: `file://` report) can read the map but never write through it. - **Attributed.** Each applied changeset is appended to `.ccc/edits.jsonl`, which is kept out of git. `ccc prompts` reads it to credit a ccc edit to the request behind it, as strongly as it credits an `Edit` or `Write`. -- **Shared.** Every step - staged, applied, reverted, and each look a read tool took - is appended to +- **Shared.** Every step - staged, applied, reverted, and each inspect an agent made - is appended to `.ccc/timeline.jsonl`, also kept out of git, so the visualiser of any ccc server on the project shows it. + An inspect is a read tool's answer, or a file the agent read with its own tools, which the server + finds in Claude Code's transcripts (subagents included) every couple of seconds. diff --git a/src/inspect.rs b/src/inspect.rs new file mode 100644 index 0000000..279ef57 --- /dev/null +++ b/src/inspect.rs @@ -0,0 +1,471 @@ +//! What agents read with their own tools - a `Read`, a `cat` or a `sed -n` in +//! a shell - taken from their transcripts as they are written. ccc's read tools +//! put their looks on the timeline as they answer; these are the rest, so the +//! visualiser shows an agent inspecting code whichever way it looked. +//! +//! Claude Code's transcripts only, a session's subagents with it: Copilot's +//! chat log keeps no record of the files its tools read. + +use crate::model::FileCache; +use serde_json::Value; +use std::collections::BTreeMap; +use std::path::{Component, Path, PathBuf}; + +// transcripts are looked for again this often - a new session, a subagent +const FOUND_EVERY: std::time::Duration = std::time::Duration::from_secs(10); +// and this often when claude keeps no directory for the project, where +// finding them means opening every other project's +const FOUND_SLOW: std::time::Duration = std::time::Duration::from_secs(600); +// the sites one read names past the file itself +const READ_SITES: usize = 59; + +// commands that read the files they name and write none +const READERS: &[&str] = &[ + "cat", "head", "tail", "sed", "awk", "nl", "less", "more", "bat", "batcat", "view", "grep", "egrep", "fgrep", "rg", "ag", + "wc", "cut", "sort", "uniq", "diff", "strings", "xxd", "od", "jq", +]; +// readers whose first word is a pattern or a script rather than a file +const PATTERNED: &[&str] = &["sed", "awk", "grep", "egrep", "fgrep", "rg", "ag", "jq"]; +// words ahead of a command that only change how it runs +const WRAPPERS: &[&str] = &["sudo", "env", "time", "command", "nohup", "exec", "nice"]; + +// One file an agent read with its own tools. +#[derive(Debug, Clone, PartialEq)] +pub struct Read { + pub at_ms: u64, + pub session: String, + // the tool call it was, so it is put down once + pub call: String, + // Read, NotebookRead, Grep, Bash + pub tool: String, + // project-relative + pub path: String, + // 1-based and inclusive, ending at `usize::MAX` for the rest of the file - + // none for the whole of it + pub lines: Option<(usize, usize)>, +} + +impl Read { + // the file and the lines read, as the timeline labels it + pub fn what(&self) -> String { + match self.lines { + None => self.path.clone(), + Some((a, b)) if a == b => format!("{}:{a}", self.path), + Some((a, usize::MAX)) => format!("{}:{a}-", self.path), + Some((a, b)) => format!("{}:{a}-{b}", self.path), + } + } + + // Where on the map it was read, as (file, line - none for the file, main): + // the one function the lines lie in, else the file and each function they + // cross. + pub fn sites(&self, caches: &[FileCache]) -> Vec<(String, usize, bool)> { + let whole = vec![(self.path.clone(), 0, true)]; + let cache = caches.iter().find(|c| crate::changes::path_str(&c.rel_path) == self.path); + let (Some((from, to)), Some(c)) = (self.lines, cache) else { + return whole; + }; + let holding = c + .funcs + .iter() + .filter(|f| f.start_line <= from && to <= f.end_line) + .min_by_key(|f| f.end_line - f.start_line); + if let Some(f) = holding { + return vec![(self.path.clone(), f.line, true)]; + } + let crossed = c.funcs.iter().filter(|f| f.start_line <= to && from <= f.end_line); + whole.into_iter().chain(crossed.take(READ_SITES).map(|f| (self.path.clone(), f.line, false))).collect() + } +} + +// The transcripts of this project's agents, each read as far as it was last. +#[derive(Default)] +pub struct Reads { + // when the transcripts were last looked for + found: Option, + // each, and the bytes of it read so far + read: BTreeMap, +} + +impl Reads { + // The reads agents made since the last call, oldest first. + pub fn fresh(&mut self, root: &Path) -> Vec { + let quick = crate::prompts::claude_root().is_some_and(|b| b.join(crate::prompts::claude_slug(root)).is_dir()); + let every = if quick { FOUND_EVERY } else { FOUND_SLOW }; + if self.found.is_none_or(|t| t.elapsed() >= every) { + let found = transcripts(root); + self.found_now(found); + } + self.tail(root) + } + + // What a transcript held when it was first found is history; one begun + // since is news from its first line. + fn found_now(&mut self, found: Vec) { + let first = self.found.is_none(); + self.read.retain(|f, _| found.contains(f)); + for f in found { + let from = if first { std::fs::metadata(&f).map_or(0, |m| m.len()) } else { 0 }; + self.read.entry(f).or_insert(from); + } + self.found = Some(std::time::Instant::now()); + } + + fn tail(&mut self, root: &Path) -> Vec { + let mut out = Vec::new(); + for (f, from) in self.read.iter_mut() { + let (records, end) = tail(f, *from); + *from = end; + out.extend(records.iter().flat_map(|r| reads_of(r, root))); + } + out.sort_by_key(|r| r.at_ms); + out + } +} + +// claude's transcripts for this project, each session's subagents beside it +fn transcripts(root: &Path) -> Vec { + let mut out = Vec::new(); + for main in crate::prompts::claude_transcripts(root).0 { + out.extend(crate::prompts::jsonl_files(&main.with_extension("").join("subagents"))); + out.push(main); + } + out +} + +// The records a transcript gained past byte `from` that hold a tool call, and +// the byte they end at. A line still being written waits for the next read; a +// transcript shorter than `from` began again, so it is read from the top. +fn tail(path: &Path, from: u64) -> (Vec, u64) { + use std::io::{Read as _, Seek, SeekFrom}; + let Ok(mut f) = std::fs::File::open(path) else { + return (Vec::new(), from); + }; + let from = if f.metadata().map_or(0, |m| m.len()) < from { 0 } else { from }; + let mut buf = Vec::new(); + if f.seek(SeekFrom::Start(from)).is_err() || f.read_to_end(&mut buf).is_err() { + return (Vec::new(), from); + } + let end = buf.iter().rposition(|&b| b == b'\n').map_or(0, |i| i + 1); + let call = b"\"tool_use\""; + let records = buf[..end] + .split(|&b| b == b'\n') + // a tool's answer can run to megabytes - only the calls are parsed + .filter(|l| l.windows(call.len()).any(|w| w == call)) + .filter_map(|l| serde_json::from_slice(l).ok()) + .collect(); + (records, from + end as u64) +} + +// the files inside the project one transcript record's tool calls read +fn reads_of(rec: &Value, root: &Path) -> Vec { + if rec["type"] != "assistant" { + return Vec::new(); + } + let at_ms = rec["timestamp"] + .as_str() + .and_then(|t| chrono::DateTime::parse_from_rfc3339(t).ok()) + .map_or(0, |d| d.timestamp_millis().max(0) as u64); + let session = rec["sessionId"].as_str().unwrap_or_default(); + let cwd = rec["cwd"].as_str().map_or_else(|| root.to_path_buf(), PathBuf::from); + let mut out = Vec::new(); + for b in rec["message"]["content"].as_array().into_iter().flatten() { + let (Some("tool_use"), Some(tool), Some(call)) = (b["type"].as_str(), b["name"].as_str(), b["id"].as_str()) else { + continue; + }; + let input = &b["input"]; + let named: Vec<(PathBuf, Option<(usize, usize)>)> = match tool { + "Read" => input["file_path"].as_str().map(|p| (cwd.join(p), read_range(input))).into_iter().collect(), + "NotebookRead" => input["notebook_path"].as_str().map(|p| (cwd.join(p), None)).into_iter().collect(), + // a search of one file looks at it; one of a directory names no file until it answers + "Grep" => input["path"].as_str().map(|p| (cwd.join(p), None)).into_iter().collect(), + "Bash" => input["command"].as_str().map(|c| shell_reads(c, &cwd)).unwrap_or_default(), + _ => Vec::new(), + }; + out.extend(named.into_iter().filter_map(|(p, lines)| { + let path = inside(root, &p)?; + Some(Read { at_ms, session: session.to_string(), call: call.to_string(), tool: tool.to_string(), path, lines }) + })); + } + out +} + +// the lines a `Read` asked for - none when it asked for the whole file +fn read_range(input: &Value) -> Option<(usize, usize)> { + let num = |v: &Value| v.as_u64().or_else(|| v.as_str()?.trim().parse().ok()).map(|n| n as usize); + let (offset, limit) = (num(&input["offset"]), num(&input["limit"])); + if offset.is_none() && limit.is_none() { + return None; + } + let from = offset.unwrap_or(1).max(1); + Some((from, limit.map_or(usize::MAX, |l| from + l.max(1) - 1))) +} + +// an absolute path as a path inside the project, relative to it +fn inside(root: &Path, path: &Path) -> Option { + let mut abs = PathBuf::new(); + for c in path.components() { + match c { + Component::ParentDir => { + abs.pop(); + } + Component::CurDir => {} + c => abs.push(c), + } + } + let rel = abs.strip_prefix(root).ok()?; + rel.components().next().is_some().then(|| crate::changes::path_str(rel)) +} + +// The files a shell command line reads, each with the lines a `sed -n 'a,bp'` +// or a `head -n` took where it says. A word is taken for a file only where the +// command it follows reads files and writes none. +fn shell_reads(line: &str, cwd: &Path) -> Vec<(PathBuf, Option<(usize, usize)>)> { + let mut cwd = cwd.to_path_buf(); + let mut out = Vec::new(); + for (words, fed) in commands(line) { + let mut words = words.iter().map(String::as_str).skip_while(|w| assignment(w) || WRAPPERS.contains(w)); + let Some(cmd) = words.next() else { + continue; + }; + let args: Vec<&str> = words.collect(); + let name = cmd.rsplit('/').next().unwrap_or(cmd); + if name == "cd" { + if let Some(dir) = args.first() { + cwd = cwd.join(dir); + } + continue; + } + let in_place = name == "sed" && args.iter().any(|a| a.starts_with("-i") || a.starts_with("--in-place")); + if !READERS.contains(&name) || in_place { + continue; + } + let lines = match name { + "sed" => args.iter().find_map(|a| sed_range(a)), + "head" => head_count(&args).map(|n| (1, n)), + _ => None, + }; + // a search's or an edit script's first word is its pattern, unless a flag gave it + let given = args.iter().any(|a| matches!(*a, "-e" | "-f" | "--regexp" | "--expression" | "--file")); + let pattern = usize::from(PATTERNED.contains(&name) && !given); + let words = args.iter().copied().filter(|a| !a.starts_with('-') && !a.bytes().all(|b| b.is_ascii_digit())); + let files = words.skip(pattern).chain(fed.iter().map(String::as_str)); + out.extend(files.map(|f| (cwd.join(f), lines))); + } + out +} + +// `NAME=value` ahead of a command +fn assignment(word: &str) -> bool { + word.split_once('=') + .is_some_and(|(k, _)| !k.is_empty() && k.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')) +} + +// the lines `a,bp` or `ap` prints under `sed -n` +fn sed_range(script: &str) -> Option<(usize, usize)> { + let body = script.strip_suffix('p')?; + let (a, b) = body.split_once(',').unwrap_or((body, body)); + let a: usize = a.parse().ok()?; + let b = if b == "$" { usize::MAX } else { b.parse().ok()? }; + (a >= 1 && b >= a).then_some((a, b)) +} + +// how many lines a `head` took, where it says +fn head_count(args: &[&str]) -> Option { + let mut it = args.iter(); + while let Some(a) = it.next() { + let n: Option = match *a { + "-n" | "--lines" => it.next().and_then(|n| n.parse().ok()), + a => a + .strip_prefix("--lines=") + .or_else(|| a.strip_prefix("-n")) + .or_else(|| a.strip_prefix('-')) + .and_then(|n| n.parse().ok()), + }; + if n.is_some() { + return n.filter(|&n| n > 0); + } + } + None +} + +// where the word being read goes +#[derive(Clone, Copy)] +enum Into { + Arg, + // fed to the command with `<` + Fed, + // written with `>` - no read + Written, +} + +// a command line on its way to its simple commands +struct Split { + commands: Vec<(Vec, Vec)>, + word: Option, + into: Into, +} + +impl Split { + fn push(&mut self, c: char) { + self.word.get_or_insert_with(String::new).push(c); + } + + fn word(&mut self) { + let Some(w) = self.word.take() else { + return; + }; + let Some((args, fed)) = self.commands.last_mut() else { + return; + }; + match std::mem::replace(&mut self.into, Into::Arg) { + Into::Arg => args.push(w), + Into::Fed => fed.push(w), + Into::Written => {} + } + } + + fn command(&mut self) { + self.word(); + self.commands.push(Default::default()); + } +} + +// A command line as its simple commands, each its words with the quotes taken +// off and the words a `<` feeds it apart. A heredoc's body is text, not +// commands, so the line is read no further than one. +fn commands(line: &str) -> Vec<(Vec, Vec)> { + let mut s = Split { commands: vec![Default::default()], word: None, into: Into::Arg }; + let mut chars = line.chars().peekable(); + while let Some(c) = chars.next() { + match c { + '\'' => { + s.word.get_or_insert_with(String::new); + for c in chars.by_ref().take_while(|&c| c != '\'') { + s.push(c); + } + } + '"' => { + s.word.get_or_insert_with(String::new); + while let Some(c) = chars.next() { + match c { + '"' => break, + '\\' => chars.next().into_iter().for_each(|n| s.push(n)), + c => s.push(c), + } + } + } + '\\' => chars.next().into_iter().filter(|&n| n != '\n').for_each(|n| s.push(n)), + ' ' | '\t' => s.word(), + '\n' | ';' | '|' | '&' | '(' | ')' => s.command(), + '#' if s.word.is_none() => { + chars.by_ref().take_while(|&c| c != '\n').for_each(drop); + s.command(); + } + '<' if chars.peek() == Some(&'<') => break, + '<' => { + s.word(); + s.into = Into::Fed; + } + '>' => { + // the `2` of `2>` is the stream, not a word + if s.word.as_deref().is_some_and(|w| !w.is_empty() && w.bytes().all(|b| b.is_ascii_digit())) { + s.word = None; + } + s.word(); + while chars.next_if(|&c| c == '>' || c == '&').is_some() {} + s.into = Into::Written; + } + c => s.push(c), + } + } + s.command(); + s.commands.retain(|(args, fed)| !args.is_empty() || !fed.is_empty()); + s.commands +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn files(line: &str) -> Vec<(String, Option<(usize, usize)>)> { + shell_reads(line, Path::new("/p/sub")) + .into_iter() + .map(|(p, lines)| (p.to_string_lossy().into_owned(), lines)) + .collect() + } + + // A shell line reads the files its reading commands name - with the lines + // a `sed -n` or `head` took - and nothing a command writes, runs or edits + // in place, a heredoc's text included. + #[test] + fn a_shell_line_reads_what_its_readers_name() { + assert_eq!(files("sed -n '120,180p' src/a.rs"), [("/p/sub/src/a.rs".into(), Some((120, 180)))]); + assert_eq!(files("cd .. && head -n 40 lib.rs | grep fn"), [("/p/sub/../lib.rs".into(), Some((1, 40)))]); + assert_eq!( + files("grep -n \"fn main\" a.rs b.rs 2>/dev/null; cat < c.rs"), + [("/p/sub/a.rs".into(), None), ("/p/sub/b.rs".into(), None), ("/p/sub/c.rs".into(), None)] + ); + assert_eq!(files("LANG=C sudo cat /etc/x"), [("/etc/x".into(), None)]); + assert!(files("sed -i 's/a/b/' a.rs").is_empty()); + assert!(files("cargo test --lib a.rs > out.txt").is_empty()); + assert!(files("cat > a.rs <<'EOF'\nfn x() {}\nEOF").is_empty()); + assert!(files("echo cat a.rs # cat b.rs").is_empty()); + } + + // A read is seen where it was taken: the one function its lines lie in, + // else the file and the functions they cross - the whole file when it + // asked for no lines. + #[test] + fn a_read_is_seen_at_the_function_its_lines_lie_in() { + let src = "fn a() {\n 1;\n}\n\nfn b() {\n 2;\n}\n"; + let caches = vec![crate::scan::read_one(Path::new("src/x.rs"), src).unwrap()]; + let read = |lines| Read { at_ms: 0, session: "s".into(), call: "c".into(), tool: "Read".into(), path: "src/x.rs".into(), lines }; + assert_eq!(read(Some((2, 2))).sites(&caches), [("src/x.rs".to_string(), 1, true)]); + assert_eq!( + read(Some((2, 6))).sites(&caches), + [("src/x.rs".to_string(), 0, true), ("src/x.rs".to_string(), 1, false), ("src/x.rs".to_string(), 5, false)] + ); + assert_eq!(read(None).sites(&caches), [("src/x.rs".to_string(), 0, true)]); + assert_eq!(read(Some((5, usize::MAX))).what(), "src/x.rs:5-"); + } + + // A transcript is read on from where it was last: what it held when it + // was found is history, each call after is a read of what it named inside + // the project, and a line still being written waits for the next look. + #[test] + fn a_transcripts_reads_are_found_as_they_are_written() { + let dir = std::env::temp_dir().join(format!("ccc-inspect-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let root = dir.join("proj"); + std::fs::create_dir_all(&root).unwrap(); + let log = dir.join("s1.jsonl"); + let call = |id: &str, name: &str, input: Value| { + json!({"type": "assistant", "timestamp": "2026-10-03T12:00:00.000Z", "sessionId": "s1", "cwd": root, + "message": {"content": [{"type": "tool_use", "id": id, "name": name, "input": input}]}}) + .to_string() + + "\n" + }; + let mut text = call("old", "Read", json!({"file_path": root.join("a.rs")})); + std::fs::write(&log, &text).unwrap(); + let mut reads = Reads::default(); + reads.found_now(vec![log.clone()]); + assert!(reads.tail(&root).is_empty(), "history"); + + text += &call("t1", "Read", json!({"file_path": root.join("src/a.rs"), "offset": 10, "limit": 5})); + text += &call("t2", "Bash", json!({"command": "sed -n '1,3p' src/b.rs && cat /etc/hosts"})); + text += &call("t3", "mcp__ccc__file", json!({"path": "src/c.rs"})); + text += &json!({"type": "user", "message": {"content": [{"type": "tool_result", "tool_use_id": "t1", "content": "x"}]}}).to_string(); + text += "\n"; + let half = call("t4", "Read", json!({"file_path": root.join("src/d.rs")})); + std::fs::write(&log, format!("{text}{}", &half[..20])).unwrap(); + let got: Vec<(String, String, String)> = reads.tail(&root).into_iter().map(|r| (r.what(), r.call, r.tool)).map(|(w, c, t)| (c, t, w)).collect(); + assert_eq!(got, [("t1".into(), "Read".into(), "src/a.rs:10-14".into()), ("t2".into(), "Bash".into(), "src/b.rs:1-3".into())]); + + std::fs::write(&log, format!("{text}{half}")).unwrap(); + let got: Vec = reads.tail(&root).into_iter().map(|r| r.what()).collect(); + assert_eq!(got, ["src/d.rs"]); + let _ = std::fs::remove_dir_all(&dir); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9462a12..53dace2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod extract; pub mod externals; pub mod html; pub mod insights; +pub mod inspect; pub mod instance; pub mod languages; pub mod model; diff --git a/src/prompts.rs b/src/prompts.rs index 626e4cc..9854bcf 100644 --- a/src/prompts.rs +++ b/src/prompts.rs @@ -207,7 +207,7 @@ fn home() -> Option { // the directory claude code keeps its transcripts in // ccc:skip -fn claude_root() -> Option { +pub(crate) fn claude_root() -> Option { if let Some(dir) = std::env::var_os("CLAUDE_CONFIG_DIR") { return Some(PathBuf::from(dir).join("projects")); } @@ -237,7 +237,7 @@ fn transcript_cwd(path: &Path) -> Option { } // ccc:skip -fn jsonl_files(dir: &Path) -> Vec { +pub(crate) fn jsonl_files(dir: &Path) -> Vec { let Ok(entries) = fs::read_dir(dir) else { return Vec::new(); }; @@ -253,7 +253,7 @@ fn jsonl_files(dir: &Path) -> Vec { // transcripts for this project: the slug first, then any other project // directory whose sessions actually ran here // ccc:skip -fn claude_transcripts(root: &Path) -> (Vec, String) { +pub(crate) fn claude_transcripts(root: &Path) -> (Vec, String) { let Some(base) = claude_root() else { return (Vec::new(), "".into()); }; @@ -415,6 +415,10 @@ fn truncate(s: &str, cap: usize) -> String { // harness metadata and slash-command expansions are not requests // ccc:skip fn user_prompt_text(rec: &Value) -> Option { + if let Some(raw) = queued_text(rec) { + let condensed = condense(&raw); + return (!condensed.is_empty()).then_some(condensed); + } if rec.get("type").and_then(|t| t.as_str()) != Some("user") { return None; } @@ -450,6 +454,32 @@ fn user_prompt_text(rec: &Value) -> Option { (!condensed.is_empty()).then_some(condensed) } +// A message the user sent while the agent worked: the harness queues it and +// hands it over mid-turn, as an attachment rather than a turn of its own - a +// request all the same, and what the agent does after it answers it. +fn queued_text(rec: &Value) -> Option { + if rec.get("type").and_then(|t| t.as_str()) != Some("attachment") { + return None; + } + let a = rec.get("attachment")?; + let is = |k: &str, want: &str| a.get(k).and_then(|v| v.as_str()).is_none_or(|v| v == want); + let human = a.get("origin").and_then(|o| o.get("kind")).and_then(|k| k.as_str()).is_none_or(|k| k == "human"); + if a.get("type").and_then(|t| t.as_str()) != Some("queued_command") || !is("commandMode", "prompt") || !human { + return None; + } + match a.get("prompt")? { + Value::String(s) => Some(s.clone()), + Value::Array(blocks) => Some( + blocks + .iter() + .filter_map(|b| b.get("text").and_then(|t| t.as_str())) + .collect::>() + .join("\n"), + ), + _ => None, + } +} + // the longest distinctive line of what a tool wrote, for finding it again // ccc:skip fn anchor_of(text: &str) -> Option { @@ -1319,6 +1349,36 @@ mod tests { assert!(turns[0].edits[0].anchor.is_some()); } + // A message sent while the agent worked reaches it queued, mid-turn - a + // request of its own, the edits after it its own - while a task's note + // to the agent is no one's request. + #[test] + fn a_message_queued_mid_turn_is_a_request_of_its_own() { + let dir = tempdir::Dir::new("prompt-queued"); + let queued = |uuid: &str, parent: &str, ts: &str, prompt: Value, origin: &str| { + serde_json::json!({ + "type": "attachment", "uuid": uuid, "parentUuid": parent, "timestamp": ts, "cwd": "/proj", + "attachment": {"type": "queued_command", "prompt": prompt, "commandMode": "prompt", "origin": {"kind": origin}}, + }) + }; + let edit = |text: &str| tool_use("Edit", serde_json::json!({"file_path": "/proj/src/net.rs", "new_string": text})); + let file = write( + dir.path(), + "s9.jsonl", + &[ + user("u1", None, "2026-08-20T10:00:00Z", serde_json::json!("rename the port field")), + assistant("a1", "u1", "2026-08-20T10:00:01Z", serde_json::json!([edit(" pub listen_port: u16,")])), + queued("q1", "a1", "2026-08-20T10:00:02Z", serde_json::json!([{"type": "text", "text": "and the host field"}]), "human"), + assistant("a2", "q1", "2026-08-20T10:00:03Z", serde_json::json!([edit(" pub listen_host: String,")])), + queued("q2", "a2", "2026-08-20T10:00:04Z", serde_json::json!("a background task finished"), "task"), + ], + ); + let turns = parse_claude(&file, Path::new("/proj")); + let got: Vec<(&str, usize)> = turns.iter().map(|t| (t.prompt.as_str(), t.edits.len())).collect(); + assert_eq!(got, [("rename the port field", 1), ("and the host field", 1)]); + assert_eq!(turns[1].id, "claude:s9:q1"); + } + #[test] fn a_turn_carries_the_model_that_answered_it_even_with_no_edit() { let dir = tempdir::Dir::new("prompt-model"); diff --git a/src/replay.rs b/src/replay.rs index 4484cd4..0c2ea38 100644 --- a/src/replay.rs +++ b/src/replay.rs @@ -39,6 +39,11 @@ fn git_raw(root: &Path, args: &[&str]) -> Option { out.status.success().then(|| String::from_utf8_lossy(&out.stdout).into_owned()) } +// a text kept for a replay, by the object id `texts_of` gave it +pub(crate) fn text_of(root: &Path, id: &str) -> Option { + git_raw(root, &["cat-file", "blob", id]) +} + // git fed `input`, with `env` set - an error carries what git said fn git_in(root: &Path, args: &[&str], input: &[u8], env: &[(&str, &str)]) -> Result { let mut child = Command::new("git") @@ -105,20 +110,27 @@ pub(crate) fn by_changeset(turns: &[Turn]) -> BTreeMap<&str, Vec<&Turn>> { // The ask a step answers: the one whose calls named its changeset - the // latest made before it, as a changeset can be staged under one ask and // applied under the next - or, for a look, which names none, the ask made -// last before it. A change made by hand answers none. +// last before it, in its own session where it names one. A change made by +// hand answers none. pub(crate) fn ask_of<'t>( human: bool, changeset: Option<&str>, + session: Option<&str>, at_ms: u64, turns: &'t [Turn], named: &BTreeMap<&str, Vec<&'t Turn>>, ) -> Option<&'t Turn> { - // a transcript's clock and the server's can sit a moment apart - let made_before = |t: &&Turn| t.epoch * 1000 <= at_ms as i64 + 5000; + // a transcript's clock and the server's can sit a moment apart - a look + // read from the transcript, which names its session, keeps the transcript's own + let lead = if session.is_some() { 0 } else { 5000 }; + let made_before = |t: &&Turn| t.epoch * 1000 <= at_ms as i64 + lead; match (human, changeset) { (true, _) => None, (_, Some(c)) => named.get(c).and_then(|ts| ts.iter().rev().find(|t| made_before(t)).or(ts.first()).copied()), - (_, None) => turns.iter().filter(made_before).max_by_key(|t| t.epoch), + (_, None) => { + let latest = |s: Option<&str>| turns.iter().filter(made_before).filter(|t| s.is_none_or(|s| t.session == s)).max_by_key(|t| t.epoch); + session.and_then(|s| latest(Some(s))).or_else(|| latest(None)) + } } } @@ -294,7 +306,7 @@ pub fn save(root: &Path, opts: &SaveOptions) -> Result { // a step no ask accounts for goes with the session it falls in let mut session: Option = None; for mut v in steps { - let ask = ask_of(v["by"] == "human", v["changeset"].as_str(), v["at"].as_u64().unwrap_or(0), &turns, &named); + let ask = ask_of(v["by"] == "human", v["changeset"].as_str(), v["session"].as_str(), v["at"].as_u64().unwrap_or(0), &turns, &named); if let Some(t) = ask { session = Some(t.session.clone()); } @@ -476,6 +488,33 @@ mod tests { use super::*; use std::fs; + // A look read from a transcript keeps the transcript's clock and serves + // the ask its own session made last before it - not one made a moment + // after, nor another session's. + #[test] + fn a_look_from_a_transcript_serves_its_own_sessions_ask() { + let turn = |id: &str, session: &str, epoch| Turn { + id: id.into(), + agent: "claude".into(), + session: session.into(), + model: None, + ts: String::new(), + branch: None, + prompt: String::new(), + edits: Vec::new(), + changesets: Vec::new(), + epoch, + }; + let turns = [turn("a", "s1", 100), turn("b", "s1", 102), turn("c", "s2", 150)]; + let named = by_changeset(&turns); + let ask = |session, at_ms| ask_of(false, None, session, at_ms, &turns, &named).map(|t| t.id.as_str()); + // the server's own look allows for the two clocks + assert_eq!(ask(None, 101_000), Some("b")); + assert_eq!(ask(Some("s1"), 101_000), Some("a")); + assert_eq!(ask(Some("s1"), 160_000), Some("b")); + assert_eq!(ask(None, 160_000), Some("c")); + } + // a repository with a branch off `main`, and a bare remote both were pushed to struct Repo { dir: PathBuf, @@ -555,7 +594,7 @@ mod tests { assert!(sh(&r.work, &["config", "--get-all", "remote.origin.fetch"]).contains(FETCH_SPEC)); assert_eq!(save(&r.work, &push).unwrap().steps, 0, "nothing new"); - feed(&r.work, &[step(4, "feature", "read", json!([]))]); + feed(&r.work, &[step(4, "feature", "inspect", json!([]))]); assert_eq!(save(&r.work, &push).unwrap().steps, 1); assert_eq!(lines(&r.work, "refs/ccc/replay/feature:sessions/unattributed.jsonl"), 4); assert_eq!(sh(&r.work, &["rev-list", "--count", "refs/ccc/replay/feature"]), "2"); diff --git a/src/scan.rs b/src/scan.rs index b3ccf2e..2da76c6 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -116,7 +116,25 @@ pub fn collect_files(root: &Path) -> Result> { // parse every discovered file into a `FileCache`, sorted by path pub fn build_caches(root: &Path, files: &[PathBuf]) -> Vec { - let mut caches = parse_all(root, files); + build_caches_reusing(root, files, |_| None) +} + +// The caches for `files`, each that `held` still has for a file - one that +// has not changed since - taken from there rather than parsed again. +pub fn build_caches_reusing(root: &Path, files: &[PathBuf], held: impl Fn(&PathBuf) -> Option) -> Vec { + let mut caches = Vec::with_capacity(files.len()); + let mut fresh = Vec::new(); + for f in files { + match held(f) { + Some(mut c) => { + // named afresh below, against whatever else is here now + c.cache_name = naming::cache_name(&c.rel_path); + caches.push(c); + } + None => fresh.push(f.clone()), + } + } + caches.extend(parse_all(root, &fresh)); caches.sort_by(|a, b| a.rel_path.cmp(&b.rel_path)); disambiguate_cache_names(&mut caches); caches diff --git a/src/serve.rs b/src/serve.rs index 46c5128..e8a8f42 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -74,6 +74,12 @@ struct MapState { feed: Mutex, // the requests behind the timeline's steps, read from agents' transcripts asks: Mutex, + // what agents read with their own tools, as far as their transcripts were read + inspects: Mutex, + // held while the map is read again - see `rescan` + rescanning: Arc>, + // each mapped file's mtime and size when it was read, so one unchanged is not parsed again + stamps: Mutex, // the advisory answer for this map generation; asking osv is a network round trip audit: Mutex>, // security findings for this generation, keyed also by whether tests were included @@ -143,11 +149,20 @@ impl Timeline { if let Some(o) = v.as_object_mut() { o.remove("server"); } - tl.log(Logged { json: v, before: None, after: None }); + as_inspect(&mut v); + tl.log(Logged::bare(v)); } tl } + // Is this look on the timeline already - the step just logged, or the + // same call another server's feed put down. + fn holds(&self, look: &Value) -> bool { + let same = |l: &Logged| l.json["status"] == "inspect" && l.json["intent"] == look["intent"]; + self.steps.back().is_some_and(same) + || (look["call"].is_string() && self.steps.iter().any(|l| same(l) && l.json["call"] == look["call"])) + } + // A step onto the end, numbered, and the event as logged. Past the cap a // look goes before an edit does - the edits are what a replay is for - // and only the newest steps keep their snapshots. @@ -158,13 +173,14 @@ impl Timeline { self.steps.push_back(l); while self.steps.len() > TIMELINE_STEPS { let older = self.steps.len() / 2; - match self.steps.iter().take(older).position(|s| s.json["status"] == "read") { + match self.steps.iter().take(older).position(|s| s.json["status"] == "inspect") { Some(i) => drop(self.steps.remove(i)), None => drop(self.steps.pop_front()), } } let n = self.steps.len(); for old in self.steps.iter_mut().take(n.saturating_sub(TIMELINE_SNAPSHOTS)) { + old.drawable = None; old.before = None; old.after = None; } @@ -174,18 +190,47 @@ impl Timeline { struct Logged { json: Value, - // the step drawn at its focus level, on each side + // what the step's sides are drawn from, while it is among the newest - + // none for a look, or a step another server shared + drawable: Option>, + // each side drawn at its focus level, once a page asked for it before: Option>, after: Option>, } -// a step on its way to the timeline, read while the map still held its before side +impl Logged { + // a step with nothing to draw + fn bare(json: Value) -> Logged { + Logged { json, drawable: None, before: None, after: None } + } +} + +// a step on its way to the timeline struct Draft { step: edit::Activity, event: vis::Event, - before: Option>, - // a part of a step, or a staged call - sides no map held - unmapped: bool, +} + +// a logged step that kept the texts it is drawn from - a write, staged or +// made by hand; a discard is drawn already as what it dropped +fn drawable_json(v: &Value) -> bool { + matches!(v["status"].as_str(), Some("staged" | "applied" | "reverted" | "edited")) && v["texts"].as_array().is_some_and(|t| !t.is_empty()) +} + +// A step logged before looks were inspects, as one now. +fn as_inspect(v: &mut Value) { + if v["status"] != "read" { + return; + } + v["status"] = json!("inspect"); + for key in ["files", "functions"] { + for t in v.get_mut(key).and_then(Value::as_array_mut).into_iter().flatten() { + t["change"] = json!("inspect"); + } + } + if v["focus"]["change"] == "read" { + v["focus"]["change"] = json!("inspect"); + } } fn now_ms() -> u64 { @@ -271,6 +316,8 @@ struct StepRef { at: u64, human: bool, changeset: Option, + // the agent session a look came from, where its transcript said + session: Option, } // Steps into runs by the ask behind them. A step an agent took belongs to the @@ -283,7 +330,7 @@ fn group_by_ask(steps: &[StepRef], turns: &[crate::prompts::Turn]) -> Value { let named = crate::replay::by_changeset(turns); let mut groups: Vec<(&str, Option<&crate::prompts::Turn>, Vec)> = Vec::new(); for s in steps { - let ask = crate::replay::ask_of(s.human, s.changeset.as_deref(), s.at, turns, &named); + let ask = crate::replay::ask_of(s.human, s.changeset.as_deref(), s.session.as_deref(), s.at, turns, &named); let kind = match (s.human, ask) { (true, _) => "hand", (_, Some(_)) => "ask", @@ -359,6 +406,8 @@ fn hand_step(files: Vec) -> Vec { impl MapState { fn build(root: &Path) -> Result { let files = scan::collect_files(root)?; + // stamped before they are read, so a write meanwhile is read again + let stamps = stamps_of(&files); let caches = scan::build_caches(root, &files); let withdrawn = scan::withdrawn_files(root, &files, &caches); let seen = seen_of(root, &caches); @@ -393,6 +442,9 @@ impl MapState { // and only what the others share from here on feed: Mutex::new(Feed { read: std::fs::metadata(feed_path(root)).map_or(0, |m| m.len()), ..Default::default() }), asks: Mutex::new(AsksCache::default()), + inspects: Mutex::new(Default::default()), + rescanning: Default::default(), + stamps: Mutex::new(stamps), audit: Mutex::new(None), sast: Mutex::new(None), deps: Mutex::new((String::new(), BTreeMap::new())), @@ -449,37 +501,16 @@ impl MapState { report } - fn rescan(&mut self) -> Result<(usize, usize)> { - let before = self.caches.len(); - let files = scan::collect_files(&self.root)?; - let caches = scan::build_caches(&self.root, &files); - // read while the map still holds what a change by hand started from - let drafts = self.found_drafts(&caches); - self.caches = caches; - self.withdrawn = scan::withdrawn_files(&self.root, &files, &self.caches); - self.contracts = ContractIndex::for_root(&self.root, &self.caches); - self.externals = manifest_deps(&self.root); - self.facade = cargo_package_name(&self.root); - self.ts = render::now_ts(); - self.invalidate(); - self.timeline_finish(drafts); - Ok((before, self.caches.len())) - } - - // swap in a fresh map (built outside lock by watcher) - fn swap_in(&mut self, caches: Vec) { - let drafts = self.found_drafts(&caches); - self.caches = caches; - // the watcher hands over caches alone; the walk is cheap next to the parse - self.withdrawn = scan::collect_files(&self.root) - .map(|files| scan::withdrawn_files(&self.root, &files, &self.caches)) - .unwrap_or_default(); - self.contracts = ContractIndex::for_root(&self.root, &self.caches); - self.externals = manifest_deps(&self.root); - self.facade = cargo_package_name(&self.root); + // a map read afresh from disk put in place of this one - see `swap_in` + fn swap(&mut self, fresh: Fresh) { + *self.stamps.get_mut().unwrap_or_else(|p| p.into_inner()) = fresh.stamps; + self.caches = fresh.caches; + self.withdrawn = fresh.withdrawn; + self.contracts = fresh.contracts; + self.externals = fresh.externals; + self.facade = fresh.facade; self.ts = render::now_ts(); self.invalidate(); - self.timeline_finish(drafts); } // What changed on disk since the timeline last looked, other than through @@ -611,6 +642,7 @@ impl MapState { at: l.json["at"].as_u64().unwrap_or(0), human: l.json["by"] == "human", changeset: l.json["changeset"].as_str().map(str::to_string), + session: l.json["session"].as_str().map(str::to_string), }) .collect(); let Some(first) = steps.first() else { @@ -695,13 +727,13 @@ impl MapState { // The first half of putting edit steps on the timeline: each split into // the parts it is made of, a definition at a time, and read into an event - // while the map still holds the side it started from - and, when a - // visualiser is open, that side drawn. + // while the map still holds the side it started from. Nothing is drawn + // here - a page asks for a step's sides when it shows them, so an edit to + // a big project waits on no drawing. fn timeline_begin(&self, tool: &str, steps: Vec) -> Vec { if steps.is_empty() { return Vec::new(); } - let model = self.timeline().open().then(|| self.vis_model()); let lang_of = |p: &str| { self.caches .iter() @@ -715,54 +747,42 @@ impl MapState { .map(|(step, part)| { let mut event = vis::event(0, now_ms(), tool, &step, lang_of); event.json["part"] = part.as_ref().map_or(Value::Null, vis::Part::json); - // a staged call starts where the calls before it left the file - let unmapped = part.is_some() || step.status == "staged"; - let before = match &model { - // a discarded step is drawn already, as the step that staged it - Some(m) if step.status != "discarded" => self.side_of(m, &step, unmapped, &event.focus, false), - _ => None, - }; - Draft { step, event, before, unmapped } + Draft { step, event } }) .collect() } // One side of a step drawn at its focus. A function's logic draws from the - // step's own text through this map; a wider view of a side no map held - - // a part's, a staged call's - draws from the map that text would make. - fn side_of(&self, m: &vis::Model, step: &edit::Activity, unmapped: bool, focus: &vis::Focus, after: bool) -> Option> { + // step's own text through this map; a wider view draws from the map the + // side's text makes of this one - and a staged step's after side, which + // no map held, from the map it would leave. + fn side_of(&self, m: &vis::Model, step: &edit::Activity, focus: &vis::Focus, after: bool) -> Option> { let flow = matches!(focus, vis::Focus::Flow(_)); - if unmapped && (!flow || (after && step.status == "staged")) { + if !flow || (after && step.status == "staged") { let (caches, m) = self.proposed(step, after); return m.snapshot(&caches, step, focus, after); } m.snapshot(&self.caches, step, focus, after) } - // The second half, once any write is folded into the map: each step's - // after side drawn from the map it left, the steps logged, and this - // server's own shared on the project's feed. A staged step is drawn from - // the map it would leave, so it can be looked over before it is written. + // The second half, once any write is folded into the map: the steps + // logged, each keeping what its sides are drawn from, and this server's + // own shared on the project's feed. fn timeline_finish(&self, drafts: Vec) { if drafts.is_empty() { return; } - let model = self.timeline().open().then(|| self.vis_model()); let mut logged = Vec::with_capacity(drafts.len()); // what each side held, kept as git objects so a saved replay draws it again let mut kept = std::collections::HashMap::new(); for d in drafts { - let after = match (&model, d.step.status) { - (Some(m), "applied" | "reverted" | "edited" | "staged") => { - self.side_of(m, &d.step, d.unmapped, &d.event.focus, true) - } - _ => None, - }; let mut json = d.event.json; if self.git && json["tool"] != ELSEWHERE { json["texts"] = crate::replay::texts_of(&self.root, &d.step.files, &mut kept); } - logged.push(Logged { json, before: d.before, after }); + // a discarded step is drawn already, as the step that staged it + let drawable = (d.step.status != "discarded").then(|| Arc::new((d.step, d.event.focus))); + logged.push(Logged { json, drawable, before: None, after: None }); } let shared: Vec = { let mut tl = self.timeline(); @@ -800,7 +820,7 @@ impl MapState { .filter(|l| l.json["seq"].as_u64().is_some_and(|s| s > since)) .map(|l| { let mut v = l.json.clone(); - v["snapshot"] = json!(l.before.is_some() || l.after.is_some()); + v["snapshot"] = json!(l.drawable.is_some() || (self.git && drawable_json(&l.json))); v }) .collect(); @@ -813,31 +833,123 @@ impl MapState { }) } - // one step whole: the event and the level it happened on, drawn each side + // One step whole: the event and the level it happened on, drawn each side + // the first time a page asks - with the timeline let go meanwhile, as a + // big project's map takes a moment to draw - and kept for the next. fn timeline_step(&self, seq: u64) -> Option { - let tl = self.timeline(); - let l = tl.steps.iter().find(|l| l.json["seq"].as_u64() == Some(seq))?; + let at = |l: &&Logged| l.json["seq"].as_u64() == Some(seq); + let (json, drawable, mut before, mut after) = { + let tl = self.timeline(); + let l = tl.steps.iter().find(at)?; + (l.json.clone(), l.drawable.clone(), l.before.clone(), l.after.clone()) + }; + // one logged before this server started, or shared by another, is put + // back together from the texts kept of it + let drawable = drawable.or_else(|| self.rebuilt(&json)); + if let Some(d) = drawable.filter(|_| before.is_none() && after.is_none()) { + let (step, focus) = &*d; + let model = self.vis_model(); + before = self.side_of(&model, step, focus, false); + if matches!(step.status, "applied" | "reverted" | "edited" | "staged") { + after = self.side_of(&model, step, focus, true); + } + let mut tl = self.timeline(); + if let Some(l) = tl.steps.iter_mut().find(|l| l.json["seq"].as_u64() == Some(seq)) { + l.before = before.clone(); + l.after = after.clone(); + } + } let side = |s: &Option>| s.as_deref().cloned().unwrap_or(Value::Null); - Some(json!({"event": l.json, "before": side(&l.before), "after": side(&l.after)})) + Some(json!({"event": json, "before": side(&before), "after": side(&after)})) + } + + // A step put back together from the texts the feed kept of it, as git + // objects - one logged before this server started, or shared by another + // server - so it is drawn like a step this server took. None without git, + // without texts, or with one of them gone. + fn rebuilt(&self, json: &Value) -> Option> { + if !self.git || !drawable_json(json) { + return None; + } + let status = match json["status"].as_str()? { + "staged" => "staged", + "applied" => "applied", + "reverted" => "reverted", + _ => "edited", + }; + // a side the step had no file on is none; one it had must still be there + let text = |v: &Value| match v.as_str() { + Some(id) => crate::replay::text_of(&self.root, id).map(Some), + None => Some(None), + }; + let mut files = Vec::new(); + for t in json["texts"].as_array()? { + files.push(edit::FileText { path: t["path"].as_str()?.to_string(), before: text(&t["before"])?, after: text(&t["after"])? }); + } + let step = edit::Activity { + changeset: json["changeset"].as_str().unwrap_or_default().to_string(), + status, + ops: Vec::new(), + files, + intent: Vec::new(), + }; + let lang_of = |p: &str| { + self.caches + .iter() + .find(|c| crate::changes::path_str(&c.rel_path) == p) + .map(|c| c.language) + .or_else(|| crate::languages::Language::from_path(Path::new(p))) + }; + let focus = vis::event(0, 0, json["tool"].as_str().unwrap_or_default(), &step, lang_of).focus; + Some(Arc::new((step, focus))) } - // An agent's look at the code through a read tool, logged as a step of - // its own and shared - unless it is the look just logged. + // An agent's look at the code through a read tool, logged as an inspect + // of its own and shared - unless it is the look just logged. fn timeline_look(&self, tool: &str, what: &str, sites: &[(String, usize, bool)]) { let Some(ev) = vis::look(now_ms(), tool, what, sites, &self.caches) else { return; }; let shared = { let mut tl = self.timeline(); - let again = tl.steps.back().is_some_and(|l| l.json["status"] == "read" && l.json["intent"] == ev.json["intent"]); - if again { + if tl.holds(&ev.json) { return; } - tl.log(Logged { json: ev.json, before: None, after: None }) + tl.log(Logged::bare(ev.json)) }; self.feed_out(&[shared]); } + // What agents read with their own tools since the last time - a `Read`, + // a `cat` - each an inspect logged and shared like a read tool's look. + fn timeline_inspects(&self) { + let reads = self.inspects.lock().unwrap_or_else(|p| p.into_inner()).fresh(&self.root); + self.timeline_reads(reads); + } + + // reads onto the timeline, each once, whichever server's transcripts or feed it came by + fn timeline_reads(&self, reads: Vec) { + if reads.is_empty() { + return; + } + let shared: Vec = { + let mut tl = self.timeline(); + let mut shared = Vec::new(); + for r in reads { + let Some(mut ev) = vis::look(r.at_ms, &r.tool, &r.what(), &r.sites(&self.caches), &self.caches) else { + continue; + }; + ev.json["call"] = json!(r.call); + ev.json["session"] = json!(r.session); + if !tl.holds(&ev.json) { + shared.push(tl.log(Logged::bare(ev.json))); + } + } + shared + }; + self.feed_out(&shared); + } + // this server's own steps onto the project's shared feed fn feed_out(&self, events: &[Value]) { if events.is_empty() { @@ -888,6 +1000,11 @@ impl MapState { if v["server"].as_str() == Some(me.as_str()) || v["by"] == "human" { continue; } + as_inspect(&mut v); + // a read this server found in the same transcript first + if v["status"] == "inspect" && tl.holds(&v) { + continue; + } let write = matches!(v["status"].as_str(), Some("applied" | "reverted")); if let Some(id) = v["changeset"].as_str().filter(|_| write) { if feed.ledgered.contains(id) { @@ -898,7 +1015,7 @@ impl MapState { if let Some(o) = v.as_object_mut() { o.remove("server"); } - tl.log(Logged { json: v, before: None, after: None }); + tl.log(Logged::bare(v)); } } @@ -1004,17 +1121,83 @@ impl MapState { } +// A map read afresh from disk, everything in it worked out before any lock +// on the map is taken. +struct Fresh { + stamps: Fingerprint, + caches: Vec, + withdrawn: Vec, + contracts: ContractIndex, + externals: Vec, + facade: Option, +} + +impl Fresh { + fn of(root: &Path, stamps: Fingerprint, caches: Vec) -> Fresh { + let files: Vec = stamps.keys().cloned().collect(); + Fresh { + stamps, + withdrawn: scan::withdrawn_files(root, &files, &caches), + contracts: ContractIndex::for_root(root, &caches), + externals: manifest_deps(root), + facade: cargo_package_name(root), + caches, + } + } +} + +// The project read again and put in place of the map. The parse takes no +// lock, so a big project's rescan holds up no reader but for the swap - one +// rescan at a time, so an older read never lands after a newer one. +fn rescan(state: &RwLock) -> Result<(usize, usize, String)> { + let (root, one) = { + let map = state.read().expect("map lock poisoned"); + (map.root.clone(), Arc::clone(&map.rescanning)) + }; + let _one = one.lock().unwrap_or_else(|p| p.into_inner()); + let now = fingerprint(&root)?; + let caches = reparse(state, &root, &now); + Ok(swap_in(state, Fresh::of(&root, now, caches))) +} + +// The project's files parsed, each unchanged since the map read it taken +// from the map - an edit's rescan parses what it wrote, not the project. +fn reparse(state: &RwLock, root: &Path, now: &Fingerprint) -> Vec { + let files: Vec = now.keys().cloned().collect(); + let map = state.read().expect("map lock poisoned"); + let was = map.stamps.lock().unwrap_or_else(|p| p.into_inner()); + let held: std::collections::HashMap<&Path, &FileCache> = map.caches.iter().map(|c| (c.rel_path.as_path(), c)).collect(); + scan::build_caches_reusing(root, &files, |f| { + let same = was.get(f).is_some_and(|s| now.get(f) == Some(s)); + let c = held.get(f.strip_prefix(root).ok()?)?; + same.then(|| (*c).clone()) + }) +} + +// A fresh map in place of the one held: what changed by hand read against the +// map it started from, the write lock taken only for the swap. The files +// before and after, and the generation it made. +fn swap_in(state: &RwLock, fresh: Fresh) -> (usize, usize, String) { + let drafts = state.read().expect("map lock poisoned").found_drafts(&fresh.caches); + let done = { + let mut map = state.write().expect("map lock poisoned"); + let before = map.caches.len(); + map.swap(fresh); + (before, map.caches.len(), map.ts.clone()) + }; + state.read().expect("map lock poisoned").timeline_finish(drafts); + done +} + // comps every mapped file with its mtime (unix nanos) and size. type Fingerprint = BTreeMap; fn fingerprint(root: &Path) -> Result { - let mut fp = Fingerprint::new(); - for f in scan::collect_files(root)? { - if let Ok(md) = std::fs::metadata(&f) { - fp.insert(f, stamp(&md)); - } - } - Ok(fp) + Ok(stamps_of(&scan::collect_files(root)?)) +} + +fn stamps_of(files: &[PathBuf]) -> Fingerprint { + files.iter().filter_map(|f| std::fs::metadata(f).ok().map(|md| (f.clone(), stamp(&md)))).collect() } // a file's mtime (unix nanos) and size - it changed when either did @@ -1037,17 +1220,18 @@ fn fingerprint_delta(a: &Fingerprint, b: &Fingerprint) -> usize { changed + added } -fn check_and_rebuild( - root: &Path, - last: &Fingerprint, -) -> Result, usize)>> { +// the project read again if anything in it moved since the map read it +fn check_and_rebuild(state: &RwLock, root: &Path) -> Result, usize)>> { let now = fingerprint(root)?; - if now == *last { - return Ok(None); - } - let delta = fingerprint_delta(last, &now); - let files: Vec = now.keys().cloned().collect(); - let caches = scan::build_caches(root, &files); + let delta = { + let map = state.read().expect("map lock poisoned"); + let was = map.stamps.lock().unwrap_or_else(|p| p.into_inner()); + if now == *was { + return Ok(None); + } + fingerprint_delta(&was, &now) + }; + let caches = reparse(state, root, &now); Ok(Some((now, caches, delta))) } @@ -1055,22 +1239,18 @@ fn check_and_rebuild( // changes as they land, whoever makes them fn spawn_watcher(state: Arc>, root: PathBuf, interval: std::time::Duration, while_open: bool) { std::thread::spawn(move || { - let mut last = fingerprint(&root).unwrap_or_default(); let mut warned = false; loop { std::thread::sleep(interval); if while_open && !state.read().expect("map lock poisoned").timeline().open() { continue; } - match check_and_rebuild(&root, &last) { + let one = Arc::clone(&state.read().expect("map lock poisoned").rescanning); + let _one = one.lock().unwrap_or_else(|p| p.into_inner()); + match check_and_rebuild(&state, &root) { Ok(Some((fp, caches, delta))) => { let n = caches.len(); - let ts = { - let mut map = state.write().expect("map lock poisoned"); - map.swap_in(caches); - map.ts.clone() - }; - last = fp; + let (_, _, ts) = swap_in(&state, Fresh::of(&root, fp, caches)); warned = false; println!("map refreshed: {n} files ({delta} changed) at {ts}"); } @@ -1087,6 +1267,17 @@ fn spawn_watcher(state: Arc>, root: PathBuf, interval: std::tim }); } +// how often agents' transcripts are read for what they read with their own tools +const INSPECT_EVERY: std::time::Duration = std::time::Duration::from_secs(2); + +// what agents read with their own tools lands on the timeline as they read it +fn spawn_inspector(state: Arc>) { + std::thread::spawn(move || loop { + std::thread::sleep(INSPECT_EVERY); + state.read().unwrap_or_else(|p| p.into_inner()).timeline_inspects(); + }); +} + // `prefix` narrows the overview to one subtree; totals then describe the // filtered set, with the project-wide file count kept alongside so a filtered // answer can never be mistaken for the whole project @@ -4446,15 +4637,8 @@ fn mcp_tool_call(state: &RwLock, params: &Value, browser: bool) -> Res } if name == "refresh" { - let mut map = state.write().expect("map lock poisoned"); - return match map.rescan() { - Ok((before, after)) => Ok(mcp_md( - &format!( - "rescanned: {before} -> {after} files (generated {})", - map.ts - ), - false, - )), + return match rescan(state) { + Ok((before, after, ts)) => Ok(mcp_md(&format!("rescanned: {before} -> {after} files (generated {ts})"), false)), Err(e) => Ok(mcp_md(&format!("error: {e}"), true)), }; } @@ -4746,9 +4930,8 @@ fn edit_tool_call(state: &RwLock, name: &str, args: &Value) -> Value { }; // a write landed - fold it into the map before anyone asks again if done.wrote { - let mut map = state.write().expect("map lock poisoned"); - match map.rescan() { - Ok((_, after)) => text.push_str(&format!("map rescanned: {after} files (generated {})\n", map.ts)), + match rescan(state) { + Ok((_, after, ts)) => text.push_str(&format!("map rescanned: {after} files (generated {ts})\n")), Err(e) => text.push_str(&format!("the map did not rescan ({e}) - call `refresh`\n")), } } @@ -5322,17 +5505,14 @@ fn route_from(state: &RwLock, method: &str, url: &str, body: &[u8], or status: 204, body: ReplyBody::Empty, }, - ("POST", "/refresh") => { - let mut map = state.write().expect("map lock poisoned"); - match map.rescan() { - Ok((before, after)) => ok(json!({ - "files_before": before, - "files_after": after, - "generated": map.ts, - })), - Err(e) => bad(500, format!("rescan failed: {e:#}")), - } - } + ("POST", "/refresh") => match rescan(state) { + Ok((before, after, ts)) => ok(json!({ + "files_before": before, + "files_after": after, + "generated": ts, + })), + Err(e) => bad(500, format!("rescan failed: {e:#}")), + }, ("POST", "/mcp") => { let msg: Value = match serde_json::from_slice(body) { Ok(v) => v, @@ -5441,6 +5621,8 @@ pub fn serve(root: &Path, opts: &ServeOptions) -> Result<()> { } } + spawn_inspector(Arc::clone(&state)); + let server = Arc::new(server); let mut workers = Vec::new(); for _ in 0..4 { @@ -6613,25 +6795,28 @@ mod tests { fs::create_dir_all(&dir).unwrap(); fs::write(dir.join("a.rs"), "fn one() {}\n").unwrap(); - let fp0 = fingerprint(&dir).unwrap(); - assert!(check_and_rebuild(&dir, &fp0).unwrap().is_none()); + let state = RwLock::new(MapState::build(&dir).unwrap()); + assert!(check_and_rebuild(&state, &dir).unwrap().is_none()); + // the change found, and the map it makes swapped in + let next = || { + let (fp, caches, delta) = check_and_rebuild(&state, &dir).unwrap().expect("a change"); + let names: Vec = caches.iter().flat_map(|c| c.funcs.iter().map(|f| f.name.clone())).collect(); + swap_in(&state, Fresh::of(&dir, fp, caches)); + (names, delta) + }; // appended function shows up in the fresh map fs::write(dir.join("a.rs"), "fn one() {}\nfn two() {}\n").unwrap(); - let (fp1, caches, delta) = check_and_rebuild(&dir, &fp0).unwrap().expect("edit detected"); - assert_eq!(delta, 1); - assert!(caches[0].funcs.iter().any(|f| f.name == "two")); + assert_eq!(next(), (vec!["one".to_string(), "two".to_string()], 1)); - // add a file + // a file added is parsed, and the one beside it kept as it was read fs::write(dir.join("b.rs"), "fn three() {}\n").unwrap(); - let (fp2, caches, _) = check_and_rebuild(&dir, &fp1).unwrap().expect("add detected"); - assert_eq!(caches.len(), 2); + assert_eq!(next().0, ["one", "two", "three"]); - // delete it again q + // and deleted again fs::remove_file(dir.join("b.rs")).unwrap(); - let (_, caches, delta) = check_and_rebuild(&dir, &fp2).unwrap().expect("delete detected"); - assert_eq!(caches.len(), 1); - assert_eq!(delta, 1); + assert_eq!(next(), (vec!["one".to_string(), "two".to_string()], 1)); + assert!(check_and_rebuild(&state, &dir).unwrap().is_none()); let _ = fs::remove_dir_all(&dir); } @@ -7591,6 +7776,29 @@ mod tests { assert_eq!(who, [("agent", "staged"), ("agent", "staged"), ("agent", "applied"), ("agent", "applied")]); } + // A step logged before a restart is drawn all the same: the texts the + // feed kept of it are git objects, and the step is put back together + // from them when a page asks to see it. + #[test] + fn a_step_from_before_a_restart_is_drawn_from_its_kept_texts() { + let (dir, before) = scratch("rebuilt", &[("src/pay.rs", "pub fn charge(n: i32) -> i32 {\n n\n}\n")]); + let git = |args: &[&str]| std::process::Command::new("git").arg("-C").arg(&dir.0).args(args).output().unwrap(); + git(&["init", "-q"]); + drop(before); + let before = RwLock::new(MapState::build(&dir.0).unwrap()); + let (err, out) = tool(&before, "edit_text", json!({"path": "src/pay.rs", "old": " n\n", "new": " n + 1\n", "apply": true})); + assert!(!err, "{out}"); + drop(before); + let after = RwLock::new(MapState::build(&dir.0).unwrap()); + let all = json_of(&route(&after, "GET", "/vis/events?since=0", b"")).clone(); + let step = all["events"].as_array().unwrap().iter().find(|e| e["status"] == "applied").expect("the write").clone(); + assert_eq!(step["snapshot"], true, "{step}"); + let seq = step["seq"].as_u64().unwrap(); + let drawn = json_of(&route(&after, "GET", &format!("/vis/event?seq={seq}"), b"")).clone(); + assert_eq!(drawn["before"]["function"]["name"], "charge", "{drawn}"); + assert!(!drawn["after"].is_null(), "{drawn}"); + } + // A server started on a project picks the timeline up where the servers // before it left it, so a restart loses none of what agents did. #[test] @@ -7609,7 +7817,7 @@ mod tests { .map(|e| (e["seq"].as_u64().unwrap(), e["status"].as_str().unwrap())) .collect(); // staged and written in one call, the change is its write alone - assert_eq!(steps, [(1, "read"), (2, "applied")]); + assert_eq!(steps, [(1, "inspect"), (2, "applied")]); } // Two servers on one project share what the ledger cannot carry: one @@ -7636,7 +7844,7 @@ mod tests { .iter() .map(|e| (e["status"].as_str().unwrap(), e["tool"].as_str().unwrap(), e["focus"]["name"].as_str().unwrap_or("-"))) .collect(); - assert_eq!(steps, [("read", "references", "charge"), ("applied", "edit_text", "fee"), ("applied", "edit_text", "charge")]); + assert_eq!(steps, [("inspect", "references", "charge"), ("applied", "edit_text", "fee"), ("applied", "edit_text", "charge")]); } // An agent's reads land on the timeline as looks at what they named - the @@ -7662,12 +7870,58 @@ mod tests { .iter() .map(|e| (e["status"].as_str().unwrap(), e["intent"][0].as_str().unwrap(), e["focus"]["level"].as_str().unwrap())) .collect(); - assert_eq!(looks, [("read", "references charge", "flow"), ("read", "file src/pay.rs", "code")]); + assert_eq!(looks, [("inspect", "references charge", "flow"), ("inspect", "file src/pay.rs", "code")]); assert_eq!(all["events"][0]["focus"]["name"], "charge"); let marked: Vec<&str> = all["events"][0]["functions"].as_array().unwrap().iter().map(|f| f["name"].as_str().unwrap()).collect(); assert_eq!(marked, ["charge", "refund"]); } + // What an agent read with its own tools lands as an inspect too - the + // function its lines lie in, the file a whole read took - each call once, + // and nothing for a file the map does not hold. + #[test] + fn an_agents_own_reads_land_on_the_timeline_as_inspects() { + let (_dir, state) = scratch( + "inspects", + &[("src/pay.rs", "pub fn charge(n: i32) -> i32 {\n n\n}\n\npub fn refund(n: i32) -> i32 {\n charge(-n)\n}\n")], + ); + let read = |call: &str, tool: &str, path: &str, lines| crate::inspect::Read { + at_ms: 1, + session: "s1".into(), + call: call.into(), + tool: tool.into(), + path: path.into(), + lines, + }; + let map = state.read().unwrap(); + map.timeline_reads(vec![read("t1", "Read", "src/pay.rs", Some((6, 6))), read("t2", "Bash", "src/pay.rs", None), read("t3", "Read", "notes.txt", None)]); + map.timeline_reads(vec![read("t1", "Read", "src/pay.rs", Some((6, 6)))]); + drop(map); + let all = json_of(&route(&state, "GET", "/vis/events?since=0", b"")).clone(); + let got: Vec<(&str, &str, &str, &str)> = all["events"] + .as_array() + .unwrap() + .iter() + .map(|e| (e["status"].as_str().unwrap(), e["intent"][0].as_str().unwrap(), e["focus"]["level"].as_str().unwrap(), e["call"].as_str().unwrap())) + .collect(); + assert_eq!(got, [("inspect", "Read src/pay.rs:6", "flow", "t1"), ("inspect", "Bash src/pay.rs", "code", "t2")]); + assert_eq!(all["events"][0]["focus"]["name"], "refund"); + assert_eq!(all["events"][0]["session"], "s1"); + let fed = fs::read_to_string(feed_path(&state.read().unwrap().root)).unwrap(); + assert_eq!(fed.lines().count(), 2, "{fed}"); + } + + // A feed written before looks were inspects reads back as inspects. + #[test] + fn a_read_from_before_reads_back_as_an_inspect() { + let mut v = json!({"status": "read", "files": [{"change": "read"}], "functions": [{"change": "read"}], "focus": {"change": "read"}}); + as_inspect(&mut v); + assert_eq!(v, json!({"status": "inspect", "files": [{"change": "inspect"}], "functions": [{"change": "inspect"}], "focus": {"change": "inspect"}})); + let mut code = json!({"status": "read", "focus": {"level": "code"}}); + as_inspect(&mut code); + assert_eq!(code, json!({"status": "inspect", "focus": {"level": "code"}})); + } + // Each step is put down to the ask whose calls named its changeset - the // latest made before it, since a changeset staged under one ask can be // applied under the next - and changes made by hand run on their own. @@ -7690,6 +7944,7 @@ mod tests { at: secs * 1000, human, changeset: changeset.map(str::to_string), + session: None, }; let turns = [turn("a", 100, &["c1"]), turn("b", 200, &["c1", "c2"])]; let steps = [ diff --git a/src/vis.rs b/src/vis.rs index 0f25aa5..a31a651 100644 --- a/src/vis.rs +++ b/src/vis.rs @@ -1314,7 +1314,7 @@ pub fn look(at_ms: u64, tool: &str, what: &str, sites: &[(String, usize, bool)], file: path.clone(), name: f.name.clone(), owner: f.owner.clone(), - change: "read", + change: "inspect", before: None, after: Some(span), lines_before: Vec::new(), @@ -1342,14 +1342,14 @@ pub fn look(at_ms: u64, tool: &str, what: &str, sites: &[(String, usize, bool)], "at": at_ms, "tool": tool, "changeset": Value::Null, - "status": "read", + "status": "inspect", "by": "agent", "intent": [asked], "on_disk": "after", "ops": [], "files": files .iter() - .map(|p| json!({"path": p, "change": "read", "added": 0, "removed": 0, "hunks": [], "diff": [], "diff_truncated": false, "whitespace": false})) + .map(|p| json!({"path": p, "change": "inspect", "added": 0, "removed": 0, "hunks": [], "diff": [], "diff_truncated": false, "whitespace": false})) .collect::>(), "files_total": files.len(), "whitespace": false, @@ -2906,8 +2906,8 @@ f :: proc(x: int) -> int { let sites = [("src/pay.rs".to_string(), 1, true), ("src/pay.rs".to_string(), 4, false)]; let ev = look(0, "references", "charge", &sites, &caches).expect("a look"); let Focus::Flow(t) = &ev.focus else { panic!("{:?}", ev.focus) }; - assert_eq!((t.name.as_str(), t.change), ("charge", "read")); - assert_eq!(ev.json["status"], "read"); + assert_eq!((t.name.as_str(), t.change), ("charge", "inspect")); + assert_eq!(ev.json["status"], "inspect"); assert_eq!(ev.json["intent"], json!(["references charge"])); let names: Vec<&str> = ev.json["functions"].as_array().unwrap().iter().map(|f| f["name"].as_str().unwrap()).collect(); assert_eq!(names, ["charge", "refund"]); From 9ef6b1fa05139fe4e921aef92a80448a665619bc Mon Sep 17 00:00:00 2001 From: colwill <62014134+colwill@users.noreply.github.com> Date: Sun, 4 Oct 2026 01:47:02 +0100 Subject: [PATCH 2/2] fix: image --- visual.png | Bin 125337 -> 124911 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/visual.png b/visual.png index d267e3d7d9b66bf6cb200ba94e453df7b7a1a8a8..068a22e4c3dc4d6d9009a82658e141b9d6d3bc3c 100644 GIT binary patch literal 124911 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℘gGyvYm z{fxv%5LFXFWX*7dwVBA<@9;r|C`O+)!dq&NpkuEvfa#-%{Jj&=K3}Yxm>gl0(tKGI zO4bc5dq2r3#KcF<$mlOIw<6gN77K}rq7wcX&RObfx?Mu*o%U831~m`9%E79C=n( z4M-zx9zvj2KOk6nzB%{$uIR*to{7=;w1T*;7i#%@*svPt_;_=)e4^_*%j2>)0FMwRHSVX{&f>g+H{%P`HK8RhR&P51u$kFJv*i6Fnm5 zS9?1Jd2Zw7m?4EtDhXuGa~E4x?Gc@b=yzGZaPVkY4lN!^=e(`m>tC$#$E`t!c&I4) z9kokm4C?h>9Co0ek&_<(*_%Ep6sj&f#lX6_47gyeueR9zSb-C8Vnr`cj-O;I8_s?9 zI34mFSP|BA#Pq@WfZ=RaEfzDC^r=i;kC#md0gmfSaCl7H`{~TGv+|Rej_45-?hg>W`*{E_3Bu$ zHKSH~ray$xRZ}ptJ6PU68LircDsLLh z0hBgcwPkfo)8g-6g!X}z$L$KX(o|DwM)!~f7OpPsjO{AC!?Wgk)tn4B&y{C^Q`i#= zO{sObRG$0xlDBRz8K{H#+P?C;U^f8#glNC46B$&x}E`f z6%kPpilcN|vb$w<9bznDN?f9)+T$&ac^9)WsLD>V0_nx8T6i^qoWN%5?bF zD7Vn|Jub=Sozp4dg|ahVwD1czsPLNRBX{`6cCC>U!6m3Iad=x>^+ZV%?`86f}leyH&+WLcwoO1U&X;e_aerMTzO$>#I!+#@3pvJj2J0rMk z+Q3S$+VMRcFK?SVSTZW39;b*+DO8v$lO+ww2;%Wh zn8(F38>NuJo^4I0yzy_)(^`z)uh&WGJRsO_KYxC}(eq{ZdAOnArXdtlj+v_mI0+=~ zyu8{5Vgwe30?~jjAJhbY`9~ld`Ri^8`4#!D=DG@G&Eg<4hB zOASaw%o%#i8o1&71V>f2n7@C9f7gX&ykiO^3VbfJzAf5Rxa$}~k$7*(Kmjt(9e|>6 zHau!akIWQLl|}S!L%dmNHihK*PN{EUL0?xY_Hc^#3(CeOX7i7ck+C64{!*)fg&U@{ zWFZ^3TAYlzzxH?1bO{E^Ts&R^r99Uw9crEXB@-Dv=hOgVSyI*B;!@+`iOqynonGyp zMqh+b^oLfzjOO~YL7G!#%{lM}(}c6K&XF`1$?GztLE?K{Kbz}>x5kmKs75Fsh-MnE zbHqq9j$kUw!R@7RB~KIBUlpKg{n0&=;(}cVx~o77Ft%)Bj&tu;ElmVV+~GV<76z!+ z=5J@lb&BQkb3~|UV8JJEKE>&`kz|5!Y1(|lHj9KQVt8Bim;;AK5_j>~3&mr-Ct2iK zv!@egls$iaYl-adSh-m%Q(3+f>WNh9M#qtN1;d#&E2@B4aGhnbzT2nuo$R*tnrV9F zqqbY%5NMm93eQ-*rz{_7STpR4eJv}bhFte#&lZ`Ch$T6mMb7j@9e-zC?5PyZ`}Ut) zfY@6ukAv&?oe>Y$5=0>9cXe~o5v%E4eMEV@a5 zcZez0wFM1JBlU%;h}`$msYDU0*tOL-MA#RqU`sk(gFi98H~qnh)V?sSqH1^UOX`m! z-6IK=Pa>Vi8Ujca1$@_cymqA=20q8{Q1DqXG8FsuZ=ZwqlC{>T^?&pHsHPaKkc?dV z#X+R{&${-#Y^E;d*SUIIZWthU>#YQw!d+VzT6zKY(n+dWqj1a!I z1)TSrv8Uq6yH30MwfgMwe2sfQeCeC#(C)|BvV)^h{*ZaTN#kYjAsI;|rSFqrScClK z?C1NZyY8f?6L)uI9o2y@8;A30?5*vyt0^=9G8xE$!OQ@1v72%elZ=aZ4qFp)NSli{vqY73a0@#^>lo_1n2ear~tdX46Px$H)N+7p%=_fAHkB)^Ut|*4_ zGj>L&AK?`oueR@}H2&x7d~6-x+6MFfNHxFhv}Gpv<#Z&)oldQAd;?8MNKQ)&R!R8P zUDeC8vcirl7z2kuQp7LSF(fp+u%snD)}-(2`ttpcqy!8?+&O!FFZ-6`zmngT&&QsL z;9Jf)Jzq?Kg#-oAU93^yVLrB>G15eloIeOa2|P?ZGv4nBR578HXAfMtDyn~i z%IEJ=q)l0_rD<_DuP<{9e}~hs{1zZ!@?rls)bc-?ke8 zU(gQO#>U3|T5#rt^;+tuaS_&aq#xQkid!7qELJGHXiWiV+$~!&ZqT1~8SX&+{PV=u zdTf$D`@a*}u!9VpM;wb16UY+Dyv@|c%M6H*K)WTzu6#+ zT`}2D*1=e84b}$5v_7yj z8|ujJkIAck#FvQ zrC?cU>Cy>O(w?3$waw?8#`%mP$e0pvi4fO(g6L(a?^l--LPa{1-4aCf-FE-dIS#}iO%H&R%Gf8l98s;7E)T$EZs&7x_2A@FOP9{?m zsi_84d)*Nz4fbri_v{&NVywS` z3rFU7Gkgi8ryxy$lIr`yvfcbDcYpF4_k7F=JlrSq9i*_Z%g|^W&>!7)Fq~90(FdActixGG3O*g5VovapC!^uL z;KBlax`1aoDcBNpcQogKF4Bh%5IqFxONQ=`YH#L1%sDw}aa2GXRqyMW{?F>_XV-IbMP(%)xTr~F;_&#b z-;gqb!^29x0=Lt9qHl*54qe561bzJtpWlf5(G4QN{Ug80n$heupGyu39_IlWm1kuGy++5GpYo$F`dLh_HUy=oKf^Jnfh^?$v~Y`LzYsPS7mx>jgJBvw|_ zGej<1y+Fl6i{Cx_-dB_DGD^atz~W%&eg6JCJPHL{$-cwtUnfHbLbCCB6kDaf$r_bP zGw8FfWvRLS40c-ITo!zEVkY(F*NMy|y#Cf(IO&VrE1K&K?+Om7{1N%?nP zYw{_udf}jtX#6=E$`sdBp3 zym@7Y{$TG-;3}bxWh)SsI~GM%7?I8?6@s4-R%X-gE_>wZL$~S^Hk1q3a6tTncqrG_ z7quGmft*e`UOlLhY#p1I*6-kmt)x~ilqd%AlUAi=Nv-U@G%10cBEGA-t2mk+2A6z(MVwG~c&}19JN43Q_Ajcg^W(Ekt&& zwX3_53i|_XQ;5zaM~ea=_OlU0cw&4O6D!VvG90s2@c2fqmPE$&TH}|FYqPR559f@6 zm<>IBD#^s=CT1vN^j2>9@ z)s{q=c#A5FQ#Z-T^p4!<=f$at#>K?~BRGvxY`a>>o?79U@oGz{w1SM-tmWVU&q;AD z#}X=_%a}8Hz>*?5Y3YiETkNa?{aI7fh%J!IW`#G7lGS^9eyaY$(0zZe`mx9PW40Af z0g2C!CzNCak%DaHszLmSA&@M@}E zP*4z5NaBh9%`?eB#K&h?&@#38e9SN=z3e8+#{NEft=e@H1l83=(eN_6bpMj}>gGxQ zG*#Tt5EBt$BA>z*t|a6cOs8h9qM_0U(Chc-99TeN=+T0+KN@VY-jl537rdweVKctS zOadD!kRAFI{P=Epf|8N5Rtooyq-4H=k;MZ6TX^&Yw>_fN*BM-1;Xn$hn_8EB-xN+~ z<_8>%Ea6bpkrlXaOA4lrkKGE;bs0;M(~6=wv8O}^6+!D^L6+>URI9D&Oo7G_(k(KLP4VqZ`Nv8!S`CHI1mt2>8Z)3E zZ~vy(RCekh%th`*r0CMtdqQ<$hEJ*=4ugI2Mu=qq*dptem2;(R-_fnoEL%85X5$K! zMFP~6kIIG{Kz%{I(?}Dby1e~0oAxcOeY*Zp5R#+iseMp1kxb0w!20teo{%*PK!-j) z0onc&?H*jV=Z?!m+EZZpwDGGsgvgmHPLlW_yMgG(>C4A^!qp2a{N#uTO!*AHa4NaQ znv0h6c8j$iO7FOfs;6htX){XHpTt4 z1`ShOQE?&5<`zh4wRrrprjo(ymgR8=(F0%CZEc&W{V?nZy!^PG;oG_~W4^Iv1rWYt zAckHG3|8ve2C}v-4{jtM=((bXzH%B(0(NO#Qd(R)dvUZGeWwAftf?vID9vJKc5vI- zX@^#e*aIxj{9JZl`U?tp&EGh&J@Wu|B`(p+;<3l_a(@xtrl7;&bwWiO)&=zOQPj3V z+RYHofQCVMC>dL7CAUy{cLX-$%2BmNFRy#7}vy@feFnsL66P(~v#_DP0{esD4$1o7>0J_OX7_ z_U@k|-3@1=by8|SDP>@Q)%HN&GD2tBmFeoD#NCZ{e_P{cO9k7{JWWr1DtoxChnriN zU#!8JwMkZOdgaTx9+SUXT7OsBsY|jJ*9K>E9fXrTAERY zjZkHZWg;7u^LicMLdZEXGZU9+kEYLrBE`6tr0}50~>D<@e zLg3pq@_z^iPmB#2s2oilK^HXoXEr%~AFn7;peQ4wiq4T`ntpB@$R2AqyYZ^TvI;z% zWl0~dj9=a`#284as7xNMKr;Muyy4ME6pcl{I3pY{)+H2AF@GwZAsZ&x(eo%SJQ;|E zfh8167A!0tS)PEg8_y(;z!M4zk**7Wev(_vk&{ok%>9}da`n1v6OfMlvPG5 z0C)w$it@>MW4Ck74#FrRBI!I8H>h$zM#WMKFKlB6D5kUJay3ZX0SZV~xY|>?*S`Q7 zK@s?csF5U`AVzF#GgI@t$6rTyK%ItNdqa}Kp@zFn4Kklw`F=udW2ZDOCcTc*@dCbD{<=x|OP7&5zH!@#u-N#-^kwd=!`n zBh(OiVjXPKL{TFNw@Za*@sxPFVq0OqT}NL2U+u)G{0OK=efAE%tCc$(e1fQ&OM|FT z`DLj3ro-Kw8G*-WNOp>eU%cFaepufY6%+Yh*u58InPB@p){>GpDLV3%NjvDHZfbP= zH(I=E2{F5hKdM2>8Opj0P66m@2tdiQd>o)^z;Q91Qjs)b$=U&et1V|sg<~CF5`v#9 zzV%alh#-jF|A9Y{8lm3OueA(bLWQLdl4W|8kM!Fp;vJR4jFGE!;fjRv=g02KwAzHZ zFDS?&xef=#yN|b32|6qV;yJ(4bz!Ho6U-i+dG2PdU3NZ;XWgI;M23=BId!onkw+%NtXFHfn3Pg$=|$iUXne)jj86lvS2KOFaa z90ElHFe*Qx28-=Dny|Nwp6wflwsSi58SFB{_P7QQ*OQNsrC!6=8=lJ@{m~p7dorG$ zksG_i`^VvN$EQ0ox0B@&n@VuV(y{qU5cbwi<^}5NNKhn$=kW|05^JEUR`RfKrsupG z#A(y|bvpSYQDb|3DntD%_vP%Jwgv*W8zf2)@xXI=eGojwAC(5g8o#CXOuG+1UW+ z{aNeqprzn^zQu?bvh0c$Kc2N-Cu?HiyR+|N`D$R_c$ieCU$`JMQP@!px2aUsibwRe>nY39zsIk8v}2G`8k zXj;4Y)@PmxW%}fcqmtJ)Jex)@X+$eJAbR)$f;A2uD}56$k~itrSE1CljW2^_kJW6y zT9*v>C&d!AzdFLVt*K(kK%R_InImANa=|@mD~97^5bZboOVclXrvSC zcm&bXpxobX|0^aBF~Y`Q_Wbp zUu$>qMV-v`GKt_)bF13t(`?v=ZLP$U(-*eZjoD2gQ_Nq$Juy{X5>D@?>MuFD1+D?rxqkco_t3KF1-VHaov=7MwzRj4p$BnyGGeruEh;g_BR!DE<=tj%(*2&$EMHfaDO4h^Nz%vzYk1 zEUAsa_G_k_xb_XIA=D!0v2=yL$gGhB&6N&{_Vzi*!LJlSFCmk@^#03n;R9VNsMzYz zX$^HCNgQ0~d_RvJP%H6sj^NuPsYe8oQ0YeiQ`@&VF}tsL3Io!O0V@aN{QQR$*zmOr#=ELwo2)%@ zqdP8z^@tuk{|r5&aZ2e2RQZxrf2&C)cgwPEo;y{T%35z94+;PH)YXRFD$mrA?_233q`9JUJwc56xGKIaQwO8C6+`);&+d10daKef<%L=$IqF;S-#Ma85+hK& z-z3^d?u7?*ztF-#on6m_>4xNO*AIfgvZ*hF47+!agsnS6)|6JJaRu)vjU6%iwAin) z>xjT!Oxdj_(#1a%#auHs8C zbzv;nl3OPdzSwBvAu+u^U~Zor<1e>XRB6JX3;6?^5R#mPXPSfm=pco6_|rg5RLN1| zuHv4Isw!yn*AgA=kW}f+`QzGaN?BQa%WK;$(U(Fw>5vqQ*C%>X@Ey)lJw!O-cvsSb z#W*S?uZ0C*W2uehn*aqC=6-rMApfY$@0l$-{E5e}?rE(pP&s0l+AQa|M1yOi>*>VP zi;Pzacw<{HxHZpP1UIHd;qbqxsmy5sBGLDLVPxCP@}dUH5(fVIcK*1t-}n&$K~kz3 zSo7ep*BK9M4U78uVL4Jl`8|I!=+SK|hPTX+)GGo!tlk_W=j0f!Fj_TOGfr&V@#SF} zge2#(HhF#M^LD9fT!`;S%5?oJYM5LwcQnkaUf_&{h+kG1*+PVs20>uu-K~(_w7~kz zZz6+CnZq=@e+*lcyJl7kku_L#p--Wjin~gEVhkQA_Jk~7$bs^2I*;9IUpwsEo-QcX z%nN%Rz?X=aG@0W!8EN7*rPyRU%4I9lnKg1}s^uV{Bc%vC9Sb1tzCUn>4;8c2G&EM9J{Pt^cX=!dvPDGQNL2_WS zSaIn)Z{ZocK{87{^7`e11Qo)C@I!zR!vc^e>XxR4nPeGmsZRfE< z--S!&5auy!sZNw~?0nn#l5K&=g;%k+7W0Uw{(D<)_Xv3J zBtWZOrQjvW*%Z}*%F{8VCu%v4y$uJa^-K_mAoYH+CL1NJ`eVl*nrjXAd)V*d7XP9w!Ne*yG5m@qB8|roYrvDV*20bl`C@|0$q)J14 zj59E1$Exg{G!YgOay2b)6!vtJ!I3&zktiH;6-Ju3Ka8f*(DsCCN?yY%IB=UfqxXH4 z$2l%3^A#q(c5`^_>R>i=y<5QR2)6R6{81=AzY9yZ{@ShO`GbPnF!~gC6KUbTJ9FG? zuM_D^w2IfUDl!PuKNAKSuGuE>m63L-=ciNQkJ6Q{q%E6C-Ko(EC@szDBL#p zG9BjW19QLj(DrKf$_!lfM)*^1RbXfV0*y6E+#fMeiV?wKiuAPFA zFtY?7o}xRj`m*KmzO|hR{r!7w`T5TJgNenADxeLU@UH~PR>kQ~u1)gR4FoIrdQ>$= zdDYWNVKefJKc32`oTaVsE6i(K564IbcVm@!40<_QfN*Jf4{(?19vew5FIk!mEN<@I z=ZfMg(s*2Zqy#5}9*{mOLUsnC!X0^H>R}WC_m~}A_JH1IcNi>fStF!xO!QDt@C7pB zBA-putEVU;GDy}RQo`kZ*`STwF~dM)^BE?88oy(`@`>KpZrdm?ya-Hj)3D5TU^H_O zm~gG4e8d#9{WBe0c|K6F<_tCLH}Z2T(}JheLB>%v)!{9@=fBF~evW5H{UDav)7y)(Jcz;&eCHR#X6Pe=7o;?X9FUv$AXOCkt!@>+)WNwvrT6d^KW=f9`zo#P(ut2Z zc-=?fBlIk@&e@w2{jgi6*&#?cKw&NSs_l$Z+{PO^HXr}PJ>R~m3meMu`mlFh zK``x3u`;JbokkI5Qj)+H2|6D0=3PetlKDDi^gJY0D-4lu3U|&+ zbccP^D&6wM$h>HHut$nm>uR5zO8gIKNhi1e1W`D4N@c+w7uQTg+${@;Vo1 zomF7-K_jF49~)dYVT77)44ES`8u`CO!iofaNa%v6dum*uff9coStawiRJBy2cxK2w zt3k5n@e&r{g7WstB#15MqU|X|d{z!igp;yOy#QP%eQfOa+cNuEo%sBT66U^KkC3=i z&Vzx;BM{|yIda$YhlM>P3lJ{(_o%UFYlP0BH9dIXnU!N)=tl0KJJy*3;h7}U$;S=d zOg1|}{nfPm=V&sMHhZMSv9AM%nV>kw*W-Bdz*Wu;zpG}QrZ#Jq zh3kQ1RGm_F8pW~1@cIKLmwz8fkSKQAWU|7SZSTxuLw&s`D=tkW1)H3O;$DwD|Jo5L#YWZSsMZp?dH$RU~ z7_P5;bg)%^=R8cBCg`)t^gsOGe_bMEYF3|Rqr?!Jpo}nPDacLHzr@JgWqs8O%je8M z>F6mJO-jjHsT-lJG(`vg1LRAiX(jFdbGSopc11^xAdQGlCHOCl)F{pNr3up1@vt(a zV)E>IO?zmb3vo1aUM9S>w>tn%=9Y7pP|wV zu3L>GRNM&7ntvRt*2^t0|KC4U%$PzFC4j~G-@_Jz-s3+f+CV{$f6{+lp#T5Bu#*-| h9KMA1-#`BO6=ARC3+F&M2m%EhG7^g76{27K{{te9VPOCO literal 125337 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