From 756f1f955edfde43ddefc2b36e6928b1b681eedb Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Mon, 11 May 2026 12:47:12 -0500 Subject: [PATCH] Add per-provider key provisioning instructions to API Keys card Each provider row now has a collapsible details panel with accurate step-by-step instructions and a direct link to the key creation page. Includes billing notes for OpenAI and Anthropic (easy to miss gotchas), free tier note for Gemini, and credits note for Grok. --- src/account.el | 100 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 16 deletions(-) diff --git a/src/account.el b/src/account.el index 2f5a963..50f0240 100644 --- a/src/account.el +++ b/src/account.el @@ -494,12 +494,26 @@ fn account_css() -> String { .roadmap-items li { font-family: var(--body); font-size: .875rem; font-weight: 300; color: var(--t2); line-height: 1.6; padding-left: 1rem; position: relative; } .roadmap-items li::before { content: \"-\"; position: absolute; left: 0; color: var(--navy-65); } .signout-section { padding-top: 1rem; display: flex; justify-content: flex-end; } - .api-key-list { display: flex; flex-direction: column; gap: 1rem; } - .api-key-row { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; } - .api-key-provider { display: flex; flex-direction: column; gap: .2rem; min-width: 120px; } + .api-key-list { display: flex; flex-direction: column; gap: 1.5rem; } + .api-key-entry { border-bottom: 1px solid var(--border); padding-bottom: 1.25rem; } + .api-key-entry:last-child { border-bottom: none; padding-bottom: 0; } + .api-key-header { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: .625rem; } .api-key-name { font-size: .875rem; font-weight: 500; color: var(--t1); } .api-key-masked { font-size: .75rem; font-family: monospace; color: var(--t3); } - .api-key-actions { display: flex; gap: .5rem; }" + .api-key-row { display: flex; align-items: center; gap: .75rem; } + .api-key-actions { display: flex; gap: .5rem; flex-shrink: 0; } + .api-key-help { margin-top: .625rem; } + .api-key-help summary { font-size: .75rem; font-weight: 500; letter-spacing: .03em; color: var(--navy-65); cursor: pointer; list-style: none; padding: .25rem 0; user-select: none; } + .api-key-help summary::-webkit-details-marker { display: none; } + .api-key-help summary::before { content: \"\\25B8 \"; font-size: .6rem; } + .api-key-help[open] summary::before { content: \"\\25BE \"; } + .api-key-help-body { padding: .75rem 0 .125rem; } + .api-key-help-body ol { padding-left: 1.25rem; display: flex; flex-direction: column; gap: .375rem; } + .api-key-help-body li { font-size: .8125rem; font-weight: 300; color: var(--t2); line-height: 1.55; } + .api-key-help-body a { color: var(--navy-65); text-decoration: underline; text-underline-offset: 2px; } + .api-key-help-body a:hover { color: var(--navy); } + .api-key-note { font-size: .75rem; font-weight: 300; color: var(--t3); margin-top: .625rem; line-height: 1.55; padding: .5rem .75rem; background: var(--bg2); border-left: 2px solid var(--navy-b); } + .api-key-note a { color: var(--navy-65); text-decoration: underline; text-underline-offset: 2px; }" "" } @@ -810,30 +824,84 @@ fn account_devices_card() -> String { ) } -fn api_key_provider_row(provider_id: String, provider_name: String, placeholder: String) -> String { +fn api_key_provider_row(provider_id: String, provider_name: String, placeholder: String, instructions: String) -> String { el_div( - "class=\"api-key-row\"", + "class=\"api-key-entry\"", el_div( - "class=\"api-key-provider\"", + "class=\"api-key-header\"", el_span("class=\"api-key-name\"", provider_name) + el_span("class=\"api-key-masked\" id=\"apikey-masked-" + provider_id + "\"", "Not configured") ) + - "" + el_div( - "class=\"api-key-actions\"", - el_button("type=\"button\" class=\"btn-primary\" style=\"padding:.5rem 1rem;font-size:.75rem\" onclick=\"saveApiKey('" + provider_id + "')\"", "Save") + - el_button("type=\"button\" class=\"btn-ghost\" style=\"padding:.5rem 1rem;font-size:.75rem;display:none\" id=\"apikey-del-" + provider_id + "\" onclick=\"deleteApiKey('" + provider_id + "')\"", "Remove") - ) + "class=\"api-key-row\"", + "" + + el_div( + "class=\"api-key-actions\"", + el_button("type=\"button\" class=\"btn-primary\" style=\"padding:.5rem 1rem;font-size:.75rem\" onclick=\"saveApiKey('" + provider_id + "')\"", "Save") + + el_button("type=\"button\" class=\"btn-ghost\" style=\"padding:.5rem 1rem;font-size:.75rem;display:none\" id=\"apikey-del-" + provider_id + "\" onclick=\"deleteApiKey('" + provider_id + "')\"", "Remove") + ) + ) + + instructions ) } fn account_api_keys_section() -> String { + let openai_help: String = + "
" + + "How to get an OpenAI key" + + "
    " + + "
  1. Go to platform.openai.com/api-keys ↗
  2. " + + "
  3. Click Create new secret key
  4. " + + "
  5. Give it a name (e.g. “Neuron”), then click Create secret key
  6. " + + "
  7. Copy the key immediately — it is only shown once
  8. " + + "
" + + "

You need billing set up before making API calls. Add a payment method at platform.openai.com ↗

" + + "
" + + let anthropic_help: String = + "
" + + "How to get an Anthropic key" + + "
    " + + "
  1. Go to console.anthropic.com/settings/keys ↗
  2. " + + "
  3. Click Create Key
  4. " + + "
  5. Give it a name (e.g. “Neuron”), then click Create Key
  6. " + + "
  7. Copy the key immediately — it is only shown once
  8. " + + "
" + + "

You need to add credits before making API calls. Go to console.anthropic.com → Plans & Billing ↗ to add credits.

" + + "
" + + let gemini_help: String = + "
" + + "How to get a Gemini key" + + "
    " + + "
  1. Go to aistudio.google.com/apikey ↗
  2. " + + "
  3. Sign in with your Google account if prompted
  4. " + + "
  5. Click Create API key
  6. " + + "
  7. Select an existing Google Cloud project or create a new one
  8. " + + "
  9. Copy the key
  10. " + + "
" + + "

The free tier (Gemini 1.5 Flash) has generous rate limits. Paid usage is billed through your Google Cloud account.

" + + "
" + + let grok_help: String = + "
" + + "How to get a Grok key" + + "
    " + + "
  1. Go to console.x.ai ↗
  2. " + + "
  3. Sign in with your X (Twitter) account
  4. " + + "
  5. In the left sidebar, click API Keys
  6. " + + "
  7. Click Create API Key, give it a name
  8. " + + "
  9. Copy the key immediately — it is only shown once
  10. " + + "
" + + "

xAI offers free monthly credits to new accounts. Usage beyond the free tier is billed per token.

" + + "
" + let providers: String = el_div( "class=\"api-key-list\"", - api_key_provider_row("openai", "OpenAI", "sk-...") + - api_key_provider_row("anthropic", "Anthropic", "sk-ant-...") + - api_key_provider_row("gemini", "Gemini", "AIza...") + - api_key_provider_row("grok", "Grok", "xai-...") + api_key_provider_row("openai", "OpenAI", "sk-...", openai_help) + + api_key_provider_row("anthropic", "Anthropic", "sk-ant-...", anthropic_help) + + api_key_provider_row("gemini", "Gemini", "AIza...", gemini_help) + + api_key_provider_row("grok", "Grok", "xai-...", grok_help) ) el_div( "id=\"api-keys-section\" style=\"display:none\"",