Repository navigation
web: a chat-first look, light/dark themes and a layout switch for the browser edition - #106
Merged
Merged
Conversation
The browser edition now opens on the chat the way a chat app does, and one click gives the editor layout (Explorer, files, chat docked on the right): - web/theme/levelcode-web-theme: "LevelCode Web Dark" / "LevelCode Web Light", following the system; initialColorTheme paints the same colours before the theme has loaded. levelcode-themes' pinned default is stripped from the staged copy (an extension's configurationDefaults outrank the embedder's). - web/ai-extension/skin*: a centred greeting with starters, a pill composer in a 768px reading column and the conversation list docked as a rail from 1000px, cut into the browser build's COPY of media/chat.html at build time and scoped under body.lc-web. The desktop page and its suites are untouched. - web/chrome.css, config defaults: floating panels, activity bar as a row, no source control / run / extensions, no Outline / Timeline, no minimap; the workbench's own chat view is told it is disabled; no Welcome walkthrough. - web/workspace: levelcode.web.layout (chatFirst | split), a status-bar switch, three commands, Cmd/Ctrl+Alt+L. A docked layout is docked again after a reload. Tests: skin, theme, layout and copy unit suites (mutation-checked), main.js's new helpers, and web/test/e2e-look.mjs (21 checks, headless Chrome).
An agent review that RAN the build (44 findings, each independently verified) found real defects in the first commit. Fixed: Layout (web/workspace) - The switch read the tab model before the extension host had the chat's tab, so closing the chat and clicking "Editor + chat" left it undocked: wait on onDidChangeTabs, and for the chat to be the active editor. - A later choice now stops an earlier arrangement still waiting (the start-up one resumed and put the chat back on the right). - A reload's restored empty group is closed on the way to chat-first; columns, not list order, say which group is on the left. - Start-up no longer reveals the Explorer or moves focus after the first time in a workspace (workspaceState, not globalState). - The layout commands and the folder command are registered before the store is opened, so a browser that refuses IndexedDB still has them. - Opening a folder from the computer sets the editor layout. - No default key binding (Cmd+Alt+L is Find in Selection on macOS). Chat skin - Card actions revealed on hover only where there is hover; a tap landed on rename/delete. Enter/Space resume a card. aria-modal off the docked list; a /sessions-opened overlay no longer reappears as a modal. - Accent text and the focus ring use the link colour (was 2.2-2.7:1). - The footer row is no longer dimmed with opacity (it dimmed its popover). - The rail and the account card take the theme's palette; the sign-in dialog scrolls in a short window; the empty state is not clipped; the chat paints its own ground; starters make sense with no file open. Workbench - Code-OSS's own Chat view is hidden for real: create() returns a disposable, so workbench.commands was undefined and the first attempt never ran. The gate key is set through the module's commands, again after the gate's own write at 5 s. The title-bar toggle is hidden. - editor.padding.top: 24 (the scroll shadow washed out line one). - No page-level color-scheme: stock webviews (Markdown preview) were white under a dark theme. - The build id covers the declarative built-ins' sources. Tests: unit 73 files on macOS and Linux Node 18; e2e-look 26 checks, ten runs in a row; the other end-to-end checks unchanged. The test helper retries F1 (a key sent while the chat takes the focus is lost).
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Layout switching has unresolved correctness issues, and some syntax colors have insufficient contrast.
6 open findings
What changed in this PR
Adds a chat-first interface to the browser edition introduced in #105, while keeping desktop sources unchanged.
Changes:
- Adds browser-only chat styling and system-following light/dark themes.
- Introduces chat-only and editor/chat layouts with status-bar controls.
- Updates build staging, documentation, and automated checks.
| File | Description |
|---|---|
| web/workspace/package.json | Declares layout commands and setting. |
| web/workspace/extension.js | Implements layout switching and restoration. |
| web/theme/levelcode-web-theme/themes/web-light.json | Adds the light palette. |
| web/theme/levelcode-web-theme/themes/web-dark.json | Adds the dark palette. |
| web/theme/levelcode-web-theme/package.json | Registers browser themes. |
| web/test/unit/workspace.test.js | Tests layout behavior. |
| web/test/unit/theme.test.js | Checks theme data and defaults. |
| web/test/unit/skin.test.js | Tests skin insertion and interactions. |
| web/test/unit/main.test.js | Tests browser appearance helpers. |
| web/test/unit/extensions.test.js | Tests extension staging and fingerprints. |
| web/test/unit/copy.test.js | Checks browser-only Welcome suppression. |
| web/test/e2e.mjs | Updates startup checks. |
| web/test/e2e-look.mjs | Adds browser appearance and layout checks. |
| web/test/e2e-byok.mjs | Reuses the palette helper. |
| web/test/cdp.mjs | Adds a retrying palette helper. |
| web/main.js | Applies initial colors and chrome overrides. |
| web/lib/extensions.mjs | Supports staging and default removal. |
| web/lib/config.mjs | Defines browser appearance defaults. |
| web/index.html | Removes page-level color-scheme metadata. |
| web/chrome.css | Hides unsupported workbench controls. |
| web/boot.css | Removes page-level color-scheme styling. |
| web/ai-extension/skin/chat.js | Adds greetings and conversation-rail behavior. |
| web/ai-extension/skin/chat.css | Styles the browser chat. |
| web/ai-extension/skin.js | Injects the skin into staged HTML. |
| web/ai-extension/copy.js | Adjusts browser copy and Welcome behavior. |
| web/ai-extension/build.mjs | Applies the chat skin during bundling. |
| scripts/build-web.mjs | Packages and fingerprints appearance assets. |
| docs/WEB.md | Documents appearance, layouts, and limitations. |
| CLAUDE.md | Adds browser styling guidance. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
… does The merge of feat/web-edition replaced the build's list of inputs with a hash of the staging folder. The test of that list now stages the extensions and hashes the result the same way, and COPY_SKIP — exported only for the list — goes.
… read in both themes
Both themes' syntax palettes (One Dark / One Light) had colours below WCAG AA's 4.5:1 on their own
editor background. Each one is moved in HSL lightness only (hue and saturation kept, within 2 degrees
after rounding) to the first value that reaches 4.5:1. theme.test.js now holds every tokenColors and
semanticTokenColors foreground of both themes to 4.5:1 on the theme's editor.background (an alpha
colour composited first); it fails on the previous themes, listing each colour.
Dark (editor #1b1c22), review comment 4274117784:
- comments, markup quotes, doc comments / doctype #5c6370 2.81 -> #7b8493 4.50
Light (editor #ffffff), review comment 4274117904:
- comments, markup quotes #a0a1a7 2.58 -> #75767e 4.52
- strings, inherited class, inline/fenced code #50a14f 3.21 -> #428541 4.50
- regexp, escapes, support types #0184bc 4.18 -> #017eb4 4.52
- variables, properties, tags, headings, JSON keys #e45649 3.67 -> #df3425 4.51
- functions, decorators, links #4078f2 4.05 -> #326ef1 4.53
- classes, types, CSS tags #c18401 3.20 -> #9f6d01 4.50
- constants, attribute names, bold #986801 4.87 -> #765101 7.12
(passed already; darkened with the class colour, which shares its hue and would otherwise be
indistinguishable from it: CIELAB delta-E 2.9, against 16.2 before and 16.9 now)
Token backgrounds: the editor does not paint them ("Token background colors are currently not
supported", colorThemeSchema.ts), so the light theme's `invalid` scope (white on a #e45649 background)
was white text on the white editor, 1.00:1. `invalid` is now the theme's red text in both themes
(light #df3425 4.51, dark #e06c75 5.32) with no background, and the test fails on any token rule that
sets one.
…of the chat, fills the window in both layouts, and works where a workspace sets it Review comment 4274117959 (the moves act on whatever is active): whenChatActive resolved on its timeout as well, so the move commands ran whether or not the chat was the active editor and could move the file beside it; and in split the second levelcode.ai.focus, after README.md was opened (and became the editor on show), was followed by the move with no wait at all. Each command that acts on the active editor (the two moves, and the join below) now goes through one guard: the tab model is read immediately before it, after waiting up to 1.5 s for the reveal to land; one that cannot be confirmed is skipped, as is every later one in that arrangement, and so is one overtaken by a newer layout request while it waited. whenChatActive resolves true/false. The wait for the chat to come to the front before the window is read at all stays: when the layout is changed by saving the setting from a settings editor opened in front of the chat, that editor's group is in the tab model until the chat is focused, and read at once the window looks like two groups already, so nothing is docked (found in a browser run of this change before it was committed; pinned by a unit case). If that wait times out, nothing is moved. Tests: chatFirst with the chat never active does not move; split waits after the README until the chat is active again and does not move if it never is (also on the reload path); a split waiting to move is dropped when chatFirst is chosen meanwhile. Review comment 4274118020 (chatFirst left file groups visible): only empty groups were closed, so a group with files (a split file editor, files put on the right) kept part of the window. Such groups are now joined into the chat's group with workbench.action.joinAllGroups, which merges into the active group and moves the editors in inactive, so no document is closed and the chat stays on show. The remaining group is then unlocked: a group the chat was docked in is auto-locked, and a lone locked group sends the next file into a new split. The usual split -> chatFirst switch (the chat alone on the right) is unchanged. Test: three arrangements with files outside the chat's group are joined and unlocked; no join when the chat is not active. Review comment 4274118061 (split in a folder without README.md): with the chat as the only tab nothing kept the first group, so the move closed it and left one group, auto-locked. An empty untitled document is opened in column one first (preserveFocus, not a preview) when there is no README and no other tab; if even that cannot be opened the chat is not moved. The existing test that pinned the old sequence for this case is changed to the new one. Review comment 4274118118 (the switch wrote only the user setting): with levelcode.web.layout in .vscode/settings.json the workspace value wins, so the status item and the commands did nothing. writeLayout() inspects the setting and updates the workspace value when there is one, the user's otherwise. A folder-level value is not a third target: the setting is window-scoped, which VS Code does not read from a multi-root folder (FOLDER_SCOPES), and in a single-folder window the folder's settings are the workspace's. Tests drive activate() and the registered commands. The vscode stand-in now always has activeTabGroup: a file shown by showTextDocument is on show until the chat's reveal lands (a test can hold the reveal back), otherwise the chat is; plus openTextDocument, inspect() and a workspace layer of settings. On the previous extension.js the 8 new or changed cases fail and the other 32 pass.
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.

Stacked on #105 (
feat/web-edition): merge that first, or retarget this atdevelopafter it lands.What
The browser edition opens on the chat, the way a chat app does, instead of on an IDE with a chat in it — and one click gives the editor layout (Explorer, files, the chat docked on the right).
levelcode.web.layout:chatFirst|split), also as three commands. Opening a folder from the computer switches to the editor layout.How, without forking the workbench
web/theme/levelcode-web-theme,initialColorThemeweb/ai-extension/skin.js,skin/chat.{css,js}— three exact insertions into the browser build's copy ofmedia/chat.html, every rule underbody.lc-web; the build fails if the page stops having the places it is cut intoweb/chrome.css, configuration defaults (web/lib/config.mjs)web/workspace/extension.jsdocs/WEB.mdhas a new "The look and the layout" section with the levers, what each one depends on, and what to check on a Code-OSS bump.The desktop app is unchanged
Nothing under
extensions/,branding/orpatches/is touched;extensions/levelcode-ai/media/chat.htmlandextensions/levelcode-themes/package.jsonare byte-identical to the parent. The only reachable path into the desktop gate is the two new unit suites (scripts/test-extensions.shdiscoversweb/test/unit/*.test.js).Tests
skin,theme,extensionssuites; the layout,main.jshelpers and copy rewrites extended. Mutation-checked: each defect put back turns its test red.web/test/e2e-look.mjs(new, 26 checks, 10 consecutive runs green): the skin, the rail, light/dark, no Welcome page, no built-in Chat view, both layouts, docked again after a reload.e2e.mjs30/30 (release and isolated-origin layouts),e2e-signin-tab.mjs20/20,e2e-byok.mjs5/5.Found by running it, not reading it
create()returns a disposable:workbench.commandsisundefined, the embedder's command API is the module'scommands. My first "hide the built-in Chat" helper ran inside atryand did nothing, and a unit test with a stand-in of the same shape passed.configurationDefaultsoutrank the embedder's:levelcode-themespins the desktop's default theme, so the staged copy has that key removed.color-schememade the Markdown preview paint white under a dark theme.modernUIfades the top 24 px of every editor, line one included.Review round (2026-10-14)
All six review threads are answered in place and resolved.
53c1039Syntax colours. Every syntax colour in both themes now reaches 4.5:1 on its editor background; before, comments were 2.8:1 (dark) and 2.6:1 (light). Only lightness changed.theme.test.jsnow checks every token colour, so a regression fails the gate.3925003The layout switch.1d32681mergesfeat/web-edition(feat(web): LevelCode in your browser — the same workbench and AI extension as a static site, signed in with a LevelCode account #105's review fixes).40c0d2aThe build id is now a hash of everything staged, from feat(web): LevelCode in your browser — the same workbench and AI extension as a static site, signed in with a LevelCode account #105, which replaces this branch's list of inputs. The id test now stages and hashes the same way.Verified on
3925003: the unit gate passes, 78 files on Node 24 and on Node 18. In headless Chrome against a release built from it:e2e-look26/26,e2e30/30,e2e-signin-tab20/20,e2e-byok5/5. Two browser probes, not committed, compared the old and new code:.vscode/settings.json: before, the switch did nothing; now it switches.Not run in a browser: the timing guard on the moves, and the no-README case (the scratch workspace always has a README).
Not done / not run
workbench.experimental.modernUIis Code-OSS's experimental flag; the pinned checkout is the only one run.Draft: for review, not for merge until #105 is decided.