@takagaki/cortex-decisions-viewer 0.12.121 → 0.12.123

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.
@@ -7,7 +7,7 @@ exports.PERMISSIONS_DATA = void 0;
7
7
  * 操作タブの[権限]がこの値を CLIENT_JS に注入して読む(公開ページと同じ中身)。
8
8
  */
9
9
  exports.PERMISSIONS_DATA = {
10
- "revisedOn": "2026年9月30日",
10
+ "revisedOn": "2026年10月8日",
11
11
  "enactedOn": "2026年9月25日",
12
12
  "tools": [
13
13
  {
@@ -292,6 +292,12 @@ exports.PERMISSIONS_DATA = {
292
292
  "accessText": "読む",
293
293
  "purpose": "会議のメモを探して読む"
294
294
  },
295
+ {
296
+ "spell": "https://www.googleapis.com/auth/drive.file",
297
+ "access": "write",
298
+ "accessText": "書く",
299
+ "purpose": "投入フォームから入った材料の根拠として、Cortex 自身が作ったフォルダに Google ドキュメントを置き、そのプロジェクトのメンバーだけに閲覧を許す(Cortex が作ったもの以外は開けません)"
300
+ },
295
301
  {
296
302
  "spell": "https://www.googleapis.com/auth/calendar",
297
303
  "access": "write",
package/dist/render.js CHANGED
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONNECTOR_ROW_ORDER = exports.BACKLOG_SPACE_MAX_AGE_MS = exports.CONNECTOR_KINDS = exports.BACKLOG_MCP_HOST = exports.CLAUDE_CONNECTORS_URL = exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.INGEST_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = exports.PERMISSION_WIRING = void 0;
3
+ exports.CONNECTOR_ROW_ORDER = exports.BACKLOG_SPACE_MAX_AGE_MS = exports.CONNECTOR_KINDS = exports.BACKLOG_MCP_HOST = exports.CLAUDE_CONNECTORS_URL = exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.INGEST_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = exports.PERMISSION_WIRING = exports.LEGACY_VIEWER_NOTICE_PARAM = void 0;
4
4
  exports.renderSite = renderSite;
5
5
  exports.renderSiteWithAssets = renderSiteWithAssets;
6
6
  exports.viewerAssetSources = viewerAssetSources;
7
7
  exports.viewerAssetNames = viewerAssetNames;
8
8
  exports.permissionToolId = permissionToolId;
9
+ exports.withoutLegacyNoticeParam = withoutLegacyNoticeParam;
10
+ exports.legacyAddressNoticeOf = legacyAddressNoticeOf;
9
11
  exports.isRepositorySlug = isRepositorySlug;
10
12
  exports.devRepositoryRefs = devRepositoryRefs;
11
13
  exports.claudeCodeRepositories = claudeCodeRepositories;
@@ -272,6 +274,62 @@ const PERMISSION_TOOL_ID = {
272
274
  function permissionToolId(adapter) {
273
275
  return Object.prototype.hasOwnProperty.call(PERMISSION_TOOL_ID, adapter) ? PERMISSION_TOOL_ID[adapter] : adapter;
274
276
  }
277
+ /**
278
+ * 古いアドレスの案内の印(cortex-engine #1075 R6-0f)。旧ビューア(Amplify)の 301 が、閉じる日を
279
+ * `?cortex_legacy_closes=YYYY-MM-DD` で付けて新しいアドレスへ送ってくる。
280
+ * 🔴 **cortex-tools の infra(`scripts/lib/legacy-viewers.cjs` の `LEGACY_VIEWER_NOTICE_PARAM`)と同じ綴り。**
281
+ * 片方だけ変えると案内が黙って消える(infra の `test/legacy-viewers.test.mjs` が字面を突き合わせる)
282
+ */
283
+ exports.LEGACY_VIEWER_NOTICE_PARAM = "cortex_legacy_closes";
284
+ /**
285
+ * アドレスのクエリから印だけを消したクエリ(R6-0f)。帯を出さない印(壊れた形・過ぎた日)もアドレスに残さない。
286
+ * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
287
+ */
288
+ function withoutLegacyNoticeParam(search, param) {
289
+ var params = new URLSearchParams(search);
290
+ if (!params.has(param))
291
+ return null;
292
+ params.delete(param);
293
+ var rest = params.toString();
294
+ return rest === "" ? "" : "?" + rest;
295
+ }
296
+ /**
297
+ * アドレスのクエリから古いアドレスの案内を読む(R6-0f)。印が無い・日付の形でない・暦に無い日なら null。
298
+ * 返すのは画面に出す日付(`11月10日`)と、**印だけを消したクエリ**(他のクエリ `?id=…` は残す)。
299
+ * `today`(`YYYY-MM-DD`・閲覧者の手元の日付)より前の日・62 日より先の日なら null。
300
+ *
301
+ * 🔴 **印の値をそのまま画面に書かない。** 誰でも作れるリンクなので、日付の形を通ったときだけ、
302
+ * 数字から組み直した文字を出す。
303
+ * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
304
+ */
305
+ function legacyAddressNoticeOf(search, param, today) {
306
+ var params = new URLSearchParams(search);
307
+ var values = params.getAll(param);
308
+ if (values.length !== 1)
309
+ return null;
310
+ var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(values[0]);
311
+ if (!m)
312
+ return null;
313
+ var y = Number(m[1]);
314
+ var mo = Number(m[2]);
315
+ var d = Number(m[3]);
316
+ var date = new Date(Date.UTC(y, mo - 1, d));
317
+ if (date.getUTCFullYear() !== y || date.getUTCMonth() !== mo - 1 || date.getUTCDate() !== d)
318
+ return null;
319
+ // 過ぎた日は出さない(誰でも作れるリンクで「使えなくなります」の嘘を出さない・閉じた後に残った印も黙らせる)
320
+ if (values[0] < today)
321
+ return null;
322
+ // 遠すぎる日も出さない(案内は閉じる 1 か月前から。62 日より先は作られたリンクとみなす)
323
+ var t = /^(\d{4})-(\d{2})-(\d{2})$/.exec(today);
324
+ if (!t)
325
+ return null;
326
+ var limit = Date.UTC(Number(t[1]), Number(t[2]) - 1, Number(t[3])) + 62 * 86400000;
327
+ if (date.getTime() > limit)
328
+ return null;
329
+ params.delete(param);
330
+ var rest = params.toString();
331
+ return { label: mo + "月" + d + "日", search: rest === "" ? "" : "?" + rest };
332
+ }
275
333
  /** 能力→アダプタの表を**テストから読むための入れ物**(CLAUDE_CODE_LINK と同じ作法・#841) */
276
334
  exports.PERMISSION_WIRING = { adapterCatalog: ADAPTER_CATALOG };
277
335
  /**
@@ -5146,6 +5204,9 @@ a.pager-btn:hover { text-decoration: none; }
5146
5204
  padding: 10px 12px; font-size: 13.5px; line-height: 1.8; color: #92400e;
5147
5205
  }
5148
5206
  .draft-note > .stale-note { flex: 1 0 100%; margin-bottom: 0; }
5207
+ /* 古いアドレスから来た人への案内(R6-0f)。#app の前に置くので、上の余白だけ足す */
5208
+ .legacy-address-wrap { padding-bottom: 0; }
5209
+ .legacy-address-note { margin: 16px 0 0; }
5149
5210
  .stale-reload-btn {
5150
5211
  flex: 0 0 auto; cursor: pointer; white-space: nowrap;
5151
5212
  padding: 6px 14px; border-radius: 999px;
@@ -5426,6 +5487,7 @@ a.ds-name:hover { text-decoration: underline; }
5426
5487
  border: 1px solid #f0b429; background: #fffdf5; border-radius: 12px;
5427
5488
  padding: 12px 16px; display: flex; flex-direction: column; gap: 8px;
5428
5489
  }
5490
+ .ds-orgnote.is-resolved { border-color: transparent; background: transparent; padding: 0; }
5429
5491
  .ds-orgnote-title { font-weight: 700; font-size: 13.5px; color: #8a5a00; }
5430
5492
  /* 案内が指す行。**囲うことで「どのチャンネルの話か」を示す**(開閉はしない) */
5431
5493
  .ds-orgnote-rows { display: flex; flex-direction: column; gap: 8px; margin-top: 2px; }
@@ -6017,6 +6079,12 @@ button.ops-card-btn:disabled { opacity: .6; cursor: default; }
6017
6079
  color: var(--text); background: var(--surface); font-family: inherit;
6018
6080
  }
6019
6081
  .file-chip.is-unguessed { background: #fffbeb; border-color: #fcd34d; color: #92400e; }
6082
+ /* 記録の種類(cortex-engine #1094 V2a)。選ぶまで赤い枠(選ばないと送れない) */
6083
+ .file-chip-kind {
6084
+ border: 1px solid var(--border); border-radius: 6px; padding: 1px 4px; font-size: 11.5px;
6085
+ color: var(--text); background: var(--surface); font-family: inherit;
6086
+ }
6087
+ .file-chip-kind.is-unset { border-color: #dc2626; color: #b91c1c; }
6020
6088
  .file-chip-guess { font-size: 11px; font-weight: 700; color: #92400e; white-space: nowrap; }
6021
6089
  /* 会議名の欄(選択肢+新規入力+入力エラー) */
6022
6090
  .meeting-field { display: flex; flex-direction: column; gap: 6px; }
@@ -6196,6 +6264,30 @@ var ADAPTER_CATALOG = ${JSON.stringify(ADAPTER_CATALOG)};
6196
6264
  // 小なり記号だけ逃がす(埋め込み形は script 要素の中なので、閉じタグの綴りを作らせない)
6197
6265
  var PERMISSION_TOOL_ID = ${JSON.stringify(PERMISSION_TOOL_ID)};
6198
6266
  ${permissionToolId.toString()}
6267
+ ${legacyAddressNoticeOf.toString()}
6268
+ ${withoutLegacyNoticeParam.toString()}
6269
+ var LEGACY_VIEWER_NOTICE_PARAM = ${JSON.stringify(exports.LEGACY_VIEWER_NOTICE_PARAM)};
6270
+ // 古いアドレスの案内の印(R6-0f)は**起動の最初に**読んで、アドレスから消す。タブや詳細の書き戻し
6271
+ // (replaceState)が先に走ると印が落ちて帯が出ない。帯を描くのは下の showLegacyAddressNotice
6272
+ var LEGACY_ADDRESS_NOTICE = (function () {
6273
+ var now = new Date();
6274
+ var pad = function (n) { return (n < 10 ? "0" : "") + n; };
6275
+ var today = now.getFullYear() + "-" + pad(now.getMonth() + 1) + "-" + pad(now.getDate());
6276
+ var search = location.search;
6277
+ var cleanSearch = withoutLegacyNoticeParam(search, LEGACY_VIEWER_NOTICE_PARAM);
6278
+ if (cleanSearch === null) return null;
6279
+ // 印は帯を出すかに関わらず消す(壊れた形・過ぎた日の印もブックマークに残さない)。
6280
+ // パスは先頭のスラッシュを 1 つにそろえる(2 つで始まると別のホストを指すリンクになる)
6281
+ var cleanUrl = "/" + location.pathname.replace(/^\\/+/, "") + cleanSearch + location.hash;
6282
+ try {
6283
+ history.replaceState(history.state, "", cleanUrl);
6284
+ } catch (e) {
6285
+ // file:// など replaceState が使えない環境では印が残るだけ
6286
+ }
6287
+ var notice = legacyAddressNoticeOf(search, LEGACY_VIEWER_NOTICE_PARAM, today);
6288
+ if (!notice) return null;
6289
+ return { label: notice.label, cleanUrl: cleanUrl };
6290
+ })();
6199
6291
  var PERMISSIONS_DATA = ${JSON.stringify(permissions_data_1.PERMISSIONS_DATA).replace(/</g, "\\u003c")};
6200
6292
  var CAP_STATE_TEXT = ${JSON.stringify(CAP_STATE_TEXT)};
6201
6293
  var CAP_STATE_MARK = ${JSON.stringify(CAP_STATE_MARK)};
@@ -8525,7 +8617,7 @@ ${calendarAddUrl.toString()}
8525
8617
  if (d.quiet) {
8526
8618
  list.appendChild(el("div", { class: "story-quiet" }, [
8527
8619
  el("span", { class: "story-quiet-date", text: d.heading || d.date }),
8528
- el("span", { text: " は動きはありませんでした" }),
8620
+ el("span", { text: d.pending ? " はまだ書けていません" : " は動きはありませんでした" }),
8529
8621
  ]));
8530
8622
  return;
8531
8623
  }
@@ -10892,10 +10984,25 @@ ${calendarAddUrl.toString()}
10892
10984
  var cards = g.items.map(function (x) {
10893
10985
  var again = !!(x.ref && seenRef[x.ref]);
10894
10986
  if (x.ref) seenRef[x.ref] = 1;
10895
- return extSourceCard(x, DS_TYPE[x.type] || { icon: "🔗", label: x.type || "その他" }, { quietHelp: again });
10987
+ var rowCard = extSourceCard(x, DS_TYPE[x.type] || { icon: "🔗", label: x.type || "その他" }, { quietHelp: again });
10988
+ // 同じリポの issues / discussions を、接続確認の結果で揃えるための目印(updateGithubOrgBand)
10989
+ if (rowCard && rowCard.setAttribute && x.ref) rowCard.setAttribute("data-ref", x.ref);
10990
+ return rowCard;
10896
10991
  });
10897
10992
  if (g.state === "warn") {
10898
- out.push(orgNoteBox(githubNotConnectedNodes(g), cards));
10993
+ var warnBox = orgNoteBox(githubNotConnectedNodes(g), cards);
10994
+ // 「接続を確認」が通ったとき帯をその場で畳めるよう、いま読めていないリポの一覧を持たせる
10995
+ //(probeSection → updateGithubOrgBand)。同じリポの issues / discussions は 1 本と数える
10996
+ var unreadRefs = [];
10997
+ g.items.forEach(function (x) {
10998
+ if (x.ref && x.gate && x.gate !== "ok" && unreadRefs.indexOf(x.ref) < 0) unreadRefs.push(x.ref);
10999
+ });
11000
+ warnBox.setAttribute("data-unread-refs", JSON.stringify(unreadRefs));
11001
+ // 見出しの件数は夜間の値のことがある(画面の行数と違う)。初期値を持ち、増減だけ当てる
11002
+ var titleEl = warnBox.querySelector(":scope > .ds-orgnote-title");
11003
+ var m0 = titleEl ? /^⚠️ (\\d+)件のリポジトリを読み取れていません/.exec(titleEl.textContent || "") : null;
11004
+ if (m0) warnBox.setAttribute("data-unread-count", m0[1]);
11005
+ out.push(warnBox);
10899
11006
  return;
10900
11007
  }
10901
11008
  if (g.state === "unsure") {
@@ -10911,6 +11018,64 @@ ${calendarAddUrl.toString()}
10911
11018
  return out;
10912
11019
  }
10913
11020
 
11021
+ /**
11022
+ * 「接続を確認」の結果を、行が属する組織の帯へ反映する。
11023
+ *
11024
+ * 読めた行を帯の「読めていないリポ」から外し、**最後の 1 本だったら帯を畳む**。
11025
+ * まだ読めない行が残るなら帯は残し、見出しの件数だけ減らす。読めなかった結果は
11026
+ * 帯に戻す(読めていた行が後から読めなくなった場合も、帯が嘘をつかない)。
11027
+ * 帯が無い行(組織が繋がっている)では何もしない。
11028
+ */
11029
+ function updateGithubOrgBand(card, ref, ok) {
11030
+ var box = card.closest ? card.closest(".ds-orgnote") : null;
11031
+ if (!box || !ref) return;
11032
+ var raw = box.getAttribute("data-unread-refs");
11033
+ if (raw == null) return;
11034
+ var refs;
11035
+ try { refs = JSON.parse(raw) || []; } catch (e) { return; }
11036
+ var at = refs.indexOf(ref);
11037
+ if (ok && at >= 0) refs.splice(at, 1);
11038
+ else if (!ok && at < 0) refs.push(ref);
11039
+ else return;
11040
+ box.setAttribute("data-unread-refs", JSON.stringify(refs));
11041
+ // 同じリポの兄弟の行(issues / discussions)も、押した行と同じ判定に揃える
11042
+ var gateSrc = card.querySelector(".ds-gate");
11043
+ var sibs = box.querySelectorAll(".ds-card[data-ref]");
11044
+ for (var si = 0; gateSrc && si < sibs.length; si++) {
11045
+ var sib = sibs[si];
11046
+ if (sib === card || sib.getAttribute("data-ref") !== ref) continue;
11047
+ var sg = sib.querySelector(".ds-gate");
11048
+ if (sg) { sg.className = gateSrc.className; sg.textContent = gateSrc.textContent; }
11049
+ var sh = sib.querySelectorAll(".ds-gate-help");
11050
+ for (var hi = 0; hi < sh.length; hi++) sh[hi].hidden = ok;
11051
+ }
11052
+ var title = box.querySelector(":scope > .ds-orgnote-title");
11053
+ var cnt = parseInt(box.getAttribute("data-unread-count"), 10);
11054
+ if (!isNaN(cnt)) {
11055
+ cnt = Math.max(0, cnt + (ok ? -1 : 1));
11056
+ box.setAttribute("data-unread-count", String(cnt));
11057
+ }
11058
+ var hasBand = !!title;
11059
+ if (!hasBand) return;
11060
+ if (refs.length === 0) {
11061
+ // 帯だけを隠す。行(.ds-orgnote-rows)は残す
11062
+ for (var i = 0; i < box.children.length; i++) {
11063
+ var c = box.children[i];
11064
+ if (c.classList.contains("ds-orgnote-rows")) continue;
11065
+ c.hidden = true;
11066
+ }
11067
+ box.classList.add("is-resolved");
11068
+ return;
11069
+ }
11070
+ for (var k = 0; k < box.children.length; k++) {
11071
+ var ch = box.children[k];
11072
+ if (!ch.classList.contains("ds-orgnote-rows")) ch.hidden = false;
11073
+ }
11074
+ box.classList.remove("is-resolved");
11075
+ // 見出しが件数を持つ形(「N件のリポジトリを読み取れていません」)だけ数え直す
11076
+ if (!isNaN(cnt)) title.textContent = "⚠️ " + Math.max(1, cnt) + "件のリポジトリを読み取れていません";
11077
+ }
11078
+
10914
11079
  /** 確かめられなかった組織の案内。**導線は出さない** */
10915
11080
  function githubUnsureNodes(g) {
10916
11081
  var o = g.meta || {};
@@ -14151,6 +14316,9 @@ ${calendarAddUrl.toString()}
14151
14316
  // 再読み込みまで)で、「接続を確認 → 閉じる → その場で接続OK」にならない。
14152
14317
  // 据え置きあり=GitHub は叩かない(ストアの行は据え置きの対象外で毎回引く)。
14153
14318
  // 取り込む能力の即答(appendDispatchProbe)は先に同じ 1 行を持っている
14319
+ // 組織の帯も同じ結果で描き直す(読めた最後の 1 本なら帯が消える)。
14320
+ // 帯はページ描画時に 1 度だけ組むので、ここで触らないと再読み込みまで残る
14321
+ if (probeTypeOf(s.type) === "github") updateGithubOrgBand(card, s.ref, j.gate === "ok");
14154
14322
  reloadLiveState();
14155
14323
  } catch (e) {
14156
14324
  msg.className = "ds-toggle-msg is-error";
@@ -18698,6 +18866,15 @@ ${calendarAddUrl.toString()}
18698
18866
  ? { txt: 1, md: 1, docx: 1, doc: 1, rtf: 1, pdf: 1, vtt: 1, srt: 1 }
18699
18867
  : { pdf: 1, docx: 1, doc: 1, pptx: 1, ppt: 1, xlsx: 1, xls: 1, csv: 1, txt: 1, md: 1, rtf: 1, png: 1, jpg: 1, jpeg: 1, gif: 1, webp: 1, svg: 1, bmp: 1, heic: 1, tiff: 1 };
18700
18868
  var allowedLabel = "テキスト・Word・PDF・字幕(.vtt/.srt)";
18869
+ // 記録の種類(cortex-engine #1094 V2a)。値は取り込みの台帳の語彙(受け口の LEDGER_RECORD_KINDS)と同じ
18870
+ // 種類を尋ねるのは、取り込みの台帳に本文が載る文字のファイルだけ(Word・PDF は本文が台帳に載らないので、選んでも残らない)
18871
+ var RECORD_KIND_EXT = { txt: 1, md: 1, csv: 1, vtt: 1, srt: 1 };
18872
+ function asksKind(entry) { return isT && !!RECORD_KIND_EXT[extOf(entry.file.name)]; }
18873
+ var RECORD_KIND_OPTIONS = [
18874
+ { value: "transcript", label: "文字起こし" },
18875
+ { value: "human_minutes", label: "人の議事録" },
18876
+ { value: "tool_ai_summary", label: "AI の要約" },
18877
+ ];
18701
18878
  function extOf(name) { var m = String(name).toLowerCase().match(/\.([a-z0-9]+)$/); return m ? m[1] : ""; }
18702
18879
  var fileInput = el("input", { type: "file", multiple: "multiple", class: "modal-file-hidden" });
18703
18880
  var fileListEl = el("div", { class: "dropzone-files" });
@@ -18727,7 +18904,7 @@ ${calendarAddUrl.toString()}
18727
18904
  var meetingBox = el("div", { class: "meeting-field" });
18728
18905
  if (meetingList.length) {
18729
18906
  meetingSelect = el("select", { class: "modal-name meeting-select" });
18730
- meetingSelect.appendChild(el("option", { value: "", text: "会議を選択してください" }));
18907
+ meetingSelect.appendChild(el("option", { value: "", text: "既にある会議を選ぶ" }));
18731
18908
  meetingList.forEach(function (m) {
18732
18909
  meetingSelect.appendChild(el("option", { value: m.path, text: m.path }));
18733
18910
  });
@@ -18884,10 +19061,13 @@ ${calendarAddUrl.toString()}
18884
19061
  refreshMeetingError();
18885
19062
  refreshDupWarn();
18886
19063
  var ok = !!(nameInput.value.trim() && selectedFiles.length);
18887
- // 文字起こしは会議名が確定し、各ファイルの日付が入っているときだけ送れる
19064
+ // 文字起こしは会議名が確定し、各ファイルの日付と記録の種類が入っているときだけ送れる
19065
+ var kindMissing = selectedFiles.filter(function (e) { return asksKind(e) && !e.recordKind; }).length;
18888
19066
  if (isT) {
18889
- ok = ok && !meetingProblem() && selectedFiles.every(function (e) { return !!e.date; });
19067
+ ok = ok && !meetingProblem() && kindMissing === 0 && selectedFiles.every(function (e) { return !!e.date; });
18890
19068
  }
19069
+ // 種類の選び忘れは件数をボタンに出す(どこを直せば押せるかが分かる)
19070
+ submitBtn.textContent = kindMissing ? "記録の種類を選んでください(あと " + kindMissing + " 件)" : "送信";
18891
19071
  submitBtn.disabled = !ok;
18892
19072
  }
18893
19073
  function renderFiles() {
@@ -18914,6 +19094,21 @@ ${calendarAddUrl.toString()}
18914
19094
  });
18915
19095
  chip.appendChild(dateInput);
18916
19096
  if (note) chip.appendChild(note);
19097
+ // 記録の種類(cortex-engine #1094 V2a)。**既定値は置かない**——間違った種類が黙って入ると、
19098
+ // 会議ごとの一次の記録の選び方(文字起こし → 人の議事録 → AI の要約)を取り違える
19099
+ if (asksKind(entry)) {
19100
+ var kindSelect = el("select", { class: "file-chip-kind" + (entry.recordKind ? "" : " is-unset"), "aria-label": entry.file.name + " の記録の種類" });
19101
+ kindSelect.appendChild(el("option", { value: "", text: "記録の種類を選ぶ" }));
19102
+ RECORD_KIND_OPTIONS.forEach(function (o) { kindSelect.appendChild(el("option", { value: o.value, text: o.label })); });
19103
+ kindSelect.value = entry.recordKind || "";
19104
+ kindSelect.addEventListener("click", function (e) { e.stopPropagation(); });
19105
+ kindSelect.addEventListener("change", function () {
19106
+ entry.recordKind = kindSelect.value;
19107
+ kindSelect.className = "file-chip-kind" + (entry.recordKind ? "" : " is-unset");
19108
+ update();
19109
+ });
19110
+ chip.appendChild(kindSelect);
19111
+ }
18917
19112
  }
18918
19113
  var x = el("button", { class: "file-chip-x", type: "button", text: "×" });
18919
19114
  x.addEventListener("click", function (e) { e.stopPropagation(); selectedFiles.splice(idx, 1); renderFiles(); update(); });
@@ -18936,7 +19131,7 @@ ${calendarAddUrl.toString()}
18936
19131
  var guess = isT ? guessMeetingDate(f.name) : null;
18937
19132
  // 推せなければ当日を仮置きする。**空のままにしない**——日付が空の入力欄は
18938
19133
  // 「入れなくてよい」に見えるが、実際は送れない(送信ボタンが無効なだけ)
18939
- selectedFiles.push({ file: f, date: isT ? (guess || todayIso()) : "", guessed: !!guess });
19134
+ selectedFiles.push({ file: f, date: isT ? (guess || todayIso()) : "", guessed: !!guess, recordKind: "" });
18940
19135
  addedNames.push(f.name || "");
18941
19136
  });
18942
19137
  if (isT && addedNames.length) autoPickMeeting(addedNames);
@@ -19027,6 +19222,7 @@ ${calendarAddUrl.toString()}
19027
19222
  var up = { key: pres.j.key, filename: f.name };
19028
19223
  // 会議日は YYYYMMDD で送る(そのまま日付ディレクトリの名前になる)
19029
19224
  if (isT) up.date = String(entries[i].date || "").split("-").join("");
19225
+ if (isT && entries[i].recordKind) up.recordKind = entries[i].recordKind;
19030
19226
  uploaded.push(up);
19031
19227
  }
19032
19228
  setStatus("取り込み中…", false);
@@ -21569,6 +21765,28 @@ ${calendarAddUrl.toString()}
21569
21765
  }
21570
21766
  })();
21571
21767
 
21768
+ /**
21769
+ * 古いアドレスから来た人への案内(cortex-engine #1075 R6-0f)。旧ビューアの 301 が閉じる日の印を付けて
21770
+ * ここへ送ってくる。**帯は症状・行は一手**(帯は 1 本・押す口はこのページのアドレス 1 つ)。
21771
+ *
21772
+ * 🔴 **印はアドレスから先に消す**(起動の最初の LEGACY_ADDRESS_NOTICE)。残すと、案内を読んだ人がブックマークし直したアドレスにも印が残り、
21773
+ * 閉じた後も帯が出続ける。消すのは印だけ(他のクエリとハッシュは持ち越す)。
21774
+ * 帯は #app の外(前)に置く——#app は画面を描き直すたびに中身を入れ替えるので、中に置くと最初の描画で消える。
21775
+ */
21776
+ (function showLegacyAddressNotice() {
21777
+ var notice = LEGACY_ADDRESS_NOTICE;
21778
+ if (!notice) return;
21779
+ var cleanUrl = notice.cleanUrl;
21780
+ var mainEl = document.getElementById("app");
21781
+ if (!mainEl || !mainEl.parentNode) return;
21782
+ var box = el("div", { class: "stale-note legacy-address-note", role: "status" }, [
21783
+ el("span", { text: "古いアドレスから開きました。古いアドレスは " + notice.label + " で使えなくなります。" }),
21784
+ el("a", { href: cleanUrl, text: "このページのアドレスをブックマークし直してください" }),
21785
+ ]);
21786
+ var wrap = el("div", { class: "container legacy-address-wrap" }, [box]);
21787
+ mainEl.parentNode.insertBefore(wrap, mainEl);
21788
+ })();
21789
+
21572
21790
  // ブラウザの「戻る」「進む」。go() を通らないので、ここで一覧⇄詳細のスクロールだけ別に見る
21573
21791
  // (#1 ブラウザの戻るでも一覧のスクロール位置を保つ)
21574
21792
  window.addEventListener("popstate", function (ev) {
package/dist/story.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.STORY_DIGEST_EMPTY_LINE = exports.STORY_MAX_MONTHS = exports.STORY_MAX_WEEKS = exports.STORY_MONTHS_DIR = exports.STORY_WEEKS_DIR = 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_FILE = void 0;
7
7
  exports.parseStoryLedger = parseStoryLedger;
8
+ exports.partialDaysOf = partialDaysOf;
8
9
  exports.onDaysOf = onDaysOf;
9
10
  exports.storyDayMarkdown = storyDayMarkdown;
10
11
  exports.isEngineQuietDay = isEngineQuietDay;
@@ -109,6 +110,18 @@ function parseStoryLedger(text) {
109
110
  return "broken";
110
111
  }
111
112
  }
113
+ /** 帳簿で途中まで書いた日(`v2.partial_at` が空でない文字列の行・engine の `partialAtOf` と同じ判じ。engine #1094)。この日は「書けている最後の日」に数えない */
114
+ function partialDaysOf(ledger) {
115
+ const out = new Set();
116
+ for (const [day, row] of Object.entries(ledger.days)) {
117
+ if (!DAY_RE.test(day) || !row || typeof row !== "object")
118
+ continue;
119
+ const v2 = row.v2;
120
+ if (v2 && typeof v2 === "object" && typeof v2.partial_at === "string" && v2.partial_at.trim() !== "")
121
+ out.add(day);
122
+ }
123
+ return out;
124
+ }
112
125
  /** 帳簿で `mode: "on"` の日 */
113
126
  function onDaysOf(ledger) {
114
127
  const out = new Set();
@@ -233,16 +246,19 @@ const mdOf = (day) => `${Number(day.slice(5, 7))}/${Number(day.slice(8, 10))}`;
233
246
  * 動きのない日の行(純関数)。`from`〜`to`(新しい方が `to`)を 1 行にする。1 日なら `2026-09-18(金)`、
234
247
  * 続くなら `9/10〜9/18`。文言(「は動きはありませんでした」)は画面が付ける
235
248
  */
236
- function quietStoryRow(from, to) {
249
+ function quietStoryRow(from, to, pending = false) {
237
250
  const heading = from === to ? `${to}(${WEEKDAYS[new Date(utcMs(to)).getUTCDay()]})` : `${mdOf(from)}〜${mdOf(to)}`;
238
- return { date: to, ...(from === to ? {} : { fromDate: from }), heading: heading, bodyHtml: "", quiet: true };
251
+ return { date: to, ...(from === to ? {} : { fromDate: from }), heading: heading, bodyHtml: "", quiet: true, ...(pending ? { pending: true } : {}) };
239
252
  }
240
253
  /**
241
254
  * ページのある日(`pages`)の間と、一番古いページから `today` までの日付の抜けを、
242
255
  * 動きのない日の行で埋めて新しい順に返す。連続する `STORY_COLLAPSE_QUIET_DAYS` 日以上は 1 行に畳む。
243
256
  * 🔴 ページより新しい日(今日まで)も埋める。`pages` が空なら空(タブを出さない)
257
+ *
258
+ * `lastDay`=日の書き手が処理し終えた最後の日(帳簿の最後の日)。これより後の空の日は「動きがなかった」のではなく
259
+ * 「まだ書けていない」日なので `pending` の行にする(動きのない日の行とは畳まない)。分からない(`null`)ときは全部今までどおり
244
260
  */
245
- function withQuietDays(pages, today) {
261
+ function withQuietDays(pages, today, lastDay = null) {
246
262
  if (!pages.length)
247
263
  return [];
248
264
  const have = new Map(pages.map((p) => [p.date, p]));
@@ -251,15 +267,17 @@ function withQuietDays(pages, today) {
251
267
  const newest = dates.reduce((a, d) => (d > a ? d : a));
252
268
  const last = today > newest ? today : newest;
253
269
  const out = [];
254
- let run = []; // 新しい日から並ぶ動きのない日
270
+ let run = []; // 新しい日から並ぶ動きのない日(まだ書けていない日とは混ぜない)
271
+ const pendingOf = (d) => lastDay !== null && d > lastDay;
255
272
  const flush = () => {
256
273
  if (!run.length)
257
274
  return;
275
+ const pending = pendingOf(run[0]);
258
276
  if (run.length >= exports.STORY_COLLAPSE_QUIET_DAYS)
259
- out.push(quietStoryRow(run[run.length - 1], run[0]));
277
+ out.push(quietStoryRow(run[run.length - 1], run[0], pending));
260
278
  else
261
279
  for (const d of run)
262
- out.push(quietStoryRow(d, d));
280
+ out.push(quietStoryRow(d, d, pending));
263
281
  run = [];
264
282
  };
265
283
  for (let ms = utcMs(last); ms >= utcMs(oldest); ms -= 86400000) {
@@ -269,8 +287,11 @@ function withQuietDays(pages, today) {
269
287
  flush();
270
288
  out.push(page);
271
289
  }
272
- else
290
+ else {
291
+ if (run.length && pendingOf(run[0]) !== pendingOf(d))
292
+ flush();
273
293
  run.push(d);
294
+ }
274
295
  }
275
296
  flush();
276
297
  return out;
@@ -376,7 +397,11 @@ ledgerDir) {
376
397
  out.push({ date: f.day, heading: heading, bodyHtml: await renderMarkdown(markdown), ...(editUrl ? { editUrl: editUrl } : {}) });
377
398
  }
378
399
  // 一番古い「動きのある日」から今日まで、日付の抜けを動きのない日の行で埋める。動きのある日が 1 つも無ければ出さない
379
- return out.length ? withQuietDays(out, today) : null;
400
+ // 最後の日=帳簿で on の最後の日(日のファイルの日付ではない。末尾に静かな日が続いても、帳簿には処理した印が残る)
401
+ // 途中まで書いた日(partial_at つき)は数えない。その日のファイルがあれば本文は出る
402
+ const partial = partialDaysOf(ledger);
403
+ const lastDay = [...on].filter((d) => !partial.has(d)).reduce((a, d) => (a === null || d > a ? d : a), null);
404
+ return out.length ? withQuietDays(out, today, lastDay) : null;
380
405
  }
381
406
  // ---------------------------------------------------------------------------
382
407
  // 週・月のあらすじ(#901・engine の週・月の書き手が書く `Cortex/Story/Weeks`・`Cortex/Story/Months`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.121",
3
+ "version": "0.12.123",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,