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

feat(chat): one column for the whole panel, widened to the reference — T7 - #75

Merged
ndemianc merged 2 commits into
developfrom
feat/chat-shell-column
Aug 16, 2026
Merged

feat(chat): one column for the whole panel, widened to the reference — T7#75
ndemianc merged 2 commits into
developfrom
feat/chat-shell-column

Conversation

Copy link
Copy Markdown
Contributor

Two problems, both visible the moment you put the two apps side by side.

1. The composer was never in the column (D9)

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

The measure moves from #log to body. That's the load-bearing part, not tidying: a custom property declared on the log is invisible to the log's siblings, which is exactly how the split arose in the first place.

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.
  • The runtime override moves too — chat.proseWidth was written onto #log. Left there it would resize the conversation and leave the composer on the stylesheet default, reopening this same split but only for users who set the setting, which is the worst place for it to hide.

Measured in headless Chrome. Composer vs 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 quietly misaligned by 4px a side before this, in the surface almost everyone uses.

2. The measure was too narrow (D1 revised, 680 → 820)

At the same window width the reference's column is ~815px against our 680 — about half the available width where we used 41%. On a parameter chosen from first principles versus the artefact we are explicitly trying to match, the artefact wins.

Stated honestly in the doc rather than smuggled in: 820px is ~130 characters at 14px, outside the print range D1 itself argues doesn't transfer — and ~815 is read off a screenshot, so 820 is a round number near a measured one, not a transcription. What makes it acceptable is that 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.

Guards

Each bypass-verified by reverting the fix:

bypass caught
composer + status dropped from the shell rule
bare width: 100% instead of the --shell-x inset
shell not centred
#log padding decoupled from --shell-x
--shell-x no longer widening at 760px
runtime override written back onto #log
a second --prose-max on #log — two sources of truth
doc drifting from the shipped cap ✓ (the existing pin, still biting)

The last one fired for real during this change: the stylesheet moved to 820 and the pin failed until D1 was rewritten. That's the guard from #71 earning its keep.

One latent test bug this exposed

The rhythm test located its media block with indexOf('@media (min-width: 760px)'). Adding a second gate on the same breakpoint made it assert against whichever came first in the file — so it would have started passing or failing on rule order rather than on the thing it checks. It now finds the block by content.

29 tests in webviewCss, 32 suites green.

…— T7

Two things, both from a side-by-side with the Claude Code console.

1. THE COMPOSER WAS NEVER IN THE COLUMN (D9)

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

The measure moves from `#log` to `body`. That is the load-bearing part, not tidying:
a custom property declared on the log is invisible to the log's siblings, which is
exactly how the split arose.

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 was written onto #log; left there
    it would resize the conversation and leave the composer on the stylesheet default,
    reopening this same split but only for users who set it.

Measured in headless Chrome, composer vs prose edges, left AND right:

              before (prose/composer)      after
  1600px      680 / 1584 (452px/side)      820 / 820 — delta 0.0
   900px      680 / 884                    820 / 820 — delta 0.0
   800px      680 / 784                    752 / 752 — delta 0.0
   420px      476 / 484                    476 / 476 — delta 0.0

The 420px row is the argument for --shell-x on its own: the sidebar was quietly
misaligned by 4px a side before this.

2. THE MEASURE WAS TOO NARROW (D1 revised, 680 -> 820)

At the same window width the reference's column is ~815px against our 680 — half the
available width where we used 41%. On a parameter chosen from first principles versus
the artefact we are explicitly trying to match, the artefact wins.

Stated honestly in the doc: 820px is ~130 characters at 14px, outside the print range
D1 already argues does not transfer, and ~815 is read off a screenshot — so 820 is a
round number near a measured one, not a transcription.

Guards, each bypass-verified by reverting the fix:
  - composer/status dropped from the shell rule; shell uncentred; bare width:100%
  - #log padding decoupled from --shell-x; --shell-x no longer widening at 760px
  - the runtime override written back onto #log
  - a second --prose-max declared on #log (two sources of truth for one column)
  - the doc drifting from the shipped cap (the existing pin, still biting)

Also fixed a latent test bug this exposed: the rhythm test located its media block with
indexOf, so adding a second min-width:760px gate made it assert against whichever came
first in the file. It now finds the block by content.

29 tests in webviewCss, 32 suites green.
Copilot AI lite review requested due to automatic review settings August 15, 2026 22:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Pull request overview

This PR updates the LevelCode AI chat webview styling to match the reference layout more closely by making the entire panel (transcript + composer/status/notice bars) share one centered column, and by widening the shipped reading measure to 820px. It also updates the CSS test suite and the chat typography design doc to guard against regressions and keep documentation pinned to shipped values.

Changes:

  • Move --prose-max (and related shell layout tokens) from #log to body, add --shell-x, and apply a shared shell-column rule to composer/status/notice bars.
  • Retarget the runtime chat.proseWidth override to document.body so it affects composer + transcript together.
  • Update/extend webviewCss tests and revise docs/CHAT-TYPOGRAPHY.md (D1 → 820px, add D9/T7).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
extensions/levelcode-ai/media/chat.html Implements the single-column shell layout, widens the measure to 820px, and moves runtime CSS var overrides to document.body.
extensions/levelcode-ai/test/webviewCss.test.js Updates and adds guards to ensure the measure and shell column stay unified, and that runtime overrides target the correct element.
docs/CHAT-TYPOGRAPHY.md Revises D1 to 820px and documents the new D9/T7 “shell column” rationale and exit criteria.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 338 to 342
const gates = [...css.matchAll(/@media \(min-width: 760px\)/g)].map((m) => m.index);
assert.ok(gates.length, 'the width gate is gone — the rhythm change would now hit the sidebar too');
const at = gates.find((i) => css.slice(i, css.indexOf('\n }', i)).includes('#log {'));
assert.ok(at !== undefined, 'no min-width:760px gate contains the #log rhythm rules');
const block = css.slice(at, css.indexOf('\n }', at));
Comment on lines +30 to +32
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
…ing the cap

Both review comments on #75 were right.

1. THE GATE LOOKUP WAS STILL ORDER-DEPENDENT

My earlier fix found the rhythm block by content instead of position, but still
terminated the slice with `indexOf('\n  }')`. The --shell-x gate is written on one line,
so it has no `\n  }` of its own — the search ran straight past it into the next multi-line
block. Measured: for a rule whose body is 27 characters, the old search returned 238, i.e.
it reached about nine times past the rule it was supposed to bound.

It happened to resolve correctly, because the swallowed span did not contain `#log {`. That
is luck of ordering, not correctness. Demonstrated by moving the one-liner gate to sit
directly before the rhythm gate: under the old mechanism the FIRST gate's slice then
contains `#log {`, so it selects the wrong gate and asserts against a span covering both —
silently. Under balanced braces the same reorder still resolves to the right gate and all
30 tests pass.

`blockAt()` matches braces, over a comment-stripped copy so a brace inside prose cannot
throw off the count.

2. THE STYLESHEET COMMENT KEPT ITS OWN COPY OF THE CAP

It still said 680px and "~116 characters" after the cap became 820. That is the third place
this number has drifted and the only one nothing was watching — the existing pin covers
CHAT-TYPOGRAPHY.md, not the stylesheet's own prose.

Not synced: the comment must not carry the number at all, so it points at `--prose-max`
instead. The two font measurements (8.13px per `ch`, 5.86px per average character) stay,
because those are facts about the typeface rather than a decision that can be revised.

Guards, each bypass-verified by reverting the fix:
  - the rhythm gate losing its padding, proving the block is located non-vacuously
  - the comment restating the current cap, and restating the OLD one (the reviewer's bug)
  - the comment no longer pointing at --prose-max
  - plus the reorder above, which the previous mechanism failed and this one survives

30 tests in webviewCss, 32 suites green.

Copy link
Copy Markdown
Contributor Author

Both right. Fixed in ffa5f0b.

1. The gate lookup was still order-dependent

Correct, and my previous fix only got half of it. I switched to finding the block by content but still terminated the slice with indexOf('\n }') — and the --shell-x gate is written on one line, so it has no \n } of its own. The search ran straight past it into the next multi-line block.

Measured on the current file:

gate rule body old literal-brace slice new balanced-brace block
{ body { --shell-x: 24px; } } 27 chars 238 chars 27 chars
the rhythm gate 335 chars 359 chars 335 chars

It reached about nine times past the rule it was supposed to bound.

It happened to resolve correctly, because the swallowed span didn't contain #log { — luck of ordering, not correctness. Demonstrated by moving the one-liner gate to sit directly before the rhythm gate:

OLD mechanism on the FIRST gate -> contains "#log {": True   (picks the WRONG gate)

At that point it would select the wrong gate and assert against a span covering both, silently passing. Under balanced braces the same reorder still resolves to the right gate and all 30 tests pass.

blockAt() now counts braces, over a comment-stripped copy so a brace inside prose can't throw off the count.

2. The stylesheet comment kept its own copy of the cap

Also right, and this is the third place this number has drifted — the pin from #71 covers CHAT-TYPOGRAPHY.md, and nothing was watching the stylesheet's own prose.

I took your second suggestion rather than the first: the comment doesn't carry the number at all now, it points at --prose-max. Syncing it would just set up the next drift. The two font measurements (ch = 8.13px, average character = 5.86px) stay — those are facts about the typeface, not a decision that can be revised.

And it's guarded now: the comment must reference --prose-max, and must contain no length other than the two viewport widths (380px, 900px) that describe where the problem showed up.

Guards

Each bypass-verified by reverting the fix:

bypass caught
rhythm gate loses its padding — proves the block is located non-vacuously
comment restates the current cap
comment restates the old cap — your bug, restored
comment stops pointing at --prose-max
the reorder above — failed by the previous mechanism, survived by this one

30 tests in webviewCss, 32 suites green.

ndemianc merged commit b7b4472 into develop Aug 16, 2026
1 check passed
ndemianc deleted the feat/chat-shell-column branch August 16, 2026 00:17
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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL