@takagaki/cortex-decisions-viewer 0.11.1 → 0.12.0
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 +587 -182
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -26,7 +26,8 @@ exports.sanitizeCapabilitiesNow = sanitizeCapabilitiesNow;
|
|
|
26
26
|
exports.sanitizeGithubOwnersNow = sanitizeGithubOwnersNow;
|
|
27
27
|
exports.splitNeededParts = splitNeededParts;
|
|
28
28
|
exports.ingestedAtOf = ingestedAtOf;
|
|
29
|
-
exports.
|
|
29
|
+
exports.keyMissingText = keyMissingText;
|
|
30
|
+
exports.deriveCapTile = deriveCapTile;
|
|
30
31
|
exports.renderConnectionMap = renderConnectionMap;
|
|
31
32
|
/** SiteDataを単一の自己完結HTML(埋め込みデータ+CSS+JS)にレンダリングする */
|
|
32
33
|
function renderSite(site) {
|
|
@@ -1182,10 +1183,11 @@ const CAPABILITY_NOW_STATES = ["on", "off", "empty", "broken", "unset"];
|
|
|
1182
1183
|
* `capabilitiesNow`(能力が配線されているか)を、**まとめ表の `capabilities` と同じ形**
|
|
1183
1184
|
* (能力名=日本語のキー)に畳む。読めた能力だけを返し、1つも無ければ null。
|
|
1184
1185
|
*
|
|
1185
|
-
* ⚠ **応答は `caps`
|
|
1186
|
-
* `
|
|
1187
|
-
* `rawFleetVal`
|
|
1188
|
-
*
|
|
1186
|
+
* ⚠ **応答は `caps` で要求した能力ぶんしか来ない。** 画面は 0.12.0 から常に全量を
|
|
1187
|
+
* 要求する(`ALL_STATE_CAPS` の項)ので普段は6能力が揃うが、**古い Lambda は一部しか
|
|
1188
|
+
* 返さない**。だから**キー単位でまとめ表へ落ちる**——ここだけは `rawFleetVal` の
|
|
1189
|
+
* 「キーごとにフォールバックしない」の例外で、そうしないと返らなかった能力の
|
|
1190
|
+
* タイルが「未接続」に落ちる。
|
|
1189
1191
|
*/
|
|
1190
1192
|
function sanitizeCapabilitiesNow(v) {
|
|
1191
1193
|
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
@@ -1293,45 +1295,140 @@ function ingestedAtOf(rows, capId) {
|
|
|
1293
1295
|
return best;
|
|
1294
1296
|
}
|
|
1295
1297
|
/**
|
|
1296
|
-
*
|
|
1298
|
+
* タイルと接続グラフの導出に要る、能力ごとの材料(0.12.0・#231 / #235 / #227)。
|
|
1297
1299
|
*
|
|
1298
|
-
*
|
|
1299
|
-
*
|
|
1300
|
-
*
|
|
1301
|
-
*
|
|
1300
|
+
* - `id` … 接続状態ストアの能力名(`STATE_CAP_NAMES` と同じ綴り)
|
|
1301
|
+
* - `question` … その能力が「実際に動いた」と言える問い。**取り込む能力は `ingested`、
|
|
1302
|
+
* 毎晩の Gold 昇格がその場で読む能力(チャット・開発)は `reachable`。**
|
|
1303
|
+
* 取り込みの行が存在しない能力に `ingested` を割り当てると、**永久に「実績なし」**になる
|
|
1304
|
+
* - `noFact` … 規則4(登録はあるが実績が無い)の文言。**能力ごとに待っているものが違う**
|
|
1305
|
+
* ので1文に丸めない。会議・共有資料は中央 Apps Script の取り込み待ち(=まだ使っていない)、
|
|
1306
|
+
* 課題管理・デザインは夜間同期待ち、チャット・開発は実測待ち
|
|
1307
|
+
*
|
|
1308
|
+
* **6能力すべてを載せること。** ここに無い能力は導出そのものが働かず、規則5
|
|
1309
|
+
* (fleet へのフォールバック)に落ちる=正本化の対象から静かに外れる。
|
|
1310
|
+
*/
|
|
1311
|
+
const TILE_CAPS = {
|
|
1312
|
+
"課題管理": { id: "issues", question: "ingested", noFact: "登録済み(まだ取り込みがありません)", keyLabel: "APIキー" },
|
|
1313
|
+
"会議": { id: "meetings", question: "ingested", noFact: "未使用(まだ取り込みがありません)", keyLabel: "" },
|
|
1314
|
+
"共有資料": { id: "materials", question: "ingested", noFact: "未使用(まだ取り込みがありません)", keyLabel: "" },
|
|
1315
|
+
"デザイン": { id: "design", question: "ingested", noFact: "登録済み(まだ取り込みがありません)", keyLabel: "トークン" },
|
|
1316
|
+
"チャット": { id: "chat", question: "reachable", noFact: "登録済み(接続は未確認です)", keyLabel: "" },
|
|
1317
|
+
"開発": { id: "dev", question: "reachable", noFact: "登録済み(接続は未確認です)", keyLabel: "" },
|
|
1318
|
+
};
|
|
1319
|
+
/**
|
|
1320
|
+
* 「鍵が無くて飛ばした」の一文(#225)。**能力ごとに鍵の呼び名が違う。**
|
|
1321
|
+
*
|
|
1322
|
+
* 画面の鍵カードは「Backlog のAPIキー」「Figma のトークン」と名乗っている。
|
|
1323
|
+
* ここで別の語を使うと、**利用者は言われたものを画面から探せない**。
|
|
1324
|
+
* 鍵を持たない能力(会議・共有資料・チャット・開発)では skipped は本来起きないが、
|
|
1325
|
+
* 起きたときに「鍵」の話をしないよう、中立の一文に倒す。
|
|
1326
|
+
*/
|
|
1327
|
+
function keyMissingText(cap) {
|
|
1328
|
+
var meta = TILE_CAPS[cap];
|
|
1329
|
+
var label = meta ? meta.keyLabel : "";
|
|
1330
|
+
return label ? label + "が未登録です" : "取り込みを飛ばしました";
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* 実測が「悪い」と言える語。**`unknown` は入れない**——測れなかっただけで、
|
|
1334
|
+
* 悪いとは言えない(入れると、一度も測れていない案件が全部⚠️になる)。
|
|
1302
1335
|
*/
|
|
1303
|
-
const
|
|
1336
|
+
const BAD_STATE_STATUSES = {
|
|
1337
|
+
ingested: ["failed", "skipped"],
|
|
1338
|
+
reachable: ["not_in_channel", "missing_scope", "no_token", "unreachable"],
|
|
1339
|
+
};
|
|
1304
1340
|
/**
|
|
1305
|
-
*
|
|
1341
|
+
* タイル1行(と接続グラフの1ノード)の状態を、**正本から導出する**(0.12.0)。
|
|
1342
|
+
*
|
|
1343
|
+
* ## 規則は上から順の優先判定(並列判定に書き換えないこと)
|
|
1344
|
+
*
|
|
1345
|
+
* 1. **登録0件** → 「未接続」(中立・点線・線なし)
|
|
1346
|
+
* 2. **登録あり × 実測が悪い** → 警告(⚠+短い一言)
|
|
1347
|
+
* 3. **登録あり × 実績が ok** → 「接続OK」
|
|
1348
|
+
* 4. **登録あり × 実績なし** → 中立(能力ごとの文言)
|
|
1349
|
+
* 5. **`stateNow` の欄が無い**(古い Lambda・ストアが読めなかった)→ `null`
|
|
1350
|
+
* =呼び出し側は従来の fleet 表示へフォールバック
|
|
1351
|
+
*
|
|
1352
|
+
* 🔴 **規則1が最優先であることが、孤児行に対する唯一の守り。** 接続先を外しても
|
|
1353
|
+
* ストアの行は残る(TTL が無い・既知の問題)。並列判定に書き換えると、
|
|
1354
|
+
* **登録を全部外した能力が、残った孤児行のせいで「接続OK」や⚠️を出し続ける**
|
|
1355
|
+
* ——#227 で見た「実際は0件なのに接続OK」がそのまま復活する。
|
|
1306
1356
|
*
|
|
1307
|
-
* 🔴
|
|
1308
|
-
*
|
|
1357
|
+
* 🔴 **規則5より規則1を先に見るのも意図的。** 登録が0件であることは `registeredNow`
|
|
1358
|
+
* だけで言い切れる事実で、ストアが読めたかどうかとは無関係。ここを逆にすると、
|
|
1359
|
+
* ストアが読めない案件でだけ「0件なのに接続OK」が残る。
|
|
1309
1360
|
*
|
|
1310
|
-
*
|
|
1311
|
-
* 従来表示のままにする。分からないことを「未使用」と言い切らない
|
|
1312
|
-
* - `seen === true` かつ行が0件 … 読めて空=**まだ一度も取り込めていない**→ 真。
|
|
1313
|
-
* 初期化直後の案件がここに来る(この機能が一番効いてほしい案件)
|
|
1314
|
-
* - `seen === true` かつ行がある … その能力の取り込み実績で決める
|
|
1361
|
+
* ## 設定そのものが OFF / 異常のときは、ここへ来ない
|
|
1315
1362
|
*
|
|
1316
|
-
*
|
|
1317
|
-
* `
|
|
1318
|
-
*
|
|
1363
|
+
* 「OFFに設定されています」「設定に異常があります」「未接続(未設置)」は
|
|
1364
|
+
* `capabilities` の導出(#125②)が答えるもので、**呼び出し側がこの関数より先に**出す。
|
|
1365
|
+
* ここが受け持つのは「設定は済んでいる(on)」か「そもそも導出が無い」場合だけ。
|
|
1366
|
+
* 混ぜると、意図的に止めた能力に「登録済み(接続は未確認です)」と出す。
|
|
1319
1367
|
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1368
|
+
* @param cap 画面の能力名(日本語)
|
|
1369
|
+
* @param ctx.registered その能力の登録件数。**分からなければ null**(0 と混ぜない——
|
|
1370
|
+
* 取得前の null を 0 として扱うと、読み込み中の一瞬だけ全能力が「未接続」になる)
|
|
1371
|
+
* @param ctx.seen 応答に `stateNow` の欄があったか。**`rows` の空とは別物**——
|
|
1372
|
+
* `sanitizeStateNow` は0件を `null` に畳むので、`rows` だけを見ると
|
|
1373
|
+
* 「読めて空(=まだ何も起きていない事実)」が「分からない」に化ける
|
|
1374
|
+
* @param ctx.rows `stateNow` の行
|
|
1375
|
+
* @returns 導出結果。`null` なら規則5(従来表示へフォールバック)
|
|
1322
1376
|
*
|
|
1323
1377
|
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
1324
|
-
* `
|
|
1378
|
+
* `TILE_CAPS` / `BAD_STATE_STATUSES` は CLIENT_JS 側にも `var` で流し込んである)。
|
|
1325
1379
|
*/
|
|
1326
|
-
function
|
|
1327
|
-
var
|
|
1328
|
-
if (!
|
|
1329
|
-
return
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
if (
|
|
1333
|
-
return
|
|
1334
|
-
|
|
1380
|
+
function deriveCapTile(cap, ctx) {
|
|
1381
|
+
var meta = TILE_CAPS[cap];
|
|
1382
|
+
if (!meta)
|
|
1383
|
+
return null;
|
|
1384
|
+
var registered = ctx ? ctx.registered : null;
|
|
1385
|
+
// ---- 規則1: 登録0件 → 未接続 ----
|
|
1386
|
+
if (registered === 0)
|
|
1387
|
+
return { rule: 1, state: "idle", text: "未接続", keyMissing: false };
|
|
1388
|
+
// ---- 規則5: 材料が届いていない → 従来表示へ ----
|
|
1389
|
+
if (!ctx || !ctx.seen)
|
|
1390
|
+
return null;
|
|
1391
|
+
var rows = Array.isArray(ctx.rows) ? ctx.rows : [];
|
|
1392
|
+
var bad = BAD_STATE_STATUSES[meta.question] || [];
|
|
1393
|
+
var failed = 0;
|
|
1394
|
+
var skipped = 0;
|
|
1395
|
+
var unreachable = 0;
|
|
1396
|
+
var okSeen = false;
|
|
1397
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1398
|
+
var r = rows[i];
|
|
1399
|
+
if (!r || r.cap !== meta.id || r.question !== meta.question)
|
|
1400
|
+
continue;
|
|
1401
|
+
// **一度でも成功していれば実績あり。** `lastOkAt` を見るのは、いま失敗している行でも
|
|
1402
|
+
// 「繋がったことがある」のは事実だから(規則2が先に立つので表示は⚠️のまま)
|
|
1403
|
+
if (r.status === "ok" || r.lastOkAt)
|
|
1404
|
+
okSeen = true;
|
|
1405
|
+
if (bad.indexOf(r.status) < 0)
|
|
1406
|
+
continue;
|
|
1407
|
+
if (r.status === "failed")
|
|
1408
|
+
failed++;
|
|
1409
|
+
else if (r.status === "skipped")
|
|
1410
|
+
skipped++;
|
|
1411
|
+
else
|
|
1412
|
+
unreachable++;
|
|
1413
|
+
}
|
|
1414
|
+
// ---- 規則2: 実測が悪い → 警告 ----
|
|
1415
|
+
//
|
|
1416
|
+
// **失敗を先に言う。** 「鍵が無くて飛ばした」より「鍵はあるのに取れなかった」ほうが
|
|
1417
|
+
// 直す先が違ううえに重い(#225 が分けたかったのはまさにこの2つ)
|
|
1418
|
+
if (failed)
|
|
1419
|
+
return { rule: 2, state: "warn", text: "同期が失敗しています", keyMissing: false };
|
|
1420
|
+
// 🔴 **skipped は「失敗」と言わない(#225)。** 鍵が無いのは取り込みの失敗ではなく
|
|
1421
|
+
// 設定が済んでいないだけで、利用者が取るべき行動が違う(鍵を入れる/原因を調べる)
|
|
1422
|
+
if (skipped)
|
|
1423
|
+
return { rule: 2, state: "warn", text: keyMissingText(cap), keyMissing: !!meta.keyLabel };
|
|
1424
|
+
if (unreachable) {
|
|
1425
|
+
return { rule: 2, state: "warn", text: unreachable + "件が読めていません", keyMissing: false };
|
|
1426
|
+
}
|
|
1427
|
+
// ---- 規則3: 実績あり → 接続OK ----
|
|
1428
|
+
if (okSeen)
|
|
1429
|
+
return { rule: 3, state: "ok", text: "接続OK", keyMissing: false };
|
|
1430
|
+
// ---- 規則4: 実績なし → 中立 ----
|
|
1431
|
+
return { rule: 4, state: "idle", text: meta.noFact, keyMissing: false };
|
|
1335
1432
|
}
|
|
1336
1433
|
function capabilityState(cap, ctx) {
|
|
1337
1434
|
// **能力→パイプラインの対応はここに持つ。** 呼び出し側に渡させると、
|
|
@@ -1555,12 +1652,16 @@ function renderConnectionMap(site) {
|
|
|
1555
1652
|
else if (internal?.lastSync) {
|
|
1556
1653
|
statusTs = internal.lastSync;
|
|
1557
1654
|
}
|
|
1558
|
-
// 時刻はクライアントでローカルタイム整形するため data-ts
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1655
|
+
// 時刻はクライアントでローカルタイム整形するため data-ts で渡す。
|
|
1656
|
+
//
|
|
1657
|
+
// 🔴 **3行目は中身が空でも必ず置く(0.12.0)。** ライブ応答が届くたびに全能力の
|
|
1658
|
+
// 状態を書き直す(`applyStateToConnMap`)ので、**書く場所が無いノードがあると
|
|
1659
|
+
// そこだけ更新できない**。以前は無ければ実行時に createElementNS で作っていたが、
|
|
1660
|
+
// 「ビルド時に組み立てる形」と「実行時に組み立てる形」が2つに分かれ、片方だけ
|
|
1661
|
+
// 直る形になっていた(座標の与え方が実際にずれていた)。
|
|
1662
|
+
const sub = `<text class="cm-sync" x="${cx}" y="67" text-anchor="middle"` +
|
|
1663
|
+
(statusTs ? ` data-ts="${escapeHtml(statusTs)}"` : "") +
|
|
1664
|
+
`>${escapeHtml(statusText)}</text>`;
|
|
1564
1665
|
const badge = warn ? `<text class="cm-badge" x="${cx + 62}" y="24" text-anchor="middle">⚠️</text>` : "";
|
|
1565
1666
|
// クリックでページ内の該当詳細カード(アンカー)へスクロールする
|
|
1566
1667
|
const anchor = `ds-${c.cap}`;
|
|
@@ -1579,19 +1680,31 @@ function renderConnectionMap(site) {
|
|
|
1579
1680
|
// cm-warn は引く。**壊れているのは「繋がったうえで失敗している」状態**で、
|
|
1580
1681
|
// 経路自体は存在する(線を消すと、設定が無いのと区別が付かなくなる)。
|
|
1581
1682
|
//
|
|
1582
|
-
//
|
|
1583
|
-
//
|
|
1584
|
-
//
|
|
1585
|
-
//
|
|
1586
|
-
//
|
|
1587
|
-
//
|
|
1683
|
+
// 🔴 **線は6本とも必ず焼く。出す/出さないはクラスで切り替える(0.12.0・#231)。**
|
|
1684
|
+
//
|
|
1685
|
+
// この図はビルド時に焼くので、**登録の現在値も実測(stateNow)もここでは分からない**
|
|
1686
|
+
// ——分かるのはライブ応答が届いた後だけ。届いてから毎回、全能力の状態を導出し直して
|
|
1687
|
+
// 当てる(`applyStateToConnMap`)。そのとき **線を作ったり消したりしない**:
|
|
1688
|
+
//
|
|
1689
|
+
// - 消す(removeChild)と、昇格(直したら線が戻る)のときに**ジオメトリを
|
|
1690
|
+
// 実行時にもう一度計算する**ことになる。曲線の制御点の式がビルド時と実行時の
|
|
1691
|
+
// 2箇所に分かれ、片方だけ直す形が生まれる
|
|
1692
|
+
// - 最初から6本置いて表示を切り替えるなら、式はここ1箇所のまま
|
|
1693
|
+
//
|
|
1694
|
+
// **`data-cm-cap` が取っ手。** 位置で数えさせないこと——線は6本とも同じ形で並ぶが、
|
|
1695
|
+
// 「何番目が何の能力か」を数える読み方は、能力が増減した瞬間に静かにずれる。
|
|
1588
1696
|
//
|
|
1589
1697
|
// 🔴 **`data-cap` は使わない。** あちらは能力ブロック(`.datasources` の中)が
|
|
1590
1698
|
// 既に名乗っていて、図はページの**上**にあるので `[data-cap="会議"]` で引くと
|
|
1591
1699
|
// 図の線のほうが先に当たる。実際、最初にそう書いてテストが12件落ちた。
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1700
|
+
//
|
|
1701
|
+
// **線を出すのは「実際に繋がっていることが証明できたもの」だけ**(従来どおり)。
|
|
1702
|
+
// 未使用(cm-off)・宣言だけで実体が無いもの(cm-idle)からは出さない。
|
|
1703
|
+
// cm-warn は出す——**壊れているのは「繋がったうえで失敗している」状態**で、
|
|
1704
|
+
// 経路自体は存在する(線を消すと、設定が無いのと区別が付かなくなる)。
|
|
1705
|
+
const linkShown = state === "cm-ok" || state === "cm-warn";
|
|
1706
|
+
links.push(`<path class="cm-link ${state}${linkShown ? "" : " is-hidden"}" data-cm-cap="${escapeHtml(c.cap)}"` +
|
|
1707
|
+
` d="M ${cx} 78 C ${cx} 152, ${REPO_X} 162, ${REPO_X} 238"></path>`);
|
|
1595
1708
|
});
|
|
1596
1709
|
// **中央ノードは GitHub のリポジトリ名そのもの(オーナー指示・2026-08-12)。**
|
|
1597
1710
|
// 以前は「📦 コンテキストリポジトリ(案件の表示名)」だったが、役割名は図の文脈から自明で、
|
|
@@ -2256,6 +2369,9 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
2256
2369
|
.cm-link.cm-warn { stroke: #d97706; }
|
|
2257
2370
|
.cm-link.cm-off { stroke: #d1d5db; opacity: .6; }
|
|
2258
2371
|
.cm-link.cm-idle { stroke: #cbd5e1; opacity: .7; }
|
|
2372
|
+
/* 線は6本とも焼いて、出す/出さないをここで切り替える(0.12.0・renderConnectionMap の項)。
|
|
2373
|
+
SVG 要素に HTML の hidden 属性は効かないので、クラスで消す */
|
|
2374
|
+
.cm-link.is-hidden { display: none; }
|
|
2259
2375
|
@keyframes cm-flow { to { stroke-dashoffset: -24; } }
|
|
2260
2376
|
@media (prefers-reduced-motion: reduce) { .cm-link.cm-ok { animation: none; } }
|
|
2261
2377
|
.cm-repo rect { fill: var(--accent-weak); stroke: var(--accent); stroke-width: 1.5; }
|
|
@@ -2325,6 +2441,10 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
2325
2441
|
.ds-toggle-btn { font-size: 12px; padding: 3px 10px; border-radius: 999px; border: 1px solid var(--border); background: #fff; color: var(--text); cursor: pointer; }
|
|
2326
2442
|
.ds-toggle-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
|
|
2327
2443
|
.ds-toggle-btn:disabled { opacity: .6; cursor: default; }
|
|
2444
|
+
/* 送信中(#222)。**押せたことが分かる見た目**にする。cursor は wait にして、
|
|
2445
|
+
「反応していないので押し直す」を止める(二重送信の一番多い入口) */
|
|
2446
|
+
.ds-toggle-btn.is-busy { cursor: wait; opacity: .75; }
|
|
2447
|
+
.ds-toggle-btn.is-busy::before { content: "◌ "; }
|
|
2328
2448
|
.ds-toggle-btn.ds-danger { color: #b42318; border-color: #f3c6c1; }
|
|
2329
2449
|
.ds-toggle-btn.ds-danger:hover:not(:disabled) { border-color: #b42318; color: #b42318; background: #fff5f4; }
|
|
2330
2450
|
/* .ds-subitems / .ds-subitem は撤去した(2026-08-21)。共有資料のフォルダと Figma の
|
|
@@ -2424,6 +2544,11 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
2424
2544
|
.ds-keyrow { flex: 1 1 100%; min-width: 0; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
|
|
2425
2545
|
/* 一覧の直後に置くとき(Figma)は、行の中ではないので区切り線を持たない */
|
|
2426
2546
|
.ds-keyrow-list { margin-top: 10px; padding-top: 0; border-top: none; }
|
|
2547
|
+
/* 鍵の効く範囲を枠で示す(#229)。Figma の鍵は案件に1本で、枠の中の全ファイルに効く。
|
|
2548
|
+
Backlog は行ごとに鍵があるので枠を持たない——**枠の有無がスコープの違いそのもの** */
|
|
2549
|
+
.ds-scope-group { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: var(--surface); }
|
|
2550
|
+
/* 鍵がどこまで効くかの1行(#229)。手順(.sec-how)より前に出るので、少し強めに */
|
|
2551
|
+
.sec-scope { font-size: 12px; color: var(--muted); margin: 0; }
|
|
2427
2552
|
/* 🔴 **hidden を効かせる(2026-08-22)。** 鍵の欄は接続先が0件のあいだ hidden 属性で
|
|
2428
2553
|
畳む(作らないのではなく隠す——理由は syncKeyCardVisibility の項)。ところが
|
|
2429
2554
|
.ops-card は display: flex、.ds-keyrow は flex アイテムを名乗るので、
|
|
@@ -2684,8 +2809,10 @@ ${sanitizeCapabilitiesNow.toString()}
|
|
|
2684
2809
|
${sanitizeGithubOwnersNow.toString()}
|
|
2685
2810
|
${splitNeededParts.toString()}
|
|
2686
2811
|
${ingestedAtOf.toString()}
|
|
2687
|
-
var
|
|
2688
|
-
${
|
|
2812
|
+
var TILE_CAPS = ${JSON.stringify(TILE_CAPS)};
|
|
2813
|
+
var BAD_STATE_STATUSES = ${JSON.stringify(BAD_STATE_STATUSES)};
|
|
2814
|
+
${keyMissingText.toString()}
|
|
2815
|
+
${deriveCapTile.toString()}
|
|
2689
2816
|
// 「はじめに」セクションのチュートリアルデータ(ビルド時に埋め込み。ボタンURL・チャレンジ4の決定は解決済み)
|
|
2690
2817
|
var TUTORIAL = (function () {
|
|
2691
2818
|
var elx = document.getElementById("tutorial-data");
|
|
@@ -4209,10 +4336,12 @@ ${usageFactMissing.toString()}
|
|
|
4209
4336
|
* 能力の状態(capabilities)。**能力ごとに、正本(capabilitiesNow)があればそちら。**
|
|
4210
4337
|
*
|
|
4211
4338
|
* ⚠ **ここは rawFleetVal の「キー単位でフォールバックしない」の意図的な例外**(工程1.5)。
|
|
4212
|
-
* あちらの禁止は「同じ画面に2
|
|
4213
|
-
*
|
|
4214
|
-
*
|
|
4215
|
-
*
|
|
4339
|
+
* あちらの禁止は「同じ画面に2つの時点が混ざると、上に出す『◯◯時点』が嘘になる」ため。
|
|
4340
|
+
*
|
|
4341
|
+
* **0.12.0 で caps は常に全量になった**(ALL_STATE_CAPS の項)ので、応答には
|
|
4342
|
+
* 6能力ぶんが揃って届く。それでも能力ごとの合成は残す——**古い Lambda が一部の能力を
|
|
4343
|
+
* 返さない期間がある**からで、丸ごと採るか丸ごと捨てるかしか無いと、その期間に
|
|
4344
|
+
* 返らなかった能力のタイルが「未接続」に落ちる。
|
|
4216
4345
|
*/
|
|
4217
4346
|
function capsView() {
|
|
4218
4347
|
var base = fleetVal("capabilities") || {};
|
|
@@ -4342,6 +4471,53 @@ ${usageFactMissing.toString()}
|
|
|
4342
4471
|
};
|
|
4343
4472
|
}
|
|
4344
4473
|
|
|
4474
|
+
/**
|
|
4475
|
+
* その能力の登録件数(規則1の材料)。**分からなければ null。**
|
|
4476
|
+
*
|
|
4477
|
+
* 🔴 **正本(registeredNow)だけを見る。** まとめ表(fleet-status.json)は夜間の
|
|
4478
|
+
* スナップショットで、外した直後も「1件」と言い続ける——#227 で「実際は0件なのに
|
|
4479
|
+
* 接続OK」を出していた当の情報源。ここでそちらへフォールバックすると、
|
|
4480
|
+
* この正本化が丸ごと無意味になる。
|
|
4481
|
+
*
|
|
4482
|
+
* **会議は null を返す。** 会議は登録の一覧を持たない(別名の書き換えは #117 で
|
|
4483
|
+
* 読み取り専用にした)ので、「0件」と言える材料が無い。会議の規則1にあたる判断
|
|
4484
|
+
* (自動取り込みが OFF)は 「capabilities」 の導出が答え、呼び出し側がこの関数より
|
|
4485
|
+
* 先に出す(deriveCapTile の項)。
|
|
4486
|
+
*/
|
|
4487
|
+
function registeredCountOf(cap) {
|
|
4488
|
+
if (!LIVE_REGISTERED) return null;
|
|
4489
|
+
var meta = TILE_CAPS[cap];
|
|
4490
|
+
if (!meta) return null;
|
|
4491
|
+
var list = LIVE_REGISTERED[meta.id];
|
|
4492
|
+
return Array.isArray(list) ? list.length : null;
|
|
4493
|
+
}
|
|
4494
|
+
|
|
4495
|
+
/**
|
|
4496
|
+
* タイル・接続グラフの導出結果(0.12.0)。**タイルも図もここを通す。**
|
|
4497
|
+
*
|
|
4498
|
+
* 🔴 **呼び出し側で材料を組み立て直さないこと。** 同じ判定が2箇所に増えると、
|
|
4499
|
+
* 「図は接続OK・タイルは未接続」という #231 が殺したはずの食い違いが復活する。
|
|
4500
|
+
*/
|
|
4501
|
+
function capDerivation(cap) {
|
|
4502
|
+
return deriveCapTile(cap, {
|
|
4503
|
+
registered: registeredCountOf(cap),
|
|
4504
|
+
seen: LIVE_STATE_SEEN,
|
|
4505
|
+
rows: LIVE_STATE,
|
|
4506
|
+
});
|
|
4507
|
+
}
|
|
4508
|
+
|
|
4509
|
+
/**
|
|
4510
|
+
* 設定そのものの導出(capabilities)が「on 以外」を答えているか。
|
|
4511
|
+
*
|
|
4512
|
+
* **答えているなら、その語が優先(#125②)。** OFF・設定に異常・未接続(未設置)は
|
|
4513
|
+
* 登録や実測より前に立つ——意図的に止めた能力に「登録済み(接続は未確認です)」と
|
|
4514
|
+
* 出すのは、設定する必要が無いものに設定を促すのと同じ誤り。
|
|
4515
|
+
*/
|
|
4516
|
+
function capConfigState(cap) {
|
|
4517
|
+
var d = capabilityDisplayState(cap, capsView());
|
|
4518
|
+
return d && d !== "on" ? d : null;
|
|
4519
|
+
}
|
|
4520
|
+
|
|
4345
4521
|
// ---------- 反映待ち(「⏳ 反映中」)----------
|
|
4346
4522
|
//
|
|
4347
4523
|
// ## 何のためか
|
|
@@ -5271,14 +5447,36 @@ ${usageFactMissing.toString()}
|
|
|
5271
5447
|
//
|
|
5272
5448
|
// **「[]」(夜間バッチは走ったが対象0件)と 「undefined」(まだ走っていない)は別物。**
|
|
5273
5449
|
// 後者に「まだありません」と書くと、実際には登録済みかもしれない案件に嘘を言う
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5450
|
+
//
|
|
5451
|
+
// 🔴 **行の一覧と同じ情報源で出し入れする(#232・0.12.0)。** 以前はこの1行を
|
|
5452
|
+
// 節を組み立てるときに1回だけ判定して固定していた。ところがリポジトリの追加は
|
|
5453
|
+
// **モーダルを開いたまま**行われ、行だけが liveList で増える——結果、実機では
|
|
5454
|
+
// **「classmethod-internal/cortex-test-repo」の行と「まだありません」が同時に**出た。
|
|
5455
|
+
//
|
|
5456
|
+
// liveList に載せると、行と同じ署名で描き直される=行が増えた瞬間に消える。
|
|
5457
|
+
// Backlog(backlogTargetsSection の emptyEl 出し入れ)で先に潰した形の横展開で、
|
|
5458
|
+
// **兄弟機能へ展開し忘れていた**(#226・#220 と同じ型の漏れ)。
|
|
5459
|
+
//
|
|
5460
|
+
// **置き場所は追加フォームの後のまま。** 文言が「上のフォームで登録すると」と
|
|
5461
|
+
// 言っているので、フォームより前に出すと指す先が無くなる
|
|
5462
|
+
//
|
|
5463
|
+
// **箱を置いてよいのは、そこへ足せる案件だけ**(liveListWanted の項)。投入の口が
|
|
5464
|
+
// 無い構成に「上のフォームで登録すると」と書いても、指す先のフォームが無い
|
|
5465
|
+
if (liveListWanted("github")) pushCap("開発", liveList("開発", function () {
|
|
5466
|
+
// 判定に使う値そのものを署名にする(行の増減と、夜間の組の一覧の両方)
|
|
5467
|
+
return JSON.stringify([liveExternals("github").length, githubOwnersView() || null]);
|
|
5468
|
+
}, function (box) {
|
|
5469
|
+
var ownersNow = githubOwnersView();
|
|
5470
|
+
// **行が1本でもあれば言わない。** 「行がある」と「まだありません」を
|
|
5471
|
+
// 同じ画面で同時に言わせないための、いちばん外側の条件
|
|
5472
|
+
if (liveExternals("github").length) return;
|
|
5473
|
+
if (!Array.isArray(ownersNow) || ownersNow.length !== 0) return;
|
|
5474
|
+
box.appendChild(el("p", {
|
|
5277
5475
|
class: "sec-how ds-all-connected",
|
|
5278
5476
|
text: "読み取り対象の開発リポジトリがまだありません。上のフォームで登録すると、"
|
|
5279
5477
|
+ "読めているかどうかを翌朝からここに出します。",
|
|
5280
5478
|
}));
|
|
5281
|
-
}
|
|
5479
|
+
}));
|
|
5282
5480
|
|
|
5283
5481
|
// 連携の鍵は、その能力のところへ置く(画面下まで往復させない)
|
|
5284
5482
|
// **GitHub(「external-sources」)はここに無い。** 読み取りトークンは廃止し、
|
|
@@ -5336,7 +5534,11 @@ ${usageFactMissing.toString()}
|
|
|
5336
5534
|
return;
|
|
5337
5535
|
}
|
|
5338
5536
|
if (k === "figma" && KEY_SLOTS.figma) {
|
|
5339
|
-
KEY_SLOTS.figma.appendChild(secretCard(k, SECRET_FORMS[k], {
|
|
5537
|
+
KEY_SLOTS.figma.appendChild(secretCard(k, SECRET_FORMS[k], {
|
|
5538
|
+
inRow: true,
|
|
5539
|
+
// Backlog(スペースごと)との違いを、見た目だけでなく言葉でも出す(#229)
|
|
5540
|
+
scopeNote: "この鍵は、上に並んでいる登録済みの全ファイルに共通です(ファイルごとの鍵ではありません)。",
|
|
5541
|
+
}));
|
|
5340
5542
|
return;
|
|
5341
5543
|
}
|
|
5342
5544
|
pushCap(SECRET_CAP[k], secretCard(k, SECRET_FORMS[k]));
|
|
@@ -5429,30 +5631,34 @@ ${usageFactMissing.toString()}
|
|
|
5429
5631
|
* 引き直す**ために同じ関数を使う——写経すると片方だけ古くなる
|
|
5430
5632
|
*/
|
|
5431
5633
|
/**
|
|
5432
|
-
* @param
|
|
5433
|
-
*
|
|
5434
|
-
*
|
|
5634
|
+
* @param derive 正本からの導出(deriveCapTile の答え)。**呼び出し側が渡す**
|
|
5635
|
+
* ——ここで引き直すと、タイルの色(st)を決めた材料と2回引くことになり、
|
|
5636
|
+
* 片方だけずれる余地ができる。null なら規則5=従来表示へフォールバック
|
|
5435
5637
|
*/
|
|
5436
|
-
function tileSub(cap, st, derived, adapter, ins, srcs,
|
|
5638
|
+
function tileSub(cap, st, derived, adapter, ins, srcs, derive) {
|
|
5437
5639
|
var internal = ins.filter(function (s) { return s.kind === cap; })[0];
|
|
5438
5640
|
var live = srcs.filter(function (s) { return externalCapability(s.type) === cap; });
|
|
5439
5641
|
var parts = [];
|
|
5440
|
-
// 🔴 **取り込みの実績が無いなら、まずそれを言う(#235 の表示層)。**
|
|
5441
|
-
// 設定は済んでいる(capabilities は on)ので「未接続」ではないし、設定ミスでもないので
|
|
5442
|
-
// 警告色にもしない(#230 の決定どおり中立表示)。ここを通るのは会議・共有資料だけで、
|
|
5443
|
-
// しかも呼び出し側が st === "ok" のときにしか立てない——OFF・設定異常・同期失敗の
|
|
5444
|
-
// 語を、この一文で上書きしないため。
|
|
5445
|
-
//
|
|
5446
|
-
// 実績が無い= st は "idle" に降りているので、下の「件数」「最終同期」は自動で消える
|
|
5447
|
-
// (繋がっていないものに読み取り範囲と同期時刻を並べない、という既存の規律に乗る)
|
|
5448
|
-
if (noFact) {
|
|
5449
|
-
parts.push((CAP_STATE_MARK[st] || "") + "未使用(まだ取り込みがありません)");
|
|
5450
5642
|
// **導出が on 以外なら、導出の語を優先する(#125②)。** broken は状態としては
|
|
5451
5643
|
// 警告(⚠️)だが、原因は「設定そのものが壊れている」であって同期の失敗ではない。
|
|
5452
5644
|
// 下の warn の文(同期が失敗しています/N件が読めていません)を出すと、
|
|
5453
|
-
//
|
|
5454
|
-
|
|
5645
|
+
// 直す場所が違うところを指す。
|
|
5646
|
+
//
|
|
5647
|
+
// 🔴 **ここが正本からの導出(下)より先。** 意図的に OFF にした能力に
|
|
5648
|
+
// 「登録済み(接続は未確認です)」と出さないため(deriveCapTile の項)
|
|
5649
|
+
if (derived && derived !== "on") {
|
|
5455
5650
|
parts.push((CAP_STATE_MARK[st] || "") + (CAP_STATE_TEXT[derived] || "状態が判定できません"));
|
|
5651
|
+
} else if (derive) {
|
|
5652
|
+
// 🔴 **正本(登録・実測)からの導出(0.12.0・#231 / #235 / #227)。**
|
|
5653
|
+
// 「設定が済んでいる」ことと「実際に動いている」ことは別。設定だけを見て
|
|
5654
|
+
// 「● 接続OK」と出すと、**画面が動いていないものを動いていると言う**。
|
|
5655
|
+
//
|
|
5656
|
+
// **ライブ参照の内訳がある警告だけは、そちらの文を使う。** attentionSummary は
|
|
5657
|
+
// 社内/顧客の出し分けを持っていて、グループの見出しと同じ文になる
|
|
5658
|
+
// (写経すると片方だけ古くなる)。内訳が空なら導出の一言に倒す——
|
|
5659
|
+
// 以前の「⚠️ 0件に問題」は、何が起きているのか読めない文だった
|
|
5660
|
+
var summary = derive.state === "warn" && live.length ? attentionSummary(live) : "";
|
|
5661
|
+
parts.push(summary || (CAP_STATE_MARK[derive.state] || "") + derive.text);
|
|
5456
5662
|
} else if (st === "warn") {
|
|
5457
5663
|
if (live.length) {
|
|
5458
5664
|
// **文面は共有の attentionSummary が持つ。** グループの summary と同じ文で、
|
|
@@ -5568,15 +5774,16 @@ ${usageFactMissing.toString()}
|
|
|
5568
5774
|
// カタログにあるが導出が答えていないアダプタ(今日は発生しない。#107 の前方互換)
|
|
5569
5775
|
? "idle"
|
|
5570
5776
|
: capabilityState(cap, stateCtx());
|
|
5571
|
-
// 🔴
|
|
5572
|
-
//
|
|
5573
|
-
//
|
|
5574
|
-
//
|
|
5777
|
+
// 🔴 **状態の正本は「登録・実測からの導出」(0.12.0・#231 / #235 / #227)。**
|
|
5778
|
+
// 設定が済んでいること(capabilities が on)と、実際に動いていることは別。
|
|
5779
|
+
// 設定だけを見て「● 接続OK」と出すと、**画面が動いていないものを動いていると言う**
|
|
5780
|
+
// ——顧客がそれを信じて資料を置かなくなる/失敗に気づけない。
|
|
5575
5781
|
//
|
|
5576
|
-
//
|
|
5577
|
-
//
|
|
5578
|
-
|
|
5579
|
-
|
|
5782
|
+
// **導出が答えないとき(規則5・古い Lambda)だけ、従来の fleet 判定のまま。**
|
|
5783
|
+
// **設定が on 以外のときは、そちらが先に立つ**(tileSub の順序と揃える——
|
|
5784
|
+
// 揃えないと「色は接続OK・文字はOFF」が同じタイルの中で並ぶ)
|
|
5785
|
+
var derive = derived && derived !== "on" ? null : capDerivation(cap);
|
|
5786
|
+
if (derive) st = derive.state;
|
|
5580
5787
|
// **ロゴはタイルの左に独立して置く。** 名前の行に入れると名前の高さに
|
|
5581
5788
|
// 縛られて小さくなる。ロゴで能力を見分けられるのが一覧の主な用途
|
|
5582
5789
|
var logo = ADAPTER_ICONS[adapter];
|
|
@@ -5591,10 +5798,15 @@ ${usageFactMissing.toString()}
|
|
|
5591
5798
|
// ——抑制しないと、同じタイルの中で「⏳ 反映中」と「⦿ 未接続」が並んで矛盾する。
|
|
5592
5799
|
// **抑制するのは unset のときだけ**(off / broken は残す。R4 と同じ理由)
|
|
5593
5800
|
// まとめ表がまだ知らない行がある能力は、**行と同じ語**に倒す(未接続とも接続OKとも言わない)
|
|
5594
|
-
|
|
5801
|
+
// 🔴 **導出が答えているなら、「登録状況を確認中です」より導出が勝つ(0.12.0)。**
|
|
5802
|
+
// あの語は「まとめ表しか材料が無く、登録の現在値が分からない」ときの折衷だった。
|
|
5803
|
+
// 正本(registeredNow)を読むようになった今は分かる——外した直後は「未接続」、
|
|
5804
|
+
// 足した直後は「登録済み(まだ取り込みがありません)」と、**何が起きたかを言える**。
|
|
5805
|
+
// ここを逆にすると、まとめ表が追いつく翌朝まで「確認中です」が居座る
|
|
5806
|
+
var pend = capPending(cap) && !derive;
|
|
5595
5807
|
var subEl = el("div", {
|
|
5596
5808
|
class: "ds-tile-sub " + (pend ? DS_GATE_PENDING.cls : "ds-gate-" + st),
|
|
5597
|
-
text: pend ? DS_GATE_PENDING.text : tileSub(cap, st, derived, adapter, internals, sources,
|
|
5809
|
+
text: pend ? DS_GATE_PENDING.text : tileSub(cap, st, derived, adapter, internals, sources, derive),
|
|
5598
5810
|
});
|
|
5599
5811
|
// **反映が届いたら、届いたスナップショットから引き直す。** 下地へ戻すと
|
|
5600
5812
|
// 「⏳ 反映中」→「⦿ 未接続」になる——データが正しくなったあとに嘘をつく。
|
|
@@ -5607,14 +5819,14 @@ ${usageFactMissing.toString()}
|
|
|
5607
5819
|
var stNow = dNow === "unset" && capabilityDisplayState(cap, capsNow) !== "unset"
|
|
5608
5820
|
? "idle"
|
|
5609
5821
|
: capabilityState(cap, stateCtx());
|
|
5610
|
-
//
|
|
5822
|
+
// 正本からの導出も、タイルを作るときと同じ式で引き直す(0.12.0)。
|
|
5611
5823
|
// ここを省くと、反映が届いた瞬間だけ「● 接続OK」へ戻る
|
|
5612
|
-
var
|
|
5613
|
-
if (
|
|
5614
|
-
var pendNow = capPending(cap);
|
|
5824
|
+
var deriveNow = dNow && dNow !== "on" ? null : capDerivation(cap);
|
|
5825
|
+
if (deriveNow) stNow = deriveNow.state;
|
|
5826
|
+
var pendNow = capPending(cap) && !deriveNow;
|
|
5615
5827
|
n.className = "ds-tile-sub " + (pendNow ? DS_GATE_PENDING.cls : "ds-gate-" + stNow);
|
|
5616
5828
|
return {
|
|
5617
|
-
text: pendNow ? DS_GATE_PENDING.text : tileSub(cap, stNow, dNow, adapter, insNow, srcsNow,
|
|
5829
|
+
text: pendNow ? DS_GATE_PENDING.text : tileSub(cap, stNow, dNow, adapter, insNow, srcsNow, deriveNow),
|
|
5618
5830
|
hidden: false,
|
|
5619
5831
|
};
|
|
5620
5832
|
});
|
|
@@ -5653,7 +5865,15 @@ ${usageFactMissing.toString()}
|
|
|
5653
5865
|
// 0.4.70 は unset で入力欄を畳んでいたので注記も抑えていたが、案A で欄を出すようにしたため揃えた
|
|
5654
5866
|
// 導出が無いデータでは判断材料が無いので、注記を出す側(=入力欄と揃う側)に倒す
|
|
5655
5867
|
// 接続先0件の抑止は入力欄と**同じ関数**を通す(keyTargetCount の 🔴 不変条件)
|
|
5656
|
-
|
|
5868
|
+
//
|
|
5869
|
+
// 🔴 **状態の行が既に鍵の不足を言っているなら、ここでは言わない(0.12.0・#225)。**
|
|
5870
|
+
// 取り込みが skipped(鍵が無くて飛ばした)の能力は、2行目が
|
|
5871
|
+
// 「⚠️ APIキーが未登録です」になる。その下にもう一度「⚠ トークンが未設定です」を
|
|
5872
|
+
// 積むと、**同じ1枚のタイルが同じことを2回言う**——#227 が潰した「別々の情報源が
|
|
5873
|
+
// 整合を取らずに1つのタイルに流れ込む」形そのもの
|
|
5874
|
+
var tokenRelevant = (derived != null ? derived !== "off" : true)
|
|
5875
|
+
&& keyTargetCount(cap) > 0
|
|
5876
|
+
&& !(derive && derive.keyMissing);
|
|
5657
5877
|
if (tokenRelevant && sk && (SITE.secretKinds || []).indexOf(sk) >= 0 && !secretPresentForTile(sk)) {
|
|
5658
5878
|
body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ トークンが未設定です" }));
|
|
5659
5879
|
}
|
|
@@ -6018,7 +6238,9 @@ ${usageFactMissing.toString()}
|
|
|
6018
6238
|
btn.addEventListener("click", async function () {
|
|
6019
6239
|
var intake = SITE.intake;
|
|
6020
6240
|
if (!intake) return;
|
|
6021
|
-
|
|
6241
|
+
// 押している間はボタンごと「確認中…」にする(#222・beginBusy の項)。
|
|
6242
|
+
// **語は下の ds-toggle-msg と揃える**——同じ操作を2つの語で言わない
|
|
6243
|
+
var endBusy = beginBusy(btn, "確認中…");
|
|
6022
6244
|
// **押されて初めて枠を出す(2026-08-20・オーナー指摘)。**
|
|
6023
6245
|
// それまでは中身が空でも点線だけが全行に出ており、**何も意味していなかった**。
|
|
6024
6246
|
// 押した結果があるときだけ、行と結果を分ける線になる
|
|
@@ -6043,7 +6265,7 @@ ${usageFactMissing.toString()}
|
|
|
6043
6265
|
// サーバの文言をそのまま出す(回数制限の案内・未対応のアダプタの説明を含んでいる)
|
|
6044
6266
|
msg.className = "ds-toggle-msg is-error";
|
|
6045
6267
|
msg.textContent = "✗ " + (j.message || "確認できませんでした。");
|
|
6046
|
-
|
|
6268
|
+
endBusy();
|
|
6047
6269
|
return;
|
|
6048
6270
|
}
|
|
6049
6271
|
msg.className = "ds-toggle-msg" + (j.gate === "ok" ? " is-ok" : " is-error");
|
|
@@ -6081,7 +6303,7 @@ ${usageFactMissing.toString()}
|
|
|
6081
6303
|
msg.className = "ds-toggle-msg is-error";
|
|
6082
6304
|
msg.textContent = "✗ 通信に失敗しました。";
|
|
6083
6305
|
}
|
|
6084
|
-
|
|
6306
|
+
endBusy();
|
|
6085
6307
|
});
|
|
6086
6308
|
return { btn: btn, detail: wrap };
|
|
6087
6309
|
}
|
|
@@ -6294,13 +6516,17 @@ ${usageFactMissing.toString()}
|
|
|
6294
6516
|
* **「最終同期」(.ds-sync)を置き換えない。** あちらは run 全体の最終成功時刻で、
|
|
6295
6517
|
* どの接続先のことか分からない。こちらは接続先1件の成否。意味が違うので併記する。
|
|
6296
6518
|
*/
|
|
6297
|
-
function ingestedEl(r) {
|
|
6519
|
+
function ingestedEl(r, cap) {
|
|
6298
6520
|
if (!r) return null;
|
|
6299
6521
|
var when = rtAgo(r.at);
|
|
6300
6522
|
var text = "";
|
|
6301
6523
|
if (r.status === "ok") text = when ? "取り込み: " + when + "に成功" : "取り込み: 成功";
|
|
6302
6524
|
else if (r.status === "failed") text = when ? "取り込み: " + when + "に失敗" : "取り込み: 失敗";
|
|
6303
|
-
|
|
6525
|
+
// 🔴 **「スキップ」で終わらせない(#225)。** 鍵が無くて飛ばしたのが skipped の実体で、
|
|
6526
|
+
// 「スキップ」とだけ書かれても**次に何をすればいいのか読み取れない**。しかも
|
|
6527
|
+
// 「失敗」と並べて置くと、原因を調べに行く側へ人を送ってしまう(直すのは鍵の登録)。
|
|
6528
|
+
// 語は鍵カードの名前と揃える(keyMissingText の項)
|
|
6529
|
+
else if (r.status === "skipped") text = "取り込み: 未実行(" + keyMissingText(cap) + ")";
|
|
6304
6530
|
if (!text) return null;
|
|
6305
6531
|
return el("span", {
|
|
6306
6532
|
class: "ds-ingest" + (r.status === "failed" ? " ds-ingest-failed" : ""),
|
|
@@ -6425,7 +6651,7 @@ ${usageFactMissing.toString()}
|
|
|
6425
6651
|
dsNameEl(t.domain + " / " + t.projectKey, "https://" + t.domain + "/projects/" + t.projectKey),
|
|
6426
6652
|
]),
|
|
6427
6653
|
// この接続先の取り込みが最後にどうなったか(段C)。測っていなければ null
|
|
6428
|
-
ingestedEl(ingestedStateOf(t.domain, t.projectKey)),
|
|
6654
|
+
ingestedEl(ingestedStateOf(t.domain, t.projectKey), "課題管理"),
|
|
6429
6655
|
]);
|
|
6430
6656
|
if (!editable) return row;
|
|
6431
6657
|
var btn = el("button", { class: "ds-toggle-btn ds-danger", type: "button", text: "この接続先を外す" });
|
|
@@ -6485,12 +6711,18 @@ ${usageFactMissing.toString()}
|
|
|
6485
6711
|
// 呼ばれるので、作り直すと**接続先を1つ足しただけで入力中の鍵と「保存中…」が消える**
|
|
6486
6712
|
//(ライブ差し替えで踏んだのと同じ事故が、別の経路で復活する)。
|
|
6487
6713
|
// appendChild は既存ノードを移すだけなので、入力値もイベントも保たれる
|
|
6488
|
-
|
|
6489
|
-
|
|
6714
|
+
//
|
|
6715
|
+
// 🔴 **見るのは正本(KEY_SLOTS.backlog)。節の中の控えを持たない(#223)。**
|
|
6716
|
+
// 応答で新しいスペースが現れたら受け皿はその場で足される(ensureKeySlots)ので、
|
|
6717
|
+
// 節を作ったときの控えを見ていると**足した受け皿がここへ来ない**=
|
|
6718
|
+
// 鍵カードが行の中に入らないまま宙に浮く
|
|
6719
|
+
var slots = KEY_SLOTS.backlog || {};
|
|
6720
|
+
for (var d in slots) {
|
|
6721
|
+
if (!Object.prototype.hasOwnProperty.call(slots, d)) continue;
|
|
6490
6722
|
if (!d) continue; // 接続先が無い案件の受け皿は box 側に置いてある(行が無い)
|
|
6491
6723
|
var host = lastRowOf[d];
|
|
6492
|
-
if (host) host.appendChild(
|
|
6493
|
-
else if (
|
|
6724
|
+
if (host) host.appendChild(slots[d]);
|
|
6725
|
+
else if (slots[d].parentNode) slots[d].parentNode.removeChild(slots[d]);
|
|
6494
6726
|
}
|
|
6495
6727
|
// **0件を黙って見せない。** 表示が無いと「まだ何も無い」のか「出し忘れ」なのか
|
|
6496
6728
|
// 区別できない。**一覧の外**(Figma / Drive と同じ)に、件数に応じて出し入れする
|
|
@@ -6525,10 +6757,75 @@ ${usageFactMissing.toString()}
|
|
|
6525
6757
|
// **鍵の欄の判断もこの一覧に従わせる(2026-08-22)。** keyTargetCount はまとめ表を
|
|
6526
6758
|
// 見るので、ここで教えないと「行はその場で出るのに鍵の欄だけ次の取り直しまで出ない」
|
|
6527
6759
|
ADOPTED_TARGET_COUNT = targets.length;
|
|
6760
|
+
// 🔴 **新しいスペースの鍵の欄を、その場で足す(#223)。** paint() より先に呼ぶ
|
|
6761
|
+
//(paint は在る受け皿を行へ入れるだけで、無い受け皿は作らない)
|
|
6762
|
+
ensureKeySlots();
|
|
6528
6763
|
paint();
|
|
6529
6764
|
return targetsGenerationOf(targets) !== before;
|
|
6530
6765
|
}
|
|
6531
6766
|
|
|
6767
|
+
/**
|
|
6768
|
+
* 応答や取り直しで**新しいスペースが現れたら**、その分の受け皿と鍵カードを足す(#223)。
|
|
6769
|
+
*
|
|
6770
|
+
* ## 直している欠陥
|
|
6771
|
+
*
|
|
6772
|
+
* 受け皿は「モーダルを開いたその瞬間の接続先の集合」から1回だけ作られていた。
|
|
6773
|
+
* その後モーダルを開いたまま別のスペース(例: beams-dev.backlog.com)の接続先を
|
|
6774
|
+
* 足すと、**行は増えるのに鍵を入れる欄がどこにも現れない**——しかも画面は
|
|
6775
|
+
* 「保存しました」と言うので、成功したようにしか見えない。フルリロードするまで
|
|
6776
|
+
* 気づけなかった(#223・受け入れテストで実際に踏んだ)。
|
|
6777
|
+
*
|
|
6778
|
+
* ## 足すだけにする理由
|
|
6779
|
+
*
|
|
6780
|
+
* 単純な「全部作り直す」に戻すと、**入力中の鍵と「保存中…」が消える**——
|
|
6781
|
+
* その再発を防ぐために「作り直さない」と決めたのが元の実装(paint の項)。
|
|
6782
|
+
* だから**既存スペースの受け皿とカードには一切触らない**。
|
|
6783
|
+
*
|
|
6784
|
+
* ⚠ **既存カードの spaceCount は古いままになる。** 1スペースだった案件に2つ目が
|
|
6785
|
+
* 増えると、先にあったカードは「1スペースぶんの答え方」(種別ごとの登録状況)を
|
|
6786
|
+
* 続ける。作り直せば揃うが、それは入力中の値を捨てるということ——**モーダルを
|
|
6787
|
+
* 開き直せば揃う**ほうを取る(登録状況の表示が少し弱くなるだけで、嘘は出ない)。
|
|
6788
|
+
*/
|
|
6789
|
+
function ensureKeySlots() {
|
|
6790
|
+
if (!editable) return;
|
|
6791
|
+
// 受け皿を作る経路を通っていない案件(intake が無い・固定IP同期)はここも通さない
|
|
6792
|
+
if (!KEY_SLOTS.backlog || !Object.keys(KEY_SLOTS.backlog).length) return;
|
|
6793
|
+
if ((SITE.secretKinds || []).indexOf("backlog") < 0 || !SECRET_FORMS["backlog"]) return;
|
|
6794
|
+
var domains = [];
|
|
6795
|
+
for (var i = 0; i < targets.length; i++) {
|
|
6796
|
+
var dm = String(targets[i].domain || "").toLowerCase();
|
|
6797
|
+
if (dm && domains.indexOf(dm) === -1) domains.push(dm);
|
|
6798
|
+
}
|
|
6799
|
+
// **0件の案件の受け皿(空文字キー)は、1スペースぶんまでは役目を果たしている。**
|
|
6800
|
+
// あれは「どのスペースとも結び付いていない」欄で、サーバは名簿の値に倒す
|
|
6801
|
+
// ——接続先が1スペースなら、それで正しい先に入る
|
|
6802
|
+
if (KEY_SLOTS.backlog[""] && domains.length <= 1) return;
|
|
6803
|
+
var added = false;
|
|
6804
|
+
for (var di = 0; di < domains.length; di++) {
|
|
6805
|
+
var dom = domains[di];
|
|
6806
|
+
if (KEY_SLOTS.backlog[dom]) continue;
|
|
6807
|
+
var slot = el("div", { class: "ds-keyrow" });
|
|
6808
|
+
slot.appendChild(secretCard("backlog", SECRET_FORMS["backlog"], {
|
|
6809
|
+
// 名乗ってよいのは名簿由来の行だけ(作るときと同じ判断・KEY_SLOTS.backlogNamed)
|
|
6810
|
+
space: KEY_SLOTS.backlogNamed ? dom : "",
|
|
6811
|
+
stateSpace: dom,
|
|
6812
|
+
spaceCount: domains.length,
|
|
6813
|
+
inRow: true,
|
|
6814
|
+
}));
|
|
6815
|
+
KEY_SLOTS.backlog[dom] = slot;
|
|
6816
|
+
added = true;
|
|
6817
|
+
}
|
|
6818
|
+
if (!added) return;
|
|
6819
|
+
// **名無しの受け皿は役目を終える。** スペースを名乗る欄と名乗らない欄が同時に出ると、
|
|
6820
|
+
// どちらに貼れば効くのかが読めない(名乗らない欄は名簿の1スペースに入る)
|
|
6821
|
+
var blank = KEY_SLOTS.backlog[""];
|
|
6822
|
+
if (blank) {
|
|
6823
|
+
if (blank.parentNode) blank.parentNode.removeChild(blank);
|
|
6824
|
+
delete KEY_SLOTS.backlog[""];
|
|
6825
|
+
}
|
|
6826
|
+
syncKeyCardVisibility();
|
|
6827
|
+
}
|
|
6828
|
+
|
|
6532
6829
|
function targetsHooks() {
|
|
6533
6830
|
return { onTargets: adoptTargets, staleNote: TARGETS_RACE_NOTE };
|
|
6534
6831
|
}
|
|
@@ -6542,7 +6839,6 @@ ${usageFactMissing.toString()}
|
|
|
6542
6839
|
// **この画面から投入できない案件(固定IP同期)には出さない。** 押しても Lambda が
|
|
6543
6840
|
// 「固定IP経由のため対応していません」で断るので、導線そのものを作らない。
|
|
6544
6841
|
// 判定は鍵カードを出すループの条件と同じ線(SITE.secretKinds に backlog があるか)。
|
|
6545
|
-
var keySlots = {};
|
|
6546
6842
|
if (editable && !Object.keys(KEY_SLOTS.backlog).length) {
|
|
6547
6843
|
// 保存時にスペースを名乗ってよいのは、名簿(fleet の backlogTarget)由来の行だけ。
|
|
6548
6844
|
// 同期ミラーのURLから作った行で名乗ると、名簿とミラーがずれている案件で
|
|
@@ -6562,8 +6858,8 @@ ${usageFactMissing.toString()}
|
|
|
6562
6858
|
//(足した接続先のドメインに対応するスロットは無いので、鍵の欄はこの受け皿に
|
|
6563
6859
|
// 残ったまま一覧の下に現れる。paint() はこのキーを移動の対象から外している)
|
|
6564
6860
|
if (!domains.length) domains.push("");
|
|
6565
|
-
|
|
6566
|
-
KEY_SLOTS.backlog =
|
|
6861
|
+
// **正本へ直接入れる**(節の中に控えを持たない・paint / ensureKeySlots の項)
|
|
6862
|
+
for (var di = 0; di < domains.length; di++) KEY_SLOTS.backlog[domains[di]] = el("div", { class: "ds-keyrow" });
|
|
6567
6863
|
}
|
|
6568
6864
|
|
|
6569
6865
|
// **box を先に作る。** paint() は 0件の1行を box へ出し入れするので、
|
|
@@ -6590,10 +6886,12 @@ ${usageFactMissing.toString()}
|
|
|
6590
6886
|
if (targetsGenerationOf(next) === targetsGenerationOf(targets) && sig === lastIngestSig) return;
|
|
6591
6887
|
targets = next;
|
|
6592
6888
|
lastIngestSig = sig;
|
|
6889
|
+
// 取り直しで新しいスペースが届くこともある(他の人が足した場合・#223)
|
|
6890
|
+
ensureKeySlots();
|
|
6593
6891
|
paint();
|
|
6594
6892
|
};
|
|
6595
6893
|
// 接続先がまだ無い案件の受け皿は、0件の1行の直後に置く(行が無いので中へ入れられない)
|
|
6596
|
-
if (
|
|
6894
|
+
if (KEY_SLOTS.backlog[""]) box.appendChild(KEY_SLOTS.backlog[""]);
|
|
6597
6895
|
if (editable) {
|
|
6598
6896
|
box.appendChild(addForm({
|
|
6599
6897
|
// **「追加」に戻す(#127)。** 1件しか持てなかった頃は「保存」(置き換え)だったが、
|
|
@@ -6885,9 +7183,24 @@ ${usageFactMissing.toString()}
|
|
|
6885
7183
|
* 据え置き、他の行の取り込み表示は普段どおり追いつかせる(removeButton の onBusy)。
|
|
6886
7184
|
*/
|
|
6887
7185
|
var figSending = {};
|
|
7186
|
+
// **ファイル一覧と鍵カードを1つの枠に括る(#229・オーナー指摘)。**
|
|
7187
|
+
//
|
|
7188
|
+
// Figma の鍵は**案件に1本**で全ファイル共通なので、特定のファイルの行の中には
|
|
7189
|
+
// 置けない(「このファイル専用」という嘘の見た目になる)。その結果、鍵カードが
|
|
7190
|
+
// レコードの外に浮いて見え、**Backlog(行の中に鍵がある)と見比べると
|
|
7191
|
+
// 「入れ損ねた」ようにしか見えなかった**。
|
|
7192
|
+
//
|
|
7193
|
+
// 枠で括ると「この枠の中のもの全部に効く鍵」が図として読める。文言の側も
|
|
7194
|
+
// 鍵カードの scopeNote が同じことを言う——**見た目と言葉の両方**で、
|
|
7195
|
+
// Backlog とのスコープの違いが伝わるようにする(片方だけだと読み落とされる)。
|
|
7196
|
+
// **見出しは付けない。** 接続先が0件のあいだ鍵カードは畳まれる(syncKeyCardVisibility)
|
|
7197
|
+
// ので、「…と、共通の鍵」のような見出しは**鍵が出ていないときに嘘になる**。
|
|
7198
|
+
// 枠そのものと、鍵カードの中の scopeNote が意味を運ぶ
|
|
7199
|
+
var figGroup = el("div", { class: "ds-scope-group" });
|
|
7200
|
+
box.appendChild(figGroup);
|
|
6888
7201
|
// **署名に取り込みの状態も混ぜる(段C第二弾)。** 一覧のJSONだけだと、応答が返る前に
|
|
6889
7202
|
// モーダルを開いた人には取り込みが1件も出ない(designIngestSigOf の項に経緯)
|
|
6890
|
-
|
|
7203
|
+
figGroup.appendChild(liveList("デザイン", function () {
|
|
6891
7204
|
return JSON.stringify(figsOf()) + "|" + designIngestSigOf(figsOf());
|
|
6892
7205
|
}, function (listBox) {
|
|
6893
7206
|
var figs = figsOf();
|
|
@@ -6916,7 +7229,7 @@ ${usageFactMissing.toString()}
|
|
|
6916
7229
|
dsNameEl(f.name || f.key, "https://www.figma.com/design/" + f.key),
|
|
6917
7230
|
]),
|
|
6918
7231
|
// このファイルの取り込みが最後にどうなったか(段C)。測っていなければ null
|
|
6919
|
-
ingestedEl(designIngestedStateOf(f.key)),
|
|
7232
|
+
ingestedEl(designIngestedStateOf(f.key), "デザイン"),
|
|
6920
7233
|
]);
|
|
6921
7234
|
row.appendChild(removeButton({
|
|
6922
7235
|
label: "このファイルを外す",
|
|
@@ -6973,7 +7286,8 @@ ${usageFactMissing.toString()}
|
|
|
6973
7286
|
//(intake / OFF のゲートをループ側に残すため。KEY_SLOTS のコメント参照)
|
|
6974
7287
|
if (!KEY_SLOTS.figma) {
|
|
6975
7288
|
KEY_SLOTS.figma = el("div", { class: "ds-keyrow ds-keyrow-list" });
|
|
6976
|
-
|
|
7289
|
+
// **枠の中に入れる**(一覧と同じ枠=この鍵が効く範囲・#229)
|
|
7290
|
+
figGroup.appendChild(KEY_SLOTS.figma);
|
|
6977
7291
|
}
|
|
6978
7292
|
box.appendChild(addForm({
|
|
6979
7293
|
title: "+ Figmaファイルを追加",
|
|
@@ -7116,6 +7430,48 @@ ${usageFactMissing.toString()}
|
|
|
7116
7430
|
// 確認 → 送信 → 結果表示、までを1箇所に置く。操作ごとに書き写すと、
|
|
7117
7431
|
// 片方だけ直したときにずれる(トグル・削除・追加のどれも同じ経路を通す)。
|
|
7118
7432
|
|
|
7433
|
+
/**
|
|
7434
|
+
* 送信中のボタンの見た目(#222)。**押せたかどうかが操作中に分かるようにする。**
|
|
7435
|
+
*
|
|
7436
|
+
* ## なぜ disabled だけでは足りなかったのか
|
|
7437
|
+
*
|
|
7438
|
+
* 送信系はどれも btn.disabled = true はしていた。ところが受け入れテストで
|
|
7439
|
+
* 「この接続先を外す」を押したとき、**画面に何の変化も起きなかった**——disabled
|
|
7440
|
+
* の見た目の差が薄く(opacity .6)、しかも押した直後に視線が行くのはボタン自身。
|
|
7441
|
+
* 「処理が本当に走っているのか、二重クリックしてよいのか」が分からない。
|
|
7442
|
+
*
|
|
7443
|
+
* **ボタンの語そのものを「処理中…」に変える。** 状態表示(ds-toggle-msg)は
|
|
7444
|
+
* ボタンの隣にあって視線の外に出やすいので、**押した場所が変わる**ことが要る。
|
|
7445
|
+
*
|
|
7446
|
+
* ## 3経路で同じ実装を通す
|
|
7447
|
+
*
|
|
7448
|
+
* 送信系は sendConfig(追加・削除・トグル)/secretCard(鍵の保存)/
|
|
7449
|
+
* probeSection(接続確認)の3つ。**同じ見た目を3回書かない**——今後アダプタが
|
|
7450
|
+
* 増えても、この関数を通す限り見た目が揃う(#222 の「共通コンポーネント化」)。
|
|
7451
|
+
*
|
|
7452
|
+
* ⚠ **スケルトンまでは作らない。** 影響を受ける行を骨組み表示にするのは DOM パッチの
|
|
7453
|
+
* 範囲を超える(行の作り直しは入力中の鍵と「保存中…」を壊してきた当のもの)。
|
|
7454
|
+
* 「押せたか分からない」はボタンの無効化+語の差し替えで死ぬ。
|
|
7455
|
+
*
|
|
7456
|
+
* @returns 元へ戻す関数。**何度呼んでも1回しか効かない**(finally と成功パスの
|
|
7457
|
+
* 両方から呼べるようにするため)。引数に語を渡すと、その語で戻す
|
|
7458
|
+
*/
|
|
7459
|
+
function beginBusy(btn, label) {
|
|
7460
|
+
if (!btn) return function () {};
|
|
7461
|
+
var before = btn.textContent;
|
|
7462
|
+
btn.disabled = true;
|
|
7463
|
+
btn.classList.add("is-busy");
|
|
7464
|
+
btn.textContent = label || "処理中…";
|
|
7465
|
+
var used = false;
|
|
7466
|
+
return function endBusy(nextLabel) {
|
|
7467
|
+
if (used) return;
|
|
7468
|
+
used = true;
|
|
7469
|
+
btn.disabled = false;
|
|
7470
|
+
btn.classList.remove("is-busy");
|
|
7471
|
+
btn.textContent = nextLabel == null ? before : nextLabel;
|
|
7472
|
+
};
|
|
7473
|
+
}
|
|
7474
|
+
|
|
7119
7475
|
/**
|
|
7120
7476
|
* サーバへ設定変更を送る。**一覧は書き換えない**
|
|
7121
7477
|
* (接続状況は夜間バッチのスナップショットなので、「もう効いている」と誤解させないため)。
|
|
@@ -7132,7 +7488,8 @@ ${usageFactMissing.toString()}
|
|
|
7132
7488
|
var intake = SITE.intake;
|
|
7133
7489
|
if (!intake) return;
|
|
7134
7490
|
var pfx = intake.url + (intake.url.indexOf("?") === -1 ? "?" : "&");
|
|
7135
|
-
|
|
7491
|
+
// 押している間はボタンごと「処理中…」にする(#222・beginBusy の項)
|
|
7492
|
+
var endBusy = beginBusy(btn);
|
|
7136
7493
|
msg.className = "ds-toggle-msg";
|
|
7137
7494
|
msg.textContent = "保存中…";
|
|
7138
7495
|
try {
|
|
@@ -7172,7 +7529,8 @@ ${usageFactMissing.toString()}
|
|
|
7172
7529
|
if (canConfirm) {
|
|
7173
7530
|
// **ボタンは必ず先に戻す。** 確認の前に戻しておかないと、「いいえ」を押した人の
|
|
7174
7531
|
// フォームが押せないまま残り、再読み込みするまで追加できなくなる
|
|
7175
|
-
|
|
7532
|
+
// (語も戻す——確認ダイアログの裏で「処理中…」のままにしない)
|
|
7533
|
+
endBusy();
|
|
7176
7534
|
msg.className = "ds-toggle-msg";
|
|
7177
7535
|
msg.textContent = "";
|
|
7178
7536
|
if (await hooks.onDuplicate()) {
|
|
@@ -7193,7 +7551,7 @@ ${usageFactMissing.toString()}
|
|
|
7193
7551
|
// 画面は既に追いついているので、済んでいることをやらせる案内になる(#127)
|
|
7194
7552
|
msg.className = "ds-toggle-msg is-error";
|
|
7195
7553
|
msg.textContent = "✗ " + ((res.status === 409 && raced && hooks.staleNote) || j.message || "保存できませんでした。");
|
|
7196
|
-
|
|
7554
|
+
endBusy();
|
|
7197
7555
|
return;
|
|
7198
7556
|
}
|
|
7199
7557
|
msg.className = "ds-toggle-msg is-ok";
|
|
@@ -7214,7 +7572,9 @@ ${usageFactMissing.toString()}
|
|
|
7214
7572
|
msg.appendChild(document.createTextNode(" "));
|
|
7215
7573
|
msg.appendChild(el("a", { href: j.refreshUrl, target: "_blank", rel: "noopener", text: "反映の状況 →" }));
|
|
7216
7574
|
}
|
|
7217
|
-
|
|
7575
|
+
// 🔴 **語を戻すのは onDone より先。** removeButton の onDone は押せない状態へ
|
|
7576
|
+
// 戻す(外した行のボタン)ので、後に置くと「処理中…」の語が居座る
|
|
7577
|
+
endBusy();
|
|
7218
7578
|
// **反映待ちとして記録する。** 一覧はスナップショットから描かれるので、
|
|
7219
7579
|
// 保存できていても数分は「未設定」と出る。その間だけ「⏳ 反映中」に差し替える。
|
|
7220
7580
|
//
|
|
@@ -7229,7 +7589,7 @@ ${usageFactMissing.toString()}
|
|
|
7229
7589
|
// 描かれるので、保存しただけでは行が出ない。ここで読み直すと、登録ファイルの中身が
|
|
7230
7590
|
// 返ってきて**足りない行として一覧に出る**——さらに「まとめ表がまだ答えていない行」が
|
|
7231
7591
|
// できるので、まとめ表が追いつくまで取り直しが続く。
|
|
7232
|
-
//
|
|
7592
|
+
// 取得中なら freshWanted の旗だけ立てて戻る(応答後にもう一度取りに行く)
|
|
7233
7593
|
if (j.changed !== false) loadLiveFleet(true);
|
|
7234
7594
|
// **何も変わっていないなら「追加した」と見せない。** 既に同じものが入っていた場合
|
|
7235
7595
|
// サーバは 200 + changed:false を返す。ここを res.ok だけで判定すると
|
|
@@ -7238,7 +7598,7 @@ ${usageFactMissing.toString()}
|
|
|
7238
7598
|
} catch (e) {
|
|
7239
7599
|
msg.className = "ds-toggle-msg is-error";
|
|
7240
7600
|
msg.textContent = "✗ 通信に失敗しました。";
|
|
7241
|
-
|
|
7601
|
+
endBusy();
|
|
7242
7602
|
}
|
|
7243
7603
|
}
|
|
7244
7604
|
|
|
@@ -7955,15 +8315,37 @@ ${usageFactMissing.toString()}
|
|
|
7955
8315
|
*/
|
|
7956
8316
|
var liveFleetRequested = false;
|
|
7957
8317
|
/**
|
|
7958
|
-
*
|
|
8318
|
+
* op=state に送る能力。**常に全能力**(0.12.0・#231 の正本化)。
|
|
8319
|
+
*
|
|
8320
|
+
* ## なぜ「開いた能力だけ」をやめたのか
|
|
8321
|
+
*
|
|
8322
|
+
* 応答は**丸ごと置き換わる**(LIVE_REGISTERED / LIVE_CAPS)。開いた能力だけを
|
|
8323
|
+
* 要求していると、**モーダルを1つ開いた瞬間にその能力だけの caps でリクエストが飛び、
|
|
8324
|
+
* 他5能力が応答から欠落する**——タイルの導出は材料が無ければ規則5(fleet への
|
|
8325
|
+
* フォールバック)へ落ちるので、**開いていない5能力のタイルが一斉に退行する**。
|
|
8326
|
+
* 「初回だけ全量・以降は開いた能力のみ」の2段構えでも、置き換えが起きる限り同じ穴が開く。
|
|
8327
|
+
*
|
|
8328
|
+
* **常に全量なら応答は常に完全**で、置き換えのままで正しい。
|
|
8329
|
+
*
|
|
8330
|
+
* ## GitHub の枠は問題にならない
|
|
7959
8331
|
*
|
|
7960
|
-
*
|
|
7961
|
-
*
|
|
8332
|
+
* op=state が飛ぶのは**ページを開いたとき・モーダルを開いたとき・反映待ちが残って
|
|
8333
|
+
* いる間の取り直し**だけで、恒常ポーリングは存在しない(stateTick は settle で止まる)。
|
|
8334
|
+
* しかも Lambda は 「projectKey + caps集合」 でキャッシュ(60秒)するので、**全ビューアが
|
|
8335
|
+
* 同じキーを共有する**——能力ごとに違うキーを作っていた頃より、むしろ当たりやすい。
|
|
7962
8336
|
*
|
|
7963
|
-
*
|
|
7964
|
-
*
|
|
8337
|
+
* ⚠ **並べ替えてから送る。** サーバのキャッシュキーは caps を並べ替えて繋ぐが、
|
|
8338
|
+
* こちらも定数の並びを固定しておく(同じ集合が何通りにも増えない)。
|
|
7965
8339
|
*/
|
|
7966
|
-
var
|
|
8340
|
+
var ALL_STATE_CAPS = Object.keys(STATE_CAP_NAMES).sort();
|
|
8341
|
+
/**
|
|
8342
|
+
* 一度でも開いた能力。**送る caps とは無関係**(送るのは常に全量)。
|
|
8343
|
+
*
|
|
8344
|
+
* 残してあるのは「モーダルを初めて開いたときに1回だけ取り直す」ためだけ。
|
|
8345
|
+
* 開いた瞬間のいまの値でモーダルを描きたいので取りに行くが、二度目からは
|
|
8346
|
+
* 反映待ちの取り直しに任せる(押すたびに Lambda を叩かない)。
|
|
8347
|
+
*/
|
|
8348
|
+
var OPENED_CAPS = [];
|
|
7967
8349
|
/**
|
|
7968
8350
|
* 画面の能力名 → 接続状態ストアの能力名(対応表の実体は STATE_CAP_NAMES)。
|
|
7969
8351
|
*
|
|
@@ -7985,26 +8367,20 @@ ${usageFactMissing.toString()}
|
|
|
7985
8367
|
}
|
|
7986
8368
|
return null;
|
|
7987
8369
|
}
|
|
7988
|
-
/**
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
8370
|
+
/**
|
|
8371
|
+
* 能力を開いた。**初回だけ取り直す。**
|
|
8372
|
+
*
|
|
8373
|
+
* ⚠ **caps は増えない**(常に全量・ALL_STATE_CAPS の項)。以前ここにあった
|
|
8374
|
+
* 「取得中に開かれた能力を取りこぼさない」ための追いかけ(catchUpOpenCaps /
|
|
8375
|
+
* liveFleetSentCaps)は、要求の中身が能力によって変わらなくなったので不要になった
|
|
8376
|
+
* ——取得中の応答にも、その能力は最初から載っている。
|
|
8377
|
+
*/
|
|
7994
8378
|
function noteOpenCap(cap) {
|
|
7995
8379
|
var id = stateCapIdOf(cap);
|
|
7996
|
-
if (!id ||
|
|
7997
|
-
|
|
8380
|
+
if (!id || OPENED_CAPS.indexOf(id) >= 0) return;
|
|
8381
|
+
OPENED_CAPS.push(id);
|
|
7998
8382
|
loadLiveFleet();
|
|
7999
8383
|
}
|
|
8000
|
-
/**
|
|
8001
|
-
* 直近の取得に載せた能力の一覧。
|
|
8002
|
-
*
|
|
8003
|
-
* **取得中に能力を開いた分を取りこぼさないため。** 取得中の loadLiveFleet は
|
|
8004
|
-
* 何もせずに戻るので、これが無いと「開いたのに、その能力だけ永久に読まれない」
|
|
8005
|
-
* (しかも次の取り直しは未確定の行が無いので張られない=二度と追いつかない)。
|
|
8006
|
-
*/
|
|
8007
|
-
var liveFleetSentCaps = [];
|
|
8008
8384
|
/**
|
|
8009
8385
|
* 次の取得で据え置きを迂回するか(保存が効いた直後の1回だけ)。
|
|
8010
8386
|
*
|
|
@@ -8025,18 +8401,17 @@ ${usageFactMissing.toString()}
|
|
|
8025
8401
|
// 素で呼ぶと描画中の ReferenceError になり、「画面が例外を出さずに描き切る」検査を割る。
|
|
8026
8402
|
if (typeof fetch !== "function") return;
|
|
8027
8403
|
liveFleetRequested = true;
|
|
8028
|
-
liveFleetSentCaps = OPEN_CAPS.slice();
|
|
8029
8404
|
var useFresh = freshWanted;
|
|
8030
8405
|
freshWanted = false;
|
|
8031
8406
|
var pfx = intake.url + (intake.url.indexOf("?") === -1 ? "?" : "&");
|
|
8032
8407
|
fetch(pfx + "op=state&t=" + encodeURIComponent(intake.token), {
|
|
8033
8408
|
method: "POST",
|
|
8034
8409
|
headers: { "content-type": "application/json" },
|
|
8035
|
-
//
|
|
8036
|
-
//
|
|
8410
|
+
// **常に全能力を要求する(ALL_STATE_CAPS の項)。** 知らない値は Lambda 側が黙って
|
|
8411
|
+
// 捨てる(断らない)ので、画面と Lambda の版が食い違っても接続状況の表示は消えない
|
|
8037
8412
|
// **保存直後だけ据え置きを迂回させる。** これが無いと、サーバが保存前の答えを返し、
|
|
8038
8413
|
// 足りない行が0件になって取り直しも張られない=手で再読み込みするまで追いつかない
|
|
8039
|
-
body: JSON.stringify(useFresh ? { caps:
|
|
8414
|
+
body: JSON.stringify(useFresh ? { caps: ALL_STATE_CAPS, fresh: true } : { caps: ALL_STATE_CAPS }),
|
|
8040
8415
|
})
|
|
8041
8416
|
// **非2xxも失敗として扱う。** ビューアとLambdaの版は独立に進むので、
|
|
8042
8417
|
// op=state 未対応のLambdaは 400 を返す。通信エラーだけを見ていると空表示になる。
|
|
@@ -8068,7 +8443,7 @@ ${usageFactMissing.toString()}
|
|
|
8068
8443
|
// 🔴 **図の降格は refreshDataSources の外で呼ぶ(#235 の表示層)。** あちらは
|
|
8069
8444
|
// モーダルが開いている間・入力中は何もせずに戻るので、中に入れると
|
|
8070
8445
|
// **モーダルを開いたまま最初の応答が届いた回だけ、図が「接続OK」のまま残る**
|
|
8071
|
-
|
|
8446
|
+
applyStateToConnMap();
|
|
8072
8447
|
// **モーダルを開いたままでも「反映中」だけは追いつかせる。** refreshDataSources は
|
|
8073
8448
|
// モーダルが開いている間・入力中は何もしない(下地を入れ替えると開いているモーダルの
|
|
8074
8449
|
// 中身が切り離される)ので、局所的に差し替える口をここに置く
|
|
@@ -8078,7 +8453,6 @@ ${usageFactMissing.toString()}
|
|
|
8078
8453
|
refreshStateTimer();
|
|
8079
8454
|
// 取得中に保存が効いていたら、迂回付きでもう一度取りに行く
|
|
8080
8455
|
if (freshWanted) loadLiveFleet();
|
|
8081
|
-
catchUpOpenCaps();
|
|
8082
8456
|
})
|
|
8083
8457
|
.catch(function () {
|
|
8084
8458
|
// 静的ビルドの値で描き続ける。ただし「古いかもしれない」ことは画面に出す
|
|
@@ -8094,7 +8468,6 @@ ${usageFactMissing.toString()}
|
|
|
8094
8468
|
refreshStateTimer();
|
|
8095
8469
|
// 取得中に保存が効いていたら、迂回付きでもう一度取りに行く
|
|
8096
8470
|
if (freshWanted) loadLiveFleet();
|
|
8097
|
-
catchUpOpenCaps();
|
|
8098
8471
|
});
|
|
8099
8472
|
}
|
|
8100
8473
|
|
|
@@ -8425,6 +8798,16 @@ ${usageFactMissing.toString()}
|
|
|
8425
8798
|
var stateSpace = (opts && opts.stateSpace) || "";
|
|
8426
8799
|
var spaceCount = (opts && opts.spaceCount) || 0;
|
|
8427
8800
|
var inRow = !!(opts && opts.inRow);
|
|
8801
|
+
/**
|
|
8802
|
+
* 鍵がどこまで効くのか(#229)。**置き場所だけでは伝わらないので、言葉にする。**
|
|
8803
|
+
*
|
|
8804
|
+
* Backlog の鍵は**スペースごと**に別なので、そのスペースの行の中に置いてある。
|
|
8805
|
+
* Figma の鍵は**案件に1本**で全ファイル共通なので、特定のファイルの行に入れると
|
|
8806
|
+
* 「このファイル専用」という嘘の見た目になり、一覧の外に置いてある。
|
|
8807
|
+
* 理屈は通っているが、**ファイルが1件だけの画面では「行の中に入れ損ねた」ように
|
|
8808
|
+
* しか見えない**(オーナー指摘)。枠で括ったうえで、この1行で言い切る。
|
|
8809
|
+
*/
|
|
8810
|
+
var scopeNote = (opts && opts.scopeNote) || "";
|
|
8428
8811
|
// **いま登録されているか**を先に出す。入れ直したのに反映されていない状態が
|
|
8429
8812
|
// 画面で分かるようにする(接続状況は夜間バッチのスナップショットなので、
|
|
8430
8813
|
// 保存の直後は前日の値のまま。時刻は上の節に出している)。
|
|
@@ -8456,7 +8839,8 @@ ${usageFactMissing.toString()}
|
|
|
8456
8839
|
var value = input.value;
|
|
8457
8840
|
if (!value.trim()) { msg.className = "ds-toggle-msg is-error"; msg.textContent = "⚠ トークンを入力してください。"; return; }
|
|
8458
8841
|
|
|
8459
|
-
|
|
8842
|
+
// 押している間はボタンごと「処理中…」にする(#222・beginBusy の項)
|
|
8843
|
+
var endBusy = beginBusy(btn);
|
|
8460
8844
|
msg.className = "ds-toggle-msg";
|
|
8461
8845
|
msg.textContent = "保存中…";
|
|
8462
8846
|
var intake = SITE.intake;
|
|
@@ -8476,10 +8860,15 @@ ${usageFactMissing.toString()}
|
|
|
8476
8860
|
if (!res.ok) {
|
|
8477
8861
|
msg.className = "ds-toggle-msg is-error";
|
|
8478
8862
|
msg.textContent = "✗ " + (j.message || "保存できませんでした。");
|
|
8479
|
-
|
|
8863
|
+
endBusy();
|
|
8480
8864
|
return;
|
|
8481
8865
|
}
|
|
8482
8866
|
msg.className = "ds-toggle-msg is-ok";
|
|
8867
|
+
// 🔴 **「保存中…」を消してから成功を書く(#220)。** ここが抜けていて、実機の
|
|
8868
|
+
// 画面に「保存中…✓ 保存しました。同期を開始しました。」と**2つの状態が同居**して
|
|
8869
|
+
// いた。appendChild は足すだけなので、消す一文が要る(sendConfig 側には在った)。
|
|
8870
|
+
// **語を書く前に必ずここを通すこと**——次に文面を足す人が同じ形で踏む
|
|
8871
|
+
msg.textContent = "";
|
|
8483
8872
|
msg.appendChild(document.createTextNode("✓ " + (j.message || "保存しました。")));
|
|
8484
8873
|
// 起動できたときだけ、実行状況への導線を出す(鍵を入れた直後に効いたか確かめられる)
|
|
8485
8874
|
if (j.runsUrl) {
|
|
@@ -8488,11 +8877,11 @@ ${usageFactMissing.toString()}
|
|
|
8488
8877
|
}
|
|
8489
8878
|
// 入れた値は画面に残さない(肩越しに見えるのを防ぐ)
|
|
8490
8879
|
input.value = "";
|
|
8491
|
-
|
|
8880
|
+
endBusy();
|
|
8492
8881
|
} catch (e) {
|
|
8493
8882
|
msg.className = "ds-toggle-msg is-error";
|
|
8494
8883
|
msg.textContent = "✗ 通信に失敗しました。";
|
|
8495
|
-
|
|
8884
|
+
endBusy();
|
|
8496
8885
|
}
|
|
8497
8886
|
});
|
|
8498
8887
|
|
|
@@ -8543,6 +8932,9 @@ ${usageFactMissing.toString()}
|
|
|
8543
8932
|
// **desc は無い種別がある(2026-08-21)。** el() は text をそのまま textContent へ
|
|
8544
8933
|
// 代入するので、無条件に出すと画面に文字列 undefined が出る
|
|
8545
8934
|
meta.desc ? el("p", { class: "ops-card-desc", text: meta.desc }) : null,
|
|
8935
|
+
// **鍵の効く範囲は、登録状況より先に言う。** 「登録済み」と読んだ後では
|
|
8936
|
+
// 「どのファイルの話か」を確かめ直すことになる(#229)
|
|
8937
|
+
scopeNote ? el("p", { class: "sec-scope", text: scopeNote }) : null,
|
|
8546
8938
|
state,
|
|
8547
8939
|
// **未登録のときだけ、先に試すべき手を出す。** 既に動いているカードに
|
|
8548
8940
|
// 「まずこれをやれ」と出すと、直す必要が無いものを触らせることになる。
|
|
@@ -8583,54 +8975,67 @@ ${usageFactMissing.toString()}
|
|
|
8583
8975
|
});
|
|
8584
8976
|
app.appendChild(wrap);
|
|
8585
8977
|
updateConnMapAsOf();
|
|
8586
|
-
|
|
8978
|
+
applyStateToConnMap();
|
|
8587
8979
|
}
|
|
8588
8980
|
|
|
8589
8981
|
/**
|
|
8590
|
-
*
|
|
8982
|
+
* 接続グラフを、正本からの導出で**毎回まるごと当て直す**(0.12.0・#231 / #235)。
|
|
8983
|
+
*
|
|
8984
|
+
* ## なぜ「片道の降格」をやめたのか
|
|
8985
|
+
*
|
|
8986
|
+
* 0.11.1 は「実績が無い会議・共有資料を cm-ok から降ろす(戻す道は無い)」形だった。
|
|
8987
|
+
* 取り込みの事実は**単調に増えるだけ**(一度取り込めたら取り消されない)なので、
|
|
8988
|
+
* あの2能力では片道で成立していた。
|
|
8591
8989
|
*
|
|
8592
|
-
*
|
|
8593
|
-
*
|
|
8594
|
-
*
|
|
8990
|
+
* **チャットと開発の「届くか」は双方向に変わる。** アクセスできない → App の設定を
|
|
8991
|
+
* 直す → 「接続を確認」を押すと ok に戻る。片道のまま一般化すると、**直したのに
|
|
8992
|
+
* リロードするまで未接続のまま**という**新しい嘘**を作る(前の嘘を消して別の嘘を
|
|
8993
|
+
* 置くことになる)。
|
|
8595
8994
|
*
|
|
8596
|
-
*
|
|
8597
|
-
* - ノードは 「cm-off」(灰・破線)にして、3行目を「未使用」にする。図の語彙は
|
|
8598
|
-
* renderConnectionMap の off と同じにする——同じ画面で図とタイルの語を割らない
|
|
8995
|
+
* ## だから冪等な再描画にする
|
|
8599
8996
|
*
|
|
8600
|
-
*
|
|
8997
|
+
* ライブ応答が届くたびに、6能力ぶんの導出結果(capDerivation)から
|
|
8998
|
+
* **ノードのクラス・3行目・線の表示を毎回計算して当てる**。降格も昇格もする。
|
|
8999
|
+
* 何度呼んでも同じ状態に落ち着く(前回の状態を読まない)。
|
|
8601
9000
|
*
|
|
8602
|
-
*
|
|
8603
|
-
*
|
|
8604
|
-
*
|
|
8605
|
-
*
|
|
9001
|
+
* 🔴 **線は消さない。表示を切り替えるだけ**(renderConnectionMap の項)。
|
|
9002
|
+
* 消してしまうと、昇格のときに曲線のジオメトリを実行時にもう一度計算することになり、
|
|
9003
|
+
* 同じ式がビルド時と実行時の2箇所に分かれる。
|
|
9004
|
+
*
|
|
9005
|
+
* 🔴 **cm-warn を隠さない。** 壊れているのは「繋がったうえで失敗している」状態で、
|
|
9006
|
+
* 経路自体は存在する。線を消すと**設定が無いのと区別が付かなくなり、失敗が図から
|
|
9007
|
+
* 消える**——「失敗が上位表示に畳み込まれない」(#231)の逆を、こちらの都合で作る。
|
|
9008
|
+
*
|
|
9009
|
+
* **導出が答えない能力(規則5・設定が on 以外)は触らない。** 焼いた図がそのまま
|
|
9010
|
+
* 残る=従来表示へのフォールバック。タイル側と同じ規律。
|
|
8606
9011
|
*/
|
|
8607
|
-
function
|
|
9012
|
+
function applyStateToConnMap() {
|
|
8608
9013
|
var wrap = app.querySelector(".conn-map");
|
|
8609
9014
|
if (!wrap) return;
|
|
8610
|
-
for (var cap in
|
|
8611
|
-
if (!Object.prototype.hasOwnProperty.call(
|
|
8612
|
-
|
|
9015
|
+
for (var cap in TILE_CAPS) {
|
|
9016
|
+
if (!Object.prototype.hasOwnProperty.call(TILE_CAPS, cap)) continue;
|
|
9017
|
+
// 設定そのものが OFF / 異常 / 未設置なら、その語(焼いた図)が正しい
|
|
9018
|
+
if (capConfigState(cap)) continue;
|
|
9019
|
+
var d = capDerivation(cap);
|
|
9020
|
+
if (!d) continue; // 規則5: 材料が無い → 焼いた図のまま
|
|
8613
9021
|
var node = wrap.querySelector('[data-target="ds-' + cap + '"]');
|
|
8614
|
-
if (!node
|
|
8615
|
-
node.setAttribute("class", "cm-node cm-
|
|
9022
|
+
if (!node) continue;
|
|
9023
|
+
node.setAttribute("class", "cm-node cm-" + d.state);
|
|
8616
9024
|
var sync = node.querySelector(".cm-sync");
|
|
8617
|
-
if (
|
|
8618
|
-
//
|
|
8619
|
-
//
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
sync.
|
|
8625
|
-
sync.setAttribute("y", "67");
|
|
8626
|
-
sync.setAttribute("text-anchor", "middle");
|
|
8627
|
-
node.appendChild(sync);
|
|
9025
|
+
if (sync) {
|
|
9026
|
+
// 🔴 **時刻の印は必ず外す。** 残すと renderConnMap の [data-ts] の整形が
|
|
9027
|
+
// あとから「最終同期 …」を書き戻し、導出した語が消える
|
|
9028
|
+
sync.removeAttribute("data-ts");
|
|
9029
|
+
// 接続OK のときだけ、ストア由来の取り込み時刻を添える(run の時刻ではない)。
|
|
9030
|
+
// 出せなければ語だけ——**繋がっていないものに同期時刻を並べない**(タイルと同じ規律)
|
|
9031
|
+
var at = d.state === "ok" ? dsFmtTime(ingestedAtOf(LIVE_STATE, TILE_CAPS[cap].id)) : "";
|
|
9032
|
+
sync.textContent = at ? "最終同期 " + at : d.text;
|
|
8628
9033
|
}
|
|
8629
|
-
// 時刻の整形(renderConnMap の [data-ts])が後から書き戻さないよう、印ごと外す
|
|
8630
|
-
sync.removeAttribute("data-ts");
|
|
8631
|
-
sync.textContent = "未使用";
|
|
8632
9034
|
var link = wrap.querySelector('.cm-link[data-cm-cap="' + cap + '"]');
|
|
8633
|
-
if (link
|
|
9035
|
+
if (link) {
|
|
9036
|
+
link.setAttribute("class", "cm-link cm-" + d.state
|
|
9037
|
+
+ (d.state === "ok" || d.state === "warn" ? "" : " is-hidden"));
|
|
9038
|
+
}
|
|
8634
9039
|
}
|
|
8635
9040
|
}
|
|
8636
9041
|
|
package/package.json
CHANGED