@takagaki/cortex-decisions-viewer 0.4.49 → 0.4.51

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
@@ -167,6 +167,11 @@ async function readFleetSources(repoRoot) {
167
167
  ingestState: s.ingestState != null ? String(s.ingestState) : undefined,
168
168
  driveState: s.driveState != null ? String(s.driveState) : undefined,
169
169
  driveFolderCount: typeof s.driveFolderCount === "number" ? s.driveFolderCount : undefined,
170
+ // 登録済みDriveフォルダのID。**1件ずつ外せるようにするために要る**
171
+ // (件数だけでは、どれを外すか選べない)
172
+ driveFolderIds: Array.isArray(s.driveFolderIds)
173
+ ? s.driveFolderIds.map((x) => String(x)).filter(Boolean)
174
+ : undefined,
170
175
  // 登録済みFigmaファイルの一覧。**1件ずつ外せるようにするために要る**
171
176
  // (先頭1件のURLしか無かった頃は、6ファイル持つ案件でどれも選べなかった)。
172
177
  figmaFiles: Array.isArray(s.figmaFiles)
package/dist/render.js CHANGED
@@ -1407,14 +1407,35 @@ const CLIENT_JS = `
1407
1407
  internals.forEach(function (s) {
1408
1408
  // ラベルの下に補足行(会議の照合キー・Drive未設定の注記)を重ねられるよう縦組みにする
1409
1409
  var col = el("div", { class: "ds-col" }, [ dsNameEl(s.label || "", s.url) ]);
1410
- var keys = s.matchKeys || [];
1410
+ // 照合キーの表示はここ1箇所に集約する(下の案内と二重に出さない)。
1411
+ // **ON/OFFのトグルは出さない。** 取り込むかどうかは「Bot を会議に招待したか」で決まる
1412
+ // (Router.gs:「フィルタは持たない…招待が同意のゲート」)。enabled はルーティングの
1413
+ // 可否であって取り込みの可否ではないので、OFFにすると**Botは録り続けるのに
1414
+ // 文字起こしがINBOXに溜まる**という、招待しないより悪い状態を作れてしまう。
1415
+ // **案件キーを先頭に足す。** 照合は「案件キー → 顧客名 → meetingNamePatterns」の
1416
+ // 3段で行われるが、fleet-status が持つのは後ろ2つだけ。第1段は艦隊レジストリの
1417
+ // キーで、投入設定から取れる。運用規約「会議名に案件キーを含める」の本体なので、
1418
+ // これが抜けていると一番使われている目印が画面に出ない。
1419
+ var keys = [];
1420
+ // **自動取り込みが動いているときだけ出す。** Teams等で手動運用している案件に
1421
+ // 会議名の条件を出すと、自動で拾われると誤解させる(実際には拾われない)。
1422
+ if (s.kind === "会議" && s.ingestState === "on") {
1423
+ // 案件キーは艦隊レジストリのもので、案件リポ自身は知らない(fleet-status にも無い)。
1424
+ // ビルド時に注入される投入設定からしか取れないため、設定UIが未配線の案件では出ない。
1425
+ if (SITE.intake && SITE.intake.projectKey) keys.push(SITE.intake.projectKey);
1426
+ (s.matchKeys || []).forEach(function (k) {
1427
+ // 照合は大小文字を区別しないので、違いだけの重複は出さない(「cortex / Cortex」は雑音)
1428
+ var dup = keys.some(function (x) { return x.toLowerCase() === String(k).toLowerCase(); });
1429
+ if (!dup) keys.push(k);
1430
+ });
1431
+ }
1411
1432
  if (keys.length) {
1412
- var shown = keys.slice(0, 5).join(" / ");
1413
- if (keys.length > 5) shown += " 他" + (keys.length - 5) + "件";
1433
+ var shown = keys.slice(0, 6).join(" / ");
1434
+ if (keys.length > 6) shown += " 他" + (keys.length - 6) + "件";
1414
1435
  col.appendChild(el("span", {
1415
1436
  class: "ds-keys",
1416
1437
  text: "取り込み対象の会議名: " + shown,
1417
- title: "会議名にこれらの語が含まれていると、自動でこのCortexに取り込まれます",
1438
+ title: "会議名にこれらの語が含まれていると、この案件に振り分けられます",
1418
1439
  }));
1419
1440
  }
1420
1441
  if (s.driveSync === false) {
@@ -1616,27 +1637,26 @@ const CLIENT_JS = `
1616
1637
  var box = el("div", {});
1617
1638
  var used = false;
1618
1639
 
1619
- if (s.kind === "会議" && usable("会議", "google-meet")) {
1640
+ // **ON になっている案件は、宣言(Home.md tools)に関わらず案内を出す。**
1641
+ // 取り込みの実際のゲートは ingest-config.json の enabled で、中央の Apps Script
1642
+ // (Projects.gs)は Home.md から client しか読まない——tools は見ていない。
1643
+ // つまり「tools: teams なのに enabled: true」なら**実際に取り込まれる**ので、
1644
+ // 宣言で隠すと、動いているのに説明が出ない状態になる。
1645
+ if (s.kind === "会議" && s.ingestState === "on") {
1646
+ box.appendChild(meetingHowTo());
1647
+ used = true;
1648
+ } else if (s.kind === "会議" && usable("会議", "google-meet")) {
1620
1649
  if (s.ingestState === "unset") {
1621
1650
  box.appendChild(el("p", { class: "sec-how", text: "会議の取り込み設定がこのリポジトリにありません(ingest-config.json)。先に設置が必要です。" }));
1622
1651
  used = true;
1623
1652
  } else if (s.ingestState === "broken") {
1624
1653
  box.appendChild(el("p", { class: "sec-how", text: "会議の取り込み設定(ingest-config.json)が壊れているため、ここからは変更できません。手で直してください。" }));
1625
1654
  used = true;
1626
- } else if (s.ingestState === "on" || s.ingestState === "off") {
1627
- box.appendChild(stateToggle({
1628
- on: s.ingestState === "on",
1629
- labelOn: "取り込みを止める",
1630
- labelOff: "取り込みを始める",
1631
- confirmOn: [
1632
- "この案件の会議の文字起こしを、自動で取り込むようにします。",
1633
- "",
1634
- "取り込まれた文字起こしから議事録が作られ、毎晩の Gold 昇格の材料になります。",
1635
- "",
1636
- "有効にしますか?",
1637
- ],
1638
- build: function (next) { return { op: "set-meeting-enabled", value: next }; },
1639
- }));
1655
+ } else if (s.ingestState === "off") {
1656
+ // 自動取り込みを止めている案件(例: Teams を使っていて手動で取り込む運用)。
1657
+ // ここでトグルを出すと「押せば始まる」と読めるが、実際は設定ファイルの意思表示なので
1658
+ // 画面から気軽に反転させるものではない。状態だけを伝える。
1659
+ box.appendChild(el("p", { class: "sec-how", text: "この案件は会議の自動取り込みを行わない設定です(ingest-config.json の enabled が false)。" }));
1640
1660
  used = true;
1641
1661
  }
1642
1662
  }
@@ -1661,7 +1681,6 @@ const CLIENT_JS = `
1661
1681
  return [
1662
1682
  "このフォルダの中身を、顧客も見るリポジトリにコピーします。",
1663
1683
  "",
1664
- "読み取りは全案件共通のアカウントで行うため、他の案件のフォルダも指定できてしまいます。",
1665
1684
  "貼ったURLがこの案件のものか、もう一度確認してください。",
1666
1685
  "",
1667
1686
  v,
@@ -1671,20 +1690,37 @@ const CLIENT_JS = `
1671
1690
  },
1672
1691
  build: function (url, name) { return { op: "add-drive-folder", url: url, name: name }; },
1673
1692
  }));
1674
- if (s.driveState === "on" || s.driveState === "off") {
1675
- box.appendChild(stateToggle({
1676
- on: s.driveState === "on",
1677
- labelOn: "自動取り込みを止める",
1678
- labelOff: "自動取り込みを始める",
1679
- confirmOn: [
1680
- "登録済みのフォルダから、資料の自動取り込みを再開します。",
1681
- "",
1682
- "フォルダの中身が、顧客も見るリポジトリにコピーされます。",
1683
- "",
1684
- "有効にしますか?",
1685
- ],
1686
- build: function (next) { return { op: "set-materials-enabled", value: next }; },
1687
- }));
1693
+ // **ON/OFFのトグルは出さない。** フォルダを追加した時点で取り込みは始まる
1694
+ // (applyAddDriveFolder が enabled を立てる)ので「始める」を押す機会が無い。
1695
+ // 止めたいならフォルダを外す——登録そのものが意思表示、という会議と同じ考え方。
1696
+ var folders = s.driveFolderIds || [];
1697
+ if (folders.length) {
1698
+ var dlist = el("div", { class: "ds-subitems" });
1699
+ folders.forEach(function (id) {
1700
+ var row = el("div", { class: "ds-subitem" }, [
1701
+ dsNameEl(id, "https://drive.google.com/drive/folders/" + id),
1702
+ ]);
1703
+ row.appendChild(removeButton({
1704
+ label: "このフォルダを外す",
1705
+ confirmLines: function () {
1706
+ var lines = [
1707
+ "このフォルダを資料の取り込み対象から外します。",
1708
+ "",
1709
+ "次回の同期から、このフォルダの中身は取り込まれなくなります。",
1710
+ "すでに取り込まれた資料はリポジトリに残ります(不要なら手で削除してください)。",
1711
+ ];
1712
+ if (folders.length === 1) {
1713
+ lines.push("", "これが最後の1件です。外すと資料の自動取り込みは止まります。");
1714
+ }
1715
+ lines.push("", id, "", "外しますか?");
1716
+ return lines;
1717
+ },
1718
+ build: function () { return { op: "remove-drive-folder", id: id }; },
1719
+ onDone: function () { row.className = "ds-subitem ds-removed"; },
1720
+ }));
1721
+ dlist.appendChild(row);
1722
+ });
1723
+ box.appendChild(dlist);
1688
1724
  }
1689
1725
  used = true;
1690
1726
  }
@@ -1744,7 +1780,6 @@ const CLIENT_JS = `
1744
1780
  return [
1745
1781
  "このFigmaファイルの画面名とサムネイルを、顧客も見るリポジトリに取り込みます。",
1746
1782
  "",
1747
- "読み取りに使うトークンは担当者個人のもので、他の案件のファイルも読めることがあります。",
1748
1783
  "貼ったURLがこの案件のものか、もう一度確認してください。",
1749
1784
  "",
1750
1785
  v,
@@ -1760,6 +1795,25 @@ const CLIENT_JS = `
1760
1795
  return used ? box : null;
1761
1796
  }
1762
1797
 
1798
+ /**
1799
+ * 会議の取り込みの案内。**操作ではなく条件を示す。**
1800
+ *
1801
+ * 取り込まれるには2つとも要る:
1802
+ * ① cortex-notetaker Bot が会議に招待されている(=取り込む意思。これが同意のゲート)
1803
+ * ② 会議名が照合キーのどれかを含む(含まなければ、録れても案件に振り分けられない)
1804
+ *
1805
+ * ②の材料を画面が出さないと、招待したのに届かない理由が分からない。
1806
+ */
1807
+ function meetingHowTo() {
1808
+ // 会議名の条件は上の行(ds-keys)が出す。ここは「誰を招待するか」だけを言う
1809
+ return el("p", {
1810
+ class: "sec-how",
1811
+ text:
1812
+ "会議に cortex-notetaker Bot を招待すると、その会議の文字起こしが取り込まれ、議事録が作られます。" +
1813
+ "招待しなければ取り込まれません(招待が取り込みの合図です)。上の会議名を含む会議が対象です。",
1814
+ });
1815
+ }
1816
+
1763
1817
  /**
1764
1818
  * 外部ソースの追加フォーム(Slack・GitHub)。
1765
1819
  *
@@ -1786,7 +1840,6 @@ const CLIENT_JS = `
1786
1840
  return [
1787
1841
  "このチャンネルを、Cortex が読みに行く対象に追加します。",
1788
1842
  "",
1789
- "読み取りは全案件共通の Bot で行うため、他の案件のチャンネルも指定できてしまいます。",
1790
1843
  "貼ったURLがこの案件のものか、もう一度確認してください。",
1791
1844
  "",
1792
1845
  v,
@@ -1855,7 +1908,14 @@ const CLIENT_JS = `
1855
1908
  return;
1856
1909
  }
1857
1910
  msg.className = "ds-toggle-msg is-ok";
1858
- msg.textContent = "" + (j.message || "保存しました。");
1911
+ msg.textContent = "";
1912
+ msg.appendChild(document.createTextNode("✓ " + (j.message || "保存しました。")));
1913
+ // **起動できたときだけ導線を出す。** 押した直後に「効いたか」を確かめられるのが要点。
1914
+ // 起動できなかった案件(権限・スタブ未配布)ではリンクを出さない——押しても何も無い
1915
+ if (j.runsUrl) {
1916
+ msg.appendChild(document.createTextNode(" "));
1917
+ msg.appendChild(el("a", { href: j.runsUrl, target: "_blank", rel: "noopener", text: "実行状況を見る →" }));
1918
+ }
1859
1919
  btn.disabled = false;
1860
1920
  if (onDone) onDone();
1861
1921
  } catch (e) {
@@ -1865,7 +1925,14 @@ const CLIENT_JS = `
1865
1925
  }
1866
1926
  }
1867
1927
 
1868
- /** 危険な方向にだけ出す確認。**何が起きるかを具体に書く**(「よろしいですか」だけにしない) */
1928
+ /**
1929
+ * 危険な方向にだけ出す確認。**何が起きるかを具体に書く**(「よろしいですか」だけにしない)。
1930
+ *
1931
+ * **不安を煽らない。** 「全案件共通のアカウントで読むので他案件のものも指定できてしまいます」と
1932
+ * 書いていたが、取り違えはサーバ側が409で実際に弾く(findRefInOtherProjects)。
1933
+ * 実在する守りがあるのに脅し文を重ねると、操作者が萎縮するだけで安全は増えない。
1934
+ * 「貼ったURLがこの案件のものか確認してください」で足りる。
1935
+ */
1869
1936
  function confirmDanger(lines) {
1870
1937
  return window.confirm(lines.join(String.fromCharCode(10)));
1871
1938
  }
@@ -1878,7 +1945,6 @@ const CLIENT_JS = `
1878
1945
  var wrap = el("div", { class: "ds-toggle" });
1879
1946
  var on = opts.on === true;
1880
1947
  var btn = el("button", { class: "ds-toggle-btn" + (on ? " is-on" : ""), type: "button" });
1881
- var who = el("input", { class: "sec-input sec-input-inline", type: "text", placeholder: "変更者のお名前", autocomplete: "off" });
1882
1948
  var msg = el("div", { class: "ds-toggle-msg" });
1883
1949
  var paint = function () {
1884
1950
  btn.textContent = on ? opts.labelOn : opts.labelOff;
@@ -1888,23 +1954,15 @@ const CLIENT_JS = `
1888
1954
 
1889
1955
  btn.addEventListener("click", function () {
1890
1956
  var next = !on;
1891
- // **入力の検証を先に。** 確認を先にすると、強い確認文を読んでOKした後に
1892
- // 「お名前を入力してください」と言われ、同じ確認をもう一度読まされる。
1893
- if (!who.value.trim()) {
1894
- msg.className = "ds-toggle-msg is-error";
1895
- msg.textContent = "⚠ お名前を入力してください。";
1896
- return;
1897
- }
1898
1957
  // 読み始める方向(危険)だけ確認する。止める方向は素通し
1899
1958
  if (next && opts.confirmOn && !confirmDanger(opts.confirmOn)) { msg.textContent = ""; return; }
1900
- sendConfig(Object.assign({ editor: who.value.trim() }, opts.build(next)), msg, btn, function () {
1959
+ sendConfig(opts.build(next), msg, btn, function () {
1901
1960
  // **押した内容をボタンに反映し、逆向きの操作を残す。** バッジや一覧は触らない
1902
1961
  on = next;
1903
1962
  paint();
1904
1963
  });
1905
1964
  });
1906
1965
  wrap.appendChild(btn);
1907
- wrap.appendChild(who);
1908
1966
  wrap.appendChild(msg);
1909
1967
  return wrap;
1910
1968
  }
@@ -1921,25 +1979,17 @@ const CLIENT_JS = `
1921
1979
  function removeButton(opts) {
1922
1980
  var wrap = el("div", { class: "ds-toggle" });
1923
1981
  var btn = el("button", { class: "ds-toggle-btn ds-danger", type: "button", text: opts.label || "削除" });
1924
- var who = el("input", { class: "sec-input sec-input-inline", type: "text", placeholder: "変更者のお名前", autocomplete: "off" });
1925
1982
  var msg = el("div", { class: "ds-toggle-msg" });
1926
1983
 
1927
1984
  btn.addEventListener("click", function () {
1928
- // 入力の検証を先に(確認文を読ませた後で入力不足を告げると、同じ確認をもう一度読ませることになる)
1929
- if (!who.value.trim()) {
1930
- msg.className = "ds-toggle-msg is-error";
1931
- msg.textContent = "⚠ お名前を入力してください。";
1932
- return;
1933
- }
1934
1985
  if (!confirmDanger(opts.confirmLines())) { msg.textContent = ""; return; }
1935
- sendConfig(Object.assign({ editor: who.value.trim() }, opts.build()), msg, btn, function () {
1986
+ sendConfig(opts.build(), msg, btn, function () {
1936
1987
  btn.disabled = true;
1937
1988
  if (opts.onDone) opts.onDone();
1938
1989
  });
1939
1990
  });
1940
1991
 
1941
1992
  wrap.appendChild(btn);
1942
- wrap.appendChild(who);
1943
1993
  wrap.appendChild(msg);
1944
1994
  return wrap;
1945
1995
  }
@@ -1953,7 +2003,6 @@ const CLIENT_JS = `
1953
2003
  var name = opts.needsName
1954
2004
  ? el("input", { class: "sec-input", type: "text", placeholder: opts.namePlaceholder || "表示名", autocomplete: "off" })
1955
2005
  : null;
1956
- var who = el("input", { class: "sec-input", type: "text", placeholder: "変更者のお名前(履歴に残ります)", autocomplete: "off" });
1957
2006
  var btn = el("button", { class: "ds-toggle-btn", type: "button", text: "追加" });
1958
2007
  var msg = el("div", { class: "ds-toggle-msg" });
1959
2008
 
@@ -1968,13 +2017,8 @@ const CLIENT_JS = `
1968
2017
  msg.textContent = "⚠ 表示名を入力してください。";
1969
2018
  return;
1970
2019
  }
1971
- if (!who.value.trim()) {
1972
- msg.className = "ds-toggle-msg is-error";
1973
- msg.textContent = "⚠ お名前を入力してください。";
1974
- return;
1975
- }
1976
2020
  if (opts.confirmLines && !confirmDanger(opts.confirmLines(main.value.trim()))) { msg.textContent = ""; return; }
1977
- var payload = Object.assign({ editor: who.value.trim() }, opts.build(main.value.trim(), name ? name.value.trim() : ""));
2021
+ var payload = opts.build(main.value.trim(), name ? name.value.trim() : "");
1978
2022
  sendConfig(payload, msg, btn, function () {
1979
2023
  // 追加できたら入力欄を空にする(同じものを二度押ししないように)
1980
2024
  main.value = "";
@@ -1987,7 +2031,6 @@ const CLIENT_JS = `
1987
2031
  opts.hint ? el("p", { class: "sec-how", text: opts.hint }) : null,
1988
2032
  main,
1989
2033
  name,
1990
- who,
1991
2034
  el("div", { class: "ds-toggle" }, [btn, msg]),
1992
2035
  ]);
1993
2036
  return box;
@@ -2126,15 +2169,12 @@ const CLIENT_JS = `
2126
2169
  : el("p", { class: "sec-state is-none", text: "現在: 未設定" });
2127
2170
 
2128
2171
  var input = el("input", { class: "sec-input", type: "password", placeholder: "トークンを貼り付け", autocomplete: "off" });
2129
- var who = el("input", { class: "sec-input", type: "text", placeholder: "変更者のお名前(履歴に残ります)", autocomplete: "off" });
2130
2172
  var btn = el("button", { class: "ds-toggle-btn", type: "button", text: "保存" });
2131
2173
  var msg = el("div", { class: "ds-toggle-msg" });
2132
2174
 
2133
2175
  btn.addEventListener("click", async function () {
2134
2176
  var value = input.value;
2135
- var editor = who.value;
2136
2177
  if (!value.trim()) { msg.className = "ds-toggle-msg is-error"; msg.textContent = "⚠ トークンを入力してください。"; return; }
2137
- if (!editor.trim()) { msg.className = "ds-toggle-msg is-error"; msg.textContent = "⚠ お名前を入力してください。"; return; }
2138
2178
 
2139
2179
  btn.disabled = true;
2140
2180
  msg.className = "ds-toggle-msg";
@@ -2146,7 +2186,7 @@ const CLIENT_JS = `
2146
2186
  method: "POST",
2147
2187
  headers: { "content-type": "application/json" },
2148
2188
  // **シークレット名は送らない。** どの連携かだけを送り、書き込み先はサーバが案件から決める。
2149
- body: JSON.stringify({ projectKey: intake.projectKey, kind: kind, value: value, editor: editor }),
2189
+ body: JSON.stringify({ projectKey: intake.projectKey, kind: kind, value: value }),
2150
2190
  });
2151
2191
  var j = await res.json().catch(function () { return {}; });
2152
2192
  if (!res.ok) {
@@ -2156,8 +2196,13 @@ const CLIENT_JS = `
2156
2196
  return;
2157
2197
  }
2158
2198
  msg.className = "ds-toggle-msg is-ok";
2159
- msg.textContent = "✓ " + (j.message || "保存しました。");
2160
- // 入れた値は画面に残さない(肩越しに見えるのを防ぐ)。お名前は続けて直せるよう残す。
2199
+ msg.appendChild(document.createTextNode("✓ " + (j.message || "保存しました。")));
2200
+ // 起動できたときだけ、実行状況への導線を出す(鍵を入れた直後に効いたか確かめられる)
2201
+ if (j.runsUrl) {
2202
+ msg.appendChild(document.createTextNode(" "));
2203
+ msg.appendChild(el("a", { href: j.runsUrl, target: "_blank", rel: "noopener", text: "実行状況を見る →" }));
2204
+ }
2205
+ // 入れた値は画面に残さない(肩越しに見えるのを防ぐ)
2161
2206
  input.value = "";
2162
2207
  btn.disabled = false;
2163
2208
  } catch (e) {
@@ -2176,7 +2221,6 @@ const CLIENT_JS = `
2176
2221
  state,
2177
2222
  el("p", { class: "sec-how", text: meta.how }),
2178
2223
  input,
2179
- who,
2180
2224
  el("div", { class: "ds-toggle" }, [btn, msg]),
2181
2225
  ]);
2182
2226
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.4.49",
3
+ "version": "0.4.51",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,