From 54679971fc526d7a8f823df7472040bc56b699af Mon Sep 17 00:00:00 2001 From: Sergii Demianchuk Date: Sat, 25 Jul 2026 21:07:35 -0400 Subject: [PATCH 1/4] redesign(ai): restyle the signed-in account card to the levelcode.ai classic look MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Manage-account popover referenced a specific model ("Plan: Max · Kimi K2.7 Code", and "unlock Kimi K2.7 Code" on the free CTA) — model churn that confused clients — and carried a verbose "Gateway mode: … proxied by the account server … levelcode.ai.providerMode" explainer that wasn't pulling its weight. Rebuilt to match the levelcode.ai "classic" (atom.io) aesthetic used across the site: - Fixed brand palette scoped to `.acctcard.classic` — One Dark by default, One Light under body.vscode-light (the convention the rest of this webview already uses). One violet accent (#7d6bff / #5b3fd6), hairline borders, 6px card / 4px controls, mono + tabular metadata. - Model-agnostic: the plan line becomes a mono `plan · Max` pill (BYOK: `your own key`); the free-tier CTA is just "Upgrade to Pro". No model name anywhere. - The gateway explainer is gone. Its space isn't refilled with credits (those live on /ai/account, one click away via Manage account) — the card now shows the email (mono) under the name and stays uncluttered. BYOK keeps a one-line "keys never leave your machine". - Sign out becomes a quiet accent link; removed the now-dead privacyNote(). Verified: chat.html scripts parse (0 errors); webviewCss + creditFormat suites green; no model refs / credits / privacyNote left in the card. (Design matches the approved mockup; a live webview render needs the VS Code host.) Co-Authored-By: Claude Opus 4.8 --- extensions/levelcode-ai/media/chat.html | 90 +++++++++++++++++++------ 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/extensions/levelcode-ai/media/chat.html b/extensions/levelcode-ai/media/chat.html index 3dd321a..c863a11 100644 --- a/extensions/levelcode-ai/media/chat.html +++ b/extensions/levelcode-ai/media/chat.html @@ -839,6 +839,58 @@ .acctbody .link { color: var(--accent); cursor: pointer; text-decoration: none; } .acctbody .link:hover { text-decoration: underline; } + /* ── Signed-in account card — the levelcode.ai "classic" (atom.io) look. A FIXED brand palette (not the + editor theme): One Dark by default, One Light under body.vscode-light — the same convention the rest of + this webview uses. One violet accent, hairline borders, tiny radii (6px card / 4px controls), and mono + + tabular-nums metadata (email · plan pill · synced) — the detail that sells the code-editor personality. */ + .acctcard.classic { + --cc-bg: #282c34; --cc-surface: #21252b; --cc-text: #dcdfe4; --cc-text2: #abb2bf; --cc-text3: #828997; + --cc-line: #3e4451; --cc-accent: #7d6bff; + --cc-mono: var(--vscode-editor-font-family, ui-monospace, "SF Mono", Menlo, Consolas, monospace); + max-width: 300px; padding: 24px 22px 20px; border-radius: 6px; + background: var(--cc-surface); color: var(--cc-text); border: 1px solid var(--cc-line); + box-shadow: 0 1px 2px rgba(0,0,0,.05); + } + body.vscode-light .acctcard.classic, body.vscode-high-contrast-light .acctcard.classic { + --cc-bg: #ffffff; --cc-surface: #fafaf9; --cc-text: #333333; --cc-text2: #555555; --cc-text3: #777777; + --cc-line: #e0e0e0; --cc-accent: #5b3fd6; + } + .acctcard.classic .ovclose { color: var(--cc-text3); } + .acctcard.classic .ovclose:hover { color: var(--cc-text); background: color-mix(in srgb, var(--cc-text3) 18%, transparent); } + .acctcard.classic .bigavatar { + width: 56px; height: 56px; border-radius: 50%; margin: 2px auto 12px; display: grid; place-items: center; + border: 1px solid var(--cc-line); background: var(--cc-bg); color: var(--cc-text); + font-weight: 600; font-size: 18px; letter-spacing: .02em; + } + .acctcard.classic h2 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -.01em; color: var(--cc-text); } + .acctcard.classic .em { margin: 5px 0 0; font-family: var(--cc-mono); font-size: 12.5px; color: var(--cc-text3); } + .acctcard.classic .planpill { + display: inline-block; margin: 12px 0 2px; border: 1px solid var(--cc-line); background: var(--cc-bg); + border-radius: 3px; padding: 4px 10px; font-family: var(--cc-mono); font-size: 12px; color: var(--cc-text2); + } + .acctcard.classic .abtn { + display: block; width: 100%; box-sizing: border-box; margin: 16px 0 0; padding: 10px 14px; border-radius: 4px; + border: 1px solid var(--cc-line); background: var(--cc-bg); color: var(--cc-text); + font-size: 14px; font-weight: 500; cursor: pointer; transition: border-color .15s ease; + } + .acctcard.classic .abtn:hover { border-color: var(--cc-accent); } + .acctcard.classic .abtn.primary { background: var(--cc-accent); color: #fff; border-color: transparent; } + .acctcard.classic .abtn.primary:hover { filter: brightness(1.06); } + .acctcard.classic .signout { + display: inline-block; margin-top: 14px; padding: 2px 6px; border: none; background: transparent; + color: var(--cc-accent); font: inherit; font-size: 13px; cursor: pointer; border-radius: 4px; + } + .acctcard.classic .signout:hover { text-decoration: underline; } + .acctcard.classic .signout:focus-visible { outline: 2px solid var(--cc-accent); outline-offset: 2px; } + .acctcard.classic .synced { + margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--cc-line); + font-family: var(--cc-mono); font-size: 11.5px; color: var(--cc-text3); line-height: 1.5; + } + .acctcard.classic .note { + margin-top: 12px; font-family: var(--cc-mono); font-size: 11.5px; color: var(--cc-text3); + line-height: 1.55; text-align: left; + } + /* ── LevelCode sign-in modal — calm, soft, THEME-FOLLOWING (matches the Manage-account card) ──── Scoped to .acctcard.lc-modal. Uses the editor theme tokens (--vscode-*, --border, --muted, --field-bg) so it adapts light/dark exactly like the signed-in card, with a gentle lavender (--accent) as the @@ -2689,41 +2741,39 @@ if (acctLastFocus && acctLastFocus.focus) { try { acctLastFocus.focus(); } catch(e){} } // restore focus to the opener acctLastFocus = null; } - // Mode-aware privacy note: BYOK = keys never touch the cloud; gateway = metered plan through LevelCode. - function privacyNote(gateway){ - return gateway - ? '
Gateway mode: AI runs through your metered LevelCode plan — no provider key needed. Requests are proxied by the account server. Switch to BYOK any time in settings (levelcode.ai.providerMode).
' - : '
BYOK mode: the account only syncs settings/skills. AI requests stay direct from your machine to your provider — your keys never touch the cloud.
'; - } function renderAccount(state){ account = state || { signedIn: false }; const gateway = account.mode === 'gateway'; if (account.signedIn){ - acctCard.classList.remove('lc-modal'); // signed-in keeps the plain themed card + acctCard.classList.remove('lc-modal'); + acctCard.classList.add('classic'); // signed-in → the levelcode.ai "classic" (atom.io) look acctAvatar.textContent = initials(account.name || account.email); acctAvatar.classList.add('signed'); - // Gateway: show the active plan (and credits when the server reports them). BYOK: plan is display-only. const paidPlan = !!(account.plan && account.plan.trim().toLowerCase() !== 'free'); - const planLine = gateway - ? '

Plan: ' + esc(account.plan || 'Free') + ' · ' - + esc(paidPlan ? 'Kimi K2.7 Code' : 'gpt-oss-120b') - + (account.credits != null ? ' · ' + esc(String(account.credits)) + ' credit' + (Number(account.credits) === 1 ? '' : 's') + ' left' : '') - + '

' - : '

' + esc(account.email || '') + (account.plan ? ' · ' + esc(account.plan) : '') + '

'; - // Free-tier Upgrade CTA (gateway only): unlock the flagship Kimi K2.7 Code. + // Model-agnostic: a mono pill, never a model name (it churns and confused clients), and no credits + // (that lives on /ai/account). "plan · Max" for gateway; "your own key" for BYOK. + const planPill = gateway + ? 'plan · ' + esc(account.plan || 'Free') + '' + : 'your own key' + (account.plan ? ' · ' + esc(account.plan) : '') + ''; + const emailLine = account.email ? '

' + esc(account.email) + '

' : ''; const upgradeBtn = (gateway && !paidPlan) - ? '' + ? '' : ''; + // Gateway needs no explainer (the "how the gateway works" blurb was removed). BYOK keeps a one-line + // privacy reassurance, which is genuinely useful there. + const note = gateway ? '' : '
Your keys never leave your machine — requests go direct to your provider.
'; acctBody.innerHTML = '
' + esc(initials(account.name || account.email)) + '
' + '

' + esc(account.name || 'Signed in') + '

' - + planLine + + emailLine + + planPill + upgradeBtn + '' - + '' - + '
' + codicon('check-circle') + ' Synced: settings · skills · keymaps
' - + privacyNote(gateway); + + '' + + '
Synced · settings · skills · keymaps
' + + note; } else { + acctCard.classList.remove('classic'); acctCard.classList.add('lc-modal'); // signed-out → the calm LevelCode sign-in modal acctAvatar.innerHTML = PERSON_SVG; acctAvatar.classList.remove('signed'); From 8169a7942e23339ef95c48f4f9f07d097a952213 Mon Sep 17 00:00:00 2001 From: Sergii Demianchuk Date: Sat, 25 Jul 2026 21:15:41 -0400 Subject: [PATCH 2/4] polish(ai): give the account-card avatar a violet-accent tint (PR #50) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The neutral hollow-ring avatar read as empty, especially in One Dark. Fill it with a soft accent tint + violet initials — anchored and on-brand, matching the original card's tinted-avatar approach in the new classic palette. (Note: the "broken/left-aligned" look in the extracted-CSS preview was a preview bug — it dropped the base `.acctbody { text-align: center }` rule. The shipped card centers all its content; verified the rule is present.) Co-Authored-By: Claude Opus 4.8 --- extensions/levelcode-ai/media/chat.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/levelcode-ai/media/chat.html b/extensions/levelcode-ai/media/chat.html index c863a11..790cc13 100644 --- a/extensions/levelcode-ai/media/chat.html +++ b/extensions/levelcode-ai/media/chat.html @@ -858,8 +858,9 @@ .acctcard.classic .ovclose { color: var(--cc-text3); } .acctcard.classic .ovclose:hover { color: var(--cc-text); background: color-mix(in srgb, var(--cc-text3) 18%, transparent); } .acctcard.classic .bigavatar { - width: 56px; height: 56px; border-radius: 50%; margin: 2px auto 12px; display: grid; place-items: center; - border: 1px solid var(--cc-line); background: var(--cc-bg); color: var(--cc-text); + width: 56px; height: 56px; border-radius: 50%; margin: 2px auto 14px; display: grid; place-items: center; + border: 1px solid color-mix(in srgb, var(--cc-accent) 42%, var(--cc-line)); + background: color-mix(in srgb, var(--cc-accent) 13%, transparent); color: var(--cc-accent); font-weight: 600; font-size: 18px; letter-spacing: .02em; } .acctcard.classic h2 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -.01em; color: var(--cc-text); } From bbb354b19f7473337b7dfaef640afd8e62af470a Mon Sep 17 00:00:00 2001 From: Sergii Demianchuk Date: Sat, 25 Jul 2026 22:05:06 -0400 Subject: [PATCH 3/4] fix(ai): actually set tabular-nums on the classic account card (PR #50 review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card's comment claimed "tabular-nums metadata" but no rule set font-variant-numeric, so digits (the email, any future counter) weren't aligned. Set it on the .acctcard.classic root — the house pattern here (used in ~10 other places) — so the comment is true and numbers align throughout the card. Co-Authored-By: Claude Opus 4.8 --- extensions/levelcode-ai/media/chat.html | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/levelcode-ai/media/chat.html b/extensions/levelcode-ai/media/chat.html index 790cc13..f51ee08 100644 --- a/extensions/levelcode-ai/media/chat.html +++ b/extensions/levelcode-ai/media/chat.html @@ -847,6 +847,7 @@ --cc-bg: #282c34; --cc-surface: #21252b; --cc-text: #dcdfe4; --cc-text2: #abb2bf; --cc-text3: #828997; --cc-line: #3e4451; --cc-accent: #7d6bff; --cc-mono: var(--vscode-editor-font-family, ui-monospace, "SF Mono", Menlo, Consolas, monospace); + font-variant-numeric: tabular-nums; /* align digits across the card (email, any future counters) */ max-width: 300px; padding: 24px 22px 20px; border-radius: 6px; background: var(--cc-surface); color: var(--cc-text); border: 1px solid var(--cc-line); box-shadow: 0 1px 2px rgba(0,0,0,.05); From 4ce2a32871b66d665c5caf207363c6ba589b74af Mon Sep 17 00:00:00 2001 From: Sergii Demianchuk Date: Sat, 25 Jul 2026 22:44:44 -0400 Subject: [PATCH 4/4] a11y(ai): high-contrast themes defer to editor tokens on the account card (PR #50 review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The classic card used a fixed brand palette; high-contrast DARK (body.vscode-high-contrast) fell through to the One Dark brand palette, overriding the user's accessibility theme, and high-contrast LIGHT was mapped to the light brand palette rather than HC tokens. Now BOTH high-contrast modes map the card's --cc-* vars onto the editor's own tokens — --vscode-foreground / editor(Widget)-background / descriptionForeground / textLink-foreground, and crucially --vscode-contrastBorder for the required visible edges. The lone filled control (free-tier Upgrade) takes --vscode-button-* instead of white-on-accent. Normal light/dark keep the brand look; HC users keep their contrast. Co-Authored-By: Claude Opus 4.8 --- extensions/levelcode-ai/media/chat.html | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/extensions/levelcode-ai/media/chat.html b/extensions/levelcode-ai/media/chat.html index f51ee08..ed2f02d 100644 --- a/extensions/levelcode-ai/media/chat.html +++ b/extensions/levelcode-ai/media/chat.html @@ -841,7 +841,8 @@ /* ── Signed-in account card — the levelcode.ai "classic" (atom.io) look. A FIXED brand palette (not the editor theme): One Dark by default, One Light under body.vscode-light — the same convention the rest of - this webview uses. One violet accent, hairline borders, tiny radii (6px card / 4px controls), and mono + + this webview uses; high-contrast themes defer to the editor's own tokens so the a11y choice wins. + One violet accent, hairline borders, tiny radii (6px card / 4px controls), and mono + tabular-nums metadata (email · plan pill · synced) — the detail that sells the code-editor personality. */ .acctcard.classic { --cc-bg: #282c34; --cc-surface: #21252b; --cc-text: #dcdfe4; --cc-text2: #abb2bf; --cc-text3: #828997; @@ -852,10 +853,27 @@ background: var(--cc-surface); color: var(--cc-text); border: 1px solid var(--cc-line); box-shadow: 0 1px 2px rgba(0,0,0,.05); } - body.vscode-light .acctcard.classic, body.vscode-high-contrast-light .acctcard.classic { + body.vscode-light .acctcard.classic { --cc-bg: #ffffff; --cc-surface: #fafaf9; --cc-text: #333333; --cc-text2: #555555; --cc-text3: #777777; --cc-line: #e0e0e0; --cc-accent: #5b3fd6; } + /* High-contrast (dark OR light): defer to the user's accessibility theme instead of the fixed brand + palette — map the card's vars onto the editor's own tokens, with contrastBorder for the required edges, + so the a11y choice wins over the brand look. */ + body.vscode-high-contrast .acctcard.classic, body.vscode-high-contrast-light .acctcard.classic { + --cc-bg: var(--vscode-editor-background, #000); + --cc-surface: var(--vscode-editorWidget-background, var(--vscode-editor-background, #000)); + --cc-text: var(--vscode-foreground); + --cc-text2: var(--vscode-foreground); + --cc-text3: var(--vscode-descriptionForeground, var(--vscode-foreground)); + --cc-line: var(--vscode-contrastBorder, var(--vscode-editorWidget-border, var(--vscode-widget-border))); + --cc-accent: var(--vscode-textLink-foreground, var(--vscode-focusBorder)); + } + /* The one filled control (free-tier Upgrade) takes the editor's button colors in HC, not white-on-accent. */ + body.vscode-high-contrast .acctcard.classic .abtn.primary, body.vscode-high-contrast-light .acctcard.classic .abtn.primary { + background: var(--vscode-button-background); color: var(--vscode-button-foreground); + border: 1px solid var(--vscode-contrastBorder, transparent); + } .acctcard.classic .ovclose { color: var(--cc-text3); } .acctcard.classic .ovclose:hover { color: var(--cc-text); background: color-mix(in srgb, var(--cc-text3) 18%, transparent); } .acctcard.classic .bigavatar {