mirror of
https://github.com/hansjone/mind-map.git
synced 2026-10-09 02:10:45 +08:00
feat: add topology view mode with router icons and link-style edges
Canvas-global nodeViewMode (bubble/card/topology) can be set at create or via prefs; topology draws NE routers with straight blue center links and exposes router icon to agents. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
200c8bd450
commit
74f0d8e78a
15 changed files with 322 additions and 103 deletions
|
|
@ -64,9 +64,10 @@ export function createChatHandler(ctx: ToolContext) {
|
|||
8. 固定坐标用 set_pinned 或 create_node 带 pos;不确定时 mindmap_get_schema。
|
||||
9. 节点属性:标题用 update_text;描述/多标签/多链接/图片URL/时间用 update_node_meta(patch);边用 update_edge_meta 或 set_edge_label。
|
||||
10. 节点类型 stylePreset:default|card|title|decision|risk|note|muted(title 仅画布锚点,勿新建;card=新闻快照卡片,配图用 imageUrl、摘要用 note、来源用 links)。
|
||||
11. 同级排序用 reorder:优先 {"type":"reorder","nodeId":"...","order":10};也可用 afterSiblingId(null=置顶)。
|
||||
12. 折叠 set_collapsed 会隐藏该节点主父链下的全部子孙并重新布局;展开后恢复。
|
||||
13. 问图:用 mindmap_query(neighbors|path|stats|orphans|missing_meta);答完可用 mindmap_highlight 高亮路径;细节用 mindmap_get_node / mindmap_get_edge。禁止编造图中没有的事实。
|
||||
11. 节点图标 icon(create_node / update_node_meta.patch):server|database|cloud|person|folder|doc|link|warning|check|star|gear|globe|router。拓扑排障图:建画布时可用 mindmap_create_canvas({ title, nodeViewMode:"topology" }),节点 icon:"router";nodeViewMode 是画布全局偏好,也可事后 set_prefs 修改。
|
||||
12. 同级排序用 reorder:优先 {"type":"reorder","nodeId":"...","order":10};也可用 afterSiblingId(null=置顶)。
|
||||
13. 折叠 set_collapsed 会隐藏该节点主父链下的全部子孙并重新布局;展开后恢复。
|
||||
14. 问图:用 mindmap_query(neighbors|path|stats|orphans|missing_meta);答完可用 mindmap_highlight 高亮路径;细节用 mindmap_get_node / mindmap_get_edge。禁止编造图中没有的事实。
|
||||
|
||||
当前空间上下文(摘要):
|
||||
${JSON.stringify(spatial, null, 0).slice(0, 6000)}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { fileURLToPath } from "node:url";
|
|||
import type { Server } from "node:http";
|
||||
import { WebSocketServer, type WebSocket } from "ws";
|
||||
import { SqliteGraphStore } from "@mind-map/graph-store";
|
||||
import { coerceOpInput, OpSchema, type Op } from "@mind-map/shared";
|
||||
import { coerceOpInput, OpSchema, NodeViewModeSchema, type Op } from "@mind-map/shared";
|
||||
import { listSkins } from "@mind-map/dream-skin";
|
||||
import { GraphError } from "@mind-map/graph-core";
|
||||
import { z } from "zod";
|
||||
|
|
@ -163,8 +163,16 @@ export function startMindMapServer(
|
|||
const body = (await c.req.json().catch(() => ({}))) as {
|
||||
title?: string;
|
||||
rootText?: string;
|
||||
nodeViewMode?: string;
|
||||
prefs?: { nodeViewMode?: string };
|
||||
};
|
||||
const g = store.createCanvas(body.title, body.rootText);
|
||||
const viewRaw = body.nodeViewMode ?? body.prefs?.nodeViewMode;
|
||||
const parsed = NodeViewModeSchema.safeParse(viewRaw);
|
||||
const g = store.createCanvas(
|
||||
body.title,
|
||||
body.rootText,
|
||||
parsed.success ? { nodeViewMode: parsed.data } : undefined,
|
||||
);
|
||||
broadcast({ type: "canvas/created", canvasId: g.canvas.id });
|
||||
return c.json(clientCanvasPayload(store, g.canvas.id));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import {
|
|||
nodeFunction,
|
||||
OP_SCHEMA_DOC,
|
||||
OP_TYPES,
|
||||
NodeIconSchema,
|
||||
NodeViewModeSchema,
|
||||
} from "@mind-map/shared";
|
||||
import { hierarchyFingerprint, layout } from "@mind-map/layout-engine";
|
||||
|
||||
|
|
@ -481,8 +483,10 @@ function estimateOverlaps(
|
|||
nodeViewMode: string,
|
||||
anchorId: string | null,
|
||||
) {
|
||||
const w = nodeViewMode === "card" ? 180 : 120;
|
||||
const h = nodeViewMode === "card" ? 72 : 36;
|
||||
const w =
|
||||
nodeViewMode === "card" ? 180 : nodeViewMode === "topology" ? 88 : 120;
|
||||
const h =
|
||||
nodeViewMode === "card" ? 72 : nodeViewMode === "topology" ? 64 : 36;
|
||||
const ids = nodes
|
||||
.filter((n) => !n.deletedAt && n.id !== anchorId && positions[n.id])
|
||||
.map((n) => n.id);
|
||||
|
|
@ -770,11 +774,30 @@ export async function runTool(
|
|||
rawArgs.rootText != null && String(rawArgs.rootText).trim()
|
||||
? String(rawArgs.rootText)
|
||||
: undefined;
|
||||
const g = ctx.store.createCanvas(title, rootText);
|
||||
const prefsArg =
|
||||
rawArgs.prefs && typeof rawArgs.prefs === "object"
|
||||
? (rawArgs.prefs as Record<string, unknown>)
|
||||
: undefined;
|
||||
const viewParsed = NodeViewModeSchema.safeParse(
|
||||
rawArgs.nodeViewMode ?? prefsArg?.nodeViewMode,
|
||||
);
|
||||
const g = ctx.store.createCanvas(
|
||||
title,
|
||||
rootText,
|
||||
viewParsed.success ? { nodeViewMode: viewParsed.data } : undefined,
|
||||
);
|
||||
return {
|
||||
ok: true,
|
||||
canvas: { id: g.canvas.id, title: g.canvas.title, rev: g.canvas.rev },
|
||||
hint: "Canvas is empty visually. Create top-level nodes with {type:'create_node', text:'...'} (omit parentId). Use parentId only when nesting under an existing visible node.",
|
||||
canvas: {
|
||||
id: g.canvas.id,
|
||||
title: g.canvas.title,
|
||||
rev: g.canvas.rev,
|
||||
prefs: { nodeViewMode: g.canvas.prefs.nodeViewMode },
|
||||
},
|
||||
hint:
|
||||
g.canvas.prefs.nodeViewMode === "topology"
|
||||
? "Topology canvas: create NE nodes with icon:'router'. Omit parentId for top-level. Prefer straight hierarchy/relation links."
|
||||
: "Canvas is empty visually. Create top-level nodes with {type:'create_node', text:'...'} (omit parentId). Use parentId only when nesting under an existing visible node.",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1506,20 +1529,12 @@ export async function runTool(
|
|||
"edge_note",
|
||||
"all",
|
||||
],
|
||||
nodeIcons: [
|
||||
"server",
|
||||
"database",
|
||||
"cloud",
|
||||
"person",
|
||||
"folder",
|
||||
"doc",
|
||||
"link",
|
||||
"warning",
|
||||
"check",
|
||||
"star",
|
||||
"gear",
|
||||
"globe",
|
||||
],
|
||||
nodeIcons: NodeIconSchema.options,
|
||||
nodeViewModes: NodeViewModeSchema.options,
|
||||
createCanvas: {
|
||||
nodeViewMode:
|
||||
"Optional on mindmap_create_canvas (canvas-global). Also changeable later via set_prefs.",
|
||||
},
|
||||
edgeLineStyles: ["solid", "dashed", "dotted"],
|
||||
edgeDirections: ["forward", "both", "none"],
|
||||
getSubgraph: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue