@takagaki/cortex-decisions-viewer 0.12.109 → 0.12.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/render.js +131 -93
  2. package/package.json +1 -1
package/dist/render.js CHANGED
@@ -57,7 +57,6 @@ exports.nextMemoryReflectGoldAtMs = nextMemoryReflectGoldAtMs;
57
57
  exports.memoryReflectDoneAtMs = memoryReflectDoneAtMs;
58
58
  exports.memoryReflectTextOf = memoryReflectTextOf;
59
59
  exports.runLogUrlOf = runLogUrlOf;
60
- exports.lastSyncRunUrlOf = lastSyncRunUrlOf;
61
60
  exports.runSourceWordOf = runSourceWordOf;
62
61
  exports.fleetLastSyncOf = fleetLastSyncOf;
63
62
  exports.stateRefKey = stateRefKey;
@@ -2722,27 +2721,6 @@ function runLogUrlOf(row) {
2722
2721
  return "";
2723
2722
  return row.runUrl;
2724
2723
  }
2725
- /**
2726
- * 「最終同期: …」の横に出すリンク先(#560)。**その時刻を書いた run のときだけ。**
2727
- *
2728
- * 「最終同期」は `lastOkAt`(最後に取り込めた時刻)で、行の `runUrl` は**いまの status を
2729
- * 書いた run**のもの。いま `failed` の行の `lastOkAt` が最大なら、その `runUrl` は失敗した run
2730
- * を指すので出さない(間違った run へ送るより、無いほうがよい)。`ok` の行でも `lastOkAt` と
2731
- * `statusAt` が違う(履歴だけ先に進んだ)回は出さない。
2732
- *
2733
- * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
2734
- */
2735
- function lastSyncRunUrlOf(row) {
2736
- // **部分失敗の回も「取り込めた」(#634)。** ここを `status === "ok"` のままにすると、
2737
- // 「最終同期」の時刻は部分失敗の回のものになったのに、その時刻を書いた run へ行けない
2738
- // ——R9(その時刻を書いた run にだけリンクを出す)が、まさにこの便の対象の回で満たせない
2739
- if (!ingestSucceeded(row))
2740
- return "";
2741
- // 「最終同期」として出した時刻がこの行の `at` でないなら、リンク先の run は別の回のもの
2742
- if (row.lastOkAt && row.lastOkAt > row.at)
2743
- return "";
2744
- return runLogUrlOf(row);
2745
- }
2746
2724
  /**
2747
2725
  * `runUrl` の無い行に添える出どころの語(#560)。リンクが出る行・語の無い書き手は空文字。
2748
2726
  *
@@ -4912,6 +4890,9 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
4912
4890
  cursor: pointer; transition: border-color .12s, transform .12s;
4913
4891
  }
4914
4892
  .card:hover { border-color: var(--accent); }
4893
+ /* 前へ/次へで移った後に一覧へ戻ったときの、今開いていたカードの目印(分類B・オーナー指摘)。
4894
+ 新しい見た目は作らず、.card:hover と同じ強調(アクセント色の枠)を使い回す */
4895
+ .card.is-returned-to { border-color: var(--accent); }
4915
4896
  .card-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 12.5px; color: var(--muted); margin-bottom: 6px; }
4916
4897
  /* card-title はカードの見出し(#1 新しいタブでも開けるよう <a> にした)。見た目は元の <div> と揃える */
4917
4898
  .card-title { display: block; font-size: 16px; font-weight: 650; margin: 2px 0 6px; color: var(--text); }
@@ -5713,10 +5694,6 @@ a.ds-tile:hover { text-decoration: none; }
5713
5694
  .ds-all-connected { color: var(--muted); }
5714
5695
  .ds-gate { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 600; flex: 0 0 auto; }
5715
5696
  .ds-gate-at { font-size: 11px; color: var(--muted); flex: 0 0 auto; }
5716
- /* 実行ログへの導線(#560)。時刻の横の添え物なので .ds-gate-at と同じ弱さ。押せることだけ
5717
- 点線の下線で示す(目立たせない——案件リポジトリに入っていない人には開けない) */
5718
- .ds-run-link { font-size: 11px; color: var(--muted); flex: 0 0 auto; text-decoration: underline dotted; text-underline-offset: 2px; }
5719
- .ds-run-link:hover { color: var(--text); }
5720
5697
  /* run の無い書き手の行の出どころ(「中央の処理」「その場で確認」)。同じ弱さの文字 */
5721
5698
  .ds-run-src { font-size: 11px; color: var(--muted); flex: 0 0 auto; }
5722
5699
  /* 接続先1件ごとの「取り込み」(段C・2026-08-24)。**「最終同期」(.ds-sync・run単位)とは
@@ -6427,7 +6404,6 @@ ${nextMemoryReflectGoldAtMs.toString()}
6427
6404
  ${memoryReflectDoneAtMs.toString()}
6428
6405
  ${memoryReflectTextOf.toString()}
6429
6406
  ${runLogUrlOf.toString()}
6430
- ${lastSyncRunUrlOf.toString()}
6431
6407
  ${runSourceWordOf.toString()}
6432
6408
  var TILE_CAPS = ${JSON.stringify(TILE_CAPS)};
6433
6409
  var BAD_STATE_STATUSES = ${JSON.stringify(BAD_STATE_STATUSES)};
@@ -8596,6 +8572,11 @@ ${calendarAddUrl.toString()}
8596
8572
  // ⚠ 詳細→詳細(後述の前へ/次へ)では更新しない。** 更新すると、何件か読み進めてから
8597
8573
  // 一覧へ戻ったときに「最後に見ていた詳細でのスクロール位置」に飛んでしまう。
8598
8574
  // 一覧に戻る行き先は常に「一覧を離れたときの位置」でなければならない
8575
+ //
8576
+ // ⚠ ただし前へ/次へを使った後は、この「離れたときの位置」より下の pagerNavUsed /
8577
+ // currentDetailId(オーナー指摘・分類B 2026-10-02)が優先する。** 前へ/次へで
8578
+ // 10件先へ移ってから「一覧へ戻る」と、離れたときの位置ではなく今開いている記録の
8579
+ // カードの位置へ戻るべき、という指摘のため
8599
8580
  var savedListScrollY = 0;
8600
8581
  // どのタブの一覧で覚えた位置か(実装レビュー②)。** route() は詳細を描くとき、その記録の
8601
8582
  // 種類で listState.tab を書き換える(例: 用語集の一覧からウィキリンクで決定の詳細へ移ると
@@ -8606,17 +8587,89 @@ ${calendarAddUrl.toString()}
8606
8587
  // 直前に描いたのが詳細(not foundも含む)か一覧か。popstate(ブラウザの戻る/進む)は
8607
8588
  // go() を通らないので、この印で「詳細→一覧」の遷移だけを見分けて復元する
8608
8589
  var lastWasDetail = false;
8590
+ // 今の詳細(currentDetailId)に着いた最後の移動が、前へ/次へ(buildDetailPager の
8591
+ // navBtn)経由だったか(オーナー指摘・分類B 2026-10-02。実装レビューで、前へ/次への
8592
+ // 後に本文中のウィキリンクを挟む等の「直前だけ別経路」のときに古い値が残っていた不具合
8593
+ // が見つかり、go() の呼び出しごとに毎回立て直す形に直した)。一覧へ戻るとき true なら、
8594
+ // savedListScrollY ではなく今開いている記録(currentDetailId)のカードの位置へ戻す
8595
+ var pagerNavUsed = false;
8596
+ // 今いる詳細のレコードID(renderDetail/renderGenericDetail の先頭で更新)。
8597
+ // pagerNavUsed と組みで、一覧へ戻るときにどのカードへ戻せばよいかを覚える
8598
+ var currentDetailId = null;
8609
8599
  if ("scrollRestoration" in history) history.scrollRestoration = "manual";
8610
8600
 
8611
- function go(id) {
8601
+ /**
8602
+ * 今のタブで一覧に並んでいる順(絞り込み後)。前へ/次へ(pagerOrder/buildDetailPager)
8603
+ * と同じ並びを使う——ここだけ別の並びにすると、戻ったときのページ・位置が実際の
8604
+ * 一覧とずれる
8605
+ */
8606
+ function currentTabFilteredItems() {
8607
+ if (listState.tab === "decisions") return filteredDecisions();
8608
+ return filteredGenericItems(findTab(listState.tab));
8609
+ }
8610
+
8611
+ /**
8612
+ * 前へ/次へで今開いている記録(currentDetailId)が、一覧の何ページ目に出るか。
8613
+ * 絞り込みに現れない(#2 のフォールバックと同じ状況)ときは null——その場合は
8614
+ * カードが一覧に無いので、呼び出し側は今までどおりの位置に任せる
8615
+ */
8616
+ function pageOfCurrentDetail() {
8617
+ var items = currentTabFilteredItems();
8618
+ for (var i = 0; i < items.length; i++) {
8619
+ if (items[i].id === currentDetailId) return Math.floor(i / PAGE_SIZE) + 1;
8620
+ }
8621
+ return null;
8622
+ }
8623
+
8624
+ /**
8625
+ * 今開いているページの中から id のカードを探し、画面に入る位置へスクロールして
8626
+ * 少しだけ目立たせる(.is-returned-to は .card:hover と同じ強調の使い回し。新しい
8627
+ * 見た目は作らない)。見つからなければ何もせず false——呼び出し側が今までどおりの
8628
+ * 位置(savedListScrollY)にフォールバックする
8629
+ */
8630
+ function scrollToDetailCard(id) {
8631
+ var cards = document.querySelectorAll(".card[data-record-id]");
8632
+ var card = null;
8633
+ for (var i = 0; i < cards.length; i++) {
8634
+ if (cards[i].getAttribute("data-record-id") === id) { card = cards[i]; break; }
8635
+ }
8636
+ if (!card) return false;
8637
+ if (card.scrollIntoView) card.scrollIntoView({ block: "center" });
8638
+ card.classList.add("is-returned-to");
8639
+ setTimeout(function () { card.classList.remove("is-returned-to"); }, 1500);
8640
+ return true;
8641
+ }
8642
+
8643
+ /**
8644
+ * @param viaPager buildDetailPager の navBtn(前へ/次へ)からの呼び出しだけ true。
8645
+ * ⚠ **直前の1回だけを見る(実装レビューで判明)。** 前へ/次へを何度か使った後でも、
8646
+ * 間に本文中のウィキリンク等(viaPager 無し)を1回でも挟めば、その時点で false に
8647
+ * 戻す——戻すのは「今の詳細(currentDetailId)に着いた最後の移動」の種類であって、
8648
+ * 一覧を離れてから1回でも前へ/次へを使ったか、ではない。でないと、前へ/次へ→
8649
+ * ウィキリンクで別の記録へ、と移ってから一覧へ戻ったときに、ウィキリンクで着いた
8650
+ * 記録が「前へ/次へで着いた」ことになってしまう
8651
+ */
8652
+ function go(id, viaPager) {
8612
8653
  // 一覧から詳細へ移るときだけ、今の一覧のスクロール位置とタブを覚える
8613
8654
  if (id && !lastWasDetail) { savedListScrollY = window.scrollY; savedListTab = listState.tab; }
8655
+ // 前へ/次へを使った後に一覧へ戻るなら、今開いている記録のカードが出るページへ
8656
+ var usePagerTarget = !id && pagerNavUsed && currentDetailId;
8657
+ if (usePagerTarget) { var p = pageOfCurrentDetail(); if (p) listState.page = p; }
8614
8658
  var url = id ? "?id=" + encodeURIComponent(id) : tabUrl(listState.tab);
8615
- history.pushState({}, "", url);
8659
+ // viaPager を履歴に残す。ブラウザの戻る/進む(popstate)は go() を通らないので、
8660
+ // その着地が前へ/次へ経由だったかを後から知る手がかりがここにしか無い
8661
+ history.pushState({ viaPager: !!viaPager }, "", url);
8616
8662
  route();
8617
- // 詳細は常に先頭から。一覧は、離れたときと同じタブに戻るときだけ元の位置へ
8618
- if (id) window.scrollTo(0, 0);
8619
- else window.scrollTo(0, listState.tab === savedListTab ? savedListScrollY : 0);
8663
+ // 詳細は常に先頭から。一覧は、前へ/次へを使っていれば今の記録のカードへ、
8664
+ // 使っていなければ離れたときと同じタブに戻るときだけ元の位置へ
8665
+ if (id) {
8666
+ pagerNavUsed = !!viaPager;
8667
+ window.scrollTo(0, 0);
8668
+ } else {
8669
+ if (!(usePagerTarget && scrollToDetailCard(currentDetailId))) {
8670
+ window.scrollTo(0, listState.tab === savedListTab ? savedListScrollY : 0);
8671
+ }
8672
+ }
8620
8673
  }
8621
8674
 
8622
8675
  var listState = { tab: DEFAULT_TAB, q: "", category: "", month: "", status: "", page: 1 };
@@ -12821,12 +12874,11 @@ ${calendarAddUrl.toString()}
12821
12874
  var t = dsFmtTime(at || fleetSyncFallback(s) || "");
12822
12875
  if (!t) return null;
12823
12876
  var sync = el("span", { class: "ds-sync", text: "最終同期: " + t });
12824
- // その時刻を書いた run へ(#560)。**ストア由来の時刻のときだけ**——まとめ表の後詰めに
12825
- // run は無い。行は「ingestedAtOf」 と同じ選び方(「ingestedRowOf」)で取る。
12826
- // run の無い書き手(中央 Apps Script の会議・資料の取り込み)は出どころの語
12877
+ // run の無い書き手(中央 Apps Script の会議・資料の取り込み)は出どころの語を添える。
12878
+ // ストア由来の時刻のときだけ——まとめ表の後詰めには行が無い。行は「ingestedAtOf」 と
12879
+ // 同じ選び方(「ingestedRowOf」)で取る
12827
12880
  var row = at ? ingestedRowOf(LIVE_STATE, capId, refs) : null;
12828
- var url = lastSyncRunUrlOf(row);
12829
- var trail = url ? [runLogLink(url)] : row ? runTrailNodes(row, "") : [];
12881
+ var trail = row ? runTrailNodes(row) : [];
12830
12882
  return trail.length ? frag([sync].concat(trail)) : sync;
12831
12883
  }
12832
12884
 
@@ -14740,37 +14792,26 @@ ${calendarAddUrl.toString()}
14740
14792
  }
14741
14793
 
14742
14794
  /* -------------------------------------------------------------------------
14743
- * 実行ログへの導線(#560)
14795
+ * run の出どころの表示(#560・オーナー決定 2026-10-01: 「実行ログ ↗」は社内・お客様とも
14796
+ * 画面から外した)
14744
14797
  * -------------------------------------------------------------------------
14745
- * 行の 「runUrl」(その行を書いた Actions の run)があれば、時刻の横に小さな「実行ログ ↗」を
14746
- * 出す。run の無い書き手の行は、出どころを語で示す(「中央の処理」「その場で確認」)。
14747
- * どちらを出すかの判断は純関数(runLogUrlOf / runSourceWordOf)が持ち、ここは描くだけ。
14798
+ * run の無い書き手の行は、出どころを語で示す(「中央の処理」「その場で確認」)。run のある
14799
+ * 行(ingest / gold / nightly)は何も添えない——リンク先が実行用リポや案件リポの GitHub
14800
+ * Actions で、多くの利用者には開けないため。出すかどうかの判断は純関数
14801
+ * (runLogUrlOf / runSourceWordOf)が持ち、ここは描くだけ。
14748
14802
  * ----------------------------------------------------------------------- */
14749
14803
 
14750
- /** リンクの title。案件リポジトリに入っていない人には開けないので、押す前に分かるようにする */
14751
- var RUN_LINK_TITLE = "GitHub Actions の実行ログを開きます(プロジェクトリポジトリへのアクセスが要ります)";
14752
- /** 一覧(開始直後・run の番号がまだ無い)の title。飛び先が違うので文言も分ける */
14753
- var RUN_LIST_TITLE = "GitHub Actions の実行の一覧を開きます(プロジェクトリポジトリへのアクセスが要ります)";
14754
- var RUN_LIST_TEXT = "実行の一覧 ↗";
14755
-
14756
- /** 「実行ログ ↗」(新しいタブ・title つき)。一覧のときは文言と title を一覧のものにする */
14757
- function runLogLink(url, text) {
14758
- var list = text === RUN_LIST_TEXT;
14759
- return el("a", { class: "ds-run-link", href: url, target: "_blank", rel: "noopener", title: list ? RUN_LIST_TITLE : RUN_LINK_TITLE, text: text || "実行ログ ↗" });
14760
- }
14761
-
14762
14804
  /**
14763
- * 行の時刻の横に添えるもの(リンクか出どころの語)。**無ければ空配列。**
14805
+ * 行の時刻の横に添える出どころの語。**無ければ空配列。**
14764
14806
  * 「.ds-gate-at」「.ds-ingest」 の**兄弟**として置く(子にすると、文言の検査と
14765
14807
  * 「いま測った結果」への差し替えが崩れる)。
14766
14808
  *
14767
14809
  * @param kind どの時刻に添えたか(「gate」=「◯分前に確認」)。「いま測った結果」への差し替えは
14768
- * **この印のものだけ**消す——「取り込み: 〜に成功」の横のリンク(別の行の run)は残す
14810
+ * **この印のものだけ**消す——「取り込み: 〜に成功」の横の語(別の行のもの)は残す
14769
14811
  */
14770
- function runTrailNodes(row, url, kind) {
14771
- var u = url === undefined ? runLogUrlOf(row) : url;
14772
- var word = u ? "" : runSourceWordOf(row);
14773
- var node = u ? runLogLink(u) : word ? el("span", { class: "ds-run-src", text: word }) : null;
14812
+ function runTrailNodes(row, kind) {
14813
+ var word = runSourceWordOf(row);
14814
+ var node = word ? el("span", { class: "ds-run-src", text: word }) : null;
14774
14815
  if (!node) return [];
14775
14816
  if (kind) node.setAttribute("data-trail", kind);
14776
14817
  return [node];
@@ -14784,11 +14825,11 @@ ${calendarAddUrl.toString()}
14784
14825
  }
14785
14826
 
14786
14827
  /**
14787
- * 「いま測った結果」に差し替えるとき、**「◯分前に確認」に添えた**古い run の導線を外す
14788
- * (同じ行が 2 つのことを言わない)。「取り込み: 〜に成功」の横のリンクは別の行の run なので残す
14828
+ * 「いま測った結果」に差し替えるとき、**「◯分前に確認」に添えた**古い出どころの語を外す
14829
+ * (同じ行が 2 つのことを言わない)。「取り込み: 〜に成功」の横の語は別の行のものなので残す
14789
14830
  */
14790
14831
  function removeRunTrail(scope) {
14791
- var old = scope.querySelectorAll('.ds-run-link[data-trail="gate"], .ds-run-src[data-trail="gate"]');
14832
+ var old = scope.querySelectorAll('.ds-run-src[data-trail="gate"]');
14792
14833
  for (var i = 0; i < old.length; i++) old[i].remove();
14793
14834
  }
14794
14835
 
@@ -14827,7 +14868,7 @@ ${calendarAddUrl.toString()}
14827
14868
  class: "ds-ingest" + (r.status === "failed" && !(f && f.partial) ? " ds-ingest-failed" : ""),
14828
14869
  text: text,
14829
14870
  });
14830
- // その行を書いた run へ(#560)。成功でも失敗でも、この時刻を書いた run のログ
14871
+ // run の無い書き手は出どころの語を添える(#560)。成功でも失敗でも同じ
14831
14872
  return frag([span].concat(runTrailNodes(r)));
14832
14873
  }
14833
14874
 
@@ -14886,8 +14927,8 @@ ${calendarAddUrl.toString()}
14886
14927
  row.appendChild(el("span", { class: "ds-gate " + gate.cls, text: gate.text }));
14887
14928
  var when = rtAgo(r.at);
14888
14929
  if (when) row.appendChild(el("span", { class: "ds-gate-at", text: when + "に確認" }));
14889
- // その行を書いた run へ(#560)。run の無い書き手は出どころの語
14890
- runTrailNodes(r, undefined, "gate").forEach(function (n) { row.appendChild(n); });
14930
+ // run の無い書き手は出どころの語を添える(#560)
14931
+ runTrailNodes(r, "gate").forEach(function (n) { row.appendChild(n); });
14891
14932
  return row;
14892
14933
  }
14893
14934
 
@@ -15036,28 +15077,15 @@ ${calendarAddUrl.toString()}
15036
15077
  at: (before && before.at) || "",
15037
15078
  startedAt: Date.now(),
15038
15079
  message: (j && j.message) || "",
15039
- // 起こした run(特定できた回)と一覧(#560)。描き直しでもリンクが消えないよう控える
15040
- runUrl: (j && RUN_URL_RE.test(String(j.runUrl || ""))) ? String(j.runUrl) : "",
15041
- runsUrl: (j && /^https:\\/\\/github\\.com\\/[^\\s"'<>]+$/.test(String(j.runsUrl || ""))) ? String(j.runsUrl) : "",
15042
15080
  };
15043
15081
  probeWaitNote(note, PROBE_WAITS[key]);
15044
15082
  // 待っているあいだは取り直しを回す(届いた行で一覧が描き直される)
15045
15083
  refreshStateTimer();
15046
15084
  }
15047
15085
 
15048
- /**
15049
- * 待ちの 1 文と、その横の導線(#560)。**その run を特定できていればそれ、無ければ一覧。**
15050
- * 開始直後は run の番号が無いことが多いので一覧へ。行が更新されて待ちが解けたら、
15051
- * 行側の「実行ログ ↗」(その run)に置き換わる。run の無い経路(固定 IP・中央)は何も出ない
15052
- */
15086
+ /** 待ちの 1 文(#548)。「同期を開始しました」等、サーバの文言をそのまま出す */
15053
15087
  function probeWaitNote(note, wait) {
15054
15088
  note.textContent = (wait && wait.message) || "";
15055
- if (!wait) return;
15056
- var link = wait.runUrl ? runLogLink(wait.runUrl) : wait.runsUrl ? runLogLink(wait.runsUrl, RUN_LIST_TEXT) : null;
15057
- if (link) {
15058
- note.appendChild(document.createTextNode(" "));
15059
- note.appendChild(link);
15060
- }
15061
15089
  }
15062
15090
 
15063
15091
  /**
@@ -15136,12 +15164,7 @@ ${calendarAddUrl.toString()}
15136
15164
  var phase = probeWaitPhaseOf(capId, ref);
15137
15165
  var wait = probeWaitOf(capId, ref);
15138
15166
  if (phase === "waiting") probeWaitNote(note, wait);
15139
- else if (phase === "timeout") {
15140
- // 15 分待って届かないなら見に行く番。**行き先を添える**(#560)——一覧でも run でも
15141
- note.textContent = PROBE_WAIT_TIMEOUT_TEXT;
15142
- var to = wait && (wait.runUrl || wait.runsUrl);
15143
- if (to) { note.appendChild(document.createTextNode(" ")); note.appendChild(runLogLink(to, wait.runUrl ? "実行ログ ↗" : RUN_LIST_TEXT)); }
15144
- }
15167
+ else if (phase === "timeout") note.textContent = PROBE_WAIT_TIMEOUT_TEXT;
15145
15168
  else note.hidden = true;
15146
15169
 
15147
15170
  checkBtn.addEventListener("click", async function () {
@@ -16465,7 +16488,7 @@ ${calendarAddUrl.toString()}
16465
16488
  // 起動できなかった案件(権限・スタブ未配布)ではリンクを出さない——押しても何も無い
16466
16489
  if (j.runsUrl) {
16467
16490
  msg.appendChild(document.createTextNode(" "));
16468
- msg.appendChild(el("a", { href: j.runUrl || j.runsUrl, target: "_blank", rel: "noopener", title: RUN_LINK_TITLE, text: "実行状況を見る →" })); // その run を特定できていればそれ(#560)、無ければ一覧
16491
+ msg.appendChild(el("a", { href: j.runUrl || j.runsUrl, target: "_blank", rel: "noopener", title: "GitHub Actions の実行ログを開きます(プロジェクトリポジトリへのアクセスが要ります)", text: "実行状況を見る →" })); // その run を特定できていればそれ、無ければ一覧
16469
16492
  }
16470
16493
  // **「反映の状況 →」のリンクは無くなった(#221・2026-08-25)。** 指していたのは
16471
16494
  // 「画面を追いつかせるための fleet-status の実行」で、その起動ごと撤去した
@@ -17051,8 +17074,8 @@ ${calendarAddUrl.toString()}
17051
17074
  var gateAtEl = s.gateAt
17052
17075
  ? el("span", { class: "ds-gate-at", text: rtAgo(s.gateAt) + "に確認" })
17053
17076
  : null;
17054
- // その行を書いた run へ(#560)。overlay が写した 「gateRunUrl」 / 「gateBy」 から決める
17055
- var gateTrail = s.gateAt ? runTrailNodes({ runUrl: s.gateRunUrl || "", by: s.gateBy || "" }, undefined, "gate") : [];
17077
+ // run の無い書き手は出どころの語を添える(#560)。overlay が写した 「gateRunUrl」 / 「gateBy」 から決める
17078
+ var gateTrail = s.gateAt ? runTrailNodes({ runUrl: s.gateRunUrl || "", by: s.gateBy || "" }, "gate") : [];
17056
17079
  var card = el("div", { class: "ds-card" }, [
17057
17080
  el("div", { class: "ds-card-main" }, [
17058
17081
  el("span", { class: "ds-icon", text: meta.icon }),
@@ -18344,7 +18367,7 @@ ${calendarAddUrl.toString()}
18344
18367
  // 起動できたときだけ、実行状況への導線を出す(鍵を入れた直後に効いたか確かめられる)
18345
18368
  if (j.runsUrl) {
18346
18369
  msg.appendChild(document.createTextNode(" "));
18347
- msg.appendChild(el("a", { href: j.runUrl || j.runsUrl, target: "_blank", rel: "noopener", title: RUN_LINK_TITLE, text: "実行状況を見る →" })); // その run を特定できていればそれ(#560)、無ければ一覧
18370
+ msg.appendChild(el("a", { href: j.runUrl || j.runsUrl, target: "_blank", rel: "noopener", title: "GitHub Actions の実行ログを開きます(プロジェクトリポジトリへのアクセスが要ります)", text: "実行状況を見る →" })); // その run を特定できていればそれ、無ければ一覧
18348
18371
  }
18349
18372
  // 入れた値は画面に残さない(肩越しに見えるのを防ぐ)
18350
18373
  input.value = "";
@@ -20190,7 +20213,8 @@ ${calendarAddUrl.toString()}
20190
20213
  if (!isPlainClick(ev)) { ev.stopPropagation(); return; }
20191
20214
  ev.preventDefault();
20192
20215
  ev.stopPropagation();
20193
- go(rec.id);
20216
+ // 前へ/次へを使ったので、一覧へ戻るときは今の記録のカードの位置へ(オーナー指摘・分類B 2026-10-02)
20217
+ go(rec.id, true);
20194
20218
  });
20195
20219
  return a;
20196
20220
  }
@@ -20224,6 +20248,7 @@ ${calendarAddUrl.toString()}
20224
20248
  // ---------- 汎用詳細 ----------
20225
20249
  function renderGenericDetail(r) {
20226
20250
  lastWasDetail = true;
20251
+ currentDetailId = r.id;
20227
20252
  app.innerHTML = "";
20228
20253
  countEl.textContent = "";
20229
20254
 
@@ -20923,6 +20948,7 @@ ${calendarAddUrl.toString()}
20923
20948
  // ---------- Decision詳細 ----------
20924
20949
  function renderDetail(d) {
20925
20950
  lastWasDetail = true;
20951
+ currentDetailId = d.id;
20926
20952
  app.innerHTML = "";
20927
20953
  countEl.textContent = "";
20928
20954
 
@@ -21014,6 +21040,8 @@ ${calendarAddUrl.toString()}
21014
21040
 
21015
21041
  function renderNotFound(id) {
21016
21042
  lastWasDetail = true;
21043
+ // 実在しない記録なので、戻る先のカードは無い(今までどおりの位置にフォールバックさせる)
21044
+ currentDetailId = null;
21017
21045
  app.innerHTML = "";
21018
21046
  var back = el("a", { class: "detail-back", href: "?", text: "← 一覧へ戻る" });
21019
21047
  back.addEventListener("click", function (ev) { ev.preventDefault(); go(null); });
@@ -21500,19 +21528,29 @@ ${calendarAddUrl.toString()}
21500
21528
 
21501
21529
  // ブラウザの「戻る」「進む」。go() を通らないので、ここで一覧⇄詳細のスクロールだけ別に見る
21502
21530
  // (#1 ブラウザの戻るでも一覧のスクロール位置を保つ)
21503
- window.addEventListener("popstate", function () {
21531
+ window.addEventListener("popstate", function (ev) {
21504
21532
  var goingToId = getParam("id");
21505
21533
  var cameFromDetail = lastWasDetail;
21506
21534
  // 「進む」で一覧→詳細に戻ることもある(戻る→スクロール→進む、で位置が古くなる実装レビュー③)。
21507
21535
  // go() と同じ条件で、そのときの一覧の位置とタブを覚え直す
21508
21536
  if (goingToId && !cameFromDetail) { savedListScrollY = window.scrollY; savedListTab = listState.tab; }
21537
+ // ブラウザの「戻る」で一覧へ戻るときも、go() と同じく前へ/次へを使っていたら
21538
+ // 今開いている記録のカードが出るページへ(オーナー指摘・分類B 2026-10-02)
21539
+ var usePagerTarget = !goingToId && cameFromDetail && pagerNavUsed && currentDetailId;
21540
+ if (usePagerTarget) { var p = pageOfCurrentDetail(); if (p) listState.page = p; }
21509
21541
  route();
21510
21542
  if (!goingToId && cameFromDetail) {
21511
- // 一覧へ戻る。離れたときと同じタブのときだけ元の位置へ(違えば先頭)
21512
- window.scrollTo(0, listState.tab === savedListTab ? savedListScrollY : 0);
21543
+ // 一覧へ戻る。前へ/次へを使っていれば今の記録のカードへ、使っていなければ
21544
+ // 離れたときと同じタブのときだけ元の位置へ(違えば先頭)
21545
+ if (!(usePagerTarget && scrollToDetailCard(currentDetailId))) {
21546
+ window.scrollTo(0, listState.tab === savedListTab ? savedListScrollY : 0);
21547
+ }
21513
21548
  } else if (goingToId) {
21514
21549
  // 詳細はブラウザの戻る/進むでも常に先頭から
21515
21550
  // (scrollRestoration を manual にしたので、ここで明示しないとブラウザがどこへも戻さない)
21551
+ // pagerNavUsed も go() と同じく、この着地が前へ/次へ経由だったか(history.state.viaPager)
21552
+ // で立て直す——go() を通らない popstate では、ここだけがその情報を持っている
21553
+ pagerNavUsed = !!(ev.state && ev.state.viaPager);
21516
21554
  window.scrollTo(0, 0);
21517
21555
  }
21518
21556
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.109",
3
+ "version": "0.12.111",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,