diff --git a/src/graph/GraphView.ts b/src/graph/GraphView.ts index c9b94c1..b67d449 100644 --- a/src/graph/GraphView.ts +++ b/src/graph/GraphView.ts @@ -19,15 +19,15 @@ export class PeopleGraphView extends ItemView { } getDisplayText(): string { - return "People Graph"; + return "People graph"; } getIcon(): string { return "users"; } - async onOpen() { - this.render(); + async onOpen(): Promise { + await this.render(); // Export button in view header this.addAction("download", "Export as PNG", async () => { @@ -49,18 +49,18 @@ export class PeopleGraphView extends ItemView { // Re-render when a note's frontmatter changes this.registerEvent( this.app.metadataCache.on("changed", () => { - this.render(); + void this.render(); }), ); } - async render() { + async render(): Promise { const container = this.containerEl.children[1] as HTMLElement; const people = await indexPeople(this.app, this.plugin.settings); renderGraph(container, people, this.plugin.settings, this.app); } - async onClose() { + async onClose(): Promise { const container = this.containerEl.children[1] as HTMLElement; container.empty(); } diff --git a/src/graph/export.ts b/src/graph/export.ts index abe4be2..96b605e 100644 --- a/src/graph/export.ts +++ b/src/graph/export.ts @@ -13,12 +13,10 @@ export async function exportGraphAsPng(container: HTMLElement): Promise { + textEl.setAttribute("font-family", "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"); + }); const serializer = new XMLSerializer(); const svgString = serializer.serializeToString(clone); diff --git a/src/graph/renderer.ts b/src/graph/renderer.ts index 2d794b4..3648d80 100644 --- a/src/graph/renderer.ts +++ b/src/graph/renderer.ts @@ -1,6 +1,6 @@ import * as d3 from "d3"; import { Notice } from "obsidian"; -import type { App } from "obsidian"; +import type { App, TFile } from "obsidian"; import type { PersonNode, PeopleGraphSettings } from "../types"; interface SimNode extends d3.SimulationNodeDatum { @@ -15,7 +15,8 @@ interface SimLink extends d3.SimulationLinkDatum { const NODE_RADIUS = 24; -function appendAvatarSilhouette(el: d3.Selection) { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function appendAvatarSilhouette(el: d3.Selection) { // Head el.append("circle") .attr("cy", -4) @@ -125,8 +126,7 @@ export function renderGraph( } // Create SVG — fills container and resizes with it - container.style.position = "relative"; - container.style.overflow = "hidden"; + container.addClass("people-graph-container"); const svg = d3 .select(container) @@ -205,18 +205,19 @@ export function renderGraph( // Free tier: dim nodes beyond limit (skip center node) const maxFree = settings.maxFreeNodes; + const nodeElements = nodeSelection.nodes(); let personIndex = 0; - nodeSelection.each(function (d) { + nodeSelection.each((d, i) => { if (d.isCenter) return; if (personIndex >= maxFree) { - d3.select(this).attr("opacity", 0.3); + d3.select(nodeElements[i]).attr("opacity", 0.3); } personIndex++; }); // Render node visuals - nodeSelection.each(function (d, i) { - const el = d3.select(this); + nodeSelection.each((d, i) => { + const el = d3.select(nodeElements[i]); if (d.isCenter) { const centerR = NODE_RADIUS + 4; @@ -331,17 +332,7 @@ export function renderGraph( const tooltip = d3 .select(container) .append("div") - .attr("class", "people-graph-tooltip") - .style("position", "absolute") - .style("display", "none") - .style("background", "#ffffff") - .style("border", "1px solid #e0e0e0") - .style("border-radius", "6px") - .style("padding", "8px 12px") - .style("font-size", "12px") - .style("pointer-events", "none") - .style("z-index", "100") - .style("box-shadow", "0 2px 8px rgba(0,0,0,0.15)"); + .attr("class", "people-graph-tooltip"); nodeSelection .on("mouseenter", (event, d) => { @@ -381,7 +372,7 @@ export function renderGraph( } const file = app.vault.getAbstractFileByPath(d.person.id); if (file) { - app.workspace.getLeaf("tab").openFile(file as any); + void app.workspace.getLeaf("tab").openFile(file as TFile); } }); diff --git a/src/main.ts b/src/main.ts index 0a6728d..b8ebdf2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,23 +14,23 @@ export default class PeopleGraphPlugin extends Plugin { (leaf) => new PeopleGraphView(leaf, this) ); - this.addRibbonIcon("users", "Open People Graph", () => { - this.activateView(); + this.addRibbonIcon("users", "Open people graph", () => { + void this.activateView(); }); this.addSettingTab(new PeopleGraphSettingTab(this.app, this)); this.addCommand({ - id: "open-people-graph", - name: "Open People Graph", + id: "open", + name: "Open graph", callback: () => { - this.activateView(); + void this.activateView(); }, }); } onunload() { - this.app.workspace.detachLeavesOfType(VIEW_TYPE_PEOPLE_GRAPH); + // no-op: don't detach leaves so user layout is preserved } async loadSettings() { diff --git a/styles.css b/styles.css index 6a2e713..73d149f 100644 --- a/styles.css +++ b/styles.css @@ -1 +1,19 @@ /* People Graph plugin styles */ + +.people-graph-container { + position: relative; + overflow: hidden; +} + +.people-graph-tooltip { + position: absolute; + display: none; + background: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 6px; + padding: 8px 12px; + font-size: 12px; + pointer-events: none; + z-index: 100; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +}