FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(chat): one column for the whole panel, widened to the reference — T7 by ndemianc · Pull Request #75 · levelcodeai/levelcode · GitHub

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .html  (1) .js  (1) .md  (1) All 3 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
81 changes: 69 additions & 12 deletions docs/CHAT-TYPOGRAPHY.md
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ Two observations worth stating plainly, because they explain almost everything:

### D1 — Constrain the measure. This is the single biggest lever.

`max-width` on the prose column, centred, with the container still full-bleed so cards, code blocks
and the composer keep their current behaviour.
`max-width` on the prose column, centred, with the container still full-bleed so cards and code blocks
keep their current behaviour. *(D9 later brought the composer into the same column; T1 shipped with it
still full-bleed.)*

**Target 680px** — measured, not estimated. An earlier draft of this section said "~72ch, which lands
near 640–700px". Both halves were wrong, and the correction is worth keeping because the mistake is
easy to repeat:
**Target 820px**. T1 shipped 680px, derived from first principles below. That number was later
overruled by the reference itself — see the revision at the end of this decision. The derivation is
kept because its correction is the reusable part; the figure it produced is not.

An earlier draft of this section said "~72ch, which lands near 640–700px". Both halves were wrong, and
that correction is worth keeping because the mistake is easy to repeat:

| measured in the shipped font at 13px | |
| --- | --- |
Expand All @@ -62,9 +66,21 @@ characters would be a 422px column, narrower than the sidebar. The print-typogra
characters does not transfer to a technical chat: it assumes prose without identifiers, file paths or
code, and a 422px column would wrap every code block constantly.

680px is chosen against the measurement: **~116 characters at 13px**, down from 154 at editor width,
while staying wide enough that a fenced block is still readable. When T2 raises the prose size the
same cap tightens to ~108 characters, which is the right direction.
680px was chosen against that measurement: ~116 characters at 13px, down from 154 at editor width,
while staying wide enough that a fenced block is still readable.

**Revised to 820px after comparing against the reference directly.** Side-by-side at the same window
width, the Claude Code console's column is **~815px** against our 680 — it uses about half the
available width where we used 41%. The user's report was "the content space is too narrow", and on a
design parameter chosen from first principles versus the artefact we are explicitly trying to match,
the artefact wins.

Stated honestly: 820px is **~130 characters** at the shipped 14px prose size, which is well outside the
print range this decision already argues does not transfer. Two things make that acceptable rather than
sloppy — the reference demonstrably reads well at that width, and this column carries tables, file
paths and fenced code, none of which wrap gracefully at 680. The risk section's "screenshots are not
measurements" caveat applies: ~815px is read off a screenshot, so 820 is a round number near a
measured one, not a precise transcription.

The elegant part: **in the sidebar this is a no-op.** The container is already narrower than the cap,
so nothing moves for existing users. It only takes effect in the editor tab, which is exactly the
Expand Down Expand Up @@ -196,6 +212,40 @@ Measured in headless Chrome at a 680px column, against `develop`:
Tint stays as the secondary cue rather than being removed: side alone would fail on any surface that
reflows the log to a single column.

### D9 — One column for the whole panel, not just the transcript.

T1 bounded the **transcript** and nothing else. `#composer`, `#status` and the four notice bars are
siblings of `#log`, not children, so none of them saw the cap: at editor width the input was a
**~1580px box sitting under an 820px conversation**. Side by side with the reference — where the
composer sits directly under the text it answers — this was the single most obvious difference left,
more than any type choice.

**The measure moves from `#log` to `body`.** This is the load-bearing part, not tidying. A custom
property declared on the log is invisible to the log's siblings, which is precisely how the split
arose. On `body`, every element in the shell resolves the same value.

Two consequences that are easy to miss:

- **`--shell-x`.** The shell insets by `calc(100% - 2 * var(--shell-x))`, the same value `#log` pads
with, rather than a bare `width: 100%`. Without it the edges agree only where the cap binds, and
out-dent by the log's padding everywhere else — including every sidebar, which is the width most
users are actually in.
- **The runtime override moves too.** `chat.proseWidth` used to be written onto `#log`. Left there it
would resize the conversation and leave the composer on the stylesheet default — reopening this exact
split, but only for users who set the setting, which is the worst place for it to hide.

Verified in headless Chrome at four widths. Composer and prose edges, left and right:

| viewport | before (prose / composer) | after |
| --- | --- | --- |
| 1600px | 680 / **1584** — 452px out-dent per side | 820 / 820 — **Δ 0.0** |
| 900px | 680 / 884 | 820 / 820 — **Δ 0.0** |
| 800px | 680 / 784 | 752 / 752 — **Δ 0.0** |
| 420px (sidebar) | 476 / 484 | 476 / 476 — **Δ 0.0** |

The 420px row is the argument for `--shell-x` on its own: the sidebar was misaligned by 4px a side
before this, quietly, in the surface almost everyone uses.

---

## 3. Slices
Expand Down Expand Up @@ -226,6 +276,11 @@ left and full-measure. **Exit:** a short turn renders as a short right-flush bub
below the column, the assistant is untouched, and the prose inside the bubble is still left-aligned —
all four measured, not eyeballed.

**T7 — the shell column** *(S)*. D9, plus D1's revision to 820px. Ships: the measure moves to `body`,
`--shell-x`, the shell rule over the composer/status/notice bars, and the runtime override retargeted.
**Exit:** composer and prose edges agree to **0.0px at 1600 / 900 / 800 / 420**, and `chat.proseWidth`
moves both together — measured, not eyeballed.

**T5 — the escape hatch** *(S)*. D7. **Folded into T2 and shipped with it.** Sequencing it last was a
mistake: T2 is the one slice that changes what every existing user sees, and shipping a divisive
change with no way back is worse than not shipping it. The plumbing is also shared — once one custom
Expand All @@ -234,10 +289,12 @@ property reaches the webview from settings, the second is a line — so splittin
Sequencing: T1 first and alone — it may turn out to be most of the perceived fix, and shipping it
by itself is the cheapest way to find out before spending effort on T2–T4.

That held up: T1–T2, T4 and T6 have shipped in that order, each visible on its own. **T3 is the only
slice of this plan still outstanding.** D8/T6 was not in the original decomposition — it came from
looking at the reference again after T4, which is the argument for shipping slices small enough to
look at.
That held up: T1–T2, T4, T6 and T7 have shipped in that order, each visible on its own. **T3 is the
only slice of this plan still outstanding.** Neither D8/T6 nor D9/T7 was in the original decomposition
— both came from looking at the reference again after shipping, which is the argument for slices small
enough to look at. D9 in particular was invisible from inside the plan: T1's own wording said the
composer "keeps its current behaviour", and it took a side-by-side screenshot to notice that was the
bug rather than the scope.

---

Expand Down
63 changes: 45 additions & 18 deletions extensions/levelcode-ai/media/chat.html
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
color: var(--vscode-foreground);
background: transparent;
display: flex; flex-direction: column; height: 100vh;
}

/* ---- conversation log ---- */
#log { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 12px;
/* T1 (docs/CHAT-TYPOGRAPHY.md D1) — the reading measure. A custom property so T5 can hand it to a
setting later without touching the rules below. */
--prose-max: 680px;
/* T7 (docs/CHAT-TYPOGRAPHY.md D9) — THE SHELL COLUMN. These three live on `body`, not on `#log`,
because the measure is a property of the whole panel: the transcript, the composer and the status
row are all children of `body`, and they have to resolve the SAME value or they cannot line up.
Declared on `#log` (where T1 put them) only the transcript could see them, which is exactly how
the composer ended up spanning the full panel while the prose sat in a 680px column. */
--prose-max: 820px;
/* The horizontal inset shared by the log's padding and the shell column's width, so the composer's
Comment on lines +30 to +32
left edge lands ON the prose's left edge at every width rather than only where the cap binds. */
--shell-x: 12px;
/* T2 (D2) — the READING type. `--vscode-font-size` is the size of menu labels and tree rows: right
for chrome, wrong for three paragraphs of explanation. Message bodies alone get their own size
and leading; every control around them keeps inheriting the workbench, so the panel still
Expand All @@ -39,7 +42,28 @@
the wrong way. At the default 13px this still resolves to 14px, so nothing moves for anyone who
has not changed it. */
--prose-size: calc(var(--vscode-font-size, 13px) + 1px);
--prose-leading: 1.65; }
--prose-leading: 1.65;
}
@media (min-width: 760px) { body { --shell-x: 24px; } }

/* Everything below the log shares the transcript's column: same width, same centre, same edges.
Before this each one spanned the whole panel, so at editor width the composer was a 1600px box
under a 680px conversation — the single thing that most made the panel look unlike the reference,
where the input sits directly under the text it answers.
`calc(100% - 2 * --shell-x)` rather than a bare `width: 100%` + margin: without it these boxes
would out-dent the transcript by the log's padding in every panel narrower than the cap, which is
most sidebars. Overlays are `position: fixed` and deliberately excluded. */
#bgTasksBar, #ctxBar, #planBar, #reviewBar, #workbar, #composer, #status {
width: calc(100% - 2 * var(--shell-x));
max-width: var(--prose-max);
margin-inline: auto;
}

/* ---- conversation log ---- */
/* The measure, the type and the inset are all declared on `body` (see above) so the composer and the
status row resolve the same values the transcript does. `--shell-x` is the log's horizontal padding
AND the shell column's inset, which is what keeps the two edges on the same line. */
#log { flex: 1; overflow-y: auto; padding: 12px var(--shell-x); display: flex; flex-direction: column; gap: 12px; }
/* Log is a flex column → children default to flex-shrink:1. Any child with overflow:hidden (e.g. the
approval/edit cards) then gets an auto min-size of 0 and the flex algorithm crushes it to an invisible
line once the log overflows. Pin every child to its natural height; the log itself scrolls instead. */
Expand All @@ -48,12 +72,15 @@
in this file are all cards, dialogs and the empty state. In a 380px sidebar the container bounded
it, so it never looked wrong — but the chat can now open as an editor tab (AI: Open Chat in
Editor), and at 900px the same CSS produced ~154-character lines.
Measured in the shipped font at 13px: the average prose character is 5.86px, so 680px is ~116
characters — a real improvement while staying wide enough that a fenced code block does not wrap
constantly. NOT expressed in `ch`: `0` is 8.13px here, 39% wider than average text, so a `ch` cap
silently overshoots by about a third.
The cap itself is `--prose-max`, declared on `body`, and is deliberately NOT restated here. This
comment carried its own copy of the number and said 680 for exactly as long as it took the cap to
become 820 — which is what a second copy of a value is always for. docs/CHAT-TYPOGRAPHY.md D1 has
how it was chosen; webviewCss.test.js fails if this comment starts quoting a length again.
NOT expressed in `ch`: `0` measures 8.13px against an average prose character of 5.86px, so a
`ch` cap silently overshoots by about a third. Those two figures stay because they are facts about
the typeface, not a decision that can be revised.
Applied to EVERY direct child, not just .msg, so messages, cards and the activity timeline share
one column instead of drifting apart at width. Below 680px this is inert — which is why the
one column instead of drifting apart at width. Below the cap this is inert — which is why the
sidebar is untouched. */
#log > * { width: 100%; max-width: var(--prose-max); margin-inline: auto; box-sizing: border-box; }
/* ---- per-response copy button (hover-reveal under a completed assistant message) ---- */
Expand Down Expand Up @@ -174,7 +201,7 @@
opens the editor tab sees nothing move. `min-width` here is the WEBVIEW's own width — the iframe
is sized to the panel, which is why the existing #empty/dialog rules already use vw/vh. */
@media (min-width: 760px) {
#log { padding: 20px 24px; gap: 1.15em; }
#log { padding: 20px var(--shell-x); gap: 1.15em; }
.msg .body p { margin-bottom: 1em; }
.msg .body ul, .msg .body ol { margin-bottom: 1em; }
/* More space ABOVE a heading than below it, so it binds to the section it introduces rather than
Expand Down Expand Up @@ -745,7 +772,7 @@

/* ---- composer ---- */
#composer {
margin: 8px; padding: 8px 8px 6px;
margin: 8px auto; padding: 8px 8px 6px;
border: 1px solid var(--border); border-radius: var(--radius);
background: var(--field-bg);
display: flex; flex-direction: column; gap: 7px;
Expand Down Expand Up @@ -3573,10 +3600,10 @@
if (m.type === 'config'){
// D7 — the escape hatch. 0 means "follow the default": clearing the property lets the stylesheet
// value win again, rather than pinning it to whatever the default happened to be at the time.
const rootLog = document.getElementById('log');
if (rootLog){
rootLog.style.setProperty('--prose-size', m.proseSize ? m.proseSize + 'px' : '');
rootLog.style.setProperty('--prose-max', m.proseWidth ? m.proseWidth + 'px' : '');
const shell = document.body;
if (shell){
shell.style.setProperty('--prose-size', m.proseSize ? m.proseSize + 'px' : '');
shell.style.setProperty('--prose-max', m.proseWidth ? m.proseWidth + 'px' : '');
}
const label = m.provider === 'claude' ? prettyModel(m.model) : m.model;
const mEl = document.getElementById('model');
Expand Down
Loading

Back | FazBrowse Home | New Git URL