Skip to content

Commit 502cd75

Browse files
authored
Merge pull request #50 from levelcodeai/redesign/account-card
redesign(ai): restyle the signed-in account card to the levelcode.ai classic look
2 parents 6a379ef + 4ce2a32 commit 502cd75

1 file changed

Lines changed: 90 additions & 20 deletions

File tree

extensions/levelcode-ai/media/chat.html

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,78 @@
839839
.acctbody .link { color: var(--accent); cursor: pointer; text-decoration: none; }
840840
.acctbody .link:hover { text-decoration: underline; }
841841

842+
/* ── Signed-in account card — the levelcode.ai "classic" (atom.io) look. A FIXED brand palette (not the
843+
editor theme): One Dark by default, One Light under body.vscode-light — the same convention the rest of
844+
this webview uses; high-contrast themes defer to the editor's own tokens so the a11y choice wins.
845+
One violet accent, hairline borders, tiny radii (6px card / 4px controls), and mono +
846+
tabular-nums metadata (email · plan pill · synced) — the detail that sells the code-editor personality. */
847+
.acctcard.classic {
848+
--cc-bg: #282c34; --cc-surface: #21252b; --cc-text: #dcdfe4; --cc-text2: #abb2bf; --cc-text3: #828997;
849+
--cc-line: #3e4451; --cc-accent: #7d6bff;
850+
--cc-mono: var(--vscode-editor-font-family, ui-monospace, "SF Mono", Menlo, Consolas, monospace);
851+
font-variant-numeric: tabular-nums; /* align digits across the card (email, any future counters) */
852+
max-width: 300px; padding: 24px 22px 20px; border-radius: 6px;
853+
background: var(--cc-surface); color: var(--cc-text); border: 1px solid var(--cc-line);
854+
box-shadow: 0 1px 2px rgba(0,0,0,.05);
855+
}
856+
body.vscode-light .acctcard.classic {
857+
--cc-bg: #ffffff; --cc-surface: #fafaf9; --cc-text: #333333; --cc-text2: #555555; --cc-text3: #777777;
858+
--cc-line: #e0e0e0; --cc-accent: #5b3fd6;
859+
}
860+
/* High-contrast (dark OR light): defer to the user's accessibility theme instead of the fixed brand
861+
palette — map the card's vars onto the editor's own tokens, with contrastBorder for the required edges,
862+
so the a11y choice wins over the brand look. */
863+
body.vscode-high-contrast .acctcard.classic, body.vscode-high-contrast-light .acctcard.classic {
864+
--cc-bg: var(--vscode-editor-background, #000);
865+
--cc-surface: var(--vscode-editorWidget-background, var(--vscode-editor-background, #000));
866+
--cc-text: var(--vscode-foreground);
867+
--cc-text2: var(--vscode-foreground);
868+
--cc-text3: var(--vscode-descriptionForeground, var(--vscode-foreground));
869+
--cc-line: var(--vscode-contrastBorder, var(--vscode-editorWidget-border, var(--vscode-widget-border)));
870+
--cc-accent: var(--vscode-textLink-foreground, var(--vscode-focusBorder));
871+
}
872+
/* The one filled control (free-tier Upgrade) takes the editor's button colors in HC, not white-on-accent. */
873+
body.vscode-high-contrast .acctcard.classic .abtn.primary, body.vscode-high-contrast-light .acctcard.classic .abtn.primary {
874+
background: var(--vscode-button-background); color: var(--vscode-button-foreground);
875+
border: 1px solid var(--vscode-contrastBorder, transparent);
876+
}
877+
.acctcard.classic .ovclose { color: var(--cc-text3); }
878+
.acctcard.classic .ovclose:hover { color: var(--cc-text); background: color-mix(in srgb, var(--cc-text3) 18%, transparent); }
879+
.acctcard.classic .bigavatar {
880+
width: 56px; height: 56px; border-radius: 50%; margin: 2px auto 14px; display: grid; place-items: center;
881+
border: 1px solid color-mix(in srgb, var(--cc-accent) 42%, var(--cc-line));
882+
background: color-mix(in srgb, var(--cc-accent) 13%, transparent); color: var(--cc-accent);
883+
font-weight: 600; font-size: 18px; letter-spacing: .02em;
884+
}
885+
.acctcard.classic h2 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -.01em; color: var(--cc-text); }
886+
.acctcard.classic .em { margin: 5px 0 0; font-family: var(--cc-mono); font-size: 12.5px; color: var(--cc-text3); }
887+
.acctcard.classic .planpill {
888+
display: inline-block; margin: 12px 0 2px; border: 1px solid var(--cc-line); background: var(--cc-bg);
889+
border-radius: 3px; padding: 4px 10px; font-family: var(--cc-mono); font-size: 12px; color: var(--cc-text2);
890+
}
891+
.acctcard.classic .abtn {
892+
display: block; width: 100%; box-sizing: border-box; margin: 16px 0 0; padding: 10px 14px; border-radius: 4px;
893+
border: 1px solid var(--cc-line); background: var(--cc-bg); color: var(--cc-text);
894+
font-size: 14px; font-weight: 500; cursor: pointer; transition: border-color .15s ease;
895+
}
896+
.acctcard.classic .abtn:hover { border-color: var(--cc-accent); }
897+
.acctcard.classic .abtn.primary { background: var(--cc-accent); color: #fff; border-color: transparent; }
898+
.acctcard.classic .abtn.primary:hover { filter: brightness(1.06); }
899+
.acctcard.classic .signout {
900+
display: inline-block; margin-top: 14px; padding: 2px 6px; border: none; background: transparent;
901+
color: var(--cc-accent); font: inherit; font-size: 13px; cursor: pointer; border-radius: 4px;
902+
}
903+
.acctcard.classic .signout:hover { text-decoration: underline; }
904+
.acctcard.classic .signout:focus-visible { outline: 2px solid var(--cc-accent); outline-offset: 2px; }
905+
.acctcard.classic .synced {
906+
margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--cc-line);
907+
font-family: var(--cc-mono); font-size: 11.5px; color: var(--cc-text3); line-height: 1.5;
908+
}
909+
.acctcard.classic .note {
910+
margin-top: 12px; font-family: var(--cc-mono); font-size: 11.5px; color: var(--cc-text3);
911+
line-height: 1.55; text-align: left;
912+
}
913+
842914
/* ── LevelCode sign-in modal — calm, soft, THEME-FOLLOWING (matches the Manage-account card) ────
843915
Scoped to .acctcard.lc-modal. Uses the editor theme tokens (--vscode-*, --border, --muted, --field-bg)
844916
so it adapts light/dark exactly like the signed-in card, with a gentle lavender (--accent) as the
@@ -2689,41 +2761,39 @@
26892761
if (acctLastFocus && acctLastFocus.focus) { try { acctLastFocus.focus(); } catch(e){} } // restore focus to the opener
26902762
acctLastFocus = null;
26912763
}
2692-
// Mode-aware privacy note: BYOK = keys never touch the cloud; gateway = metered plan through LevelCode.
2693-
function privacyNote(gateway){
2694-
return gateway
2695-
? '<div class="note priv">Gateway mode: AI runs through your <b>metered LevelCode plan</b> — no provider key needed. Requests are proxied by the account server. Switch to BYOK any time in settings (<code>levelcode.ai.providerMode</code>).</div>'
2696-
: '<div class="note priv">BYOK mode: the account only syncs settings/skills. AI requests stay <b>direct from your machine to your provider</b> — your keys never touch the cloud.</div>';
2697-
}
26982764
function renderAccount(state){
26992765
account = state || { signedIn: false };
27002766
const gateway = account.mode === 'gateway';
27012767
if (account.signedIn){
2702-
acctCard.classList.remove('lc-modal'); // signed-in keeps the plain themed card
2768+
acctCard.classList.remove('lc-modal');
2769+
acctCard.classList.add('classic'); // signed-in → the levelcode.ai "classic" (atom.io) look
27032770
acctAvatar.textContent = initials(account.name || account.email);
27042771
acctAvatar.classList.add('signed');
2705-
// Gateway: show the active plan (and credits when the server reports them). BYOK: plan is display-only.
27062772
const paidPlan = !!(account.plan && account.plan.trim().toLowerCase() !== 'free');
2707-
const planLine = gateway
2708-
? '<p class="meta">Plan: <b>' + esc(account.plan || 'Free') + '</b> · '
2709-
+ esc(paidPlan ? 'Kimi K2.7 Code' : 'gpt-oss-120b')
2710-
+ (account.credits != null ? ' · ' + esc(String(account.credits)) + ' credit' + (Number(account.credits) === 1 ? '' : 's') + ' left' : '')
2711-
+ '</p>'
2712-
: '<p class="meta">' + esc(account.email || '') + (account.plan ? ' · ' + esc(account.plan) : '') + '</p>';
2713-
// Free-tier Upgrade CTA (gateway only): unlock the flagship Kimi K2.7 Code.
2773+
// Model-agnostic: a mono pill, never a model name (it churns and confused clients), and no credits
2774+
// (that lives on /ai/account). "plan · Max" for gateway; "your own key" for BYOK.
2775+
const planPill = gateway
2776+
? '<span class="planpill">plan · ' + esc(account.plan || 'Free') + '</span>'
2777+
: '<span class="planpill">your own key' + (account.plan ? ' · ' + esc(account.plan) : '') + '</span>';
2778+
const emailLine = account.email ? '<p class="em">' + esc(account.email) + '</p>' : '';
27142779
const upgradeBtn = (gateway && !paidPlan)
2715-
? '<button class="abtn primary" data-act="upgrade">' + codicon('sparkle') + ' Upgrade to Pro — unlock Kimi K2.7 Code</button>'
2780+
? '<button class="abtn primary" data-act="upgrade">' + codicon('sparkle') + ' Upgrade to Pro</button>'
27162781
: '';
2782+
// Gateway needs no explainer (the "how the gateway works" blurb was removed). BYOK keeps a one-line
2783+
// privacy reassurance, which is genuinely useful there.
2784+
const note = gateway ? '' : '<div class="note">Your keys never leave your machine — requests go direct to your provider.</div>';
27172785
acctBody.innerHTML =
27182786
'<div class="bigavatar">' + esc(initials(account.name || account.email)) + '</div>'
27192787
+ '<h2 id="lcTitle">' + esc(account.name || 'Signed in') + '</h2>'
2720-
+ planLine
2788+
+ emailLine
2789+
+ planPill
27212790
+ upgradeBtn
27222791
+ '<button class="abtn" data-act="manage">Manage account</button>'
2723-
+ '<button class="abtn" data-act="signout">Sign out</button>'
2724-
+ '<div class="synced">' + codicon('check-circle') + ' Synced: settings · skills · keymaps</div>'
2725-
+ privacyNote(gateway);
2792+
+ '<button class="signout" data-act="signout">Sign out</button>'
2793+
+ '<div class="synced">Synced · settings · skills · keymaps</div>'
2794+
+ note;
27262795
} else {
2796+
acctCard.classList.remove('classic');
27272797
acctCard.classList.add('lc-modal'); // signed-out → the calm LevelCode sign-in modal
27282798
acctAvatar.innerHTML = PERSON_SVG;
27292799
acctAvatar.classList.remove('signed');

0 commit comments

Comments
 (0)