@agent-format/renderer
Advanced tools
+307
-459
@@ -26,4 +26,6 @@ "use strict"; | ||
| downloadPrintableHtml: () => downloadPrintableHtml, | ||
| findVariantComponent: () => findVariantComponent, | ||
| openInViewer: () => openInViewer, | ||
| useHost: () => useHost | ||
| useHost: () => useHost, | ||
| usePlugins: () => usePlugins | ||
| }); | ||
@@ -421,5 +423,292 @@ module.exports = __toCommonJS(index_exports); | ||
| // src/sections/InheritanceDiagramSection.tsx | ||
| // src/sections/FamilyGraphSection.tsx | ||
| var import_react = require("react"); | ||
| // src/plugins.ts | ||
| function findVariantComponent(plugins, sectionType, variant) { | ||
| if (!variant) return void 0; | ||
| for (const plugin of plugins) { | ||
| const component = plugin.variants?.[sectionType]?.[variant]; | ||
| if (component) return component; | ||
| } | ||
| return void 0; | ||
| } | ||
| // src/sections/FamilyGraphSection.tsx | ||
| var import_jsx_runtime13 = require("react/jsx-runtime"); | ||
| var MAX_GENERATIONS = 8; | ||
| var CARD_W = 220; | ||
| var CARD_H = 120; | ||
| var COL_GAP = 40; | ||
| var ROW_GAP = 80; | ||
| var MARGIN_X = 20; | ||
| var MARGIN_Y = 20; | ||
| function FamilyGraphSectionView({ section, setHeaderActions }) { | ||
| const plugins = usePlugins(); | ||
| const variant = section.data?.variant; | ||
| const VariantComponent = findVariantComponent(plugins, "family-graph", variant); | ||
| if (VariantComponent) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(VariantComponent, { section, setHeaderActions }); | ||
| } | ||
| if (section.type === "inheritance-diagram") { | ||
| const AliasComponent = findVariantComponent(plugins, "inheritance-diagram", variant); | ||
| if (AliasComponent) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AliasComponent, { section, setHeaderActions }); | ||
| } | ||
| } | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DefaultGenealogy, { section }); | ||
| } | ||
| function DefaultGenealogy({ section }) { | ||
| const persons = section.data?.persons ?? []; | ||
| const rels = section.data?.relationships ?? []; | ||
| const focusedId = section.data?.focusedPersonId; | ||
| const layout = (0, import_react.useMemo)(() => computeLayout(persons, rels), [persons, rels]); | ||
| if (persons.length === 0) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "af-empty", children: "No persons in diagram." }); | ||
| } | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "af-family-graph", style: { overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)( | ||
| "svg", | ||
| { | ||
| xmlns: "http://www.w3.org/2000/svg", | ||
| width: layout.width, | ||
| height: layout.height, | ||
| viewBox: `0 0 ${layout.width} ${layout.height}`, | ||
| role: "img", | ||
| "aria-label": "Family graph", | ||
| children: [ | ||
| layout.parentChildEdges.map((edge, i) => { | ||
| const p = cardById(layout, edge.parent); | ||
| const c = cardById(layout, edge.child); | ||
| if (!p || !c) return null; | ||
| const px = p.x + CARD_W / 2; | ||
| const py = p.y + CARD_H; | ||
| const cx = c.x + CARD_W / 2; | ||
| const cy = c.y; | ||
| const midY = (py + cy) / 2; | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "polyline", | ||
| { | ||
| points: `${px},${py} ${px},${midY} ${cx},${midY} ${cx},${cy}`, | ||
| fill: "none", | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| opacity: "0.6" | ||
| }, | ||
| `pc-${i}` | ||
| ); | ||
| }), | ||
| layout.spouseEdges.map((edge, i) => { | ||
| const a = cardById(layout, edge.a); | ||
| const b = cardById(layout, edge.b); | ||
| if (!a || !b) return null; | ||
| const ay = a.y + CARD_H / 2; | ||
| const by = b.y + CARD_H / 2; | ||
| const ax = a.x + CARD_W; | ||
| const bx = b.x; | ||
| if (a.y !== b.y) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: a.x + CARD_W / 2, | ||
| y1: ay, | ||
| x2: b.x + CARD_W / 2, | ||
| y2: by, | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| strokeDasharray: edge.dissolved ? "4 3" : void 0, | ||
| opacity: "0.6" | ||
| }, | ||
| `sp-${i}` | ||
| ); | ||
| } | ||
| const mid = (ay + by) / 2; | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("g", { children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: ax, | ||
| y1: mid - 3, | ||
| x2: bx, | ||
| y2: mid - 3, | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| strokeDasharray: edge.dissolved ? "4 3" : void 0, | ||
| opacity: "0.8" | ||
| } | ||
| ), | ||
| !edge.dissolved && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: ax, | ||
| y1: mid + 3, | ||
| x2: bx, | ||
| y2: mid + 3, | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| opacity: "0.8" | ||
| } | ||
| ) | ||
| ] }, `sp-${i}`); | ||
| }), | ||
| layout.cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| PersonCard, | ||
| { | ||
| person: card.person, | ||
| x: card.x, | ||
| y: card.y, | ||
| focused: card.id === focusedId | ||
| }, | ||
| card.id | ||
| )) | ||
| ] | ||
| } | ||
| ) }); | ||
| } | ||
| function cardById(layout, id) { | ||
| return layout.cards.find((c) => c.id === id); | ||
| } | ||
| function PersonCard({ | ||
| person, | ||
| x, | ||
| y, | ||
| focused | ||
| }) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("g", { transform: `translate(${x}, ${y})`, children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "rect", | ||
| { | ||
| width: CARD_W, | ||
| height: CARD_H, | ||
| rx: 8, | ||
| fill: focused ? "var(--af-accent-soft, #eef2ff)" : "var(--af-bg-alt, #f7f7f8)", | ||
| stroke: focused ? "var(--af-accent, #2251ff)" : "var(--af-border, #e5e7eb)", | ||
| strokeWidth: focused ? 2 : 1 | ||
| } | ||
| ), | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("text", { x: 12, y: 24, fontSize: "14", fontWeight: "600", fill: "currentColor", children: person.name }), | ||
| person.role && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "text", | ||
| { | ||
| x: 12, | ||
| y: 44, | ||
| fontSize: "11", | ||
| fill: "currentColor", | ||
| opacity: "0.7", | ||
| children: person.role | ||
| } | ||
| ), | ||
| person.birthday && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("text", { x: 12, y: 66, fontSize: "11", fill: "currentColor", opacity: "0.7", children: [ | ||
| "b. ", | ||
| person.birthday | ||
| ] }), | ||
| person.deathDate && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("text", { x: 12, y: 82, fontSize: "11", fill: "currentColor", opacity: "0.7", children: [ | ||
| "d. ", | ||
| person.deathDate | ||
| ] }), | ||
| person.address && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("text", { x: 12, y: 100, fontSize: "10", fill: "currentColor", opacity: "0.55", children: truncate(person.address, 30) }) | ||
| ] }); | ||
| } | ||
| function truncate(s, max) { | ||
| return s.length <= max ? s : s.slice(0, max - 1) + "\u2026"; | ||
| } | ||
| function computeLayout(persons, rels) { | ||
| const byId = new Map(persons.map((p) => [p.id, p])); | ||
| const childrenOf = /* @__PURE__ */ new Map(); | ||
| const parentsOf = /* @__PURE__ */ new Map(); | ||
| const spouseEdges = []; | ||
| for (const r of rels) { | ||
| if (r.type === "parent-child") { | ||
| if (!byId.has(r.person1Id) || !byId.has(r.person2Id)) continue; | ||
| if (!childrenOf.has(r.person1Id)) childrenOf.set(r.person1Id, []); | ||
| childrenOf.get(r.person1Id).push(r.person2Id); | ||
| if (!parentsOf.has(r.person2Id)) parentsOf.set(r.person2Id, []); | ||
| parentsOf.get(r.person2Id).push(r.person1Id); | ||
| } else if (r.type === "spouse") { | ||
| if (!byId.has(r.person1Id) || !byId.has(r.person2Id)) continue; | ||
| spouseEdges.push({ | ||
| a: r.person1Id, | ||
| b: r.person2Id, | ||
| dissolved: r.dissolved | ||
| }); | ||
| } | ||
| } | ||
| const depth = /* @__PURE__ */ new Map(); | ||
| const seeds = persons.filter((p) => !parentsOf.has(p.id)).map((p) => p.id); | ||
| const queue = seeds.map((id) => ({ id, d: 0 })); | ||
| while (queue.length > 0) { | ||
| const { id, d } = queue.shift(); | ||
| if (d > MAX_GENERATIONS) continue; | ||
| if (depth.has(id)) continue; | ||
| depth.set(id, d); | ||
| for (const childId of childrenOf.get(id) ?? []) { | ||
| if (!depth.has(childId)) queue.push({ id: childId, d: d + 1 }); | ||
| } | ||
| } | ||
| for (const s of spouseEdges) { | ||
| if (!depth.has(s.a) && depth.has(s.b)) depth.set(s.a, depth.get(s.b)); | ||
| if (!depth.has(s.b) && depth.has(s.a)) depth.set(s.b, depth.get(s.a)); | ||
| } | ||
| for (const p of persons) { | ||
| if (!depth.has(p.id)) depth.set(p.id, 0); | ||
| } | ||
| const byGen = /* @__PURE__ */ new Map(); | ||
| for (const p of persons) { | ||
| const d = depth.get(p.id); | ||
| if (!byGen.has(d)) byGen.set(d, []); | ||
| byGen.get(d).push(p); | ||
| } | ||
| const sortedGens = Array.from(byGen.entries()).sort((a, b) => a[0] - b[0]); | ||
| for (const [, row] of sortedGens) { | ||
| placeSpousesAdjacent(row, spouseEdges); | ||
| } | ||
| const rowCount = Math.max(...sortedGens.map(([, r]) => r.length), 1); | ||
| const rowWidth = rowCount * CARD_W + (rowCount - 1) * COL_GAP; | ||
| const cards = []; | ||
| sortedGens.forEach(([d, row]) => { | ||
| const y = MARGIN_Y + d * (CARD_H + ROW_GAP); | ||
| const thisRowWidth = row.length * CARD_W + (row.length - 1) * COL_GAP; | ||
| const offsetX = MARGIN_X + (rowWidth - thisRowWidth) / 2; | ||
| row.forEach((p, i) => { | ||
| cards.push({ | ||
| id: p.id, | ||
| person: p, | ||
| x: offsetX + i * (CARD_W + COL_GAP), | ||
| y | ||
| }); | ||
| }); | ||
| }); | ||
| const width = MARGIN_X * 2 + rowWidth; | ||
| const height = MARGIN_Y * 2 + sortedGens.length * CARD_H + Math.max(sortedGens.length - 1, 0) * ROW_GAP; | ||
| const parentChildEdges = []; | ||
| for (const r of rels) { | ||
| if (r.type !== "parent-child") continue; | ||
| if (!byId.has(r.person1Id) || !byId.has(r.person2Id)) continue; | ||
| parentChildEdges.push({ parent: r.person1Id, child: r.person2Id }); | ||
| } | ||
| return { cards, parentChildEdges, spouseEdges, width, height }; | ||
| } | ||
| function placeSpousesAdjacent(row, spouseEdges) { | ||
| for (const edge of spouseEdges) { | ||
| const ai = row.findIndex((p) => p.id === edge.a); | ||
| const bi = row.findIndex((p) => p.id === edge.b); | ||
| if (ai < 0 || bi < 0 || Math.abs(ai - bi) === 1) continue; | ||
| const [b] = row.splice(bi, 1); | ||
| const insertAt = ai < bi ? ai + 1 : ai; | ||
| row.splice(insertAt, 0, b); | ||
| } | ||
| } | ||
| // src/sections/Fallback.tsx | ||
| var import_jsx_runtime14 = require("react/jsx-runtime"); | ||
| function FallbackSectionView({ section }) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "af-fallback", children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [ | ||
| "Renderer for section type ", | ||
| /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: section.type }), | ||
| " is not yet implemented in this viewer." | ||
| ] }), | ||
| /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pre", { children: JSON.stringify(section.data, null, 2) }) | ||
| ] }); | ||
| } | ||
| // src/host.ts | ||
@@ -541,460 +830,12 @@ function fallbackOpenLink(url) { | ||
| // src/sections/InheritanceDiagramSection.tsx | ||
| var import_jsx_runtime13 = require("react/jsx-runtime"); | ||
| var MAX_GENERATIONS = 6; | ||
| var LINE_H = 22; | ||
| var NAME_SIZE = 16; | ||
| var NAME_LINE_H = 22; | ||
| var DBL_GAP = 4; | ||
| var HORIZ_Y_GAP = 90; | ||
| var CHILD_V_GAP = 30; | ||
| var GEN_X_STEP = 340; | ||
| var SPOUSE_GAP = 90; | ||
| var TEXT_X = 60; | ||
| var CHILD_TEXT_X = 480; | ||
| var TRUNK_X = CHILD_TEXT_X - 30; | ||
| var DBL_X = 80; | ||
| var DEC_TOP_Y = 20; | ||
| function InheritanceDiagramSectionView({ section, setHeaderActions }) { | ||
| const data = section.data; | ||
| const persons = data?.persons ?? []; | ||
| const rels = data?.relationships ?? []; | ||
| const variant = data?.variant ?? "jp-court"; | ||
| const svgRef = (0, import_react.useRef)(null); | ||
| const host = useHost(); | ||
| (0, import_react.useEffect)(() => { | ||
| if (!setHeaderActions) return; | ||
| if (!svgRef.current) return; | ||
| if (persons.length === 0 || variant !== "jp-court") { | ||
| setHeaderActions(null); | ||
| return; | ||
| } | ||
| const sectionLabel = section.label || "\u76F8\u7D9A\u95A2\u4FC2\u8AAC\u660E\u56F3"; | ||
| const documentTitle = sectionLabel; | ||
| const headerTitle = "\u76F8 \u7D9A \u95A2 \u4FC2 \u8AAC \u660E \u56F3"; | ||
| const onClick = () => { | ||
| const svgEl = svgRef.current; | ||
| if (!svgEl) return; | ||
| const serialized = new XMLSerializer().serializeToString(svgEl); | ||
| const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10); | ||
| void downloadPrintableHtml({ | ||
| svgMarkup: serialized, | ||
| titleLabel: headerTitle, | ||
| documentTitle, | ||
| filename: `inheritance-diagram-${today}.html`, | ||
| host | ||
| }); | ||
| }; | ||
| setHeaderActions( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| className: "af-action-btn", | ||
| onClick, | ||
| title: "\u5370\u5237\u30FBPDF \u4FDD\u5B58\u7528\u306E HTML \u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\uFF08\u958B\u3044\u3066 \u2318P \u3067 PDF \u4FDD\u5B58\u3001\u88C1\u5224\u6240\u63D0\u51FA\u7528 A3 \u6A2A\u66F8\u5F0F\uFF09", | ||
| children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { "aria-hidden": true, children: "\u2B07" }), | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "PDF" }) | ||
| ] | ||
| } | ||
| ) | ||
| ); | ||
| return () => setHeaderActions(null); | ||
| }, [setHeaderActions, section.id, section.label, persons.length, variant, host]); | ||
| if (persons.length === 0) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "af-empty", children: "No persons in diagram." }); | ||
| } | ||
| if (variant !== "jp-court") { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "af-empty", children: [ | ||
| 'Inheritance-diagram variant "', | ||
| variant, | ||
| '" is not yet implemented. Only "jp-court" is supported in v0.1.' | ||
| ] }); | ||
| } | ||
| const focused = data?.focusedPersonId ? persons.find((p) => p.id === data.focusedPersonId) : null; | ||
| const decedent = focused || persons.find((p) => Boolean(p.deathDate)) || persons[0]; | ||
| if (!decedent) return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "af-empty", children: "No decedent." }); | ||
| const findSpouseOfRoot = () => { | ||
| const rel = rels.find( | ||
| (r) => r.type === "spouse" && (r.person1Id === decedent.id || r.person2Id === decedent.id) | ||
| ); | ||
| if (!rel) return null; | ||
| const sid = rel.person1Id === decedent.id ? rel.person2Id : rel.person1Id; | ||
| return persons.find((p) => p.id === sid) ?? null; | ||
| }; | ||
| const findChildren = (parentId, spouseId) => { | ||
| const pids = /* @__PURE__ */ new Set([parentId]); | ||
| if (spouseId) pids.add(spouseId); | ||
| const kids = []; | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| for (const r of rels) { | ||
| if (r.type === "parent-child" && pids.has(r.person1Id)) { | ||
| if (seen.has(r.person2Id)) continue; | ||
| const c = persons.find((p) => p.id === r.person2Id); | ||
| if (c) { | ||
| seen.add(r.person2Id); | ||
| kids.push(c); | ||
| } | ||
| } | ||
| } | ||
| return kids; | ||
| }; | ||
| const findSpouse = (personId) => { | ||
| const rel = rels.find( | ||
| (r) => r.type === "spouse" && r.person1Id !== decedent.id && r.person2Id !== decedent.id && (r.person1Id === personId || r.person2Id === personId) | ||
| ); | ||
| if (!rel) return null; | ||
| const sid = rel.person1Id === personId ? rel.person2Id : rel.person1Id; | ||
| return persons.find((p) => p.id === sid) ?? null; | ||
| }; | ||
| const blockHeight = (p) => { | ||
| let n = 0; | ||
| if (p.address) n++; | ||
| if (p.birthday) n++; | ||
| if (p.deathDate) n++; | ||
| n++; | ||
| return n * LINE_H + NAME_LINE_H; | ||
| }; | ||
| let elementKey = 0; | ||
| const nextKey = () => `el-${++elementKey}`; | ||
| const renderBlock = (p, x, topY, roleLabel) => { | ||
| const elements = []; | ||
| let y = topY; | ||
| if (p.address) { | ||
| const lbl = roleLabel === "\u88AB\u76F8\u7D9A\u4EBA" ? "\u6700\u5F8C\u306E\u4F4F\u6240" : "\u4F4F\u6240"; | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("text", { x, y, fontSize: "11pt", children: `${lbl}\u3000${p.address}` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| } | ||
| if (p.birthday) { | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("text", { x, y, fontSize: "11pt", children: `\u51FA\u751F\u3000${p.birthday}` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| } | ||
| if (p.deathDate) { | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("text", { x, y, fontSize: "11pt", children: `\u6B7B\u4EA1\u3000${p.deathDate}` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| } | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("text", { x: x + 8, y, fontSize: "11pt", children: `\uFF08${roleLabel}\uFF09` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "text", | ||
| { | ||
| x, | ||
| y, | ||
| fontSize: `${NAME_SIZE}pt`, | ||
| fontWeight: "bold", | ||
| letterSpacing: "0.2em", | ||
| children: p.name | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| const nameBaseY = y; | ||
| y += NAME_LINE_H; | ||
| return { elements, nameBaseY, blockEndY: y }; | ||
| }; | ||
| const measureChildSlot = (c, depth) => { | ||
| if (depth > MAX_GENERATIONS) return blockHeight(c); | ||
| let h = blockHeight(c); | ||
| const sp = findSpouse(c.id); | ||
| if (sp) h += SPOUSE_GAP + blockHeight(sp); | ||
| const grandkids = findChildren(c.id, sp ? sp.id : null); | ||
| if (grandkids.length > 0) { | ||
| const subH = measureChildGroup(grandkids, depth + 1); | ||
| h = Math.max(h, subH); | ||
| } | ||
| return h; | ||
| }; | ||
| const measureChildGroup = (childList, depth) => { | ||
| let total = 0; | ||
| childList.forEach((c, i) => { | ||
| if (i > 0) total += CHILD_V_GAP; | ||
| total += measureChildSlot(c, depth); | ||
| }); | ||
| return total; | ||
| }; | ||
| const lineProps = { | ||
| stroke: "#000", | ||
| strokeWidth: 1.2 | ||
| }; | ||
| const renderChildGroup = (childList, textX, startY, depth) => { | ||
| const elements = []; | ||
| const nameYs = []; | ||
| let cy = startY; | ||
| if (depth > MAX_GENERATIONS) { | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)( | ||
| "text", | ||
| { | ||
| x: textX, | ||
| y: cy, | ||
| fontSize: "11pt", | ||
| fill: "#999", | ||
| children: [ | ||
| "\u2026 (tree truncated at generation ", | ||
| MAX_GENERATIONS, | ||
| ")" | ||
| ] | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| return { elements, nameYs, endY: cy + LINE_H }; | ||
| } | ||
| childList.forEach((c, i) => { | ||
| if (i > 0) cy += CHILD_V_GAP; | ||
| const cb = renderBlock(c, textX, cy, c.role || "\u76F8\u7D9A\u4EBA"); | ||
| elements.push(...cb.elements); | ||
| nameYs.push(cb.nameBaseY); | ||
| const childSpouse = findSpouse(c.id); | ||
| let connectorY = cb.nameBaseY; | ||
| if (childSpouse) { | ||
| const spTopY = cb.blockEndY + SPOUSE_GAP; | ||
| const spBlock2 = renderBlock( | ||
| childSpouse, | ||
| textX, | ||
| spTopY, | ||
| childSpouse.role || "\u914D\u5076\u8005" | ||
| ); | ||
| elements.push(...spBlock2.elements); | ||
| const miniDblX = textX + 20; | ||
| const miniGapTop = cb.blockEndY + 5; | ||
| const miniGapBot = spTopY - 25; | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: miniDblX - DBL_GAP, | ||
| y1: miniGapTop, | ||
| x2: miniDblX - DBL_GAP, | ||
| y2: miniGapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: miniDblX + DBL_GAP, | ||
| y1: miniGapTop, | ||
| x2: miniDblX + DBL_GAP, | ||
| y2: miniGapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| connectorY = cb.blockEndY + SPOUSE_GAP / 2; | ||
| } | ||
| const grandkids = findChildren(c.id, childSpouse ? childSpouse.id : null); | ||
| if (grandkids.length > 0) { | ||
| const gcTextX = textX + GEN_X_STEP; | ||
| const gcTrunkX = gcTextX - 30; | ||
| const gcResult = renderChildGroup(grandkids, gcTextX, cy, depth + 1); | ||
| elements.push(...gcResult.elements); | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: textX + 180, | ||
| y1: connectorY, | ||
| x2: gcTrunkX, | ||
| y2: connectorY, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| if (gcResult.nameYs.length > 0) { | ||
| const gcTrunkTop = Math.min(connectorY, gcResult.nameYs[0]); | ||
| const gcTrunkBot = Math.max( | ||
| connectorY, | ||
| gcResult.nameYs[gcResult.nameYs.length - 1] | ||
| ); | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: gcTrunkX, | ||
| y1: gcTrunkTop, | ||
| x2: gcTrunkX, | ||
| y2: gcTrunkBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| gcResult.nameYs.forEach((gny) => { | ||
| elements.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: gcTrunkX, | ||
| y1: gny, | ||
| x2: gcTextX - 5, | ||
| y2: gny, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| }); | ||
| } | ||
| } | ||
| cy += measureChildSlot(c, depth); | ||
| }); | ||
| return { elements, nameYs, endY: cy }; | ||
| }; | ||
| const dec = renderBlock(decedent, TEXT_X, DEC_TOP_Y, "\u88AB\u76F8\u7D9A\u4EBA"); | ||
| const spouse = findSpouseOfRoot(); | ||
| const spouseTopY = dec.blockEndY + HORIZ_Y_GAP; | ||
| const spBlock = spouse ? renderBlock(spouse, TEXT_X, spouseTopY, spouse.role || "\u76F8\u7D9A\u4EBA") : null; | ||
| const dblMidY = dec.blockEndY + HORIZ_Y_GAP / 2; | ||
| const children = findChildren(decedent.id, spouse ? spouse.id : null); | ||
| const totalChildH = children.length > 0 ? measureChildGroup(children, 0) : 0; | ||
| let childGroupTopY = dblMidY - totalChildH / 2; | ||
| if (childGroupTopY < 10) childGroupTopY = 10; | ||
| const childResult = children.length > 0 ? renderChildGroup(children, CHILD_TEXT_X, childGroupTopY, 0) : { elements: [], nameYs: [], endY: childGroupTopY }; | ||
| const svgH = Math.max(spBlock ? spBlock.blockEndY : dec.blockEndY, childResult.endY) + 30; | ||
| const svgParts = []; | ||
| svgParts.push(...dec.elements); | ||
| if (spBlock) svgParts.push(...spBlock.elements); | ||
| if (spouse) { | ||
| const gapTop = dec.blockEndY + 5; | ||
| const gapBot = spouseTopY - 25; | ||
| svgParts.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: DBL_X - DBL_GAP, | ||
| y1: gapTop, | ||
| x2: DBL_X - DBL_GAP, | ||
| y2: gapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| svgParts.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: DBL_X + DBL_GAP, | ||
| y1: gapTop, | ||
| x2: DBL_X + DBL_GAP, | ||
| y2: gapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| if (children.length > 0) { | ||
| svgParts.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: DBL_X + DBL_GAP, | ||
| y1: dblMidY, | ||
| x2: TRUNK_X, | ||
| y2: dblMidY, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| } | ||
| } else if (children.length > 0) { | ||
| svgParts.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: TEXT_X + 200, | ||
| y1: dec.nameBaseY, | ||
| x2: TRUNK_X, | ||
| y2: dec.nameBaseY, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| } | ||
| svgParts.push(...childResult.elements); | ||
| if (childResult.nameYs.length > 0) { | ||
| const trunkTop = Math.min( | ||
| spouse ? dblMidY : dec.nameBaseY, | ||
| childResult.nameYs[0] | ||
| ); | ||
| const trunkBot = childResult.nameYs[childResult.nameYs.length - 1]; | ||
| svgParts.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: TRUNK_X, | ||
| y1: trunkTop, | ||
| x2: TRUNK_X, | ||
| y2: trunkBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| childResult.nameYs.forEach((cny) => { | ||
| svgParts.push( | ||
| /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "line", | ||
| { | ||
| x1: TRUNK_X, | ||
| y1: cny, | ||
| x2: CHILD_TEXT_X - 5, | ||
| y2: cny, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| }); | ||
| } | ||
| return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "af-inheritance-diagram", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( | ||
| "svg", | ||
| { | ||
| ref: svgRef, | ||
| xmlns: "http://www.w3.org/2000/svg", | ||
| width: "100%", | ||
| viewBox: `0 0 1400 ${svgH}`, | ||
| style: { overflow: "visible" }, | ||
| children: svgParts | ||
| } | ||
| ) }); | ||
| } | ||
| // src/sections/Fallback.tsx | ||
| var import_jsx_runtime14 = require("react/jsx-runtime"); | ||
| function FallbackSectionView({ section }) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "af-fallback", children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [ | ||
| "Renderer for section type ", | ||
| /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: section.type }), | ||
| " is not yet implemented in this viewer." | ||
| ] }), | ||
| /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pre", { children: JSON.stringify(section.data, null, 2) }) | ||
| ] }); | ||
| } | ||
| // src/index.tsx | ||
| var import_jsx_runtime15 = require("react/jsx-runtime"); | ||
| var HostContext = (0, import_react2.createContext)(void 0); | ||
| var PluginsContext = (0, import_react2.createContext)([]); | ||
| function useHost() { | ||
| return (0, import_react2.useContext)(HostContext); | ||
| } | ||
| function usePlugins() { | ||
| return (0, import_react2.useContext)(PluginsContext); | ||
| } | ||
| function AgentRenderer({ | ||
@@ -1004,6 +845,7 @@ data, | ||
| host, | ||
| showOpenInViewer = true | ||
| showOpenInViewer = true, | ||
| plugins = [] | ||
| }) { | ||
| const sections = [...data.sections].sort((a, b) => a.order - b.order); | ||
| return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(HostContext.Provider, { value: host, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `af-root ${className ?? ""}`, children: [ | ||
| return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(HostContext.Provider, { value: host, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PluginsContext.Provider, { value: plugins, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `af-root ${className ?? ""}`, children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("header", { className: "af-header", children: [ | ||
@@ -1034,3 +876,3 @@ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "af-header-main", children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "af-sections", children: sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionFrame, { section }, section.id)) }) | ||
| ] }) }); | ||
| ] }) }) }); | ||
| } | ||
@@ -1079,5 +921,9 @@ function SectionFrame({ section }) { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ReferencesSectionView, { section }); | ||
| case "family-graph": | ||
| // `inheritance-diagram` is a deprecated alias for `family-graph`; | ||
| // both route to the same component so existing files keep rendering. | ||
| // eslint-disable-next-line no-fallthrough | ||
| case "inheritance-diagram": | ||
| return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)( | ||
| InheritanceDiagramSectionView, | ||
| FamilyGraphSectionView, | ||
| { | ||
@@ -1097,5 +943,7 @@ section, | ||
| downloadPrintableHtml, | ||
| findVariantComponent, | ||
| openInViewer, | ||
| useHost | ||
| useHost, | ||
| usePlugins | ||
| }); | ||
| //# sourceMappingURL=index.cjs.map |
+82
-12
| import * as react_jsx_runtime from 'react/jsx-runtime'; | ||
| import { ReactElement } from 'react'; | ||
| import { ComponentType, ReactElement } from 'react'; | ||
| type SectionType = 'kanban' | 'checklist' | 'notes' | 'timeline' | 'table' | 'log' | 'metrics' | 'diagram' | 'report' | 'form' | 'links' | 'references' | 'inheritance-diagram'; | ||
| type SectionType = 'kanban' | 'checklist' | 'notes' | 'timeline' | 'table' | 'log' | 'metrics' | 'diagram' | 'report' | 'form' | 'links' | 'references' | 'family-graph' | 'inheritance-diagram'; | ||
| interface SectionBase { | ||
@@ -233,3 +233,3 @@ id: string; | ||
| } | ||
| interface InheritanceDiagramPerson { | ||
| interface FamilyGraphPerson { | ||
| id: string; | ||
@@ -243,3 +243,3 @@ name: string; | ||
| } | ||
| interface InheritanceDiagramRelationship { | ||
| interface FamilyGraphRelationship { | ||
| type: 'spouse' | 'parent-child'; | ||
@@ -251,13 +251,37 @@ person1Id: string; | ||
| } | ||
| interface InheritanceDiagramData { | ||
| variant: string; | ||
| persons: InheritanceDiagramPerson[]; | ||
| relationships: InheritanceDiagramRelationship[]; | ||
| interface FamilyGraphData { | ||
| /** | ||
| * Optional style template. Core ships the default genealogy layout; known | ||
| * plugins can register additional variants (e.g. `jp-court` for the | ||
| * Japanese 相続関係説明図 template via `@agent-format/jp-court`). Unknown | ||
| * variants fall back to the default layout. | ||
| */ | ||
| variant?: string; | ||
| persons: FamilyGraphPerson[]; | ||
| relationships: FamilyGraphRelationship[]; | ||
| focusedPersonId?: string; | ||
| } | ||
| interface FamilyGraphSection extends SectionBase { | ||
| type: 'family-graph'; | ||
| data: FamilyGraphData; | ||
| } | ||
| /** | ||
| * @deprecated Use `FamilyGraphPerson`. Kept as a re-export so existing | ||
| * consumers compile while they migrate. | ||
| */ | ||
| type InheritanceDiagramPerson = FamilyGraphPerson; | ||
| /** @deprecated Use `FamilyGraphRelationship`. */ | ||
| type InheritanceDiagramRelationship = FamilyGraphRelationship; | ||
| /** @deprecated Use `FamilyGraphData`. */ | ||
| type InheritanceDiagramData = FamilyGraphData; | ||
| /** | ||
| * @deprecated Use `FamilyGraphSection` with `type: 'family-graph'`. The | ||
| * renderer still accepts the `inheritance-diagram` section type at runtime | ||
| * as a backward-compatible alias. | ||
| */ | ||
| interface InheritanceDiagramSection extends SectionBase { | ||
| type: 'inheritance-diagram'; | ||
| data: InheritanceDiagramData; | ||
| data: FamilyGraphData; | ||
| } | ||
| type Section = KanbanSection | ChecklistSection | NotesSection | TimelineSection | TableSection | LogSection | MetricsSection | DiagramSection | ReportSection | FormSection | LinksSection | ReferencesSection | InheritanceDiagramSection; | ||
| type Section = KanbanSection | ChecklistSection | NotesSection | TimelineSection | TableSection | LogSection | MetricsSection | DiagramSection | ReportSection | FormSection | LinksSection | ReferencesSection | FamilyGraphSection | InheritanceDiagramSection; | ||
| type AgentTaskTrigger = 'manual' | 'daily' | 'weekly'; | ||
@@ -313,2 +337,37 @@ interface AgentTask { | ||
| interface VariantRendererProps<S extends Section = Section> { | ||
| section: S; | ||
| /** | ||
| * Optional: let a plugin mount header-right action buttons (PDF export, | ||
| * format-specific toggles, etc.) via the same hook core renderers use. | ||
| * Safe to ignore. | ||
| */ | ||
| setHeaderActions?: (node: ReactElement | null) => void; | ||
| } | ||
| type VariantComponent = ComponentType<VariantRendererProps>; | ||
| interface RendererPlugin { | ||
| /** | ||
| * Package identifier, e.g. `@agent-format/jp-court`. Used for diagnostics | ||
| * and to de-duplicate if the same plugin gets passed twice. | ||
| */ | ||
| name: string; | ||
| /** | ||
| * Nested map: sectionType → variantName → component. Example: | ||
| * | ||
| * { | ||
| * 'family-graph': { | ||
| * 'jp-court': JPCourtFamilyGraphView, | ||
| * }, | ||
| * } | ||
| */ | ||
| variants?: Partial<Record<SectionType | string, Record<string, VariantComponent>>>; | ||
| } | ||
| /** | ||
| * Walk the supplied plugin list in order and return the first registered | ||
| * variant component for (sectionType, variant), or undefined if no plugin | ||
| * claims it. First-wins so app code can override upstream plugins by placing | ||
| * their plugin earlier in the array. | ||
| */ | ||
| declare function findVariantComponent(plugins: ReadonlyArray<RendererPlugin>, sectionType: string, variant: string | undefined): VariantComponent | undefined; | ||
| declare function openInViewer(data: AgentFile, host?: HostBridge): Promise<boolean>; | ||
@@ -333,2 +392,7 @@ declare function buildPrintableHtml({ svgMarkup, titleLabel, documentTitle, pageSize, margin, fontFamily, autoPrint, }: { | ||
| declare function useHost(): HostBridge | undefined; | ||
| /** | ||
| * Returns the plugin list provided to the nearest AgentRenderer. Section | ||
| * renderers use this to look up variant-specific components. | ||
| */ | ||
| declare function usePlugins(): ReadonlyArray<RendererPlugin>; | ||
| interface AgentRendererProps { | ||
@@ -349,4 +413,10 @@ data: AgentFile; | ||
| showOpenInViewer?: boolean; | ||
| /** | ||
| * Optional domain-specific plugins (e.g. `@agent-format/jp-court`) that | ||
| * register variant renderers for section types like `family-graph`. | ||
| * Earlier plugins win on conflicting `(sectionType, variant)` pairs. | ||
| */ | ||
| plugins?: ReadonlyArray<RendererPlugin>; | ||
| } | ||
| declare function AgentRenderer({ data, className, host, showOpenInViewer, }: AgentRendererProps): react_jsx_runtime.JSX.Element; | ||
| declare function AgentRenderer({ data, className, host, showOpenInViewer, plugins, }: AgentRendererProps): react_jsx_runtime.JSX.Element; | ||
| interface SectionViewExtras { | ||
@@ -361,2 +431,2 @@ /** | ||
| export { type AgentConfig, type AgentFile, type AgentMemory, AgentRenderer, type AgentTask, type AgentTaskTrigger, type ChecklistGroup, type ChecklistItem, type ChecklistSection, type DiagramNode, type DiagramSection, type FormField, type FormSection, type FormSubmission, type HostBridge, type InheritanceDiagramData, type InheritanceDiagramPerson, type InheritanceDiagramRelationship, type InheritanceDiagramSection, type KanbanColumn, type KanbanData, type KanbanItem, type KanbanItemComment, type KanbanLabel, type KanbanSection, type KanbanTeamMember, type LinkItem, type LinksSection, type LogEntry, type LogSection, type MetricCard, type MetricsSection, type NoteBlock, type NotesSection, type ReferenceFileItem, type ReferencesSection, type ReportEntry, type ReportSection, type Section, type SectionBase, type SectionType, type SectionViewExtras, type TableColumn, type TableColumnType, type TableSection, type TimelineItem, type TimelineMilestone, type TimelineSection, buildPrintableHtml, downloadPrintableHtml, openInViewer, useHost }; | ||
| export { type AgentConfig, type AgentFile, type AgentMemory, AgentRenderer, type AgentTask, type AgentTaskTrigger, type ChecklistGroup, type ChecklistItem, type ChecklistSection, type DiagramNode, type DiagramSection, type FamilyGraphData, type FamilyGraphPerson, type FamilyGraphRelationship, type FamilyGraphSection, type FormField, type FormSection, type FormSubmission, type HostBridge, type InheritanceDiagramData, type InheritanceDiagramPerson, type InheritanceDiagramRelationship, type InheritanceDiagramSection, type KanbanColumn, type KanbanData, type KanbanItem, type KanbanItemComment, type KanbanLabel, type KanbanSection, type KanbanTeamMember, type LinkItem, type LinksSection, type LogEntry, type LogSection, type MetricCard, type MetricsSection, type NoteBlock, type NotesSection, type ReferenceFileItem, type ReferencesSection, type RendererPlugin, type ReportEntry, type ReportSection, type Section, type SectionBase, type SectionType, type SectionViewExtras, type TableColumn, type TableColumnType, type TableSection, type TimelineItem, type TimelineMilestone, type TimelineSection, type VariantComponent, type VariantRendererProps, buildPrintableHtml, downloadPrintableHtml, findVariantComponent, openInViewer, useHost, usePlugins }; |
+82
-12
| import * as react_jsx_runtime from 'react/jsx-runtime'; | ||
| import { ReactElement } from 'react'; | ||
| import { ComponentType, ReactElement } from 'react'; | ||
| type SectionType = 'kanban' | 'checklist' | 'notes' | 'timeline' | 'table' | 'log' | 'metrics' | 'diagram' | 'report' | 'form' | 'links' | 'references' | 'inheritance-diagram'; | ||
| type SectionType = 'kanban' | 'checklist' | 'notes' | 'timeline' | 'table' | 'log' | 'metrics' | 'diagram' | 'report' | 'form' | 'links' | 'references' | 'family-graph' | 'inheritance-diagram'; | ||
| interface SectionBase { | ||
@@ -233,3 +233,3 @@ id: string; | ||
| } | ||
| interface InheritanceDiagramPerson { | ||
| interface FamilyGraphPerson { | ||
| id: string; | ||
@@ -243,3 +243,3 @@ name: string; | ||
| } | ||
| interface InheritanceDiagramRelationship { | ||
| interface FamilyGraphRelationship { | ||
| type: 'spouse' | 'parent-child'; | ||
@@ -251,13 +251,37 @@ person1Id: string; | ||
| } | ||
| interface InheritanceDiagramData { | ||
| variant: string; | ||
| persons: InheritanceDiagramPerson[]; | ||
| relationships: InheritanceDiagramRelationship[]; | ||
| interface FamilyGraphData { | ||
| /** | ||
| * Optional style template. Core ships the default genealogy layout; known | ||
| * plugins can register additional variants (e.g. `jp-court` for the | ||
| * Japanese 相続関係説明図 template via `@agent-format/jp-court`). Unknown | ||
| * variants fall back to the default layout. | ||
| */ | ||
| variant?: string; | ||
| persons: FamilyGraphPerson[]; | ||
| relationships: FamilyGraphRelationship[]; | ||
| focusedPersonId?: string; | ||
| } | ||
| interface FamilyGraphSection extends SectionBase { | ||
| type: 'family-graph'; | ||
| data: FamilyGraphData; | ||
| } | ||
| /** | ||
| * @deprecated Use `FamilyGraphPerson`. Kept as a re-export so existing | ||
| * consumers compile while they migrate. | ||
| */ | ||
| type InheritanceDiagramPerson = FamilyGraphPerson; | ||
| /** @deprecated Use `FamilyGraphRelationship`. */ | ||
| type InheritanceDiagramRelationship = FamilyGraphRelationship; | ||
| /** @deprecated Use `FamilyGraphData`. */ | ||
| type InheritanceDiagramData = FamilyGraphData; | ||
| /** | ||
| * @deprecated Use `FamilyGraphSection` with `type: 'family-graph'`. The | ||
| * renderer still accepts the `inheritance-diagram` section type at runtime | ||
| * as a backward-compatible alias. | ||
| */ | ||
| interface InheritanceDiagramSection extends SectionBase { | ||
| type: 'inheritance-diagram'; | ||
| data: InheritanceDiagramData; | ||
| data: FamilyGraphData; | ||
| } | ||
| type Section = KanbanSection | ChecklistSection | NotesSection | TimelineSection | TableSection | LogSection | MetricsSection | DiagramSection | ReportSection | FormSection | LinksSection | ReferencesSection | InheritanceDiagramSection; | ||
| type Section = KanbanSection | ChecklistSection | NotesSection | TimelineSection | TableSection | LogSection | MetricsSection | DiagramSection | ReportSection | FormSection | LinksSection | ReferencesSection | FamilyGraphSection | InheritanceDiagramSection; | ||
| type AgentTaskTrigger = 'manual' | 'daily' | 'weekly'; | ||
@@ -313,2 +337,37 @@ interface AgentTask { | ||
| interface VariantRendererProps<S extends Section = Section> { | ||
| section: S; | ||
| /** | ||
| * Optional: let a plugin mount header-right action buttons (PDF export, | ||
| * format-specific toggles, etc.) via the same hook core renderers use. | ||
| * Safe to ignore. | ||
| */ | ||
| setHeaderActions?: (node: ReactElement | null) => void; | ||
| } | ||
| type VariantComponent = ComponentType<VariantRendererProps>; | ||
| interface RendererPlugin { | ||
| /** | ||
| * Package identifier, e.g. `@agent-format/jp-court`. Used for diagnostics | ||
| * and to de-duplicate if the same plugin gets passed twice. | ||
| */ | ||
| name: string; | ||
| /** | ||
| * Nested map: sectionType → variantName → component. Example: | ||
| * | ||
| * { | ||
| * 'family-graph': { | ||
| * 'jp-court': JPCourtFamilyGraphView, | ||
| * }, | ||
| * } | ||
| */ | ||
| variants?: Partial<Record<SectionType | string, Record<string, VariantComponent>>>; | ||
| } | ||
| /** | ||
| * Walk the supplied plugin list in order and return the first registered | ||
| * variant component for (sectionType, variant), or undefined if no plugin | ||
| * claims it. First-wins so app code can override upstream plugins by placing | ||
| * their plugin earlier in the array. | ||
| */ | ||
| declare function findVariantComponent(plugins: ReadonlyArray<RendererPlugin>, sectionType: string, variant: string | undefined): VariantComponent | undefined; | ||
| declare function openInViewer(data: AgentFile, host?: HostBridge): Promise<boolean>; | ||
@@ -333,2 +392,7 @@ declare function buildPrintableHtml({ svgMarkup, titleLabel, documentTitle, pageSize, margin, fontFamily, autoPrint, }: { | ||
| declare function useHost(): HostBridge | undefined; | ||
| /** | ||
| * Returns the plugin list provided to the nearest AgentRenderer. Section | ||
| * renderers use this to look up variant-specific components. | ||
| */ | ||
| declare function usePlugins(): ReadonlyArray<RendererPlugin>; | ||
| interface AgentRendererProps { | ||
@@ -349,4 +413,10 @@ data: AgentFile; | ||
| showOpenInViewer?: boolean; | ||
| /** | ||
| * Optional domain-specific plugins (e.g. `@agent-format/jp-court`) that | ||
| * register variant renderers for section types like `family-graph`. | ||
| * Earlier plugins win on conflicting `(sectionType, variant)` pairs. | ||
| */ | ||
| plugins?: ReadonlyArray<RendererPlugin>; | ||
| } | ||
| declare function AgentRenderer({ data, className, host, showOpenInViewer, }: AgentRendererProps): react_jsx_runtime.JSX.Element; | ||
| declare function AgentRenderer({ data, className, host, showOpenInViewer, plugins, }: AgentRendererProps): react_jsx_runtime.JSX.Element; | ||
| interface SectionViewExtras { | ||
@@ -361,2 +431,2 @@ /** | ||
| export { type AgentConfig, type AgentFile, type AgentMemory, AgentRenderer, type AgentTask, type AgentTaskTrigger, type ChecklistGroup, type ChecklistItem, type ChecklistSection, type DiagramNode, type DiagramSection, type FormField, type FormSection, type FormSubmission, type HostBridge, type InheritanceDiagramData, type InheritanceDiagramPerson, type InheritanceDiagramRelationship, type InheritanceDiagramSection, type KanbanColumn, type KanbanData, type KanbanItem, type KanbanItemComment, type KanbanLabel, type KanbanSection, type KanbanTeamMember, type LinkItem, type LinksSection, type LogEntry, type LogSection, type MetricCard, type MetricsSection, type NoteBlock, type NotesSection, type ReferenceFileItem, type ReferencesSection, type ReportEntry, type ReportSection, type Section, type SectionBase, type SectionType, type SectionViewExtras, type TableColumn, type TableColumnType, type TableSection, type TimelineItem, type TimelineMilestone, type TimelineSection, buildPrintableHtml, downloadPrintableHtml, openInViewer, useHost }; | ||
| export { type AgentConfig, type AgentFile, type AgentMemory, AgentRenderer, type AgentTask, type AgentTaskTrigger, type ChecklistGroup, type ChecklistItem, type ChecklistSection, type DiagramNode, type DiagramSection, type FamilyGraphData, type FamilyGraphPerson, type FamilyGraphRelationship, type FamilyGraphSection, type FormField, type FormSection, type FormSubmission, type HostBridge, type InheritanceDiagramData, type InheritanceDiagramPerson, type InheritanceDiagramRelationship, type InheritanceDiagramSection, type KanbanColumn, type KanbanData, type KanbanItem, type KanbanItemComment, type KanbanLabel, type KanbanSection, type KanbanTeamMember, type LinkItem, type LinksSection, type LogEntry, type LogSection, type MetricCard, type MetricsSection, type NoteBlock, type NotesSection, type ReferenceFileItem, type ReferencesSection, type RendererPlugin, type ReportEntry, type ReportSection, type Section, type SectionBase, type SectionType, type SectionViewExtras, type TableColumn, type TableColumnType, type TableSection, type TimelineItem, type TimelineMilestone, type TimelineSection, type VariantComponent, type VariantRendererProps, buildPrintableHtml, downloadPrintableHtml, findVariantComponent, openInViewer, useHost, usePlugins }; |
+305
-459
@@ -392,5 +392,292 @@ // src/index.tsx | ||
| // src/sections/InheritanceDiagramSection.tsx | ||
| import { useEffect, useRef } from "react"; | ||
| // src/sections/FamilyGraphSection.tsx | ||
| import { useMemo } from "react"; | ||
| // src/plugins.ts | ||
| function findVariantComponent(plugins, sectionType, variant) { | ||
| if (!variant) return void 0; | ||
| for (const plugin of plugins) { | ||
| const component = plugin.variants?.[sectionType]?.[variant]; | ||
| if (component) return component; | ||
| } | ||
| return void 0; | ||
| } | ||
| // src/sections/FamilyGraphSection.tsx | ||
| import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime"; | ||
| var MAX_GENERATIONS = 8; | ||
| var CARD_W = 220; | ||
| var CARD_H = 120; | ||
| var COL_GAP = 40; | ||
| var ROW_GAP = 80; | ||
| var MARGIN_X = 20; | ||
| var MARGIN_Y = 20; | ||
| function FamilyGraphSectionView({ section, setHeaderActions }) { | ||
| const plugins = usePlugins(); | ||
| const variant = section.data?.variant; | ||
| const VariantComponent = findVariantComponent(plugins, "family-graph", variant); | ||
| if (VariantComponent) { | ||
| return /* @__PURE__ */ jsx13(VariantComponent, { section, setHeaderActions }); | ||
| } | ||
| if (section.type === "inheritance-diagram") { | ||
| const AliasComponent = findVariantComponent(plugins, "inheritance-diagram", variant); | ||
| if (AliasComponent) { | ||
| return /* @__PURE__ */ jsx13(AliasComponent, { section, setHeaderActions }); | ||
| } | ||
| } | ||
| return /* @__PURE__ */ jsx13(DefaultGenealogy, { section }); | ||
| } | ||
| function DefaultGenealogy({ section }) { | ||
| const persons = section.data?.persons ?? []; | ||
| const rels = section.data?.relationships ?? []; | ||
| const focusedId = section.data?.focusedPersonId; | ||
| const layout = useMemo(() => computeLayout(persons, rels), [persons, rels]); | ||
| if (persons.length === 0) { | ||
| return /* @__PURE__ */ jsx13("p", { className: "af-empty", children: "No persons in diagram." }); | ||
| } | ||
| return /* @__PURE__ */ jsx13("div", { className: "af-family-graph", style: { overflow: "auto" }, children: /* @__PURE__ */ jsxs12( | ||
| "svg", | ||
| { | ||
| xmlns: "http://www.w3.org/2000/svg", | ||
| width: layout.width, | ||
| height: layout.height, | ||
| viewBox: `0 0 ${layout.width} ${layout.height}`, | ||
| role: "img", | ||
| "aria-label": "Family graph", | ||
| children: [ | ||
| layout.parentChildEdges.map((edge, i) => { | ||
| const p = cardById(layout, edge.parent); | ||
| const c = cardById(layout, edge.child); | ||
| if (!p || !c) return null; | ||
| const px = p.x + CARD_W / 2; | ||
| const py = p.y + CARD_H; | ||
| const cx = c.x + CARD_W / 2; | ||
| const cy = c.y; | ||
| const midY = (py + cy) / 2; | ||
| return /* @__PURE__ */ jsx13( | ||
| "polyline", | ||
| { | ||
| points: `${px},${py} ${px},${midY} ${cx},${midY} ${cx},${cy}`, | ||
| fill: "none", | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| opacity: "0.6" | ||
| }, | ||
| `pc-${i}` | ||
| ); | ||
| }), | ||
| layout.spouseEdges.map((edge, i) => { | ||
| const a = cardById(layout, edge.a); | ||
| const b = cardById(layout, edge.b); | ||
| if (!a || !b) return null; | ||
| const ay = a.y + CARD_H / 2; | ||
| const by = b.y + CARD_H / 2; | ||
| const ax = a.x + CARD_W; | ||
| const bx = b.x; | ||
| if (a.y !== b.y) { | ||
| return /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: a.x + CARD_W / 2, | ||
| y1: ay, | ||
| x2: b.x + CARD_W / 2, | ||
| y2: by, | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| strokeDasharray: edge.dissolved ? "4 3" : void 0, | ||
| opacity: "0.6" | ||
| }, | ||
| `sp-${i}` | ||
| ); | ||
| } | ||
| const mid = (ay + by) / 2; | ||
| return /* @__PURE__ */ jsxs12("g", { children: [ | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: ax, | ||
| y1: mid - 3, | ||
| x2: bx, | ||
| y2: mid - 3, | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| strokeDasharray: edge.dissolved ? "4 3" : void 0, | ||
| opacity: "0.8" | ||
| } | ||
| ), | ||
| !edge.dissolved && /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: ax, | ||
| y1: mid + 3, | ||
| x2: bx, | ||
| y2: mid + 3, | ||
| stroke: "currentColor", | ||
| strokeWidth: "1.2", | ||
| opacity: "0.8" | ||
| } | ||
| ) | ||
| ] }, `sp-${i}`); | ||
| }), | ||
| layout.cards.map((card) => /* @__PURE__ */ jsx13( | ||
| PersonCard, | ||
| { | ||
| person: card.person, | ||
| x: card.x, | ||
| y: card.y, | ||
| focused: card.id === focusedId | ||
| }, | ||
| card.id | ||
| )) | ||
| ] | ||
| } | ||
| ) }); | ||
| } | ||
| function cardById(layout, id) { | ||
| return layout.cards.find((c) => c.id === id); | ||
| } | ||
| function PersonCard({ | ||
| person, | ||
| x, | ||
| y, | ||
| focused | ||
| }) { | ||
| return /* @__PURE__ */ jsxs12("g", { transform: `translate(${x}, ${y})`, children: [ | ||
| /* @__PURE__ */ jsx13( | ||
| "rect", | ||
| { | ||
| width: CARD_W, | ||
| height: CARD_H, | ||
| rx: 8, | ||
| fill: focused ? "var(--af-accent-soft, #eef2ff)" : "var(--af-bg-alt, #f7f7f8)", | ||
| stroke: focused ? "var(--af-accent, #2251ff)" : "var(--af-border, #e5e7eb)", | ||
| strokeWidth: focused ? 2 : 1 | ||
| } | ||
| ), | ||
| /* @__PURE__ */ jsx13("text", { x: 12, y: 24, fontSize: "14", fontWeight: "600", fill: "currentColor", children: person.name }), | ||
| person.role && /* @__PURE__ */ jsx13( | ||
| "text", | ||
| { | ||
| x: 12, | ||
| y: 44, | ||
| fontSize: "11", | ||
| fill: "currentColor", | ||
| opacity: "0.7", | ||
| children: person.role | ||
| } | ||
| ), | ||
| person.birthday && /* @__PURE__ */ jsxs12("text", { x: 12, y: 66, fontSize: "11", fill: "currentColor", opacity: "0.7", children: [ | ||
| "b. ", | ||
| person.birthday | ||
| ] }), | ||
| person.deathDate && /* @__PURE__ */ jsxs12("text", { x: 12, y: 82, fontSize: "11", fill: "currentColor", opacity: "0.7", children: [ | ||
| "d. ", | ||
| person.deathDate | ||
| ] }), | ||
| person.address && /* @__PURE__ */ jsx13("text", { x: 12, y: 100, fontSize: "10", fill: "currentColor", opacity: "0.55", children: truncate(person.address, 30) }) | ||
| ] }); | ||
| } | ||
| function truncate(s, max) { | ||
| return s.length <= max ? s : s.slice(0, max - 1) + "\u2026"; | ||
| } | ||
| function computeLayout(persons, rels) { | ||
| const byId = new Map(persons.map((p) => [p.id, p])); | ||
| const childrenOf = /* @__PURE__ */ new Map(); | ||
| const parentsOf = /* @__PURE__ */ new Map(); | ||
| const spouseEdges = []; | ||
| for (const r of rels) { | ||
| if (r.type === "parent-child") { | ||
| if (!byId.has(r.person1Id) || !byId.has(r.person2Id)) continue; | ||
| if (!childrenOf.has(r.person1Id)) childrenOf.set(r.person1Id, []); | ||
| childrenOf.get(r.person1Id).push(r.person2Id); | ||
| if (!parentsOf.has(r.person2Id)) parentsOf.set(r.person2Id, []); | ||
| parentsOf.get(r.person2Id).push(r.person1Id); | ||
| } else if (r.type === "spouse") { | ||
| if (!byId.has(r.person1Id) || !byId.has(r.person2Id)) continue; | ||
| spouseEdges.push({ | ||
| a: r.person1Id, | ||
| b: r.person2Id, | ||
| dissolved: r.dissolved | ||
| }); | ||
| } | ||
| } | ||
| const depth = /* @__PURE__ */ new Map(); | ||
| const seeds = persons.filter((p) => !parentsOf.has(p.id)).map((p) => p.id); | ||
| const queue = seeds.map((id) => ({ id, d: 0 })); | ||
| while (queue.length > 0) { | ||
| const { id, d } = queue.shift(); | ||
| if (d > MAX_GENERATIONS) continue; | ||
| if (depth.has(id)) continue; | ||
| depth.set(id, d); | ||
| for (const childId of childrenOf.get(id) ?? []) { | ||
| if (!depth.has(childId)) queue.push({ id: childId, d: d + 1 }); | ||
| } | ||
| } | ||
| for (const s of spouseEdges) { | ||
| if (!depth.has(s.a) && depth.has(s.b)) depth.set(s.a, depth.get(s.b)); | ||
| if (!depth.has(s.b) && depth.has(s.a)) depth.set(s.b, depth.get(s.a)); | ||
| } | ||
| for (const p of persons) { | ||
| if (!depth.has(p.id)) depth.set(p.id, 0); | ||
| } | ||
| const byGen = /* @__PURE__ */ new Map(); | ||
| for (const p of persons) { | ||
| const d = depth.get(p.id); | ||
| if (!byGen.has(d)) byGen.set(d, []); | ||
| byGen.get(d).push(p); | ||
| } | ||
| const sortedGens = Array.from(byGen.entries()).sort((a, b) => a[0] - b[0]); | ||
| for (const [, row] of sortedGens) { | ||
| placeSpousesAdjacent(row, spouseEdges); | ||
| } | ||
| const rowCount = Math.max(...sortedGens.map(([, r]) => r.length), 1); | ||
| const rowWidth = rowCount * CARD_W + (rowCount - 1) * COL_GAP; | ||
| const cards = []; | ||
| sortedGens.forEach(([d, row]) => { | ||
| const y = MARGIN_Y + d * (CARD_H + ROW_GAP); | ||
| const thisRowWidth = row.length * CARD_W + (row.length - 1) * COL_GAP; | ||
| const offsetX = MARGIN_X + (rowWidth - thisRowWidth) / 2; | ||
| row.forEach((p, i) => { | ||
| cards.push({ | ||
| id: p.id, | ||
| person: p, | ||
| x: offsetX + i * (CARD_W + COL_GAP), | ||
| y | ||
| }); | ||
| }); | ||
| }); | ||
| const width = MARGIN_X * 2 + rowWidth; | ||
| const height = MARGIN_Y * 2 + sortedGens.length * CARD_H + Math.max(sortedGens.length - 1, 0) * ROW_GAP; | ||
| const parentChildEdges = []; | ||
| for (const r of rels) { | ||
| if (r.type !== "parent-child") continue; | ||
| if (!byId.has(r.person1Id) || !byId.has(r.person2Id)) continue; | ||
| parentChildEdges.push({ parent: r.person1Id, child: r.person2Id }); | ||
| } | ||
| return { cards, parentChildEdges, spouseEdges, width, height }; | ||
| } | ||
| function placeSpousesAdjacent(row, spouseEdges) { | ||
| for (const edge of spouseEdges) { | ||
| const ai = row.findIndex((p) => p.id === edge.a); | ||
| const bi = row.findIndex((p) => p.id === edge.b); | ||
| if (ai < 0 || bi < 0 || Math.abs(ai - bi) === 1) continue; | ||
| const [b] = row.splice(bi, 1); | ||
| const insertAt = ai < bi ? ai + 1 : ai; | ||
| row.splice(insertAt, 0, b); | ||
| } | ||
| } | ||
| // src/sections/Fallback.tsx | ||
| import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime"; | ||
| function FallbackSectionView({ section }) { | ||
| return /* @__PURE__ */ jsxs13("div", { className: "af-fallback", children: [ | ||
| /* @__PURE__ */ jsxs13("div", { children: [ | ||
| "Renderer for section type ", | ||
| /* @__PURE__ */ jsx14("strong", { children: section.type }), | ||
| " is not yet implemented in this viewer." | ||
| ] }), | ||
| /* @__PURE__ */ jsx14("pre", { children: JSON.stringify(section.data, null, 2) }) | ||
| ] }); | ||
| } | ||
| // src/host.ts | ||
@@ -512,460 +799,12 @@ function fallbackOpenLink(url) { | ||
| // src/sections/InheritanceDiagramSection.tsx | ||
| import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime"; | ||
| var MAX_GENERATIONS = 6; | ||
| var LINE_H = 22; | ||
| var NAME_SIZE = 16; | ||
| var NAME_LINE_H = 22; | ||
| var DBL_GAP = 4; | ||
| var HORIZ_Y_GAP = 90; | ||
| var CHILD_V_GAP = 30; | ||
| var GEN_X_STEP = 340; | ||
| var SPOUSE_GAP = 90; | ||
| var TEXT_X = 60; | ||
| var CHILD_TEXT_X = 480; | ||
| var TRUNK_X = CHILD_TEXT_X - 30; | ||
| var DBL_X = 80; | ||
| var DEC_TOP_Y = 20; | ||
| function InheritanceDiagramSectionView({ section, setHeaderActions }) { | ||
| const data = section.data; | ||
| const persons = data?.persons ?? []; | ||
| const rels = data?.relationships ?? []; | ||
| const variant = data?.variant ?? "jp-court"; | ||
| const svgRef = useRef(null); | ||
| const host = useHost(); | ||
| useEffect(() => { | ||
| if (!setHeaderActions) return; | ||
| if (!svgRef.current) return; | ||
| if (persons.length === 0 || variant !== "jp-court") { | ||
| setHeaderActions(null); | ||
| return; | ||
| } | ||
| const sectionLabel = section.label || "\u76F8\u7D9A\u95A2\u4FC2\u8AAC\u660E\u56F3"; | ||
| const documentTitle = sectionLabel; | ||
| const headerTitle = "\u76F8 \u7D9A \u95A2 \u4FC2 \u8AAC \u660E \u56F3"; | ||
| const onClick = () => { | ||
| const svgEl = svgRef.current; | ||
| if (!svgEl) return; | ||
| const serialized = new XMLSerializer().serializeToString(svgEl); | ||
| const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10); | ||
| void downloadPrintableHtml({ | ||
| svgMarkup: serialized, | ||
| titleLabel: headerTitle, | ||
| documentTitle, | ||
| filename: `inheritance-diagram-${today}.html`, | ||
| host | ||
| }); | ||
| }; | ||
| setHeaderActions( | ||
| /* @__PURE__ */ jsxs12( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| className: "af-action-btn", | ||
| onClick, | ||
| title: "\u5370\u5237\u30FBPDF \u4FDD\u5B58\u7528\u306E HTML \u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\uFF08\u958B\u3044\u3066 \u2318P \u3067 PDF \u4FDD\u5B58\u3001\u88C1\u5224\u6240\u63D0\u51FA\u7528 A3 \u6A2A\u66F8\u5F0F\uFF09", | ||
| children: [ | ||
| /* @__PURE__ */ jsx13("span", { "aria-hidden": true, children: "\u2B07" }), | ||
| /* @__PURE__ */ jsx13("span", { children: "PDF" }) | ||
| ] | ||
| } | ||
| ) | ||
| ); | ||
| return () => setHeaderActions(null); | ||
| }, [setHeaderActions, section.id, section.label, persons.length, variant, host]); | ||
| if (persons.length === 0) { | ||
| return /* @__PURE__ */ jsx13("p", { className: "af-empty", children: "No persons in diagram." }); | ||
| } | ||
| if (variant !== "jp-court") { | ||
| return /* @__PURE__ */ jsxs12("p", { className: "af-empty", children: [ | ||
| 'Inheritance-diagram variant "', | ||
| variant, | ||
| '" is not yet implemented. Only "jp-court" is supported in v0.1.' | ||
| ] }); | ||
| } | ||
| const focused = data?.focusedPersonId ? persons.find((p) => p.id === data.focusedPersonId) : null; | ||
| const decedent = focused || persons.find((p) => Boolean(p.deathDate)) || persons[0]; | ||
| if (!decedent) return /* @__PURE__ */ jsx13("p", { className: "af-empty", children: "No decedent." }); | ||
| const findSpouseOfRoot = () => { | ||
| const rel = rels.find( | ||
| (r) => r.type === "spouse" && (r.person1Id === decedent.id || r.person2Id === decedent.id) | ||
| ); | ||
| if (!rel) return null; | ||
| const sid = rel.person1Id === decedent.id ? rel.person2Id : rel.person1Id; | ||
| return persons.find((p) => p.id === sid) ?? null; | ||
| }; | ||
| const findChildren = (parentId, spouseId) => { | ||
| const pids = /* @__PURE__ */ new Set([parentId]); | ||
| if (spouseId) pids.add(spouseId); | ||
| const kids = []; | ||
| const seen = /* @__PURE__ */ new Set(); | ||
| for (const r of rels) { | ||
| if (r.type === "parent-child" && pids.has(r.person1Id)) { | ||
| if (seen.has(r.person2Id)) continue; | ||
| const c = persons.find((p) => p.id === r.person2Id); | ||
| if (c) { | ||
| seen.add(r.person2Id); | ||
| kids.push(c); | ||
| } | ||
| } | ||
| } | ||
| return kids; | ||
| }; | ||
| const findSpouse = (personId) => { | ||
| const rel = rels.find( | ||
| (r) => r.type === "spouse" && r.person1Id !== decedent.id && r.person2Id !== decedent.id && (r.person1Id === personId || r.person2Id === personId) | ||
| ); | ||
| if (!rel) return null; | ||
| const sid = rel.person1Id === personId ? rel.person2Id : rel.person1Id; | ||
| return persons.find((p) => p.id === sid) ?? null; | ||
| }; | ||
| const blockHeight = (p) => { | ||
| let n = 0; | ||
| if (p.address) n++; | ||
| if (p.birthday) n++; | ||
| if (p.deathDate) n++; | ||
| n++; | ||
| return n * LINE_H + NAME_LINE_H; | ||
| }; | ||
| let elementKey = 0; | ||
| const nextKey = () => `el-${++elementKey}`; | ||
| const renderBlock = (p, x, topY, roleLabel) => { | ||
| const elements = []; | ||
| let y = topY; | ||
| if (p.address) { | ||
| const lbl = roleLabel === "\u88AB\u76F8\u7D9A\u4EBA" ? "\u6700\u5F8C\u306E\u4F4F\u6240" : "\u4F4F\u6240"; | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13("text", { x, y, fontSize: "11pt", children: `${lbl}\u3000${p.address}` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| } | ||
| if (p.birthday) { | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13("text", { x, y, fontSize: "11pt", children: `\u51FA\u751F\u3000${p.birthday}` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| } | ||
| if (p.deathDate) { | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13("text", { x, y, fontSize: "11pt", children: `\u6B7B\u4EA1\u3000${p.deathDate}` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| } | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13("text", { x: x + 8, y, fontSize: "11pt", children: `\uFF08${roleLabel}\uFF09` }, nextKey()) | ||
| ); | ||
| y += LINE_H; | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "text", | ||
| { | ||
| x, | ||
| y, | ||
| fontSize: `${NAME_SIZE}pt`, | ||
| fontWeight: "bold", | ||
| letterSpacing: "0.2em", | ||
| children: p.name | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| const nameBaseY = y; | ||
| y += NAME_LINE_H; | ||
| return { elements, nameBaseY, blockEndY: y }; | ||
| }; | ||
| const measureChildSlot = (c, depth) => { | ||
| if (depth > MAX_GENERATIONS) return blockHeight(c); | ||
| let h = blockHeight(c); | ||
| const sp = findSpouse(c.id); | ||
| if (sp) h += SPOUSE_GAP + blockHeight(sp); | ||
| const grandkids = findChildren(c.id, sp ? sp.id : null); | ||
| if (grandkids.length > 0) { | ||
| const subH = measureChildGroup(grandkids, depth + 1); | ||
| h = Math.max(h, subH); | ||
| } | ||
| return h; | ||
| }; | ||
| const measureChildGroup = (childList, depth) => { | ||
| let total = 0; | ||
| childList.forEach((c, i) => { | ||
| if (i > 0) total += CHILD_V_GAP; | ||
| total += measureChildSlot(c, depth); | ||
| }); | ||
| return total; | ||
| }; | ||
| const lineProps = { | ||
| stroke: "#000", | ||
| strokeWidth: 1.2 | ||
| }; | ||
| const renderChildGroup = (childList, textX, startY, depth) => { | ||
| const elements = []; | ||
| const nameYs = []; | ||
| let cy = startY; | ||
| if (depth > MAX_GENERATIONS) { | ||
| elements.push( | ||
| /* @__PURE__ */ jsxs12( | ||
| "text", | ||
| { | ||
| x: textX, | ||
| y: cy, | ||
| fontSize: "11pt", | ||
| fill: "#999", | ||
| children: [ | ||
| "\u2026 (tree truncated at generation ", | ||
| MAX_GENERATIONS, | ||
| ")" | ||
| ] | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| return { elements, nameYs, endY: cy + LINE_H }; | ||
| } | ||
| childList.forEach((c, i) => { | ||
| if (i > 0) cy += CHILD_V_GAP; | ||
| const cb = renderBlock(c, textX, cy, c.role || "\u76F8\u7D9A\u4EBA"); | ||
| elements.push(...cb.elements); | ||
| nameYs.push(cb.nameBaseY); | ||
| const childSpouse = findSpouse(c.id); | ||
| let connectorY = cb.nameBaseY; | ||
| if (childSpouse) { | ||
| const spTopY = cb.blockEndY + SPOUSE_GAP; | ||
| const spBlock2 = renderBlock( | ||
| childSpouse, | ||
| textX, | ||
| spTopY, | ||
| childSpouse.role || "\u914D\u5076\u8005" | ||
| ); | ||
| elements.push(...spBlock2.elements); | ||
| const miniDblX = textX + 20; | ||
| const miniGapTop = cb.blockEndY + 5; | ||
| const miniGapBot = spTopY - 25; | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: miniDblX - DBL_GAP, | ||
| y1: miniGapTop, | ||
| x2: miniDblX - DBL_GAP, | ||
| y2: miniGapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: miniDblX + DBL_GAP, | ||
| y1: miniGapTop, | ||
| x2: miniDblX + DBL_GAP, | ||
| y2: miniGapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| connectorY = cb.blockEndY + SPOUSE_GAP / 2; | ||
| } | ||
| const grandkids = findChildren(c.id, childSpouse ? childSpouse.id : null); | ||
| if (grandkids.length > 0) { | ||
| const gcTextX = textX + GEN_X_STEP; | ||
| const gcTrunkX = gcTextX - 30; | ||
| const gcResult = renderChildGroup(grandkids, gcTextX, cy, depth + 1); | ||
| elements.push(...gcResult.elements); | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: textX + 180, | ||
| y1: connectorY, | ||
| x2: gcTrunkX, | ||
| y2: connectorY, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| if (gcResult.nameYs.length > 0) { | ||
| const gcTrunkTop = Math.min(connectorY, gcResult.nameYs[0]); | ||
| const gcTrunkBot = Math.max( | ||
| connectorY, | ||
| gcResult.nameYs[gcResult.nameYs.length - 1] | ||
| ); | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: gcTrunkX, | ||
| y1: gcTrunkTop, | ||
| x2: gcTrunkX, | ||
| y2: gcTrunkBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| gcResult.nameYs.forEach((gny) => { | ||
| elements.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: gcTrunkX, | ||
| y1: gny, | ||
| x2: gcTextX - 5, | ||
| y2: gny, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| }); | ||
| } | ||
| } | ||
| cy += measureChildSlot(c, depth); | ||
| }); | ||
| return { elements, nameYs, endY: cy }; | ||
| }; | ||
| const dec = renderBlock(decedent, TEXT_X, DEC_TOP_Y, "\u88AB\u76F8\u7D9A\u4EBA"); | ||
| const spouse = findSpouseOfRoot(); | ||
| const spouseTopY = dec.blockEndY + HORIZ_Y_GAP; | ||
| const spBlock = spouse ? renderBlock(spouse, TEXT_X, spouseTopY, spouse.role || "\u76F8\u7D9A\u4EBA") : null; | ||
| const dblMidY = dec.blockEndY + HORIZ_Y_GAP / 2; | ||
| const children = findChildren(decedent.id, spouse ? spouse.id : null); | ||
| const totalChildH = children.length > 0 ? measureChildGroup(children, 0) : 0; | ||
| let childGroupTopY = dblMidY - totalChildH / 2; | ||
| if (childGroupTopY < 10) childGroupTopY = 10; | ||
| const childResult = children.length > 0 ? renderChildGroup(children, CHILD_TEXT_X, childGroupTopY, 0) : { elements: [], nameYs: [], endY: childGroupTopY }; | ||
| const svgH = Math.max(spBlock ? spBlock.blockEndY : dec.blockEndY, childResult.endY) + 30; | ||
| const svgParts = []; | ||
| svgParts.push(...dec.elements); | ||
| if (spBlock) svgParts.push(...spBlock.elements); | ||
| if (spouse) { | ||
| const gapTop = dec.blockEndY + 5; | ||
| const gapBot = spouseTopY - 25; | ||
| svgParts.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: DBL_X - DBL_GAP, | ||
| y1: gapTop, | ||
| x2: DBL_X - DBL_GAP, | ||
| y2: gapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| svgParts.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: DBL_X + DBL_GAP, | ||
| y1: gapTop, | ||
| x2: DBL_X + DBL_GAP, | ||
| y2: gapBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| if (children.length > 0) { | ||
| svgParts.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: DBL_X + DBL_GAP, | ||
| y1: dblMidY, | ||
| x2: TRUNK_X, | ||
| y2: dblMidY, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| } | ||
| } else if (children.length > 0) { | ||
| svgParts.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: TEXT_X + 200, | ||
| y1: dec.nameBaseY, | ||
| x2: TRUNK_X, | ||
| y2: dec.nameBaseY, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| } | ||
| svgParts.push(...childResult.elements); | ||
| if (childResult.nameYs.length > 0) { | ||
| const trunkTop = Math.min( | ||
| spouse ? dblMidY : dec.nameBaseY, | ||
| childResult.nameYs[0] | ||
| ); | ||
| const trunkBot = childResult.nameYs[childResult.nameYs.length - 1]; | ||
| svgParts.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: TRUNK_X, | ||
| y1: trunkTop, | ||
| x2: TRUNK_X, | ||
| y2: trunkBot, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| childResult.nameYs.forEach((cny) => { | ||
| svgParts.push( | ||
| /* @__PURE__ */ jsx13( | ||
| "line", | ||
| { | ||
| x1: TRUNK_X, | ||
| y1: cny, | ||
| x2: CHILD_TEXT_X - 5, | ||
| y2: cny, | ||
| ...lineProps | ||
| }, | ||
| nextKey() | ||
| ) | ||
| ); | ||
| }); | ||
| } | ||
| return /* @__PURE__ */ jsx13("div", { className: "af-inheritance-diagram", children: /* @__PURE__ */ jsx13( | ||
| "svg", | ||
| { | ||
| ref: svgRef, | ||
| xmlns: "http://www.w3.org/2000/svg", | ||
| width: "100%", | ||
| viewBox: `0 0 1400 ${svgH}`, | ||
| style: { overflow: "visible" }, | ||
| children: svgParts | ||
| } | ||
| ) }); | ||
| } | ||
| // src/sections/Fallback.tsx | ||
| import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime"; | ||
| function FallbackSectionView({ section }) { | ||
| return /* @__PURE__ */ jsxs13("div", { className: "af-fallback", children: [ | ||
| /* @__PURE__ */ jsxs13("div", { children: [ | ||
| "Renderer for section type ", | ||
| /* @__PURE__ */ jsx14("strong", { children: section.type }), | ||
| " is not yet implemented in this viewer." | ||
| ] }), | ||
| /* @__PURE__ */ jsx14("pre", { children: JSON.stringify(section.data, null, 2) }) | ||
| ] }); | ||
| } | ||
| // src/index.tsx | ||
| import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime"; | ||
| var HostContext = createContext(void 0); | ||
| var PluginsContext = createContext([]); | ||
| function useHost() { | ||
| return useContext(HostContext); | ||
| } | ||
| function usePlugins() { | ||
| return useContext(PluginsContext); | ||
| } | ||
| function AgentRenderer({ | ||
@@ -975,6 +814,7 @@ data, | ||
| host, | ||
| showOpenInViewer = true | ||
| showOpenInViewer = true, | ||
| plugins = [] | ||
| }) { | ||
| const sections = [...data.sections].sort((a, b) => a.order - b.order); | ||
| return /* @__PURE__ */ jsx15(HostContext.Provider, { value: host, children: /* @__PURE__ */ jsxs14("div", { className: `af-root ${className ?? ""}`, children: [ | ||
| return /* @__PURE__ */ jsx15(HostContext.Provider, { value: host, children: /* @__PURE__ */ jsx15(PluginsContext.Provider, { value: plugins, children: /* @__PURE__ */ jsxs14("div", { className: `af-root ${className ?? ""}`, children: [ | ||
| /* @__PURE__ */ jsxs14("header", { className: "af-header", children: [ | ||
@@ -1005,3 +845,3 @@ /* @__PURE__ */ jsxs14("div", { className: "af-header-main", children: [ | ||
| /* @__PURE__ */ jsx15("div", { className: "af-sections", children: sections.map((section) => /* @__PURE__ */ jsx15(SectionFrame, { section }, section.id)) }) | ||
| ] }) }); | ||
| ] }) }) }); | ||
| } | ||
@@ -1050,5 +890,9 @@ function SectionFrame({ section }) { | ||
| return /* @__PURE__ */ jsx15(ReferencesSectionView, { section }); | ||
| case "family-graph": | ||
| // `inheritance-diagram` is a deprecated alias for `family-graph`; | ||
| // both route to the same component so existing files keep rendering. | ||
| // eslint-disable-next-line no-fallthrough | ||
| case "inheritance-diagram": | ||
| return /* @__PURE__ */ jsx15( | ||
| InheritanceDiagramSectionView, | ||
| FamilyGraphSectionView, | ||
| { | ||
@@ -1067,5 +911,7 @@ section, | ||
| downloadPrintableHtml, | ||
| findVariantComponent, | ||
| openInViewer, | ||
| useHost | ||
| useHost, | ||
| usePlugins | ||
| }; | ||
| //# sourceMappingURL=index.js.map |
+1
-1
| { | ||
| "name": "@agent-format/renderer", | ||
| "version": "0.1.4", | ||
| "version": "0.1.5", | ||
| "description": "React renderer for the agent file format (.agent).", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
288984
-1.32%2903
-7.55%