@@ -72,14 +72,18 @@ export function renderApiKeysPage(data: ApiKeysPageData): string {
7272
7373 <!-- Newly-created secret banner (populated by JS, shown once) -->
7474 <div id="new-key-banner" class="hidden mb-6 rounded-xl bg-emerald-50 dark:bg-emerald-900/20 p-4 ring-1 ring-emerald-200 dark:ring-emerald-800">
75- <p class="text-sm font-medium text-emerald-800 dark:text-emerald-300 mb-2">
76- Copy your new key now — it will not be shown again.
77- </p>
75+ <div class="flex items-start justify-between mb-2">
76+ <p class="text-sm font-medium text-emerald-800 dark:text-emerald-300">
77+ Copy your new key now — it will not be shown again.
78+ </p>
79+ <button type="button" onclick="document.getElementById('new-key-banner').classList.add('hidden')"
80+ class="ml-4 text-emerald-600 dark:text-emerald-400 hover:text-emerald-800 dark:hover:text-emerald-200 text-lg leading-none">×</button>
81+ </div>
7882 <div class="flex items-center gap-2">
7983 <code id="new-key-value" class="flex-1 rounded-lg bg-white dark:bg-zinc-900 px-3 py-2 text-sm font-mono text-zinc-950 dark:text-white ring-1 ring-zinc-950/10 dark:ring-white/10 break-all"></code>
8084 <button type="button" onclick="copyNewKey()"
8185 class="rounded-lg bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-500 transition-colors">
82- Copy
86+ Copy & dismiss
8387 </button>
8488 </div>
8589 </div>
@@ -151,11 +155,50 @@ export function renderApiKeysPage(data: ApiKeysPageData): string {
151155 document.getElementById('create-key-modal').classList.add('hidden')
152156 document.getElementById('new-key-value').textContent = apiKey.key
153157 document.getElementById('new-key-banner').classList.remove('hidden')
154- setTimeout(() => location.reload(), 1500)
158+ // Add new key row to table immediately so user can see it without reloading
159+ const tbody = document.getElementById('keys-tbody')
160+ const emptyRow = tbody.querySelector('td[colspan]')
161+ if (emptyRow) emptyRow.closest('tr').remove()
162+ const tr = document.createElement('tr')
163+ tr.className = 'border-t border-zinc-950/5 dark:border-white/10'
164+ tr.setAttribute('data-key-id', apiKey.id)
165+ const prefix = apiKey.prefix
166+ const today = new Date().toISOString().slice(0, 10)
167+ const expires = apiKey.expiresAt ? new Date(apiKey.expiresAt).toISOString().slice(0, 10) : 'Never'
168+ // Build row using DOM methods to avoid nested template-literal escaping issues
169+ const nameTd = document.createElement('td')
170+ nameTd.className = 'py-3 pr-4 text-sm font-medium text-zinc-950 dark:text-white'
171+ nameTd.textContent = apiKey.name
172+ const prefixTd = document.createElement('td')
173+ prefixTd.className = 'py-3 pr-4 text-sm font-mono text-zinc-500 dark:text-zinc-400'
174+ prefixTd.textContent = prefix + '…'
175+ const createdTd = document.createElement('td')
176+ createdTd.className = 'py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400'
177+ createdTd.textContent = today
178+ const lastUsedTd = document.createElement('td')
179+ lastUsedTd.className = 'py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400'
180+ lastUsedTd.textContent = 'Never'
181+ const expiresTd = document.createElement('td')
182+ expiresTd.className = 'py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400'
183+ expiresTd.textContent = expires
184+ const actionsTd = document.createElement('td')
185+ actionsTd.className = 'py-3 text-right'
186+ const revokeBtn = document.createElement('button')
187+ revokeBtn.type = 'button'
188+ revokeBtn.className = 'rounded-lg bg-red-50 dark:bg-red-900/20 px-3 py-1.5 text-sm font-medium text-red-700 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/40 ring-1 ring-red-200 dark:ring-red-800 transition-colors'
189+ revokeBtn.textContent = 'Revoke'
190+ revokeBtn.addEventListener('click', () => revokeApiKey(apiKey.id, apiKey.name))
191+ actionsTd.appendChild(revokeBtn)
192+ tr.appendChild(nameTd); tr.appendChild(prefixTd); tr.appendChild(createdTd)
193+ tr.appendChild(lastUsedTd); tr.appendChild(expiresTd); tr.appendChild(actionsTd)
194+ tbody.appendChild(tr)
155195 }
156196 function copyNewKey() {
157197 const v = document.getElementById('new-key-value').textContent
158- navigator.clipboard.writeText(v).then(() => window.showNotification && window.showNotification('Copied', 'success'))
198+ navigator.clipboard.writeText(v).then(() => {
199+ window.showNotification && window.showNotification('Copied to clipboard', 'success')
200+ document.getElementById('new-key-banner').classList.add('hidden')
201+ })
159202 }
160203 async function revokeApiKey(id, name) {
161204 if (!confirm('Revoke "' + name + '"? This cannot be undone and will break any client using it.')) return
0 commit comments