2026-10-27 TUE UTC 5:36 AM TPE 1:36 PM Auto-updates hourly 中文 EN
LIVE WIRE
← Back to the site changelog Site Changelog

Original commit record

fix: clarify flash dates, hide duplicate ticker links and correct civil news categories (#382)

SHA
399f7f209f89ca355b77b4fe872cd46c52ac1ff7
Commit date
Author
CHI HSUN YANG · 125815713+nnneeeooo-nnneeeooo@users.noreply.github.com
Committer
GitHub · noreply@github.com

Changed files (6)

  • data/articles/2026-10-21T16.json modified · +1 / −1
  • data/articles/2026-10-22T10.json modified · +1 / −1
  • pipeline/CONTRACTS.md modified · +5 / −0
  • pipeline/build.py modified · +1 / −1
  • templates/base.html modified · +2 / −2
  • tests/test_news_chronology.py modified · +44 / −1

Full diff

diff --git a/data/articles/2026-10-21T16.json b/data/articles/2026-10-21T16.json\nindex 149d5728e..b2ec7eceb 100644\n--- a/data/articles/2026-10-21T16.json\n+++ b/data/articles/2026-10-21T16.json\n@@ -730,7 +730,7 @@\n     {\n       "id": "a-20261021-1637-japan-airlines-cancels-flights-at-nine-a",\n       "publishedUtc": "2026-10-21T16:37Z",\n-      "cat": "mil",\n+      "cat": "ops",\n       "primarySource": "Simple Flying",\n       "image": null,\n       "sourceImageCandidates": [\ndiff --git a/data/articles/2026-10-22T10.json b/data/articles/2026-10-22T10.json\nindex 5574c9844..84f4c48d5 100644\n--- a/data/articles/2026-10-22T10.json\n+++ b/data/articles/2026-10-22T10.json\n@@ -3,7 +3,7 @@\n     {\n       "id": "a-20261022-1054-taiwan-caa-says-all-domestic-flights-wil",\n       "publishedUtc": "2026-10-22T10:54Z",\n-      "cat": "mil",\n+      "cat": "ops",\n       "primarySource": "CAA Taiwan",\n       "image": null,\n       "sourceImageCandidates": [],\ndiff --git a/pipeline/CONTRACTS.md b/pipeline/CONTRACTS.md\nindex bd740cdc6..bf4b08de2 100644\n--- a/pipeline/CONTRACTS.md\n+++ b/pipeline/CONTRACTS.md\n@@ -241,6 +241,11 @@ Rare-aircraft monitor state, committed only when changed:\n \n Most recent first, max 10 kept:\n \n+Public flash and ticker labels use Taipei time for today's entries and\n+`MM/DD` for older entries, matching the news feed's date policy. The second\n+marquee pass is a visual duplicate only: it is hidden from assistive\n+technology and its links are excluded from keyboard navigation.\n+\n ```jsonc\n [ {"timeUtc": "2026-07-26T05:47Z", "hot": true, "pinned": true,\n    "zh": "...", "en": "...",\ndiff --git a/pipeline/build.py b/pipeline/build.py\nindex d51243670..c057bffa3 100644\n--- a/pipeline/build.py\n+++ b/pipeline/build.py\n@@ -2256,7 +2256,7 @@ def prep_flashes(raw, known_ids):\n             # A hidden/archived article must not leave an orphan ticker item.\n             continue\n         out.append({\n-            "time": clock_12(dt.astimezone(TPE)),\n+            "time": _honest_time_label(dt, False),\n             "hot": bool(f.get("hot")),\n             "pinned": bool(f.get("pinned")),\n             "zh": zh, "en": en,\ndiff --git a/templates/base.html b/templates/base.html\nindex f5021babc..b0a562bd4 100644\n--- a/templates/base.html\n+++ b/templates/base.html\n@@ -158,10 +158,10 @@ gtag('config', '{{ ga4_measurement_id }}', {\n     <div class="ticker-window">\n       <div class="marquee-track">\n         {% for pass in range(2) %}\n-        <span class="ticker-pass">\n+        <span class="ticker-pass"{% if pass == 1 %} aria-hidden="true"{% endif %}>\n           {%- for it in ticker_items -%}\n           {%- if it.url -%}\n-          <a class="ticker-item" href="{{ it.url }}"{% if it.external %} target="_blank" rel="noopener"{% endif %}><b class="ticker-time{% if it.hot %} hot{% endif %}">{{ it.time }}</b>{% if it.priority|default(false) %}<span class="ticker-pin">{{ t.priorityLabel }}</span>{% endif %}&#8195;{{ it.text }}<span class="ticker-go">↗</span></a>\n+          <a class="ticker-item" href="{{ it.url }}"{% if pass == 1 %} tabindex="-1"{% endif %}{% if it.external %} target="_blank" rel="noopener"{% endif %}><b class="ticker-time{% if it.hot %} hot{% endif %}">{{ it.time }}</b>{% if it.priority|default(false) %}<span class="ticker-pin">{{ t.priorityLabel }}</span>{% endif %}&#8195;{{ it.text }}<span class="ticker-go">↗</span></a>\n           {%- else -%}\n           <span class="ticker-item"><b class="ticker-time{% if it.hot %} hot{% endif %}">{{ it.time }}</b>{% if it.priority|default(false) %}<span class="ticker-pin">{{ t.priorityLabel }}</span>{% endif %}&#8195;{{ it.text }}</span>\n           {%- endif -%}\ndiff --git a/tests/test_news_chronology.py b/tests/test_news_chronology.py\nindex 7159cf662..9d35308da 100644\n--- a/tests/test_news_chronology.py\n+++ b/tests/test_news_chronology.py\n@@ -2,9 +2,13 @@\n import json\n import sys\n import tempfile\n+from datetime import datetime, timezone\n from pathlib import Path\n from unittest.mock import patch\n \n+from bs4 import BeautifulSoup\n+from jinja2 import Environment, FileSystemLoader, StrictUndefined\n+\n sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "pipeline"))\n import build\n \n@@ -52,6 +56,45 @@ def test_news_chronology():\n     assert build.prep_article(revised)["news_dt"] == collected[1]["news_dt"]\n \n \n+def test_flash_dates_and_marquee_accessibility():\n+    # Taipei has crossed midnight while UTC is still on the previous day.\n+    now = datetime(2026, 10, 24, 16, 30, tzinfo=timezone.utc)\n+    rows = [\n+        {"timeUtc": "2026-10-24T16:15Z", "articleId": "today",\n+         "zh": "今日快訊", "en": "Today's flash", "hot": True},\n+        {"timeUtc": "2026-10-24T15:59Z", "articleId": "yesterday",\n+         "zh": "昨日快訊", "en": "Yesterday's flash"},\n+        {"timeUtc": "2026-10-22T10:54Z", "articleId": "older",\n+         "zh": "舊快訊", "en": "Older flash"},\n+    ]\n+    with patch.object(build, "now_utc", return_value=now):\n+        flashes = build.prep_flashes(rows, {"today", "yesterday", "older"})\n+    assert [f["time"] for f in flashes] == ["12:15 AM", "10/24", "10/22"]\n+    env = Environment(loader=FileSystemLoader(build.TEMPLATES_DIR),\n+                      undefined=StrictUndefined, autoescape=True)\n+    for lang in ("zh", "en"):\n+        ticker = build.flash_view(flashes, lang, {"today"})\n+        assert ticker[0]["hot"] and ticker[0]["priority"]\n+        assert ticker[1]["url"] == build.page_url(lang, "news/yesterday/")\n+        context = build.base_ctx(\n+            lang, "home", "", title="Test", description="Test", ticker=ticker,\n+            build={"date": "2026-10-25", "utc_hm": "4:30 PM",\n+                   "tpe_hm": "12:30 AM", "stamp": "2026-10-24 4:30 PM"})\n+        soup = BeautifulSoup(env.get_template("base.html").render(**context),\n+                             "html.parser")\n+        original, duplicate = soup.select(".ticker-pass")\n+        assert not original.has_attr("aria-hidden")\n+        assert duplicate["aria-hidden"] == "true"\n+        assert len(original.select("a")) == len(duplicate.select("a")) == 3\n+        assert all(a.get("tabindex") is None for a in original.select("a"))\n+        assert all(a.get("tabindex") == "-1" for a in duplicate.select("a"))\n+        assert [a["href"] for a in original.select("a")] == [\n+            a["href"] for a in duplicate.select("a")]\n+        assert [t.text for t in original.select(".ticker-time")] == [\n+            "12:15 AM", "10/24", "10/22"]\n+\n+\n if __name__ == "__main__":\n     test_news_chronology()\n-    print("PASS news chronology, source-time fallback and search timestamps")\n+    test_flash_dates_and_marquee_accessibility()\n+    print("PASS news chronology, flash dates and marquee accessibility")

You found a secret theme!

8-bit mode is unlocked. Refresh to apply it now. You can switch themes anytime using the theme selector.