@takagaki/cortex-decisions-viewer 0.12.83 → 0.12.85
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/README.md +1 -1
- package/dist/build.js +12 -1
- package/dist/cli.js +1 -1
- package/dist/engagement.js +2 -0
- package/dist/home-project.js +121 -0
- package/dist/rederivation.js +36 -1
- package/dist/render.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ cortex-decisions-viewer build [dir] [options]
|
|
|
40
40
|
| --- | --- | --- |
|
|
41
41
|
| `[dir]` | `Decisions` | Decisionsディレクトリ(リポジトリルートからの相対 or 絶対) |
|
|
42
42
|
| `-o, --out <dir>` | `site` | 出力ディレクトリ |
|
|
43
|
-
| `-t, --title <title>` | `Decisions` |
|
|
43
|
+
| `-t, --title <title>` | `Decisions` | サイトタイトル(`Cortex/Home.md` の frontmatter `project` が読めればそちらを優先。これは読めないときの控え) |
|
|
44
44
|
| `--repo-url <url>` | (git remoteから解決) | GitHubリポジトリURL。CIで git remote が引けない場合に明示指定 |
|
|
45
45
|
| `--branch <branch>` | `main`(環境変数 `AWS_BRANCH` があればそれ) | 編集リンク・参照リンクが指すブランチ |
|
|
46
46
|
| `--exclude <files...>` | (なし) | 一覧から除外する追加ファイル名。既定で `README.md` / `template.md` は除外 |
|
package/dist/build.js
CHANGED
|
@@ -96,6 +96,7 @@ function takeProposals(filePath) {
|
|
|
96
96
|
return hit;
|
|
97
97
|
}
|
|
98
98
|
const external_id_1 = require("./external-id");
|
|
99
|
+
const home_project_1 = require("./home-project");
|
|
99
100
|
const render_1 = require("./render");
|
|
100
101
|
/**
|
|
101
102
|
* この画面を作ったパッケージの版。**package.json を唯一の出どころにする。**
|
|
@@ -141,6 +142,10 @@ const SANITIZE_OPTIONS = {
|
|
|
141
142
|
// タスクリスト `- [ ]` / `- [x]` のチェック状態を残すのに要る。
|
|
142
143
|
// 落とすと**装飾ではなく情報(未完了/完了の別)が消える**
|
|
143
144
|
"input",
|
|
145
|
+
// 折りたたみ(#702)。engine が関係者の記録の末尾に「この人が関わった決定」を
|
|
146
|
+
// details で畳んで書く。落とすと一覧が常時展開のベタテキストになる。
|
|
147
|
+
// 属性は 1 つも許可しない(open も通さない=常に閉じた状態で出る)
|
|
148
|
+
"details", "summary",
|
|
144
149
|
],
|
|
145
150
|
allowedAttributes: {
|
|
146
151
|
a: ["href", "target", "rel"],
|
|
@@ -213,6 +218,8 @@ async function renderGroundingSection(markdown) {
|
|
|
213
218
|
*/
|
|
214
219
|
function toSearchText(md) {
|
|
215
220
|
return md
|
|
221
|
+
.replace(/<!--[\s\S]*?-->/g, " ") // HTML コメント(engine の機械ブロックの印など・画面にも出ない)
|
|
222
|
+
.replace(/<\/?(details|summary)>/g, " ") // 折りたたみのタグ(#702)。文字は検索に要らない
|
|
216
223
|
.replace(/!\[[^\]]*\]\([^)]*\)/g, " ") // 画像
|
|
217
224
|
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1") // リンクはラベルだけ残す
|
|
218
225
|
.replace(/[#>*_~`|]+/g, " ") // markdown記号・表の罫線
|
|
@@ -2075,12 +2082,15 @@ async function build(opts) {
|
|
|
2075
2082
|
let home = null;
|
|
2076
2083
|
let homeGrounding;
|
|
2077
2084
|
let homeUpdatedAt;
|
|
2085
|
+
// Home.md の本文(ヘッダーの案件名を frontmatter の `project` から決めるため。無ければ null)
|
|
2086
|
+
let homeRaw = null;
|
|
2078
2087
|
const sections = [];
|
|
2079
2088
|
// グラフに渡す区画(**並べ替える前**の配列を持つ。上の sections とレコードの実体は同じ)
|
|
2080
2089
|
const graphSections = [];
|
|
2081
2090
|
if (knowledgeRoot) {
|
|
2082
2091
|
try {
|
|
2083
2092
|
const raw = await node_fs_1.promises.readFile(path.join(knowledgeRoot, "Home.md"), "utf8");
|
|
2093
|
+
homeRaw = raw;
|
|
2084
2094
|
home = await parseGeneric(raw, "Home.md", toRepoRelative(knowledgeRoot), repo.baseUrl, repo.branch);
|
|
2085
2095
|
// 自己記述の節を取り出し、**本文からは抜く**(最上部に出すので、残すと2度出る)。
|
|
2086
2096
|
// 旧い使い方の説明もここで落ちる(説明はビューアが持つようになった)
|
|
@@ -2188,7 +2198,8 @@ async function build(opts) {
|
|
|
2188
2198
|
// 案件アイコン(#348)。Cortex構成でなければ undefined(旧レイアウトには置き場が無い)
|
|
2189
2199
|
const projectIcon = await findProjectIcon(knowledgeRoot);
|
|
2190
2200
|
const site = {
|
|
2191
|
-
|
|
2201
|
+
// 🔴 **案件名の正本は Home.md の `project`**(engine #699 F10)。`--title` は読めないときの控え
|
|
2202
|
+
title: (0, home_project_1.resolveSiteTitle)(homeRaw, opts.title),
|
|
2192
2203
|
generatedAt: opts.generatedAt,
|
|
2193
2204
|
// **package.json から読む。** 手で書くと、上げ忘れて嘘の版が出る
|
|
2194
2205
|
viewerVersion: PKG_VERSION,
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ program
|
|
|
12
12
|
.description("Decisionsディレクトリを静的サイトにビルドする")
|
|
13
13
|
.argument("[dir]", "Decisionsディレクトリ(未指定なら Cortex/Decisions/records → Cortex/Decisions → Decisions の順で自動検出)")
|
|
14
14
|
.option("-o, --out <dir>", "出力ディレクトリ", "site")
|
|
15
|
-
.option("-t, --title <title>", "
|
|
15
|
+
.option("-t, --title <title>", "サイトタイトル(Cortex/Home.md の project が読めればそちらを優先。これは読めないときの控え)", "Decisions")
|
|
16
16
|
.option("--repo-url <url>", "GitHubリポジトリURL(未指定なら git remote から解決)")
|
|
17
17
|
.option("--branch <branch>", "編集リンクが指すブランチ(既定: main / 環境変数 AWS_BRANCH)")
|
|
18
18
|
.option("--exclude <files...>", "一覧から除外する追加ファイル名(既定で README.md / template.md は除外)", [])
|
package/dist/engagement.js
CHANGED
|
@@ -78,6 +78,8 @@ exports.WEIGHTS = Object.freeze({
|
|
|
78
78
|
"record.confirmed": 2.0,
|
|
79
79
|
"record.edited": 2.0,
|
|
80
80
|
"record.deleted": 0,
|
|
81
|
+
// 本文を取り込んだ(engine #695)。人の行為ではないので 0(話した人は `utterance.spoken` で数える)
|
|
82
|
+
"body.captured": 0,
|
|
81
83
|
});
|
|
82
84
|
const DAY_MS = 86400000;
|
|
83
85
|
const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `Cortex/Home.md` の frontmatter `project`(案件の表示名)を読む(engine #699 F10)。
|
|
4
|
+
*
|
|
5
|
+
* ## なぜビューアが自分で読むのか
|
|
6
|
+
*
|
|
7
|
+
* 案件の表示名の**正本は Home.md の `project`**(`title` は頁の名前 "Home" で、表示名ではない)。
|
|
8
|
+
* これまでヘッダーの案件名は `--title`(engine の viewer-config.json の title、旧 Amplify は
|
|
9
|
+
* `viewerTitle ?? key`)の写しだけを見ていたので、Home.md で改名しても写しを直すまで
|
|
10
|
+
* ヘッダーが古い名前のままだった。build の時点で Home を読んでいるので、ここで正本を直接見る。
|
|
11
|
+
* 読めないとき(Home が無い・`project` が空・`{{プロジェクト名}}` のまま等)は `--title` に倒す。
|
|
12
|
+
*
|
|
13
|
+
* ## 読み方は infra の正本の写し
|
|
14
|
+
*
|
|
15
|
+
* 正本は `infra/scripts/lib/home-md.mjs` の `parseHomeProjectName`(名簿・Apps Script と同じ規則)。
|
|
16
|
+
* ビューアは npm で配る別パッケージなので import できず、写しを持つ。
|
|
17
|
+
* **規則を変えるときは両側を同じ便で直すこと。** 一致は `viewer/test/home-grounding.test.mjs` の
|
|
18
|
+
* parity の節が、infra の題材(`infra/test/fixtures/home-md-cases.mjs`・通る題材と弾く題材の両方)で検査する。
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.parseHomeProjectName = parseHomeProjectName;
|
|
22
|
+
exports.resolveSiteTitle = resolveSiteTitle;
|
|
23
|
+
/** 区切り行か(`---`。行末の空白は許す) */
|
|
24
|
+
const isDelimiter = (line) => /^---[ \t]*$/.test(line);
|
|
25
|
+
/** `project:` の右側を値にする(正本の `parseScalar` と同じ規則) */
|
|
26
|
+
function parseScalar(rest) {
|
|
27
|
+
const raw = rest.trim();
|
|
28
|
+
if (raw === "")
|
|
29
|
+
return { ok: false, reason: "EMPTY" };
|
|
30
|
+
const q = raw.charAt(0);
|
|
31
|
+
if (q === '"') {
|
|
32
|
+
let out = "";
|
|
33
|
+
for (let i = 1; i < raw.length; i += 1) {
|
|
34
|
+
const c = raw.charAt(i);
|
|
35
|
+
if (c === "\\") {
|
|
36
|
+
// `\"` と `\\` だけを剥がす(他のエスケープは原文のまま)
|
|
37
|
+
const n = raw.charAt(i + 1);
|
|
38
|
+
if (n === '"' || n === "\\") {
|
|
39
|
+
out += n;
|
|
40
|
+
i += 1;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
out += c;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (c === '"')
|
|
47
|
+
return { ok: true, value: out };
|
|
48
|
+
out += c;
|
|
49
|
+
}
|
|
50
|
+
return { ok: false, reason: "BROKEN_VALUE" };
|
|
51
|
+
}
|
|
52
|
+
if (q === "'") {
|
|
53
|
+
let out = "";
|
|
54
|
+
for (let i = 1; i < raw.length; i += 1) {
|
|
55
|
+
const c = raw.charAt(i);
|
|
56
|
+
if (c === "'") {
|
|
57
|
+
if (raw.charAt(i + 1) === "'") {
|
|
58
|
+
out += "'";
|
|
59
|
+
i += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
return { ok: true, value: out };
|
|
63
|
+
}
|
|
64
|
+
out += c;
|
|
65
|
+
}
|
|
66
|
+
return { ok: false, reason: "BROKEN_VALUE" };
|
|
67
|
+
}
|
|
68
|
+
// 裸の値: 空白の後ろの `#` から先はコメント。行頭の `#` は値が無いのと同じ
|
|
69
|
+
if (q === "#")
|
|
70
|
+
return { ok: false, reason: "EMPTY" };
|
|
71
|
+
const bare = (/^(.*?)(?:[ \t]+#.*)?$/.exec(raw) ?? [raw, raw])[1].trim();
|
|
72
|
+
// YAML の null は「無い」
|
|
73
|
+
if (bare === "null" || bare === "~")
|
|
74
|
+
return { ok: false, reason: "EMPTY" };
|
|
75
|
+
// 1 行の文字列以外(ブロックスカラー・flow・アンカー/エイリアス/タグ)は読まない
|
|
76
|
+
if (/^[|>][+-]?$/.test(bare) || /^[\[{&*!]/.test(bare))
|
|
77
|
+
return { ok: false, reason: "UNSUPPORTED" };
|
|
78
|
+
return { ok: true, value: bare };
|
|
79
|
+
}
|
|
80
|
+
/** Home.md の本文から `project` を読む(純関数。正本 `parseHomeProjectName` の写し) */
|
|
81
|
+
function parseHomeProjectName(markdown) {
|
|
82
|
+
const text = String(markdown ?? "").replace(/^/, "").replace(/\r\n?/g, "\n");
|
|
83
|
+
const lines = text.split("\n");
|
|
84
|
+
if (lines.length === 0 || !isDelimiter(lines[0]))
|
|
85
|
+
return { ok: false, reason: "NO_FRONTMATTER" };
|
|
86
|
+
let end = -1;
|
|
87
|
+
for (let i = 1; i < lines.length; i += 1) {
|
|
88
|
+
if (isDelimiter(lines[i])) {
|
|
89
|
+
end = i;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (end < 0)
|
|
94
|
+
return { ok: false, reason: "UNCLOSED_FRONTMATTER" };
|
|
95
|
+
for (let i = 1; i < end; i += 1) {
|
|
96
|
+
const m = /^project:(.*)$/.exec(lines[i]);
|
|
97
|
+
if (!m)
|
|
98
|
+
continue;
|
|
99
|
+
const scalar = parseScalar(m[1]);
|
|
100
|
+
if (!scalar.ok)
|
|
101
|
+
return scalar;
|
|
102
|
+
const value = scalar.value.trim();
|
|
103
|
+
if (value === "")
|
|
104
|
+
return { ok: false, reason: "EMPTY" };
|
|
105
|
+
if (/\{\{[\s\S]*\}\}/.test(value))
|
|
106
|
+
return { ok: false, reason: "PLACEHOLDER" };
|
|
107
|
+
return { ok: true, project: value };
|
|
108
|
+
}
|
|
109
|
+
return { ok: false, reason: "MISSING" };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* ヘッダーに出す案件名を決める。Home.md の `project` が読めればそれ、読めなければ `--title`。
|
|
113
|
+
* @param homeRaw Home.md の本文(無ければ null)
|
|
114
|
+
* @param fallback `--title` の値
|
|
115
|
+
*/
|
|
116
|
+
function resolveSiteTitle(homeRaw, fallback) {
|
|
117
|
+
if (homeRaw == null)
|
|
118
|
+
return fallback;
|
|
119
|
+
const parsed = parseHomeProjectName(homeRaw);
|
|
120
|
+
return parsed.ok ? parsed.project : fallback;
|
|
121
|
+
}
|
package/dist/rederivation.js
CHANGED
|
@@ -289,6 +289,41 @@ function normalizeForCompare(text) {
|
|
|
289
289
|
* 使われない 1 行(`side: cm`)が移行で消えたことだった。`identities` のような欄が
|
|
290
290
|
* 足された晩に、説明文が 1 文字も変わっていないのに印を立てない。
|
|
291
291
|
*/
|
|
292
|
+
/** engine が関係者の記録の末尾に毎晩書く「この人が関わった決定」の印(#702) */
|
|
293
|
+
const MEMBER_DECISIONS_START = "<!-- cortex-engine:member-decisions -->";
|
|
294
|
+
const MEMBER_DECISIONS_END = "<!-- /cortex-engine:member-decisions -->";
|
|
295
|
+
/**
|
|
296
|
+
* 本文から engine の機械ブロック(「この人が関わった決定」)を取り除く(#702)。
|
|
297
|
+
*
|
|
298
|
+
* 🔴 **engine の写し(`scripts/rederive/content-sha.mjs`)と同じ規則。** 変えたら両方を変える。
|
|
299
|
+
* 開始の印**だけ**の行(行末の `\r` は無視)から、終了の印**だけ**の行までを(両端を含めて)消す。終了の印が
|
|
300
|
+
* 無ければ本文の終わりまで消す。塊の前後の空行も除き、両側に本文が残れば空行 1 行でつなぐ。そのあと末尾の空行を落とす。ブロックの無い本文は触らない。
|
|
301
|
+
*
|
|
302
|
+
* 毎晩書き換わる機械の一覧を「人が確認した版から中身が変わっています」に数えないため。
|
|
303
|
+
* ブロックの外を人が 1 文字でも直せば、従来どおり指紋は変わる。
|
|
304
|
+
*/
|
|
305
|
+
function stripMemberDecisions(body) {
|
|
306
|
+
const lines = body.split("\n");
|
|
307
|
+
// 行末の `\r` は見ない(CRLF の記録でも印の行として当てる。当てないと毎晩印が立つ)
|
|
308
|
+
const is = (marker) => (line) => line.replace(/\r$/, "") === marker;
|
|
309
|
+
const start = lines.findIndex(is(MEMBER_DECISIONS_START));
|
|
310
|
+
if (start < 0)
|
|
311
|
+
return body;
|
|
312
|
+
const endRel = lines.slice(start + 1).findIndex(is(MEMBER_DECISIONS_END));
|
|
313
|
+
const end = endRel < 0 ? lines.length : start + 1 + endRel + 1;
|
|
314
|
+
const isBlank = (line) => line.trim() === "";
|
|
315
|
+
// 塊の前後の空行も一緒に除き、両側に本文が残れば空行 1 行でつなぐ(engine の書き手と同じ規則・#702 実装レビュー R1)
|
|
316
|
+
const before = lines.slice(0, start);
|
|
317
|
+
while (before.length && isBlank(before[before.length - 1]))
|
|
318
|
+
before.pop();
|
|
319
|
+
const after = lines.slice(end);
|
|
320
|
+
while (after.length && isBlank(after[0]))
|
|
321
|
+
after.shift();
|
|
322
|
+
const kept = [...before, ...(before.length && after.length ? [""] : []), ...after];
|
|
323
|
+
while (kept.length && isBlank(kept[kept.length - 1]))
|
|
324
|
+
kept.pop();
|
|
325
|
+
return kept.join("\n");
|
|
326
|
+
}
|
|
292
327
|
function comparableContent(raw) {
|
|
293
328
|
let data = {};
|
|
294
329
|
let content = String(raw ?? "");
|
|
@@ -303,7 +338,7 @@ function comparableContent(raw) {
|
|
|
303
338
|
}
|
|
304
339
|
return {
|
|
305
340
|
description: normalizeForCompare(String(data.description ?? "")),
|
|
306
|
-
body: normalizeForCompare(content),
|
|
341
|
+
body: normalizeForCompare(stripMemberDecisions(content)),
|
|
307
342
|
};
|
|
308
343
|
}
|
|
309
344
|
/** 2 つの版で「説明文と本文」が同じか(frontmatter の他の欄の差は見ない) */
|
package/dist/render.js
CHANGED
|
@@ -5741,6 +5741,10 @@ button.ops-card-btn:disabled { opacity: .6; cursor: default; }
|
|
|
5741
5741
|
.body pre code { background: none; padding: 0; color: inherit; }
|
|
5742
5742
|
.body table { border-collapse: collapse; width: 100%; }
|
|
5743
5743
|
.body th, .body td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; }
|
|
5744
|
+
/* 本文中の折りたたみ(#702・関係者の記録の「この人が関わった決定」)。見出しの 1 行を押して開く */
|
|
5745
|
+
.body details { margin: 12px 0; }
|
|
5746
|
+
.body details > summary { cursor: pointer; }
|
|
5747
|
+
.body details[open] > summary { margin-bottom: 8px; }
|
|
5744
5748
|
.body-loading { color: var(--muted); font-size: 13px; padding: 8px 2px; }
|
|
5745
5749
|
|
|
5746
5750
|
/* 一行の結果表示(トースト)。#348 の案件アイコンで使う。
|
package/package.json
CHANGED