@takagaki/cortex-decisions-viewer 0.12.192 → 0.12.194

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/render.js CHANGED
@@ -11391,6 +11391,12 @@ ${story_1.splitByNameLinks.toString()}
11391
11391
  */
11392
11392
  var LIVE_FLEET = null;
11393
11393
  var LIVE_FLEET_FAILED = false;
11394
+ /**
11395
+ * どのワークスペースのチャンネルでも通知先にできる栓(op=state の notifyAnyWorkspace・#1370)。
11396
+ * **ライブだけの値**で、静的ビルドには無い(取得前・栓 off・古い intake では false=「準備中」)。
11397
+ * true のときだけ、ワークスペースを宣言した行に[通知先にする]を出す
11398
+ */
11399
+ var LIVE_NOTIFY_ANY_WS = false;
11394
11400
  /**
11395
11401
  * リアルタイム同期の可否(op=state の realtime。#113)。取得前・取得失敗時は null。
11396
11402
  *
@@ -13090,7 +13096,8 @@ ${story_1.splitByNameLinks.toString()}
13090
13096
  // 既定なので、タグを忘れると Slack の一覧が Google Chat モーダルに丸ごと出る
13091
13097
  if (liveListWanted("slack")) pushCap("チャット", liveList(
13092
13098
  "チャット",
13093
- function () { return externalsSig("slack", slackWorkspaces()); },
13099
+ // 通知先の栓(#1370)も入れる。行が同じでも、栓が開いた(ライブが届いた)ら行の出し分けを描き直す
13100
+ function () { return externalsSig("slack", [slackWorkspaces(), LIVE_NOTIFY_ANY_WS]); },
13094
13101
  function (box) {
13095
13102
  var items = liveExternals("slack");
13096
13103
  // 注記は**行より前**に置く(0件なら一覧の代わり・行があれば添え書き)
@@ -14322,13 +14329,16 @@ ${story_1.splitByNameLinks.toString()}
14322
14329
  *
14323
14330
  * 書き換わるのは チャット/notify.json だけで、**読み取りの対象(Gold 昇格)は動かない。**
14324
14331
  * 載っている=通知先という決め方の正本は engine の notify-targets.mjs で、
14325
- * **画面が別のフィルタを持たない**のがその規約——出し分けの条件も、あちらが落とす条件
14326
- * (ワークスペースを宣言している=顧客ワークスペース)と同じ1つだけにする。
14332
+ * **画面が別のフィルタを持たない**のがその規約。
14327
14333
  *
14328
- * **顧客ワークスペースの行にはトグルを出さない(#104)。** 読み取り専用の接続なので
14329
- * chat:write が無く、投げれば必ず失敗する(サーバも断る)。押せる形にしておくと
14330
- * 「押したら断られる」だけの操作が並ぶので、代わりに理由をその行に常時出す
14331
- * ——ワークスペースの札の説明は hover しないと読めない。
14334
+ * **ワークスペースを宣言した行(接続したワークスペースのチャンネル)の出し分け(#1370)。**
14335
+ * どのワークスペースかでは分けない。分けるのは 2 つだけ:
14336
+ * - 栓(LIVE_NOTIFY_ANY_WS)が開いていない … [通知先にする]を出さず「準備中」を出す
14337
+ * (夜間の通知がまだ宣言のある宛先へ投稿しないので、押せると「保存できたのに届かない」になる)
14338
+ * - 栓が開いている … 接続の canPost(op=state が台帳から重ねる・ライブだけの値)で
14339
+ * false → 「投稿の許可が無いので通知先にできません」+[ワークスペースを接続し直す]
14340
+ * true・不明 → [通知先にする](不明は押したときにサーバが確かめ、許可が無ければ同じ文と一手をその場に出す)
14341
+ * 理由は行に常時出す——ワークスペースの札の説明は hover しないと読めない。
14332
14342
  *
14333
14343
  * **確認ダイアログは出さない。** ON も OFF も同じ画面から即時に戻せる
14334
14344
  * (取り消せない「登録を解除」とは扱いを分ける)。
@@ -14338,21 +14348,26 @@ ${story_1.splitByNameLinks.toString()}
14338
14348
  */
14339
14349
  function notifyToggle(s, badges) {
14340
14350
  var wrap = el("div", { class: "ds-toggle" });
14341
- if (s.workspace) {
14342
- wrap.appendChild(el("span", {
14343
- class: "ds-toggle-msg",
14344
- text: "通知先にできません(読み取り専用の接続)",
14345
- }));
14351
+ // Slack の行で、ワークスペースを宣言しているか(Google Chat のスペースは workspace を持たない)
14352
+ var wsRow = s.type === "slack" && !!s.workspace;
14353
+ var on = s.notify === true;
14354
+ if (wsRow && !LIVE_NOTIFY_ANY_WS) {
14355
+ wrap.appendChild(el("span", { class: "ds-toggle-msg", text: NOTIFY_WS_PENDING_TEXT }));
14356
+ return wrap;
14357
+ }
14358
+ // 許可が無いと分かっている接続は、押せる形にしない(押しても断られるだけ)。
14359
+ // **通知先のまま許可を失った行は止められるようにする**(止める方向を塞がない)
14360
+ if (wsRow && !on && workspaceCanPost(s.workspace) === false) {
14361
+ notifyNoPostNote(wrap);
14346
14362
  return wrap;
14347
14363
  }
14348
- var on = s.notify === true;
14349
14364
  var labelOf = function () { return on ? "通知を止める" : "通知先にする"; };
14350
14365
  var btn = el("button", { class: "ds-toggle-btn", type: "button", text: labelOf() });
14351
14366
  var msg = el("div", { class: "ds-toggle-msg" });
14352
14367
  btn.addEventListener("click", async function () {
14353
14368
  var next = !on;
14354
- // **ワークスペースは送らない。** この分岐に来るのは宣言の無い行だけで、
14355
- // サーバも ON では受け取らない(送れば断られる)
14369
+ // **ワークスペースは Slack の行で宣言があるときだけ送る(#1370)。** サーバは登録簿の行と照合し、
14370
+ // 書く値も登録簿の行から引き写す(ここで送る値は鍵の照合にだけ使われる)。社内の行・スペースは送らない
14356
14371
  // 行の見た目を「いまの向き」へ揃える。**送信した瞬間に呼ぶ(楽観更新・#968 E)**・成功でも呼ぶ(同じ値で冪等)・
14357
14372
  // 断られたら元の向きで呼んで戻す
14358
14373
  var apply = function (v) {
@@ -14367,11 +14382,22 @@ ${story_1.splitByNameLinks.toString()}
14367
14382
  if (!on && badge) badges.removeChild(badge);
14368
14383
  };
14369
14384
  var before = on;
14370
- await sendConfig({ op: "set-notify", ref: s.ref, value: next }, msg, btn, function () {
14385
+ var payload = { op: "set-notify", ref: s.ref, value: next };
14386
+ if (wsRow) payload.workspace = String(s.workspace);
14387
+ await sendConfig(payload, msg, btn, function () {
14371
14388
  apply(next);
14372
14389
  }, {
14373
14390
  onOptimistic: function () { apply(next); },
14374
14391
  onRevert: function () { apply(before); },
14392
+ // 許可が無いと分かった(不明のまま押した)ときは、同じ文と[接続し直す]をその場に出す
14393
+ onRefused: function (j) {
14394
+ if (!wsRow || !j || j.code !== NOTIFY_WS_NO_POST_CODE) return false;
14395
+ msg.className = "ds-toggle-msg";
14396
+ msg.textContent = "";
14397
+ if (btn.parentNode === wrap) wrap.removeChild(btn);
14398
+ notifyNoPostNote(wrap);
14399
+ return true;
14400
+ },
14375
14401
  });
14376
14402
  });
14377
14403
  wrap.appendChild(btn);
@@ -14379,6 +14405,32 @@ ${story_1.splitByNameLinks.toString()}
14379
14405
  return wrap;
14380
14406
  }
14381
14407
 
14408
+ /** 栓が開くまでの、ワークスペースを宣言した行の文(#1370)。ID や組織の内部事情は書かない */
14409
+ var NOTIFY_WS_PENDING_TEXT = "このワークスペースへの通知は準備中です";
14410
+ /** 投稿の許可が無い接続の文(#1370)。サーバの NOTIFY_WORKSPACE_NO_POST と同じ症状を言う */
14411
+ var NOTIFY_WS_NO_POST_TEXT = "このワークスペースは投稿の許可が無いので通知先にできません。";
14412
+ /** サーバの理由コード(intake の NOTIFY_WORKSPACE_CODES.noPost と同じ綴り) */
14413
+ var NOTIFY_WS_NO_POST_CODE = "NOTIFY_WORKSPACE_NO_POST";
14414
+ /**
14415
+ * 接続(ワークスペース)が投稿できるか。op=state が台帳から重ねる canPost(**ライブだけの値**)を読む。
14416
+ * true / false のどちらでもなければ null(不明)——静的ビルドは build.ts が canPost を通さないので必ず不明
14417
+ */
14418
+ function workspaceCanPost(teamId) {
14419
+ var list = slackWorkspaces() || [];
14420
+ for (var i = 0; i < list.length; i++) {
14421
+ if (list[i] && list[i].teamId === teamId) return typeof list[i].canPost === "boolean" ? list[i].canPost : null;
14422
+ }
14423
+ return null;
14424
+ }
14425
+ /** 「投稿の許可が無い」の文と[ワークスペースを接続し直す](既存の接続の流れ・承認で許可を取り直す) */
14426
+ function notifyNoPostNote(wrap) {
14427
+ if (wrap.querySelector(".ds-notify-nopost")) return;
14428
+ var note = el("span", { class: "ds-toggle-msg ds-notify-nopost", text: NOTIFY_WS_NO_POST_TEXT });
14429
+ wrap.appendChild(note);
14430
+ var act = slackConnectInline("ワークスペースを接続し直す");
14431
+ if (act) wrap.appendChild(act);
14432
+ }
14433
+
14382
14434
  /**
14383
14435
  * Google Chat スペースの登録解除(#153)。removeChannelButton と同型で、
14384
14436
  * 書き換わる先が google-chat-spaces.json・鍵が ref のみ(スペースの ref は
@@ -16713,6 +16765,8 @@ ${story_1.splitByNameLinks.toString()}
16713
16765
  * 断られたときこそ、画面を正しい状態へ追いつかせないと堂々巡りになる
16714
16766
  * onOptimistic … 送信した瞬間に呼ぶ(本当の楽観更新・#968 E)。呼び手が自分で直す画面(通知先の札・入力欄)を先に更新する
16715
16767
  * onRevert … 断られた・通信が落ちた・何も変わらなかったときに呼ぶ。onOptimistic で直したものを元へ戻す
16768
+ * onRefused(j, status) … 断られたとき(重複の確認・「既に登録されています」以外)に、応答(j.code を含む)を渡す。
16769
+ * true を返すと既定の「✗ 理由」を出さない(呼び手が理由に合わせた一手を描いた・#1370)
16716
16770
  *
16717
16771
  * ## 楽観更新(cortex-engine #968 E)
16718
16772
  * 仮置き(notePendingOp)を**送信時**に書き、待ち表示(「保存中…」・「処理中…」)を出さずに一覧を先に更新する。
@@ -16846,6 +16900,11 @@ ${story_1.splitByNameLinks.toString()}
16846
16900
  // サーバの文言をそのまま出す(貼り方の案内・取り違えの警告を含んでいる)。
16847
16901
  // **例外は「他の人が先に変えていた」だけ**——サーバは再読み込みを案内するが、
16848
16902
  // 画面は既に追いついているので、済んでいることをやらせる案内になる(#127)
16903
+ // 理由コード(j.code・#1370)で次の一手を出し分ける呼び手には、先に渡す。true が返れば呼び手が描いた
16904
+ if (hooks && typeof hooks.onRefused === "function" && hooks.onRefused(j, res.status) === true) {
16905
+ endBusy();
16906
+ return;
16907
+ }
16849
16908
  msg.className = "ds-toggle-msg is-error";
16850
16909
  msg.textContent = "✗ " + ((res.status === 409 && raced && hooks.staleNote) || j.message || "保存できませんでした。");
16851
16910
  endBusy();
@@ -17422,7 +17481,7 @@ ${story_1.splitByNameLinks.toString()}
17422
17481
  badges.appendChild(el("span", {
17423
17482
  class: "badge ds-workspace",
17424
17483
  text: workspaceLabel(s.workspace),
17425
- title: "接続した Slack ワークスペースのチャンネルです(読み取り専用の接続なので、通知は届きません)。",
17484
+ title: "接続した Slack ワークスペースのチャンネルです。",
17426
17485
  }));
17427
17486
  } else if (s.orgUnknown === true) {
17428
17487
  badges.appendChild(el("span", {
@@ -17525,7 +17584,7 @@ ${story_1.splitByNameLinks.toString()}
17525
17584
  card.appendChild(removeChannelButton(s, card));
17526
17585
  // 通知先の入り切り。**解除と同じ条件で出す**——書き換える先は違う(notify.json)が、
17527
17586
  // どちらも「チャット/channels.json に登録がある行」でしか効かない。
17528
- // 仮置きの行(#388)には出さない——ワークスペースが未確定で、顧客ワークスペースなら
17587
+ // 仮置きの行(#388)には出さない——ワークスペースが未確定で、鍵(ワークスペース込み)が決まらない
17529
17588
  // 押しても断られる(本物の行が来てから)
17530
17589
  if (s.pending !== true) card.appendChild(notifyToggle(s, badges));
17531
17590
  }
@@ -17537,7 +17596,7 @@ ${story_1.splitByNameLinks.toString()}
17537
17596
  card.appendChild(removeSpaceButton(s, card));
17538
17597
  // 通知先の入り切り(#572)。Slack と同じ notifyToggle・同じ文言・同じ op(サーバは
17539
17598
  // ref の形(spaces/…)で Google Chat と判定する)。スペースの行に workspace は無いので
17540
- // 「読み取り専用の接続」の分岐には入らず、常にボタンが出る。仮置きの行(本物の行が
17599
+ // ワークスペースの出し分け(栓・投稿の許可)には入らず、常にボタンが出る。仮置きの行(本物の行が
17541
17600
  // 来るまで)には Slack と同じく出さない
17542
17601
  if (s.pending !== true) card.appendChild(notifyToggle(s, badges));
17543
17602
  }
@@ -18033,6 +18092,8 @@ ${story_1.splitByNameLinks.toString()}
18033
18092
  // ここより後に置くと**その案件だけ素通り**する
18034
18093
  // 追加の確認の栓と確認済みの一覧(#1357)。fleet より先に読む(立ち上げ直後の案件でも追加欄を作り替える)
18035
18094
  googleInfoApply(j && j.googleConfirm);
18095
+ // 通知先の栓(#1370)。fleet より先に読む(立ち上げ直後の案件でも行の出し分けが追いつく)。厳密比較
18096
+ LIVE_NOTIFY_ANY_WS = !!(j && j.notifyAnyWorkspace === true);
18036
18097
  LIVE_REGISTERED = sanitizeRegisteredNow(j && j.registeredNow);
18037
18098
  LIVE_REGISTERED_AT = startedAt;
18038
18099
  LIVE_STATE = sanitizeStateNow(j && j.stateNow);
@@ -18519,7 +18580,7 @@ ${story_1.splitByNameLinks.toString()}
18519
18580
  shots: [
18520
18581
  { src: GITHUB_SHOT_STEP1, width: "960", height: "540", heading: "1. GitHub でリポジトリを開き、URL をコピー", alt: "手順 1。GitHub のリポジトリのページ。上部の組織名とリポジトリ名(このページの URL)が赤枠で示されている。" },
18521
18582
  { src: GITHUB_SHOT_STEP2, width: "960", height: "540", heading: "2. Cortex の欄に URL を貼って追加", alt: "手順 2。Cortex の GitHub のモーダル。「GitHubリポジトリを追加」の欄と[追加]が赤枠で示されている。" },
18522
- { src: GITHUB_SHOT_STEP3, width: "960", height: "540", heading: "3. 「アクセス不可」と出たらインストール設定を開く", alt: "手順 3。リポジトリの行に「アクセス不可」と「リンク先でこのリポジトリを選んで、Cortex の GitHub App を入れてください。インストール設定を開く」が出ている。" },
18583
+ { src: GITHUB_SHOT_STEP3, width: "960", height: "540", heading: "3. 「アクセス不可」と出たら GitHub App を入れる", alt: "手順 3。リポジトリの行に「アクセス不可」と「リンク先でこのリポジトリを選んで、Cortex の GitHub App を入れてください。GitHub App を入れる →」が出ていて、リンクが赤枠で示されている。" },
18523
18584
  { src: GITHUB_SHOT_STEP4, width: "960", height: "540", heading: "4. GitHub で自分のアカウントの Continue", alt: "手順 4。GitHub のアカウントを選ぶ画面で、Continue のボタンが赤枠で示されている。" },
18524
18585
  { src: GITHUB_SHOT_STEP5, width: "960", height: "540", heading: "5. リポジトリのある組織を選ぶ", alt: "手順 5。App を入れる先の一覧で、リポジトリのある組織の行が赤枠で示されている。" },
18525
18586
  { src: GITHUB_SHOT_STEP6, width: "960", height: "540", heading: "6. リポジトリを検索して選ぶ", alt: "手順 6。Select repositories の検索欄と候補のリポジトリが赤枠で示されている。" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.192",
3
+ "version": "0.12.194",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,