From 005a25b77afef18db7c91e597750e8c1893eecbd Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 6 Apr 2026 18:07:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 23 +++++ app/static/app.js | 55 ----------- app/static/style.css | 191 ++++++++++++++++++--------------------- app/templates/index.html | 41 ++++----- 4 files changed, 125 insertions(+), 185 deletions(-) diff --git a/Dockerfile b/Dockerfile index acf247a..d8b2a9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,5 +18,28 @@ RUN --mount=type=cache,target=/root/.cache/pip \ COPY . . +# 可选:构建时自动拉取远端仓库最新代码覆盖工作目录(默认关闭)。 +# 放在 COPY 之后,避免影响依赖层缓存;仅在你显式开启时才会触发网络更新。 +# 用法示例: +# docker compose build \ +# --build-arg GIT_AUTO_UPDATE=1 \ +# --build-arg GIT_REMOTE_URL=https://github.com/you/repo.git \ +# --build-arg GIT_REF=main +ARG GIT_AUTO_UPDATE=0 +ARG GIT_REMOTE_URL= +ARG GIT_REF=main +RUN if [ "$GIT_AUTO_UPDATE" = "1" ] && [ -n "$GIT_REMOTE_URL" ]; then \ + set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends git ca-certificates; \ + rm -rf /var/lib/apt/lists/*; \ + tmpdir="$(mktemp -d)"; \ + git clone --depth=1 --branch "$GIT_REF" "$GIT_REMOTE_URL" "$tmpdir/repo"; \ + cp -a "$tmpdir/repo/." /app/; \ + rm -rf "$tmpdir"; \ + else \ + echo "Skip remote code update (GIT_AUTO_UPDATE=0 or GIT_REMOTE_URL empty)"; \ + fi + EXPOSE 8000 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/app/static/app.js b/app/static/app.js index 234a804..00bae9b 100644 --- a/app/static/app.js +++ b/app/static/app.js @@ -51,32 +51,6 @@ async function postJSON(url, body) { return data; } -function renderTrace(trace, headerRid) { - const wrap = $("traceWrap"); - const pre = $("traceJson"); - const badge = $("traceBadge"); - if (!pre || !wrap) return; - - if (!trace || Object.keys(trace).length === 0) { - pre.textContent = headerRid - ? JSON.stringify({ request_id: headerRid, note: "响应中无 trace 字段" }, null, 2) - : "(尚无数据)完成一次「AI 改写」后,这里会显示请求 ID、耗时、质检与降级原因。"; - if (badge) badge.textContent = ""; - return; - } - - const merged = { ...trace }; - if (headerRid && !merged.request_id) merged.request_id = headerRid; - pre.textContent = JSON.stringify(merged, null, 2); - - const mode = merged.mode || ""; - if (badge) { - badge.textContent = mode === "ai" ? "AI" : mode === "fallback" ? "保底" : ""; - badge.className = "trace-badge " + (mode === "ai" ? "is-ai" : mode === "fallback" ? "is-fallback" : ""); - } - wrap.open = true; -} - $("rewriteBtn").addEventListener("click", async () => { const sourceText = $("sourceText").value.trim(); if (sourceText.length < 20) { @@ -99,7 +73,6 @@ $("rewriteBtn").addEventListener("click", async () => { $("summary").value = data.summary || ""; $("body").value = data.body_markdown || ""; updateCounters(); - renderTrace(data.trace, data._requestId); const tr = data.trace || {}; const modelLine = tr.model ? `模型 ${tr.model}` : ""; if (data.mode === "fallback") { @@ -193,32 +166,4 @@ $("imBtn").addEventListener("click", async () => { $(id).addEventListener("input", updateCounters); }); -async function loadBackendConfig() { - const el = $("backendConfig"); - if (!el) return; - try { - const res = await fetch("/api/config"); - const c = await res.json(); - if (!c.openai_configured) { - el.textContent = - "后端未配置 OPENAI_API_KEY:改写将使用本地保底稿,千问不会参与。请在 .env 中配置并重启容器。"; - el.style.color = "#b42318"; - return; - } - const name = - c.provider === "dashscope" - ? "通义千问(DashScope 兼容接口)" - : "OpenAI 兼容接口"; - const host = c.base_url_host ? ` · ${c.base_url_host}` : ""; - const to = c.openai_timeout_sec != null ? ` · 单轮最长等待 ${c.openai_timeout_sec}s` : ""; - el.textContent = `已接入:${c.openai_model} · ${name}${host}${to}`; - el.style.color = ""; - } catch (e) { - el.textContent = "无法读取 /api/config(请确认服务已启动)"; - el.style.color = "#b42318"; - } -} - -loadBackendConfig(); updateCounters(); -renderTrace(null, ""); diff --git a/app/static/style.css b/app/static/style.css index f6cdf7a..ec5bb8e 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -1,11 +1,12 @@ :root { - --bg: #f3f7f5; + --bg: #ecfeff; --panel: #ffffff; - --line: #d7e3dd; - --text: #1a3128; - --muted: #5e7a6f; - --accent: #18794e; - --accent-2: #0f5f3d; + --line: #d9eef2; + --text: #164e63; + --muted: #457386; + --accent: #0891b2; + --accent-2: #0e7490; + --accent-soft: #f0fdff; } * { @@ -14,17 +15,18 @@ body { margin: 0; - background: radial-gradient(circle at 10% 20%, #e6f4ec, transparent 35%), - radial-gradient(circle at 90% 80%, #dff0ff, transparent 30%), - var(--bg); + background: var(--bg); color: var(--text); - font-family: "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif; + font-family: Inter, "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif; + height: 100vh; + overflow: hidden; } .topbar { - max-width: 1280px; - margin: 20px auto 0; - padding: 0 16px; + max-width: 1240px; + height: 72px; + margin: 0 auto; + padding: 0 20px; display: flex; justify-content: space-between; align-items: center; @@ -32,42 +34,43 @@ body { .brand h1 { margin: 0; + font-size: 32px; + letter-spacing: -0.02em; } .brand .muted { margin: 6px 0 0; } -.backend-config { - margin: 8px 0 0; - line-height: 1.5; -} - .badge { font-size: 12px; font-weight: 700; - color: #0f5f3d; - background: #eaf7f0; - border: 1px solid #cde6d7; + color: #fafafa; + background: var(--accent-2); + border: 1px solid var(--accent-2); padding: 5px 10px; border-radius: 999px; } .layout { - max-width: 1280px; - margin: 14px auto 24px; - padding: 0 16px; + max-width: 1240px; + height: calc(100vh - 72px); + margin: 0 auto; + padding: 0 20px; display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; + grid-template-columns: minmax(320px, 42%) 1fr; + gap: 12px; + overflow: hidden; } .panel { background: var(--panel); border: 1px solid var(--line); - border-radius: 14px; - padding: 18px; - box-shadow: 0 8px 24px rgba(32, 84, 55, 0.07); + border-radius: 12px; + padding: 14px; + box-shadow: 0 6px 24px rgba(8, 145, 178, 0.08); + min-height: 0; + overflow: hidden; } h1, @@ -75,16 +78,25 @@ h2 { margin-top: 0; } +.panel-head { + margin-bottom: 10px; +} + +.panel-head h2 { + margin: 0; + font-size: 18px; +} + .muted { color: var(--muted); - margin-top: -6px; + margin-top: 2px; } label { display: block; - margin-top: 10px; - margin-bottom: 6px; - font-size: 14px; + margin-top: 8px; + margin-bottom: 4px; + font-size: 13px; font-weight: 600; } @@ -105,8 +117,8 @@ button { width: 100%; border-radius: 10px; border: 1px solid var(--line); - padding: 10px 12px; - font-size: 14px; + padding: 8px 10px; + font-size: 13px; transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease; } @@ -118,13 +130,13 @@ textarea { input:focus, textarea:focus { outline: none; - border-color: #8ec6aa; - box-shadow: 0 0 0 3px rgba(24, 121, 78, 0.12); + border-color: #7dd3e7; + box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.15); } button { cursor: pointer; - margin-top: 12px; + margin-top: 8px; font-weight: 700; } @@ -142,6 +154,22 @@ button.primary:hover { background: var(--accent-2); } +button.secondary { + background: #fff; + color: var(--text); + border-color: var(--line); +} + +button.secondary:hover { + background: #f8fdff; +} + +.subtle-btn { + background: #fff; + border-color: #a5dae6; + color: var(--accent-2); +} + button:disabled { cursor: not-allowed; opacity: 0.65; @@ -173,67 +201,29 @@ button:disabled { } .status { - min-height: 22px; - margin-top: 8px; + min-height: 18px; + margin-top: 6px; color: var(--accent-2); - font-weight: 600; + font-weight: 500; + font-size: 12px; } .small { - font-size: 13px; - margin: 0 0 12px; -} - -.flow-hint { - margin: 0 0 14px 18px; - padding: 0; - font-size: 13px; - line-height: 1.6; -} - -.trace-wrap { - margin-top: 12px; - padding: 10px 12px; - border: 1px dashed var(--line); - border-radius: 10px; - background: #f9fbf9; -} - -.trace-wrap summary { - cursor: pointer; - font-weight: 700; - color: var(--text); -} - -.trace-badge { - margin-left: 8px; - font-size: 11px; - padding: 2px 8px; - border-radius: 999px; - font-weight: 700; -} - -.trace-badge.is-ai { - background: #eaf7f0; - color: #0f5f3d; - border: 1px solid #cde6d7; -} - -.trace-badge.is-fallback { - background: #fff4e6; - color: #9a3412; - border: 1px solid #fed7aa; + font-size: 12px; + margin: 0 0 6px; } .body-split { display: grid; - grid-template-columns: 1fr 1fr; - gap: 12px; + grid-template-columns: 1fr 0.9fr; + gap: 10px; align-items: stretch; + min-height: 0; } .body-split textarea { - min-height: 280px; + min-height: 170px; + max-height: 240px; } .preview-panel { @@ -244,10 +234,10 @@ button:disabled { .markdown-preview { flex: 1; - min-height: 280px; - max-height: 480px; + min-height: 170px; + max-height: 240px; overflow: auto; - padding: 12px 14px; + padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: #fafcfb; @@ -258,7 +248,7 @@ button:disabled { .markdown-preview h2 { font-size: 1.15rem; margin: 1em 0 0.5em; - color: var(--accent-2); + color: #111827; } .markdown-preview h3 { @@ -280,23 +270,14 @@ button:disabled { margin: 0.25em 0; } -.trace-json { - margin: 10px 0 0; - padding: 10px; - max-height: 220px; - overflow: auto; - font-size: 11px; - line-height: 1.45; - background: #fff; - border-radius: 8px; - border: 1px solid var(--line); - white-space: pre-wrap; - word-break: break-word; -} - @media (max-width: 960px) { + body { + overflow: auto; + } + .layout { grid-template-columns: 1fr; + height: auto; } .body-split { diff --git a/app/templates/index.html b/app/templates/index.html index 55e37ac..4b66746 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -10,27 +10,22 @@

{{ app_name }}

-

粘贴原文 → 洗成约 5 段、500 字内 的短文(无小标题)→ 右侧预览 → 满意后发布。

-

+

从原文到公众号草稿,一页完成改写、封面和发布。

-
Beta
+
Studio
-

输入与改写策略

-
    -
  1. 粘贴原文并设置语气/读者
  2. -
  3. 点击改写 → 右侧为短标题、摘要与五段正文(段落间空一行)
  4. -
  5. 看「运行追踪」:模式为 AI 且模型名正确,即千问/接口已生效
  6. -
  7. 人工改好后 →「发布到公众号草稿箱」(需配置 WECHAT_*)
  8. -
+
+

内容输入

+
- + 0 字
- +
@@ -62,8 +57,9 @@
-

发布内容

-

下方「运行追踪」会显示本次请求 ID、耗时、质检项与是否降级,便于与容器日志对照。

+
+

发布内容

+
@@ -72,12 +68,12 @@ 0 字 - +
- +

未上传时将使用后端默认封面策略。

@@ -87,24 +83,19 @@ 0 字
- +
- 与公众号 HTML 渲染接近 + 实时同步
-
- 运行追踪 -

-        
-
- - + +