{
  "sha": "8b4981da4bb6d76a7cc6b49f83c3c9e4de399ad4",
  "message": "修正首頁新聞摘要提早換行 (#130)\n\n讓首頁與主題頁摘要延伸使用來源欄下方空間，並保留手機版順序與無水平溢位。",
  "author": {
    "login": "nnneeeooo-nnneeeooo",
    "name": "",
    "email": ""
  },
  "committer": {
    "login": "web-flow",
    "name": "",
    "email": ""
  },
  "created_at": "2026-08-20T15:49:46Z",
  "files": [
    {
      "filename": "static/site.css",
      "previous_filename": "",
      "status": "modified",
      "additions": 21,
      "deletions": 10,
      "changes": 31
    },
    {
      "filename": "templates/home.html",
      "previous_filename": "",
      "status": "modified",
      "additions": 1,
      "deletions": 1,
      "changes": 2
    },
    {
      "filename": "templates/topic.html",
      "previous_filename": "",
      "status": "modified",
      "additions": 1,
      "deletions": 1,
      "changes": 2
    },
    {
      "filename": "tests/test_mobile_layout.py",
      "previous_filename": "",
      "status": "modified",
      "additions": 17,
      "deletions": 8,
      "changes": 25
    }
  ],
  "diff": "@@ -243,15 +243,19 @@ a.flash-row:hover { color: inherit; }\\n \\n .feed-row {\\n   display: grid; grid-template-columns: 48px 84px minmax(0, 1fr) auto;\\n-  gap: 14px; align-items: start; padding: 13px 0;\\n+  grid-template-areas:\\n+    \"time category copy source\"\\n+    \". . summary summary\";\\n+  column-gap: 14px; row-gap: 0; align-items: start; padding: 13px 0;\\n   border-top: 1px solid var(--color-divider);\\n   color: inherit; text-decoration: none; cursor: pointer;\\n }\\n .feed-row:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); color: inherit; }\\n-.feed-time { font-size: 13px; font-weight: 800; }\\n-.feed-category { justify-content: flex-start; }\\n-.feed-copy { min-width: 0; }\\n+.feed-time { grid-area: time; font-size: 13px; font-weight: 800; }\\n+.feed-category { grid-area: category; justify-content: flex-start; }\\n+.feed-copy { grid-area: copy; min-width: 0; }\\n .feed-source {\\n+  grid-area: source;\\n   padding: 3px 9px; border: 1px solid var(--color-divider);\\n   font-size: 11px; white-space: nowrap;\\n }\\n@@ -282,7 +286,7 @@ a.flash-row:hover { color: inherit; }\\n   overflow-wrap: anywhere; text-overflow: ellipsis;\\n }\\n .summary-preview--hero { margin: 0; }\\n-.summary-preview--feed { margin-top: 7px; }\\n+.summary-preview--feed { grid-area: summary; margin-top: 7px; }\\n .summary-preview--feed .summary-preview__icon { width: 15px; height: 15px; }\\n .summary-preview--feed .summary-preview__text {\\n   font-size: 12.5px; line-height: 1.55; max-height: 3.1em;\\n@@ -595,11 +599,18 @@ a.flash-row:hover { color: inherit; }\\n   .hero-title { font-size: 27px; line-height: 1.2; }\\n   .hero-meta { flex-wrap: wrap; gap: 7px 12px; }\\n   .hero-meta > :last-child { margin-left: 0 !important; }\\n-  .feed-row { grid-template-columns: auto minmax(0, 1fr); gap: 7px 10px; padding: 15px 0; }\\n-  .feed-time { grid-column: 1; }\\n-  .feed-category { grid-column: 2; justify-self: start; }\\n-  .feed-copy { grid-column: 1 / -1; }\\n-  .feed-source { grid-column: 1 / -1; justify-self: start; white-space: normal; }\\n+  .feed-row {\\n+    grid-template-columns: auto minmax(0, 1fr);\\n+    grid-template-areas:\\n+      \"time category\"\\n+      \"copy copy\"\\n+      \"summary summary\"\\n+      \"source source\";\\n+    gap: 7px 10px; padding: 15px 0;\\n+  }\\n+  .feed-category { justify-self: start; }\\n+  .feed-source { justify-self: start; white-space: normal; }\\n+  .summary-preview--feed { margin-top: 0; }\\n   .article-page { padding: 20px 12px 44px; }\\n   .article-title { font-size: 29px; line-height: 1.22; }\\n   .article-meta { gap: 7px 12px; }\\n@@ -82,8 +82,8 @@ <h6 style=\"margin:0;\">{{ t.latest }}</h6>\\n         <span class=\"feed-category tag {{ item.tag_class }}\">{{ item.cat_label }}{% if item.article_format == \"brief\" %} · {{ t.briefLabel }}{% elif item.article_format == \"roundup\" %} · {{ t.roundupLabel }}{% endif %}</span>\\n         <span class=\"feed-copy\">\\n           <span style=\"display:block;font-size:15.5px;font-weight:700;line-height:1.4;letter-spacing:-.01em;\">{{ item.title }}</span>\\n-          {{ notification_summary(item.display_summary, t.summaryLabel, 'feed') }}\\n         </span>\\n+        {{ notification_summary(item.display_summary, t.summaryLabel, 'feed') }}\\n         <span class=\"feed-source text-muted\">{{ item.source }}</span>\\n       </a>\\n       {% else %}\\n@@ -15,8 +15,8 @@ <h1>{{ topic.title }}</h1>\\n         <span class=\"feed-category tag {{ item.tag_class }}\">{{ item.cat_label }}</span>\\n         <span class=\"feed-copy\">\\n           <span style=\"display:block;font-size:15.5px;font-weight:700;line-height:1.4;letter-spacing:-.01em;\">{{ item.title }}</span>\\n-          {{ notification_summary(item.display_summary, t.summaryLabel, 'feed') }}\\n         </span>\\n+        {{ notification_summary(item.display_summary, t.summaryLabel, 'feed') }}\\n         <span class=\"feed-source text-muted\">{{ item.source }}</span>\\n       </a>\\n       {% else %}\\n@@ -1,6 +1,7 @@\\n \"\"\"Offline regression checks for AVWIRE's shared mobile layout.\"\"\"\\n from __future__ import annotations\\n \\n+import re\\n import sys\\n from pathlib import Path\\n \\n@@ -41,19 +42,25 @@ def main() -> None:\\n     assert 'class=\"feed-copy\"' in home\\n     assert 'class=\"summary-preview summary-preview--hero\"' in home\\n     assert 'class=\"summary-preview summary-preview--feed\"' in home\\n+    assert re.search(\\n+        r'<span class=\"feed-copy\">.*?</span>\\s*</span>\\s*'\\n+        r'<div class=\"summary-preview summary-preview--feed\"',\\n+        home,\\n+        re.DOTALL,\\n+    )\\n     assert 'aria-label=\"重點摘要\"' in home\\n     assert \"<span>重點摘要</span>\" not in home\\n     assert 'class=\"article-page\"' in article\\n     assert 'class=\"summary-preview' not in article\\n     assert 'class=\"article-source-row\"' in article\\n-    summaries = (\\n-        (\"AirAdvisor發布2026機場排名；倫敦希斯洛以16.94分居首\", airadvisor),\\n-        (\"捷星航空2027年2月2日起收取頭頂置物箱費；單程25澳幣起\", jetstar),\\n-        (\"David Cummins就任TSA第八任局長；監管全美逾430座機場安檢\", david),\\n-        (\"阿聯酋航空夏季暫停PRG、GLA A380航班；10月恢復每日服務\", emirates),\\n+    feed_summaries = re.findall(\\n+        r'<div class=\"summary-preview summary-preview--feed\".*?'\\n+        r'<span class=\"summary-preview__text\">(.*?)</span>',\\n+        home,\\n+        re.DOTALL,\\n     )\\n-    for summary, article_html in summaries:\\n-        assert summary in home\\n+    assert feed_summaries and all(summary.strip() for summary in feed_summaries)\\n+    for article_html in (airadvisor, jetstar, david, emirates):\\n         assert 'class=\"summary-preview' not in article_html\\n \\n     zh_long = \"中華航空8月10日宣布調整桃園至福岡航班，受機場作業限制影響，兩班延後三小時，旅客須留意報到時間。\" * 2\\n@@ -83,7 +90,9 @@ def main() -> None:\\n     assert \".js .site-nav.is-open { display: grid; }\" in css\\n     assert \"grid-template-columns: repeat(2, minmax(0, 1fr))\" in css\\n     assert \".center-col { order: 1;\" in css\\n-    assert \".feed-copy { grid-column: 1 / -1; }\" in css\\n+    assert '\". . summary summary\"' in css\\n+    assert '\"summary summary\"' in css\\n+    assert \".summary-preview--feed { grid-area: summary; margin-top: 7px; }\" in css\\n     assert \".summary-preview__text\" in css\\n     assert \"grid-template-columns: auto minmax(0, 1fr);\" in css\\n     assert \"-webkit-line-clamp: 2\" in css"
}
