@takagaki/cortex-decisions-viewer 0.12.162 → 0.12.164
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 +59 -32
- package/dist/external-id.js +1 -1
- package/dist/render.js +39 -95
- package/dist/story.js +49 -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.RELATED_MATERIALS_SECTIONS = exports.RELATED_URL_MAX = exports.RELATED_MATERIALS_FIELD = exports.DECISION_FIRMNESS = exports.PROJECT_ICON_EXTS = exports.MIRROR_RELS = void 0;
|
|
39
|
+
exports.RELATED_MATERIALS_SECTIONS = exports.RELATED_URL_MAX = exports.RELATED_MATERIALS_FIELD = exports.DECISION_FIRMNESS = exports.GRAPH_RELS = exports.GRAPH_RECORD_TYPES = exports.PROJECT_ICON_EXTS = exports.MIRROR_RELS = void 0;
|
|
40
40
|
exports.sanitizeBody = sanitizeBody;
|
|
41
41
|
exports.findProjectIcon = findProjectIcon;
|
|
42
42
|
exports.readFleetSources = readFleetSources;
|
|
@@ -1025,7 +1025,15 @@ function sanitizeFm(v) {
|
|
|
1025
1025
|
}
|
|
1026
1026
|
// グラフの凡例で独立した分類(=色)を持つ type。Gold 4区画(decision/term/member/rule)は
|
|
1027
1027
|
// それぞれ独立の分類にする(「その他レコード」に丸めない——2026-08-28 オーナー裁定)
|
|
1028
|
-
|
|
1028
|
+
/** つながり画面の点にする記録の種類(仕様 9 章 graph.records-and-evidence)。Home・レポート・その他は点にしない */
|
|
1029
|
+
exports.GRAPH_RECORD_TYPES = new Set(["decision", "term", "member", "rule"]);
|
|
1030
|
+
/** 区画(置き場)の名前 → 点の種類。`type` の無い記録の受け皿 */
|
|
1031
|
+
const GRAPH_SECTION_TYPES = { Decisions: "decision", Glossary: "term", Members: "member", Rules: "rule" };
|
|
1032
|
+
/**
|
|
1033
|
+
* つながり画面の線にする relations の種類(仕様 9 章・記録どうしの frontmatter のつながり)。
|
|
1034
|
+
* 根拠・生成元・関連・置換と、根拠への参照・出典だけ。話題(about)など、機械が名前の一致で付けた手がかりは線にしない
|
|
1035
|
+
*/
|
|
1036
|
+
exports.GRAPH_RELS = new Set(["based_on", "derived_from", "relates_to", "supersedes", "reference", "source"]);
|
|
1029
1037
|
/** ディレクトリ配下の .md を再帰的に列挙する(存在しなければ空) */
|
|
1030
1038
|
async function walkMd(dirAbs) {
|
|
1031
1039
|
const out = [];
|
|
@@ -1676,7 +1684,7 @@ function attachGlow(nodes, glow, recordRelPaths) {
|
|
|
1676
1684
|
* ほうがよい(`createRefResolver` と同じ考え方)。崩れた文字(宛先に見えない参照)も同様に、
|
|
1677
1685
|
* 点は作るが飛び先は付けない。
|
|
1678
1686
|
*/
|
|
1679
|
-
function buildGraph(decisions,
|
|
1687
|
+
function buildGraph(decisions, sections, targetUrls, repoBaseUrl, book = (0, evidence_names_1.emptyEvidenceBook)()) {
|
|
1680
1688
|
const nodes = new Map();
|
|
1681
1689
|
const edges = [];
|
|
1682
1690
|
const addNode = (node) => {
|
|
@@ -1688,11 +1696,14 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
1688
1696
|
for (const d of decisions) {
|
|
1689
1697
|
addNode({ id: d.id, label: d.title, group: "decision", recordId: d.id });
|
|
1690
1698
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1699
|
+
// 🔴 点は決定・ルール・用語・関係者の記録と、その根拠だけ(仕様 graph.question / graph.records-and-evidence)。
|
|
1700
|
+
// Home・レポート・その他レコードは点にしない
|
|
1693
1701
|
for (const sec of sections) {
|
|
1694
1702
|
for (const r of sec.records) {
|
|
1695
|
-
|
|
1703
|
+
// 種類は type で決め、type の無い記録は置き場(区画)で決める。どちらでも 4 種類に当たらないものは点にしない
|
|
1704
|
+
const group = exports.GRAPH_RECORD_TYPES.has(r.rtype) ? r.rtype : GRAPH_SECTION_TYPES[sec.name];
|
|
1705
|
+
if (!group)
|
|
1706
|
+
continue;
|
|
1696
1707
|
addNode({ id: r.id, label: r.title, group, recordId: r.id });
|
|
1697
1708
|
}
|
|
1698
1709
|
}
|
|
@@ -1700,11 +1711,10 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
1700
1711
|
const fileNameToId = new Map();
|
|
1701
1712
|
for (const d of decisions)
|
|
1702
1713
|
fileNameToId.set(d.fileName, d.id);
|
|
1703
|
-
if (home)
|
|
1704
|
-
fileNameToId.set(home.fileName, home.id);
|
|
1705
1714
|
for (const sec of sections)
|
|
1706
1715
|
for (const r of sec.records)
|
|
1707
|
-
|
|
1716
|
+
if (nodes.has(r.id))
|
|
1717
|
+
fileNameToId.set(r.fileName, r.id);
|
|
1708
1718
|
// 🔴 **GitHubの写し(blob)へ飛ぶ点は作らない**(2026-10-01 オーナー決定・分類B)。
|
|
1709
1719
|
// `targetUrls`(minute:/material:)や詳細画面と共用の解決器(RefResolver)は、元ツールの
|
|
1710
1720
|
// URLが無いとき自分のリポジトリの blob URL に倒すことがある(詳細画面ではそれでよい設計)。
|
|
@@ -1745,6 +1755,8 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
1745
1755
|
return ev.id;
|
|
1746
1756
|
};
|
|
1747
1757
|
// 参照先IDから層を推定してノード化する
|
|
1758
|
+
// 🔴 引けない宛先(記録でも根拠でもないもの)は点を作らない。数だけ数えて、ビルドのログに出す(黙って捨てない)
|
|
1759
|
+
let unresolvedTargets = 0;
|
|
1748
1760
|
const classifyTarget = (target, humanLabel) => {
|
|
1749
1761
|
if (nodes.has(target) && !labelRank.has(target))
|
|
1750
1762
|
return target;
|
|
@@ -1755,30 +1767,44 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
1755
1767
|
return resolved;
|
|
1756
1768
|
if (target.startsWith(NO_ORIGINAL_TOOL_PREFIX)) {
|
|
1757
1769
|
// 元ツールが無い根拠(engine #968 の将来の語彙)。リンクにせず、機械の語(urn:…)を出さない
|
|
1758
|
-
return addNode({ id: target, label: NO_ORIGINAL_TOOL_LABEL, group: "
|
|
1770
|
+
return addNode({ id: target, label: NO_ORIGINAL_TOOL_LABEL, group: "silver" });
|
|
1759
1771
|
}
|
|
1760
1772
|
const ev = identifyEvidence(target, identityCtx, humanLabel);
|
|
1761
1773
|
if (ev)
|
|
1762
1774
|
return addEvidence(ev);
|
|
1775
|
+
// 点にしない記録(Home・レポート・その他)へのパス参照は、根拠ではない。点を作らず数える
|
|
1776
|
+
if (/(^|\/)Cortex\/(Home\.md|[^/]+\/records\/)/.test(target)) {
|
|
1777
|
+
unresolvedTargets++;
|
|
1778
|
+
return null;
|
|
1779
|
+
}
|
|
1763
1780
|
if (target.includes("/") || target.toLowerCase().endsWith(".md")) {
|
|
1764
1781
|
// リポジトリ内ファイルへの生のパス参照で、帳(写しの課題・資料・議事録)に当たらないもの。
|
|
1765
1782
|
// 元ツールのURLがまだ無い記録は、点にするが飛び先を付けない(#968 の書き換えを待つ)。
|
|
1766
1783
|
// 名前は最後の段から拡張子を外したもの(`.md` のようなファイルの語を出さない)
|
|
1767
1784
|
return addNode({ id: target, label: (0, evidence_names_1.pathFallbackLabel)(target), group: "silver" });
|
|
1768
1785
|
}
|
|
1769
|
-
|
|
1786
|
+
unresolvedTargets++;
|
|
1787
|
+
return null;
|
|
1770
1788
|
};
|
|
1771
1789
|
const addRelationEdges = (fromId, relations) => {
|
|
1772
1790
|
for (const r of relations) {
|
|
1773
1791
|
// 🔴 `superseded_by` は描かない(#699 F3)。新しい側の `supersedes` の写しで、描くと同じ置き換えが 2 本になる
|
|
1774
1792
|
if (exports.MIRROR_RELS.has(r.rel))
|
|
1775
1793
|
continue;
|
|
1776
|
-
|
|
1794
|
+
// 🔴 線にするのは記録どうしの根拠・生成元・関連・置換だけ(話題 about などは引かない)
|
|
1795
|
+
if (!exports.GRAPH_RELS.has(r.rel))
|
|
1796
|
+
continue;
|
|
1797
|
+
const to = classifyTarget(r.target);
|
|
1798
|
+
if (to)
|
|
1799
|
+
edges.push({ from: fromId, to, rel: r.rel });
|
|
1777
1800
|
}
|
|
1778
1801
|
};
|
|
1779
1802
|
const addSourceEdge = (fromId, source) => {
|
|
1780
|
-
if (source && source.trim() !== "")
|
|
1781
|
-
|
|
1803
|
+
if (source && source.trim() !== "") {
|
|
1804
|
+
const to = classifyTarget(source.trim());
|
|
1805
|
+
if (to)
|
|
1806
|
+
edges.push({ from: fromId, to, rel: "source" });
|
|
1807
|
+
}
|
|
1782
1808
|
};
|
|
1783
1809
|
const addReferenceEdges = (fromId, references) => {
|
|
1784
1810
|
for (const ref of references) {
|
|
@@ -1787,21 +1813,16 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
1787
1813
|
// (`[名前](URL)` の名前は `identifyEvidence` が書かれていた値から拾う。解決器が付けた名前は
|
|
1788
1814
|
// 種類の既定のこともあるので、人の名前としては渡さない)
|
|
1789
1815
|
const raw = REF_RAW.get(ref);
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
};
|
|
1796
|
-
const addLinkEdges = (fromId, links) => {
|
|
1797
|
-
for (const link of links) {
|
|
1798
|
-
edges.push({ from: fromId, to: classifyTarget(link), rel: "link" });
|
|
1816
|
+
const to = raw !== undefined
|
|
1817
|
+
? classifyTarget(raw)
|
|
1818
|
+
: classifyTarget(ref.url ?? ref.label, ref.url && ref.label !== ref.url ? ref.label : undefined);
|
|
1819
|
+
if (to)
|
|
1820
|
+
edges.push({ from: fromId, to, rel: "reference" });
|
|
1799
1821
|
}
|
|
1800
1822
|
};
|
|
1801
1823
|
for (const d of decisions) {
|
|
1802
1824
|
addRelationEdges(d.id, d.relations);
|
|
1803
1825
|
addReferenceEdges(d.id, d.references);
|
|
1804
|
-
addLinkEdges(d.id, d.bodyLinks);
|
|
1805
1826
|
}
|
|
1806
1827
|
// 決めた人の線(決定 → 関係者・#1094)。relations には書かれないので `decided_by[].member` から引く。
|
|
1807
1828
|
// 🔴 相手の関係者がグラフに無ければ引かない(宛先の無い点を作らない=classifyTarget を通さない)
|
|
@@ -1814,16 +1835,12 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
1814
1835
|
edges.push({ from: d.id, to: who.member, rel: "decided_by" });
|
|
1815
1836
|
}
|
|
1816
1837
|
}
|
|
1817
|
-
if (home) {
|
|
1818
|
-
addRelationEdges(home.id, home.relations);
|
|
1819
|
-
addSourceEdge(home.id, home.source);
|
|
1820
|
-
addLinkEdges(home.id, home.bodyLinks);
|
|
1821
|
-
}
|
|
1822
1838
|
for (const sec of sections) {
|
|
1823
1839
|
for (const r of sec.records) {
|
|
1840
|
+
if (!nodes.has(r.id))
|
|
1841
|
+
continue; // 点にしない種類(Home・レポート・その他)の線は引かない
|
|
1824
1842
|
addRelationEdges(r.id, r.relations);
|
|
1825
1843
|
addSourceEdge(r.id, r.source);
|
|
1826
|
-
addLinkEdges(r.id, r.bodyLinks);
|
|
1827
1844
|
}
|
|
1828
1845
|
}
|
|
1829
1846
|
// 自己ループと重複辺を除去する(同じURLが複数回貼られていても辺は1本)
|
|
@@ -1837,6 +1854,9 @@ function buildGraph(decisions, home, sections, targetUrls, repoBaseUrl, book = (
|
|
|
1837
1854
|
seen.add(key);
|
|
1838
1855
|
return true;
|
|
1839
1856
|
});
|
|
1857
|
+
if (unresolvedTargets > 0) {
|
|
1858
|
+
console.log(` つながり: 引けない根拠 ${unresolvedTargets} 件は点にしませんでした(記録でも元のツールの根拠でもない宛先)`);
|
|
1859
|
+
}
|
|
1840
1860
|
return { nodes: [...nodes.values()], edges: deduped };
|
|
1841
1861
|
}
|
|
1842
1862
|
/** 反発(O(n²))。点が少ないうちはこれが最速。 */
|
|
@@ -2332,7 +2352,7 @@ async function buildSite(opts, views) {
|
|
|
2332
2352
|
const rankedDecisions = (0, ranking_1.orderByRanking)(decisions, (d) => recordRelPaths.get(d.id), memoryRanking?.Decisions ?? null);
|
|
2333
2353
|
// 期間フィルタ用の月(YYYY-MM)を日付から導出し、降順(新しい順)にする
|
|
2334
2354
|
const months = uniqueSorted(decisions.map((d) => d.date.slice(0, 7)).filter((m) => /^\d{4}-\d{2}$/.test(m))).reverse();
|
|
2335
|
-
const graph = buildGraph(decisions,
|
|
2355
|
+
const graph = buildGraph(decisions, graphSections, targetUrls, repo.baseUrl, evidenceBook);
|
|
2336
2356
|
computeLayout(graph); // 各ノードに x/y を書き込む(クライアントはこれを初期位置に使いウォームアップ省略)
|
|
2337
2357
|
// 点の明るさと大きさ(engine #551)。順位のファイルが読めない案件では 1 点も付かない(今までと同じ画面)
|
|
2338
2358
|
if (memoryRanking)
|
|
@@ -2347,11 +2367,18 @@ async function buildSite(opts, views) {
|
|
|
2347
2367
|
// ストーリー(#901 V2-5)の日本時間の今日。日・週・月の3つで同じ日にそろえるため1回だけ計算する
|
|
2348
2368
|
const storyToday = (0, story_1.todayJst)(new Date(opts.generatedAt));
|
|
2349
2369
|
// ストーリーの日々の動き(#901 V2-5)。帳簿で on の日のファイルが無ければ null(キーごと出さない)
|
|
2370
|
+
const shownIds = new Set([...decisions.map((d) => d.id), ...sections.flatMap((s) => s.records.map((r) => r.id))]);
|
|
2371
|
+
const idByRepoPath = new Map();
|
|
2372
|
+
for (const [id, rel] of recordRelPaths)
|
|
2373
|
+
if (shownIds.has(id))
|
|
2374
|
+
idByRepoPath.set(rel, id);
|
|
2350
2375
|
const story = await (0, story_1.readStory)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null,
|
|
2351
2376
|
// 動きのない日を埋める終点=ビルドの時刻の日本時間の日付(時計を注入できる)
|
|
2352
2377
|
storyToday, ledgerDir,
|
|
2353
2378
|
// 名前のリンク(story.screen-name-links): 日の relations の関係者・用語・ルールを、画面にある記録の表示名・別名・同義語で本文に当てる
|
|
2354
|
-
(targets) => (0, story_1.storyNameLinks)(targets, storyNameSources(sections))
|
|
2379
|
+
(targets) => (0, story_1.storyNameLinks)(targets, storyNameSources(sections)),
|
|
2380
|
+
// 本文の相対リンク(決定の記録など)の飛び先: 画面に載る記録(決定・各区画)のパス → id
|
|
2381
|
+
(repoPath) => idByRepoPath.get(repoPath));
|
|
2355
2382
|
// 週・月のあらすじ(#901)。週・月のファイルが無ければ null(キーごと出さない=今までと同じ画面)
|
|
2356
2383
|
const storyDigest = await (0, story_1.readStoryDigest)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null, storyToday, ledgerDir);
|
|
2357
2384
|
// 案件アイコン(#348)。Cortex構成でなければ undefined(旧レイアウトには置き場が無い)
|
package/dist/external-id.js
CHANGED
|
@@ -31,7 +31,7 @@ exports.memberSearchTerms = memberSearchTerms;
|
|
|
31
31
|
/**
|
|
32
32
|
* このリポジトリの内側の呼び名。外の ID と見なさない。
|
|
33
33
|
*
|
|
34
|
-
* 前半は記録の id
|
|
34
|
+
* 前半は記録の id の種類(記録の種類の一覧と同じ)、後半は Silver の参照
|
|
35
35
|
* (`build.ts` の `classifyTarget` が正本の URL に解決する 3 つ)。
|
|
36
36
|
*/
|
|
37
37
|
const INSIDE_KINDS = new Set([
|
package/dist/render.js
CHANGED
|
@@ -4790,14 +4790,6 @@ a:hover { text-decoration: underline; }
|
|
|
4790
4790
|
box-shadow: var(--shadow); cursor: grab; display: block;
|
|
4791
4791
|
}
|
|
4792
4792
|
.graph-canvas.dragging { cursor: grabbing; }
|
|
4793
|
-
.graph-legend {
|
|
4794
|
-
display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
|
|
4795
|
-
margin: 10px 2px 0; font-size: 12.5px; color: var(--muted);
|
|
4796
|
-
}
|
|
4797
|
-
.graph-legend .dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 4px; vertical-align: -1px; }
|
|
4798
|
-
.graph-legend .line { display: inline-block; width: 16px; height: 0; border-top: 3px solid; margin-right: 4px; vertical-align: 4px; }
|
|
4799
|
-
.graph-legend .sep { color: var(--muted); margin-left: 8px; padding-left: 8px; border-left: 1px solid var(--border); }
|
|
4800
|
-
.graph-legend label { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; color: var(--text); }
|
|
4801
4793
|
|
|
4802
4794
|
.graph-reset {
|
|
4803
4795
|
position: absolute; top: 12px; right: 12px; z-index: 2;
|
|
@@ -8182,10 +8174,10 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
8182
8174
|
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
8183
8175
|
},
|
|
8184
8176
|
"graph": {
|
|
8185
|
-
text: "
|
|
8177
|
+
text: "決定・ルール・用語・関係者と、その根拠(元のツール)のつながりの地図です。点をクリックすると、そのページや元のツール(Backlog・GitHub等)に移動できます。",
|
|
8186
8178
|
},
|
|
8187
8179
|
"explorer": {
|
|
8188
|
-
text: "
|
|
8180
|
+
text: "決定・ルール・用語・関係者を起点に、つながり(根拠・出典)を1列ずつ辿る表示です。行をクリックすると右に次の列が開きます。",
|
|
8189
8181
|
},
|
|
8190
8182
|
"story": {
|
|
8191
8183
|
text: "このプロジェクトの日々の動きを、日次・週次・月次に分けてまとめた記録です。直したいことや足したいことは、各カードの「GitHubで編集」か「AIで編集」から書けます。",
|
|
@@ -19940,42 +19932,30 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19940
19932
|
term: { color: "#4ade80", label: "用語集" },
|
|
19941
19933
|
member: { color: "#f472b6", label: "関係者" },
|
|
19942
19934
|
rule: { color: "#fde047", label: "ルール" },
|
|
19943
|
-
report: { color: "#22d3ee", label: "レポート" },
|
|
19944
|
-
overview: { color: "#fb923c", label: "Home" },
|
|
19945
|
-
record: { color: "#0e7490", label: "その他レコード" },
|
|
19946
19935
|
silver: { color: "#9ca3af", label: "根拠(課題・議事録・資料・リンク等)" },
|
|
19947
19936
|
bronze: { color: "#9ca3af", label: "根拠(課題・議事録・資料・リンク等)" },
|
|
19948
19937
|
external: { color: "#9ca3af", label: "根拠(課題・議事録・資料・リンク等)" },
|
|
19949
|
-
phantom: { color: "#d4d9e0", label: "参照先が見つからないもの" },
|
|
19950
19938
|
};
|
|
19939
|
+
/* 点の色は区画を見分けるためだけに残す。凡例(分類の一覧)は出さない(仕様 graph.content-only) */
|
|
19940
|
+
var GROUP_FALLBACK = { color: "#9ca3af" };
|
|
19951
19941
|
/* 線(関係)の種類ごとの色・ラベルと、「Goldどうし」か「根拠」かの分類(engine #551・分類A 10/1)。
|
|
19952
19942
|
* **一覧(凡例)はこの表から組む**ので、将来 about(決定・ルール→用語)や is_a/part_of、
|
|
19953
19943
|
* 人→用語・人→人の線を足すときは、ここに1行足すだけでよい——凡例もトグルも自動で追従し、
|
|
19954
19944
|
* 既存の行・色は変わらない(語彙が増えても凡例が崩れない作り)。
|
|
19955
19945
|
* kind: "gold"=Goldのレコードどうしを結ぶ線/"evidence"=Goldがその根拠を指す線(弱いつながり)。 */
|
|
19956
19946
|
var REL_META = {
|
|
19957
|
-
based_on: { color: "#2563eb", label: "根拠"
|
|
19958
|
-
derived_from: { color: "#0d9488", label: "生成元"
|
|
19959
|
-
relates_to: { color: "#94a3b8", label: "関連"
|
|
19960
|
-
supersedes: { color: "#dc2626", label: "置換"
|
|
19961
|
-
reference: { color: "#d1d5db", label: "参照"
|
|
19962
|
-
source: { color: "#d1d5db", label: "出典"
|
|
19963
|
-
|
|
19964
|
-
// 話題の線(決定・ルール・関係者 → 用語・engine #551 の about)。名前の一致で機械が付けた手がかりなので、
|
|
19965
|
-
// 根拠の線と同じく細く薄い「ゆるいつながり」として描き、凡例は日本語の 1 行(栓 about_relations の on の条件)
|
|
19966
|
-
about: { color: "#ddd6fe", label: "話題", kind: "topic" },
|
|
19967
|
-
// 決めた人の線(決定 → 関係者・#1094)。relations ではなく decided_by から引く。
|
|
19968
|
-
// 🔴 凡例には**この線がグラフにあるときだけ**出す(欄の無い案件の凡例を変えない)
|
|
19969
|
-
decided_by: { color: "#f472b6", label: "決めた人", kind: "gold", onlyWhenPresent: true },
|
|
19947
|
+
based_on: { color: "#2563eb", label: "根拠" },
|
|
19948
|
+
derived_from: { color: "#0d9488", label: "生成元" },
|
|
19949
|
+
relates_to: { color: "#94a3b8", label: "関連" },
|
|
19950
|
+
supersedes: { color: "#dc2626", label: "置換" },
|
|
19951
|
+
reference: { color: "#d1d5db", label: "参照" },
|
|
19952
|
+
source: { color: "#d1d5db", label: "出典" },
|
|
19953
|
+
decided_by: { color: "#f472b6", label: "決めた人" },
|
|
19970
19954
|
};
|
|
19971
|
-
var WEAK_RELS = {};
|
|
19972
19955
|
var REL_COLOR = {};
|
|
19973
|
-
Object.keys(REL_META).forEach(function (r) {
|
|
19974
|
-
REL_COLOR[r] = REL_META[r].color;
|
|
19975
|
-
if (REL_META[r].kind === "evidence" || REL_META[r].kind === "topic") WEAK_RELS[r] = true;
|
|
19976
|
-
});
|
|
19956
|
+
Object.keys(REL_META).forEach(function (r) { REL_COLOR[r] = REL_META[r].color; });
|
|
19977
19957
|
// period: 期間で光らせる(engine #551)。unit は all(光らせない)/day/week、day は基準の日(YYYY-MM-DD)
|
|
19978
|
-
var graphState = {
|
|
19958
|
+
var graphState = { q: "", period: { unit: "all", day: null } };
|
|
19979
19959
|
|
|
19980
19960
|
/** 期間の範囲(両端を含む YYYY-MM-DD)。「すべて」・日付が無いときは null。週は月曜〜日曜(ストーリーの週と同じ区切り) */
|
|
19981
19961
|
function graphPeriodRange(period) {
|
|
@@ -20007,7 +19987,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20007
19987
|
countEl.textContent = "点 " + graph.nodes.length + " / つながり " + graph.edges.length;
|
|
20008
19988
|
|
|
20009
19989
|
var filters = el("div", { class: "filters" });
|
|
20010
|
-
var search = el("input", { type: "search", placeholder: "
|
|
19990
|
+
var search = el("input", { type: "search", placeholder: "キーワードで探す(決定・ルール・用語・関係者は本文の中まで探します。見つかった点とそのつながりが光ります)" });
|
|
20011
19991
|
search.value = graphState.q;
|
|
20012
19992
|
filters.appendChild(search);
|
|
20013
19993
|
app.appendChild(filters);
|
|
@@ -20019,43 +19999,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20019
19999
|
wrap.appendChild(canvas);
|
|
20020
20000
|
app.appendChild(wrap);
|
|
20021
20001
|
|
|
20022
|
-
//
|
|
20023
|
-
var legend = el("div", { class: "graph-legend" });
|
|
20024
|
-
var legendShown = {};
|
|
20025
|
-
Object.keys(GROUP_STYLE).forEach(function (g) {
|
|
20026
|
-
var anyNode = graph.nodes.some(function (n) { return n.group === g; });
|
|
20027
|
-
if (!anyNode || legendShown[GROUP_STYLE[g].label]) return;
|
|
20028
|
-
legendShown[GROUP_STYLE[g].label] = true;
|
|
20029
|
-
var item = el("span", {}, [el("span", { class: "dot" }), document.createTextNode(GROUP_STYLE[g].label)]);
|
|
20030
|
-
item.querySelector(".dot").style.background = GROUP_STYLE[g].color;
|
|
20031
|
-
legend.appendChild(item);
|
|
20032
|
-
});
|
|
20033
|
-
|
|
20034
|
-
// 線(関係)の色凡例。グラフ内の有無に関わらず全種別を、Goldどうし/根拠の2群で表示する
|
|
20035
|
-
[["gold", "線(Goldどうし):"], ["evidence", "線(根拠):"], ["topic", "線(話題):"]].forEach(function (pair) {
|
|
20036
|
-
var kind = pair[0], heading = pair[1];
|
|
20037
|
-
var keys = Object.keys(REL_META).filter(function (r) {
|
|
20038
|
-
if (REL_META[r].kind !== kind) return false;
|
|
20039
|
-
return !REL_META[r].onlyWhenPresent || graph.edges.some(function (e) { return e.rel === r; });
|
|
20040
|
-
});
|
|
20041
|
-
if (!keys.length) return;
|
|
20042
|
-
legend.appendChild(el("span", { class: "sep", text: heading }));
|
|
20043
|
-
keys.forEach(function (r) {
|
|
20044
|
-
var li = el("span", {}, [el("span", { class: "line" }), document.createTextNode(REL_META[r].label)]);
|
|
20045
|
-
li.querySelector(".line").style.borderTopColor = REL_META[r].color;
|
|
20046
|
-
legend.appendChild(li);
|
|
20047
|
-
});
|
|
20048
|
-
});
|
|
20049
|
-
|
|
20050
|
-
var toggle = el("label", {});
|
|
20051
|
-
var cb = el("input", { type: "checkbox" });
|
|
20052
|
-
cb.checked = graphState.showWeak;
|
|
20053
|
-
cb.addEventListener("change", function () { graphState.showWeak = cb.checked; renderList(); });
|
|
20054
|
-
toggle.appendChild(cb);
|
|
20055
|
-
toggle.appendChild(document.createTextNode("ゆるいつながり(参照・本文リンク・話題)も表示"));
|
|
20056
|
-
legend.appendChild(toggle);
|
|
20057
|
-
app.appendChild(legend);
|
|
20058
|
-
|
|
20002
|
+
// 🔴 凡例(分類・線の種類の一覧)と、根拠の点を隠す切り替えは持たない(仕様 graph.content-only / graph.evidence-always)
|
|
20059
20003
|
startGraph(canvas, search, graph, periodCtl);
|
|
20060
20004
|
}
|
|
20061
20005
|
|
|
@@ -20117,12 +20061,9 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20117
20061
|
}
|
|
20118
20062
|
|
|
20119
20063
|
function startGraph(canvas, searchInput, graph, periodCtl) {
|
|
20120
|
-
var allEdges = graph.edges
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
// レコード以外の点は、**ゆるいつながりを外す前**の線(消した記録の線は graphWithoutDeleted が落としてある)が 1 本でもあれば出す。
|
|
20124
|
-
// 🔴 **表示を絞る切り替えで根拠の点を隠さない**(仕様 graph.evidence-always)——切り替えで変わるのは線だけ。
|
|
20125
|
-
// group では見分けない(元のツールが無い根拠も group phantom で作られる)。消した記録だけが指していた点は線が無いので出ない
|
|
20064
|
+
var allEdges = graph.edges;
|
|
20065
|
+
// レコード以外の点(根拠)は、線が 1 本でもあれば出す。🔴 根拠の点を隠す切り替えは持たない(仕様 graph.evidence-always)。
|
|
20066
|
+
// 消した記録だけが指していた点は線が無いので出ない
|
|
20126
20067
|
var used = {};
|
|
20127
20068
|
graph.edges.forEach(function (e) { used[e.from] = true; used[e.to] = true; });
|
|
20128
20069
|
// ビルド時に算出済みの座標(n.x/n.y)があればそれを使う=ウォームアップ不要で開いた瞬間に整列済み。
|
|
@@ -20587,8 +20528,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20587
20528
|
var EDGE_DARK = {
|
|
20588
20529
|
based_on: "rgba(125,165,255,0.85)", derived_from: "rgba(94,234,212,0.75)",
|
|
20589
20530
|
relates_to: "rgba(148,163,184,0.6)", supersedes: "rgba(248,113,113,0.9)",
|
|
20590
|
-
reference: "rgba(180,196,230,0.
|
|
20591
|
-
about: "rgba(196,181,253,0.22)",
|
|
20531
|
+
reference: "rgba(180,196,230,0.6)", source: "rgba(180,196,230,0.6)",
|
|
20592
20532
|
decided_by: "rgba(244,114,182,0.75)",
|
|
20593
20533
|
};
|
|
20594
20534
|
// 画面に入っている範囲(世界座標)。両端が同じ側の外にある線は描かない
|
|
@@ -20608,9 +20548,9 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20608
20548
|
var span = Math.max(highO - lowO, 1) * SWEEP_STEP_MS;
|
|
20609
20549
|
var prog = Math.max(0, Math.min(1, (sweepT - lowO * SWEEP_STEP_MS) / span));
|
|
20610
20550
|
if (prog > 0) {
|
|
20611
|
-
ctx.globalAlpha =
|
|
20551
|
+
ctx.globalAlpha = 0.95;
|
|
20612
20552
|
ctx.strokeStyle = "#fcd34d";
|
|
20613
|
-
ctx.lineWidth = (e.rel === "supersedes" ? 2 :
|
|
20553
|
+
ctx.lineWidth = (e.rel === "supersedes" ? 2 : 1.6) / view.scale;
|
|
20614
20554
|
ctx.beginPath();
|
|
20615
20555
|
ctx.moveTo(lowN.x, lowN.y);
|
|
20616
20556
|
ctx.lineTo(lowN.x + (highN.x - lowN.x) * prog, lowN.y + (highN.y - lowN.y) * prog);
|
|
@@ -20619,18 +20559,18 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20619
20559
|
} else {
|
|
20620
20560
|
ctx.globalAlpha = 0.06; // リネージ外は沈める
|
|
20621
20561
|
ctx.strokeStyle = EDGE_DARK[e.rel] || "rgba(180,196,230,0.3)";
|
|
20622
|
-
ctx.lineWidth =
|
|
20562
|
+
ctx.lineWidth = 1.6 / view.scale;
|
|
20623
20563
|
ctx.beginPath(); ctx.moveTo(e.a.x, e.a.y); ctx.lineTo(e.b.x, e.b.y); ctx.stroke();
|
|
20624
20564
|
}
|
|
20625
20565
|
return;
|
|
20626
20566
|
}
|
|
20627
20567
|
// 通常 / 検索
|
|
20628
20568
|
var hot = searching && (matched[e.a.id] || matched[e.b.id]);
|
|
20629
|
-
var a = hot ? 0.95 :
|
|
20569
|
+
var a = hot ? 0.95 : 0.85;
|
|
20630
20570
|
if (searching && !hot) a = 0.06;
|
|
20631
20571
|
ctx.globalAlpha = a;
|
|
20632
20572
|
ctx.strokeStyle = hot ? "#fcd34d" : (EDGE_DARK[e.rel] || "rgba(180,196,230,0.3)");
|
|
20633
|
-
ctx.lineWidth = (hot ? 1.8 : e.rel === "supersedes" ? 2 :
|
|
20573
|
+
ctx.lineWidth = (hot ? 1.8 : e.rel === "supersedes" ? 2 : 1.6) / view.scale;
|
|
20634
20574
|
ctx.beginPath();
|
|
20635
20575
|
ctx.moveTo(e.a.x, e.a.y);
|
|
20636
20576
|
ctx.lineTo(e.b.x, e.b.y);
|
|
@@ -20642,7 +20582,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20642
20582
|
var cw = canvas.width, ch = canvas.height;
|
|
20643
20583
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
20644
20584
|
nodes.forEach(function (n) {
|
|
20645
|
-
var style = GROUP_STYLE[n.group] ||
|
|
20585
|
+
var style = GROUP_STYLE[n.group] || GROUP_FALLBACK;
|
|
20646
20586
|
var alphaN = 1;
|
|
20647
20587
|
if (searching && !lit[n.id]) alphaN = 0.12;
|
|
20648
20588
|
if (hovered) {
|
|
@@ -20923,7 +20863,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20923
20863
|
(adjDir[e.from] = adjDir[e.from] || []).push({ id: e.to, rel: e.rel, dir: "out" });
|
|
20924
20864
|
(adjDir[e.to] = adjDir[e.to] || []).push({ id: e.from, rel: e.rel, dir: "in" });
|
|
20925
20865
|
});
|
|
20926
|
-
var REL_ORDER = ["supersedes", "based_on", "derived_from", "relates_to", "decided_by", "source", "reference"
|
|
20866
|
+
var REL_ORDER = ["supersedes", "based_on", "derived_from", "relates_to", "decided_by", "source", "reference"];
|
|
20927
20867
|
|
|
20928
20868
|
function recordText(rec) {
|
|
20929
20869
|
if (!rec) return "";
|
|
@@ -20935,11 +20875,17 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20935
20875
|
|
|
20936
20876
|
// Gold層のフォルダ(1列目)。検索ヒット表示と使い分ける
|
|
20937
20877
|
var goldGroups = [];
|
|
20938
|
-
|
|
20939
|
-
//
|
|
20940
|
-
|
|
20878
|
+
// 削除済みは列にも件数にも出さない(一覧・グラフと同じ扱いに揃える)。
|
|
20879
|
+
// 🔴 点にならない記録(Home・レポート・その他)は出さない——グラフの点(nodeById)にある記録だけ
|
|
20880
|
+
var inGraph = {};
|
|
20881
|
+
(explorerGraph.nodes || []).forEach(function (n) { if (n.recordId) inGraph[n.recordId] = true; });
|
|
20882
|
+
function graphIds(list) {
|
|
20883
|
+
return visibleRecords(list).map(function (r) { return r.id; }).filter(function (id) { return inGraph[id]; });
|
|
20884
|
+
}
|
|
20885
|
+
goldGroups.push({ name: "Decisions", ids: graphIds(SITE.decisions) });
|
|
20941
20886
|
(SITE.sections || []).forEach(function (sec) {
|
|
20942
|
-
|
|
20887
|
+
var ids = graphIds(sec.records);
|
|
20888
|
+
if (ids.length) goldGroups.push({ name: sec.name, ids: ids });
|
|
20943
20889
|
});
|
|
20944
20890
|
|
|
20945
20891
|
function renderExplorer() {
|
|
@@ -20981,7 +20927,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
20981
20927
|
app.appendChild(wrap);
|
|
20982
20928
|
|
|
20983
20929
|
function nodeDot(n) {
|
|
20984
|
-
var style = GROUP_STYLE[n.group] ||
|
|
20930
|
+
var style = GROUP_STYLE[n.group] || GROUP_FALLBACK;
|
|
20985
20931
|
var dot = el("span", { class: "mc-dot" });
|
|
20986
20932
|
dot.style.background = style.color;
|
|
20987
20933
|
return dot;
|
|
@@ -21145,10 +21091,8 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
21145
21091
|
path.setAttribute("d", "M " + x1 + " " + y1 + " C " + mx + " " + y1 + ", " + mx + " " + y2 + ", " + x2 + " " + y2);
|
|
21146
21092
|
path.setAttribute("fill", "none");
|
|
21147
21093
|
path.setAttribute("stroke", REL_COLOR[rows[j].getAttribute("data-rel")] || "#cbd5e1");
|
|
21148
|
-
|
|
21149
|
-
|
|
21150
|
-
path.setAttribute("stroke-width", weakLine ? "1" : "1.5");
|
|
21151
|
-
path.setAttribute("opacity", weakLine ? "0.5" : "0.85");
|
|
21094
|
+
path.setAttribute("stroke-width", "1.5");
|
|
21095
|
+
path.setAttribute("opacity", "0.85");
|
|
21152
21096
|
lines.appendChild(path);
|
|
21153
21097
|
}
|
|
21154
21098
|
}
|
package/dist/story.js
CHANGED
|
@@ -19,6 +19,7 @@ exports.isEngineQuietDay = isEngineQuietDay;
|
|
|
19
19
|
exports.todayJst = todayJst;
|
|
20
20
|
exports.quietStoryRow = quietStoryRow;
|
|
21
21
|
exports.withQuietDays = withQuietDays;
|
|
22
|
+
exports.linkRecordRefs = linkRecordRefs;
|
|
22
23
|
exports.readStory = readStory;
|
|
23
24
|
exports.storyWeekHeading = storyWeekHeading;
|
|
24
25
|
exports.storyMonthHeading = storyMonthHeading;
|
|
@@ -75,19 +76,29 @@ const MACHINE_WORD_RE = /urn:|cortex-engine|sha=|<!--|-->|\.md\b|Days\/|story-da
|
|
|
75
76
|
* 門に掛ける前にリンクの文字だけに剝がす。外の URL(`https://…`・`<https://…>`)は対象外(そのまま残す)。
|
|
76
77
|
* 本文に素で書かれた `foo.md` はここでは剝がさず、`MACHINE_WORD_RE` が行ごと伏せる
|
|
77
78
|
*/
|
|
78
|
-
const RELATIVE_MD_LINK_RE = /\[([^\]\n]*)\]\(\s*<?(?![a-zA-Z][a-zA-Z0-9+.-]*:|\/\/|#)[^)\s<>]*\.md(?:#[^)\s<>]*)?>?\s*\)/g;
|
|
79
|
+
const RELATIVE_MD_LINK_RE = /\[([^\]\n]*)\]\(\s*<?(?![a-zA-Z][a-zA-Z0-9+.-]*:|\/\/|#)([^)\s<>]*\.md)(?:#[^)\s<>]*)?>?\s*\)/g;
|
|
79
80
|
const LINK_OPEN_RE = /\[[^\]\n]*\]\([^)\s]*/g;
|
|
80
81
|
/**
|
|
81
82
|
* 相対 `.md` のリンクを文字に剝がす。剝がした結果、剝がす前の行に無かった `](` のリンクが現れる行(入れ子のリンクなど)は、
|
|
82
83
|
* 剝がさず元のまま返す(`.md` が残るので `MACHINE_WORD_RE` が行ごと伏せる)。後ろの関所に頼らない
|
|
83
84
|
*/
|
|
84
|
-
const unlinkRelativeMd = (line) => {
|
|
85
|
+
const unlinkRelativeMd = (line, keepRecordRefs = false) => {
|
|
85
86
|
const out = line.replace(RELATIVE_MD_LINK_RE, "$1");
|
|
86
87
|
if (out === line)
|
|
87
88
|
return line;
|
|
88
89
|
const before = new Set(line.match(LINK_OPEN_RE) ?? []);
|
|
89
|
-
|
|
90
|
+
if ((out.match(LINK_OPEN_RE) ?? []).some((m) => !before.has(m)))
|
|
91
|
+
return line;
|
|
92
|
+
if (!keepRecordRefs)
|
|
93
|
+
return out;
|
|
94
|
+
// 記録(`…/records/<名前>.md`)を指すものは、`.md` を落とした印のリンク `#rec:<相対パス>` にして残す(門の `.md` の伏せを通すため)。
|
|
95
|
+
// 画面の中の飛び先への差し替えは `linkRecordRefs`(build が記録の表を握って渡す)。それ以外の相対リンクは今までどおり文字に剝がす
|
|
96
|
+
return line.replace(RELATIVE_MD_LINK_RE, (_m, text, href) => RECORD_HREF_RE.test(href) ? `[${text}](${RECORD_REF_PREFIX}${href.slice(0, -3)})` : text);
|
|
90
97
|
};
|
|
98
|
+
/** 記録の相対パス(`…/records/<名前>.md`) */
|
|
99
|
+
const RECORD_HREF_RE = /(?:^|\/)records\/[^/]+\.md$/;
|
|
100
|
+
/** 記録を指すリンクの印。門(`.md` の伏せ)と関所(相対 URL を許す)を通り、`linkRecordRefs` が飛び先に差し替える */
|
|
101
|
+
const RECORD_REF_PREFIX = "#rec:";
|
|
91
102
|
/** 太字だけの 1 行(筋の名前の行) */
|
|
92
103
|
const BOLD_ONLY_RE = /^\*\*[^*]+\*\*$/;
|
|
93
104
|
/** 見出しの行(`##`〜`######`) */
|
|
@@ -346,7 +357,7 @@ function storyDayMarkdown(content, day) {
|
|
|
346
357
|
}
|
|
347
358
|
// 機械の行: 印と機械の語の行を伏せる
|
|
348
359
|
// 決定の記録へのリンクは、門に掛ける前に文字だけに剝がす(行ごと伏せない)
|
|
349
|
-
const clean = (ls) => ls.map(unlinkRelativeMd).filter((l) => !STORY_MARKER_RE.test(l.trim()) && !MACHINE_WORD_RE.test(l));
|
|
360
|
+
const clean = (ls) => ls.map((l) => unlinkRelativeMd(l, true)).filter((l) => !STORY_MARKER_RE.test(l.trim()) && !MACHINE_WORD_RE.test(l));
|
|
350
361
|
// 🔴 人の追記: 落とすのは**印の行だけ**(人が書いた行を画面が黙って消さない・実装レビュー 🟠2)
|
|
351
362
|
const humanKept = human.filter((l) => !STORY_MARKER_RE.test(l.trim()));
|
|
352
363
|
const out = withoutEmptySections(withoutEmptyBoldHeads([...clean(main), ...clean(lateAfter)]));
|
|
@@ -466,6 +477,35 @@ async function loadStoryLedger(repoRoot, warn, ledgerDir) {
|
|
|
466
477
|
}
|
|
467
478
|
return ledger;
|
|
468
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* 本文(関所を通した HTML)の相対リンクのうち、記録(`Cortex/<区画>/records/*.md`)を指すものを
|
|
482
|
+
* 画面の中のその記録への飛び先(人名・用語のリンクと同じ `a.wikilink[data-go]`)にする。
|
|
483
|
+
*
|
|
484
|
+
* - 相対パスはその日のファイルの置き場から解く(`../../../Decisions/records/x.md` → `Cortex/Decisions/records/x.md`)
|
|
485
|
+
* - 🔴 記録が見つからない(`idOf` が undefined)相対リンクは `<a>` を外して文字のまま残す(壊れたリンク・行き止まりを作らない)
|
|
486
|
+
* - 外の URL(http・https・mailto など `scheme:`・`//`)とページ内の `#…` は触らない
|
|
487
|
+
*
|
|
488
|
+
* 呼び手は**サニタイズ後**の HTML を渡す(属性値・文字はエスケープ済み。ここで新しい HTML は混ぜず、data-go の値だけ & と " を逃がす)
|
|
489
|
+
*/
|
|
490
|
+
function linkRecordRefs(html, dayRepoPath, idOf) {
|
|
491
|
+
const baseDir = node_path_1.default.posix.dirname(dayRepoPath);
|
|
492
|
+
return html.replace(/<a href="([^"]*)">([\s\S]*?)<\/a>/g, (whole, href, inner) => {
|
|
493
|
+
const isRef = href.startsWith(RECORD_REF_PREFIX);
|
|
494
|
+
if (!isRef && (/^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith("//") || href.startsWith("#")))
|
|
495
|
+
return whole;
|
|
496
|
+
let target = isRef ? href.slice(RECORD_REF_PREFIX.length).replace(/&/g, "&") + ".md" : href.replace(/&/g, "&").replace(/[?#].*$/, "");
|
|
497
|
+
try {
|
|
498
|
+
target = decodeURIComponent(target);
|
|
499
|
+
}
|
|
500
|
+
catch { /* 解けない綴りはそのまま照合して見つからなければ文字に落とす */ }
|
|
501
|
+
const resolved = target.startsWith("/") ? node_path_1.default.posix.normalize(target.slice(1)) : node_path_1.default.posix.join(baseDir, target);
|
|
502
|
+
const id = /^Cortex\/[^/]+\/records\/[^/]+\.md$/.test(resolved) ? idOf(resolved) : undefined;
|
|
503
|
+
if (!id)
|
|
504
|
+
return inner;
|
|
505
|
+
const attr = id.replace(/&/g, "&").replace(/"/g, """);
|
|
506
|
+
return `<a class="wikilink" data-go="${attr}">${inner}</a>`;
|
|
507
|
+
});
|
|
508
|
+
}
|
|
469
509
|
/**
|
|
470
510
|
* 案件のストーリーを読む。**載せる日が無ければ `null`**(タブも器も出さない)。
|
|
471
511
|
*
|
|
@@ -479,7 +519,9 @@ today = todayJst(),
|
|
|
479
519
|
// 帳簿の置き場(既定は `<repoRoot>/.cortex`)
|
|
480
520
|
ledgerDir,
|
|
481
521
|
// 日の `relations` の target から名前のリンクの表を作る口(build が記録の表を握って渡す。無ければリンクは付けない)
|
|
482
|
-
linksOf = () => []
|
|
522
|
+
linksOf = () => [],
|
|
523
|
+
// 本文の相対リンクが指す記録(リポジトリ相対パス)の id。画面にある記録だけ返す(無ければリンクは文字に落とす)
|
|
524
|
+
recordIdOf = () => undefined) {
|
|
483
525
|
const ledger = await loadStoryLedger(repoRoot, true, ledgerDir);
|
|
484
526
|
if (!ledger)
|
|
485
527
|
return null;
|
|
@@ -543,7 +585,7 @@ linksOf = () => []) {
|
|
|
543
585
|
const links = linksOf(dayRelationTargets(fm));
|
|
544
586
|
const partialHour = dayPartialHour(fm);
|
|
545
587
|
out.push({
|
|
546
|
-
date: f.day, heading: heading, bodyHtml: await renderMarkdown(markdown),
|
|
588
|
+
date: f.day, heading: heading, bodyHtml: linkRecordRefs(await renderMarkdown(markdown), f.rel, recordIdOf),
|
|
547
589
|
...(editUrl ? { editUrl: editUrl } : {}),
|
|
548
590
|
...(partialHour !== null ? { partialHour: partialHour } : {}),
|
|
549
591
|
...(links.length ? { links: links } : {}),
|
|
@@ -613,7 +655,7 @@ function storyDigestParts(content) {
|
|
|
613
655
|
const end = body.findIndex((l) => DIGEST_END_RE.test(l.trim()));
|
|
614
656
|
const at = body.findIndex((l, i) => i > end && l.trim() === HUMAN_HEADING);
|
|
615
657
|
// 機械の部分の `#` の見出しの行は落とす(流れは段落だけの約束。見出しが紛れると後ろの文が別の節に見える)
|
|
616
|
-
const main = withoutHiddenBoldGroups((at < 0 ? body : body.slice(0, at)).map(unlinkRelativeMd).filter((l) => !STORY_MARKER_RE.test(l.trim()) && !/^\s{0,3}#{1,6}(\s|$)/.test(l)))
|
|
658
|
+
const main = withoutHiddenBoldGroups((at < 0 ? body : body.slice(0, at)).map((l) => unlinkRelativeMd(l)).filter((l) => !STORY_MARKER_RE.test(l.trim()) && !/^\s{0,3}#{1,6}(\s|$)/.test(l)))
|
|
617
659
|
.filter((l) => !MACHINE_WORD_RE.test(l));
|
|
618
660
|
const human = (at < 0 ? [] : body.slice(at + 1)).filter((l) => !STORY_MARKER_RE.test(l.trim()));
|
|
619
661
|
let phase = null;
|
package/package.json
CHANGED