@takagaki/cortex-decisions-viewer 0.12.115 → 0.12.116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.js +82 -1
- package/dist/evidence-names.js +30 -1
- package/dist/render.js +160 -7
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.PROJECT_ICON_EXTS = exports.MIRROR_RELS = void 0;
|
|
39
|
+
exports.DECISION_FIRMNESS = exports.PROJECT_ICON_EXTS = exports.MIRROR_RELS = void 0;
|
|
40
40
|
exports.sanitizeBody = sanitizeBody;
|
|
41
41
|
exports.findProjectIcon = findProjectIcon;
|
|
42
42
|
exports.readFleetSources = readFleetSources;
|
|
@@ -55,6 +55,9 @@ exports.bodyFileSlug = bodyFileSlug;
|
|
|
55
55
|
exports.bodyStampId = bodyStampId;
|
|
56
56
|
exports.bodyStampComment = bodyStampComment;
|
|
57
57
|
exports.build = build;
|
|
58
|
+
exports.decisionFirmnessOf = decisionFirmnessOf;
|
|
59
|
+
exports.decisionTopicOf = decisionTopicOf;
|
|
60
|
+
exports.decidedByOf = decidedByOf;
|
|
58
61
|
exports.createRefResolver = createRefResolver;
|
|
59
62
|
const node_crypto_1 = require("node:crypto");
|
|
60
63
|
const node_fs_1 = require("node:fs");
|
|
@@ -1523,6 +1526,16 @@ async function buildEvidenceBook(repoRoot, targetUrls, externalSources = []) {
|
|
|
1523
1526
|
if (key && name && !book.names.has(key))
|
|
1524
1527
|
book.names.set(key, name);
|
|
1525
1528
|
};
|
|
1529
|
+
// 片付けで移した議事録のリンク(URL の鍵 → 「定例名 日付」・cortex-engine #968 R-6・A-R6-3)。表が無い・読めない案件は今どおり
|
|
1530
|
+
// (種類の既定の名前)。資料より先に載せる(同じ鍵に資料の題名が先に付かないように)
|
|
1531
|
+
try {
|
|
1532
|
+
const table = JSON.parse(await node_fs_1.promises.readFile(path.join(repoRoot, evidence_names_1.MOVED_MEETINGS_FILE), "utf8"));
|
|
1533
|
+
for (const [url, name] of (0, evidence_names_1.movedMeetingNamesOf)(table))
|
|
1534
|
+
putName(foldKeyOf(url), name);
|
|
1535
|
+
}
|
|
1536
|
+
catch {
|
|
1537
|
+
// 表が無い(片付けていない案件)・壊れている
|
|
1538
|
+
}
|
|
1526
1539
|
// 課題(キー → 「キー 題名」)・Backlog のドキュメントと Wiki(URL の鍵 → 題名)
|
|
1527
1540
|
const backlogDir = await findBacklogDir(repoRoot, "課題管理");
|
|
1528
1541
|
for (const sub of BACKLOG_SECTIONS) {
|
|
@@ -2024,6 +2037,17 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
2024
2037
|
addReferenceEdges(d.id, d.references);
|
|
2025
2038
|
addLinkEdges(d.id, d.bodyLinks);
|
|
2026
2039
|
}
|
|
2040
|
+
// 決めた人の線(決定 → 関係者・#1094)。relations には書かれないので `decided_by[].member` から引く。
|
|
2041
|
+
// 🔴 相手の関係者がグラフに無ければ引かない(宛先の無い点を作らない=classifyTarget を通さない)
|
|
2042
|
+
for (const d of decisions) {
|
|
2043
|
+
for (const who of d.decidedBy ?? []) {
|
|
2044
|
+
if (!("member" in who))
|
|
2045
|
+
continue;
|
|
2046
|
+
const node = nodes.get(who.member);
|
|
2047
|
+
if (node && node.group === "member")
|
|
2048
|
+
edges.push({ from: d.id, to: who.member, rel: "decided_by" });
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2027
2051
|
if (home) {
|
|
2028
2052
|
addRelationEdges(home.id, home.relations);
|
|
2029
2053
|
addSourceEdge(home.id, home.source);
|
|
@@ -2705,6 +2729,10 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch, ref
|
|
|
2705
2729
|
const references = referencesRaw.map((r) => refs.resolve(r));
|
|
2706
2730
|
const relations = toRelations(data.relations);
|
|
2707
2731
|
const followupHuman = (0, rederivation_1.humanFollowupOf)(data.followup);
|
|
2732
|
+
// 記憶 v2 の任意の欄(#1094)。**欄が無い・語彙外の記録ではキーごと載せない**(埋め込みの形を今と同じに保つ)
|
|
2733
|
+
const firmness = decisionFirmnessOf(data.firmness);
|
|
2734
|
+
const topic = decisionTopicOf(data.topic);
|
|
2735
|
+
const decidedBy = decidedByOf(data.decided_by);
|
|
2708
2736
|
// 本文先頭の H1 はタイトルと重複するため除去(詳細画面ではタイトルを別途表示する)
|
|
2709
2737
|
const bodyHtml = await renderRecordBody(content);
|
|
2710
2738
|
const filePath = repoRelDir ? `${repoRelDir}/${fileName}` : fileName;
|
|
@@ -2735,8 +2763,61 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch, ref
|
|
|
2735
2763
|
fmErrors: validateFm(data),
|
|
2736
2764
|
// 担当者の答え(#705)。**語彙外・形が違うものはキーごと載せない**
|
|
2737
2765
|
...(followupHuman ? { followupHuman: followupHuman } : {}),
|
|
2766
|
+
...(firmness ? { firmness } : {}),
|
|
2767
|
+
...(topic ? { topic } : {}),
|
|
2768
|
+
...(decidedBy ? { decidedBy } : {}),
|
|
2738
2769
|
};
|
|
2739
2770
|
}
|
|
2771
|
+
/**
|
|
2772
|
+
* 記憶 v2 の決定の新しい欄の読み手(#1094)。
|
|
2773
|
+
*
|
|
2774
|
+
* 🔴 **写し。正本は cortex-engine `scripts/gold/decision-v2-fields.mjs` の `decisionV2FieldsOf`**。
|
|
2775
|
+
* 同じバイト列の表 `test/fixtures/decision-v2-fields-contract.json` を両リポに置き、
|
|
2776
|
+
* `test/supersedes-view.test.mjs` の「[契約] …」が全ケースを流す。規則を変えるときは engine の正本・この写し・表を同じ日に直す
|
|
2777
|
+
*/
|
|
2778
|
+
/** 決定の固さの語彙(並びも正本と同じ) */
|
|
2779
|
+
exports.DECISION_FIRMNESS = ["確定", "仮", "検討中"];
|
|
2780
|
+
/** `decided_by` の要素が持ってよい鍵 */
|
|
2781
|
+
const DECIDED_BY_KEYS = ["member", "name", "url"];
|
|
2782
|
+
const MEMBER_ID_RE = /^member:\S/;
|
|
2783
|
+
const DECIDED_BY_URL_RE = /^https?:\/\/\S+$/;
|
|
2784
|
+
const isText = (v) => typeof v === "string" && v.trim() !== "";
|
|
2785
|
+
/** frontmatter の `firmness` → 語彙の 1 語。語彙外・文字列でないものは undefined(綴りはそのまま比べる) */
|
|
2786
|
+
function decisionFirmnessOf(value) {
|
|
2787
|
+
return exports.DECISION_FIRMNESS.includes(value) ? value : undefined;
|
|
2788
|
+
}
|
|
2789
|
+
/** frontmatter の `topic` → 論点の札の名前(前後の空白を落とす)。空・文字列でないものは undefined */
|
|
2790
|
+
function decisionTopicOf(value) {
|
|
2791
|
+
return isText(value) ? value.trim() : undefined;
|
|
2792
|
+
}
|
|
2793
|
+
/** `decided_by` の要素 1 つ。読めなければ null(文字列・知らない鍵・member と name/url の同居・member: で始まらない・空の name・http(s) でない url) */
|
|
2794
|
+
function decidedByEntryOf(entry) {
|
|
2795
|
+
if (entry == null || typeof entry !== "object" || Array.isArray(entry))
|
|
2796
|
+
return null;
|
|
2797
|
+
const o = entry;
|
|
2798
|
+
if (Object.keys(o).some((k) => !DECIDED_BY_KEYS.includes(k)))
|
|
2799
|
+
return null;
|
|
2800
|
+
const has = (k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
2801
|
+
if (has("member")) {
|
|
2802
|
+
if (has("name") || has("url"))
|
|
2803
|
+
return null;
|
|
2804
|
+
return typeof o.member === "string" && MEMBER_ID_RE.test(o.member) ? { member: o.member } : null;
|
|
2805
|
+
}
|
|
2806
|
+
if (!has("name") || !isText(o.name))
|
|
2807
|
+
return null;
|
|
2808
|
+
if (o.url == null)
|
|
2809
|
+
return { name: o.name };
|
|
2810
|
+
return typeof o.url === "string" && DECIDED_BY_URL_RE.test(o.url) ? { name: o.name, url: o.url } : null;
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* frontmatter の `decided_by` → 決めた人の並び。🔴 形の崩れた要素は**飛ばす**。
|
|
2814
|
+
* 欄がリストでなければ undefined(無いものとして扱い、旧い `deciders` を出す)。空のリストは []
|
|
2815
|
+
*/
|
|
2816
|
+
function decidedByOf(value) {
|
|
2817
|
+
if (!Array.isArray(value))
|
|
2818
|
+
return undefined;
|
|
2819
|
+
return value.map(decidedByEntryOf).filter((e) => e !== null);
|
|
2820
|
+
}
|
|
2740
2821
|
/**
|
|
2741
2822
|
* 解決した参照 → 書かれていた値(#968 tools 1)。つながり画面は点の鍵をこの値から決める
|
|
2742
2823
|
* (`site-data` に載せない——機械の語を画面の材料に増やさない)
|
package/dist/evidence-names.js
CHANGED
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* 対応表で URL に引けるものは同じ鍵に寄せる——今の ID と新しい URL の根拠が混ざっても 1 つの点になる。
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.emptyEvidenceBook = void 0;
|
|
16
|
+
exports.MOVED_MEETINGS_FILE = exports.emptyEvidenceBook = void 0;
|
|
17
17
|
exports.minuteFoldKey = minuteFoldKey;
|
|
18
18
|
exports.minuteLabel = minuteLabel;
|
|
19
|
+
exports.movedMeetingNamesOf = movedMeetingNamesOf;
|
|
19
20
|
exports.minuteRefOfPath = minuteRefOfPath;
|
|
20
21
|
exports.slackChannelOfLabel = slackChannelOfLabel;
|
|
21
22
|
exports.backlogTailOf = backlogTailOf;
|
|
@@ -55,6 +56,34 @@ function minuteLabel(id) {
|
|
|
55
56
|
const d = m[2];
|
|
56
57
|
return `${lastSegment(m[1])} ${d.slice(0, 4)}-${d.slice(4, 6)}-${d.slice(6, 8)}`;
|
|
57
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* 片付けで移した会議の表(engine が目次と同じコミットで置く・cortex-engine #968 R-6・A-R6-3)。
|
|
61
|
+
* 写しを片付けた案件だけが持つ。無ければ今どおり(移した議事録のリンクは種類の既定の名前で出る)
|
|
62
|
+
*/
|
|
63
|
+
exports.MOVED_MEETINGS_FILE = ".cortex/mirror-moved-meetings.json";
|
|
64
|
+
/**
|
|
65
|
+
* 移した会議の表 → `[移した URL, 会議の名前(「定例名 日付」)]` の並び。形が違う表・行は読まない(名前を当て推量しない)。
|
|
66
|
+
* 名前は `minuteLabel`(`minute:` の根拠と同じ 1 本)で作る——同じ会議が `minute:` で書かれても URL で書かれても同じ名前になる
|
|
67
|
+
*/
|
|
68
|
+
function movedMeetingNamesOf(table) {
|
|
69
|
+
if (!table || typeof table !== "object")
|
|
70
|
+
return [];
|
|
71
|
+
const t = table;
|
|
72
|
+
if (t.version !== 1 || !Array.isArray(t.meetings))
|
|
73
|
+
return [];
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const row of t.meetings) {
|
|
76
|
+
if (!row || typeof row !== "object")
|
|
77
|
+
continue;
|
|
78
|
+
const { url, ref } = row;
|
|
79
|
+
if (typeof url !== "string" || !/^https:\/\/\S+$/.test(url) || typeof ref !== "string")
|
|
80
|
+
continue;
|
|
81
|
+
const name = minuteLabel(ref);
|
|
82
|
+
if (name)
|
|
83
|
+
out.push([url, name]);
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
58
87
|
/**
|
|
59
88
|
* 議事録の写しのパス(`会議/{…}/{定例名}/{YYYYMMDD}/{…}minutes{…}.md`)→ `minute:{定例名}:{YYYYMMDD}`。
|
|
60
89
|
* `buildTargetUrlMap` の `minute:` の組み方と同じ規約(定例名は日付の 1 つ上の段)
|
package/dist/render.js
CHANGED
|
@@ -5314,6 +5314,15 @@ a.pager-btn:hover { text-decoration: none; }
|
|
|
5314
5314
|
.followup-section { margin: 14px 0 4px; }
|
|
5315
5315
|
/* 置き換え(#699 F3)。印は「その後」の置き換え済みと同じ色(同じ意味なので) */
|
|
5316
5316
|
.badge.retired { background: #f5f5f4; color: #57534e; border: 1px solid #d6d3d1; }
|
|
5317
|
+
/* 論点・固さ・「今」(#1094)。欄の無い記録には付かない */
|
|
5318
|
+
.badge.now { background: #1d4ed8; color: #fff; }
|
|
5319
|
+
.card.is-now { border-left: 3px solid #1d4ed8; }
|
|
5320
|
+
.card.is-faded { opacity: .55; }
|
|
5321
|
+
.badge.topic { background: #f5f3ff; color: #5b21b6; border: 1px solid #ddd6fe; font-weight: 500; }
|
|
5322
|
+
.badge.firmness-fixed { background: #ecfdf5; color: #047857; border: 1px solid #a7f3d0; }
|
|
5323
|
+
.badge.firmness-tentative { background: #fffbeb; color: #92400e; border: 1px solid #fde68a; }
|
|
5324
|
+
.badge.firmness-open { background: #f8fafc; color: #475569; border: 1px dashed #cbd5e1; }
|
|
5325
|
+
.decision-topic { margin-bottom: 4px; }
|
|
5317
5326
|
.supersede-section { margin: 14px 0 4px; }
|
|
5318
5327
|
.supersede-list { list-style: none; margin: 0 0 8px; padding: 0; }
|
|
5319
5328
|
.supersede-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; font-size: 13.5px; line-height: 1.7; padding: 2px 0; }
|
|
@@ -7360,6 +7369,110 @@ ${calendarAddUrl.toString()}
|
|
|
7360
7369
|
return box;
|
|
7361
7370
|
}
|
|
7362
7371
|
|
|
7372
|
+
/* -------------------------------------------------------------------------
|
|
7373
|
+
* 論点・固さ・決めた人(記憶 v2・cortex-engine #1094)
|
|
7374
|
+
* -------------------------------------------------------------------------
|
|
7375
|
+
* 欄(topic / firmness / decidedBy)は**ビルド側で語彙・形を確かめてある**。欄の無い記録では
|
|
7376
|
+
* キーごと無いので、画面は有無だけを見る。🔴 **欄の無い記録だけの案件では要素を 1 つも作らない**
|
|
7377
|
+
* (絞り込み・「今」・固さの札・決めた人の節が出ない=今と同じ DOM)。
|
|
7378
|
+
* 「置き換えられた」の判定は isRetired(ビルドが supersedes.ts で組んだ答え)をそのまま使う。
|
|
7379
|
+
*/
|
|
7380
|
+
function hasTopic(rec) {
|
|
7381
|
+
return !!(rec && typeof rec.topic === "string" && rec.topic);
|
|
7382
|
+
}
|
|
7383
|
+
/** 論点の札の一覧(札を持つ決定が無ければ空) */
|
|
7384
|
+
function topicOptions(items) {
|
|
7385
|
+
var seen = {};
|
|
7386
|
+
var out = [];
|
|
7387
|
+
(items || []).forEach(function (d) {
|
|
7388
|
+
if (!hasTopic(d) || Object.prototype.hasOwnProperty.call(seen, d.topic)) return;
|
|
7389
|
+
seen[d.topic] = true;
|
|
7390
|
+
out.push(d.topic);
|
|
7391
|
+
});
|
|
7392
|
+
return out.sort(function (a, b) { return a.localeCompare(b, "ja"); });
|
|
7393
|
+
}
|
|
7394
|
+
/** 新しい順(決定日の降順 → ID の降順)。日付の無いものは最後 */
|
|
7395
|
+
function newestDecisionFirst(a, b) {
|
|
7396
|
+
if (a.date !== b.date) {
|
|
7397
|
+
if (!a.date) return 1;
|
|
7398
|
+
if (!b.date) return -1;
|
|
7399
|
+
return a.date < b.date ? 1 : -1;
|
|
7400
|
+
}
|
|
7401
|
+
return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
|
|
7402
|
+
}
|
|
7403
|
+
/**
|
|
7404
|
+
* 札ごとの「今」の決定(一番新しく、置き換えられていない 1 件)の ID の集合。
|
|
7405
|
+
* 削除済みは数えない(一覧と同じ材料)
|
|
7406
|
+
*/
|
|
7407
|
+
function nowDecisionIds() {
|
|
7408
|
+
var best = {};
|
|
7409
|
+
visibleRecords(SITE.decisions).forEach(function (d) {
|
|
7410
|
+
if (!hasTopic(d) || isRetired(d)) return;
|
|
7411
|
+
var cur = best[d.topic];
|
|
7412
|
+
if (!cur || newestDecisionFirst(d, cur) < 0) best[d.topic] = d;
|
|
7413
|
+
});
|
|
7414
|
+
var ids = {};
|
|
7415
|
+
Object.keys(best).forEach(function (t) { ids[best[t].id] = true; });
|
|
7416
|
+
return ids;
|
|
7417
|
+
}
|
|
7418
|
+
function isNowDecision(rec) {
|
|
7419
|
+
return !!(rec && rec._kind === "decision" && hasTopic(rec) && nowDecisionIds()[rec.id]);
|
|
7420
|
+
}
|
|
7421
|
+
function nowBadge(rec) {
|
|
7422
|
+
return isNowDecision(rec) ? el("span", { class: "badge now", title: "この論点で今有効な決定", text: "今" }) : null;
|
|
7423
|
+
}
|
|
7424
|
+
/** 論点の札(一覧の行)。札が無ければ null */
|
|
7425
|
+
function topicBadge(rec) {
|
|
7426
|
+
return hasTopic(rec) ? el("span", { class: "badge topic", title: "論点", text: rec.topic }) : null;
|
|
7427
|
+
}
|
|
7428
|
+
var FIRMNESS_CLASS = { "確定": "fixed", "仮": "tentative", "検討中": "open" };
|
|
7429
|
+
/** 固さの小札。語彙外は出さない(ビルド側でも捨てている・二重の網) */
|
|
7430
|
+
function firmnessBadge(rec) {
|
|
7431
|
+
var f = rec && rec.firmness;
|
|
7432
|
+
if (typeof f !== "string" || !Object.prototype.hasOwnProperty.call(FIRMNESS_CLASS, f)) return null;
|
|
7433
|
+
return el("span", { class: "badge firmness firmness-" + FIRMNESS_CLASS[f], title: "固さ", text: f });
|
|
7434
|
+
}
|
|
7435
|
+
/** 置き換えられた決定を薄く出すか(論点の札を持つ決定だけ。欄の無い記録の見た目は変えない) */
|
|
7436
|
+
function fadedClass(rec) {
|
|
7437
|
+
return hasTopic(rec) && isRetired(rec) ? " is-faded" : "";
|
|
7438
|
+
}
|
|
7439
|
+
/** 決めた人 1 人の札。関係者が名簿にあればそのページへ、{name, url} は url があれば元の URL へ */
|
|
7440
|
+
function decidedByChip(who) {
|
|
7441
|
+
if (!who || typeof who !== "object") return null;
|
|
7442
|
+
if (typeof who.member === "string") {
|
|
7443
|
+
var rec = byId[who.member];
|
|
7444
|
+
var fallback = who.member.slice("member:".length);
|
|
7445
|
+
if (rec && rec._kind === "generic") {
|
|
7446
|
+
var a = el("a", { class: "chip decided-by-member", href: "?id=" + encodeURIComponent(who.member), text: humanTitle(rec.title) || fallback });
|
|
7447
|
+
a.addEventListener("click", function (ev) {
|
|
7448
|
+
if (ev.defaultPrevented || ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
|
|
7449
|
+
ev.preventDefault();
|
|
7450
|
+
go(who.member);
|
|
7451
|
+
});
|
|
7452
|
+
return a;
|
|
7453
|
+
}
|
|
7454
|
+
return el("span", { class: "chip", text: fallback });
|
|
7455
|
+
}
|
|
7456
|
+
if (typeof who.name !== "string" || !who.name) return null;
|
|
7457
|
+
var raw = typeof who.url === "string" ? who.url.toLowerCase() : "";
|
|
7458
|
+
var url = raw.indexOf("http://") === 0 || raw.indexOf("https://") === 0 ? who.url : "";
|
|
7459
|
+
if (url) return el("a", { class: "chip decided-by-link", href: url, target: "_blank", rel: "noopener", text: who.name });
|
|
7460
|
+
return el("span", { class: "chip", text: who.name });
|
|
7461
|
+
}
|
|
7462
|
+
/** 検索に使う決めた人の表記(decidedBy があればそちら、無ければ deciders) */
|
|
7463
|
+
function deciderWords(d) {
|
|
7464
|
+
if (Array.isArray(d.decidedBy) && d.decidedBy.length) {
|
|
7465
|
+
return d.decidedBy.map(function (w) {
|
|
7466
|
+
if (w && typeof w.member === "string") {
|
|
7467
|
+
var r = byId[w.member];
|
|
7468
|
+
return r && r.title ? r.title : w.member.slice("member:".length);
|
|
7469
|
+
}
|
|
7470
|
+
return w && typeof w.name === "string" ? w.name : "";
|
|
7471
|
+
}).join(" ");
|
|
7472
|
+
}
|
|
7473
|
+
return (d.deciders || []).join(" ");
|
|
7474
|
+
}
|
|
7475
|
+
|
|
7363
7476
|
// 未確認が1件も無い一覧では絞り込みを出さない
|
|
7364
7477
|
function hasUnverified(items) {
|
|
7365
7478
|
return (items || []).some(function (r) { return machineOwned(confirmPathOf(r), r.rstatus); });
|
|
@@ -8680,7 +8793,7 @@ ${calendarAddUrl.toString()}
|
|
|
8680
8793
|
}
|
|
8681
8794
|
}
|
|
8682
8795
|
|
|
8683
|
-
var listState = { tab: DEFAULT_TAB, q: "", category: "", month: "", status: "", page: 1 };
|
|
8796
|
+
var listState = { tab: DEFAULT_TAB, q: "", category: "", month: "", status: "", topic: "", page: 1 };
|
|
8684
8797
|
|
|
8685
8798
|
// 一覧のページネーション(描画コスト対策。1ページあたりの件数)
|
|
8686
8799
|
var PAGE_SIZE = 30;
|
|
@@ -19304,6 +19417,13 @@ ${calendarAddUrl.toString()}
|
|
|
19304
19417
|
if (statusOptsD.length) {
|
|
19305
19418
|
filters.appendChild(buildSelect("すべての状態", statusOptsD, listState.status, function (v) { listState.status = v; listState.page = 1; drawCards(); }, statusLabel));
|
|
19306
19419
|
}
|
|
19420
|
+
// 論点の札(#1094)。札を持つ決定が 1 件も無い案件では出さない
|
|
19421
|
+
var topicOpts = topicOptions(visibleRecords(SITE.decisions));
|
|
19422
|
+
if (topicOpts.length) {
|
|
19423
|
+
var topicSel = buildSelect("すべての論点", topicOpts, listState.topic, function (v) { listState.topic = v; listState.page = 1; drawCards(); });
|
|
19424
|
+
topicSel.classList.add("topic-filter");
|
|
19425
|
+
filters.appendChild(topicSel);
|
|
19426
|
+
}
|
|
19307
19427
|
app.appendChild(filters);
|
|
19308
19428
|
|
|
19309
19429
|
var addBarD = aiAddBar("Decisions");
|
|
@@ -19335,16 +19455,25 @@ ${calendarAddUrl.toString()}
|
|
|
19335
19455
|
var q = listState.q.trim().toLowerCase();
|
|
19336
19456
|
// 削除済みは絞り込みの手前で落とす(分母も一緒に減るので「12/13件」が残らない)
|
|
19337
19457
|
var all = visibleRecords(SITE.decisions);
|
|
19338
|
-
|
|
19458
|
+
var out = all.filter(function (d) {
|
|
19339
19459
|
if (listState.month && d.date.slice(0, 7) !== listState.month) return false;
|
|
19340
19460
|
if (listState.category && d.category !== listState.category) return false;
|
|
19341
19461
|
if (!matchesStatusFilter(d, !!SITE.decisionFollowup)) return false;
|
|
19462
|
+
if (!matchesTopicFilter(d)) return false;
|
|
19342
19463
|
if (q) {
|
|
19343
|
-
var hay = (d.title + " " + d.summary + " " + d.searchText + " " + d
|
|
19464
|
+
var hay = (d.title + " " + d.summary + " " + d.searchText + " " + deciderWords(d)).toLowerCase();
|
|
19344
19465
|
if (hay.indexOf(q) === -1) return false;
|
|
19345
19466
|
}
|
|
19346
19467
|
return true;
|
|
19347
19468
|
});
|
|
19469
|
+
// 同じ論点の中は新しい順(決定日の降順 → ID の降順)
|
|
19470
|
+
if (listState.topic) out.sort(newestDecisionFirst);
|
|
19471
|
+
return out;
|
|
19472
|
+
}
|
|
19473
|
+
/** 論点の絞り込み(#1094)。選んでいなければ全件、選んだら同じ札の決定だけ */
|
|
19474
|
+
function matchesTopicFilter(d) {
|
|
19475
|
+
if (!listState.topic) return true;
|
|
19476
|
+
return hasTopic(d) && d.topic === listState.topic;
|
|
19348
19477
|
}
|
|
19349
19478
|
|
|
19350
19479
|
function drawCards() {
|
|
@@ -19367,6 +19496,7 @@ ${calendarAddUrl.toString()}
|
|
|
19367
19496
|
|
|
19368
19497
|
pg.slice.forEach(function (d) {
|
|
19369
19498
|
var meta = el("div", { class: "card-meta" }, [
|
|
19499
|
+
nowBadge(d),
|
|
19370
19500
|
draftBadge(d),
|
|
19371
19501
|
proposalBadge(d),
|
|
19372
19502
|
followupBadge(d),
|
|
@@ -19374,13 +19504,15 @@ ${calendarAddUrl.toString()}
|
|
|
19374
19504
|
latestBadge(d),
|
|
19375
19505
|
el("span", { class: "mono", text: d.date || "----" }),
|
|
19376
19506
|
catBadge(d.category),
|
|
19507
|
+
firmnessBadge(d),
|
|
19508
|
+
topicBadge(d),
|
|
19377
19509
|
]);
|
|
19378
19510
|
var eb = fmErrorBadge(d);
|
|
19379
19511
|
if (eb) meta.appendChild(eb);
|
|
19380
19512
|
var traceD = el("a", { class: "mc-trace", href: "?tab=graph", text: "🕸 つながり" });
|
|
19381
19513
|
traceD.addEventListener("click", function (ev) { ev.preventDefault(); ev.stopPropagation(); openExplorerFor(d); });
|
|
19382
19514
|
meta.appendChild(traceD);
|
|
19383
|
-
var card = el("div", { class: "card", title: d.id, "data-record-id": d.id }, [
|
|
19515
|
+
var card = el("div", { class: "card" + fadedClass(d) + (isNowDecision(d) ? " is-now" : ""), title: d.id, "data-record-id": d.id }, [
|
|
19384
19516
|
meta,
|
|
19385
19517
|
// 消したレコードは一覧でも取り消し線(次のビルドまで一覧には残る)
|
|
19386
19518
|
cardTitleLink(d, d.deleted ? " is-deleted" : ""),
|
|
@@ -20404,6 +20536,9 @@ ${calendarAddUrl.toString()}
|
|
|
20404
20536
|
// 話題の線(決定・ルール・関係者 → 用語・engine #551 の about)。名前の一致で機械が付けた手がかりなので、
|
|
20405
20537
|
// 根拠の線と同じく細く薄い「ゆるいつながり」として描き、凡例は日本語の 1 行(栓 about_relations の on の条件)
|
|
20406
20538
|
about: { color: "#ddd6fe", label: "話題", kind: "topic" },
|
|
20539
|
+
// 決めた人の線(決定 → 関係者・#1094)。relations ではなく decided_by から引く。
|
|
20540
|
+
// 🔴 凡例には**この線がグラフにあるときだけ**出す(欄の無い案件の凡例を変えない)
|
|
20541
|
+
decided_by: { color: "#f472b6", label: "決めた人", kind: "gold", onlyWhenPresent: true },
|
|
20407
20542
|
};
|
|
20408
20543
|
var WEAK_RELS = {};
|
|
20409
20544
|
var REL_COLOR = {};
|
|
@@ -20471,7 +20606,10 @@ ${calendarAddUrl.toString()}
|
|
|
20471
20606
|
// 線(関係)の色凡例。グラフ内の有無に関わらず全種別を、Goldどうし/根拠の2群で表示する
|
|
20472
20607
|
[["gold", "線(Goldどうし):"], ["evidence", "線(根拠):"], ["topic", "線(話題):"]].forEach(function (pair) {
|
|
20473
20608
|
var kind = pair[0], heading = pair[1];
|
|
20474
|
-
var keys = Object.keys(REL_META).filter(function (r) {
|
|
20609
|
+
var keys = Object.keys(REL_META).filter(function (r) {
|
|
20610
|
+
if (REL_META[r].kind !== kind) return false;
|
|
20611
|
+
return !REL_META[r].onlyWhenPresent || graph.edges.some(function (e) { return e.rel === r; });
|
|
20612
|
+
});
|
|
20475
20613
|
if (!keys.length) return;
|
|
20476
20614
|
legend.appendChild(el("span", { class: "sep", text: heading }));
|
|
20477
20615
|
keys.forEach(function (r) {
|
|
@@ -21019,6 +21157,7 @@ ${calendarAddUrl.toString()}
|
|
|
21019
21157
|
relates_to: "rgba(148,163,184,0.6)", supersedes: "rgba(248,113,113,0.9)",
|
|
21020
21158
|
reference: "rgba(180,196,230,0.32)", source: "rgba(180,196,230,0.32)", link: "rgba(180,196,230,0.22)",
|
|
21021
21159
|
about: "rgba(196,181,253,0.22)",
|
|
21160
|
+
decided_by: "rgba(244,114,182,0.75)",
|
|
21022
21161
|
};
|
|
21023
21162
|
// 画面に入っている範囲(世界座標)。両端が同じ側の外にある線は描かない
|
|
21024
21163
|
var em = 4 / view.scale; // 線の太さの分の余白(画面で 4px)
|
|
@@ -21270,12 +21409,14 @@ ${calendarAddUrl.toString()}
|
|
|
21270
21409
|
app.appendChild(head);
|
|
21271
21410
|
|
|
21272
21411
|
var meta = el("div", { class: "detail-meta" }, [
|
|
21412
|
+
nowBadge(d),
|
|
21273
21413
|
draftBadge(d),
|
|
21274
21414
|
proposalBadge(d),
|
|
21275
21415
|
retiredBadge(d),
|
|
21276
21416
|
latestBadge(d),
|
|
21277
21417
|
el("span", { class: "mono", text: d.date || "----" }),
|
|
21278
21418
|
catBadge(d.category),
|
|
21419
|
+
firmnessBadge(d),
|
|
21279
21420
|
]);
|
|
21280
21421
|
meta.title = d.id;
|
|
21281
21422
|
meta.setAttribute("data-record-id", d.id);
|
|
@@ -21295,7 +21436,18 @@ ${calendarAddUrl.toString()}
|
|
|
21295
21436
|
app.appendChild(el("div", { class: "summary-box", text: d.summary }));
|
|
21296
21437
|
}
|
|
21297
21438
|
|
|
21298
|
-
|
|
21439
|
+
// 論点(#1094)。札の無い決定では何も作らない
|
|
21440
|
+
if (hasTopic(d)) {
|
|
21441
|
+
app.appendChild(el("div", { class: "section-label", text: "論点" }));
|
|
21442
|
+
app.appendChild(el("div", { class: "decision-topic" }, [el("span", { class: "chip", text: d.topic })]));
|
|
21443
|
+
}
|
|
21444
|
+
|
|
21445
|
+
// 決めた人(#1094)。decided_by に読めた人がいればそちらを出し、いなければ(欄が無い・空・全部崩れ)今どおり deciders
|
|
21446
|
+
var whoChips = Array.isArray(d.decidedBy) ? d.decidedBy.map(decidedByChip).filter(Boolean) : [];
|
|
21447
|
+
if (whoChips.length) {
|
|
21448
|
+
app.appendChild(el("div", { class: "section-label", text: "決めた人" }));
|
|
21449
|
+
app.appendChild(el("div", { class: "deciders decided-by" }, whoChips));
|
|
21450
|
+
} else if (d.deciders && d.deciders.length) {
|
|
21299
21451
|
app.appendChild(el("div", { class: "section-label", text: "決定者" }));
|
|
21300
21452
|
app.appendChild(el("div", { class: "deciders" }, d.deciders.map(function (p) {
|
|
21301
21453
|
return el("span", { class: "chip", text: p });
|
|
@@ -21344,6 +21496,7 @@ ${calendarAddUrl.toString()}
|
|
|
21344
21496
|
based_on: "根拠", derived_from: "由来", relates_to: "関連",
|
|
21345
21497
|
supersedes: "置換", superseded_by: "置換先", reference: "参照", source: "出典", link: "リンク",
|
|
21346
21498
|
about: "話題",
|
|
21499
|
+
decided_by: "決めた人",
|
|
21347
21500
|
};
|
|
21348
21501
|
var explorerState = { folder: null, path: [], q: "", sx: 0, autoRight: false };
|
|
21349
21502
|
// 索引もグラフ本体と同じ材料(削除済みを除いたもの)から作る。別々に間引くと、
|
|
@@ -21356,7 +21509,7 @@ ${calendarAddUrl.toString()}
|
|
|
21356
21509
|
(adjDir[e.from] = adjDir[e.from] || []).push({ id: e.to, rel: e.rel, dir: "out" });
|
|
21357
21510
|
(adjDir[e.to] = adjDir[e.to] || []).push({ id: e.from, rel: e.rel, dir: "in" });
|
|
21358
21511
|
});
|
|
21359
|
-
var REL_ORDER = ["supersedes", "based_on", "derived_from", "relates_to", "source", "reference", "link", "about"];
|
|
21512
|
+
var REL_ORDER = ["supersedes", "based_on", "derived_from", "relates_to", "decided_by", "source", "reference", "link", "about"];
|
|
21360
21513
|
|
|
21361
21514
|
function recordText(rec) {
|
|
21362
21515
|
if (!rec) return "";
|
package/package.json
CHANGED