@takagaki/cortex-decisions-viewer 0.12.150 → 0.12.151

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.
Files changed (2) hide show
  1. package/dist/render.js +121 -3
  2. package/package.json +1 -1
package/dist/render.js CHANGED
@@ -4972,6 +4972,18 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
4972
4972
  .card-facet-yomi { font-size: 11.5px; }
4973
4973
  .card-titlerow { display: flex; align-items: baseline; gap: 12px; margin: 0 0 6px; }
4974
4974
  .card-titlerow .card-title { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; margin: 2px 0 0; }
4975
+ /* 関係者のアイコン(名前の左・一覧と各ページで同じ見た目)。portal.id がある人はポータルの人のページへのリンク */
4976
+ .card-namerow { display: flex; align-items: center; gap: 10px; margin: 2px 0 6px; }
4977
+ .card-namerow .card-title { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; margin: 0; }
4978
+ .card-titlerow .mface { align-self: center; }
4979
+ .detail-namerow { display: flex; align-items: center; gap: 12px; margin: 0 0 10px; }
4980
+ .detail-namerow .detail-title { margin: 0; min-width: 0; overflow-wrap: anywhere; }
4981
+ .mface { position: relative; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; overflow: hidden; background: var(--border, #e5e7eb); color: var(--muted, #6b7280); font-size: 13px; font-weight: 700; line-height: 1; text-decoration: none; box-sizing: border-box; }
4982
+ a.mface:hover { box-shadow: 0 0 0 2px var(--accent, #4f46e5); }
4983
+ .mface svg { width: 18px; height: 18px; display: block; }
4984
+ .mface img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
4985
+ .detail-namerow .mface { width: 36px; height: 36px; font-size: 16px; }
4986
+ .detail-namerow .mface svg { width: 22px; height: 22px; }
4975
4987
  /* card-title はカードの見出し(#1 新しいタブでも開けるよう <a> にした)。見た目は元の <div> と揃える */
4976
4988
  .card-title { display: block; font-size: 16px; font-weight: 650; margin: 2px 0 6px; color: var(--text); }
4977
4989
  .card-summary { color: #3b424c; font-size: 14px; margin: 0; }
@@ -7595,6 +7607,105 @@ ${story_1.splitByNameLinks.toString()}
7595
7607
  var id = ask && ask.subject && ask.subject.userId;
7596
7608
  return typeof id === "string" && /^[0-9A-Za-z_-]{1,64}$/.test(id) ? PORTAL_ORIGIN + "/people/" + encodeURIComponent(id) : null;
7597
7609
  }
7610
+ // ---------- 関係者のアイコン(名前の左・cortex-engine #1775) ----------
7611
+ //
7612
+ // 🔴 **正は記録の frontmatter の portal.id**(V4 の boundIds の重なりで人を探さない)。portal の欄が無い・形が違う人は
7613
+ // 人型のデフォルト(押せない)。portal.id がある人は「頭文字の丸」を押せるリンクで出し、ポータルの人のページへ行く。
7614
+ // 🔴 **画像の URL は配布物に焼かない。** 画面に出ている人の portal.id を集めて intake の member-faces を 1 回呼び
7615
+ // (失敗は覚えない)、返った相対パスを PORTAL_ORIGIN につないで img にする。取れない・読み込めないときは頭文字のまま。
7616
+ // 🔴 portal の欄の中身(email を持つ)は画面に生で出さない(アイコンとリンクにだけ使う・member.no-email)。
7617
+ // 🔴 値は全部 textContent / setAttribute で入れる。img は alt=""(名前は横に出ている)・no-referrer・lazy。
7618
+ var MF_ID = /^[0-9A-Za-z_-]{1,64}$/;
7619
+ var MF_AVATAR = /^\\/icons\\/u\\/[0-9A-Za-z_-]+\\.(?:jpg|jpeg|png|webp|gif)$/;
7620
+ /** 記録の portal.id(形が違えば空文字) */
7621
+ function mfPortalId(rec) {
7622
+ var p = rec && rec.fm ? rec.fm.portal : null;
7623
+ var id = p && typeof p === "object" && !Array.isArray(p) ? p.id : "";
7624
+ return typeof id === "string" && MF_ID.test(id) ? id : "";
7625
+ }
7626
+ /** 取れた画像の相対パス(userId -> パス)・もう頼んだ人・まだ待っている子 */
7627
+ var mfFaces = {};
7628
+ var mfAsked = {};
7629
+ var mfWaiting = [];
7630
+ var mfTimer = null;
7631
+ function mfAttach(box, path) {
7632
+ if (!box.isConnected && !box.parentNode) return;
7633
+ var img = document.createElement("img");
7634
+ img.setAttribute("alt", "");
7635
+ img.setAttribute("referrerpolicy", "no-referrer");
7636
+ img.setAttribute("loading", "lazy");
7637
+ img.addEventListener("error", function () { if (img.parentNode) img.parentNode.removeChild(img); });
7638
+ img.setAttribute("src", PORTAL_ORIGIN + path);
7639
+ box.appendChild(img);
7640
+ }
7641
+ function mfFlush() {
7642
+ mfTimer = null;
7643
+ var waiting = mfWaiting;
7644
+ mfWaiting = [];
7645
+ if (!waiting.length || !SITE.intake || !actorTicket()) return;
7646
+ var ids = [];
7647
+ waiting.forEach(function (w) {
7648
+ if (Object.prototype.hasOwnProperty.call(mfFaces, w.id)) { if (mfFaces[w.id]) mfAttach(w.box, mfFaces[w.id]); return; }
7649
+ if (!Object.prototype.hasOwnProperty.call(mfAsked, w.id) && ids.indexOf(w.id) < 0 && ids.length < 200) ids.push(w.id);
7650
+ });
7651
+ if (!ids.length) return;
7652
+ ids.forEach(function (id) { mfAsked[id] = 1; });
7653
+ var give = function (j) {
7654
+ (j.people || []).forEach(function (one) {
7655
+ if (one && typeof one.userId === "string" && typeof one.avatarUrl === "string" && MF_AVATAR.test(one.avatarUrl)) mfFaces[one.userId] = one.avatarUrl;
7656
+ });
7657
+ ids.forEach(function (id) { if (!Object.prototype.hasOwnProperty.call(mfFaces, id)) mfFaces[id] = null; });
7658
+ waiting.forEach(function (w) { if (mfFaces[w.id]) mfAttach(w.box, mfFaces[w.id]); });
7659
+ };
7660
+ var forget = function () { ids.forEach(function (id) { delete mfAsked[id]; }); };
7661
+ intakeJson(callIntake("member-faces", { op: "member-faces", userIds: ids })).then(function (res) {
7662
+ var j = (res && res.j) || {};
7663
+ if (res && res.ok && j.ok === true && Array.isArray(j.people)) give(j);
7664
+ else forget();
7665
+ }, forget);
7666
+ }
7667
+ function mfWant(id, box) {
7668
+ mfWaiting.push({ id: id, box: box });
7669
+ if (!mfTimer) mfTimer = setTimeout(mfFlush, 0);
7670
+ }
7671
+ var SVG_NS = "http://www.w3.org/2000/svg";
7672
+ /** 人型のデフォルト(インライン SVG・はてなは使わない) */
7673
+ function mfDefaultSvg() {
7674
+ var svg = document.createElementNS(SVG_NS, "svg");
7675
+ svg.setAttribute("viewBox", "0 0 24 24");
7676
+ svg.setAttribute("aria-hidden", "true");
7677
+ svg.setAttribute("focusable", "false");
7678
+ var c = document.createElementNS(SVG_NS, "circle");
7679
+ c.setAttribute("cx", "12"); c.setAttribute("cy", "8"); c.setAttribute("r", "4"); c.setAttribute("fill", "currentColor");
7680
+ var b = document.createElementNS(SVG_NS, "path");
7681
+ b.setAttribute("d", "M4 21c0-4.4 3.6-7 8-7s8 2.6 8 7z"); b.setAttribute("fill", "currentColor");
7682
+ svg.appendChild(c);
7683
+ svg.appendChild(b);
7684
+ return svg;
7685
+ }
7686
+ /** 関係者 1 人のアイコン。一覧のカードも詳細も、この 1 つの関数で組む */
7687
+ function mfAvatar(rec) {
7688
+ var id = mfPortalId(rec);
7689
+ if (!id) {
7690
+ var plain = el("span", { class: "mface mface-default", "data-face": "default" });
7691
+ plain.appendChild(mfDefaultSvg());
7692
+ return plain;
7693
+ }
7694
+ var name = Array.from(humanTitle(rec.title))[0] || "";
7695
+ var a = el("a", {
7696
+ class: "mface mface-portal",
7697
+ "data-face": "portal",
7698
+ href: PORTAL_ORIGIN + "/people/" + encodeURIComponent(id),
7699
+ target: "_blank",
7700
+ rel: "noopener",
7701
+ title: "ポータルでこの人のページを開く",
7702
+ });
7703
+ a.appendChild(el("span", { class: "mface-initial", text: name }));
7704
+ // 行ごと(カード・詳細)の click に伝えない(カードを押した扱いで詳細へ移らない)
7705
+ a.addEventListener("click", function (ev) { ev.stopPropagation(); });
7706
+ mfWant(id, a);
7707
+ return a;
7708
+ }
7598
7709
  /**
7599
7710
  * 断られたときの 1 行(code ごとに一手)。**帯は症状・行は一手・繰り返さない。**
7600
7711
  * final: 押し直しても同じ答えになる(ボタンを畳む)/false: 時間をおけば通りうる(ボタンを残す)
@@ -8013,9 +8124,11 @@ ${story_1.splitByNameLinks.toString()}
8013
8124
  var eb = fmErrorBadge(rec);
8014
8125
  if (eb) meta.appendChild(eb);
8015
8126
  var title = cardTitleLink(rec, titleExtra);
8016
- if (!meta.childNodes.length) return [el("div", { class: "card-titlerow" }, [title, trace])];
8127
+ // 関係者は名前の左にアイコン(詳細と同じ mfAvatar)
8128
+ var face = isMemberRec(rec) ? mfAvatar(rec) : null;
8129
+ if (!meta.childNodes.length) return [el("div", { class: "card-titlerow" }, [face, title, trace])];
8017
8130
  meta.appendChild(trace);
8018
- return [meta, title];
8131
+ return [meta, face ? el("div", { class: "card-namerow" }, [face, title]) : title];
8019
8132
  }
8020
8133
 
8021
8134
  /**
@@ -20131,7 +20244,10 @@ ${story_1.splitByNameLinks.toString()}
20131
20244
  if (detailPagerG) app.appendChild(detailPagerG);
20132
20245
 
20133
20246
  var head = el("div", { class: "detail-head" });
20134
- head.appendChild(el("div", {}, [ el("h1", { class: "detail-title", text: humanTitle(r.title) }) ]));
20247
+ var titleH1 = el("h1", { class: "detail-title", text: humanTitle(r.title) });
20248
+ head.appendChild(isMemberRec(r)
20249
+ ? el("div", { class: "detail-namerow" }, [ mfAvatar(r), titleH1 ])
20250
+ : el("div", {}, [ titleH1 ]));
20135
20251
  var traceBtn2 = el("a", { class: "trace-btn", href: "?tab=graph", text: "🕸 つながり" });
20136
20252
  traceBtn2.addEventListener("click", function (ev) { ev.preventDefault(); openExplorerFor(r); });
20137
20253
  var editRowR = el("div", { class: "detail-actions-row" });
@@ -21472,6 +21588,8 @@ ${story_1.splitByNameLinks.toString()}
21472
21588
  panel.appendChild(el("summary", { class: "fm-title", text: "レコード情報(frontmatter)" }));
21473
21589
  var grid = el("div", { class: "fm-grid" });
21474
21590
  Object.keys(fm).forEach(function (k) {
21591
+ // 🔴 portal(中にポータルのアカウントのメールを持つ)は生で出さない。アイコンとリンクにだけ使う(member.no-email)
21592
+ if (k === "portal") return;
21475
21593
  grid.appendChild(el("div", { class: "fm-key", text: k }));
21476
21594
  var cell = el("div", { class: "fm-val" });
21477
21595
  var v = fm[k];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.150",
3
+ "version": "0.12.151",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,