@takagaki/cortex-decisions-viewer 0.12.53 → 0.12.55
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 +236 -113
- package/package.json +2 -2
package/dist/render.js
CHANGED
|
@@ -2169,19 +2169,17 @@ function calendarStateRow(rows) {
|
|
|
2169
2169
|
* 接続状態ストアの行から、案件カレンダーの ID を引く(#428・#452)。
|
|
2170
2170
|
*
|
|
2171
2171
|
* 🔴 **ID は「最新の行」ではなく「ID を持つ最新の行」から引く。** 状態を運ぶ行
|
|
2172
|
-
* (`calendarStateRow
|
|
2173
|
-
*
|
|
2174
|
-
*
|
|
2175
|
-
*
|
|
2172
|
+
* (`calendarStateRow`)と別に引く理由は、**カレンダーが出来ている案件にも
|
|
2173
|
+
* `requested` の行が立つ**から——受け口(`?op=calendar-request`)は共有の是正にも
|
|
2174
|
+
* 使われ、裏の経路が番兵(`ref = "calendar"`)の `requested` を書いてそれが最新になる。
|
|
2175
|
+
* 最新の1行だけから ID を引くと、**既にあるカレンダーのリンクとコピー欄が
|
|
2176
|
+
* 最長20分ぶん画面から消え**、さらに待つと「作成が終わっていません」まで出る。
|
|
2176
2177
|
* **一度できたカレンダーは消えない**ので、ID は落とさないのが正しい。
|
|
2177
2178
|
*
|
|
2178
|
-
*
|
|
2179
|
-
*
|
|
2180
|
-
*
|
|
2181
|
-
*
|
|
2182
|
-
* intake は**カレンダーが出来ている案件の依頼にだけ ID を載せる**(#427)。
|
|
2183
|
-
* 判定を2枚に割ると片方だけ直り、**一括作成の直後に「共有を更新しています」**が
|
|
2184
|
-
* 出る形へ静かに戻る。
|
|
2179
|
+
* ⚠ かつては `calendarPhase` が**「作成の依頼が処理中」と「共有の更新が処理中」を
|
|
2180
|
+
* 分ける唯一の材料**としてもこれを呼んでいた(#452)。その出し分けは
|
|
2181
|
+
* **#544 で画面から「共有を更新」を外したときに無くなった**(出す先が消えたため)。
|
|
2182
|
+
* ここが残っているのは ID を落とさないためだけ。
|
|
2185
2183
|
*
|
|
2186
2184
|
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
2187
2185
|
*/
|
|
@@ -2267,8 +2265,8 @@ function calendarRowCount(raw) {
|
|
|
2267
2265
|
* - `none` … まだ無い(行が 1 本も無い)→ 「この案件のカレンダーを作る」
|
|
2268
2266
|
* - `pending` … 依頼が立っている → 「作成中(最長 N 分)」
|
|
2269
2267
|
* - `stale` … 約束の 3 倍を過ぎた → 「作成が終わっていません(依頼は届いています)」
|
|
2270
|
-
* - `ready` … ID がある → 手順の画像/Google カレンダーに追加/ID
|
|
2271
|
-
* この案件のミーティングを作成(#452
|
|
2268
|
+
* - `ready` … ID がある → 手順の画像/Google カレンダーに追加/ID のコピー/
|
|
2269
|
+
* この案件のミーティングを作成(#452 の並び。「共有を更新」は #544 で外した)
|
|
2272
2270
|
* - `failed` … 作れなかった → 理由の1行+もう一度
|
|
2273
2271
|
*
|
|
2274
2272
|
* 🔴 **`none` は「行が 1 本も無い」ときだけ。** 契約(裏の経路 #427)は
|
|
@@ -2302,28 +2300,18 @@ function calendarPhase(s, rows, ctx, nowMs) {
|
|
|
2302
2300
|
var unclear = { phase: "unclear", calendarId: "", detail: "", note: "", at: at };
|
|
2303
2301
|
// ---- ID があれば必ず ready(最新の行が何であっても降ろさない) ----
|
|
2304
2302
|
if (id) {
|
|
2305
|
-
//
|
|
2303
|
+
// 失敗は**添える1行**として渡す。「作れませんでした」とは言わない
|
|
2306
2304
|
//(出来ているので嘘になり、押し直すと 2 本目が作られる)
|
|
2307
2305
|
//
|
|
2308
|
-
// 🔴
|
|
2309
|
-
//
|
|
2310
|
-
//
|
|
2311
|
-
//
|
|
2312
|
-
//
|
|
2313
|
-
//
|
|
2314
|
-
//
|
|
2315
|
-
// `existing.calendarId` を返すため)
|
|
2316
|
-
// - 一括作成がまだ閉じていない案件は `calendarId` が空なので**番兵のまま**で、
|
|
2317
|
-
// ID は夜間の集計(fleet)からしか引けない
|
|
2318
|
-
// ⚠ 行は「増える」のではなく**同じ SK に上書き**される(接続状態ストアの SK は `ref` を
|
|
2319
|
-
// 含むので、番兵の行と ID の行は別の SK)。だから「作成時の行が残っているから分かる」
|
|
2320
|
-
// ではなく、**押した依頼そのものが ID を載せて書かれるから分かる**。
|
|
2321
|
-
// ここを見ないと、**一括作成の直後に「共有を更新しています(最長 20 分)。」が出る**
|
|
2322
|
-
//(#452 の実機確認)。押してもいない人には、できることの無い1行になる。
|
|
2306
|
+
// 🔴 **`requested`(依頼が立っている)は note にしない(#544)。**
|
|
2307
|
+
// かつては「共有を更新しています(最長 20 分)。」を出すための材料で、
|
|
2308
|
+
// 「押した人にだけ出す」ために **その行が ID を載せているか**を見ていた(#452)。
|
|
2309
|
+
// **その画面のボタンは #544 で外した**ので、出す先が無い——残すと
|
|
2310
|
+
// 「誰も読まない値を計算し続ける」ことになり、次に触る人が使えると誤解する。
|
|
2311
|
+
// ⚠ **失敗の枝は残す。** これは「共有を更新」由来とは限らず、
|
|
2312
|
+
// **作成の書き戻し失敗**(カレンダーは出来ているのに報告が書けなかった回)でも立つ
|
|
2323
2313
|
var note = "";
|
|
2324
|
-
if (
|
|
2325
|
-
note = "requested";
|
|
2326
|
-
else if ((BAD_STATE_STATUSES.calendar || []).indexOf(status) >= 0)
|
|
2314
|
+
if ((BAD_STATE_STATUSES.calendar || []).indexOf(status) >= 0)
|
|
2327
2315
|
note = detail || "failed";
|
|
2328
2316
|
return { phase: "ready", calendarId: id, detail: "", note: note, at: at };
|
|
2329
2317
|
}
|
|
@@ -3975,7 +3963,10 @@ a.ds-tile:hover { text-decoration: none; }
|
|
|
3975
3963
|
.ds-probe-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
3976
3964
|
/* いつ測った結果かを必ず添える。夜間のスナップショットと混ざると、どちらの話か読めない */
|
|
3977
3965
|
.ds-probe-at { font-size: 11.5px; color: var(--muted); }
|
|
3978
|
-
|
|
3966
|
+
/* flex: 1 1 100% は #548 で足した——「接続を確認」の即答は、この段を .ds-probe の中では
|
|
3967
|
+
なく**行(.ds-card)に直接**置く。付けないと ID と「外す」の横に押し込まれて折り返す
|
|
3968
|
+
(.ds-gate-help と同じ理由)。.ds-probe の中(display: block)では効かない */
|
|
3969
|
+
.ds-probe-help { flex: 1 1 100%; margin: 6px 0 0; font-size: 12px; line-height: 1.7; color: #6b7280;
|
|
3979
3970
|
min-width: 0; white-space: normal; overflow-wrap: anywhere; }
|
|
3980
3971
|
/* 「アクセス不可」の原因の候補(0.12.2)。**箇条書きにする**——候補が3つあるので、
|
|
3981
3972
|
一続きの文章にすると読む人が「自分がどれなのか」を自分で切り出すことになる
|
|
@@ -4098,7 +4089,8 @@ a.ds-tile:hover { text-decoration: none; }
|
|
|
4098
4089
|
見分けられなかったので、下の2規則(主ボタンと控えめな文字リンク)に格を分けてある */
|
|
4099
4090
|
/* 必ず 1 回押す主ボタン(#452)。見た目は .ds-toggle-btn から借りるので、ここは
|
|
4100
4091
|
リンク固有の分だけ——値を写すと色と枠が 2 枚になる。**太さも足さない**
|
|
4101
|
-
|
|
4092
|
+
(借り元の .ds-toggle-btn が既定の太さなので、ここだけ太いと「同じ見た目」から外れる。
|
|
4093
|
+
隣に並んでいた「共有を更新」は #544 で外した) */
|
|
4102
4094
|
.mt-cal-primary { display: inline-block; margin: 8px 0 0; text-decoration: none; }
|
|
4103
4095
|
/* 素の a:hover が下線を引く。**ボタンの見た目に下線が出ると壊れて見える**ので打ち消す。
|
|
4104
4096
|
⚠ この CSS はテンプレート文字列の中なので、コメントにバッククォートを書かないこと */
|
|
@@ -4543,10 +4535,9 @@ ${deriveCapTile.toString()}
|
|
|
4543
4535
|
// (語を2枚持たないため)ので、宣言の前に呼ぶと undefined を読む
|
|
4544
4536
|
${isCalendarId.toString()}
|
|
4545
4537
|
${calendarStateRow.toString()}
|
|
4546
|
-
// calendarIdOf
|
|
4547
|
-
//
|
|
4548
|
-
//
|
|
4549
|
-
// 順序そのものは効かない
|
|
4538
|
+
// calendarIdOf が呼ぶ(#452。#544 で calendarPhase 側の呼び出しは無くなった)。
|
|
4539
|
+
// **読みやすさのために呼ぶ側より前に置いてあるだけ**——関数宣言は巻き上がるので、
|
|
4540
|
+
// 上の BAD_STATE_STATUSES(var)と違って順序そのものは効かない
|
|
4550
4541
|
${calendarIdFromRows.toString()}
|
|
4551
4542
|
${calendarIdOf.toString()}
|
|
4552
4543
|
${calendarRowCount.toString()}
|
|
@@ -6629,18 +6620,48 @@ ${calendarAddUrl.toString()}
|
|
|
6629
6620
|
});
|
|
6630
6621
|
return p;
|
|
6631
6622
|
}
|
|
6623
|
+
/**
|
|
6624
|
+
* ボタンの detail が「機械語の痕跡」でしかない能力(#548・設計v2 🔴2)。
|
|
6625
|
+
*
|
|
6626
|
+
* この3つは中央が艦隊の鍵で叩く経路(gate)で、押した回の detail には判定の語そのもの
|
|
6627
|
+
* ("unreachable")や生のエラー("ratelimited")が入る。**それ以外の能力
|
|
6628
|
+
* (design / materials / issues)のボタンは、engine の取り込みと同じ判定器の
|
|
6629
|
+
* 同じ1文を書く**ので、出さないと「読めません」の理由が押した人に届かない。
|
|
6630
|
+
*/
|
|
6631
|
+
var GATE_BUTTON_CAPS = ["chat", "dev", "google-chat"];
|
|
6632
|
+
/**
|
|
6633
|
+
* 外部ソースの種別 → 接続状態ストアの能力 id。**上の除外を引くためだけに使う。**
|
|
6634
|
+
* 知らない種別は空文字(=除外に当たらない=理由を出す側に倒す)。
|
|
6635
|
+
*/
|
|
6636
|
+
function gateCapOfType(type) {
|
|
6637
|
+
var t = String(type || "");
|
|
6638
|
+
if (t === "slack") return "chat";
|
|
6639
|
+
if (t === "google-chat") return "google-chat";
|
|
6640
|
+
if (t.indexOf("github") === 0) return "dev";
|
|
6641
|
+
return "";
|
|
6642
|
+
}
|
|
6632
6643
|
/**
|
|
6633
6644
|
* 休止状態の行に出す「最後に測った理由」(表にエントリが無い状態のフォールバック)。
|
|
6634
6645
|
* needed(次の一手・URL 込み)を優先し、無ければ detail。
|
|
6635
6646
|
*
|
|
6636
|
-
* 🔴
|
|
6637
|
-
*
|
|
6638
|
-
*
|
|
6639
|
-
* needed の側。夜間(gold)の detail
|
|
6647
|
+
* 🔴 **gate 経路の書き手が button の detail は出さない。** Slack / GitHub / Google Chat の
|
|
6648
|
+
* ボタンは detail に判定の語そのもの("unreachable")や生のエラー("ratelimited")を
|
|
6649
|
+
* 書く——lastFailDetail を空で潰さないための痕跡で、表示用の値ではない(intake の
|
|
6650
|
+
* op=probe のコメント)。人向けの文を持つのは needed の側。夜間(gold)の detail は
|
|
6651
|
+
* 人向けの文なので出す。
|
|
6652
|
+
*
|
|
6653
|
+
* 🔴 **除外は能力で絞る(#548)。** Figma / Drive / Backlog のボタンは中央がその場で
|
|
6654
|
+
* 案件の鍵で叩いて、engine の取り込みと同じ判定器の同じ1文を detail に書く。
|
|
6655
|
+
* 「button なら出さない」と一律にすると、**押した人にだけ理由が届かない**
|
|
6656
|
+
* (夜間まで待てば同じ文が出る、という逆立ちした画面になる)。
|
|
6657
|
+
*
|
|
6658
|
+
* @param s needed / detail / gateBy に加えて **cap(接続状態ストアの能力 id)** を渡す。
|
|
6659
|
+
* cap を渡さない呼び方は「除外に当たらない」=理由を出す側に倒れる
|
|
6640
6660
|
*/
|
|
6641
6661
|
function stateReasonOf(s) {
|
|
6642
6662
|
if (s.needed) return String(s.needed);
|
|
6643
|
-
|
|
6663
|
+
var hidden = s.gateBy === "button" && GATE_BUTTON_CAPS.indexOf(String(s.cap || "")) >= 0;
|
|
6664
|
+
if (s.detail && !hidden) return String(s.detail);
|
|
6644
6665
|
return "";
|
|
6645
6666
|
}
|
|
6646
6667
|
/**
|
|
@@ -6988,23 +7009,9 @@ ${calendarAddUrl.toString()}
|
|
|
6988
7009
|
function calendarCanRequest() { return !CALENDAR_DENIED && !!actorTicket(); }
|
|
6989
7010
|
|
|
6990
7011
|
/**
|
|
6991
|
-
* この画面で「共有を更新」を押したか(#452)。
|
|
6992
|
-
*
|
|
6993
|
-
* 🔴 **押した手応えを消さないために要る。** 「共有を更新しています(最長 N 分)。」は
|
|
6994
|
-
* ストアの行が ID を載せているときだけ出す(calendarPhase の項)が、**一括作成が
|
|
6995
|
-
* まだ閉じていない案件では、押しても番兵の行しか書かれない**。押すと行の時刻が変わって
|
|
6996
|
-
* meetingCalendarSig が変わり、liveList が箱を作り直す——ボタン脇の
|
|
6997
|
-
* 「✓ 依頼しました」ごと消えるので、**押した人の画面が押す前と 1 文字も変わらない**。
|
|
6998
|
-
*
|
|
6999
|
-
* 判定を 2 枚にはしていない(calendarPhase は純関数のまま)。ここが持っているのは
|
|
7000
|
-
* 「サーバが何を知っているか」ではなく **「この人が押したか」**で、これは
|
|
7001
|
-
* サーバの行から復元できない——CALENDAR_DENIED を画面が覚えているのと同じ事情。
|
|
7002
|
-
* 画面を開き直せば消える(そのときは行が答えを持っている)。
|
|
7003
|
-
*
|
|
7004
7012
|
* ⚠ ここから下は CLIENT_JS のテンプレート文字列の中。**コメントにバッククォートを
|
|
7005
7013
|
* 書かないこと**(文字列がそこで終わり、ビルドが丸ごと壊れる)。
|
|
7006
7014
|
*/
|
|
7007
|
-
var CALENDAR_SHARE_PRESSED = false;
|
|
7008
7015
|
|
|
7009
7016
|
/**
|
|
7010
7017
|
* 案件カレンダーの行(#428・設計は #418 の §6)。
|
|
@@ -7088,26 +7095,20 @@ ${calendarAddUrl.toString()}
|
|
|
7088
7095
|
// 色は**見る人ごとの設定**で、仕組みでは揃えられない(#418 の調査4)。
|
|
7089
7096
|
// 質問にはせず、事実として1行だけ置く(オーナー決定 A-5: 画面に計器は出さない)
|
|
7090
7097
|
box.appendChild(el("p", { class: "mt-cal-note", text: "色はご自身のカレンダーで変えられます。" }));
|
|
7091
|
-
// 🔴
|
|
7092
|
-
//
|
|
7093
|
-
//
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
box.appendChild(el("p", { class: "mt-cal-note", text: "案件にメンバーを足したら押してください。その人もこのカレンダーを見られるようになります(1 時間ごとに自動でも更新されます)" }));
|
|
7098
|
-
}
|
|
7099
|
-
// 作成済みのまま進行中・失敗の報告が最新なら、その1行だけ添える。
|
|
7100
|
-
// **「作れませんでした」とは言わない**(出来ているので嘘になり、押し直すと 2 本目が作られる)
|
|
7098
|
+
// 🔴 **「共有を更新」のボタンと説明はここに在った(#452)。#544 で外した。**
|
|
7099
|
+
// ポータル側が**メンバーの増減のたびに**共有を直させるようになったので、
|
|
7100
|
+
// 押す理由が無くなった(取りこぼしは毎時の便が最長 1 時間で埋める)。
|
|
7101
|
+
// ⚠ 押す場所が無い画面に「共有を更新しています」「終わっていません」だけを出さない
|
|
7102
|
+
// ——利用者にできることが無い 1 行になる。**受け口(?op=calendar-request)は残っている**
|
|
7103
|
+
// ので、運用が直接叩いて是正できる(docs/calendar-provisioning-runbook.md)。
|
|
7101
7104
|
//
|
|
7102
|
-
//
|
|
7103
|
-
//
|
|
7104
|
-
//
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
} else if (cal.note) {
|
|
7105
|
+
// 作成済みのまま失敗の報告が最新なら、その1行だけ添える。
|
|
7106
|
+
// ⚠ **これは「共有を更新」由来とは限らない**(作成の書き戻し失敗でも立つ。#452)ので、
|
|
7107
|
+
// ボタンと一緒に消さない。**「作れませんでした」とは言わない**
|
|
7108
|
+
//(出来ているので嘘になり、押し直すと 2 本目が作られる)
|
|
7109
|
+
if (cal.note) {
|
|
7108
7110
|
box.appendChild(el("p", { class: "mt-cal-warn", text: "共有の更新が終わっていません(" + cal.note + ")。" }));
|
|
7109
7111
|
}
|
|
7110
|
-
box.appendChild(calendarRequestButton("共有を更新", "share"));
|
|
7111
7112
|
// 🔴 **主ボタンと格を分ける(#452)。** 同じ大きさ・同じ色で2本並ぶと、
|
|
7112
7113
|
// 必ず押すもの(Google カレンダーに追加)がどれか分からない。予定を作るのは
|
|
7113
7114
|
// たまにしかしないので、一番下に小さく薄い文字リンクで出す
|
|
@@ -7154,10 +7155,11 @@ ${calendarAddUrl.toString()}
|
|
|
7154
7155
|
}
|
|
7155
7156
|
|
|
7156
7157
|
/**
|
|
7157
|
-
* 依頼を出すボタン。**
|
|
7158
|
+
* 依頼を出すボタン。**2つ(作る・もう一度)とも同じ1本の経路**で、
|
|
7158
7159
|
* 押した直後の見え方だけが違う(裏の経路は冪等・#427)。
|
|
7160
|
+
* ⚠ 3 つ目の「共有を更新」は #544 で外した(受け口はそのまま残っている)。
|
|
7159
7161
|
*
|
|
7160
|
-
* **確認を出すのは「作る」だけ。**
|
|
7162
|
+
* **確認を出すのは「作る」だけ。** 「もう一度」は取り返しがつく
|
|
7161
7163
|
* (同じ依頼が立ち直るだけ)ので、確認を挟むと押す手が増えるだけになる。
|
|
7162
7164
|
*
|
|
7163
7165
|
* 🔴 **本文は空**(projectKey だけ)。共有先のメールをブラウザから受け取らない——
|
|
@@ -7210,9 +7212,6 @@ ${calendarAddUrl.toString()}
|
|
|
7210
7212
|
// 待ちの言い方は行から描く 1 行(pending)と揃える(#540)。
|
|
7211
7213
|
// 片方だけ「最長 20 分」だと、同じ待ちが 2 通りに見える
|
|
7212
7214
|
msg.textContent = "✓ 依頼しました。作成中です(数分〜十数分)。";
|
|
7213
|
-
// **押したことを覚える(#452)。** 下の取り直しで箱ごと作り直されると
|
|
7214
|
-
// この 1 行は消えるので、消えない側(行から描く 1 行)に引き継ぐ
|
|
7215
|
-
if (kind === "share") CALENDAR_SHARE_PRESSED = true;
|
|
7216
7215
|
// pending-ops(#388)は使わない。**あれは registeredNow のキーで live 判定する
|
|
7217
7216
|
// 仕組みで、カレンダーは stateNow 由来**。fresh で取り直せば「作成中」が出る
|
|
7218
7217
|
loadLiveFleet(true);
|
|
@@ -10913,22 +10912,26 @@ ${calendarAddUrl.toString()}
|
|
|
10913
10912
|
return row;
|
|
10914
10913
|
}
|
|
10915
10914
|
if (r.status === "ok" || r.status === "no_token" || r.status === "missing_scope" || r.status === "not_in_channel") return row;
|
|
10916
|
-
|
|
10915
|
+
// **cap を渡す(#548)。** この3能力(design / materials / issues)のボタンは
|
|
10916
|
+
// 判定器の1文を detail に書くので、gate 経路の除外に当てない(stateReasonOf の項)
|
|
10917
|
+
var reason = stateReasonOf({ needed: r.needed, detail: r.detail, gateBy: r.by, cap: capId });
|
|
10917
10918
|
// needed は Slack の行と同じ部品を通す(URL がリンクになる・スコープ名がラベル化される)
|
|
10918
10919
|
if (reason) row.appendChild(r.needed ? neededNode(reason, "ds-gate-help") : el("p", { class: "ds-gate-help", text: reason }));
|
|
10919
10920
|
return row;
|
|
10920
10921
|
}
|
|
10921
10922
|
|
|
10922
10923
|
/* -------------------------------------------------------------------------
|
|
10923
|
-
*
|
|
10924
|
+
* 「接続を確認」と「今すぐ同期」(#523 S3・#548・Backlog / Drive / Figma)
|
|
10924
10925
|
* -------------------------------------------------------------------------
|
|
10925
|
-
*
|
|
10926
|
-
*
|
|
10927
|
-
*
|
|
10926
|
+
* #548 までは、この3つは中央が鍵を読まない(#105 決定5)ので、押すとその案件の同期が
|
|
10927
|
+
* 1回起きるだけだった。オーナーがその境界を覆し、いまは「接続を確認」は Slack / GitHub と
|
|
10928
|
+
* 同じく**中央がその場で叩いて答えを返す**(即答・「showProbeVerdict」)。同期を1回
|
|
10929
|
+
* 起こす従来の動きは「今すぐ同期」(送る値に 「sync: true」)に移った。
|
|
10928
10930
|
*
|
|
10929
|
-
*
|
|
10931
|
+
* 待ちの仕組み(下)は「今すぐ同期」と、Backlog が IP 制限で叩けず夜間と同じ経路へ
|
|
10932
|
+
* 倒れた回のために残る:
|
|
10930
10933
|
* 1. 押す前にその行の 「at」 を控える(🔴2・壁時計を使わない)
|
|
10931
|
-
* 2.
|
|
10934
|
+
* 2. 「同期を開始しました」を出して、取り直し(op=state)を回す
|
|
10932
10935
|
* 3. 控えた 「at」 より新しい行が届いたら、待ちを解いて札を描き直す
|
|
10933
10936
|
* ——札そのものは 「appendReachableChip」 が描くので、ここは待ちの表示だけ
|
|
10934
10937
|
* ----------------------------------------------------------------------- */
|
|
@@ -11006,19 +11009,105 @@ ${calendarAddUrl.toString()}
|
|
|
11006
11009
|
}
|
|
11007
11010
|
|
|
11008
11011
|
/**
|
|
11009
|
-
*
|
|
11012
|
+
* 断られた回の後始末。**待ちに入れない**(出ない結果を15分待たせない)。
|
|
11013
|
+
* 文言はサーバのものをそのまま出す(回数制限・鍵未登録・未登録の接続先・
|
|
11014
|
+
* 登録簿が読めない・ロール未登録・起動失敗が、全部ここへ来る)。
|
|
11015
|
+
*/
|
|
11016
|
+
function probeDeclined(note, key, j) {
|
|
11017
|
+
note.textContent = (j && j.message) || "確認できませんでした。";
|
|
11018
|
+
delete PROBE_WAITS[key];
|
|
11019
|
+
}
|
|
11020
|
+
|
|
11021
|
+
/**
|
|
11022
|
+
* 従来の待ちに入る(同期が1回起きて、答えは接続状態ストアの行として後から届く)。
|
|
11023
|
+
* 通るのは2つ——「今すぐ同期」と、Backlog が IP 制限でこの場では叩けず
|
|
11024
|
+
* **夜間と同じ経路へ倒れた回**(#548)。
|
|
11025
|
+
*
|
|
11026
|
+
* 🔴 **控えた 「at」 で待つ(🔴2・壁時計を使わない)。** 押した時刻で見ると、
|
|
11027
|
+
* 端末の時計がずれている人には永久に届かない(または届く前に届いたことになる)。
|
|
11028
|
+
*/
|
|
11029
|
+
function probeEnterWait(note, key, capId, ref, before, j) {
|
|
11030
|
+
PROBE_WAITS[key] = {
|
|
11031
|
+
cap: capId, ref: ref,
|
|
11032
|
+
at: (before && before.at) || "",
|
|
11033
|
+
startedAt: Date.now(),
|
|
11034
|
+
message: (j && j.message) || "",
|
|
11035
|
+
};
|
|
11036
|
+
note.textContent = (j && j.message) || "";
|
|
11037
|
+
// 待っているあいだは取り直しを回す(届いた行で一覧が描き直される)
|
|
11038
|
+
refreshStateTimer();
|
|
11039
|
+
}
|
|
11040
|
+
|
|
11041
|
+
/**
|
|
11042
|
+
* 即答(#548 の direct)を行に映す。**「probeSection」 と同じ見た目にする**——
|
|
11043
|
+
* 夜間のスナップショットの判定といま測った判定を**同じ行に並べない**ため
|
|
11044
|
+
* (並べると「⚠️ と 接続OK が同時に出ている」になり、この節で何度も直してきた
|
|
11045
|
+
* 食い違いを画面の側から作ることになる)。置き換えたことが分かるよう、
|
|
11046
|
+
* いつ測ったかを必ず添える。
|
|
11047
|
+
*
|
|
11048
|
+
* 🔴 **札が無い行もある**(一度も測っていない接続先= 「appendReachableChip」 が
|
|
11049
|
+
* 何も足していない行)。その場合は札を作って**ボタンの前**に差し込む——後ろに置くと
|
|
11050
|
+
* 行の読み順(札 → ボタン)が崩れる。
|
|
11051
|
+
*
|
|
11052
|
+
* @param anchor 差し込む位置の目印(1つ目のボタン)
|
|
11053
|
+
*/
|
|
11054
|
+
function showProbeVerdict(row, capId, anchorIn, j) {
|
|
11055
|
+
var gate = dsGateForCap(capId, j.gate);
|
|
11056
|
+
// 差し込む位置は**行の最初のボタン**の前(「外す」が出る行では「外す」の前)。
|
|
11057
|
+
// 「接続を確認」の前に置くと、外せる行だけ「外す → 札 → ボタン」の並びになる(🟡10)
|
|
11058
|
+
var anchor = anchorIn;
|
|
11059
|
+
var btns = row.querySelectorAll(".ds-toggle-btn");
|
|
11060
|
+
for (var bi = 0; bi < btns.length; bi++) {
|
|
11061
|
+
// **行の直下にあるボタンだけ**が目印になれる(入れ子の中のボタンの前には差し込めない)
|
|
11062
|
+
if (btns[bi].parentNode === row) { anchor = btns[bi]; break; }
|
|
11063
|
+
}
|
|
11064
|
+
var chip = row.querySelector(".ds-gate");
|
|
11065
|
+
if (!chip) {
|
|
11066
|
+
chip = el("span", { class: "ds-gate" });
|
|
11067
|
+
row.insertBefore(chip, anchor);
|
|
11068
|
+
}
|
|
11069
|
+
chip.className = "ds-gate " + gate.cls;
|
|
11070
|
+
chip.textContent = gate.text;
|
|
11071
|
+
var at = row.querySelector(".ds-gate-at");
|
|
11072
|
+
if (!at) {
|
|
11073
|
+
at = el("span", { class: "ds-gate-at" });
|
|
11074
|
+
row.insertBefore(at, anchor);
|
|
11075
|
+
}
|
|
11076
|
+
at.textContent = "いま測った結果(" + (dsFmtTime(j.checkedAt) || "たった今") + ")";
|
|
11077
|
+
// **夜間の判定に添えてあった案内は隠す。** 置き換えた判定と食い違う案内が下に残ると、
|
|
11078
|
+
// 同じ行が2つのことを言う(probeSection の stale 処理と同じ)
|
|
11079
|
+
var stale = row.querySelectorAll(".ds-gate-help");
|
|
11080
|
+
for (var si = 0; si < stale.length; si++) stale[si].hidden = true;
|
|
11081
|
+
// **押し直しで段が増えないよう、前回の結果は消してから出す**(probeSection の札と同じ)
|
|
11082
|
+
var old = row.querySelectorAll(".ds-probe-help");
|
|
11083
|
+
for (var oi = 0; oi < old.length; oi++) old[oi].remove();
|
|
11084
|
+
// 次の一手は語の表(DS_GATE_CAP)が持っていればそれ。無ければ判定器の1文(detail)
|
|
11085
|
+
// ——どちらも**1段だけ**(帯は症状・行は一手・繰り返さない)
|
|
11086
|
+
var nodes = gateHelpNodes(gate, "ds-probe-help");
|
|
11087
|
+
if (!nodes.length && j.detail) nodes = [el("p", { class: "ds-probe-help", text: String(j.detail) })];
|
|
11088
|
+
nodes.forEach(function (n) { row.appendChild(n); });
|
|
11089
|
+
}
|
|
11090
|
+
|
|
11091
|
+
/**
|
|
11092
|
+
* 接続先1件の「接続を確認」「今すぐ同期」(2ボタン+その場の返事1文)を行に足す。
|
|
11093
|
+
*
|
|
11094
|
+
* **なぜ2つに割れたか(#548)。** 中央が案件の鍵で1回だけ叩けるようになった
|
|
11095
|
+
* (オーナーが #105 決定5 を覆した)ので、「接続を確認」は**その場で答えが返る**
|
|
11096
|
+
* ——押した人を数分〜十数分待たせない。一方「取り込みをやり直したい」は別の用事なので、
|
|
11097
|
+
* 同期を1回起こす従来の動きは「今すぐ同期」として残す(送る値は 「sync: true」)。
|
|
11010
11098
|
*
|
|
11011
11099
|
* **ボタンは行の中**(Slack の行と同じ・2026-08-20 の「1行が2段になる」を繰り返さない)。
|
|
11012
|
-
*
|
|
11100
|
+
* 🔴 **その場の返事(「.ds-probe-wait」)は2つのボタンで1つ・行の最後。** 2つ持つと
|
|
11101
|
+
* 行が2段になり、片方に古い返事が残る。「ds-gate-help」 は付けない——あれは
|
|
11102
|
+
* 「最後に測った理由」の1段で、行の補足が1段であることを見張る検査(#523 S1)が
|
|
11103
|
+
* 数えている。押した人へのその場の返事は別の話なので、専用の class を持たせる。
|
|
11013
11104
|
*/
|
|
11014
11105
|
function appendDispatchProbe(row, capId, ref) {
|
|
11015
11106
|
var type = dispatchProbeTypeOf(capId);
|
|
11016
11107
|
// **書き込み経路が無ければ出さない**(押しても何も起きないボタンを置かない)
|
|
11017
11108
|
if (!type || !SITE.intake || !ref) return row;
|
|
11018
|
-
var
|
|
11019
|
-
|
|
11020
|
-
// 行の補足が1段であることを見張る検査(#523 S1)が数えている。待ちの1文は
|
|
11021
|
-
// 別の話(押した人へのその場の返事)なので、専用の class を持たせる
|
|
11109
|
+
var checkBtn = el("button", { class: "ds-toggle-btn", type: "button", text: "接続を確認" });
|
|
11110
|
+
var syncBtn = el("button", { class: "ds-toggle-btn", type: "button", text: "今すぐ同期" });
|
|
11022
11111
|
var note = el("p", { class: "ds-probe-wait" });
|
|
11023
11112
|
var phase = probeWaitPhaseOf(capId, ref);
|
|
11024
11113
|
var wait = probeWaitOf(capId, ref);
|
|
@@ -11026,40 +11115,72 @@ ${calendarAddUrl.toString()}
|
|
|
11026
11115
|
else if (phase === "timeout") note.textContent = PROBE_WAIT_TIMEOUT_TEXT;
|
|
11027
11116
|
else note.hidden = true;
|
|
11028
11117
|
|
|
11029
|
-
|
|
11030
|
-
var endBusy = beginBusy(
|
|
11118
|
+
checkBtn.addEventListener("click", async function () {
|
|
11119
|
+
var endBusy = beginBusy(checkBtn, "確認中…");
|
|
11031
11120
|
note.hidden = false;
|
|
11032
11121
|
note.textContent = "確認中…";
|
|
11033
11122
|
// 🔴 **押す前に、いまの行の 「at」 を控える(🔴2)。** 行が無ければ空文字
|
|
11034
|
-
//(どんな 「at」
|
|
11123
|
+
//(どんな 「at」 が来ても「新しい」と読める)。使うのは IP 制限で倒れた回だけだが、
|
|
11124
|
+
// 応答が返ってからでは「押す前の行」を控えられない
|
|
11035
11125
|
var before = probeLatestRow(capId, ref);
|
|
11036
11126
|
var key = probeWaitKey(capId, ref);
|
|
11037
11127
|
try {
|
|
11038
11128
|
var res = await callIntake("probe", { type: type, ref: ref });
|
|
11039
11129
|
var j = await res.json().catch(function () { return {}; });
|
|
11040
11130
|
if (!res.ok || j.queued === false) {
|
|
11041
|
-
|
|
11042
|
-
note.textContent = j.message || "確認できませんでした。";
|
|
11043
|
-
delete PROBE_WAITS[key];
|
|
11131
|
+
probeDeclined(note, key, j);
|
|
11044
11132
|
endBusy();
|
|
11045
11133
|
return;
|
|
11046
11134
|
}
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11135
|
+
if (!j.gate) {
|
|
11136
|
+
// 語が返らない回=この場では叩けなかった回。Backlog の IP 制限
|
|
11137
|
+
//(「reason」 が 「ip-restricted」)で夜間と同じ経路へ倒したときと、
|
|
11138
|
+
// 即答をまだ知らない古い Lambda が相手のとき。どちらも従来どおり待つ
|
|
11139
|
+
probeEnterWait(note, key, capId, ref, before, j);
|
|
11140
|
+
endBusy();
|
|
11141
|
+
return;
|
|
11142
|
+
}
|
|
11143
|
+
// 即答。🔴 **待ちには入れない(🟡7)**——結果はもう行に出ているので、
|
|
11144
|
+
// 待ちに入れると「届いた行」を探して15分待つ空回りになる
|
|
11145
|
+
delete PROBE_WAITS[key];
|
|
11146
|
+
note.hidden = true;
|
|
11147
|
+
note.textContent = "";
|
|
11148
|
+
showProbeVerdict(row, capId, checkBtn, j);
|
|
11149
|
+
// ストアにも 「button」 の行が書かれている。**取りに行くのは1回だけ**——
|
|
11150
|
+
// 届いた行で一覧が描き直され、この行の札が正本の値に揃う
|
|
11151
|
+
loadLiveFleet();
|
|
11056
11152
|
} catch (e) {
|
|
11057
11153
|
note.textContent = "通信に失敗しました。";
|
|
11058
11154
|
delete PROBE_WAITS[key];
|
|
11059
11155
|
}
|
|
11060
11156
|
endBusy();
|
|
11061
11157
|
});
|
|
11062
|
-
|
|
11158
|
+
|
|
11159
|
+
syncBtn.addEventListener("click", async function () {
|
|
11160
|
+
var endBusy = beginBusy(syncBtn, "同期中…");
|
|
11161
|
+
note.hidden = false;
|
|
11162
|
+
note.textContent = "同期中…";
|
|
11163
|
+
// 押す前の 「at」 を控える(上と同じ理由・壁時計を使わない)
|
|
11164
|
+
var before = probeLatestRow(capId, ref);
|
|
11165
|
+
var key = probeWaitKey(capId, ref);
|
|
11166
|
+
try {
|
|
11167
|
+
var res = await callIntake("probe", { type: type, ref: ref, sync: true });
|
|
11168
|
+
var j = await res.json().catch(function () { return {}; });
|
|
11169
|
+
if (!res.ok || j.queued === false) {
|
|
11170
|
+
probeDeclined(note, key, j);
|
|
11171
|
+
endBusy();
|
|
11172
|
+
return;
|
|
11173
|
+
}
|
|
11174
|
+
probeEnterWait(note, key, capId, ref, before, j);
|
|
11175
|
+
} catch (e) {
|
|
11176
|
+
note.textContent = "通信に失敗しました。";
|
|
11177
|
+
delete PROBE_WAITS[key];
|
|
11178
|
+
}
|
|
11179
|
+
endBusy();
|
|
11180
|
+
});
|
|
11181
|
+
|
|
11182
|
+
row.appendChild(checkBtn);
|
|
11183
|
+
row.appendChild(syncBtn);
|
|
11063
11184
|
row.appendChild(note);
|
|
11064
11185
|
return row;
|
|
11065
11186
|
}
|
|
@@ -11068,7 +11189,7 @@ ${calendarAddUrl.toString()}
|
|
|
11068
11189
|
* 一覧に出ている接続先の「届くか」をひとつの文字列にする(描き直しの要否の判定用)。
|
|
11069
11190
|
*
|
|
11070
11191
|
* **待ちの相も混ぜる(#523 S3)。** 混ぜないと、15分の上限に達しても一覧が
|
|
11071
|
-
*
|
|
11192
|
+
* 作り直されず、「同期を開始しました」が出たまま止まる(待てば出ると言い続ける形)。
|
|
11072
11193
|
*/
|
|
11073
11194
|
function reachSigOf(capId, refs) {
|
|
11074
11195
|
var now = Date.now();
|
|
@@ -12955,7 +13076,9 @@ ${calendarAddUrl.toString()}
|
|
|
12955
13076
|
var restingParts = restingHelpPartsOf(s.type, s.gate);
|
|
12956
13077
|
if (restingParts) card.appendChild(restingHelpNode(restingParts, "ds-gate-help ds-gate-resting"));
|
|
12957
13078
|
else {
|
|
12958
|
-
|
|
13079
|
+
// **能力に畳んでから渡す(#548)。** ここへ来るのは Slack / GitHub / Google Chat の
|
|
13080
|
+
// 行だけなので、ボタンが書いた detail(判定語)は従来どおり出さない
|
|
13081
|
+
var reason = stateReasonOf({ needed: s.needed, detail: s.detail, gateBy: s.gateBy, cap: gateCapOfType(s.type) });
|
|
12959
13082
|
if (reason) card.appendChild(neededNode(reason, "ds-gate-help ds-gate-reason"));
|
|
12960
13083
|
}
|
|
12961
13084
|
}
|
|
@@ -12969,9 +13092,9 @@ ${calendarAddUrl.toString()}
|
|
|
12969
13092
|
// 判定と文面は infra/lambda/shared/github-gate.mjs 側(画面に分岐を持たせない)。
|
|
12970
13093
|
// **Google Chat は #347(同じ理由)。** スペースにアプリを追加した人が、その場で
|
|
12971
13094
|
// 直ったことを確かめられる。判定は infra/lambda/shared/google-chat-gate.mjs 側。
|
|
12972
|
-
// **Backlog / Figma / Drive は「接続を確認」の型が違う**(#523 S3・レジストリの
|
|
12973
|
-
// 「probe:
|
|
12974
|
-
//
|
|
13095
|
+
// **Backlog / Figma / Drive は「接続を確認」の型が違う**(#523 S3・#548・レジストリの
|
|
13096
|
+
// 「probe: direct」)。中央が案件の鍵で叩いて即答し、「今すぐ同期」も並ぶので、
|
|
13097
|
+
// ここではなくそれぞれの一覧の行が 「appendDispatchProbe」 で出す。
|
|
12975
13098
|
//
|
|
12976
13099
|
// **origin === "explicit" でも出す。** 確認は読むだけで、どの登録ファイル由来かに
|
|
12977
13100
|
// 関係なく効く(書き込みになる「表示名を更新する」だけが由来で出し分かれる)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takagaki/cortex-decisions-viewer",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.55",
|
|
4
4
|
"description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"build": "tsc -p tsconfig.json",
|
|
22
22
|
"prepublishOnly": "npm run build",
|
|
23
23
|
"demo": "npm run build && node dist/cli.js build ../../aidd-project-cortex/Cortex/Decisions/records --out .demo-site",
|
|
24
|
-
"test": "npm run test:syntax && npm run build && node --test test/*.test.mjs",
|
|
24
|
+
"test": "npm run test:syntax && npm run build && node --test --test-concurrency=4 test/*.test.mjs",
|
|
25
25
|
"test:syntax": "node --test test/embedded-js-syntax.test.mjs"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|