@markdy/core 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,6 +64,8 @@ try {
64
64
  |---|---|---|
65
65
  | `parse` | `(source, opts?) => DiagramAST` | Parse MarkdyScript source into a diagram AST |
66
66
  | `compile` | `(ast) => RenderPlan` | Lay out nodes, route edges, and schedule cues |
67
+ | `compilePlan` | `(ast, theme) => RenderPlan` | Compile AST against a resolved theme token set |
68
+ | `computeAdaptiveDimensions` | `(ast, edges?) => { width, height }` | Compute content-adaptive canvas dimensions from topology & items |
67
69
  | `parseAndCompile` | `(source) => { ast, plan }` | Parse and compile in one call |
68
70
  | `ParseError` | class | Error with `.line` number for diagnostics |
69
71
  | `DiagramAST` | type | Parsed scene: meta, nodes, edges, groups, patterns, beats |
package/dist/index.d.ts CHANGED
@@ -78,6 +78,10 @@ type SceneMeta = {
78
78
  theme: string;
79
79
  direction: LayoutDirection;
80
80
  duration?: number;
81
+ /** Whether width was explicitly specified by the author in the script. */
82
+ explicitWidth?: boolean;
83
+ /** Whether height was explicitly specified by the author in the script. */
84
+ explicitHeight?: boolean;
81
85
  /** Opt-in diagram mode; defaults to architecture. */
82
86
  type?: DiagramType;
83
87
  /** Playback, controls, interaction, and chrome behavior. Source of truth. */
@@ -369,6 +373,14 @@ type RenderPlan = {
369
373
  duration: number;
370
374
  };
371
375
 
376
+ /**
377
+ * Computes optimal, content-adaptive canvas width and height for a diagram AST
378
+ * when dimensions are omitted or partially specified in the script.
379
+ */
380
+ declare function computeAdaptiveDimensions(ast: DiagramAST, edges?: RoutedEdge[]): {
381
+ width: number;
382
+ height: number;
383
+ };
372
384
  declare function compilePlan(ast: DiagramAST, theme: ThemeTokens): RenderPlan;
373
385
 
374
386
  /**
@@ -633,4 +645,4 @@ declare function resolveOutputPreset(name: string): OutputPreset;
633
645
  /** List all available preset names. */
634
646
  declare function listOutputPresets(): string[];
635
647
 
636
- export { ARCH_RULE_PRESETS, type AnnotationDecl, type ArchRuleType, type ArchitecturePreset, type ArchitectureRule, type ArchitectureViolation, BEAT_CUE_KEYWORDS, type BeatDecl, type BeatRange, type Box, CUE_ALIASES, type CardinalPort, type Cue, DIAGRAM_TYPES, type Diagnostic, type DiagramAST, type DiagramDiffResult, type DiagramType, type DiffChangeType, EDGE_OPERATORS, type EdgeDecl, type EdgeDiff, type EdgeKind, type EdgeSelector, type FlowSegment, type GroupBoundary, type GroupDecl, type LayoutDirection, type MarkdyConfig, NODE_ALIASES, NODE_KINDS, type NodeDecl, type NodeDiff, type NodeSelector, type NodeShape, OUTPUT_PRESETS, type OutputPreset, PLAYER_FLAT_KEYS, PLAYER_GROUPS, ParseError, type ParseOptions, type ParseResult, type PatternDecl, type PlayerChromeConfig, type PlayerConfig, type PlayerControlsConfig, type PlayerInteractionConfig, type PlayerOverrides, type PlayerPlaybackConfig, type PlayerProgress, type PlayerScope, type Point, type PositionedNode, type RenderPlan, type RepairPromptBundle, type ResolvedPlayer, type RoutedEdge, type RoutedPath, type RuleSeverity, SCENE_KEYS, type SceneMeta, type SemanticProfile, type SequenceActivation, type SequenceMessage, type StyleDecl, TECHNICAL_NODE_KINDS, TECHNICAL_NODE_TYPES, THEMES, type ThemeGeneratorOptions, type ThemeTokens, type TimedCue, type TreeBus, VISUAL_PRIMITIVE_TYPES, analyzeAndBuildRepairPrompt, applyPlayerSetting, canonicalNodeKind, classifyTechnology, compile, compilePlan, compressMarkdyToUrlHash, decompressMarkdyFromUrlHash, diffDiagramASTs, generateThemeFromBrand, getBoxPortPosition, humanizeId, listOutputPresets, nodeRole, parse, parseAndCompile, resolveArchitectureConfig, resolveOutputPreset, resolvePlayer, resolveTheme, routeOrthogonalEdge, selectOptimalPorts, validateArchitecture };
648
+ export { ARCH_RULE_PRESETS, type AnnotationDecl, type ArchRuleType, type ArchitecturePreset, type ArchitectureRule, type ArchitectureViolation, BEAT_CUE_KEYWORDS, type BeatDecl, type BeatRange, type Box, CUE_ALIASES, type CardinalPort, type Cue, DIAGRAM_TYPES, type Diagnostic, type DiagramAST, type DiagramDiffResult, type DiagramType, type DiffChangeType, EDGE_OPERATORS, type EdgeDecl, type EdgeDiff, type EdgeKind, type EdgeSelector, type FlowSegment, type GroupBoundary, type GroupDecl, type LayoutDirection, type MarkdyConfig, NODE_ALIASES, NODE_KINDS, type NodeDecl, type NodeDiff, type NodeSelector, type NodeShape, OUTPUT_PRESETS, type OutputPreset, PLAYER_FLAT_KEYS, PLAYER_GROUPS, ParseError, type ParseOptions, type ParseResult, type PatternDecl, type PlayerChromeConfig, type PlayerConfig, type PlayerControlsConfig, type PlayerInteractionConfig, type PlayerOverrides, type PlayerPlaybackConfig, type PlayerProgress, type PlayerScope, type Point, type PositionedNode, type RenderPlan, type RepairPromptBundle, type ResolvedPlayer, type RoutedEdge, type RoutedPath, type RuleSeverity, SCENE_KEYS, type SceneMeta, type SemanticProfile, type SequenceActivation, type SequenceMessage, type StyleDecl, TECHNICAL_NODE_KINDS, TECHNICAL_NODE_TYPES, THEMES, type ThemeGeneratorOptions, type ThemeTokens, type TimedCue, type TreeBus, VISUAL_PRIMITIVE_TYPES, analyzeAndBuildRepairPrompt, applyPlayerSetting, canonicalNodeKind, classifyTechnology, compile, compilePlan, compressMarkdyToUrlHash, computeAdaptiveDimensions, decompressMarkdyFromUrlHash, diffDiagramASTs, generateThemeFromBrand, getBoxPortPosition, humanizeId, listOutputPresets, nodeRole, parse, parseAndCompile, resolveArchitectureConfig, resolveOutputPreset, resolvePlayer, resolveTheme, routeOrthogonalEdge, selectOptimalPorts, validateArchitecture };
package/dist/index.js CHANGED
@@ -874,8 +874,8 @@ function assignRanks(nodeIds, edges, direction) {
874
874
  }
875
875
  return ranks;
876
876
  }
877
- function snapGrid(n) {
878
- return Math.round(n / 4) * 4;
877
+ function snapGrid(n, step = 4) {
878
+ return Math.round(n / step) * step;
879
879
  }
880
880
  function layoutRanked(ast, edges, opts) {
881
881
  const nodeIds = Object.keys(ast.nodes);
@@ -1938,8 +1938,171 @@ function buildSequencePlan(ast, cues) {
1938
1938
  });
1939
1939
  return { messages, activations };
1940
1940
  }
1941
+ function computeAdaptiveDimensions(ast, edges) {
1942
+ const explicitW = ast.meta.explicitWidth === true;
1943
+ const explicitH = ast.meta.explicitHeight === true;
1944
+ if (explicitW && explicitH) {
1945
+ return { width: ast.meta.width, height: ast.meta.height };
1946
+ }
1947
+ const effectiveEdges = edges ?? collectStructuralEdges(ast);
1948
+ const nodeIds = Object.keys(ast.nodes);
1949
+ const nodeCount = nodeIds.length;
1950
+ const dtype = diagramType(ast);
1951
+ const direction = ast.meta.direction ?? "LR";
1952
+ const isVertical = direction === "TB" || direction === "BT";
1953
+ const groupCount = Object.keys(ast.groups).length;
1954
+ let autoW = 1280;
1955
+ let autoH = 720;
1956
+ if (nodeCount === 0) {
1957
+ autoW = 1280;
1958
+ autoH = 720;
1959
+ } else if (dtype === "sequence") {
1960
+ const flowCues = ast.beats.flatMap((b) => b.cues).filter((c) => c.kind === "flow");
1961
+ const count = Math.max(nodeCount, 1);
1962
+ const flowCount = flowCues.length;
1963
+ const requiredW = SAFE * 2 + Math.max(count * 220, 800);
1964
+ const requiredH = TITLE_BAND + NODE_H + 56 + Math.max(flowCount * 76, 280) + SAFE + 48;
1965
+ autoW = Math.max(1088, Math.min(2560, requiredW));
1966
+ autoH = Math.max(640, Math.min(1800, requiredH));
1967
+ } else if (dtype === "tree") {
1968
+ let measureSubtree2 = function(id) {
1969
+ const kids = children.get(id) ?? [];
1970
+ if (kids.length === 0) {
1971
+ subtreeSpan.set(id, minLeafWidth);
1972
+ return minLeafWidth;
1973
+ }
1974
+ let total = 0;
1975
+ for (const kid of kids) total += measureSubtree2(kid);
1976
+ const span = Math.max(minLeafWidth, total);
1977
+ subtreeSpan.set(id, span);
1978
+ return span;
1979
+ };
1980
+ var measureSubtree = measureSubtree2;
1981
+ const children = /* @__PURE__ */ new Map();
1982
+ const hasParent = /* @__PURE__ */ new Set();
1983
+ for (const id of nodeIds) children.set(id, []);
1984
+ const treeEdges = effectiveEdges.some((e) => e.structural) ? effectiveEdges.filter((e) => e.structural) : effectiveEdges;
1985
+ for (const e of treeEdges) {
1986
+ if (ast.nodes[e.from] && ast.nodes[e.to] && e.from !== e.to) {
1987
+ children.get(e.from)?.push(e.to);
1988
+ hasParent.add(e.to);
1989
+ }
1990
+ }
1991
+ const roots = nodeIds.filter((id) => !hasParent.has(id));
1992
+ const depth = /* @__PURE__ */ new Map();
1993
+ const queue = [...roots];
1994
+ for (const r of roots) depth.set(r, 0);
1995
+ while (queue.length > 0) {
1996
+ const cur = queue.shift();
1997
+ const d = depth.get(cur) ?? 0;
1998
+ for (const child of children.get(cur) ?? []) {
1999
+ if (!depth.has(child)) {
2000
+ depth.set(child, d + 1);
2001
+ queue.push(child);
2002
+ }
2003
+ }
2004
+ }
2005
+ for (const id of nodeIds) if (!depth.has(id)) depth.set(id, 0);
2006
+ const maxDepth = Math.max(...depth.values(), 0);
2007
+ const minLeafWidth = NODE_W + 48;
2008
+ const subtreeSpan = /* @__PURE__ */ new Map();
2009
+ let totalRootsWidth = 0;
2010
+ const activeRoots = roots.length > 0 ? roots : nodeIds;
2011
+ for (const r of activeRoots) totalRootsWidth += measureSubtree2(r);
2012
+ const requiredW = SAFE * 2 + totalRootsWidth;
2013
+ const requiredH = TITLE_BAND + SAFE * 2 + (maxDepth + 1) * 140;
2014
+ autoW = Math.max(1120, Math.min(2560, requiredW));
2015
+ autoH = Math.max(640, Math.min(1600, requiredH));
2016
+ } else if (dtype === "swimlane") {
2017
+ const laneCount = Math.max(groupCount, 1);
2018
+ const maxInLane = Math.max(
2019
+ ...Object.values(ast.groups).map((g) => g.members.length),
2020
+ Math.ceil(nodeCount / laneCount),
2021
+ 1
2022
+ );
2023
+ const requiredW = SAFE * 2 + 140 + maxInLane * 220;
2024
+ const requiredH = TITLE_BAND + SAFE * 2 + laneCount * 140;
2025
+ autoW = Math.max(1152, Math.min(2560, requiredW));
2026
+ autoH = Math.max(640, Math.min(1600, requiredH));
2027
+ } else if (dtype === "pyramid") {
2028
+ const tierCount = Math.max(nodeCount, 1);
2029
+ autoW = 1280;
2030
+ const requiredH = TITLE_BAND + SAFE * 2 + tierCount * 110;
2031
+ autoH = Math.max(640, Math.min(1440, requiredH));
2032
+ } else if (dtype === "layers") {
2033
+ const layerCount = Math.max(nodeCount, 1);
2034
+ autoW = 1280;
2035
+ const requiredH = TITLE_BAND + SAFE * 2 + layerCount * 96;
2036
+ autoH = Math.max(640, Math.min(1440, requiredH));
2037
+ } else if (dtype === "timeline" || dtype === "gantt") {
2038
+ const milestones = Math.max(nodeCount, 1);
2039
+ const requiredW = SAFE * 2 + milestones * 240;
2040
+ const requiredH = TITLE_BAND + SAFE * 2 + 380;
2041
+ autoW = Math.max(1200, Math.min(2560, requiredW));
2042
+ autoH = Math.max(640, Math.min(1200, requiredH));
2043
+ } else if (dtype === "loop" || dtype === "flywheel" || dtype === "radar" || dtype === "venn" || dtype === "constellation") {
2044
+ const N = Math.max(nodeCount, 3);
2045
+ const minRadius = 200;
2046
+ const radiusNeeded = Math.max(minRadius, N * 180 / (2 * Math.PI));
2047
+ const requiredW = SAFE * 2 + radiusNeeded * 2 + 240;
2048
+ const requiredH = TITLE_BAND + SAFE * 2 + radiusNeeded * 2 + 140;
2049
+ autoW = Math.max(1088, Math.min(2200, requiredW));
2050
+ autoH = Math.max(720, Math.min(1600, requiredH));
2051
+ } else if (dtype === "quadrant") {
2052
+ autoW = 1200;
2053
+ autoH = 800;
2054
+ } else {
2055
+ const forceVertical = dtype === "flowchart" && isVertical || isVertical;
2056
+ const ranks = assignRanks(nodeIds, effectiveEdges, forceVertical ? "TB" : "LR");
2057
+ const byRank = /* @__PURE__ */ new Map();
2058
+ for (const id of nodeIds) {
2059
+ const r = ranks.get(id) ?? 0;
2060
+ if (!byRank.has(r)) byRank.set(r, []);
2061
+ byRank.get(r).push(id);
2062
+ }
2063
+ const rankCount = Math.max(...byRank.keys(), 0) + 1;
2064
+ const maxInRank = Math.max(...[...byRank.values()].map((v) => v.length), 1);
2065
+ const groupPaddingBonus = groupCount > 0 ? GROUP_PAD * 2 : 0;
2066
+ if (forceVertical) {
2067
+ const requiredW = SAFE * 2 + maxInRank * NODE_W + (maxInRank - 1) * 44 + groupPaddingBonus;
2068
+ const requiredH = SAFE * 2 + TITLE_BAND + rankCount * NODE_H + (rankCount - 1) * 56 + groupPaddingBonus;
2069
+ if (nodeCount <= 2) {
2070
+ autoW = 960;
2071
+ autoH = 640;
2072
+ } else if (nodeCount <= 4 && rankCount <= 3) {
2073
+ autoW = 1024;
2074
+ autoH = 720;
2075
+ } else {
2076
+ autoW = Math.max(960, Math.min(2400, requiredW));
2077
+ autoH = Math.max(720, Math.min(2e3, requiredH));
2078
+ }
2079
+ } else {
2080
+ const requiredW = SAFE * 2 + rankCount * NODE_W + (rankCount - 1) * 44 + groupPaddingBonus;
2081
+ const requiredH = SAFE * 2 + TITLE_BAND + maxInRank * NODE_H + (maxInRank - 1) * 36 + groupPaddingBonus;
2082
+ if (nodeCount <= 2 && rankCount <= 2) {
2083
+ autoW = 1024;
2084
+ autoH = 576;
2085
+ } else if (nodeCount <= 4 && rankCount <= 3 && maxInRank <= 2) {
2086
+ autoW = 1152;
2087
+ autoH = 648;
2088
+ } else {
2089
+ autoW = Math.max(1152, Math.min(2560, requiredW));
2090
+ autoH = Math.max(648, Math.min(1600, requiredH));
2091
+ }
2092
+ }
2093
+ }
2094
+ return {
2095
+ width: explicitW ? ast.meta.width : snapGrid(autoW, 16),
2096
+ height: explicitH ? ast.meta.height : snapGrid(autoH, 16)
2097
+ };
2098
+ }
1941
2099
  function compilePlan(ast, theme) {
1942
2100
  const structuralEdges = collectStructuralEdges(ast);
2101
+ if (!ast.meta.explicitWidth || !ast.meta.explicitHeight) {
2102
+ const dims = computeAdaptiveDimensions(ast, structuralEdges);
2103
+ if (!ast.meta.explicitWidth) ast.meta.width = dims.width;
2104
+ if (!ast.meta.explicitHeight) ast.meta.height = dims.height;
2105
+ }
1943
2106
  const nodes = layoutNodes(ast, structuralEdges);
1944
2107
  const groupBoundaries = computeGroupBoundaries(ast, nodes);
1945
2108
  const treeBuses = computeTreeBuses(ast, nodes, structuralEdges.filter((edge) => edge.structural));
@@ -2986,8 +3149,15 @@ function parse(source, opts = {}) {
2986
3149
  diagnostics.push({ severity: "warning", message: `unknown scene property '${k}'`, line: lineNo });
2987
3150
  continue;
2988
3151
  }
2989
- if (k === "width" || k === "height" || k === "fps") meta[k] = Number(v);
2990
- else if (k === "duration") meta.duration = Number(v);
3152
+ if (k === "width") {
3153
+ meta.width = Number(v);
3154
+ meta.explicitWidth = true;
3155
+ } else if (k === "height") {
3156
+ meta.height = Number(v);
3157
+ meta.explicitHeight = true;
3158
+ } else if (k === "fps") {
3159
+ meta.fps = Number(v);
3160
+ } else if (k === "duration") meta.duration = Number(v);
2991
3161
  else if (k === "theme") meta.theme = String(v);
2992
3162
  else if (k === "direction" || k === "layout") meta.direction = String(v).toUpperCase();
2993
3163
  else if (PLAYER_FLAT_KEY_SET.has(k)) {
@@ -4270,6 +4440,7 @@ export {
4270
4440
  compile,
4271
4441
  compilePlan,
4272
4442
  compressMarkdyToUrlHash,
4443
+ computeAdaptiveDimensions,
4273
4444
  decompressMarkdyFromUrlHash,
4274
4445
  diffDiagramASTs,
4275
4446
  generateThemeFromBrand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/core",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
4
4
  "description": "Diagram-native MarkdyScript parser and compiler (auto-layout, edge routing, cue scheduling) — zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "type": "module",