|
839 | 839 | .acctbody .link { color: var(--accent); cursor: pointer; text-decoration: none; } |
840 | 840 | .acctbody .link:hover { text-decoration: underline; } |
841 | 841 |
|
| 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 | + |
842 | 914 | /* ── LevelCode sign-in modal — calm, soft, THEME-FOLLOWING (matches the Manage-account card) ──── |
843 | 915 | Scoped to .acctcard.lc-modal. Uses the editor theme tokens (--vscode-*, --border, --muted, --field-bg) |
844 | 916 | so it adapts light/dark exactly like the signed-in card, with a gentle lavender (--accent) as the |
|
2689 | 2761 | if (acctLastFocus && acctLastFocus.focus) { try { acctLastFocus.focus(); } catch(e){} } // restore focus to the opener |
2690 | 2762 | acctLastFocus = null; |
2691 | 2763 | } |
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 | | - } |
2698 | 2764 | function renderAccount(state){ |
2699 | 2765 | account = state || { signedIn: false }; |
2700 | 2766 | const gateway = account.mode === 'gateway'; |
2701 | 2767 | 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 |
2703 | 2770 | acctAvatar.textContent = initials(account.name || account.email); |
2704 | 2771 | acctAvatar.classList.add('signed'); |
2705 | | - // Gateway: show the active plan (and credits when the server reports them). BYOK: plan is display-only. |
2706 | 2772 | 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>' : ''; |
2714 | 2779 | 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>' |
2716 | 2781 | : ''; |
| 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>'; |
2717 | 2785 | acctBody.innerHTML = |
2718 | 2786 | '<div class="bigavatar">' + esc(initials(account.name || account.email)) + '</div>' |
2719 | 2787 | + '<h2 id="lcTitle">' + esc(account.name || 'Signed in') + '</h2>' |
2720 | | - + planLine |
| 2788 | + + emailLine |
| 2789 | + + planPill |
2721 | 2790 | + upgradeBtn |
2722 | 2791 | + '<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; |
2726 | 2795 | } else { |
| 2796 | + acctCard.classList.remove('classic'); |
2727 | 2797 | acctCard.classList.add('lc-modal'); // signed-out → the calm LevelCode sign-in modal |
2728 | 2798 | acctAvatar.innerHTML = PERSON_SVG; |
2729 | 2799 | acctAvatar.classList.remove('signed'); |
|
0 commit comments