@takagaki/cortex-decisions-viewer 0.4.20 → 0.4.22

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
@@ -107,7 +107,7 @@ function resolveKnowledgeRoot(decisionsDirAbs) {
107
107
  return null;
108
108
  }
109
109
  /**
110
- * リポジトリルートの fleet-status.json から externalSources / pipelines を読む。
110
+ * リポジトリルートの fleet-status.json から externalSources / internalSources / pipelines を読む。
111
111
  * ファイルが無い・パース不能・当該フィールドが無い場合は undefined を返し、
112
112
  * ビューア側で「データソースと自動化」セクションを出さない(後方互換・旧fleet-statusで壊れない)。
113
113
  */
@@ -130,11 +130,27 @@ async function readFleetSources(repoRoot) {
130
130
  type: String(s.type ?? ""),
131
131
  name: String(s.name ?? ""),
132
132
  ref: s.ref != null ? String(s.ref) : undefined,
133
+ url: s.url != null ? String(s.url) : undefined,
133
134
  gold: typeof s.gold === "boolean" ? s.gold : undefined,
134
135
  notify: typeof s.notify === "boolean" ? s.notify : undefined,
135
136
  gate: s.gate != null ? String(s.gate) : undefined,
136
137
  }))
137
138
  : undefined;
139
+ const internalSources = Array.isArray(obj.internalSources)
140
+ ? obj.internalSources
141
+ .filter((s) => s != null && typeof s === "object")
142
+ .map((s) => ({
143
+ kind: String(s.kind ?? ""),
144
+ tool: String(s.tool ?? ""),
145
+ label: String(s.label ?? ""),
146
+ url: s.url != null ? String(s.url) : undefined,
147
+ lastSync: s.lastSync != null ? String(s.lastSync) : undefined,
148
+ matchKeys: Array.isArray(s.matchKeys)
149
+ ? s.matchKeys.map((k) => String(k)).filter(Boolean)
150
+ : undefined,
151
+ driveSync: typeof s.driveSync === "boolean" ? s.driveSync : undefined,
152
+ }))
153
+ : undefined;
138
154
  const pipelines = Array.isArray(obj.pipelines)
139
155
  ? obj.pipelines
140
156
  .filter((p) => p != null && typeof p === "object")
@@ -142,9 +158,11 @@ async function readFleetSources(repoRoot) {
142
158
  id: String(p.id ?? ""),
143
159
  label: String(p.label ?? ""),
144
160
  lastSuccess: p.lastSuccess != null ? String(p.lastSuccess) : undefined,
161
+ lastConclusion: p.lastConclusion != null ? String(p.lastConclusion) : undefined,
162
+ lastRun: p.lastRun != null ? String(p.lastRun) : undefined,
145
163
  }))
146
164
  : undefined;
147
- return { externalSources, pipelines };
165
+ return { externalSources, internalSources, pipelines };
148
166
  }
149
167
  /** ディレクトリ内の .md を汎用レコードとして読み込む(存在しなければ空配列) */
150
168
  async function loadGenericRecords(dirAbs, exclude, repoBaseUrl, branch) {
@@ -927,6 +945,7 @@ async function build(opts) {
927
945
  }
928
946
  : undefined,
929
947
  externalSources: fleet.externalSources,
948
+ internalSources: fleet.internalSources,
930
949
  pipelines: fleet.pipelines,
931
950
  };
932
951
  // サムネイル(デザイン画面等)をサイトに同梱し、ノードのパスをサイト内相対に書き換える
package/dist/render.js CHANGED
@@ -339,6 +339,27 @@ a:hover { text-decoration: underline; }
339
339
  .ds-icon { font-size: 18px; line-height: 1; flex: 0 0 auto; }
340
340
  .ds-type { font-size: 12px; font-weight: 600; color: var(--muted); flex: 0 0 auto; }
341
341
  .ds-name { font-size: 14.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
342
+ a.ds-name { color: var(--accent); }
343
+ a.ds-name:hover { text-decoration: underline; }
344
+ .ds-note { color: var(--muted); font-size: 12px; line-height: 1.7; margin: 6px 0 0; }
345
+ .ds-sync { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
346
+ .ds-col { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
347
+ .ds-keys { font-size: 12px; color: var(--muted); line-height: 1.6; }
348
+ .ds-drive-note { font-size: 12px; color: #6b7280; line-height: 1.6; }
349
+ .ds-group { border: none; margin: 0; }
350
+ .ds-group > summary {
351
+ display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; cursor: pointer;
352
+ background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
353
+ padding: 14px 16px; box-shadow: var(--shadow); font-size: 14.5px; font-weight: 600; color: var(--text);
354
+ list-style: none;
355
+ }
356
+ .ds-group > summary::-webkit-details-marker { display: none; }
357
+ .ds-group > summary::after { content: "▸"; margin-left: auto; color: var(--muted); font-size: 12px; }
358
+ .ds-group[open] > summary::after { content: "▾"; }
359
+ .ds-group[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
360
+ .ds-group > .ds-list { margin-top: 8px; padding-left: 12px; border-left: 2px solid var(--border); }
361
+ .badge.ds-sum-ok { background: #e9f7ef; color: #1f7a45; }
362
+ .badge.ds-sum-warn { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
342
363
  .ds-badges { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; flex: 0 0 auto; }
343
364
  .badge.ds-gold { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
344
365
  .badge.ds-excluded { background: #eceef1; color: #6b7280; }
@@ -355,6 +376,7 @@ a:hover { text-decoration: underline; }
355
376
  }
356
377
  .ds-pipe-label { font-size: 14px; font-weight: 600; color: var(--text); }
357
378
  .ds-pipe-time { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
379
+ .ds-pipe-status { font-size: 13px; line-height: 1; flex: 0 0 auto; }
358
380
  /* ---- 操作タブ ---- */
359
381
  .ops-intro { color: var(--muted); font-size: 14px; line-height: 1.8; margin: 4px 0 8px; }
360
382
  .ops-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
@@ -690,6 +712,14 @@ const CLIENT_JS = `
690
712
  "no_token": { text: "トークン未設定", cls: "ds-gate-muted" },
691
713
  "unknown": { text: "不明", cls: "ds-gate-muted" },
692
714
  };
715
+ var DS_KIND_ICON = {
716
+ "課題管理": "📋",
717
+ "会議": "🗒️",
718
+ "共有資料": "📎",
719
+ "デザイン": "🎨",
720
+ "開発": "🐙",
721
+ "チャット": "💬",
722
+ };
693
723
  function dsFmtTime(iso) {
694
724
  var d = new Date(iso);
695
725
  if (isNaN(d.getTime())) return "";
@@ -697,38 +727,88 @@ const CLIENT_JS = `
697
727
  var mm = ("0" + d.getMinutes()).slice(-2);
698
728
  return (d.getMonth() + 1) + "/" + d.getDate() + " " + hh + ":" + mm;
699
729
  }
730
+ // 名前表示: url があれば新しいタブで正本を開くリンクにする
731
+ function dsNameEl(text, url) {
732
+ if (url) return el("a", { class: "ds-name", href: url, target: "_blank", rel: "noopener", text: text });
733
+ return el("span", { class: "ds-name", text: text });
734
+ }
700
735
  function renderDataSources() {
736
+ var internals = SITE.internalSources || [];
701
737
  var sources = SITE.externalSources || [];
702
738
  var pipes = SITE.pipelines || [];
703
- if (!sources.length && !pipes.length) return; // 両方無ければセクションごと出さない
739
+ if (!internals.length && !sources.length && !pipes.length) return; // すべて無ければセクションごと出さない
704
740
 
705
741
  var sec = el("section", { class: "datasources" });
706
742
  sec.appendChild(el("h2", { class: "datasources-head", text: "データソースと自動化" }));
707
743
  sec.appendChild(el("p", { class: "datasources-intro", text: "このCortexが接続している情報源と、毎晩自動でGoldに昇格する処理の一覧です。" }));
708
744
 
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 });
745
+ if (internals.length) {
746
+ sec.appendChild(el("div", { class: "ds-sub", text: "リポジトリ内の情報源(自動同期)" }));
747
+ var ilist = el("div", { class: "ds-list" });
748
+ internals.forEach(function (s) {
749
+ // ラベルの下に補足行(会議の照合キー・Drive未設定の注記)を重ねられるよう縦組みにする
750
+ var col = el("div", { class: "ds-col" }, [ dsNameEl(s.label || "", s.url) ]);
751
+ var keys = s.matchKeys || [];
752
+ if (keys.length) {
753
+ var shown = keys.slice(0, 5).join(" / ");
754
+ if (keys.length > 5) shown += " 他" + (keys.length - 5) + "";
755
+ col.appendChild(el("span", {
756
+ class: "ds-keys",
757
+ text: "取り込み対象の会議名: " + shown,
758
+ title: "会議名にこれらの語が含まれていると、自動でこのCortexに取り込まれます",
759
+ }));
760
+ }
761
+ if (s.driveSync === false) {
762
+ col.appendChild(el("span", { class: "ds-drive-note", text: "Drive自動同期は未設定(手動で置いたファイルの変換のみ)" }));
763
+ }
720
764
  var card = el("div", { class: "ds-card" }, [
721
765
  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 || "" }),
766
+ el("span", { class: "ds-icon", text: DS_KIND_ICON[s.kind] || "📁" }),
767
+ el("span", { class: "ds-type", text: s.kind || "" }),
768
+ col,
725
769
  ]),
726
- badges,
727
- gateEl,
770
+ s.lastSync && dsFmtTime(s.lastSync)
771
+ ? el("span", { class: "ds-sync", text: "最終同期: " + dsFmtTime(s.lastSync) })
772
+ : null,
728
773
  ]);
729
- list.appendChild(card);
774
+ ilist.appendChild(card);
775
+ });
776
+ sec.appendChild(ilist);
777
+ sec.appendChild(el("p", { class: "ds-note", text: "これらの更新は毎晩の Gold 昇格が読み取ります。" }));
778
+ }
779
+
780
+ if (sources.length) {
781
+ sec.appendChild(el("div", { class: "ds-sub", text: "外部ソース(ライブ参照)" }));
782
+ // ツール(type)別にグループ化し、多いグループ(4件以上)は折りたたむ
783
+ var groups = [];
784
+ var byType = {};
785
+ sources.forEach(function (s) {
786
+ var g = byType[s.type];
787
+ if (!g) { g = { type: s.type, items: [] }; byType[s.type] = g; groups.push(g); }
788
+ g.items.push(s);
789
+ });
790
+ var UNIT = { "slack": "チャンネル", "github-issues": "リポジトリ", "github-discussions": "リポジトリ" };
791
+ var wrap = el("div", { class: "ds-list" });
792
+ groups.forEach(function (g) {
793
+ var meta = DS_TYPE[g.type] || { icon: "🔗", label: g.type || "その他" };
794
+ var cards = el("div", { class: "ds-list" });
795
+ g.items.forEach(function (s) { cards.appendChild(extSourceCard(s, meta)); });
796
+ if (g.items.length >= 4) {
797
+ // 折りたたみ(既定は閉)。summaryに件数と状態の要約を出す
798
+ var bad = 0;
799
+ g.items.forEach(function (s) { if (s.gate !== "ok") bad++; });
800
+ var sum = el("summary", {}, [
801
+ el("span", { text: meta.icon + " " + meta.label + "(" + g.items.length + (UNIT[g.type] || "件") + ")" }),
802
+ bad === 0
803
+ ? el("span", { class: "badge ds-sum-ok", text: "接続OK" })
804
+ : el("span", { class: "badge ds-sum-warn", text: "⚠️ " + bad + "件に問題" }),
805
+ ]);
806
+ wrap.appendChild(el("details", { class: "ds-group" }, [sum, cards]));
807
+ } else {
808
+ while (cards.firstChild) wrap.appendChild(cards.firstChild);
809
+ }
730
810
  });
731
- sec.appendChild(list);
811
+ sec.appendChild(wrap);
732
812
  }
733
813
 
734
814
  if (pipes.length) {
@@ -736,7 +816,13 @@ const CLIENT_JS = `
736
816
  var plist = el("div", { class: "ds-pipes" });
737
817
  pipes.forEach(function (p) {
738
818
  var t = p.lastSuccess ? dsFmtTime(p.lastSuccess) : "";
739
- var children = [ el("span", { class: "ds-pipe-label", text: p.label || p.id || "" }) ];
819
+ var children = [];
820
+ if (p.lastConclusion === "success") {
821
+ children.push(el("span", { class: "ds-pipe-status", text: "✅", title: "直近の実行は成功しています" }));
822
+ } else if (p.lastConclusion === "failure") {
823
+ children.push(el("span", { class: "ds-pipe-status", text: "❌", title: "直近の実行が失敗しています" }));
824
+ }
825
+ children.push(el("span", { class: "ds-pipe-label", text: p.label || p.id || "" }));
740
826
  if (t) children.push(el("span", { class: "ds-pipe-time", text: "最終成功: " + t }));
741
827
  plist.appendChild(el("div", { class: "ds-pipe" }, children));
742
828
  });
@@ -745,6 +831,23 @@ const CLIENT_JS = `
745
831
 
746
832
  app.appendChild(sec);
747
833
  }
834
+ // 外部ソース1件のカード(グループ化表示・展開表示の両方から使う)
835
+ function extSourceCard(s, meta) {
836
+ var badges = el("div", { class: "ds-badges" });
837
+ if (s.gold === true) badges.appendChild(el("span", { class: "badge ds-gold", text: "Gold昇格の読み取り対象" }));
838
+ else if (s.gold === false) badges.appendChild(el("span", { class: "badge ds-excluded", text: "除外" }));
839
+ if (s.notify === true) badges.appendChild(el("span", { class: "badge ds-notify", text: "通知先" }));
840
+ var gate = DS_GATE[s.gate] || DS_GATE["unknown"];
841
+ return el("div", { class: "ds-card" }, [
842
+ el("div", { class: "ds-card-main" }, [
843
+ el("span", { class: "ds-icon", text: meta.icon }),
844
+ el("span", { class: "ds-type", text: meta.label }),
845
+ dsNameEl(s.name || "", s.url),
846
+ ]),
847
+ badges,
848
+ el("span", { class: "ds-gate " + gate.cls, text: gate.text }),
849
+ ]);
850
+ }
748
851
 
749
852
  // ---------- 操作タブ(Cortex自体への操作。まずは取り込み。今後この受け皿に機能を足す) ----------
750
853
  function renderOps() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.4.20",
3
+ "version": "0.4.22",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,