export interface LayoutNode { id: string; text: string; depth: number; x: number; y: number; width: number; height: number; /** Font size to render this node's text at, in px. */ fontSize: number; isRoot: boolean; collapsed?: boolean; side: 1 | -1; /** * Direction this node's children fan out in: 'right' (default for * mindmap right side / tree), 'left' (mindmap left side), or 'down' * (org mode). */ _dir: 'right' | 'left' | 'down'; /** Direction this node's children fan out in, as a signed scalar * (1 = right, -1 = left). Mirrors `_dir` for callers that need * a number. Kept in sync by applyDoLayout and forceRight — when * the height-based balancer moves a child to the opposite side, * the whole subtree gets its `side` / `_dir` / `_dirRight` * re-stamped so descendants fan the right way. */ _dirRight: 1 | -1; /** Layout-only: vertical extent of this node's subtree (post-order * walk result, read by layoutHorizontal). Not for public use. */ _subtreeH: number; /** Layout-only: horizontal extent of this node's subtree (read by * layoutVertical / org mode). Not for public use. */ _subtreeW: number; /** Optional embedded image (mirrored from MindMapNode.image). * The renderer uses src/width/height; naturalW/H lock the * resize aspect ratio. */ image?: MindMapImage; /** Mirrored from MindMapNode.link. Read by the renderer to * show a link icon next to the text. */ link?: { url: string; }; /** Mirrored from MindMapNode.note. Read by the renderer to * show a note icon next to the text. */ note?: { text: string; }; /** Mirrored from MindMapNode.richContent. Read by the renderer * to show a small framed body under the node title (code / list / * table / paragraph). Undefined means the node is plain text * only — the default behaviour, unchanged from before this * field was introduced. */ richContent?: RichContent; /** Mirrored from MindMapNode.markers. Read by the renderer to * show small marker icons before the node text. */ markers?: string[]; /** Mirrored from MindMapNode.tags. Read by the renderer to show * small colored pills below the node title. */ tags?: string[]; /** Inset (px) the SVG edge anchor should retreat from the * geometric box edge on the in-side, to land at the visible * content edge instead. Set to `.zm-node` padding + * `.zm-rich` padding + 2 for code/table nodes (their visible * content sits well inside the box); 0 for plain nodes (the * geometric edge IS the visible edge). Used by lineAnchor * in MindMap.vue — non-zero for nodes whose first child of * the line would otherwise appear to pierce the rich body. */ _richInsetX?: number; children: LayoutNode[]; parent: LayoutNode | null; } import type { MindMapNode, MindMapImage, RichContent } from '../types'; /** Return the rendered font size for a node at the given depth, scaled * by the host's `theme.fontSize` (default 14). The base table is * tuned at 14px; values scale linearly so a 30px theme produces * roughly 2.14× larger nodes. */ declare function fontAt(depth: number, baseFontSize?: number): number; declare function heightAt(depth: number, baseFontSize?: number): number; /** Horizontal (or vertical, in org mode) gap between a parent at the * given depth and its immediate children. The gap shrinks with * depth so deeper tiers sit closer together, but never below * H_GAP_MIN. */ declare function hGapForDepth(parentDepth: number): number; /** Max text-label width in px. MUST match `max-width` on `.zm-text` * in MindMap.vue. The layout reserves this much room for the label * regardless of how long the actual text is — the DOM then truncates * with `text-overflow: ellipsis`. Without this cap, a long string * pushes the rendered node box past the layout's reserved width and * the line anchor (computed from layout width) ends up inside the * box. The 200px default is the same value `flow-mindmap` shipped * with before; keeping it keeps edge anchors stable. */ export declare const TEXT_MAX_W = 200; export type LayoutMode = 'mindmap' | 'tree' | 'org'; export interface LayoutOptions { mode?: LayoutMode; /** @deprecated kept for API compat; ignored in 1.html-style layout. */ balanced?: boolean; /** Base font size (px) used to scale node metrics (font/height/ * min-width). The internal tier table is tuned for 14px; values * scale linearly. Default 14. */ baseFontSize?: number; /** Extra vertical gap (px) between first-level branches (children * of the root). Adds to the base V_GAP so each top-level subtree * gets more breathing room. Default 0 (no extra gap). */ branchGap?: number; /** * When true, layout() leaves each LayoutNode's existing x/y in * place — it still does the doLayout split / redirect / stack * walk, but skips writing to child.x / child.y. Used after a * drag: we commit the offset into the data tree, clear the * per-node offset map, then re-run layout with this flag so * the dragged node (and its subtree) stay where the user put * them. The root is still forced to (0, 0) — to put the * dragged node at a new position, the surrounding tree moves * relative to it. New nodes added later get the algorithmic * position since their LayoutNode has x = 0, y = 0. */ preservePositions?: boolean; /** * Per-node measured size of the rendered rich body (the * `
` element above the title), in px. The * caller (MindMap.vue) populates this after each render with * `el.offsetWidth` / `el.offsetHeight`; layout reads it for * nodes that carry code / table rich content. When a node has * a measured size we use it directly so the box grows / * shrinks to fit the content. When the caller hasn't * measured a node yet we fall back to the fixed floor / cap. * IDs not present in the map are simply ignored — useful for * the very first render before measurement has happened. */ richHeights?: Record; richWidths?: Record; } export declare function layout(root: MindMapNode, options?: LayoutOptions): { root: LayoutNode; width: number; height: number; vbX: number; vbY: number; vbW: number; vbH: number; }; export declare const LAYOUT: { /** Legacy single-size values for callers that still expect them. */ NODE_W: number; NODE_H: number; NODE_FONTS: number[]; NODE_HEIGHTS: number[]; NODE_MIN_W: number[]; NODE_PAD_H: number[]; NODE_FONT_WEIGHTS: number[]; H_GAP: number; H_GAP_MIN: number; H_GAP_DECAY: number; V_GAP: number; SIDE_PADDING: number; heightAt: typeof heightAt; fontAt: typeof fontAt; /** Depth-dependent horizontal gap between a parent at `parentDepth` * and its children. Deeper tiers get a smaller gap, clamped to * H_GAP_MIN. */ hGapForDepth: typeof hGapForDepth; /** Clear the text-measurement cache (call after font load). */ clearMeasureCache: () => void; }; export {};