- Two separate cards (register + login) -> single unified card
- handleRegister + handleLogin -> single handleAuth that tries login first,
falls back to register if login fails (new user detection)
- Single email/password input, single button, single status display
- Enter key submits on both fields
- File size 57873 -> 51329 chars (-11%)
Test: test_tksea_portal_assets.sh PASS, verify_frontend_smoke.sh PASS,
verify_quality_gates.sh PASS (gofmt+vet+cov+integration)
The reorder_hints.py script incorrectly removed several <input> elements
when moving .hint spans before inputs. This commit restores the file to
the correct state from commit 56474264, preserving all inputs while
keeping the hints in their proper places.
Fixes: admin-username input missing, several other inputs corrupted
Two-part UX upgrade:
1. CSS — upgrade .hint from a generic "card with slate background" to a
proper info-banner:
- background: var(--color-primary-soft) (translucent teal 12%) instead
of var(--bg-elev-2) (slate card) — visually distinct from any
<input> card, so it can never be confused with one
- border-left: 2px solid var(--color-primary) — clear "this is a hint"
teal accent
- padding: 8px 12px 8px 14px (smaller, lighter)
- font-size: 12.5px (slightly larger for readability)
- margin: 4px 0 8px 0 (sits between field label and input)
- .hint code { monospace, teal-300, teal-tinted background } for inline
<code> tokens
- .hint strong { text-default color } for emphasis
Also: label > input/select/textarea forced to display:block width:100%
margin-top:6px (after the hint, hint + input collapse to margin-top:0)
2. HTML — reorder 11 labels across providers.html (7) and
admin-batch-import.html (4) so the .hint span sits BEFORE the
<input>/<select>/<textarea> it describes. Datalist stays adjacent to its
owning input.
Pattern before: <label>Field
<input>
<span class="hint">desc</span>
</label>
Pattern after: <label>Field
<span class="hint">desc</span>
<input>
</label>
Why: Linear/Vercel canonical form pattern is label + info banner above +
clean input below. The previous "input then hint" layout was just an
artifact of how the inline-script-dedup pass emitted the fields, not a
deliberate UX choice.
Verification (chrome remote-debugging, 7 pages, all .hint elements):
Page n_hints covered
https://sub.tksea.top/portal/ 2 0
https://sub.tksea.top/portal/admin/ 0 0
https://sub.tksea.top/portal/admin/providers.html 8 0
https://sub.tksea.top/portal/admin/accounts.html 0 0
https://sub.tksea.top/portal/admin/logical-groups 1 0
https://sub.tksea.top/portal/admin/route-health 0 0
https://sub.tksea.top/portal/admin-batch-import 4 0
Total: 7/7 pages, 0 hint covered by any input
Local tests still PASS:
- test_tksea_portal_assets.sh
- verify_frontend_smoke.sh
5 .hint spans (ADMIN TOKEN / PACK PATH / PROVIDER ID / SMOKE TEST MODEL /
COMMIT MESSAGE) were being visually overlapped by their sibling <input>
boxes because:
- .hint was inline, padding 10px 12px with background+border
- <input> was inline-block
- label was display:block, font-size 12px
- the inline .hint box was being squeezed into the same flow line as the
input, so its last 8-10px were covered
Verified via getBoundingClientRect + elementFromPoint probe before fix:
hint.top < input.bottom (5 fields) = hint overlapped by input
Fix: force .hint to display:block with margin-top:6px so it always sits
below its input as a separate block; also force label > input/select/textarea
to display:block width:100% so the form field always stretches and never
inlines with the field label text.
Verified after fix: 0 hints overlapped on providers.html (the worst case
with 8 .hint spans).
CSS-only change; no HTML edits. Affects all 8 portal pages. Smoke +
frontend assets tests still PASS.
All three admin pages had two parallel inline <script> blocks (a modern S1
that used adminRuntime + a legacy S2 that was self-contained). Both had
a nested <script> text inside S1 that the browser tolerated only because
the second script re-ran any state-affecting calls. Merge into a single
inline script per page; fix the nested <script> comment.
- providers.html: 100371 -> 62761 chars (-37610, -37%)
- accounts.html: 54878 -> 33098 chars (-21780, -40%)
- batch-import: 43861 -> 26570 chars (-17291, -39%)
Also rename draftProviderIDInput -> providerIDInput in providers.html
(the old draft-provider-id input was removed during the earlier workflow
merge, leaving the script with a null addEventListener on draft id).
All scripts pass node --check. Both test_tksea_portal_assets.sh and
verify_frontend_smoke.sh PASS.
Problem: provider manifest form had all-empty fields with cryptic
placeholders, users had to know what model IDs to type.
Fix on /portal/admin/providers.html (Provider Manifest 草稿):
- DISPLAY NAME: datalist of common vendors (OpenAI / DeepSeek /
硅基流动 / Moonshot / 智谱 / Anthropic / 零一万物 / MiniMax / Qwen / Baichuan / 混元)
- PLATFORM: datalist of common platforms (openai / openai-compatible /
deepseek / anthropic / gemini / zhipu / moonshot / minimax / qwen / ...)
- SMOKE TEST MODEL: datalist of common smoke models + auto-fills with
first model from MODELS field if user leaves it empty
- BASE URL PLACEHOLDER: datalist of common base URLs (12 presets)
- MODELS: chip-row of 11 common models (gpt-5.4, gpt-5.4-mini,
deepseek-chat, MiniMax-M2.7-highspeed, kimi-k2.6, glm-4.6,
claude-sonnet-4-5, gemini-2.5-pro, qwen3-coder-plus, gpt-4o, o4-mini)
+ clear button. Click chip → append to MODELS field (dedup).
- KEYS textarea: 6 rows + example placeholder (sk-example-1/2/3)
Fix on /portal/admin-batch-import.html (发起导入):
- HOST ID: datalist of common host_ids + hint about loading pack first
- ENTRIES textarea: 6 rows + multi-line hint explaining
base_url|api_key|model1,model2 format, optional model, batch import
JS change: syncDraftHelperState() in providers.html now auto-fills
smoke_test_model with first model if user hasn't filled it yet.
Also fixed: 2 duplicate copies of syncDraftHelperState (from
earlier batch script restoration) — both now have the new logic.
Verification:
- bash scripts/test/test_tksea_portal_assets.sh → PASS
- bash scripts/test/verify_frontend_smoke.sh → PASS
- browser_console click test: gpt-5.4 + deepseek-chat + kimi-k2.6 chips
→ models='gpt-5.4,deepseek-chat,kimi-k2.6' + smoke='gpt-5.4' auto-fill ✓
- screenshot: /tmp/portal-screenshots/admin-providers-v5.png