The canvas column holds what a run is waiting on — its question and its result - #86
Open
pierreprudh wants to merge 13 commits into
Open
The canvas column holds what a run is waiting on — its question and its result#86pierreprudh wants to merge 13 commits into
pierreprudh wants to merge 13 commits into
Conversation
The card the harness drew for `userAnswerQuestion` boxed a bordered answer field inside a bordered card and filled Submit with accent — two frames and a colour the design system spends nowhere else. It is a composer addressed to one question, so it is drawn as one now: the question on top, a borderless answer box lined up under it, a hairline foot carrying Skip and the composer's own round send. An empty answer is no longer sent: ⌘↩ and the button stay inert until there is text, and Skip is how you say nothing. In Focus it belongs beside the conversation rather than under it, so QuestionCard takes the plan's dock/island split. The canvas corner is now a column AiPanel owns: the plan island and the question stack in it with a gap, instead of two cards positioning themselves against the canvas and having to measure each other. TodoStrip's island wrapper became a flow item in that column and its folded mark right-aligns with alignSelf. A question island holds the column open on its own, so the reading column slides left for it the same way it does for a plan. `.ai-qa-card` was a class three review cards wore with no rule behind it — every message around them rose while they appeared with no entrance. It now carries the settle-in, with .klide-ask-island beside it on the todo island's curve, both reduced-motion guarded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
In Focus the "Review result" entry sat at the far bottom of the transcript, under the turn that produced it — so the moment anything else streamed in, the way into the evidence scrolled away, and a long conversation carried one entry per finished turn. It joins the canvas column instead, directly under the plan: the newest reviewable completion, one entry, in the corner the reader is already watching. Everywhere else it stays inline under its turn, where the transcript is the only surface there is. CompletionCard takes the same variant split as the plan and the question, and the island entry wears the folded plan mark's shape — glass, hairline, 30px, right-aligned — so the column reads as one family down its whole height. Both placements now ask for changes through one handler rather than two copies of the same prompt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
320px of islands plus a 760px reading column needs 1116px of canvas, and Klide's own window on a laptop does not have it: the column kept its width while the prose lost its. So the corner is measured now. The islands take whatever is left once the prose has a readable 560px, clamped to 232–320px, and the plan, the result and the question shrink together — the reading column never drops below its share, and the inset the conversation gives up follows the measurement instead of the constant. Under 260px the column is a corner rather than a panel, so the result entry drops its words for its mark: ReviewIcon plus the file count, with the label it dropped moving to the tooltip and the accessible name. The inline entry does the same in a panel too narrow to spell it out. Two things that could not fit the window before: - The drawer is a side sheet on a roomy window and a full-bleed one under 560px — 360px of content plus two 22px gutters stopped fitting somewhere around a 520px viewport, and a sheet whose edges are off-screen reads as broken. Under 620px of height the chrome gives its padding back and one command's output can no longer own the whole sheet. - The column keeps a height budget and scrolls inside it. A wheel over any card bubbles to that scroller even though the column takes no hits itself, so a question stays reachable under a long plan on a short window. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The review entry opened a native <dialog>: the only showModal() in Klide, and the one overlay not built the way every other one is. On the canvas that dialog was a child of the island entry, which passes its clicks through to the column — so the sheet took no clicks at all while its backdrop blocked everything else, and the app read as frozen. So the result is a window in the column instead, built like the plan above it: the same glass and corners, a header that is the whole hit target, a hairline that wipes in under it, and the evidence opening inside the card rather than over the app. Its chevron shows on hover or once open, the way the plan's rows do. Open, the card takes the rest of the side. It carries no height of its own — the column is a definite height now and the card grows into what the plan and a question are not using, with the evidence scrolling and the two actions on the card's floor. A capped body ended the card mid-word against a hard edge, which is the "black bar" it read as. The column moved out of the canvas and onto the panel: the canvas stops above the composer, so a column parented there cut an open card off at that seam. `focusGutterRight` already insets the scroller *and* the composer by the column's width, so the side below the canvas was reserved for it and standing empty. The transcript's entry keeps a sheet, but portalled to the body on the app's Z scale — an AI panel sits inside transformed, clipped ancestors that a sheet left in the tree inherits. `ResultEvidence` is shared by both surfaces so they cannot drift, and is exported so its markup is testable without a DOM to click in. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A right-aligned card in a column of full-width ones read as a different kind of thing, and it is not: both are windows on the canvas. So the result card fills the column at either size — compact keeps the width and centres its one mark rather than shrinking to a button in the middle of the stack. It closes, too. The plan has always had a way out of the corner; the result now carries the same mark at the same size, always visible at full width (a window you can put away should say so before it is hovered) and revealed on hover when the column is narrow, where the resting state is one mark and nothing else. Dismissal is held by run id rather than a flag, so the next run's result comes back on its own — the rule the plan follows when a new plan replaces a hidden one — without persisting a preference that only matters while the corner is in view. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closing a card put that card away; nothing put the column away. The corner kept standing with a folded plan mark in it, and there was no way back to the plain canvas — which is the view most of the reading happens in. So the column takes a switch of its own, at its head: the app's pane control from icons.tsx, mirrored, since this pane is on the right. Hidden, the switch is the only thing left in the corner — a way back that disappears is a panel lost for good — and the conversation gets its full width, because the inset follows what is actually in the column. One override: a parked question ignores the switch. That card holds the run, and a hidden question is a run stranded with no way to answer it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The head carried a pane switch that toggled both ways, which meant the open column wore a control that says "there is a panel here" to a reader already looking at one. Open, it is one close button and nothing else. Closed, the way back is the marks themselves, the way the plan has always folded: the plan's reopen pill with its count, and beside it the result's with its file count — icons in the corner of the main view, either of which opens the column. TodoStrip grew a `folded` mode for that, reusing the mark it already draws when a plan is dismissed, and keeping a dismissed plan dismissed — closing the column is not a reason to bring one back. The marks are still something prose can run under, so the conversation gives up their width (76px) rather than the panel's. Overlapping was the thing the column existed to prevent; folding it away should not reintroduce it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…the shots Closing out the day's work on the canvas column. The code here is a peer session's, committed rather than left in the tree overnight: every card in the column now holds its own height (a card that clips its corners has an automatic minimum size of zero, so the column was squashing the plan and the question to fit instead of scrolling them), and the result card's header was tidied around the one-mark rule. The PR's screenshots move to the current state: the open column headed by one close button, and the main view with the plan's and the result's marks in the corner. Typecheck clean, 913 tests green with all of it in the tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The docked Artifact Inspector opened on every base surface, Focus included, where its header ended up as a strip of file tabs squeezed between the composer and the status bar — the same review work the canvas column already does, in a worse place. Focus does not render it now: the result card in the right-hand column lists a run's files and opens its evidence in place, and the canvas keeps one review surface instead of two. With no inspector to open, Focus passes no `onReviewChanges`, so the routes into it (the result's per-file arrows, its "Review changes" footer, the revert row's review link) hide themselves rather than pointing at a panel that will not appear. The workbench is untouched: no column there, so the docked inspector remains its review surface. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Staging only my own hunks out of a file another session was editing pulled one of its lines in with the context: `onOpenArtifact`, whose handler lives in that session's uncommitted work. The branch built here, where the tree has both halves, and broke in CI, where only mine had landed — the tell that the commit was not self-contained. The line goes back to its owner's tree, untouched; the committed file typechecks on its own again. Verified by checking the commit out into a detached tree and running `tsc` against that alone, rather than against a working copy that quietly supplies the missing half. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three things stood between the harness noticing a deliverable and the reader seeing one, and the first two only showed up on the live path. `git status --porcelain` collapses a wholly-untracked directory into one `?? q3-demo/` entry, so the first real test recorded the folder — 96 bytes, no name of the deck inside it. `-uall` asks for the files, and a path ending in `/` is dropped as the directory it is. `turnDriver.handleEvent` is a switch naming the events that reach the fold; everything else hits `default` and is dropped. `artifact_produced` was missing from it, so a produced document persisted to disk, replayed after a reload, and was invisible while the run was happening — which is when it matters. The live-and-replay test now carries one through the driver, so the next omission fails the build. The card grows a Documents section, in the shared evidence so the island and the sheet cannot drift. Each row names the file, its folder and its size — the one fact that tells a real deck from an empty one a script failed to fill — and opens it: text in the inspector, everything else in the application the machine already owns it with, through a new `open_entry`. And a picture, because a list of filenames is not a preview. macOS already draws decks, spreadsheets and PDFs for Quick Look, so `preview_file` asks `qlmanage` for a thumbnail (~200ms, returned as a data URI) rather than Klide growing a converter per format. Images skip it and come straight off disk. No preview is a normal answer: the row keeps its name and its size. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two things a run makes you deal with — a question it is parked on, and the result it finished with — lived at the bottom of the transcript, under whatever turn produced them. Both scroll away. On the Focus canvas there is already a column for things you watch rather than read: the plan island. They join it.
Focus canvas — plan, result, question, one column, sized off the window
Three canvas widths, top to bottom: 1210px → a 320px column with the full
Review result · 1 file ↗; 900px → 304px; 700px → 232px and the entry down to its mark and count. Same glass, same hairline, same radius all the way down; the question names itself the way the plan does; the review entry wears the folded plan mark's shape. The last frame is the workbench placement, where the transcript is the only surface there is and both stay inline.The corner is measured, not fixed
320px of islands plus a 760px reading column needs 1116px of canvas, which a laptop window does not have — the column used to keep its width while the prose lost its. The islands now take whatever is left once the prose has a readable 560px, clamped to 232–320px, and the inset the conversation gives up follows that measurement. Under 260px the column is a corner rather than a panel, so the result entry drops its words for
ReviewIcon+ the file count (label moves to the tooltip and the accessible name). The drawer is a side sheet on a roomy window and full-bleed under 560px; under 620px of height its chrome gives padding back and one command's output can no longer own the whole sheet. The column also keeps a height budget and scrolls inside it, so a question stays reachable under a long plan on a short window.The question card
The card the harness drew for
userAnswerQuestionhad a bordered answer field inside a bordered card and an accent-filledSubmit ⌘↩pill — two frames and a colour the design system spends nowhere else. It is a composer addressed to one question, so it is drawn as one: question on top, borderless answer box lined up under it, hairline foot carrying Skip and the composer's own round send (grey while empty, accent once there is text). In the island a hairline separates question from answer, because in 320px the placeholder read as one more line of the question.An empty answer is no longer an answer — ⌘↩ and the send button stay inert until there is text; Skip is how you say nothing. Before, ⌘↩ on an empty box sent
""to the harness as the user's reply.The result entry
The newest completion worth opening, once, instead of one row per finished turn down the transcript.
CompletionCardtakes the sameinline|islandsplit, and both placements now ask for changes through one handler rather than two copies of the same prompt.The column itself
TodoStrip's island used to position itself attop:16 right:18; three self-positioning cards would have had to measure each other.AiPanelowns one absolutely-positioned column now —gap: 10,pointer-events: noneso the conversation stays clickable around the cards, each card taking its own clicks back, a height budget it scrolls inside (a wheel over any card bubbles to it), and every card holding its own height so a long plan scrolls rather than squashing the question under it.Open, it heads with one close button. Closed, the way back is the marks themselves, the way the plan has always folded: the plan's reopen pill with its count, and beside it the result's with its file count — icons in the corner of the main view, either of which opens the column. The conversation gives up the marks' 76px rather than the panel's width, because overlapping is the thing the column exists to prevent. A parked question overrides the close: that card holds the run, and a hidden question strands it.
Each level of "put away" has its own scope — the plan's ✕ folds the plan to its mark, the result's ✕ dismisses that result until the next run produces one (held by run id, not a flag), the column's ✕ clears the corner entirely.
Also
.ai-qa-cardhad no rule behind it — a class three review cards wore (question, diff, command) while every message around them rose. It now carries the settle-in, with.klide-ask-islandbeside it on the todo island's curve, both reduced-motion guarded.AskIconjoinssrc/icons.tsx(PhosphorChatTeardropDots, theAiIconfamily) instead of the private?-circle SVG the panel drew.Checks
npx tsc --noEmitcleanQuestionCard.test.tsx(renders in both placements, names itself only on the canvas, send inert on blank/whitespace)Not yet dogfooded against a live
userAnswerQuestionpause. The result entry's placement is deliberately provisional — it is in the column "for now", and moving it costs one line.🤖 Generated with Claude Code