@takagaki/cortex-decisions-viewer 0.4.18 → 0.4.20

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 CHANGED
@@ -106,6 +106,46 @@ function resolveKnowledgeRoot(decisionsDirAbs) {
106
106
  }
107
107
  return null;
108
108
  }
109
+ /**
110
+ * リポジトリルートの fleet-status.json から externalSources / pipelines を読む。
111
+ * ファイルが無い・パース不能・当該フィールドが無い場合は undefined を返し、
112
+ * ビューア側で「データソースと自動化」セクションを出さない(後方互換・旧fleet-statusで壊れない)。
113
+ */
114
+ async function readFleetSources(repoRoot) {
115
+ let json;
116
+ try {
117
+ const raw = await node_fs_1.promises.readFile(path.join(repoRoot, "fleet-status.json"), "utf8");
118
+ json = JSON.parse(raw);
119
+ }
120
+ catch {
121
+ return {}; // 無い・壊れている → セクション非表示
122
+ }
123
+ if (json == null || typeof json !== "object")
124
+ return {};
125
+ const obj = json;
126
+ const externalSources = Array.isArray(obj.externalSources)
127
+ ? obj.externalSources
128
+ .filter((s) => s != null && typeof s === "object")
129
+ .map((s) => ({
130
+ type: String(s.type ?? ""),
131
+ name: String(s.name ?? ""),
132
+ ref: s.ref != null ? String(s.ref) : undefined,
133
+ gold: typeof s.gold === "boolean" ? s.gold : undefined,
134
+ notify: typeof s.notify === "boolean" ? s.notify : undefined,
135
+ gate: s.gate != null ? String(s.gate) : undefined,
136
+ }))
137
+ : undefined;
138
+ const pipelines = Array.isArray(obj.pipelines)
139
+ ? obj.pipelines
140
+ .filter((p) => p != null && typeof p === "object")
141
+ .map((p) => ({
142
+ id: String(p.id ?? ""),
143
+ label: String(p.label ?? ""),
144
+ lastSuccess: p.lastSuccess != null ? String(p.lastSuccess) : undefined,
145
+ }))
146
+ : undefined;
147
+ return { externalSources, pipelines };
148
+ }
109
149
  /** ディレクトリ内の .md を汎用レコードとして読み込む(存在しなければ空配列) */
110
150
  async function loadGenericRecords(dirAbs, exclude, repoBaseUrl, branch) {
111
151
  let entries;
@@ -139,13 +179,15 @@ async function parseGeneric(raw, fileName, repoRelDir, repoBaseUrl, branch) {
139
179
  const id = String(data.id ?? fileName.replace(/\.md$/i, ""));
140
180
  const title = String(data.title ?? (h1 ? h1[1].trim() : fileName.replace(/\.md$/i, "")));
141
181
  const date = normalizeDate(data.date);
142
- // 用語集: synonyms を補足行に。それ以外は frontmatter summary があれば使う
182
+ // description(OKF互換の1文要約)を最優先。無ければ用語集のsynonyms補足行、旧summary(移行期互換)の順
143
183
  const synonyms = toStringArray(data.synonyms);
144
- const summary = synonyms.length
145
- ? "同義語: " + synonyms.join(" / ")
146
- : data.summary != null
147
- ? String(data.summary)
148
- : "";
184
+ const summary = data.description != null
185
+ ? String(data.description)
186
+ : synonyms.length
187
+ ? "同義語: " + synonyms.join(" / ")
188
+ : data.summary != null
189
+ ? String(data.summary)
190
+ : "";
149
191
  const bodyHtml = externalLinksToNewTab((await marked_1.marked.parse(content, { async: true })).replace(/^\s*<h1[^>]*>[\s\S]*?<\/h1>\s*/, ""));
150
192
  const filePath = repoRelDir ? `${repoRelDir}/${fileName}` : fileName;
151
193
  const editUrl = repoBaseUrl
@@ -864,6 +906,8 @@ async function build(opts) {
864
906
  const contentEntries = await scanContent(repoRoot, knowledgeRoot ? path.basename(knowledgeRoot) : null, repo.baseUrl, repo.branch);
865
907
  const graph = buildGraph(decisions, home, sections, contentEntries, targetUrls, repo.baseUrl, repo.branch);
866
908
  computeLayout(graph); // 各ノードに x/y を書き込む(クライアントはこれを初期位置に使いウォームアップ省略)
909
+ // データソースと自動化(fleet-status.json 由来)。無ければ undefined でセクション非表示。
910
+ const fleet = await readFleetSources(repoRoot);
867
911
  const site = {
868
912
  title: opts.title,
869
913
  generatedAt: opts.generatedAt,
@@ -882,6 +926,8 @@ async function build(opts) {
882
926
  projectKey: opts.projectKey,
883
927
  }
884
928
  : undefined,
929
+ externalSources: fleet.externalSources,
930
+ pipelines: fleet.pipelines,
885
931
  };
886
932
  // サムネイル(デザイン画面等)をサイトに同梱し、ノードのパスをサイト内相対に書き換える
887
933
  await node_fs_1.promises.mkdir(outAbs, { recursive: true });
@@ -918,7 +964,7 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch) {
918
964
  const date = normalizeDate(data.date);
919
965
  const sprint = data.sprint != null ? String(data.sprint) : "";
920
966
  const category = data.category != null ? String(data.category) : "";
921
- const summary = data.summary != null ? String(data.summary) : "";
967
+ const summary = data.description != null ? String(data.description) : data.summary != null ? String(data.summary) : ""; // description優先(旧summaryは移行期互換)
922
968
  const deciders = toStringArray(data.deciders);
923
969
  const referencesRaw = toStringArray(data.references);
924
970
  const references = referencesRaw.map((r) => resolveReference(r, repoBaseUrl, branch));
package/dist/render.js CHANGED
@@ -324,6 +324,37 @@ a:hover { text-decoration: underline; }
324
324
  }
325
325
  .edit-btn:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
326
326
  .section-label { font-size: 12px; font-weight: 700; color: var(--muted); letter-spacing: .04em; text-transform: uppercase; margin: 24px 0 8px; }
327
+ /* ---- データソースと自動化 ---- */
328
+ .datasources { margin-top: 34px; }
329
+ .datasources-head { font-size: 18px; font-weight: 700; color: var(--text); margin: 0 0 4px; }
330
+ .datasources-intro { color: var(--muted); font-size: 13.5px; line-height: 1.8; margin: 0 0 14px; }
331
+ .ds-sub { font-size: 13px; font-weight: 700; color: var(--muted); margin: 20px 0 8px; }
332
+ .ds-list { display: flex; flex-direction: column; gap: 10px; }
333
+ .ds-card {
334
+ background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
335
+ padding: 14px 16px; box-shadow: var(--shadow);
336
+ display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
337
+ }
338
+ .ds-card-main { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; }
339
+ .ds-icon { font-size: 18px; line-height: 1; flex: 0 0 auto; }
340
+ .ds-type { font-size: 12px; font-weight: 600; color: var(--muted); flex: 0 0 auto; }
341
+ .ds-name { font-size: 14.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
342
+ .ds-badges { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; flex: 0 0 auto; }
343
+ .badge.ds-gold { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
344
+ .badge.ds-excluded { background: #eceef1; color: #6b7280; }
345
+ .badge.ds-notify { background: #eff6ff; color: #1e40af; border: 1px solid #bfdbfe; }
346
+ .ds-gate { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 600; flex: 0 0 auto; }
347
+ .ds-gate::before { content: ""; width: 8px; height: 8px; border-radius: 999px; background: currentColor; }
348
+ .ds-gate-ok { color: #1f7a45; }
349
+ .ds-gate-warn { color: #b45309; }
350
+ .ds-gate-muted { color: #6b7280; }
351
+ .ds-pipes { display: flex; flex-direction: column; gap: 8px; }
352
+ .ds-pipe {
353
+ display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px;
354
+ padding: 10px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow);
355
+ }
356
+ .ds-pipe-label { font-size: 14px; font-weight: 600; color: var(--text); }
357
+ .ds-pipe-time { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
327
358
  /* ---- 操作タブ ---- */
328
359
  .ops-intro { color: var(--muted); font-size: 14px; line-height: 1.8; margin: 4px 0 8px; }
329
360
  .ops-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
@@ -642,6 +673,77 @@ const CLIENT_JS = `
642
673
  var homeBody = el("div", { class: "body" });
643
674
  app.appendChild(homeBody);
644
675
  loadBody(h, homeBody);
676
+ renderDataSources();
677
+ }
678
+
679
+ // ---------- データソースと自動化(fleet-status.json 由来。無ければ非表示) ----------
680
+ // 顧客も読む面なので内部用語(cron・stub等)を避け「毎晩自動で〜」調にする。
681
+ var DS_TYPE = {
682
+ "slack": { icon: "💬", label: "Slack" },
683
+ "github-issues": { icon: "🐙", label: "GitHub Issues" },
684
+ "github-discussions": { icon: "💠", label: "GitHub Discussions" },
685
+ };
686
+ var DS_GATE = {
687
+ "ok": { text: "接続OK", cls: "ds-gate-ok" },
688
+ "not_in_channel": { text: "bot未招待", cls: "ds-gate-warn" },
689
+ "unreachable": { text: "アクセス不可", cls: "ds-gate-warn" },
690
+ "no_token": { text: "トークン未設定", cls: "ds-gate-muted" },
691
+ "unknown": { text: "不明", cls: "ds-gate-muted" },
692
+ };
693
+ function dsFmtTime(iso) {
694
+ var d = new Date(iso);
695
+ if (isNaN(d.getTime())) return "";
696
+ var hh = ("0" + d.getHours()).slice(-2);
697
+ var mm = ("0" + d.getMinutes()).slice(-2);
698
+ return (d.getMonth() + 1) + "/" + d.getDate() + " " + hh + ":" + mm;
699
+ }
700
+ function renderDataSources() {
701
+ var sources = SITE.externalSources || [];
702
+ var pipes = SITE.pipelines || [];
703
+ if (!sources.length && !pipes.length) return; // 両方無ければセクションごと出さない
704
+
705
+ var sec = el("section", { class: "datasources" });
706
+ sec.appendChild(el("h2", { class: "datasources-head", text: "データソースと自動化" }));
707
+ sec.appendChild(el("p", { class: "datasources-intro", text: "このCortexが接続している情報源と、毎晩自動でGoldに昇格する処理の一覧です。" }));
708
+
709
+ if (sources.length) {
710
+ sec.appendChild(el("div", { class: "ds-sub", text: "外部ソース" }));
711
+ var list = el("div", { class: "ds-list" });
712
+ sources.forEach(function (s) {
713
+ var meta = DS_TYPE[s.type] || { icon: "🔗", label: s.type || "その他" };
714
+ var badges = el("div", { class: "ds-badges" });
715
+ if (s.gold === true) badges.appendChild(el("span", { class: "badge ds-gold", text: "Gold昇格の読み取り対象" }));
716
+ else if (s.gold === false) badges.appendChild(el("span", { class: "badge ds-excluded", text: "除外" }));
717
+ if (s.notify === true) badges.appendChild(el("span", { class: "badge ds-notify", text: "通知先" }));
718
+ var gate = DS_GATE[s.gate] || DS_GATE["unknown"];
719
+ var gateEl = el("span", { class: "ds-gate " + gate.cls, text: gate.text });
720
+ var card = el("div", { class: "ds-card" }, [
721
+ el("div", { class: "ds-card-main" }, [
722
+ el("span", { class: "ds-icon", text: meta.icon }),
723
+ el("span", { class: "ds-type", text: meta.label }),
724
+ el("span", { class: "ds-name", text: s.name || "" }),
725
+ ]),
726
+ badges,
727
+ gateEl,
728
+ ]);
729
+ list.appendChild(card);
730
+ });
731
+ sec.appendChild(list);
732
+ }
733
+
734
+ if (pipes.length) {
735
+ sec.appendChild(el("div", { class: "ds-sub", text: "自動化パイプライン" }));
736
+ var plist = el("div", { class: "ds-pipes" });
737
+ pipes.forEach(function (p) {
738
+ var t = p.lastSuccess ? dsFmtTime(p.lastSuccess) : "";
739
+ var children = [ el("span", { class: "ds-pipe-label", text: p.label || p.id || "" }) ];
740
+ if (t) children.push(el("span", { class: "ds-pipe-time", text: "最終成功: " + t }));
741
+ plist.appendChild(el("div", { class: "ds-pipe" }, children));
742
+ });
743
+ sec.appendChild(plist);
744
+ }
745
+
746
+ app.appendChild(sec);
645
747
  }
646
748
 
647
749
  // ---------- 操作タブ(Cortex自体への操作。まずは取り込み。今後この受け皿に機能を足す) ----------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.4.18",
3
+ "version": "0.4.20",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,