{
  "sha": "6661e3dd0ccc9af247c1d9f27f795ab260437d87",
  "message": "Article footer credits the drafting model with a vendor mark\n\nEvery article page now ends with an AI-model line: a square monogram in\nthe vendor brand color (NVIDIA / Google / DeepSeek / Qwen / Z.ai /\nMistral AI / Anthropic, matching the site square-tag style) plus the\nshort model name, e.g. Nemotron 3 Ultra, derived from the article's\nwriter field. Articles without a writer field simply omit the line.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
  "author": {
    "login": "avwire",
    "name": "avwire",
    "email": ""
  },
  "committer": {
    "login": "avwire",
    "name": "avwire",
    "email": ""
  },
  "created_at": "2026-07-26T20:02:20Z",
  "files": [
    {
      "filename": "pipeline/build.py",
      "previous_filename": "",
      "status": "modified",
      "additions": 39,
      "deletions": 0,
      "changes": 39
    },
    {
      "filename": "static/site.css",
      "previous_filename": "",
      "status": "modified",
      "additions": 8,
      "deletions": 0,
      "changes": 8
    },
    {
      "filename": "templates/article.html",
      "previous_filename": "",
      "status": "modified",
      "additions": 10,
      "deletions": 1,
      "changes": 11
    }
  ],
  "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 %}"
}
