llm-proxy(ui): 修 placeholder token 泄漏 + UI 重做 + λ favicon
deploy llm-proxy / build-and-deploy (push) Successful in 1m46s

- 修:token 输入框 placeholder 之前硬编码了真实 token (`e.g.
  famzheng-llm-2026`),等于明文泄露。改成 `your auth token`
- UI 重做 — 100dvh 锁 viewport(处理移动软键盘)+ grid 布局
  让 thread 永远占中间、footer 永远贴底
- textarea autogrow(最高 200px,超出内部滚)
- 复制按钮 / smooth scroll-to-bottom (double rAF) /
  iOS momentum scroll / safe-area padding
- 错误状态独立 row,monospace + 红底
- λ favicon(紫蓝渐变 + 绿色在线点 + glow)— SVG include_str!
  进 binary,`/favicon.svg` + `/favicon.ico` 同源响应
This commit is contained in:
Fam Zheng
2026-05-18 00:34:49 +01:00
parent a5e97adf85
commit a8e5100380
3 changed files with 32 additions and 0 deletions
+13
View File
@@ -37,6 +37,8 @@ async fn main() -> std::io::Result<()> {
.route("/healthz", get(|| async { "ok" }))
.route("/", get(|| async { Redirect::permanent("/chat") }))
.route("/chat", get(chat_ui))
.route("/favicon.svg", get(favicon))
.route("/favicon.ico", get(favicon)) // 浏览器默认会请求 .ico,让它共享同一 SVG
.merge(chat_api)
.layer(TraceLayer::new_for_http());
@@ -47,11 +49,22 @@ async fn main() -> std::io::Result<()> {
}
const CHAT_HTML: &str = include_str!("../web/chat.html");
const FAVICON_SVG: &str = include_str!("../web/favicon.svg");
async fn chat_ui() -> Html<&'static str> {
Html(CHAT_HTML)
}
async fn favicon() -> impl IntoResponse {
(
[
(axum::http::header::CONTENT_TYPE, "image/svg+xml"),
(axum::http::header::CACHE_CONTROL, "public, max-age=604800"),
],
FAVICON_SVG,
)
}
/// 验 `Authorization: token <PROXY_AUTH_TOKEN>`,错的直接 401。
async fn require_token(
State(cfg): State<Arc<proxy::Config>>,
+1
View File
@@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#0f1419" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>llm.famzheng.me</title>
<style>
:root {
+18
View File
@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c3aed"/>
<stop offset="100%" stop-color="#06b6d4"/>
</linearGradient>
<filter id="glow">
<feGaussianBlur stdDeviation="1.2"/>
</filter>
</defs>
<rect x="2" y="2" width="60" height="60" rx="14" fill="url(#bg)"/>
<text x="32" y="46" text-anchor="middle"
font-family="ui-serif, Georgia, 'Times New Roman', serif"
font-size="42" font-weight="700" fill="white"
style="font-style: italic;">λ</text>
<circle cx="49" cy="49" r="6.5" fill="#4ade80" filter="url(#glow)" opacity="0.5"/>
<circle cx="49" cy="49" r="4.5" fill="#4ade80"/>
</svg>

After

Width:  |  Height:  |  Size: 756 B