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
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