Article footer credits the drafting model with a vendor mark
Every article page now ends with an AI-model line: a square monogram in
the vendor brand color (NVIDIA / Google / DeepSeek / Qwen / Z.ai /
Mistral AI / Anthropic, matching the site square-tag style) plus the
short model name, e.g. Nemotron 3 Ultra, derived from the article's
writer field. Articles without a writer field simply omit the line.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full diff
@@ -65,6 +65,7 @@\n "delays": "今日延誤熱點機場", "autoCompiled": "自動彙整",\n "refreshNote": "本文隨來源每小時更新", "sourcesBox": "資料來源 Sources",\n "genNote": "本文由自動化系統彙整生成,內容以原始來源為準。",\n+ "aiModel": "AI 撰稿模型",\n "incKicker": "Incident Database", "incTitle": "事故資料庫",\n "incSub": "自動彙整全球民航事故與事件,資料來自各國調查機關與專業追報來源,每小時更新。",\n "incCountLabel": "筆紀錄",\n@@ -116,6 +117,7 @@\n "delays": "Delay hotspots today", "autoCompiled": "Auto-compiled",\n "refreshNote": "Refreshes hourly with sources", "sourcesBox": "Sources",\n "genNote": "This article was compiled automatically; the original sources prevail.",\n+ "aiModel": "Drafted by",\n "incKicker": "Incident Database", "incTitle": "Incident database",\n "incSub": "Automatically aggregated civil-aviation accidents and incidents, from state investigators and specialist trackers. Updated hourly.",\n "incCountLabel": "records",\n@@ -258,6 +260,41 @@ def page_url(lang: str, sub: str) -> str:\n \n # ── data loading / preparation ───────────────────────────────────────────────\n \n+# Article-footer model credit: writer id -> vendor mark + short model name.\n+# The square monogram uses the vendor's brand color (site style is square\n+# tags, radius 0) - official logo artwork can replace it later if licensed.\n+_WRITER_BADGES = (\n+ ("nemotron-3-ultra", "NVIDIA", "N", "#76b900", "Nemotron 3 Ultra"),\n+ ("nemotron-3-super", "NVIDIA", "N", "#76b900", "Nemotron 3 Super"),\n+ ("nemotron", "NVIDIA", "N", "#76b900", "Nemotron"),\n+ ("deepseek-v4-pro", "DeepSeek", "D", "#4d6bfe", "DeepSeek V4 Pro"),\n+ ("deepseek", "DeepSeek", "D", "#4d6bfe", "DeepSeek"),\n+ ("gemini-3.6-flash", "Google", "G", "#4285f4", "Gemini 3.6 Flash"),\n+ ("gemini-3.5-flash", "Google", "G", "#4285f4", "Gemini 3.5 Flash"),\n+ ("gemini", "Google", "G", "#4285f4", "Gemini"),\n+ ("qwen3.5", "Qwen", "Q", "#615ced", "Qwen 3.5"),\n+ ("qwen", "Qwen", "Q", "#615ced", "Qwen"),\n+ ("glm-5.2", "Z.ai", "Z", "#141414", "GLM-5.2"),\n+ ("glm", "Z.ai", "Z", "#141414", "GLM"),\n+ ("mistral-medium-3.5", "Mistral AI", "M", "#fa500f", "Mistral Medium 3.5"),\n+ ("mistral", "Mistral AI", "M", "#fa500f", "Mistral"),\n+ ("claude-opus-5", "Anthropic", "A", "#d97757", "Claude Opus 5"),\n+ ("claude", "Anthropic", "A", "#d97757", "Claude"),\n+)\n+\n+\n+def writer_badge(writer):\n+ """{"vendor", "initial", "color", "model"} for a writer id, or None."""\n+ if not isinstance(writer, str) or ":" not in writer:\n+ return None\n+ model_id = writer.split(":", 1)[1].lower()\n+ for needle, vendor, initial, color, model in _WRITER_BADGES:\n+ if needle in model_id:\n+ return {"vendor": vendor, "initial": initial,\n+ "color": color, "model": model}\n+ return None\n+\n+\n def prep_article(raw):\n if not isinstance(raw, dict):\n return None\n@@ -343,6 +380,7 @@ def side(own, other):\n "zh": side(zh, en),\n "en": side(en, zh),\n "sources": sources,\n+ "writer_badge": writer_badge(raw.get("writer")),\n }\n \n \n@@ -376,6 +414,7 @@ def art_view(a, lang: str):\n cat_label=_bi(CATS.get(a["cat"]), lang, a["cat"]),\n source=a["source"], time=a["time"], meta_ts=a["meta_ts"],\n sources=a["sources"], url=page_url(lang, f"news/{a['id']}/"),\n+ writer_badge=a["writer_badge"],\n external=False,\n )\n return v\n@@ -112,6 +112,14 @@ a.flash-row:hover { color: inherit; }\n \n .is-hidden { display: none !important; }\n \n+/* — article footer: AI model credit (vendor-colored square monogram) — */\n+.model-mark {\n+ width: 16px; height: 16px; flex-shrink: 0;\n+ display: inline-flex; align-items: center; justify-content: center;\n+ color: #fff; font-weight: 800; font-size: 10px; line-height: 1;\n+ border: 1px solid var(--color-divider);\n+}\n+\n /* — footer — */\n .footer-grid {\n padding: 24px 20px 32px;\n@@ -23,7 +23,16 @@ <h6 style="margin:0 0 10px;">{{ t.sourcesBox }}</h6>\n <span class="text-muted" style="font-size:11.5px;">{{ s.host }}</span>\n </div>\n {% endfor %}\n- <div class="text-muted" style="font-size:12px;margin-top:10px;border-top:1px solid var(--color-divider);padding-top:10px;">{{ t.genNote }}</div>\n+ <div class="text-muted" style="font-size:12px;margin-top:10px;border-top:1px solid var(--color-divider);padding-top:10px;display:flex;align-items:center;gap:14px;flex-wrap:wrap;">\n+ <span>{{ t.genNote }}</span>\n+ {% if a.writer_badge %}\n+ <span style="margin-left:auto;display:inline-flex;align-items:center;gap:7px;white-space:nowrap;">\n+ <span>{{ t.aiModel }}</span>\n+ <span class="model-mark" style="background:{{ a.writer_badge.color }};" title="{{ a.writer_badge.vendor }}">{{ a.writer_badge.initial }}</span>\n+ <span style="font-weight:700;color:var(--color-text);">{{ a.writer_badge.model }}</span>\n+ </span>\n+ {% endif %}\n+ </div>\n </div>\n </div>\n {% endblock %}