@retikz/core 0.2.0-alpha.3 → 0.2.0-alpha.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../src/compile/compile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAwC,MAAM,OAAO,CAAC;AACtE,OAAO,KAAK,EAAa,KAAK,EAA6B,MAAM,cAAc,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAoBjD,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AA4CrE,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,IAAI,EACA,2BAA2B,GAC3B,gBAAgB,GAChB,0BAA0B,GAC1B,wBAAwB,GACxB,yBAAyB,GACzB,sBAAsB,GACtB,8BAA8B,GAC9B,oBAAoB,GACpB,mBAAmB,GACnB,yBAAyB,GACzB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,2BAA2B;AAC3B,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC1C,CAAC;AAsDF;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,IAAI,EAAE,EAAE,UAAS,cAAmB,KAAG,KAgOrE,CAAC"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../src/compile/compile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAwC,MAAM,OAAO,CAAC;AACtE,OAAO,KAAK,EAAa,KAAK,EAA6B,MAAM,cAAc,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAoBjD,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AA4CrE,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,IAAI,EACA,2BAA2B,GAC3B,gBAAgB,GAChB,0BAA0B,GAC1B,wBAAwB,GACxB,yBAAyB,GACzB,sBAAsB,GACtB,8BAA8B,GAC9B,oBAAoB,GACpB,mBAAmB,GACnB,yBAAyB,GACzB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,2BAA2B;AAC3B,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC1C,CAAC;AA6FF;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,IAAI,EAAE,EAAE,UAAS,cAAmB,KAAG,KA4RrE,CAAC"}
@@ -56,6 +56,18 @@ var defaultWarnDispatcher = (warning) => {
56
56
  if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
57
57
  console.warn(`[retikz] ${warning.code} at ${warning.path}: ${warning.message}`);
58
58
  };
59
+ var makePathPlaceholder = () => ({ type: "path-placeholder" });
60
+ /** 把内部 sink 收窄回公开 ScenePrimitive[]:占位已全部回填(compileToScene 末端 placeholderBalance 无条件校验兜底) */
61
+ var sealSink = (sink) => sink;
62
+ /** dev 诊断:递归找出残留占位的 index 路径,供末端无条件校验报错时定位 */
63
+ var collectPlaceholderLocators = (prims, prefix = "primitives") => {
64
+ const locators = [];
65
+ prims.forEach((prim, idx) => {
66
+ if (prim.type === "path-placeholder") locators.push(`${prefix}[${idx}]`);
67
+ else if (prim.type === "group") locators.push(...collectPlaceholderLocators(prim.children, `${prefix}[${idx}].children`));
68
+ });
69
+ return locators;
70
+ };
59
71
  /** scope.transforms 解析失败时根据失败成因映射的 warn code */
60
72
  var scopeTransformWarnCode = (scope) => {
61
73
  for (const t of scope.transforms ?? []) {
@@ -98,11 +110,32 @@ var compileToScene = (ir, options = {}) => {
98
110
  });
99
111
  }
100
112
  const primitives = [];
113
+ /** 已 push 但未回填的占位计数;compileToScene 返回前必须归零(无条件守 Scene 公开契约) */
114
+ let placeholderBalance = 0;
115
+ /**
116
+ * primitive → 显式 zIndex 旁路记录(缺省视为 0);sealSink 后按它稳定排序,不写进 primitive 本体(保 Scene 输出纯净)。
117
+ * key 只会是 real ScenePrimitive——占位 PathPlaceholder 永不进此 Map(占位即将被回填替换)。
118
+ */
119
+ const zIndexOf = /* @__PURE__ */ new Map();
120
+ /**
121
+ * 按 zIndex 升序原地稳定排序:同 zIndex 保持原 IR 顺序(decorate-sort 带原始下标)。全 0 键 = 恒等。
122
+ * 仅在 sealSink(占位已回填、类型已收窄回 ScenePrimitive)之后调用。
123
+ */
124
+ const stableSortByZIndex = (arr) => {
125
+ const decorated = arr.map((prim, index) => ({
126
+ prim,
127
+ index,
128
+ z: zIndexOf.get(prim) ?? 0
129
+ }));
130
+ decorated.sort((a, b) => a.z - b.z || a.index - b.index);
131
+ for (let i = 0; i < arr.length; i++) arr[i] = decorated[i].prim;
132
+ return arr;
133
+ };
101
134
  const nameStack = new NameStack({ onDuplicate: (info) => onWarn(formatDuplicateWarning(info)) });
102
135
  const allPoints = [];
103
136
  /**
104
137
  * 解析一批本层收集的 pending paths(lookup-only 阶段)
105
- * @description path primitive 一律 push 到顶层 `primitives` —— 端点已是全局坐标,不能进 GroupPrim 否则被 scope.transform 二次 apply。NameStack 切到 pass2 守门:path 解析中误调 register 抛 internal error;解析完切回 pass1 让上层 scope 子树继续 register 子节点。
138
+ * @description 两种落点:有 `slot`(scopeChain 为空)→ 原位 splice 回填该 path 在本层 sink 占的位(按引用定位免索引漂移),保住与同层 node 的 IR 声明序;无 `slot`(scopeChain 非空)→ hoist 到顶层 `primitives`,因端点已是全局坐标、进 transformed GroupPrim 会被 scope.transform 二次 apply。NameStack 切到 pass2 守门:path 解析中误调 register 抛 internal error;解析完切回 pass1 让上层 scope 子树继续 register 子节点。
106
139
  * `item.scopeChain` 记录该 path 所属 scope 累积 transform 链——传给 emitPathPrimitive,
107
140
  * 让 step.to 内的 polar/at/offset 字面量按"当前 scope 局部度量 + 末端 apply chain"投影回全局。
108
141
  */
@@ -116,10 +149,18 @@ var compileToScene = (ir, options = {}) => {
116
149
  irPath: item.irPath,
117
150
  scopeChain: item.scopeChain
118
151
  });
119
- if (result) {
120
- for (const prim of result.primitives) primitives.push(prim);
121
- for (const p of result.points) allPoints.push(p);
152
+ if (item.slot) {
153
+ const idx = item.slot.sink.indexOf(item.slot.placeholder);
154
+ if (idx === -1) throw new Error("internal: path placeholder missing from its sink");
155
+ const real = result?.primitives ?? [];
156
+ item.slot.sink.splice(idx, 1, ...real);
157
+ if (item.zIndex !== void 0) for (const prim of real) zIndexOf.set(prim, item.zIndex);
158
+ placeholderBalance--;
159
+ } else if (result) for (const prim of result.primitives) {
160
+ primitives.push(prim);
161
+ if (item.zIndex !== void 0) zIndexOf.set(prim, item.zIndex);
122
162
  }
163
+ if (result) for (const p of result.points) allPoints.push(p);
123
164
  }
124
165
  } finally {
125
166
  nameStack.exitLookupPhase();
@@ -143,7 +184,10 @@ var compileToScene = (ir, options = {}) => {
143
184
  const layout = layoutNode(resolveNodeStyle(child, styleStack), measureText, nameStack, nodeDistance, chain, resolveLabelDefault(styleStack), effectiveShapes);
144
185
  const globalLayout = chain.length === 0 ? layout : projectLayoutToGlobal(layout, chain);
145
186
  if (child.id) nameStack.register(child.id, globalLayout, `${locatorPrefix}children[${i}].node.id`);
146
- for (const prim of emitNodePrimitives(layout, round)) sink.push(prim);
187
+ for (const prim of emitNodePrimitives(layout, round)) {
188
+ sink.push(prim);
189
+ if (child.zIndex !== void 0) zIndexOf.set(prim, child.zIndex);
190
+ }
147
191
  allPoints.push(rect.anchor(globalLayout.rect, "north-west"), rect.anchor(globalLayout.rect, "north-east"), rect.anchor(globalLayout.rect, "south-west"), rect.anchor(globalLayout.rect, "south-east"));
148
192
  layoutsAccumulator.push(globalLayout);
149
193
  } else if (child.type === "coordinate") {
@@ -196,22 +240,40 @@ var compileToScene = (ir, options = {}) => {
196
240
  if (innerSink.length === 0 && !hasOwnTransforms && child.id === void 0) continue;
197
241
  const group = {
198
242
  type: "group",
199
- children: innerSink
243
+ children: stableSortByZIndex(sealSink(innerSink))
200
244
  };
201
245
  if (hasOwnTransforms) group.transforms = [...ownTransforms];
202
246
  sink.push(group);
203
- } else pathsAccumulator.push({
204
- path: resolveEffectivePath(child, styleStack),
205
- irPath: `${locatorPrefix}children[${i}].path`,
206
- scopeChain: chain
207
- });
247
+ if (child.zIndex !== void 0) zIndexOf.set(group, child.zIndex);
248
+ } else {
249
+ const pending = {
250
+ path: resolveEffectivePath(child, styleStack),
251
+ irPath: `${locatorPrefix}children[${i}].path`,
252
+ scopeChain: chain,
253
+ zIndex: child.zIndex
254
+ };
255
+ if (chain.length === 0) {
256
+ const placeholder = makePathPlaceholder();
257
+ sink.push(placeholder);
258
+ pending.slot = {
259
+ sink,
260
+ placeholder
261
+ };
262
+ placeholderBalance++;
263
+ }
264
+ pathsAccumulator.push(pending);
265
+ }
208
266
  }
209
267
  };
210
268
  const rootPaths = [];
211
269
  processChildren(ir.children, [], primitives, "", [], rootPaths, []);
212
270
  resolvePendingPaths(rootPaths);
271
+ if (placeholderBalance !== 0) {
272
+ const detail = typeof process !== "undefined" && process.env.NODE_ENV !== "production" ? ` at ${collectPlaceholderLocators(primitives).join(", ")}` : "";
273
+ throw new Error(`internal: ${placeholderBalance} unresolved path placeholder(s) leaked into Scene output${detail}`);
274
+ }
213
275
  return {
214
- primitives,
276
+ primitives: stableSortByZIndex(sealSink(primitives)),
215
277
  layout: computeLayout(allPoints, layoutPadding, round)
216
278
  };
217
279
  };
@@ -79,6 +79,10 @@ export type NodeLabelLayout = {
79
79
  fontFamily?: string;
80
80
  fontWeight?: string | number;
81
81
  fontStyle?: 'normal' | 'italic' | 'oblique';
82
+ /** label 文本自旋模式(none / radial / tangent / 数字角度);缺省 = 不旋转 */
83
+ rotate?: 'none' | 'radial' | 'tangent' | number;
84
+ /** 自旋后若文字倒置则翻 180°;缺省 false */
85
+ keepUpright?: boolean;
82
86
  };
83
87
  /**
84
88
  * 取节点 shape 在 toward 方向的附着点(path 端点贴边用)
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/compile/node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/compile/node.ts"],"names":[],"mappings":"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"}
@@ -78,19 +78,51 @@ var LABEL_DIRECTION_MAP = {
78
78
  }
79
79
  };
80
80
  /**
81
- * 算 label 中心点
82
- * @description 8 方向:节点对应 anchor 出发按单位向量 × distance 外推;数字角度:先取 angleBoundary 边界点再沿 (cos,sin) × distance 外推
81
+ * 算 label 中心点(节点局部坐标系,未旋转)
82
+ * @description 8 方向:节点对应 anchor 出发按单位向量 × distance 外推;数字角度:先取 angleBoundary 边界点再沿 (cos,sin) × distance 外推。
83
+ * 两个分支都在 **axis-aligned rect(rotate=0)** 上算——node 自身 rotate 由外层 GroupPrim 统一施加;
84
+ * 若用带 rotate 的 rect,label 位置会被 anchorOf / angleBoundaryOf 旋转一次、再被外层 group 旋转一次(双重旋转)。
85
+ * anchorOf / angleBoundaryOf 本身不改(path anchor `'A.north'` / `'A.30'` 仍需带 rotate 的 rect)。
83
86
  */
84
87
  var labelCenter = (layout, label) => {
88
+ const aaLayout = {
89
+ ...layout,
90
+ rect: {
91
+ ...layout.rect,
92
+ rotate: 0
93
+ }
94
+ };
85
95
  if (typeof label.position === "number") {
86
96
  const rad = label.position * Math.PI / 180;
87
- const [bx, by] = angleBoundaryOf(layout, label.position);
97
+ const [bx, by] = angleBoundaryOf(aaLayout, label.position);
88
98
  return [bx + Math.cos(rad) * label.distance, by + Math.sin(rad) * label.distance];
89
99
  }
90
100
  const { anchor, vec } = LABEL_DIRECTION_MAP[label.position];
91
- const [bx, by] = anchorOf(layout, anchor);
101
+ const [bx, by] = anchorOf(aaLayout, anchor);
92
102
  return [bx + vec[0] * label.distance, by + vec[1] * label.distance];
93
103
  };
104
+ /** 角度换算常量(弧度 → 度) */
105
+ var RAD_TO_DEG = 180 / Math.PI;
106
+ /**
107
+ * 算 label 文本自旋角度(度,屏幕 y-down,节点局部系)
108
+ * @description radial = atan2(label中心 − node中心);tangent = radial + 90;number = 原值;none / 缺省 = 0。
109
+ * keepUpright 时把"偏离正立 > 90°"的角度翻 180° 保阅读方向。方向向量在局部坐标算,node 自身 rotate 由外层 group 叠加。
110
+ */
111
+ var resolveLabelRotateDeg = (label, lx, ly, cx, cy) => {
112
+ const mode = label.rotate;
113
+ if (mode === void 0 || mode === "none") return 0;
114
+ let deg;
115
+ if (typeof mode === "number") deg = mode;
116
+ else {
117
+ const radial = Math.atan2(ly - cy, lx - cx) * RAD_TO_DEG;
118
+ deg = mode === "tangent" ? radial + 90 : radial;
119
+ }
120
+ if (label.keepUpright) {
121
+ const norm = (deg % 360 + 360) % 360;
122
+ if (norm > 90 && norm < 270) deg += 180;
123
+ }
124
+ return deg;
125
+ };
94
126
  /**
95
127
  * 取节点 shape 在指定角度方向的边界点
96
128
  * @description 角度是节点**局部坐标系**下的极角(度数:0°=局部 +x,90°=局部 +y)。layout.rect.rotate 把局部基绕中心旋转,得到世界系下的视觉方向;shape boundaryPoint 内部用 rotate-aware 投影,所以这里把局部 (cos, sin) 经 rect.rotate 旋转后加到中心当作世界系 toward 传入。不应用 margin(同 anchorOf);用于 `'A.30'` 落点
@@ -177,7 +209,9 @@ var layoutNode = (node, measureText, nameStack, nodeDistance, scopeChain = [], l
177
209
  fontSize: (labFont?.size ?? labelDefault?.font?.size ?? baseFontSize) * fontScale,
178
210
  fontFamily: labFont?.family ?? labelDefault?.font?.family ?? fontFamily,
179
211
  fontWeight: labFont?.weight ?? labelDefault?.font?.weight ?? fontWeight,
180
- fontStyle: labFont?.style ?? labelDefault?.font?.style ?? fontStyle
212
+ fontStyle: labFont?.style ?? labelDefault?.font?.style ?? fontStyle,
213
+ rotate: lab.rotate,
214
+ keepUpright: lab.keepUpright
181
215
  };
182
216
  });
183
217
  return {
@@ -257,37 +291,54 @@ var emitNodePrimitives = (layout, round) => {
257
291
  measuredHeight: round(layout.textHeight)
258
292
  });
259
293
  }
260
- if (layout.labels) for (const lab of layout.labels) {
261
- const [lx, ly] = labelCenter(layout, lab);
262
- inner.push({
263
- type: "text",
264
- x: round(lx),
265
- y: round(ly),
266
- lines: [{ text: lab.text }],
267
- fontSize: lab.fontSize,
268
- fontFamily: lab.fontFamily,
269
- fontWeight: lab.fontWeight,
270
- fontStyle: lab.fontStyle,
271
- align: "middle",
272
- baseline: "middle",
273
- lineHeight: round(lab.fontSize * DEFAULT_LINE_HEIGHT_FACTOR),
274
- fill: lab.textColor ?? "currentColor",
275
- opacity: lab.opacity ?? layout.opacity,
276
- measuredWidth: 0,
277
- measuredHeight: round(lab.fontSize)
278
- });
294
+ if (layout.labels) {
295
+ const cx = layout.rect.x;
296
+ const cy = layout.rect.y;
297
+ for (const lab of layout.labels) {
298
+ const [lx, ly] = labelCenter(layout, lab);
299
+ const textPrim = {
300
+ type: "text",
301
+ x: round(lx),
302
+ y: round(ly),
303
+ lines: [{ text: lab.text }],
304
+ fontSize: lab.fontSize,
305
+ fontFamily: lab.fontFamily,
306
+ fontWeight: lab.fontWeight,
307
+ fontStyle: lab.fontStyle,
308
+ align: "middle",
309
+ baseline: "middle",
310
+ lineHeight: round(lab.fontSize * DEFAULT_LINE_HEIGHT_FACTOR),
311
+ fill: lab.textColor ?? "currentColor",
312
+ opacity: lab.opacity ?? layout.opacity,
313
+ measuredWidth: 0,
314
+ measuredHeight: round(lab.fontSize)
315
+ };
316
+ const deg = resolveLabelRotateDeg(lab, lx, ly, cx, cy);
317
+ if (deg === 0) inner.push(textPrim);
318
+ else inner.push({
319
+ type: "group",
320
+ transforms: [{
321
+ kind: "rotate",
322
+ degrees: round(deg),
323
+ cx: round(lx),
324
+ cy: round(ly)
325
+ }],
326
+ children: [textPrim]
327
+ });
328
+ }
279
329
  }
280
- if (layout.rotateDeg === 0) return inner;
281
- return [{
330
+ if (!(layout.rotateDeg !== 0 || layout.lines !== void 0)) return inner;
331
+ const group = {
282
332
  type: "group",
283
- transforms: [{
284
- kind: "rotate",
285
- degrees: round(layout.rotateDeg),
286
- cx: round(layout.rect.x),
287
- cy: round(layout.rect.y)
288
- }],
289
333
  children: inner
334
+ };
335
+ if (layout.rotateDeg !== 0) group.transforms = [{
336
+ kind: "rotate",
337
+ degrees: round(layout.rotateDeg),
338
+ cx: round(layout.rect.x),
339
+ cy: round(layout.rect.y)
290
340
  }];
341
+ return [group];
291
342
  };
292
343
  //#endregion
293
344
  export { anchorOf, angleBoundaryOf, boundaryPointOf, emitNodePrimitives, layoutNode };
@@ -63,6 +63,8 @@ export declare const NodeLabelSchema: z.ZodObject<{
63
63
  weight?: number | "normal" | "bold" | undefined;
64
64
  style?: "normal" | "italic" | "oblique" | undefined;
65
65
  }>>;
66
+ rotate: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["none", "radial", "tangent"]>, z.ZodNumber]>>;
67
+ keepUpright: z.ZodOptional<z.ZodBoolean>;
66
68
  }, "strip", z.ZodTypeAny, {
67
69
  text: string;
68
70
  distance?: number | undefined;
@@ -75,6 +77,8 @@ export declare const NodeLabelSchema: z.ZodObject<{
75
77
  } | undefined;
76
78
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
77
79
  textColor?: string | undefined;
80
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
81
+ keepUpright?: boolean | undefined;
78
82
  }, {
79
83
  text: string;
80
84
  distance?: number | undefined;
@@ -87,6 +91,8 @@ export declare const NodeLabelSchema: z.ZodObject<{
87
91
  } | undefined;
88
92
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
89
93
  textColor?: string | undefined;
94
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
95
+ keepUpright?: boolean | undefined;
90
96
  }>;
91
97
  /** Node label IR 类型 */
92
98
  export type IRNodeLabel = z.infer<typeof NodeLabelSchema>;
@@ -243,6 +249,8 @@ export declare const NodeSchema: z.ZodObject<{
243
249
  weight?: number | "normal" | "bold" | undefined;
244
250
  style?: "normal" | "italic" | "oblique" | undefined;
245
251
  }>>;
252
+ rotate: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["none", "radial", "tangent"]>, z.ZodNumber]>>;
253
+ keepUpright: z.ZodOptional<z.ZodBoolean>;
246
254
  }, "strip", z.ZodTypeAny, {
247
255
  text: string;
248
256
  distance?: number | undefined;
@@ -255,6 +263,8 @@ export declare const NodeSchema: z.ZodObject<{
255
263
  } | undefined;
256
264
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
257
265
  textColor?: string | undefined;
266
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
267
+ keepUpright?: boolean | undefined;
258
268
  }, {
259
269
  text: string;
260
270
  distance?: number | undefined;
@@ -267,6 +277,8 @@ export declare const NodeSchema: z.ZodObject<{
267
277
  } | undefined;
268
278
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
269
279
  textColor?: string | undefined;
280
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
281
+ keepUpright?: boolean | undefined;
270
282
  }>, z.ZodArray<z.ZodObject<{
271
283
  text: z.ZodString;
272
284
  position: z.ZodOptional<z.ZodUnion<[z.ZodNativeEnum<{
@@ -298,6 +310,8 @@ export declare const NodeSchema: z.ZodObject<{
298
310
  weight?: number | "normal" | "bold" | undefined;
299
311
  style?: "normal" | "italic" | "oblique" | undefined;
300
312
  }>>;
313
+ rotate: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["none", "radial", "tangent"]>, z.ZodNumber]>>;
314
+ keepUpright: z.ZodOptional<z.ZodBoolean>;
301
315
  }, "strip", z.ZodTypeAny, {
302
316
  text: string;
303
317
  distance?: number | undefined;
@@ -310,6 +324,8 @@ export declare const NodeSchema: z.ZodObject<{
310
324
  } | undefined;
311
325
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
312
326
  textColor?: string | undefined;
327
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
328
+ keepUpright?: boolean | undefined;
313
329
  }, {
314
330
  text: string;
315
331
  distance?: number | undefined;
@@ -322,7 +338,10 @@ export declare const NodeSchema: z.ZodObject<{
322
338
  } | undefined;
323
339
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
324
340
  textColor?: string | undefined;
341
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
342
+ keepUpright?: boolean | undefined;
325
343
  }>, "many">]>>;
344
+ zIndex: z.ZodOptional<z.ZodNumber>;
326
345
  }, "strip", z.ZodTypeAny, {
327
346
  type: "node";
328
347
  position: [number, number] | import('./position').PolarPosition | {
@@ -368,6 +387,8 @@ export declare const NodeSchema: z.ZodObject<{
368
387
  } | undefined;
369
388
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
370
389
  textColor?: string | undefined;
390
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
391
+ keepUpright?: boolean | undefined;
371
392
  } | {
372
393
  text: string;
373
394
  distance?: number | undefined;
@@ -380,13 +401,16 @@ export declare const NodeSchema: z.ZodObject<{
380
401
  } | undefined;
381
402
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
382
403
  textColor?: string | undefined;
404
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
405
+ keepUpright?: boolean | undefined;
383
406
  }[] | undefined;
384
407
  stroke?: string | undefined;
385
408
  strokeWidth?: number | undefined;
386
409
  fillOpacity?: number | undefined;
387
410
  drawOpacity?: number | undefined;
388
- id?: string | undefined;
411
+ zIndex?: number | undefined;
389
412
  rotate?: number | undefined;
413
+ id?: string | undefined;
390
414
  align?: "left" | "right" | "center" | undefined;
391
415
  lineHeight?: number | undefined;
392
416
  dashed?: boolean | undefined;
@@ -448,6 +472,8 @@ export declare const NodeSchema: z.ZodObject<{
448
472
  } | undefined;
449
473
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
450
474
  textColor?: string | undefined;
475
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
476
+ keepUpright?: boolean | undefined;
451
477
  } | {
452
478
  text: string;
453
479
  distance?: number | undefined;
@@ -460,13 +486,16 @@ export declare const NodeSchema: z.ZodObject<{
460
486
  } | undefined;
461
487
  position?: number | "above" | "below" | "left" | "right" | "above-left" | "above-right" | "below-left" | "below-right" | undefined;
462
488
  textColor?: string | undefined;
489
+ rotate?: number | "none" | "radial" | "tangent" | undefined;
490
+ keepUpright?: boolean | undefined;
463
491
  }[] | undefined;
464
492
  stroke?: string | undefined;
465
493
  strokeWidth?: number | undefined;
466
494
  fillOpacity?: number | undefined;
467
495
  drawOpacity?: number | undefined;
468
- id?: string | undefined;
496
+ zIndex?: number | undefined;
469
497
  rotate?: number | undefined;
498
+ id?: string | undefined;
470
499
  align?: "left" | "right" | "center" | undefined;
471
500
  lineHeight?: number | undefined;
472
501
  dashed?: boolean | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/ir/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAKxC;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;CAKd,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAEzD,+BAA+B;AAC/B,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCzB,CAAC;AAEJ,uBAAuB;AACvB,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE1D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuLpB,CAAC;AAEJ,sCAAsC;AACtC,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/ir/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAKxC;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;CAKd,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAEzD,+BAA+B;AAC/B,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CzB,CAAC;AAEJ,uBAAuB;AACvB,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE1D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+LpB,CAAC;AAEJ,sCAAsC;AACtC,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
@@ -32,7 +32,13 @@ var NodeLabelSchema = z.object({
32
32
  distance: z.number().nonnegative().optional().describe("Gap between the node border and the label center, in user units. Default 12."),
33
33
  textColor: z.string().optional().describe("Label text color; falls back to currentColor."),
34
34
  opacity: z.number().min(0).max(1).optional().describe("Label-only opacity 0..1; multiplied with the node opacity if both are set."),
35
- font: FontSchema.optional().describe("Label font overrides; missing fields inherit from the parent node font, then renderer defaults.")
35
+ font: FontSchema.optional().describe("Label font overrides; missing fields inherit from the parent node font, then renderer defaults."),
36
+ rotate: z.union([z.enum([
37
+ "none",
38
+ "radial",
39
+ "tangent"
40
+ ]), z.number()]).optional().describe("Rotate the label text around its own center. `none` (default) = horizontal; `radial` = along the node-center -> label-center direction; `tangent` = radial + 90 deg; a number = explicit degrees (screen y-down: 0 = +x, 90 = +y). Only changes text orientation, not placement."),
41
+ keepUpright: z.boolean().optional().describe("When true, flips the rotated label 180 deg if it would otherwise read upside-down (more than 90 deg from upright). Default false (strict geometric angle).")
36
42
  }).describe("Extra text attached around a node border. Multiple labels supported via array form on `Node.label`.");
37
43
  var NodeSchema = z.object({
38
44
  type: z.literal("node").describe("Discriminator marking this child as a node"),
@@ -72,7 +78,8 @@ var NodeSchema = z.object({
72
78
  padding: z.number().nonnegative().optional().describe("Symmetric inner padding (alias for `innerXSep` + `innerYSep`); axis-specific fields take precedence."),
73
79
  margin: z.number().nonnegative().optional().describe("Symmetric outer margin (alias for `outerSep`); axis-specific field takes precedence."),
74
80
  font: FontSchema.optional().describe("Font spec for the inner text label (family / size / weight / style); all fields optional, all fall back to renderer defaults."),
75
- label: z.union([NodeLabelSchema, z.array(NodeLabelSchema)]).optional().describe("Extra label(s) attached around the node border (TikZ `[label=above:foo]`); single object or array form. Compiled into one TextPrim per label, positioned by `position` direction / angle and `distance`.")
81
+ label: z.union([NodeLabelSchema, z.array(NodeLabelSchema)]).optional().describe("Extra label(s) attached around the node border (TikZ `[label=above:foo]`); single object or array form. Compiled into one TextPrim per label, positioned by `position` direction / angle and `distance`."),
82
+ zIndex: z.number().int().finite().optional().describe("Explicit stacking order among sibling IR children. Higher draws on top. Omitted = 0 = source order. Sorting is stable: same zIndex keeps source order. Scoped per group (a node inside a scope only restacks within that scope).")
76
83
  }).describe("Node primitive: a positioned, optionally textual shape (rectangle / circle / ellipse / diamond)");
77
84
  //#endregion
78
85
  export { NODE_SHAPES, NODE_TEXT_ALIGNS, NodeLabelSchema, NodeSchema };
@@ -163,6 +163,7 @@ export declare const PathSchema: z.ZodObject<{
163
163
  opacity: z.ZodOptional<z.ZodNumber>;
164
164
  fillOpacity: z.ZodOptional<z.ZodNumber>;
165
165
  drawOpacity: z.ZodOptional<z.ZodNumber>;
166
+ zIndex: z.ZodOptional<z.ZodNumber>;
166
167
  children: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
167
168
  type: z.ZodLiteral<"step">;
168
169
  kind: z.ZodLiteral<"move">;
@@ -1342,6 +1343,7 @@ export declare const PathSchema: z.ZodObject<{
1342
1343
  thickness?: "ultraThin" | "veryThin" | "thin" | "semithick" | "thick" | "veryThick" | "ultraThick" | undefined;
1343
1344
  fillOpacity?: number | undefined;
1344
1345
  drawOpacity?: number | undefined;
1346
+ zIndex?: number | undefined;
1345
1347
  }, {
1346
1348
  type: "path";
1347
1349
  children: ({
@@ -1582,6 +1584,7 @@ export declare const PathSchema: z.ZodObject<{
1582
1584
  thickness?: "ultraThin" | "veryThin" | "thin" | "semithick" | "thick" | "veryThick" | "ultraThick" | undefined;
1583
1585
  fillOpacity?: number | undefined;
1584
1586
  drawOpacity?: number | undefined;
1587
+ zIndex?: number | undefined;
1585
1588
  }>;
1586
1589
  /** 路径:由若干 step 动作(move/line/...)组成 */
1587
1590
  export type IRPath = z.infer<typeof PathSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../../src/ir/path/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwGpB,CAAC;AAEJ,sCAAsC;AACtC,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../../src/ir/path/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgHpB,CAAC;AAEJ,sCAAsC;AACtC,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
@@ -39,6 +39,7 @@ var PathSchema = z.object({
39
39
  opacity: z.number().min(0).max(1).optional().describe("Whole-path opacity 0..1; multiplies onto stroke and fill."),
40
40
  fillOpacity: z.number().min(0).max(1).optional().describe("Fill opacity 0..1; affects only the closed-region fill."),
41
41
  drawOpacity: z.number().min(0).max(1).optional().describe("Stroke opacity 0..1 (TikZ `draw opacity`); affects only the path stroke."),
42
+ zIndex: z.number().int().finite().optional().describe("Explicit stacking order among sibling IR children. Higher draws on top. Omitted = 0 = source order. Sorting is stable: same zIndex keeps source order. Scoped per group (a path inside a scope only restacks within that scope)."),
42
43
  children: z.array(StepSchema).min(2).describe("Sequence of step actions defining the path; the first should usually be a `move`")
43
44
  }).describe("A drawn path composed of a sequence of step actions (move / line / ...)");
44
45
  //#endregion