@takagaki/cortex-decisions-viewer 0.10.1 → 0.10.2

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 +160 -7
  2. package/package.json +1 -1
package/dist/render.js CHANGED
@@ -2087,6 +2087,12 @@ a.ds-name:hover { text-decoration: underline; }
2087
2087
  .ds-keyrow { flex: 1 1 100%; min-width: 0; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
2088
2088
  /* 一覧の直後に置くとき(Figma)は、行の中ではないので区切り線を持たない */
2089
2089
  .ds-keyrow-list { margin-top: 10px; padding-top: 0; border-top: none; }
2090
+ /* 🔴 **hidden を効かせる(2026-08-22)。** 鍵の欄は接続先が0件のあいだ hidden 属性で
2091
+ 畳む(作らないのではなく隠す——理由は syncKeyCardVisibility の項)。ところが
2092
+ .ops-card は display: flex、.ds-keyrow は flex アイテムを名乗るので、
2093
+ UA 既定の [hidden] { display: none } が**負ける**。畳んだつもりで出たままになる。
2094
+ ⚠ CSS は jsdom から見えないので、この行はテストでは守れない(実機で見ること)。 */
2095
+ [hidden] { display: none !important; }
2090
2096
  /* 行の中に入った鍵カードは、カードの枠を持たない(行の一部に見せる) */
2091
2097
  .ops-card.ds-keycard { border: none; box-shadow: none; padding: 0; background: transparent; gap: 6px; }
2092
2098
  .ops-card.ds-keycard > .ops-card-head { gap: 8px; }
@@ -3930,6 +3936,113 @@ ${nextStateDelay.toString()}
3930
3936
  return null;
3931
3937
  }
3932
3938
 
3939
+ /** 鍵の種別 → その鍵が効く能力。**3つの口が共有する対応表**(下の不変条件を参照) */
3940
+ var SECRET_CAP = { figma: "デザイン", backlog: "課題管理" };
3941
+
3942
+ /**
3943
+ * 「この画面の鍵の欄に貼ってください」と案内しているチェックの id → その鍵が効く能力。
3944
+ *
3945
+ * **欄が出ていないときに出すと、存在しない場所を指す案内になる。** 最後の接続先を
3946
+ * 外した直後(登録は0件・夜間のチェックは古いまま)に実際にそうなっていた。
3947
+ * **id をここ1箇所に集める**——散らすと片方だけ直る。
3948
+ */
3949
+ var KEY_POINTING_CHECKS = { backlog_secrets: "課題管理", figma_token: "デザイン" };
3950
+
3951
+ /**
3952
+ * 応答に添えられた接続先の一覧を採ったときの件数(Backlog の adoptTargets)。
3953
+ *
3954
+ * **鍵の欄の出し入れだけがこれを見る。** まとめ表(fleetVal)が新しい接続先を知るのは
3955
+ * 次の取り直しの後なので、これが無いと「行はその場で出るのに、鍵の欄だけ遅れて出る」。
3956
+ * 一覧そのものの正本は adoptTargets が持つローカルの targets で、ここは写しにしない
3957
+ *(件数しか持たないのは、判断に要るのが件数だけだから)。
3958
+ * **節を作り直すときに捨てる**(buildDataSources の先頭)——作り直しはスナップショットから
3959
+ * 描き直すことなので、そこから先は素直にスナップショットを信じる。
3960
+ */
3961
+ var ADOPTED_TARGET_COUNT = null;
3962
+
3963
+ /**
3964
+ * 鍵(APIキー/トークン)が意味を持つ接続先の数(オーナー決定 2026-08-22)。
3965
+ * **0件なら、鍵の入力欄もタイルの「⚠ トークンが未設定です」も出さない**——
3966
+ * 繋ぐ先が無いのに鍵だけ求めるのは順番の逆な案内になる。最初の一歩は
3967
+ * 「+ 接続先を追加」に揃える(2026-08-12 の「未接続でも欄を出す」B判断を上書き)。
3968
+ *
3969
+ * 🔴 **口は3つ。全部この関数を通す**(0.4.71 からの不変条件を 2026-08-22 に拡張):
3970
+ * 1. 鍵の入力カードの表示/非表示(syncKeyCardVisibility)
3971
+ * 2. タイルの「⚠ トークンが未設定です」(tokenRelevant)
3972
+ * 3. 鍵の欄を指すチェック行(KEY_POINTING_CHECKS)
3973
+ * 別々に書くと「欄はあるのに未設定と言われない」「言われるのに欄が無い」
3974
+ * 「無い欄を指す案内だけが出る」のどれかが起きる(3つとも実機で出た形)。
3975
+ */
3976
+ function keyTargetCount(cap) {
3977
+ if (cap === "課題管理") {
3978
+ // 応答で採った一覧のほうが新しい(まとめ表が追いつくのは次の取り直しから)
3979
+ if (ADOPTED_TARGET_COUNT != null) return ADOPTED_TARGET_COUNT;
3980
+ return backlogTargetsNow(internalSourceOf("課題管理")).length;
3981
+ }
3982
+ if (cap === "デザイン") {
3983
+ var d = internalSourceOf("デザイン");
3984
+ return d && d.figmaFiles ? d.figmaFiles.length : 0;
3985
+ }
3986
+ return 1;
3987
+ }
3988
+
3989
+ /**
3990
+ * この画面がいま持っている鍵カード(種別つき)。secretCard が作るたびに載せ、
3991
+ * buildDataSources が節を作り直すたびに入れ替える。
3992
+ *
3993
+ * **DOM を検索しないのはなぜか。** カードは buildDataSources の中で作られるが、
3994
+ * 置かれるのは能力モーダルの中身(=タイルを押すまで文書に入らない切り離されたノード)。
3995
+ * 組み立て直後に document から探しても**1枚も見つからない**。
3996
+ */
3997
+ var KEY_CARDS = [];
3998
+
3999
+ /**
4000
+ * 鍵の欄と、その受け皿の出し入れ(オーナー決定 2026-08-22)。
4001
+ *
4002
+ * 🔴 **カードは常に作る。隠すだけにする。** 生成そのものを止めると、
4003
+ * **0件でモーダルを開いたまま最初の接続先を足しても欄が現れない**——
4004
+ * カードを作るのは buildDataSources のループ1回きりで、そのあとの描き直し
4005
+ *(paint / liveList)は**鍵の欄を作り直さない**(入力中の値と「保存中…」を守るため)。
4006
+ * 節ごとの差し替え(refreshDataSources)もモーダルが開いている間は何もしない。
4007
+ * つまり**閉じて開き直すまで出ない**。表示の切り替えなら描き直しのたびに当て直せる。
4008
+ *
4009
+ * **受け皿ごと隠す。** .ds-keyrow は margin-top / padding-top / border-top を持つので、
4010
+ * カードだけ畳むと**理由の無い余白と区切り線**が残る(レビューで実測)。
4011
+ * 判定は「中に見えているカードが1枚も無いか」——OFF にした能力で受け皿だけが
4012
+ * 残る形(カードを作らずに return する経路)も同じ理由でここに拾われる。
4013
+ *
4014
+ * ⚠ **3つの口のうち、描き直しに追随するのはこのカードだけ。** タイルの注記
4015
+ *(tokenRelevant)とチェック行(allChecks)は**節を組み立てるときに固定**される。
4016
+ * したがって**モーダルを開いたまま最後の1件を外した瞬間だけ**、鍵の欄は消えるのに
4017
+ * タイル側は古いまま(「トークンが未設定です」や鍵の欄を指す案内が残る)になりうる。
4018
+ * 閉じれば節が作り直されて揃う。タイルはオーバーレイの裏で読めないので実害が小さいと
4019
+ * 判断して、ここは合わせていない——**揃えるなら3つとも同じ描き直しに載せること**
4020
+ *(注記だけを動的にすると、判定が1箇所という不変条件のほうが先に壊れる)。
4021
+ */
4022
+ function syncKeyCardVisibility() {
4023
+ var shown = {};
4024
+ for (var k in SECRET_CAP) {
4025
+ if (Object.prototype.hasOwnProperty.call(SECRET_CAP, k)) shown[k] = keyTargetCount(SECRET_CAP[k]) > 0;
4026
+ }
4027
+ for (var i = 0; i < KEY_CARDS.length; i++) {
4028
+ // **対応表に無い種別は隠さない**(keyTargetCount の既定と同じく出す側に倒す)。
4029
+ // 3つ目の鍵が足されて分岐を書き忘れた場合は key-card-visibility の [全種0件] が落ちる
4030
+ KEY_CARDS[i].el.hidden = shown[KEY_CARDS[i].kind] === false;
4031
+ }
4032
+ eachKeySlot(function (slot) {
4033
+ slot.hidden = !slot.querySelector("[data-secret-kind]:not([hidden])");
4034
+ });
4035
+ }
4036
+
4037
+ /** いま在る鍵の受け皿(.ds-keyrow)を順に渡す。置き場の正本は KEY_SLOTS */
4038
+ function eachKeySlot(fn) {
4039
+ var slots = KEY_SLOTS.backlog || {};
4040
+ for (var d in slots) {
4041
+ if (Object.prototype.hasOwnProperty.call(slots, d) && slots[d]) fn(slots[d]);
4042
+ }
4043
+ if (KEY_SLOTS.figma) fn(KEY_SLOTS.figma);
4044
+ }
4045
+
3933
4046
  /**
3934
4047
  * 能力ごとの「**スナップショットが答えている集合**」。
3935
4048
  *
@@ -4282,6 +4395,10 @@ ${nextStateDelay.toString()}
4282
4395
 
4283
4396
  function buildDataSources() {
4284
4397
  KEY_SLOTS = { backlog: {}, backlogNamed: false, figma: null };
4398
+ // 鍵カードも受け皿と同じく作り直す(前回の切り離されたカードを掴んだままにしない)
4399
+ KEY_CARDS = [];
4400
+ // 作り直しはスナップショットから描き直すこと。応答で採った件数はここで捨てる
4401
+ ADOPTED_TARGET_COUNT = null;
4285
4402
  var internals = fleetVal("internalSources") || [];
4286
4403
  var sources = fleetVal("externalSources") || [];
4287
4404
  var pipes = fleetVal("pipelines") || [];
@@ -4678,7 +4795,7 @@ ${nextStateDelay.toString()}
4678
4795
  // 連携の鍵は、その能力のところへ置く(画面下まで往復させない)
4679
4796
  // **GitHub(「external-sources」)はここに無い。** 読み取りトークンは廃止し、
4680
4797
  // 読み取りは GitHub App に一本化した。**この表に戻すと入力欄が復活する**
4681
- var SECRET_CAP = { figma: "デザイン", backlog: "課題管理" };
4798
+ // (対応表そのものは SECRET_CAP。3つの口で共有するので上に置いてある)
4682
4799
  (SITE.secretKinds || []).forEach(function (k) {
4683
4800
  if (!SECRET_FORMS[k]) return;
4684
4801
  // **意図的にOFFにした能力にだけ、入力欄を出さない(0.4.71)。** 使わないと決めたものに
@@ -4698,6 +4815,11 @@ ${nextStateDelay.toString()}
4698
4815
  }));
4699
4816
  return;
4700
4817
  }
4818
+ // 🔴 **接続先が0件でも、ここでは return しない(オーナー決定 2026-08-22 の実装注意)。**
4819
+ // 「0件なら鍵の欄を出さない」のは決まりだが、**生成を止めると直せなくなる**——
4820
+ // モーダルを開いたまま最初の接続先を足しても、欄は閉じて開き直すまで現れない
4821
+ //(作るのはこのループ1回きりで、以後の描き直しは鍵の欄を作り直さない)。
4822
+ // カードは常に作り、出し入れは syncKeyCardVisibility が hidden で行う
4701
4823
  // **Backlog は接続先のスペースごとにカードを出す(2026-08-20・#127)。**
4702
4824
  // キーはスペースごとに別なので、欄が1つだと**どちらの鍵を入れたのか
4703
4825
  // 誰にも分からない**まま片方の棚に入る(入れたのに動かない、が起きる)。
@@ -4780,8 +4902,16 @@ ${nextStateDelay.toString()}
4780
4902
  figma_token: "デザイン", figma_inventory: "デザイン",
4781
4903
  dev_sources_registered: "開発", dev_issues_derived: "開発",
4782
4904
  };
4905
+ // 🔴 **鍵の欄を指すチェックは、鍵の欄が出ているときだけ出す(オーナー決定 2026-08-22)。**
4906
+ // 「この画面の『Backlog のAPIキー』に貼ってください」は、接続先が0件で欄を畳んでいる
4907
+ // あいだは**存在しない欄を指す案内**になる(最後の接続先を外した直後に実際に出た。
4908
+ // 夜間のチェックは古いままなので、登録が0件になっても missing が残っている)。
4909
+ // **ここで落とすので、タイルの注記と「セットアップの残り」の両方から消える**
4910
+ // ——片方だけで畳むと、落ちた行がもう片方に湧いて出る(loose の拾い直しに落ちるため)
4783
4911
  var allChecks = (fleetVal("checks") || []).filter(function (c) {
4784
- return c != null && c.status === "missing";
4912
+ if (c == null || c.status !== "missing") return false;
4913
+ var kc = KEY_POINTING_CHECKS[c.id];
4914
+ return !(kc && keyTargetCount(kc) === 0);
4785
4915
  });
4786
4916
  // **タイルに出せたものを覚えておく。** 振り分け先の能力タイルが立たない案件
4787
4917
  //(tools に宣言が無い・行も無い)では出す場所が無くなり、項目が**黙って消える**。
@@ -5005,7 +5135,8 @@ ${nextStateDelay.toString()}
5005
5135
  // 「入力欄はあるのに未設定だと言われない」「言われるのに入力欄が無い」のどちらかが起きる。
5006
5136
  // 0.4.70 は unset で入力欄を畳んでいたので注記も抑えていたが、案A で欄を出すようにしたため揃えた
5007
5137
  // 導出が無いデータでは判断材料が無いので、注記を出す側(=入力欄と揃う側)に倒す
5008
- var tokenRelevant = derived != null ? derived !== "off" : true;
5138
+ // 接続先0件の抑止は入力欄と**同じ関数**を通す(keyTargetCount 🔴 不変条件)
5139
+ var tokenRelevant = (derived != null ? derived !== "off" : true) && keyTargetCount(cap) > 0;
5009
5140
  if (tokenRelevant && sk && (SITE.secretKinds || []).indexOf(sk) >= 0 && !((fleetVal("secretSources") || {})[sk])) {
5010
5141
  body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ トークンが未設定です" }));
5011
5142
  }
@@ -5127,6 +5258,8 @@ ${nextStateDelay.toString()}
5127
5258
  sec.appendChild(todo);
5128
5259
  }
5129
5260
 
5261
+ // 組み立てたカードに、いまの件数で表示/非表示を当てる(詳細は syncKeyCardVisibility)
5262
+ syncKeyCardVisibility();
5130
5263
  return sec;
5131
5264
  }
5132
5265
  function renderDataSources() {
@@ -5677,6 +5810,10 @@ ${nextStateDelay.toString()}
5677
5810
  // **出し入れのたびに反映中を当て直す。** 外している間の描き直しはこの行に届かない
5678
5811
  // ので、戻すときに当てないと古い文言のまま出る
5679
5812
  applyReflect(emptyEl, reflectingNow());
5813
+ // 🔴 **鍵の欄の出し入れは、描き直しのたびに当て直す(2026-08-22)。** 最初の1件を
5814
+ // 足した瞬間に欄が現れ、最後の1件を外した瞬間に消えるのはここが効いているから。
5815
+ // 節ごとの差し替え(refreshDataSources)はモーダルが開いている間は何もしない
5816
+ syncKeyCardVisibility();
5680
5817
  }
5681
5818
 
5682
5819
  /**
@@ -5694,6 +5831,9 @@ ${nextStateDelay.toString()}
5694
5831
  if (x && x.domain && x.projectKey) next.push({ domain: String(x.domain), projectKey: String(x.projectKey) });
5695
5832
  }
5696
5833
  targets = next;
5834
+ // **鍵の欄の判断もこの一覧に従わせる(2026-08-22)。** keyTargetCount はまとめ表を
5835
+ // 見るので、ここで教えないと「行はその場で出るのに鍵の欄だけ次の取り直しまで出ない」
5836
+ ADOPTED_TARGET_COUNT = targets.length;
5697
5837
  paint();
5698
5838
  return targetsGenerationOf(targets) !== before;
5699
5839
  }
@@ -5722,9 +5862,14 @@ ${nextStateDelay.toString()}
5722
5862
  var dom = String(targets[ti].domain || "").toLowerCase();
5723
5863
  if (dom && domains.indexOf(dom) === -1) domains.push(dom);
5724
5864
  }
5725
- // **0件の案件にも1つ出す。** 「接続先を足してからでないと鍵を入れられない」形にすると、
5726
- // 立ち上げ直後の案件が最初の一歩を踏めない(#138 で直したのと同じ穴)。
5865
+ // **0件の案件にも受け皿は1つ作る。ただし中身は隠れている(2026-08-22)。**
5866
+ // オーナー決定で「接続先が0件なら鍵の欄は出さない」になったが、**作らないのではなく
5867
+ // 隠す**——受け皿ごと無いと、モーダルを開いたまま最初の1件を足したときに
5868
+ // 鍵の欄を置く場所が無く、閉じて開き直すまで現れない(syncKeyCardVisibility の項)。
5869
+ // 受け皿は0件のあいだ hidden なので、区切り線と余白も出ない。
5727
5870
  // 空文字のキーは「どのスペースとも結び付いていない受け皿」で、行の中には入らない
5871
+ //(足した接続先のドメインに対応するスロットは無いので、鍵の欄はこの受け皿に
5872
+ // 残ったまま一覧の下に現れる。paint() はこのキーを移動の対象から外している)
5728
5873
  if (!domains.length) domains.push("");
5729
5874
  for (var di = 0; di < domains.length; di++) keySlots[domains[di]] = el("div", { class: "ds-keyrow" });
5730
5875
  KEY_SLOTS.backlog = keySlots;
@@ -6088,7 +6233,7 @@ ${nextStateDelay.toString()}
6088
6233
  // how / note)。全文をここに移すと二重になり、片方だけ直る形になる
6089
6234
  // ——カードは接続マップとタイルの注記と条件を揃えているので、動かすと壊れやすい。
6090
6235
  hint: [
6091
- "Figma でトークンを発行します(読み取りだけ。スコープ2つ)。手順は上の「Figma のトークン」の欄にあります。",
6236
+ "Figma でトークンを発行します(読み取りだけ。スコープ2つ)。手順は「Figma のトークン」の欄にあります(ファイルを追加すると上に出ます)。",
6092
6237
  "発行したアカウントを、読み取りたい Figma ファイルに閲覧者として招待します(招待が無いと、トークンが正しくても読めません)。",
6093
6238
  "そのファイルのURLを下の欄に貼って「追加」を押します。",
6094
6239
  "追加したファイルは、画面名と Figma へのリンクが毎晩同期されます(画像は取り込まれません)。",
@@ -7001,6 +7146,9 @@ ${nextStateDelay.toString()}
7001
7146
  box.__sig = next;
7002
7147
  while (box.firstChild) box.removeChild(box.firstChild);
7003
7148
  build(box);
7149
+ // 一覧が変わった=接続先の件数が変わったかもしれない。鍵の欄を当て直す
7150
+ // (Figma の鍵の欄は一覧の**外**にあるので、ここで見ないと取り残される)
7151
+ syncKeyCardVisibility();
7004
7152
  };
7005
7153
  box.__rebuild();
7006
7154
  return box;
@@ -7521,7 +7669,7 @@ ${nextStateDelay.toString()}
7521
7669
  // 押すまで見えないと成立しない
7522
7670
  // - .sec-preferred … 「誰のアカウントで発行するのか」を**手順より先に**伝える行。
7523
7671
  // 未登録のときだけ出る=これから貼る人にだけ出るので、畳むと存在理由が消える
7524
- return el("div", { class: "ops-card" + (inRow ? " ds-keycard" : ""), "data-secret-kind": kind }, [
7672
+ var card = el("div", { class: "ops-card" + (inRow ? " ds-keycard" : ""), "data-secret-kind": kind }, [
7525
7673
  el("div", { class: "ops-card-head" }, [
7526
7674
  el("span", { class: "ops-card-icon", text: meta.icon }),
7527
7675
  el("h3", { class: "ops-card-title", text: cardTitle }),
@@ -7542,6 +7690,11 @@ ${nextStateDelay.toString()}
7542
7690
  // 説明が落ちるのは、いちばん落としてはいけない種類の欠落
7543
7691
  el("p", { class: "sec-note", text: "入れた値はここには表示されません(保存だけできます)。保存先は案件ごとに分かれているので、他の案件には影響しません。" }),
7544
7692
  ]);
7693
+ // **作った鍵カードは1枚残らずここに載せる。** 出し入れ(接続先が0件のときに畳む)は
7694
+ // DOM 検索ではなくこの控えで回す——カードは能力モーダルの中身で、タイルを押すまで
7695
+ // 文書に入っていないため(syncKeyCardVisibility の項)
7696
+ KEY_CARDS.push({ kind: kind, el: card });
7697
+ return card;
7545
7698
  }
7546
7699
 
7547
7700
  // ---------- 接続マップ(ビルド時生成のSVGを<template>から取り出して表示) ----------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,