@takagaki/cortex-decisions-viewer 0.12.157 → 0.12.159

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
@@ -1074,8 +1074,8 @@ const MEETING_DATE_DIR = /^\d{8}$/;
1074
1074
  * すでにある会議を列挙する(#277。投入モーダルの「会議名」の選択肢になる)。
1075
1075
  *
1076
1076
  * **「会議名」は画面から自由に打てるが、打たせるのは最後の手段にする。** 手で打つと
1077
- * 表記ゆれ(`定例` と `定例MTG`)で同じ会議が2つのディレクトリに割れ、議事録の連番も
1078
- * 履歴も分かれる。すでにある名前を選べれば、その事故はほとんど起きない。
1077
+ * 表記ゆれ(`定例` と `定例MTG`)で同じ会議が2つのディレクトリに割れ、
1078
+ * 履歴が分かれる。すでにある名前を選べれば、その事故はほとんど起きない。
1079
1079
  *
1080
1080
  * 会議とみなすのは「**日付ディレクトリ(`YYYYMMDD`)を直接ぶら下げているディレクトリ**」。
1081
1081
  * 名前の規則ではなく実際の中身で決めるので、`会議/定例/デイリースクラム/20260120/` のような
@@ -1084,25 +1084,10 @@ const MEETING_DATE_DIR = /^\d{8}$/;
1084
1084
  * **会議ディレクトリ直下に日付ディレクトリがある形(相対パスが空)は除外する。**
1085
1085
  * その形は「会議名なしで投入された回」で、選択肢に出しても送り先にできない
1086
1086
  * (`meeting` が空文字になり Lambda が 400 を返す)。
1087
- *
1088
- * `minuteDates` は、その日付ディレクトリに議事録(`*_minutes.md`)が**すでにある**日付。
1089
- * 同じ日に文字起こしを足しても議事録は作り直されないので、送る前に画面で伝えるために使う。
1090
1087
  */
1091
1088
  async function collectMeetings(repoRoot) {
1092
1089
  const rootAbs = path.join(repoRoot, MEETING_DIR);
1093
1090
  const out = [];
1094
- const hasMinute = async (dirAbs) => {
1095
- try {
1096
- for (const e of await node_fs_1.promises.readdir(dirAbs, { withFileTypes: true })) {
1097
- if (e.isFile() && e.name.endsWith("_minutes.md"))
1098
- return true;
1099
- }
1100
- }
1101
- catch {
1102
- // 読めない回は「議事録なし」扱い(注意を出さないだけで、投入は妨げない)
1103
- }
1104
- return false;
1105
- };
1106
1091
  const walk = async (relParts) => {
1107
1092
  let entries;
1108
1093
  try {
@@ -1116,15 +1101,9 @@ async function collectMeetings(repoRoot) {
1116
1101
  e.name !== "templates" &&
1117
1102
  !e.name.startsWith("."));
1118
1103
  const rel = relParts.join("/");
1119
- const dates = dirs.filter((d) => MEETING_DATE_DIR.test(d.name)).map((d) => d.name);
1120
- if (dates.length && rel) {
1121
- const minuteDates = [];
1122
- for (const d of dates.sort()) {
1123
- if (await hasMinute(path.join(rootAbs, ...relParts, d)))
1124
- minuteDates.push(d);
1125
- }
1126
- out.push({ path: rel, minuteDates });
1127
- }
1104
+ const hasDates = dirs.some((d) => MEETING_DATE_DIR.test(d.name));
1105
+ if (hasDates && rel)
1106
+ out.push({ path: rel });
1128
1107
  // 日付ディレクトリの中は回の中身なので降りない(会議名にはならない)。
1129
1108
  // 深さは Lambda 側の上限(isValidMeetingDir の 5 セグメント)と揃える——
1130
1109
  // それより深い会議を選択肢に出しても、選んだ瞬間に 400 で断られるだけ
package/dist/render.js CHANGED
@@ -4972,10 +4972,9 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
4972
4972
  .card-facet-yomi { font-size: 11.5px; }
4973
4973
  .card-titlerow { display: flex; align-items: baseline; gap: 12px; margin: 0 0 6px; }
4974
4974
  .card-titlerow .card-title { flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere; margin: 2px 0 0; }
4975
- /* 関係者のアイコン(名前のすぐ右・一覧と各ページで同じ見た目。名前は他の区画と同じく左詰め)。portal.id がある人はポータルの人のページへのリンク */
4976
- .card-namerow { display: flex; align-items: center; gap: 10px; margin: 2px 0 6px; }
4977
- .card-namerow .card-title { flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere; margin: 0; }
4978
- .card-titlerow .mface { align-self: center; }
4975
+ /* 関係者のアイコン。一覧のカードは右端(名前と説明文の 2 行の中央)・詳細ページは名前のすぐ右。portal.id がある人はポータルの人のページへのリンク */
4976
+ .card-facerow { display: flex; align-items: center; gap: 12px; }
4977
+ .card-facemain { flex: 1 1 auto; min-width: 0; }
4979
4978
  .detail-namerow { display: flex; align-items: center; gap: 12px; margin: 0 0 10px; }
4980
4979
  .detail-namerow .detail-title { margin: 0; min-width: 0; flex: 0 1 auto; overflow-wrap: anywhere; }
4981
4980
  .mface { position: relative; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; overflow: hidden; background: var(--border, #e5e7eb); color: var(--muted, #6b7280); font-size: 13px; font-weight: 700; line-height: 1; text-decoration: none; box-sizing: border-box; }
@@ -6094,11 +6093,6 @@ button.ops-card-btn:disabled { opacity: .6; cursor: default; }
6094
6093
  /* 会議名の欄(選択肢+新規入力+入力エラー) */
6095
6094
  .meeting-field { display: flex; flex-direction: column; gap: 6px; }
6096
6095
  .meeting-error { min-height: 0; }
6097
- /* 「その日はもう議事録がある」の注意。送信は妨げないので、エラー(赤)とは色を変える */
6098
- .modal-warn {
6099
- background: #fffbeb; border: 1px solid #fcd34d; border-radius: 8px;
6100
- padding: 8px 12px; font-size: 12.5px; color: #92400e; line-height: 1.7;
6101
- }
6102
6096
  .summary-box { background: var(--accent-weak); border: 1px solid #dce6fb; border-radius: var(--radius); padding: 12px 16px; color: #1e3a8a; }
6103
6097
  .deciders { display: flex; flex-wrap: wrap; gap: 8px; }
6104
6098
  .refs { margin: 0; padding-left: 18px; }
@@ -6680,7 +6674,7 @@ ${story_1.splitByNameLinks.toString()}
6680
6674
  * fleet-status.json のトップに置いた値を読む(cortex-engine #1094 便 γ 5-14)。
6681
6675
  * 値が届いていない(古い生成物)ときは空にして、宛先を名指さない文に倒す
6682
6676
  * (他の組織の画面に別の会社のアドレスを出さない)。
6683
- * 議事録の取り込みでも同じアカウントを使っている。
6677
+ * 会議の文字起こしの取り込みでも同じアカウントを使っている。
6684
6678
  */
6685
6679
  var DRIVE_INGEST_ACCOUNT = typeof SITE.driveReaderEmail === "string" ? SITE.driveReaderEmail : "";
6686
6680
  // Google Chat の「スペースにアプリを追加する」手順の画像(#389)。理由は
@@ -8124,11 +8118,22 @@ ${story_1.splitByNameLinks.toString()}
8124
8118
  var eb = fmErrorBadge(rec);
8125
8119
  if (eb) meta.appendChild(eb);
8126
8120
  var title = cardTitleLink(rec, titleExtra);
8127
- // 関係者は名前のすぐ右にアイコン(詳細と同じ mfAvatar・名前は左詰め)
8121
+ // 関係者のアイコンは一覧ではカードの右端(名前と説明文の 2 行の中央)。返す配列の face に持たせ、呼び出し側が組む
8128
8122
  var face = isMemberRec(rec) ? mfAvatar(rec) : null;
8129
- if (!meta.childNodes.length) return [el("div", { class: "card-titlerow" }, [title, face, trace])];
8130
- meta.appendChild(trace);
8131
- return [meta, face ? el("div", { class: "card-namerow" }, [title, face]) : title];
8123
+ var out;
8124
+ if (!meta.childNodes.length) out = [el("div", { class: "card-titlerow" }, [title, trace])];
8125
+ else { meta.appendChild(trace); out = [meta, title]; }
8126
+ out.face = face;
8127
+ return out;
8128
+ }
8129
+
8130
+ /** 一覧カードの本体を組む。関係者(head.face がある)は、上の段(.card-meta)の下の「名前+説明文」と右端のアイコンを横に並べる。 */
8131
+ function cardBody(head, rest) {
8132
+ var nodes = head.concat(rest);
8133
+ if (!head.face) return nodes;
8134
+ var top = nodes.filter(function (n) { return n && n.classList && n.classList.contains("card-meta"); });
8135
+ var main = nodes.filter(function (n) { return top.indexOf(n) < 0; });
8136
+ return top.concat([el("div", { class: "card-facerow" }, [el("div", { class: "card-facemain" }, main), head.face])]);
8132
8137
  }
8133
8138
 
8134
8139
  /**
@@ -12990,7 +12995,7 @@ ${story_1.splitByNameLinks.toString()}
12990
12995
  // いまの一覧が一番価値を出しているのはそこなので、畳んで失わないようにする。
12991
12996
  var CAP_META = {
12992
12997
  "課題管理": { icon: "📋", desc: "課題・ドキュメントの同期" },
12993
- "会議": { icon: "🎙", desc: "文字起こしから議事録を生成" },
12998
+ "会議": { icon: "🎙", desc: "会議の文字起こしを取り込み" },
12994
12999
  "共有資料": { icon: "📁", desc: "資料をMarkdown化して取り込み" },
12995
13000
  "チャット": { icon: "💬", desc: "チャンネルの会話をGoldの材料に" },
12996
13001
  "デザイン": { icon: "🎨", desc: "Figmaのコメント(指摘)の取り込み" },
@@ -13544,7 +13549,7 @@ ${story_1.splitByNameLinks.toString()}
13544
13549
  if (manual) {
13545
13550
  var mBody = el("div", { class: "ds-tile-body" }, [
13546
13551
  el("div", { class: "ds-tile-name", text: "文字起こしを手動で追加" }),
13547
- el("div", { class: "ds-tile-desc", text: "自動取り込みできない会議(顧客主催・別ツール)の文字起こしから議事録を作ります" }),
13552
+ el("div", { class: "ds-tile-desc", text: "自動取り込みできない会議(顧客主催・別ツール)の文字起こしを取り込みます" }),
13548
13553
  ]);
13549
13554
  var mTile = el("button", { class: "ds-tile", type: "button", id: "ds-manual-" + cap }, [
13550
13555
  el("span", { class: "ds-tile-logo", text: "📥" }),
@@ -15751,16 +15756,16 @@ ${story_1.splitByNameLinks.toString()}
15751
15756
  // 読む人の操作対象ではない——画面から直せないものなので、次の一歩は「担当者へ連絡」。
15752
15757
  // 「手で直してください」も出さない(PM に設定ファイルを編集させる案内になっていた)。
15753
15758
  if (s.ingestState === "unset") {
15754
- box.appendChild(el("p", { class: "sec-how", text: "このプロジェクトでは議事録の自動取り込みがまだ用意されていません。cortex の担当者にご連絡ください。" }));
15759
+ box.appendChild(el("p", { class: "sec-how", text: "このプロジェクトでは会議の自動取り込みがまだ用意されていません。cortex の担当者にご連絡ください。" }));
15755
15760
  used = true;
15756
15761
  } else if (s.ingestState === "broken") {
15757
- box.appendChild(el("p", { class: "sec-how", text: "議事録の取り込み設定を読み取れないため、この画面からは変更できません。cortex の担当者にご連絡ください。" }));
15762
+ box.appendChild(el("p", { class: "sec-how", text: "会議の取り込み設定を読み取れないため、この画面からは変更できません。cortex の担当者にご連絡ください。" }));
15758
15763
  used = true;
15759
15764
  } else if (s.ingestState === "off") {
15760
15765
  // 自動取り込みを止めている案件(例: Teams を使っていて手動で取り込む運用)。
15761
15766
  // ここでトグルを出すと「押せば始まる」と読めるが、実際は設定ファイルの意思表示なので
15762
15767
  // 画面から気軽に反転させるものではない。状態だけを伝える。
15763
- box.appendChild(el("p", { class: "sec-how", text: "このプロジェクトは議事録の自動取り込みを停止しています。" }));
15768
+ box.appendChild(el("p", { class: "sec-how", text: "このプロジェクトは会議の自動取り込みを停止しています。" }));
15764
15769
  used = true;
15765
15770
  }
15766
15771
  }
@@ -17986,7 +17991,7 @@ ${story_1.splitByNameLinks.toString()}
17986
17991
  function intakeCardFor(cap) {
17987
17992
  if (!SITE.intake) return null;
17988
17993
  if (cap === "会議") {
17989
- return opsCard("🗒️", "会議の文字起こしを追加", "顧客主催の会議など、自動取り込みできない会議の文字起こしファイル(.txt / .docx 等)から議事録を生成します。ファイルを選ぶかテキストを貼って送信すると、数分で反映されます。", "transcript", "文字起こしを追加");
17994
+ return opsCard("🗒️", "会議の文字起こしを追加", "顧客主催の会議など、自動取り込みできない会議の文字起こしファイル(.txt / .docx 等)を取り込みます。ファイルを選ぶかテキストを貼って送信すると、数分で保存され、夜の処理で記憶の材料になります。", "transcript", "文字起こしを追加");
17990
17995
  }
17991
17996
  // 共有資料の手動追加は #968 でやめた(入口は Google ドライブ 1 つ)。ここでは作らない
17992
17997
  return null;
@@ -18908,7 +18913,7 @@ ${story_1.splitByNameLinks.toString()}
18908
18913
  // 列挙の先頭を PDF から元のファイルに入れ替えてある(**受け付ける拡張子は 1 つも変えていない**。
18909
18914
  // allowedExt は下のまま)。
18910
18915
  modal.appendChild(el("p", { class: "modal-desc", text: isT
18911
- ? "文字起こしファイル(テキスト・Word・PDF・字幕 等)を選んで送信してください。(送信後、数分で議事録が自動生成されます)"
18916
+ ? "文字起こしファイル(テキスト・Word・PDF・字幕 等)を選んで送信してください。(送信後、数分で保存され、夜の処理で記憶の材料になります)"
18912
18917
  : "資料ファイル(PowerPoint・Word・Excel・PDF・テキスト・画像 等)を選んで送信してください。元のファイルがあれば PDF より優先してください(画面を保存した PDF・スキャンした PDF からは文字を取り出せません)。(送信後、数分で内容が Markdown に変換されて取り込まれます)" }));
18913
18918
 
18914
18919
  var selectedFiles = [];
@@ -18947,7 +18952,6 @@ ${story_1.splitByNameLinks.toString()}
18947
18952
  var meetingSelect = null;
18948
18953
  var meetingNewInput = null;
18949
18954
  var meetingError = null;
18950
- var dupWarn = null;
18951
18955
  // ユーザーが会議を自分で選んだか。**選んだ後はファイル名からの推測で上書きしない**
18952
18956
  var meetingTouched = false;
18953
18957
  if (isT) {
@@ -18970,9 +18974,6 @@ ${story_1.splitByNameLinks.toString()}
18970
18974
  meetingError.hidden = true;
18971
18975
  meetingBox.appendChild(meetingError);
18972
18976
  modal.appendChild(fieldRow("会議名(必須)", meetingBox));
18973
- dupWarn = el("div", { class: "modal-warn meeting-dup-warn" });
18974
- dupWarn.hidden = true;
18975
- modal.appendChild(dupWarn);
18976
18977
  }
18977
18978
 
18978
18979
  modal.appendChild(fieldRow("お名前(必須)", nameInput));
@@ -19081,35 +19082,9 @@ ${story_1.splitByNameLinks.toString()}
19081
19082
  }
19082
19083
  return null;
19083
19084
  }
19084
- function compactDate(iso) { return String(iso || "").split("-").join(""); }
19085
-
19086
- // ---- すでに議事録がある日 ----
19087
- function selectedMeeting() {
19088
- var v = meetingValue();
19089
- if (!v) return null;
19090
- for (var i = 0; i < meetingList.length; i++) if (meetingList[i].path === v) return meetingList[i];
19091
- return null;
19092
- }
19093
- function dupEntries() {
19094
- var m = selectedMeeting();
19095
- if (!m || !m.minuteDates || !m.minuteDates.length) return [];
19096
- return selectedFiles.filter(function (e) {
19097
- return e.date && m.minuteDates.indexOf(compactDate(e.date)) !== -1;
19098
- });
19099
- }
19100
- function refreshDupWarn() {
19101
- if (!dupWarn) return;
19102
- var dups = dupEntries();
19103
- dupWarn.hidden = !dups.length;
19104
- dupWarn.textContent = dups.length
19105
- ? "⚠ この日付には既に議事録があります。ファイルは保存されますが、議事録の自動生成は行われません: "
19106
- + dups.map(function (e) { return e.file.name; }).join(", ")
19107
- : "";
19108
- }
19109
19085
 
19110
19086
  function update() {
19111
19087
  refreshMeetingError();
19112
- refreshDupWarn();
19113
19088
  var ok = !!(nameInput.value.trim() && selectedFiles.length);
19114
19089
  // 文字起こしは会議名が確定し、各ファイルの日付と記録の種類が入っているときだけ送れる
19115
19090
  var kindMissing = selectedFiles.filter(function (e) { return asksKind(e) && !e.recordKind; }).length;
@@ -19169,7 +19144,7 @@ ${story_1.splitByNameLinks.toString()}
19169
19144
  function isAllowed(f) {
19170
19145
  // 資料はフロントで弾かない(どんなファイルも受け付ける)。対応可否の最終判断はバックエンドに任せる。
19171
19146
  if (!isT) return true;
19172
- // 文字起こしは議事録化に不適なファイルを避けるため拡張子で判定する。
19147
+ // 文字起こしは文字起こしに不適なファイルを避けるため拡張子で判定する。
19173
19148
  var ext = extOf(f.name);
19174
19149
  return !!(ext && allowedExt[ext]);
19175
19150
  }
@@ -19210,10 +19185,7 @@ ${story_1.splitByNameLinks.toString()}
19210
19185
  // 閉じる経路(Escape・背景クリック)は makeModal 側に用意されている
19211
19186
  cancelBtn.addEventListener("click", function () { mk.close(); });
19212
19187
  submitBtn.addEventListener("click", function () {
19213
- // 全ファイルが「議事録のある日」なら、成功メッセージも「生成はされない」側に寄せる。
19214
- // ✓ の直後に「数分後に議事録が自動生成され…」と出すと、待っても出ない
19215
- var allDup = isT && selectedFiles.length > 0 && dupEntries().length === selectedFiles.length;
19216
- submitIntake(kind, selectedFiles, nameInput.value, status, submitBtn, isT ? meetingValue() : null, allDup);
19188
+ submitIntake(kind, selectedFiles, nameInput.value, status, submitBtn, isT ? meetingValue() : null);
19217
19189
  });
19218
19190
  }
19219
19191
  /**
@@ -19238,7 +19210,7 @@ ${story_1.splitByNameLinks.toString()}
19238
19210
  el("div", { class: "modal-reopen-note", text: "開き直したら、このタブに戻ってもう一度[送信]を押してください。入力はそのまま使えます。" }),
19239
19211
  ]);
19240
19212
  }
19241
- async function submitIntake(kind, entries, submitter, status, submitBtn, meeting, allDup) {
19213
+ async function submitIntake(kind, entries, submitter, status, submitBtn, meeting) {
19242
19214
  var intake = SITE.intake;
19243
19215
  var isT = kind === "transcript";
19244
19216
  function setStatus(msg, isError) { status.className = "modal-status" + (isError ? " modal-status-error" : ""); status.textContent = msg; }
@@ -19286,9 +19258,7 @@ ${story_1.splitByNameLinks.toString()}
19286
19258
  if (!sub.ok) return fail((sub.j && sub.j.message) || "取り込みに失敗しました。時間をおいて再度お試しください。", sub.j && sub.j.code);
19287
19259
  status.className = "modal-status";
19288
19260
  status.textContent = "";
19289
- status.appendChild(el("div", { class: "modal-ok", text: allDup
19290
- ? "✓ 送信しました。保存されました(この日付には既に議事録があるため、議事録の自動生成は行われません)"
19291
- : "✓ 送信しました。数分後に議事録が自動生成され反映されます。" }));
19261
+ status.appendChild(el("div", { class: "modal-ok", text: "✓ 送信しました。保存されました。夜の処理で記憶の材料になります。" }));
19292
19262
  var paths = (sub.j && sub.j.committed) || [];
19293
19263
  if (paths.length && sub.j.repo) {
19294
19264
  var br = SITE.branch || "main";
@@ -19463,7 +19433,7 @@ ${story_1.splitByNameLinks.toString()}
19463
19433
  draftBadge(r),
19464
19434
  r.date ? el("span", { class: "mono", text: r.date }) : null,
19465
19435
  ].concat(cardFacets(r)), r.deleted ? " is-deleted" : "");
19466
- var card = el("div", { class: "card", title: r.id, "data-record-id": r.id }, head.concat([
19436
+ var card = el("div", { class: "card", title: r.id, "data-record-id": r.id }, cardBody(head, [
19467
19437
  r.summary ? el("p", { class: "card-summary", text: r.summary }) : null,
19468
19438
  ]));
19469
19439
  card.addEventListener("click", function () { go(r.id); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.157",
3
+ "version": "0.12.159",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,