@takagaki/cortex-decisions-viewer 0.12.98 → 0.12.100

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.
package/dist/build.js CHANGED
@@ -2362,7 +2362,9 @@ async function build(opts) {
2362
2362
  // Decisions ディレクトリで実行すると pathspec に一致せず常に空になる
2363
2363
  const aiHistory = (0, repo_1.collectAiChangeHistory)(process.cwd());
2364
2364
  // ストーリーの日々の動き(#901 V2-5)。帳簿で on の日のファイルが無ければ null(キーごと出さない)
2365
- const story = await (0, story_1.readStory)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null);
2365
+ const story = await (0, story_1.readStory)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null,
2366
+ // 動きのない日を埋める終点=ビルドの時刻の日本時間の日付(時計を注入できる)
2367
+ (0, story_1.todayJst)(new Date(opts.generatedAt)));
2366
2368
  // 案件アイコン(#348)。Cortex構成でなければ undefined(旧レイアウトには置き場が無い)
2367
2369
  const projectIcon = await findProjectIcon(knowledgeRoot);
2368
2370
  const site = {
package/dist/render.js CHANGED
@@ -4741,6 +4741,8 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
4741
4741
  .story-card {
4742
4742
  border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 18px; background: var(--surface);
4743
4743
  }
4744
+ .story-quiet { margin: -6px 0; padding: 0 18px; font-size: 12.5px; color: var(--muted); }
4745
+ .story-quiet-date { font-variant-numeric: tabular-nums; }
4744
4746
  .story-card-heading { margin: 0 0 8px; font-size: 15px; }
4745
4747
  .story-card-edit { display: inline-block; margin-top: 8px; font-size: 12.5px; }
4746
4748
  .story-card .body h2 { font-size: 14px; margin: 14px 0 6px; }
@@ -6332,6 +6334,18 @@ ${calendarAddUrl.toString()}
6332
6334
  // href に javascript: を置けるし、html: は innerHTML への抜け道(実使用は
6333
6335
  // homeGroundingSections の1箇所=ビルド時サニタイズ済みのHTMLのみ)。
6334
6336
  // 外部由来のURLを href に載せるときは、呼び出し側で isHttp() を通すこと。
6337
+ /**
6338
+ * 見出しに出す言葉。題名が記録の ID の形(term:… / rule:… / member:… / 20260928-002)のままなら、
6339
+ * 種類の接頭辞と決定番号を落として人の言葉だけにする(#551)。ID は見出しに出さず、
6340
+ * カードの title 属性と data-record-id にだけ残す。
6341
+ */
6342
+ function humanTitle(t) {
6343
+ var s = String(t == null ? "" : t).trim();
6344
+ var m = /^(?:term|rule|member|decision|person|glossary):\s*(.+)$/i.exec(s);
6345
+ if (m) s = m[1].trim();
6346
+ if (/^\d{8}-\d{3}(?:-|$)/.test(s)) s = s.replace(/^\d{8}-\d{3}-?/, "").trim();
6347
+ return s || "(題名なし)";
6348
+ }
6335
6349
  function el(tag, props, children) {
6336
6350
  var e = document.createElement(tag);
6337
6351
  if (props) Object.keys(props).forEach(function (k) {
@@ -6765,8 +6779,8 @@ ${calendarAddUrl.toString()}
6765
6779
  function followupDecisionEl(id) {
6766
6780
  var hit = null;
6767
6781
  (SITE.decisions || []).forEach(function (dd) { if (!hit && dd.id === id) hit = dd; });
6768
- if (!hit) return el("span", { class: "mono", text: id });
6769
- var a = el("a", { href: "?id=" + encodeURIComponent(id), text: hit.title || id });
6782
+ if (!hit) return el("span", { title: id, text: "(表示できない決定)" });
6783
+ var a = el("a", { href: "?id=" + encodeURIComponent(id), text: humanTitle(hit.title) });
6770
6784
  a.addEventListener("click", function (ev) { ev.preventDefault(); go(id); });
6771
6785
  return a;
6772
6786
  }
@@ -7012,7 +7026,7 @@ ${calendarAddUrl.toString()}
7012
7026
  var rec = byId[id];
7013
7027
  var li = el("li", { class: "supersede-row" });
7014
7028
  if (rec && rec._kind === "decision") {
7015
- var a = el("a", { href: "?id=" + encodeURIComponent(id), text: rec.title });
7029
+ var a = el("a", { href: "?id=" + encodeURIComponent(id), text: humanTitle(rec.title) });
7016
7030
  a.addEventListener("click", function (ev) {
7017
7031
  if (ev.defaultPrevented || ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
7018
7032
  ev.preventDefault();
@@ -7020,7 +7034,7 @@ ${calendarAddUrl.toString()}
7020
7034
  });
7021
7035
  li.appendChild(a);
7022
7036
  }
7023
- li.appendChild(el("span", { class: "supersede-id mono", text: (date ? date + " · " : "") + id }));
7037
+ if (date) li.appendChild(el("span", { class: "supersede-id mono", title: id, text: date }));
7024
7038
  if (unconfirmed) {
7025
7039
  li.appendChild(el("span", { class: "supersede-unconfirmed", title: "置き換えた側の決定が、まだ人の確認を経ていません", text: "未確認" }));
7026
7040
  }
@@ -8372,6 +8386,14 @@ ${calendarAddUrl.toString()}
8372
8386
  function renderStory() {
8373
8387
  var list = el("div", { class: "story-cards" });
8374
8388
  (SITE.story || []).forEach(function (d) {
8389
+ // 動きのない日は薄い 1 行(日付+文言。畳んだ行は「9/10〜9/18」)
8390
+ if (d.quiet) {
8391
+ list.appendChild(el("div", { class: "story-quiet" }, [
8392
+ el("span", { class: "story-quiet-date", text: d.heading || d.date }),
8393
+ el("span", { text: " は動きはありませんでした" }),
8394
+ ]));
8395
+ return;
8396
+ }
8375
8397
  list.appendChild(el("section", { class: "story-card" }, [
8376
8398
  el("h3", { class: "story-card-heading", text: d.heading || d.date }),
8377
8399
  d.bodyHtml ? el("div", { class: "body", html: d.bodyHtml }) : null,
@@ -18559,17 +18581,16 @@ ${calendarAddUrl.toString()}
18559
18581
  latestBadge(d),
18560
18582
  el("span", { class: "mono", text: d.date || "----" }),
18561
18583
  catBadge(d.category),
18562
- el("span", { class: "mono", text: d.id }),
18563
18584
  ]);
18564
18585
  var eb = fmErrorBadge(d);
18565
18586
  if (eb) meta.appendChild(eb);
18566
18587
  var traceD = el("a", { class: "mc-trace", href: "?tab=graph", text: "🕸 つながり" });
18567
18588
  traceD.addEventListener("click", function (ev) { ev.preventDefault(); ev.stopPropagation(); openExplorerFor(d); });
18568
18589
  meta.appendChild(traceD);
18569
- var card = el("div", { class: "card" }, [
18590
+ var card = el("div", { class: "card", title: d.id, "data-record-id": d.id }, [
18570
18591
  meta,
18571
18592
  // 消したレコードは一覧でも取り消し線(次のビルドまで一覧には残る)
18572
- el("div", { class: "card-title" + (d.deleted ? " is-deleted" : ""), text: d.title }),
18593
+ el("div", { class: "card-title" + (d.deleted ? " is-deleted" : ""), text: humanTitle(d.title) }),
18573
18594
  d.summary ? el("p", { class: "card-summary", text: d.summary }) : null,
18574
18595
  ]);
18575
18596
  card.addEventListener("click", function () { go(d.id); });
@@ -18630,16 +18651,15 @@ ${calendarAddUrl.toString()}
18630
18651
  proposalBadge(r),
18631
18652
  latestBadge(r),
18632
18653
  r.date ? el("span", { class: "mono", text: r.date }) : null,
18633
- el("span", { class: "mono", text: r.id }),
18634
18654
  ]);
18635
18655
  var ebr = fmErrorBadge(r);
18636
18656
  if (ebr) meta.appendChild(ebr);
18637
18657
  var traceR = el("a", { class: "mc-trace", href: "?tab=graph", text: "🕸 つながり" });
18638
18658
  traceR.addEventListener("click", function (ev) { ev.preventDefault(); ev.stopPropagation(); openExplorerFor(r); });
18639
18659
  meta.appendChild(traceR);
18640
- var card = el("div", { class: "card" }, [
18660
+ var card = el("div", { class: "card", title: r.id, "data-record-id": r.id }, [
18641
18661
  meta,
18642
- el("div", { class: "card-title" + (r.deleted ? " is-deleted" : ""), text: r.title }),
18662
+ el("div", { class: "card-title" + (r.deleted ? " is-deleted" : ""), text: humanTitle(r.title) }),
18643
18663
  r.summary ? el("p", { class: "card-summary", text: r.summary }) : null,
18644
18664
  ]);
18645
18665
  card.addEventListener("click", function () { go(r.id); });
@@ -19440,7 +19460,7 @@ ${calendarAddUrl.toString()}
19440
19460
  app.appendChild(back);
19441
19461
 
19442
19462
  var head = el("div", { class: "detail-head" });
19443
- head.appendChild(el("div", {}, [ el("h1", { class: "detail-title", text: r.title }) ]));
19463
+ head.appendChild(el("div", {}, [ el("h1", { class: "detail-title", text: humanTitle(r.title) }) ]));
19444
19464
  var traceBtn2 = el("a", { class: "trace-btn", href: "?tab=graph", text: "🕸 つながり" });
19445
19465
  traceBtn2.addEventListener("click", function (ev) { ev.preventDefault(); openExplorerFor(r); });
19446
19466
  var editRowR = el("div", { class: "detail-actions-row" });
@@ -19458,14 +19478,14 @@ ${calendarAddUrl.toString()}
19458
19478
  ]));
19459
19479
  app.appendChild(head);
19460
19480
 
19461
- app.appendChild(el("div", { class: "detail-meta" }, [
19481
+ var metaR = el("div", { class: "detail-meta", title: r.id, "data-record-id": r.id }, [
19462
19482
  draftBadge(r),
19463
19483
  proposalBadge(r),
19464
19484
  latestBadge(r),
19465
19485
  r.date ? el("span", { class: "mono", text: r.date }) : null,
19466
- el("span", { class: "mono", text: r.id }),
19467
19486
  r.summary ? el("span", { text: r.summary }) : null,
19468
- ]));
19487
+ ]);
19488
+ app.appendChild(metaR);
19469
19489
 
19470
19490
  var en2 = fmErrorNote(r);
19471
19491
  if (en2) app.appendChild(en2);
@@ -20116,7 +20136,7 @@ ${calendarAddUrl.toString()}
20116
20136
  app.appendChild(back);
20117
20137
 
20118
20138
  var head = el("div", { class: "detail-head" });
20119
- var headLeft = el("div", {}, [ el("h1", { class: "detail-title", text: d.title }) ]);
20139
+ var headLeft = el("div", {}, [ el("h1", { class: "detail-title", text: humanTitle(d.title) }) ]);
20120
20140
  head.appendChild(headLeft);
20121
20141
  var traceBtn = el("a", { class: "trace-btn", href: "?tab=graph", text: "🕸 つながり" });
20122
20142
  traceBtn.addEventListener("click", function (ev) { ev.preventDefault(); openExplorerFor(d); });
@@ -20142,8 +20162,9 @@ ${calendarAddUrl.toString()}
20142
20162
  latestBadge(d),
20143
20163
  el("span", { class: "mono", text: d.date || "----" }),
20144
20164
  catBadge(d.category),
20145
- el("span", { class: "mono", text: d.id }),
20146
20165
  ]);
20166
+ meta.title = d.id;
20167
+ meta.setAttribute("data-record-id", d.id);
20147
20168
  app.appendChild(meta);
20148
20169
 
20149
20170
  var en = fmErrorNote(d);
package/dist/story.js CHANGED
@@ -3,10 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.STORY_MAX_FILE_BYTES = exports.STORY_EMPTY_LINE = exports.STORY_MAX_DAYS = exports.STORY_DAYS_DIR = exports.STORY_LEDGER_PATH = void 0;
6
+ exports.STORY_MAX_FILE_BYTES = exports.STORY_COLLAPSE_QUIET_DAYS = exports.STORY_EMPTY_LINE = exports.STORY_MAX_DAYS = exports.STORY_DAYS_DIR = exports.STORY_LEDGER_PATH = void 0;
7
7
  exports.parseStoryLedger = parseStoryLedger;
8
8
  exports.onDaysOf = onDaysOf;
9
9
  exports.storyDayMarkdown = storyDayMarkdown;
10
+ exports.isEngineQuietDay = isEngineQuietDay;
11
+ exports.todayJst = todayJst;
12
+ exports.quietStoryRow = quietStoryRow;
13
+ exports.withQuietDays = withQuietDays;
10
14
  exports.readStory = readStory;
11
15
  /**
12
16
  * ストーリーのタブ(#901 記憶 v2・V2-5)。engine V2-3 が書く日々の動き
@@ -56,6 +60,10 @@ const MACHINE_WORD_RE = /urn:|cortex-engine|sha=|<!--|-->|\.md\b|Days\/|story-da
56
60
  const SUBHEADING_RE = /^#{2,6}\s/;
57
61
  /** 伏せて本文が空になった日の 1 行 */
58
62
  exports.STORY_EMPTY_LINE = "この日は表示できる行がありません。";
63
+ /** engine の「大きな動きなし」の日の行(`scripts/story/gates.mjs` の `QUIET_LINE`)。この 1 行だけの日は「動きのない日」と同じ見た目にそろえる */
64
+ const ENGINE_QUIET_LINE = "大きな動きなし";
65
+ /** 動きのない日がこの日数以上続いたら 1 行に畳む(分類 B・`docs/product-decisions.md` 2026-09-29) */
66
+ exports.STORY_COLLAPSE_QUIET_DAYS = 3;
59
67
  /** 1 枚の上限(これを超えるファイルは読まない・分類 B。engine の 1 枚は 4 KiB + 人の追記) */
60
68
  exports.STORY_MAX_FILE_BYTES = 64 * 1024;
61
69
  /** 帳簿を読む(投げない)。無い → `{ missing }`、壊れ・版違い → `{ broken }` */
@@ -161,6 +169,71 @@ function storyDayMarkdown(content, day) {
161
169
  // 伏せて空になった日は、見出しだけのカードにしない
162
170
  return { heading: heading, markdown: md || exports.STORY_EMPTY_LINE };
163
171
  }
172
+ /** 「大きな動きなし」の 1 行だけの日か(人の追記が 1 行でもあれば違う。機械の印・空行は数えない) */
173
+ function isEngineQuietDay(content) {
174
+ const lines = String(content ?? "").replace(/^\ufeff/, "").split("\n").map((l) => l.replace(/\r$/, "").trim());
175
+ const at = lines.findIndex((l) => l === HUMAN_HEADING);
176
+ const main = (at < 0 ? lines : lines.slice(0, at)).filter((l) => l !== "" && !STORY_MARKER_RE.test(l) && !/^#\s/.test(l));
177
+ const human = at < 0 ? [] : lines.slice(at + 1).filter((l) => l !== "" && !STORY_MARKER_RE.test(l));
178
+ return human.length === 0 && main.length === 1 && main[0].replace(/^[-*]\s+/, "") === ENGINE_QUIET_LINE;
179
+ }
180
+ const WEEKDAYS = ["日", "月", "火", "水", "木", "金", "土"];
181
+ const utcMs = (day) => Date.parse(`${day}T00:00:00Z`);
182
+ const dayOfMs = (ms) => new Date(ms).toISOString().slice(0, 10);
183
+ /** 日本時間の今日(`YYYY-MM-DD`)。時計は注入できる */
184
+ function todayJst(now = new Date()) {
185
+ // 不正な時刻でビルドを落とさない(実時計に倒す)
186
+ if (Number.isNaN(now.getTime()))
187
+ now = new Date();
188
+ return dayOfMs(now.getTime() + 9 * 3600 * 1000);
189
+ }
190
+ const mdOf = (day) => `${Number(day.slice(5, 7))}/${Number(day.slice(8, 10))}`;
191
+ /**
192
+ * 動きのない日の行(純関数)。`from`〜`to`(新しい方が `to`)を 1 行にする。1 日なら `2026-09-18(金)`、
193
+ * 続くなら `9/10〜9/18`。文言(「は動きはありませんでした」)は画面が付ける
194
+ */
195
+ function quietStoryRow(from, to) {
196
+ const heading = from === to ? `${to}(${WEEKDAYS[new Date(utcMs(to)).getUTCDay()]})` : `${mdOf(from)}〜${mdOf(to)}`;
197
+ return { date: to, ...(from === to ? {} : { fromDate: from }), heading: heading, bodyHtml: "", quiet: true };
198
+ }
199
+ /**
200
+ * ページのある日(`pages`)の間と、一番古いページから `today` までの日付の抜けを、
201
+ * 動きのない日の行で埋めて新しい順に返す。連続する `STORY_COLLAPSE_QUIET_DAYS` 日以上は 1 行に畳む。
202
+ * 🔴 ページより新しい日(今日まで)も埋める。`pages` が空なら空(タブを出さない)
203
+ */
204
+ function withQuietDays(pages, today) {
205
+ if (!pages.length)
206
+ return [];
207
+ const have = new Map(pages.map((p) => [p.date, p]));
208
+ const dates = pages.map((p) => p.date);
209
+ const oldest = dates.reduce((a, d) => (d < a ? d : a));
210
+ const newest = dates.reduce((a, d) => (d > a ? d : a));
211
+ const last = today > newest ? today : newest;
212
+ const out = [];
213
+ let run = []; // 新しい日から並ぶ動きのない日
214
+ const flush = () => {
215
+ if (!run.length)
216
+ return;
217
+ if (run.length >= exports.STORY_COLLAPSE_QUIET_DAYS)
218
+ out.push(quietStoryRow(run[run.length - 1], run[0]));
219
+ else
220
+ for (const d of run)
221
+ out.push(quietStoryRow(d, d));
222
+ run = [];
223
+ };
224
+ for (let ms = utcMs(last); ms >= utcMs(oldest); ms -= 86400000) {
225
+ const d = dayOfMs(ms);
226
+ const page = have.get(d);
227
+ if (page) {
228
+ flush();
229
+ out.push(page);
230
+ }
231
+ else
232
+ run.push(d);
233
+ }
234
+ flush();
235
+ return out;
236
+ }
164
237
  /**
165
238
  * 案件のストーリーを読む。**載せる日が無ければ `null`**(タブも器も出さない)。
166
239
  *
@@ -168,7 +241,9 @@ function storyDayMarkdown(content, day) {
168
241
  */
169
242
  async function readStory(repoRoot, renderMarkdown,
170
243
  // その日のファイルを GitHub で開く URL(リポジトリ相対パス → URL。リポジトリの URL が無い案件は null)
171
- editUrlOf = () => null) {
244
+ editUrlOf = () => null,
245
+ // 日本時間の今日(テストは時計を注入する)
246
+ today = todayJst()) {
172
247
  let text;
173
248
  try {
174
249
  text = await node_fs_1.promises.readFile(node_path_1.default.join(repoRoot, exports.STORY_LEDGER_PATH), "utf8");
@@ -229,7 +304,10 @@ editUrlOf = () => null) {
229
304
  }
230
305
  files.sort((a, b) => (a.day < b.day ? 1 : a.day > b.day ? -1 : 0));
231
306
  const out = [];
232
- for (const f of files.slice(0, exports.STORY_MAX_DAYS)) {
307
+ for (const f of files) {
308
+ // 上限は「動きのある日」の枚数で数える(「大きな動きなし」の日で枠を使わない)
309
+ if (out.length >= exports.STORY_MAX_DAYS)
310
+ break;
233
311
  let raw;
234
312
  try {
235
313
  raw = await node_fs_1.promises.readFile(f.abs, "utf8");
@@ -237,9 +315,13 @@ editUrlOf = () => null) {
237
315
  catch {
238
316
  continue;
239
317
  }
318
+ // 「大きな動きなし」だけの日は、ページの無い日と同じ「動きはありませんでした」の行にそろえる
319
+ if (isEngineQuietDay(raw))
320
+ continue;
240
321
  const { heading, markdown } = storyDayMarkdown(raw, f.day);
241
322
  const editUrl = editUrlOf(f.rel);
242
323
  out.push({ date: f.day, heading: heading, bodyHtml: await renderMarkdown(markdown), ...(editUrl ? { editUrl: editUrl } : {}) });
243
324
  }
244
- return out.length ? out : null;
325
+ // 一番古い「動きのある日」から今日まで、日付の抜けを動きのない日の行で埋める。動きのある日が 1 つも無ければ出さない
326
+ return out.length ? withQuietDays(out, today) : null;
245
327
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.98",
3
+ "version": "0.12.100",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,