mirror of
https://github.com/hansjone/mind-map.git
synced 2026-10-09 02:10:45 +08:00
feat: add PNG/SVG export and expandable card property tips
Toolbar exports PNG (fit+raster) and SVG; card click expands full fields, hover shows property tooltips, double-click opens the editor. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
23ff27122f
commit
4c9bd5b86f
22 changed files with 978 additions and 315 deletions
|
|
@ -390,6 +390,35 @@ export function startMindMapServer(
|
|||
}
|
||||
});
|
||||
|
||||
app.get("/api/canvases/:id/export.svg", async (c) => {
|
||||
try {
|
||||
const id = c.req.param("id");
|
||||
const result = (await runTool(
|
||||
"mindmap_export",
|
||||
{ format: "svg", canvasId: id },
|
||||
{ store, broadcast, defaultCanvasId: id },
|
||||
)) as {
|
||||
ok?: boolean;
|
||||
svg?: string;
|
||||
title?: string;
|
||||
};
|
||||
if (!result?.ok || typeof result.svg !== "string") {
|
||||
return c.json({ ok: false, error: "export_failed" }, 400);
|
||||
}
|
||||
const safe =
|
||||
String(result.title ?? "mindmap").replace(/[\\/:*?"<>|]+/g, "_") ||
|
||||
"mindmap";
|
||||
return new Response(result.svg, {
|
||||
headers: {
|
||||
"Content-Type": "image/svg+xml; charset=utf-8",
|
||||
"Content-Disposition": `attachment; filename="${safe}.svg"`,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
return c.json({ ok: false, error: "not_found" }, 404);
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/tools/:name", async (c) => {
|
||||
const name = c.req.param("name");
|
||||
const args = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ function escapeXml(s: string): string {
|
|||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function exportSvg(
|
||||
export function exportSvg(
|
||||
nodes: MindNode[],
|
||||
edges: MindEdge[],
|
||||
positions: Record<string, { x: number; y: number }>,
|
||||
|
|
@ -1108,24 +1108,37 @@ export async function runTool(
|
|||
snap.canvas.title,
|
||||
anchorId,
|
||||
);
|
||||
if (format === "png") {
|
||||
// Server has no font/raster stack; return SVG payload plus png mime hint.
|
||||
// Web UI toolbar「PNG」rasterizes the live canvas (CJK-accurate).
|
||||
return {
|
||||
ok: true,
|
||||
format: "svg",
|
||||
requestedFormat: "png",
|
||||
title: snap.canvas.title,
|
||||
mimeType: "image/svg+xml",
|
||||
svg,
|
||||
nodeCount: publicNodes.length,
|
||||
edgeCount: publicEdges.length,
|
||||
hint:
|
||||
"PNG raster needs the web UI toolbar button (system fonts). This tool returns SVG which pastes into docs/PPT; save as .svg or convert locally.",
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
format: format === "png" ? "svg" : "svg",
|
||||
format: "svg",
|
||||
title: snap.canvas.title,
|
||||
mimeType: "image/svg+xml",
|
||||
svg,
|
||||
nodeCount: publicNodes.length,
|
||||
edgeCount: publicEdges.length,
|
||||
hint:
|
||||
format === "png"
|
||||
? "Agent export returns SVG (vector). For raster PNG use the web UI「导出PNG」; SVG pastes into docs/PPT as well."
|
||||
: "SVG can be saved as .svg or opened in browser / Office.",
|
||||
hint: "SVG can be saved as .svg or opened in browser / Office.",
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
error: "invalid_op",
|
||||
message: 'format must be "json" | "md" | "svg" (png → svg for agents; UI has raster PNG)',
|
||||
message: 'format must be "json" | "md" | "svg" | "png"',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue