@takagaki/cortex-decisions-viewer 0.4.53 → 0.4.55
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 +142 -43
- package/dist/render.js +30 -10
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.readFleetSources = readFleetSources;
|
|
40
40
|
exports.build = build;
|
|
41
|
+
exports.createRefResolver = createRefResolver;
|
|
41
42
|
const node_fs_1 = require("node:fs");
|
|
42
43
|
const path = __importStar(require("node:path"));
|
|
43
44
|
const gray_matter_1 = __importDefault(require("gray-matter"));
|
|
@@ -172,6 +173,13 @@ async function readFleetSources(repoRoot) {
|
|
|
172
173
|
driveFolderIds: Array.isArray(s.driveFolderIds)
|
|
173
174
|
? s.driveFolderIds.map((x) => String(x)).filter(Boolean)
|
|
174
175
|
: undefined,
|
|
176
|
+
// フォルダの表示名(ID→名前)。**ここに列挙し忘れると、型エラーも例外も出さずに
|
|
177
|
+
// 静かに消える**(origin / figmaFiles / driveFolderIds で3回踏んでいる)。
|
|
178
|
+
driveFolderNames: s.driveFolderNames != null && typeof s.driveFolderNames === "object" && !Array.isArray(s.driveFolderNames)
|
|
179
|
+
? Object.fromEntries(Object.entries(s.driveFolderNames)
|
|
180
|
+
.map(([k, v]) => [k, String(v ?? "").trim()])
|
|
181
|
+
.filter(([, v]) => v))
|
|
182
|
+
: undefined,
|
|
175
183
|
// 登録済みFigmaファイルの一覧。**1件ずつ外せるようにするために要る**
|
|
176
184
|
// (先頭1件のURLしか無かった頃は、6ファイル持つ案件でどれも選べなかった)。
|
|
177
185
|
figmaFiles: Array.isArray(s.figmaFiles)
|
|
@@ -344,10 +352,57 @@ async function walkMd(dirAbs) {
|
|
|
344
352
|
* - 課題キー(例: PJ_CORTEX-13)→ 同期mdに記載された Backlog Issue Link(実Backlog URL)
|
|
345
353
|
* - minute:{定例名}:{YYYYMMDD} → 議事録ファイルのGitHub blobリンク
|
|
346
354
|
*/
|
|
355
|
+
/**
|
|
356
|
+
* マーカーファイルの在り処からディレクトリ名を求める(`cortex-engine/scripts/fleet-status.mjs`
|
|
357
|
+
* の `findDirByMarker` と同じ考え方)。
|
|
358
|
+
*
|
|
359
|
+
* **ディレクトリ名をハードコードしない。** `customize-tooling` で改名している案件が実在する
|
|
360
|
+
* (ある案件は `課題管理/`→`Backlog/`・`会議/`→`MTG/`・`デザイン/`→`Figma/`)。
|
|
361
|
+
* 決め打ちだと、その案件では課題キーもFigmaも**永久に解決できない**。
|
|
362
|
+
*/
|
|
363
|
+
async function findDirByMarker(repoRoot, marker, fallback) {
|
|
364
|
+
try {
|
|
365
|
+
for (const d of await node_fs_1.promises.readdir(repoRoot, { withFileTypes: true })) {
|
|
366
|
+
if (!d.isDirectory() || d.name === "node_modules" || d.name.startsWith("."))
|
|
367
|
+
continue;
|
|
368
|
+
try {
|
|
369
|
+
await node_fs_1.promises.stat(path.join(repoRoot, d.name, marker));
|
|
370
|
+
return d.name;
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// 直下に無ければ1階層下も見る(backlog-settings.json は issues/ の下に置かれる)
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
for (const sub of await node_fs_1.promises.readdir(path.join(repoRoot, d.name), { withFileTypes: true })) {
|
|
377
|
+
if (!sub.isDirectory())
|
|
378
|
+
continue;
|
|
379
|
+
try {
|
|
380
|
+
await node_fs_1.promises.stat(path.join(repoRoot, d.name, sub.name, marker));
|
|
381
|
+
return d.name;
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
// 次へ
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
// 読めないディレクトリは飛ばす
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
// repoRoot が読めなければ既定名で進む
|
|
395
|
+
}
|
|
396
|
+
return fallback;
|
|
397
|
+
}
|
|
347
398
|
async function buildTargetUrlMap(repoRoot, repoBaseUrl, branch) {
|
|
348
399
|
const map = new Map();
|
|
349
|
-
|
|
350
|
-
|
|
400
|
+
const issuesDir = await findDirByMarker(repoRoot, "backlog-settings.json", "課題管理");
|
|
401
|
+
const designDir = await findDirByMarker(repoRoot, "figma.json", "デザイン");
|
|
402
|
+
const meetingDir = await findDirByMarker(repoRoot, "ingest-config.json", "会議");
|
|
403
|
+
const materialsDir = await findDirByMarker(repoRoot, "materials-config.json", "共有資料");
|
|
404
|
+
// 課題キー → Backlog URL({課題管理}/issues/ の同期mdの基本情報から抽出)
|
|
405
|
+
for (const file of await walkMd(path.join(repoRoot, issuesDir, "issues"))) {
|
|
351
406
|
try {
|
|
352
407
|
const head = (await node_fs_1.promises.readFile(file, "utf8")).slice(0, 4000);
|
|
353
408
|
const keyMatch = head.match(/^- 課題キー: (\S+)$/m);
|
|
@@ -360,7 +415,7 @@ async function buildTargetUrlMap(repoRoot, repoBaseUrl, branch) {
|
|
|
360
415
|
}
|
|
361
416
|
}
|
|
362
417
|
// material:{slug} → 共有資料の正本(Drive等のsource URL)。無ければ変換済みmdのGitHubリンク
|
|
363
|
-
for (const file of await walkMd(path.join(repoRoot,
|
|
418
|
+
for (const file of await walkMd(path.join(repoRoot, materialsDir))) {
|
|
364
419
|
if (path.basename(file).toLowerCase() === "link.md")
|
|
365
420
|
continue;
|
|
366
421
|
try {
|
|
@@ -390,22 +445,32 @@ async function buildTargetUrlMap(repoRoot, repoBaseUrl, branch) {
|
|
|
390
445
|
// 読めないファイルはスキップ
|
|
391
446
|
}
|
|
392
447
|
}
|
|
393
|
-
// design:{fileKey}:{nodeId} → Figma
|
|
394
|
-
|
|
448
|
+
// design:{fileKey}:{nodeId} → Figmaディープリンク
|
|
449
|
+
//
|
|
450
|
+
// **frontmatter からは取れない。** インベントリは frontmatter を持たない
|
|
451
|
+
// (frontmatter を持つのは Gold層だけ=オントロジー規約)。参照IDもFigmaのURLも**本文**にある:
|
|
452
|
+
// - 参照ID: `design:xxx:103:1836`
|
|
453
|
+
// - [Figmaで開く](https://www.figma.com/design/...)
|
|
454
|
+
// 以前は `data.type === "design"` を見ていたので、この解決は**一度も効いていなかった**。
|
|
455
|
+
for (const file of await walkMd(path.join(repoRoot, designDir, "inventory"))) {
|
|
395
456
|
try {
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
457
|
+
const body = await node_fs_1.promises.readFile(file, "utf8");
|
|
458
|
+
const id = body.match(/^- 参照ID: `(design:[^`]+)`\s*$/m);
|
|
459
|
+
const url = body.match(/^- \[Figmaで開く\]\((https?:[^)]+)\)\s*$/m);
|
|
460
|
+
if (id && url)
|
|
461
|
+
map.set(id[1], url[1]);
|
|
400
462
|
}
|
|
401
463
|
catch {
|
|
402
464
|
// 読めないファイルはスキップ
|
|
403
465
|
}
|
|
404
466
|
}
|
|
405
467
|
// minute:{定例名}:{YYYYMMDD} → 議事録ファイルのGitHubリンク
|
|
406
|
-
// パス規約:
|
|
468
|
+
// パス規約: {会議}/{フェーズ}/{定例名}/{YYYYMMDD}/*minutes*.md
|
|
469
|
+
//
|
|
470
|
+
// **以前は存在しない `ミーティング/` を見ていた**(艦隊は全案件 `会議/`。改名案件は `MTG/`)。
|
|
471
|
+
// そのため minute: の解決は一度も効いていなかった。
|
|
407
472
|
if (repoBaseUrl) {
|
|
408
|
-
for (const file of await walkMd(path.join(repoRoot,
|
|
473
|
+
for (const file of await walkMd(path.join(repoRoot, meetingDir))) {
|
|
409
474
|
const name = path.basename(file).toLowerCase();
|
|
410
475
|
if (!name.includes("minutes"))
|
|
411
476
|
continue;
|
|
@@ -560,7 +625,7 @@ async function scanContent(repoRoot, goldDirName, repoBaseUrl, branch) {
|
|
|
560
625
|
* 参照先がリポジトリ内のレコードでないIDは、層を推定したファントムノードとして表示する
|
|
561
626
|
* (minute:/material:→Silver、課題キー・外部URL→Bronze、Goldの未解決ID→phantom)。
|
|
562
627
|
*/
|
|
563
|
-
function buildGraph(decisions, home, sections, content, targetUrls, repoBaseUrl, branch) {
|
|
628
|
+
function buildGraph(decisions, home, sections, content, targetUrls, repoBaseUrl, branch, refs) {
|
|
564
629
|
const nodes = new Map();
|
|
565
630
|
const edges = [];
|
|
566
631
|
const addNode = (node) => {
|
|
@@ -620,7 +685,12 @@ function buildGraph(decisions, home, sections, content, targetUrls, repoBaseUrl,
|
|
|
620
685
|
else if (target.includes("/") || target.toLowerCase().endsWith(".md")) {
|
|
621
686
|
group = "silver"; // リポジトリ内ファイルへのパス参照 → GitHubのコピーへ飛べるようにする
|
|
622
687
|
label = target.split("/").pop() ?? target;
|
|
623
|
-
|
|
688
|
+
// **実在するものだけリンクにする。** 詳細画面(resolveReference)と同じ判定を使う。
|
|
689
|
+
// ここだけ無条件に blob URL を作っていたため、同じ参照が「詳細ではテキスト・
|
|
690
|
+
// グラフでは404リンク」に分かれていた(グラフのノードは window.open される)。
|
|
691
|
+
const blobUrl = repoBaseUrl && refs.exists(target)
|
|
692
|
+
? `${repoBaseUrl}/blob/${branch}/${encodePath(target)}`
|
|
693
|
+
: undefined;
|
|
624
694
|
return addNode({ id: target, label, group, url: blobUrl });
|
|
625
695
|
}
|
|
626
696
|
return addNode({ id: target, label, group });
|
|
@@ -941,15 +1011,23 @@ async function build(opts) {
|
|
|
941
1011
|
// (Decisionsディレクトリで実行すると `-- Cortex/` に一致せず常に空になる)。
|
|
942
1012
|
// 浅いclone・git無しの環境では空集合が返り、「最新」ラベルが出ないだけで他の表示は壊れない。
|
|
943
1013
|
LATEST_PATHS = (0, repo_1.latestGeneratedPaths)(process.cwd());
|
|
1014
|
+
// Cortex構成なら、ホームと「records/ を持つディレクトリ」をすべて読み込む(旧構成では空)
|
|
1015
|
+
// ディレクトリ名はハードコードしない: 案件が新しいレコード種別のディレクトリを足せば、そのままタブとして表示される
|
|
1016
|
+
const knowledgeRoot = resolveKnowledgeRoot(dirAbs);
|
|
1017
|
+
// **参照の解決器は decisions のパースより前に作る。** parseDecision が references を
|
|
1018
|
+
// 解決するので、あとから作ると空の表・空の実在判定が渡り、**実在するパスまで
|
|
1019
|
+
// 「リンクにしない」に倒れて全滅する**(以前は順序が逆だった)。
|
|
1020
|
+
const repoRoot = knowledgeRoot ? path.dirname(knowledgeRoot) : path.dirname(dirAbs);
|
|
1021
|
+
const targetUrls = knowledgeRoot
|
|
1022
|
+
? await buildTargetUrlMap(repoRoot, repo.baseUrl, repo.branch)
|
|
1023
|
+
: new Map();
|
|
1024
|
+
const refs = createRefResolver({ repoRoot, targetUrls, repoBaseUrl: repo.baseUrl, branch: repo.branch });
|
|
944
1025
|
const decisions = [];
|
|
945
1026
|
for (const fileName of mdFiles) {
|
|
946
1027
|
const raw = await node_fs_1.promises.readFile(path.join(dirAbs, fileName), "utf8");
|
|
947
|
-
const rec = await parseDecision(raw, fileName, repoRelDir, repo.baseUrl, repo.branch);
|
|
1028
|
+
const rec = await parseDecision(raw, fileName, repoRelDir, repo.baseUrl, repo.branch, refs);
|
|
948
1029
|
decisions.push(rec);
|
|
949
1030
|
}
|
|
950
|
-
// Cortex構成なら、ホームと「records/ を持つディレクトリ」をすべて読み込む(旧構成では空)
|
|
951
|
-
// ディレクトリ名はハードコードしない: 案件が新しいレコード種別のディレクトリを足せば、そのままタブとして表示される
|
|
952
|
-
const knowledgeRoot = resolveKnowledgeRoot(dirAbs);
|
|
953
1031
|
let home = null;
|
|
954
1032
|
const sections = [];
|
|
955
1033
|
if (knowledgeRoot) {
|
|
@@ -982,12 +1060,8 @@ async function build(opts) {
|
|
|
982
1060
|
const categories = uniqueSorted(decisions.map((d) => d.category).filter(Boolean));
|
|
983
1061
|
// 期間フィルタ用の月(YYYY-MM)を日付から導出し、降順(新しい順)にする
|
|
984
1062
|
const months = uniqueSorted(decisions.map((d) => d.date.slice(0, 7)).filter((m) => /^\d{4}-\d{2}$/.test(m))).reverse();
|
|
985
|
-
const repoRoot = knowledgeRoot ? path.dirname(knowledgeRoot) : path.dirname(dirAbs);
|
|
986
|
-
const targetUrls = knowledgeRoot
|
|
987
|
-
? await buildTargetUrlMap(repoRoot, repo.baseUrl, repo.branch)
|
|
988
|
-
: new Map();
|
|
989
1063
|
const contentEntries = await scanContent(repoRoot, knowledgeRoot ? path.basename(knowledgeRoot) : null, repo.baseUrl, repo.branch);
|
|
990
|
-
const graph = buildGraph(decisions, home, sections, contentEntries, targetUrls, repo.baseUrl, repo.branch);
|
|
1064
|
+
const graph = buildGraph(decisions, home, sections, contentEntries, targetUrls, repo.baseUrl, repo.branch, refs);
|
|
991
1065
|
computeLayout(graph); // 各ノードに x/y を書き込む(クライアントはこれを初期位置に使いウォームアップ省略)
|
|
992
1066
|
// データソースと自動化(fleet-status.json 由来)。無ければ undefined でセクション非表示。
|
|
993
1067
|
const fleet = await readFleetSources(repoRoot);
|
|
@@ -1048,7 +1122,7 @@ async function build(opts) {
|
|
|
1048
1122
|
const sectionCount = sections.reduce((n, s) => n + s.records.length, 0);
|
|
1049
1123
|
return { count: decisions.length + (home ? 1 : 0) + sectionCount, outDir: outAbs };
|
|
1050
1124
|
}
|
|
1051
|
-
async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch) {
|
|
1125
|
+
async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch, refs) {
|
|
1052
1126
|
const { data, content } = (0, gray_matter_1.default)(raw);
|
|
1053
1127
|
const id = String(data.id ?? fileName.replace(/\.md$/i, ""));
|
|
1054
1128
|
const title = String(data.title ?? stripIdPrefix(fileName));
|
|
@@ -1058,7 +1132,7 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch) {
|
|
|
1058
1132
|
const summary = data.description != null ? String(data.description) : data.summary != null ? String(data.summary) : ""; // description優先(旧summaryは移行期互換)
|
|
1059
1133
|
const deciders = toStringArray(data.deciders);
|
|
1060
1134
|
const referencesRaw = toStringArray(data.references);
|
|
1061
|
-
const references = referencesRaw.map((r) =>
|
|
1135
|
+
const references = referencesRaw.map((r) => refs.resolve(r));
|
|
1062
1136
|
const relations = toRelations(data.relations);
|
|
1063
1137
|
// 本文先頭の H1 はタイトルと重複するため除去(詳細画面ではタイトルを別途表示する)
|
|
1064
1138
|
const bodyHtml = externalLinksToNewTab((await marked_1.marked.parse(content, { async: true })).replace(/^\s*<h1[^>]*>[\s\S]*?<\/h1>\s*/, ""));
|
|
@@ -1088,25 +1162,50 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch) {
|
|
|
1088
1162
|
fmErrors: validateFm(data),
|
|
1089
1163
|
};
|
|
1090
1164
|
}
|
|
1091
|
-
function
|
|
1092
|
-
const
|
|
1093
|
-
//
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1165
|
+
function createRefResolver(opts) {
|
|
1166
|
+
const { repoRoot, targetUrls, repoBaseUrl, branch } = opts;
|
|
1167
|
+
// **走査結果を流用しない。** scanContent は `.md` しか歩かず SCAN_SKIP_DIRS を除外するので、
|
|
1168
|
+
// 実在する `.txt`(Geminiメモ)や `tmp/` 配下のファイルを「実在しない」と誤判定して
|
|
1169
|
+
// **いま動いているリンクを消す**。参照は1リポジトリあたり数百件なので stat で十分。
|
|
1170
|
+
const seen = new Map();
|
|
1171
|
+
const exists = (relPath) => {
|
|
1172
|
+
const hit = seen.get(relPath);
|
|
1173
|
+
if (hit !== undefined)
|
|
1174
|
+
return hit;
|
|
1175
|
+
let ok = false;
|
|
1176
|
+
try {
|
|
1177
|
+
// パス要素に混ざる制御文字・絶対パス・親ディレクトリ参照は見に行かない
|
|
1178
|
+
if (relPath && !path.isAbsolute(relPath) && !relPath.split("/").includes("..")) {
|
|
1179
|
+
(0, node_fs_1.statSync)(path.join(repoRoot, relPath));
|
|
1180
|
+
ok = true;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
catch {
|
|
1184
|
+
ok = false;
|
|
1185
|
+
}
|
|
1186
|
+
seen.set(relPath, ok);
|
|
1187
|
+
return ok;
|
|
1188
|
+
};
|
|
1189
|
+
const resolve = (ref) => {
|
|
1190
|
+
const trimmed = String(ref ?? "").trim();
|
|
1191
|
+
if (!trimmed)
|
|
1192
|
+
return { label: trimmed, url: null };
|
|
1193
|
+
const mdLink = trimmed.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
|
|
1194
|
+
if (mdLink)
|
|
1195
|
+
return { label: mdLink[1], url: mdLink[2] };
|
|
1196
|
+
if (/^https?:\/\//.test(trimmed))
|
|
1197
|
+
return { label: trimmed, url: trimmed };
|
|
1198
|
+
// 正本のURL(課題キー・material:・design:・minute:)。**ここが「ツールへ飛ぶ」の本体**
|
|
1199
|
+
const known = targetUrls.get(trimmed);
|
|
1200
|
+
if (known)
|
|
1201
|
+
return { label: trimmed, url: known };
|
|
1202
|
+
// 実在するファイルだけ blob URL にする
|
|
1203
|
+
if (repoBaseUrl && exists(trimmed)) {
|
|
1204
|
+
return { label: trimmed, url: `${repoBaseUrl}/blob/${branch}/${encodePath(trimmed)}` };
|
|
1205
|
+
}
|
|
1206
|
+
return { label: trimmed, url: null };
|
|
1207
|
+
};
|
|
1208
|
+
return { resolve, exists };
|
|
1110
1209
|
}
|
|
1111
1210
|
/** dirをリポジトリルート相対(posix)に。cwd配下でなければ basename を返す。 */
|
|
1112
1211
|
function toRepoRelative(dir) {
|
package/dist/render.js
CHANGED
|
@@ -781,6 +781,12 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
781
781
|
.ds-addform .sec-input { margin-bottom: 6px; }
|
|
782
782
|
.sec-input-inline { width: auto; max-width: 220px; margin-bottom: 0; }
|
|
783
783
|
.sec-how { color: var(--muted); font-size: 12px; line-height: 1.7; margin: 0 0 10px; }
|
|
784
|
+
.sec-how p { margin: 0; }
|
|
785
|
+
/* 取り込みの条件は箇条書きで出す(ANDであることを読み飛ばせないように) */
|
|
786
|
+
.sec-how-list { margin: 4px 0; padding-left: 1.3em; }
|
|
787
|
+
.sec-how-list li { margin: 2px 0; }
|
|
788
|
+
/* 0件を黙って見せない(追加フォームだけだと「未登録」か「読めていない」か区別できない) */
|
|
789
|
+
.ds-empty { color: var(--muted); font-size: 12.5px; margin: 4px 0 10px; }
|
|
784
790
|
.sec-input { width: 100%; box-sizing: border-box; font-size: 13px; padding: 7px 10px; margin-bottom: 8px; border: 1px solid var(--border); border-radius: 6px; background: #fff; color: var(--text); }
|
|
785
791
|
.sec-input:focus { outline: none; border-color: var(--accent); }
|
|
786
792
|
.ops-card {
|
|
@@ -1694,12 +1700,18 @@ const CLIENT_JS = `
|
|
|
1694
1700
|
// (applyAddDriveFolder が enabled を立てる)ので「始める」を押す機会が無い。
|
|
1695
1701
|
// 止めたいならフォルダを外す——登録そのものが意思表示、という会議と同じ考え方。
|
|
1696
1702
|
var folders = s.driveFolderIds || [];
|
|
1703
|
+
// 名前は任意。**無ければIDを出す**(既存の案件は名前を持っていない)
|
|
1704
|
+
var fnames = s.driveFolderNames || {};
|
|
1697
1705
|
if (folders.length) {
|
|
1698
1706
|
var dlist = el("div", { class: "ds-subitems" });
|
|
1699
1707
|
folders.forEach(function (id) {
|
|
1708
|
+
var fname = fnames[id];
|
|
1700
1709
|
var row = el("div", { class: "ds-subitem" }, [
|
|
1701
|
-
dsNameEl(id, "https://drive.google.com/drive/folders/" + id),
|
|
1710
|
+
dsNameEl(fname || id, "https://drive.google.com/drive/folders/" + id),
|
|
1702
1711
|
]);
|
|
1712
|
+
// **名前を出したときはIDも併記する。** 同名のフォルダを見分けられなくなるのと、
|
|
1713
|
+
// 「このIDを外す」という確認文と画面の表示が食い違うのを防ぐ
|
|
1714
|
+
if (fname) row.appendChild(el("span", { class: "ds-sync", text: id }));
|
|
1703
1715
|
row.appendChild(removeButton({
|
|
1704
1716
|
label: "このフォルダを外す",
|
|
1705
1717
|
confirmLines: function () {
|
|
@@ -1769,16 +1781,20 @@ const CLIENT_JS = `
|
|
|
1769
1781
|
flist.appendChild(row);
|
|
1770
1782
|
});
|
|
1771
1783
|
box.appendChild(flist);
|
|
1784
|
+
} else {
|
|
1785
|
+
// **0件を黙って見せない。** 追加フォームだけだと「まだ登録していない」のか
|
|
1786
|
+
// 「読めていない」のか区別できない(Driveは driveState で理由まで出している)
|
|
1787
|
+
box.appendChild(el("p", { class: "ds-empty", text: "同期対象のFigmaファイルはまだ登録されていません。" }));
|
|
1772
1788
|
}
|
|
1773
1789
|
box.appendChild(addForm({
|
|
1774
1790
|
title: "+ Figmaファイルを追加",
|
|
1775
1791
|
placeholder: "https://www.figma.com/design/…",
|
|
1776
1792
|
namePlaceholder: "メモ(任意)",
|
|
1777
1793
|
requiredNote: "FigmaのURLを入力してください。",
|
|
1778
|
-
hint: "
|
|
1794
|
+
hint: "このファイルの画面インベントリ(画面名・Figmaへのリンク)が毎晩同期されます。画像は取り込まれません。",
|
|
1779
1795
|
confirmLines: function (v) {
|
|
1780
1796
|
return [
|
|
1781
|
-
"このFigma
|
|
1797
|
+
"このFigmaファイルの画面名とFigmaへのリンクを、顧客も見るリポジトリに取り込みます(画像は取り込みません)。",
|
|
1782
1798
|
"",
|
|
1783
1799
|
"貼ったURLがこの案件のものか、もう一度確認してください。",
|
|
1784
1800
|
"",
|
|
@@ -1805,13 +1821,17 @@ const CLIENT_JS = `
|
|
|
1805
1821
|
* ②の材料を画面が出さないと、招待したのに届かない理由が分からない。
|
|
1806
1822
|
*/
|
|
1807
1823
|
function meetingHowTo() {
|
|
1808
|
-
//
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
});
|
|
1824
|
+
// **2つの条件がANDであることを、読み飛ばせない形で出す。** 散文で並べていたときに
|
|
1825
|
+
// 「会議名は合っているのにBotを招待しておらず取り込まれなかった」事故が実際に起きた。
|
|
1826
|
+
// 会議名の条件は上の行(ds-keys)が出すので、ここでは「上の語」と参照するに留める。
|
|
1827
|
+
var wrap = el("div", { class: "sec-how" });
|
|
1828
|
+
wrap.appendChild(el("p", { text: "次の2つが揃った会議だけが取り込まれます。" }));
|
|
1829
|
+
var ul = el("ul", { class: "sec-how-list" });
|
|
1830
|
+
ul.appendChild(el("li", { text: "cortex-notetaker Bot が招待されている(定例はシリーズに1回でOK)" }));
|
|
1831
|
+
ul.appendChild(el("li", { text: "会議名に上の語のどれかが含まれている" }));
|
|
1832
|
+
wrap.appendChild(ul);
|
|
1833
|
+
wrap.appendChild(el("p", { text: "どちらか一方だけでは取り込まれません。" }));
|
|
1834
|
+
return wrap;
|
|
1815
1835
|
}
|
|
1816
1836
|
|
|
1817
1837
|
/**
|
package/package.json
CHANGED