@takagaki/cortex-decisions-viewer 0.12.172 → 0.12.173
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 +12 -0
- package/dist/member-origin.js +115 -0
- package/dist/render.js +69 -8
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -82,6 +82,7 @@ const memory_evidence_1 = require("./memory-evidence");
|
|
|
82
82
|
const hands_1 = require("./hands");
|
|
83
83
|
const rederivation_1 = require("./rederivation");
|
|
84
84
|
const external_id_1 = require("./external-id");
|
|
85
|
+
const member_origin_1 = require("./member-origin");
|
|
85
86
|
const home_project_1 = require("./home-project");
|
|
86
87
|
const supersedes_1 = require("./supersedes");
|
|
87
88
|
const minify_1 = require("./minify");
|
|
@@ -2400,6 +2401,8 @@ async function buildSite(opts, views) {
|
|
|
2400
2401
|
// 「記憶 v2 の案件か」は 1 本の栓 `memory_v2_writer` の値で決める(cortex-engine #1094 鎖 3 V10b)。橋の間は写しとの OR
|
|
2401
2402
|
const memoryHands = await (0, hands_1.readMemoryHands)(repoRoot, ledgerDir);
|
|
2402
2403
|
const memoryV2 = (0, hands_1.isMemoryV2)(opts.memoryV2Writer, memoryHands !== null);
|
|
2404
|
+
// 関係者の「つながっている ID」の出どころ(cortex-engine #1911 便 B4b)。`--view-dir` の中の平文を読むだけ。無い・読めない案件は何も付かない(今と 1 バイトも同じ)
|
|
2405
|
+
const memberOrigins = opts.viewDir ? await (0, member_origin_1.readMemberOrigins)(path.resolve(opts.viewDir)) : null;
|
|
2403
2406
|
// 記録の id → リポジトリルート相対のパス(順位のファイルの鍵と突き合わせる。`repoRoot` 起点・cwd 起点にしない)
|
|
2404
2407
|
const recordRelPaths = new Map();
|
|
2405
2408
|
const relOf = (dir, fileName) => {
|
|
@@ -2463,6 +2466,15 @@ async function buildSite(opts, views) {
|
|
|
2463
2466
|
// 旧い取り下げが普通に見えないよう、`retracted_by` を 1 本以上持つルールは一覧(と件数・つながり)から外す
|
|
2464
2467
|
if (e.name === "Rules")
|
|
2465
2468
|
records = records.filter((r) => !r.relations.some((x) => x.rel === "retracted_by"));
|
|
2469
|
+
// つながっている ID の出どころ(関係者だけ・`fm.identities` と同じ並びの別の表)
|
|
2470
|
+
if (memberOrigins)
|
|
2471
|
+
for (const r of records) {
|
|
2472
|
+
if (!(0, external_id_1.isMemberRecord)(r.rtype, r.fm.id, e.name))
|
|
2473
|
+
continue;
|
|
2474
|
+
const o = (0, member_origin_1.originsOfMember)(r.fm, String(r.fm.id ?? r.id), memberOrigins);
|
|
2475
|
+
if (o)
|
|
2476
|
+
r.idOrigins = o;
|
|
2477
|
+
}
|
|
2466
2478
|
// 関連の資料(cortex-engine #1094 F8)。ルール・用語だけ・欄の無い記録には何も付かない
|
|
2467
2479
|
attachRelatedMaterials(e.name, records, refs);
|
|
2468
2480
|
// 根拠(references)。ルール・用語・関係者を決定と同じリンクにする(用語・関係者の移行前の source は先頭の 1 本)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ORIGIN_WORDS = exports.MEMBER_ORIGIN_FILE = void 0;
|
|
7
|
+
exports.identityKeyOf = identityKeyOf;
|
|
8
|
+
exports.parseMemberOrigins = parseMemberOrigins;
|
|
9
|
+
exports.readMemberOrigins = readMemberOrigins;
|
|
10
|
+
exports.originsOfMember = originsOfMember;
|
|
11
|
+
/**
|
|
12
|
+
* 関係者の「つながっている ID」の出どころ(cortex-engine #1911 便 B4b・仕様 `member.identity-origin`)。
|
|
13
|
+
*
|
|
14
|
+
* engine の記憶側の晩の終わりの段 `origin`(`scripts/memory-v2/memory/post.mjs` の `stageMemberOrigin`)が、各 ID 行が「どうやって分かったか」を
|
|
15
|
+
* 保管庫の `gold/v1/c/view/member-identity-origin.json` に置く。`--view-dir` の作業場(`view-dir.ts`)に取り出された同じ名前のファイルを、ここは**読むだけ**。
|
|
16
|
+
* 形は `{ "v": 1, "members": { "<関係者の id>": { "<主張の鍵 pc:…>": "self|peer|exact|name|ai" } } }`(封筒・目次なし。名簿の要約と同じ平文)。
|
|
17
|
+
*
|
|
18
|
+
* ## 🔴 約束
|
|
19
|
+
*
|
|
20
|
+
* - **記録の `fm.identities[]` の要素には足さない。** 画面の編集の書き戻しで機械の行が人の行に固まらないよう、記録ごとに**並びが `fm.identities` と同じ別の表**
|
|
21
|
+
* (`idOrigins`・出どころの語か `null`)で持つ。画面は `idOrigins[i]` を `fm.identities[i]` の出どころとして読む
|
|
22
|
+
* - **ブラウザ側にハッシュを持ち込まない。** 鍵(`pc:<種類>:<sha256 先頭 32>`)の突き合わせはビルドでだけ行う
|
|
23
|
+
* - **読めない値は昇格させない。** ファイルが無い・壊れている・版が違う・語彙の外の値・鍵の形が違う行は無かったことにする(その行は空欄のまま)
|
|
24
|
+
* - 鍵の計算は engine の `identityKeyOf`(`scripts/memory-v2/spine/person-keys.mjs`)と**完全に同じ**: 小文字に畳むのはメールだけ(GitHub の login は畳まない)、
|
|
25
|
+
* 値は前後の空白を落とす。同じ固定ケース表(`test/fixtures/identity-origin-keys.json`)を `viewer/test/member-origin.test.mjs` が検査する
|
|
26
|
+
*/
|
|
27
|
+
const node_crypto_1 = require("node:crypto");
|
|
28
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
29
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
30
|
+
const external_id_1 = require("./external-id");
|
|
31
|
+
/** `--view-dir` の中の置き場(engine `ORIGIN_VIEW_NAME` + `.json`) */
|
|
32
|
+
exports.MEMBER_ORIGIN_FILE = "member-identity-origin.json";
|
|
33
|
+
/** 出どころの語彙(engine `ORIGIN_WORDS`・強い順) */
|
|
34
|
+
exports.ORIGIN_WORDS = ["self", "peer", "exact", "name", "ai"];
|
|
35
|
+
/** 主張の ID の種類(engine `PERSON_ID_KINDS`) */
|
|
36
|
+
const ID_KINDS = ["email", "git_author", "github_login", "slack_user", "google_chat_user", "figma_user", "backlog_user"];
|
|
37
|
+
const KEY_RE = /^pc:[a-z_]+:[0-9a-f]{32}$/;
|
|
38
|
+
const isWord = (w) => typeof w === "string" && exports.ORIGIN_WORDS.includes(w);
|
|
39
|
+
/** 主張の鍵(engine `identityKeyOf` と同じ)。形が違えば `null` */
|
|
40
|
+
function identityKeyOf(entry) {
|
|
41
|
+
const raw = entry !== null && typeof entry === "object" && !Array.isArray(entry) ? entry.id : entry;
|
|
42
|
+
const s = String(raw ?? "");
|
|
43
|
+
const i = s.indexOf(":");
|
|
44
|
+
if (i <= 0)
|
|
45
|
+
return null;
|
|
46
|
+
const kind = s.slice(0, i);
|
|
47
|
+
const id = s.slice(i + 1).trim();
|
|
48
|
+
if (!ID_KINDS.includes(kind) || !id)
|
|
49
|
+
return null;
|
|
50
|
+
let v = id;
|
|
51
|
+
if (kind === "email") {
|
|
52
|
+
v = id.toLowerCase();
|
|
53
|
+
if (!/^[^@\s]+@[^@\s]+$/.test(v))
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return `pc:${kind}:${(0, node_crypto_1.createHash)("sha256").update(v, "utf8").digest("hex").slice(0, 32)}`;
|
|
57
|
+
}
|
|
58
|
+
/** 中身を読む(投げない)。形が違う・版が違う → `null`。語彙の外の値・鍵の形が違う行は捨てる */
|
|
59
|
+
function parseMemberOrigins(text) {
|
|
60
|
+
let data;
|
|
61
|
+
try {
|
|
62
|
+
data = JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
68
|
+
return null;
|
|
69
|
+
const o = data;
|
|
70
|
+
if (o.v !== 1 || !o.members || typeof o.members !== "object" || Array.isArray(o.members))
|
|
71
|
+
return null;
|
|
72
|
+
const out = new Map();
|
|
73
|
+
for (const [id, keys] of Object.entries(o.members)) {
|
|
74
|
+
if (!keys || typeof keys !== "object" || Array.isArray(keys))
|
|
75
|
+
continue;
|
|
76
|
+
const m = new Map();
|
|
77
|
+
for (const [k, w] of Object.entries(keys))
|
|
78
|
+
if (KEY_RE.test(k) && isWord(w))
|
|
79
|
+
m.set(k, w);
|
|
80
|
+
if (m.size)
|
|
81
|
+
out.set(id, m);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/** `--view-dir` から読む。無い → `null`(黙る)・壊れている → `null`(build のログに 1 行) */
|
|
86
|
+
async function readMemberOrigins(viewDir) {
|
|
87
|
+
let text;
|
|
88
|
+
try {
|
|
89
|
+
text = await promises_1.default.readFile(node_path_1.default.join(viewDir, exports.MEMBER_ORIGIN_FILE), "utf8");
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const parsed = parseMemberOrigins(text);
|
|
95
|
+
if (!parsed)
|
|
96
|
+
console.warn(`${exports.MEMBER_ORIGIN_FILE} を読めませんでした(つながっている ID の「どうやって分かったか」は空欄で出します)`);
|
|
97
|
+
return parsed;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 関係者 1 人の `fm.identities` と同じ並びの出どころの表。1 行も当たらなければ `undefined`(キーごと載せない=出どころの無い案件は今と 1 バイトも同じ)。
|
|
101
|
+
* `fm.identities` の要素が文字列でも `{ id }` でも鍵は同じ(engine と同じ)
|
|
102
|
+
*/
|
|
103
|
+
function originsOfMember(fm, memberId, origins) {
|
|
104
|
+
if (!origins)
|
|
105
|
+
return undefined;
|
|
106
|
+
const mine = origins.get(memberId);
|
|
107
|
+
const list = fm[external_id_1.FM_LINKED_IDS];
|
|
108
|
+
if (!mine || !Array.isArray(list))
|
|
109
|
+
return undefined;
|
|
110
|
+
const out = list.map((e) => {
|
|
111
|
+
const k = identityKeyOf(e);
|
|
112
|
+
return k ? mine.get(k) ?? null : null;
|
|
113
|
+
});
|
|
114
|
+
return out.some((w) => w !== null) ? out : undefined;
|
|
115
|
+
}
|
package/dist/render.js
CHANGED
|
@@ -19366,7 +19366,9 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19366
19366
|
// 名前一致/AI が判定。レコード情報のパネルも同じ表を引く(2 度書かない)
|
|
19367
19367
|
var LI_BASIS = {
|
|
19368
19368
|
verified: { text: "ポータルで確定", tone: "is-firm" },
|
|
19369
|
-
|
|
19369
|
+
// 🔴 「本人が申告」は画面に出さない(cortex-engine #1911 便 B4b・リーダーの答え 6238278773 の 3)。旧い declared の行は人が書いた行と機械の行が
|
|
19370
|
+
// 混ざっていて、本人が申告した証拠ではない。本人の確定は出どころ(LI_ORIGIN の self)だけで言う。declared は空欄(読めない値と同じく何も言わない)
|
|
19371
|
+
declared: { text: "", tone: "is-plain" },
|
|
19370
19372
|
exact: { text: "メール一致", tone: "is-firm" },
|
|
19371
19373
|
name_match: { text: "名前一致", tone: "is-machine" },
|
|
19372
19374
|
name_only: { text: "名前一致", tone: "is-machine" },
|
|
@@ -19379,6 +19381,15 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19379
19381
|
var LI_BASIS_UNREADABLE = "(根拠を読み取れません)";
|
|
19380
19382
|
// basis が無い行(出どころを言わない)。空欄で出す(「本人が申告」に倒さない)
|
|
19381
19383
|
var LI_BASIS_NONE = { text: "", tone: "is-plain" };
|
|
19384
|
+
// 出どころ(cortex-engine #1911 便 B4b・仕様 member.portal-id-list の( )の言い方)。ビルドが記録ごとに持つ表 idOrigins[i](fm.identities と同じ並び)の語。
|
|
19385
|
+
// 🔴 語彙の外の値は無かったことにする(読めない値を昇格させない)。ハッシュ・鍵の突き合わせはビルドでだけ行う
|
|
19386
|
+
var LI_ORIGIN = {
|
|
19387
|
+
self: { text: "本人が確定", tone: "is-firm" },
|
|
19388
|
+
peer: { text: "周りの人が確定", tone: "is-firm" },
|
|
19389
|
+
exact: { text: "同じメール・同じコミット", tone: "is-firm" },
|
|
19390
|
+
name: { text: "名前の一致", tone: "is-machine" },
|
|
19391
|
+
ai: { text: "AI が材料から結んだ", tone: "is-machine" },
|
|
19392
|
+
};
|
|
19382
19393
|
// 🔴 **5 列目の「場」(scope)は #704 で足した。** engine は綴りだけ予約していて、いまは
|
|
19383
19394
|
// 人が手で書いたときだけ入る。無い行は素の空(「いつから」と同じ——空が正常)
|
|
19384
19395
|
var LI_COLS = ["道具", "この人のページ", "どうやって分かったか", "いつから", "場"];
|
|
@@ -19425,6 +19436,19 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19425
19436
|
* 🔴 **語彙の外はそのまま出して、中立の色に置く**——知らない根拠を、確かな側にも
|
|
19426
19437
|
* 機械の側にも倒さない
|
|
19427
19438
|
*/
|
|
19439
|
+
/** 出どころの語 → 画面の言葉と色。語彙の外・無い → null(呼び手は basis に戻る) */
|
|
19440
|
+
function liOrigin(word) {
|
|
19441
|
+
return typeof word === "string" && Object.prototype.hasOwnProperty.call(LI_ORIGIN, word) ? LI_ORIGIN[word] : null;
|
|
19442
|
+
}
|
|
19443
|
+
/**
|
|
19444
|
+
* この行の「どうやって分かったか」。出どころの表がある記録(記憶 v2 の案件)は 5 つの言い方だけで、表に無い行は空欄
|
|
19445
|
+
* (取り下げた行・結び先が別の人の行に、旧い basis の言い方を出さない)。表が無い記録(出どころを渡されない案件)だけ、今までの basis の言い方
|
|
19446
|
+
*/
|
|
19447
|
+
function liWhy(rec, index, ent) {
|
|
19448
|
+
var origins = rec && Array.isArray(rec.idOrigins) ? rec.idOrigins : null;
|
|
19449
|
+
if (origins) return liOrigin(origins[index]) || LI_BASIS_NONE;
|
|
19450
|
+
return liBasis(ent ? ent.basis : null);
|
|
19451
|
+
}
|
|
19428
19452
|
function liBasis(raw) {
|
|
19429
19453
|
// 🔴 **declared(いちばん強い段)に倒してよいのは「欄が無い/空」のときだけ。**
|
|
19430
19454
|
// 以前は typeof raw === "string" でない値を一律に空扱いしていたので、
|
|
@@ -19507,6 +19531,43 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19507
19531
|
}
|
|
19508
19532
|
return host;
|
|
19509
19533
|
}
|
|
19534
|
+
/**
|
|
19535
|
+
* Slack の行の「場」の列に出す言い方(リンクの行き先の判断には使わない・表示だけ)。
|
|
19536
|
+
*
|
|
19537
|
+
* 🔴 **内部の値(team_id・組織のドメインなど)を画面に出さない。**
|
|
19538
|
+
* 1. 場が接続済みワークスペースの team_id かドメインと一致し、操作画面で登録した名前(name)があれば、その名前
|
|
19539
|
+
* 2. 名前が引けないときは、登録した Slack チャンネルの URL のホスト(xxx.slack.com の形)をそのまま。
|
|
19540
|
+
* 場と結べる登録(workspace か URL のホストの頭が場)のホストが 1 つに決まるときだけ
|
|
19541
|
+
* 3. どちらも引けなければ場の値のまま(推し量らない)
|
|
19542
|
+
*/
|
|
19543
|
+
function liScopeLabel(kind, scope) {
|
|
19544
|
+
if (kind !== "slack_user" || scope === "") return scope;
|
|
19545
|
+
var want = scope.toLowerCase();
|
|
19546
|
+
var list = slackWorkspaces() || [];
|
|
19547
|
+
var hit = null;
|
|
19548
|
+
for (var i = 0; i < list.length; i++) {
|
|
19549
|
+
var w = list[i];
|
|
19550
|
+
if (!w) continue;
|
|
19551
|
+
if (w.teamId === scope || (typeof w.domain === "string" && w.domain.toLowerCase() === want)) { hit = w; break; }
|
|
19552
|
+
}
|
|
19553
|
+
var named = hit && typeof hit.name === "string" ? hit.name.trim() : "";
|
|
19554
|
+
// 名前が引けなかったワークスペースは name に team_id が入る(空にはしない)=名前ではない
|
|
19555
|
+
if (named !== "" && named !== hit.teamId && named.toLowerCase() !== want && named.toLowerCase() !== String(hit.domain || "").toLowerCase()) return named;
|
|
19556
|
+
var srcs = (fleetVal("externalSources") || []).filter(function (x) {
|
|
19557
|
+
return x && x.type === "slack" && typeof x.url === "string";
|
|
19558
|
+
});
|
|
19559
|
+
var host = "";
|
|
19560
|
+
for (var j = 0; j < srcs.length; j++) {
|
|
19561
|
+
var m = LI_SLACK_CHANNEL_HOST.exec(srcs[j].url);
|
|
19562
|
+
if (!m) continue;
|
|
19563
|
+
var h = m[1].toLowerCase() + ".slack.com";
|
|
19564
|
+
var tied = srcs[j].workspace === scope || (hit && srcs[j].workspace === hit.teamId) || m[1].toLowerCase() === want;
|
|
19565
|
+
if (!tied) continue;
|
|
19566
|
+
if (host && host !== h) return scope;
|
|
19567
|
+
host = h;
|
|
19568
|
+
}
|
|
19569
|
+
return host || scope;
|
|
19570
|
+
}
|
|
19510
19571
|
/**
|
|
19511
19572
|
* Backlog のユーザーのページを組むスペースのドメイン(決められなければ空文字)。
|
|
19512
19573
|
*
|
|
@@ -19576,7 +19637,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19576
19637
|
var list = fm ? fm[FM_LINKED_IDS] : null;
|
|
19577
19638
|
if (!Array.isArray(list)) return [];
|
|
19578
19639
|
var out = [];
|
|
19579
|
-
list.forEach(function (ent) {
|
|
19640
|
+
list.forEach(function (ent, index) {
|
|
19580
19641
|
if (!ent || typeof ent !== "object" || Array.isArray(ent)) return;
|
|
19581
19642
|
if (typeof ent.id !== "string") return;
|
|
19582
19643
|
var whole = ent.id.trim();
|
|
@@ -19584,7 +19645,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19584
19645
|
var typed = LI_TYPED_ID.exec(whole);
|
|
19585
19646
|
var kind = typed ? typed[1] : "";
|
|
19586
19647
|
var value = typed ? typed[2] : whole;
|
|
19587
|
-
var basis =
|
|
19648
|
+
var basis = liWhy(rec, index, ent);
|
|
19588
19649
|
// 場は文字列のときだけ(数やオブジェクトを String() して出さない)
|
|
19589
19650
|
var scope = typeof ent.scope === "string" ? ent.scope.trim() : "";
|
|
19590
19651
|
out.push({
|
|
@@ -19595,7 +19656,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19595
19656
|
tone: basis.tone,
|
|
19596
19657
|
// 🔴 since が無い行の「いつから」は素の空。印(ダッシュ等)を足さない——空が正常
|
|
19597
19658
|
since: typeof ent.since === "string" ? ent.since : "",
|
|
19598
|
-
scope: scope,
|
|
19659
|
+
scope: liScopeLabel(kind, scope),
|
|
19599
19660
|
});
|
|
19600
19661
|
});
|
|
19601
19662
|
return out;
|
|
@@ -19858,7 +19919,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
19858
19919
|
if (linked) app.appendChild(linked);
|
|
19859
19920
|
var history = changeLogSection(rec);
|
|
19860
19921
|
if (history) app.appendChild(history);
|
|
19861
|
-
var fmp = renderFmPanel(rec.fm);
|
|
19922
|
+
var fmp = renderFmPanel(rec.fm, rec.idOrigins);
|
|
19862
19923
|
if (fmp) app.appendChild(fmp);
|
|
19863
19924
|
appendDeleteFoot(rec);
|
|
19864
19925
|
// 開き直しても削除済みのまま見せる(印は rec が持っている)
|
|
@@ -21379,7 +21440,7 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
21379
21440
|
}
|
|
21380
21441
|
return String(x);
|
|
21381
21442
|
}
|
|
21382
|
-
function renderFmPanel(fm) {
|
|
21443
|
+
function renderFmPanel(fm, idOrigins) {
|
|
21383
21444
|
if (!fm || !Object.keys(fm).length) return null;
|
|
21384
21445
|
var panel = el("details", { class: "fm-panel" });
|
|
21385
21446
|
panel.appendChild(el("summary", { class: "fm-title", text: "レコード情報(frontmatter)" }));
|
|
@@ -21415,10 +21476,10 @@ ${story_1.splitByNameLinks.toString()}
|
|
|
21415
21476
|
// 🔴 **欄自体は隠さない**(frontmatter のパネルは今までどおり全部そのまま出す)。
|
|
21416
21477
|
// ただしこの欄はオブジェクトの配列なので、下の join に落とすと [object Object] になる
|
|
21417
21478
|
// ——1行1件で id と根拠の日本語を出す(上の「つながっている ID」と同じ言葉で)
|
|
21418
|
-
v.forEach(function (ent) {
|
|
21479
|
+
v.forEach(function (ent, index) {
|
|
21419
21480
|
var line = el("div", { class: "fm-li" });
|
|
21420
21481
|
if (ent && typeof ent === "object" && !Array.isArray(ent) && typeof ent.id === "string") {
|
|
21421
|
-
var basisText =
|
|
21482
|
+
var basisText = liWhy({ idOrigins: idOrigins }, index, ent).text;
|
|
21422
21483
|
line.textContent = basisText ? ent.id + "(" + basisText + ")" : ent.id;
|
|
21423
21484
|
} else {
|
|
21424
21485
|
// 形が外れている要素も消さずに読める形で残す(文字列で書かれた欄を含む)
|
package/package.json
CHANGED