@takagaki/cortex-decisions-viewer 0.4.19 → 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;
@@ -866,6 +906,8 @@ async function build(opts) {
866
906
  const contentEntries = await scanContent(repoRoot, knowledgeRoot ? path.basename(knowledgeRoot) : null, repo.baseUrl, repo.branch);
867
907
  const graph = buildGraph(decisions, home, sections, contentEntries, targetUrls, repo.baseUrl, repo.branch);
868
908
  computeLayout(graph); // 各ノードに x/y を書き込む(クライアントはこれを初期位置に使いウォームアップ省略)
909
+ // データソースと自動化(fleet-status.json 由来)。無ければ undefined でセクション非表示。
910
+ const fleet = await readFleetSources(repoRoot);
869
911
  const site = {
870
912
  title: opts.title,
871
913
  generatedAt: opts.generatedAt,
@@ -884,6 +926,8 @@ async function build(opts) {
884
926
  projectKey: opts.projectKey,
885
927
  }
886
928
  : undefined,
929
+ externalSources: fleet.externalSources,
930
+ pipelines: fleet.pipelines,
887
931
  };
888
932
  // サムネイル(デザイン画面等)をサイトに同梱し、ノードのパスをサイト内相対に書き換える
889
933
  await node_fs_1.promises.mkdir(outAbs, { recursive: true });
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.19",
3
+ "version": "0.4.20",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,