@zombie-mermaid/svg-renderer 2.2.6 → 3.1.0

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.
@@ -15,6 +15,7 @@ import {
15
15
  buildStyleBlock,
16
16
  renderMultilineText,
17
17
  escapeAttr,
18
+ f,
18
19
  } from '@zombie-mermaid/core'
19
20
  import { withDataSrc } from '../renderer.ts'
20
21
  import {
@@ -140,12 +141,12 @@ function arrowMarkerDefs(): string {
140
141
  const w = ARROW_HEAD.width
141
142
  const h = ARROW_HEAD.height
142
143
  return (
143
- ` <marker id="seq-arrow" markerWidth="${w}" markerHeight="${h}" refX="${w}" refY="${h / 2}" orient="auto-start-reverse">` +
144
- `\n <polygon points="0 0, ${w} ${h / 2}, 0 ${h}" fill="var(--_arrow)" />` +
144
+ f` <marker id="seq-arrow" markerWidth="${w}" markerHeight="${h}" refX="${w}" refY="${h / 2}" orient="auto-start-reverse">` +
145
+ f`\n <polygon points="0 0, ${w} ${h / 2}, 0 ${h}" fill="var(--_arrow)" />` +
145
146
  `\n </marker>` +
146
147
  // Open arrow head (just lines, no fill)
147
- `\n <marker id="seq-arrow-open" markerWidth="${w}" markerHeight="${h}" refX="${w}" refY="${h / 2}" orient="auto-start-reverse">` +
148
- `\n <polyline points="0 0, ${w} ${h / 2}, 0 ${h}" fill="none" stroke="var(--_arrow)" stroke-width="1" />` +
148
+ f`\n <marker id="seq-arrow-open" markerWidth="${w}" markerHeight="${h}" refX="${w}" refY="${h / 2}" orient="auto-start-reverse">` +
149
+ f`\n <polyline points="0 0, ${w} ${h / 2}, 0 ${h}" fill="none" stroke="var(--_arrow)" stroke-width="1" />` +
149
150
  `\n </marker>`
150
151
  )
151
152
  }
@@ -164,7 +165,7 @@ function renderActor(actor: PositionedActor, fontSizes: FontSizes): string {
164
165
 
165
166
  // Semantic wrapper with actor metadata
166
167
  parts.push(
167
- `<g class="actor" data-id="${escapeAttr(id)}" data-label="${escapeAttr(label)}" data-type="${type}">`,
168
+ f`<g class="actor" data-id="${escapeAttr(id)}" data-label="${escapeAttr(label)}" data-type="${type}">`,
168
169
  )
169
170
 
170
171
  if (type === 'actor') {
@@ -179,13 +180,13 @@ function renderActor(actor: PositionedActor, fontSizes: FontSizes): string {
179
180
  const iconStroke = 'var(--_line)' // use line color for actor icon strokes
180
181
 
181
182
  parts.push(
182
- ` <g transform="translate(${tx},${ty}) scale(${s})">` +
183
+ f` <g transform="translate(${tx},${ty}) scale(${s})">` +
183
184
  // Outer circle
184
- `\n <path d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" fill="none" stroke="${iconStroke}" stroke-width="${sw}" />` +
185
+ f`\n <path d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" fill="none" stroke="${iconStroke}" stroke-width="${sw}" />` +
185
186
  // Head
186
- `\n <path d="M15 10C15 11.6569 13.6569 13 12 13C10.3431 13 9 11.6569 9 10C9 8.34315 10.3431 7 12 7C13.6569 7 15 8.34315 15 10Z" fill="none" stroke="${iconStroke}" stroke-width="${sw}" />` +
187
+ f`\n <path d="M15 10C15 11.6569 13.6569 13 12 13C10.3431 13 9 11.6569 9 10C9 8.34315 10.3431 7 12 7C13.6569 7 15 8.34315 15 10Z" fill="none" stroke="${iconStroke}" stroke-width="${sw}" />` +
187
188
  // Shoulders
188
- `\n <path d="M5.62842 18.3563C7.08963 17.0398 9.39997 16 12 16C14.6 16 16.9104 17.0398 18.3716 18.3563" fill="none" stroke="${iconStroke}" stroke-width="${sw}" />` +
189
+ f`\n <path d="M5.62842 18.3563C7.08963 17.0398 9.39997 16 12 16C14.6 16 16.9104 17.0398 18.3716 18.3563" fill="none" stroke="${iconStroke}" stroke-width="${sw}" />` +
189
190
  `\n </g>`,
190
191
  )
191
192
  // Label below the icon (supports multi-line)
@@ -196,15 +197,15 @@ function renderActor(actor: PositionedActor, fontSizes: FontSizes): string {
196
197
  x,
197
198
  y + height + 14,
198
199
  fontSizes.nodeLabel,
199
- `font-size="${fontSizes.nodeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.nodeLabel}" fill="var(--_text)"`,
200
+ f`font-size="${fontSizes.nodeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.nodeLabel}" fill="var(--_text)"`,
200
201
  ),
201
202
  )
202
203
  } else {
203
204
  // Participant: rectangle box with label (supports multi-line)
204
205
  const boxX = x - width / 2
205
206
  parts.push(
206
- ` <rect x="${boxX}" y="${y}" width="${width}" height="${height}" rx="4" ry="4" ` +
207
- `fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.outerBox}" />`,
207
+ f` <rect x="${boxX}" y="${y}" width="${width}" height="${height}" rx="4" ry="4" ` +
208
+ f`fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.outerBox}" />`,
208
209
  )
209
210
  parts.push(
210
211
  ' ' +
@@ -213,7 +214,7 @@ function renderActor(actor: PositionedActor, fontSizes: FontSizes): string {
213
214
  x,
214
215
  y + height / 2,
215
216
  fontSizes.nodeLabel,
216
- `font-size="${fontSizes.nodeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.nodeLabel}" fill="var(--_text)"`,
217
+ f`font-size="${fontSizes.nodeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.nodeLabel}" fill="var(--_text)"`,
217
218
  ),
218
219
  )
219
220
  }
@@ -228,8 +229,8 @@ function renderActor(actor: PositionedActor, fontSizes: FontSizes): string {
228
229
  */
229
230
  function renderLifeline(lifeline: Lifeline): string {
230
231
  const line =
231
- `<line class="lifeline" data-actor="${escapeAttr(lifeline.actorId)}" ` +
232
- `x1="${lifeline.x}" y1="${lifeline.topY}" x2="${lifeline.x}" y2="${lifeline.bottomY}" ` +
232
+ f`<line class="lifeline" data-actor="${escapeAttr(lifeline.actorId)}" ` +
233
+ f`x1="${lifeline.x}" y1="${lifeline.topY}" x2="${lifeline.x}" y2="${lifeline.bottomY}" ` +
233
234
  `stroke="var(--_line)" stroke-width="0.75" stroke-dasharray="6 4" />`
234
235
  if (!lifeline.destroyed) return line
235
236
  // `destroy X`: the lifeline ends at the destroying message's row, marked
@@ -239,9 +240,9 @@ function renderLifeline(lifeline: Lifeline): string {
239
240
  const r = DESTROY_CROSS_HALF
240
241
  return (
241
242
  line +
242
- `\n<path class="destroy" data-actor="${escapeAttr(lifeline.actorId)}" ` +
243
- `d="M${x - r} ${y - r} L${x + r} ${y + r} M${x + r} ${y - r} L${x - r} ${y + r}" ` +
244
- `fill="none" stroke="var(--_line)" stroke-width="${STROKE_WIDTHS.outerBox}" stroke-linecap="round" />`
243
+ f`\n<path class="destroy" data-actor="${escapeAttr(lifeline.actorId)}" ` +
244
+ f`d="M${x - r} ${y - r} L${x + r} ${y + r} M${x + r} ${y - r} L${x - r} ${y + r}" ` +
245
+ f`fill="none" stroke="var(--_line)" stroke-width="${STROKE_WIDTHS.outerBox}" stroke-linecap="round" />`
245
246
  )
246
247
  }
247
248
 
@@ -254,9 +255,9 @@ const DESTROY_CROSS_HALF = 8
254
255
  */
255
256
  function renderActivation(activation: Activation): string {
256
257
  return (
257
- `<rect class="activation" data-actor="${escapeAttr(activation.actorId)}" ` +
258
- `x="${activation.x}" y="${activation.topY}" width="${activation.width}" height="${activation.bottomY - activation.topY}" ` +
259
- `fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />`
258
+ f`<rect class="activation" data-actor="${escapeAttr(activation.actorId)}" ` +
259
+ f`x="${activation.x}" y="${activation.topY}" width="${activation.width}" height="${activation.bottomY - activation.topY}" ` +
260
+ f`fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />`
260
261
  )
261
262
  }
262
263
 
@@ -273,14 +274,14 @@ function renderMessage(msg: PositionedMessage, fontSizes: FontSizes): string {
273
274
  // automatically flips 180° for marker-start so it points outward at the
274
275
  // line's start instead of reusing the marker-end orientation.
275
276
  const markerStart = msg.bidirectional
276
- ? ` marker-start="url(#${markerId})"`
277
+ ? f` marker-start="url(#${markerId})"`
277
278
  : ''
278
279
 
279
280
  // Semantic wrapper with message metadata
280
281
  parts.push(
281
- `<g class="message" data-from="${escapeAttr(msg.from)}" data-to="${escapeAttr(msg.to)}" ` +
282
- `data-label="${escapeAttr(msg.label)}" data-line-style="${msg.lineStyle}" ` +
283
- `data-arrow-head="${msg.arrowHead}" data-self="${msg.isSelf}" data-bidirectional="${msg.bidirectional}">`,
282
+ f`<g class="message" data-from="${escapeAttr(msg.from)}" data-to="${escapeAttr(msg.to)}" ` +
283
+ f`data-label="${escapeAttr(msg.label)}" data-line-style="${msg.lineStyle}" ` +
284
+ f`data-arrow-head="${msg.arrowHead}" data-self="${msg.isSelf}" data-bidirectional="${msg.bidirectional}">`,
284
285
  )
285
286
 
286
287
  if (msg.isSelf) {
@@ -290,8 +291,8 @@ function renderMessage(msg: PositionedMessage, fontSizes: FontSizes): string {
290
291
  const loopH = 20
291
292
  const labelPadding = 8 // Space between loop and label
292
293
  parts.push(
293
- ` <polyline points="${msg.x1},${msg.y} ${msg.x1 + loopW},${msg.y} ${msg.x1 + loopW},${msg.y + loopH} ${msg.x2},${msg.y + loopH}" ` +
294
- `fill="none" stroke="var(--_line)" stroke-width="${STROKE_WIDTHS.connector}"${dashArray} marker-end="url(#${markerId})"${markerStart} />`,
294
+ f` <polyline points="${msg.x1},${msg.y} ${msg.x1 + loopW},${msg.y} ${msg.x1 + loopW},${msg.y + loopH} ${msg.x2},${msg.y + loopH}" ` +
295
+ f`fill="none" stroke="var(--_line)" stroke-width="${STROKE_WIDTHS.connector}"${dashArray} marker-end="url(#${markerId})"${markerStart} />`,
295
296
  )
296
297
  // Label to the right of the loop (supports multi-line)
297
298
  parts.push(
@@ -301,14 +302,14 @@ function renderMessage(msg: PositionedMessage, fontSizes: FontSizes): string {
301
302
  msg.x1 + loopW + labelPadding,
302
303
  msg.y + loopH / 2,
303
304
  fontSizes.edgeLabel,
304
- `font-size="${fontSizes.edgeLabel}" text-anchor="start" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
305
+ f`font-size="${fontSizes.edgeLabel}" text-anchor="start" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
305
306
  ),
306
307
  )
307
308
  } else {
308
309
  // Normal message: horizontal arrow
309
310
  parts.push(
310
- ` <line x1="${msg.x1}" y1="${msg.y}" x2="${msg.x2}" y2="${msg.y}" ` +
311
- `stroke="var(--_line)" stroke-width="${STROKE_WIDTHS.connector}"${dashArray} marker-end="url(#${markerId})"${markerStart} />`,
311
+ f` <line x1="${msg.x1}" y1="${msg.y}" x2="${msg.x2}" y2="${msg.y}" ` +
312
+ f`stroke="var(--_line)" stroke-width="${STROKE_WIDTHS.connector}"${dashArray} marker-end="url(#${markerId})"${markerStart} />`,
312
313
  )
313
314
  // Label above the arrow, centered (supports multi-line)
314
315
  const midX = (msg.x1 + msg.x2) / 2
@@ -319,7 +320,7 @@ function renderMessage(msg: PositionedMessage, fontSizes: FontSizes): string {
319
320
  midX,
320
321
  msg.y - 10,
321
322
  fontSizes.edgeLabel,
322
- `font-size="${fontSizes.edgeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
323
+ f`font-size="${fontSizes.edgeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
323
324
  ),
324
325
  )
325
326
  }
@@ -353,13 +354,13 @@ function renderSeqNumberBadge(
353
354
  : msg.x1
354
355
  return (
355
356
  `<g class="seq-number">` +
356
- `<circle cx="${cx}" cy="${msg.y}" r="${radius}" fill="var(--bg)" stroke="var(--_arrow)" stroke-width="${STROKE_WIDTHS.innerBox}" />` +
357
+ f`<circle cx="${cx}" cy="${msg.y}" r="${radius}" fill="var(--bg)" stroke="var(--_arrow)" stroke-width="${STROKE_WIDTHS.innerBox}" />` +
357
358
  renderMultilineText(
358
359
  String(msg.seqNumber),
359
360
  cx,
360
361
  msg.y,
361
362
  fontSize,
362
- `font-size="${fontSize}" text-anchor="middle" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_arrow)"`,
363
+ f`font-size="${fontSize}" text-anchor="middle" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_arrow)"`,
363
364
  ) +
364
365
  `</g>`
365
366
  )
@@ -389,17 +390,17 @@ function renderParticipantBox(
389
390
  box: PositionedParticipantBox,
390
391
  fontSizes: FontSizes,
391
392
  ): string {
392
- const labelAttr = box.label ? ` data-label="${escapeAttr(box.label)}"` : ''
393
+ const labelAttr = box.label ? f` data-label="${escapeAttr(box.label)}"` : ''
393
394
  const colorAttr =
394
- box.color !== undefined ? ` data-color="${escapeAttr(box.color)}"` : ''
395
+ box.color !== undefined ? f` data-color="${escapeAttr(box.color)}"` : ''
395
396
  const fill =
396
397
  box.color !== undefined
397
- ? `color-mix(in srgb, ${escapeAttr(box.color)} ${BOX_COLOR_MIX_PERCENT}%, var(--bg))`
398
+ ? f`color-mix(in srgb, ${escapeAttr(box.color)} ${BOX_COLOR_MIX_PERCENT}%, var(--bg))`
398
399
  : 'none'
399
400
  const parts: string[] = [
400
- `<g class="participant-box"${labelAttr}${colorAttr}>`,
401
- ` <rect x="${box.x}" y="${box.y}" width="${box.width}" height="${box.height}" rx="4" ry="4" ` +
402
- `fill="${fill}" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />`,
401
+ f`<g class="participant-box"${labelAttr}${colorAttr}>`,
402
+ f` <rect x="${box.x}" y="${box.y}" width="${box.width}" height="${box.height}" rx="4" ry="4" ` +
403
+ f`fill="${fill}" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />`,
403
404
  ]
404
405
  if (box.label) {
405
406
  // Centred in the label band the layout reserved above the actor boxes.
@@ -410,7 +411,7 @@ function renderParticipantBox(
410
411
  box.x + box.width / 2,
411
412
  box.y + boxLabelHeight(fontSizes.edgeLabel) / 2,
412
413
  fontSizes.edgeLabel,
413
- `font-size="${fontSizes.edgeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.groupHeader}" fill="var(--_text-sec)"`,
414
+ f`font-size="${fontSizes.edgeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.groupHeader}" fill="var(--_text-sec)"`,
414
415
  ),
415
416
  )
416
417
  }
@@ -427,21 +428,21 @@ function renderBlock(block: PositionedBlock, fontSizes: FontSizes): string {
427
428
 
428
429
  // Semantic wrapper with block metadata
429
430
  const labelAttr = block.label
430
- ? ` data-label="${escapeAttr(block.label)}"`
431
+ ? f` data-label="${escapeAttr(block.label)}"`
431
432
  : ''
432
433
  parts.push(
433
- `<g class="block" data-type="${escapeAttr(block.type)}"${labelAttr}>`,
434
+ f`<g class="block" data-type="${escapeAttr(block.type)}"${labelAttr}>`,
434
435
  )
435
436
 
436
437
  // Outer rectangle
437
438
  parts.push(
438
- ` <rect x="${block.x}" y="${block.y}" width="${block.width}" height="${block.height}" ` +
439
- `rx="0" ry="0" fill="none" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.outerBox}" />`,
439
+ f` <rect x="${block.x}" y="${block.y}" width="${block.width}" height="${block.height}" ` +
440
+ f`rx="0" ry="0" fill="none" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.outerBox}" />`,
440
441
  )
441
442
 
442
443
  // Type label tab (top-left corner)
443
444
  // For multi-line block labels, we use the first line for the tab but show full label
444
- const labelText = `${block.type}${block.label ? ` [${block.label}]` : ''}`
445
+ const labelText = f`${block.type}${block.label ? f` [${block.label}]` : ''}`
445
446
  // Audited for issue #100: `String.prototype.split` always returns an
446
447
  // array with at least one element (even splitting `''` yields `['']`),
447
448
  // so index 0 is guaranteed to exist regardless of whether `labelText`
@@ -457,8 +458,8 @@ function renderBlock(block: PositionedBlock, fontSizes: FontSizes): string {
457
458
  const tabHeight = 18
458
459
 
459
460
  parts.push(
460
- ` <rect x="${block.x}" y="${block.y}" width="${tabWidth}" height="${tabHeight}" ` +
461
- `fill="var(--_group-hdr)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.outerBox}" />`,
461
+ f` <rect x="${block.x}" y="${block.y}" width="${tabWidth}" height="${tabHeight}" ` +
462
+ f`fill="var(--_group-hdr)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.outerBox}" />`,
462
463
  )
463
464
  // Block type label (supports multi-line via <br> tags)
464
465
  parts.push(
@@ -468,14 +469,14 @@ function renderBlock(block: PositionedBlock, fontSizes: FontSizes): string {
468
469
  block.x + 6,
469
470
  block.y + tabHeight / 2,
470
471
  fontSizes.edgeLabel,
471
- `font-size="${fontSizes.edgeLabel}" font-weight="${FONT_WEIGHTS.groupHeader}" fill="var(--_text-sec)"`,
472
+ f`font-size="${fontSizes.edgeLabel}" font-weight="${FONT_WEIGHTS.groupHeader}" fill="var(--_text-sec)"`,
472
473
  ),
473
474
  )
474
475
 
475
476
  // Divider lines (for alt/else, par/and)
476
477
  for (const divider of block.dividers) {
477
478
  parts.push(
478
- ` <line x1="${block.x}" y1="${divider.y}" x2="${block.x + block.width}" y2="${divider.y}" ` +
479
+ f` <line x1="${block.x}" y1="${divider.y}" x2="${block.x + block.width}" y2="${divider.y}" ` +
479
480
  `stroke="var(--_line)" stroke-width="0.75" stroke-dasharray="6 4" />`,
480
481
  )
481
482
  if (divider.label) {
@@ -483,11 +484,11 @@ function renderBlock(block: PositionedBlock, fontSizes: FontSizes): string {
483
484
  parts.push(
484
485
  ' ' +
485
486
  renderMultilineText(
486
- `[${divider.label}]`,
487
+ f`[${divider.label}]`,
487
488
  block.x + 8,
488
489
  divider.y + 14,
489
490
  fontSizes.edgeLabel,
490
- `font-size="${fontSizes.edgeLabel}" text-anchor="start" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
491
+ f`font-size="${fontSizes.edgeLabel}" text-anchor="start" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
491
492
  ),
492
493
  )
493
494
  }
@@ -509,37 +510,37 @@ function renderNote(note: PositionedNote, fontSizes: FontSizes): string {
509
510
  // Build actor reference attribute if present
510
511
  const actorsAttr =
511
512
  note.actors && note.actors.length > 0
512
- ? ` data-actors="${note.actors.map(escapeAttr).join(',')}"`
513
+ ? f` data-actors="${note.actors.map(escapeAttr).join(',')}"`
513
514
  : ''
514
515
  const positionAttr = note.position
515
- ? ` data-position="${escapeAttr(note.position)}"`
516
+ ? f` data-position="${escapeAttr(note.position)}"`
516
517
  : ''
517
518
 
518
519
  // Note body: polygon with top-right corner cut off
519
520
  // (x,y) → (x+w-fold,y) → (x+w,y+fold) → (x+w,y+h) → (x,y+h)
520
521
  const bodyPoints = [
521
- `${x},${y}`,
522
- `${x + w - foldSize},${y}`,
523
- `${x + w},${y + foldSize}`,
524
- `${x + w},${y + h}`,
525
- `${x},${y + h}`,
522
+ f`${x},${y}`,
523
+ f`${x + w - foldSize},${y}`,
524
+ f`${x + w},${y + foldSize}`,
525
+ f`${x + w},${y + h}`,
526
+ f`${x},${y + h}`,
526
527
  ].join(' ')
527
528
 
528
529
  return (
529
- `<g class="note"${positionAttr}${actorsAttr}>` +
530
+ f`<g class="note"${positionAttr}${actorsAttr}>` +
530
531
  // Note body with bg fill and clipped corner
531
- `\n <polygon points="${bodyPoints}" ` +
532
- `fill="var(--bg)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />` +
532
+ f`\n <polygon points="${bodyPoints}" ` +
533
+ f`fill="var(--bg)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />` +
533
534
  // Fold triangle (the folded-over corner)
534
- `\n <polygon points="${x + w - foldSize},${y} ${x + w},${y + foldSize} ${x + w - foldSize},${y + foldSize}" ` +
535
- `fill="var(--_inner-stroke)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />` +
535
+ f`\n <polygon points="${x + w - foldSize},${y} ${x + w},${y + foldSize} ${x + w - foldSize},${y + foldSize}" ` +
536
+ f`fill="var(--_inner-stroke)" stroke="var(--_node-stroke)" stroke-width="${STROKE_WIDTHS.innerBox}" />` +
536
537
  // Note text (supports multi-line)
537
- `\n ${renderMultilineText(
538
+ f`\n ${renderMultilineText(
538
539
  note.text,
539
540
  x + w / 2,
540
541
  y + h / 2,
541
542
  fontSizes.edgeLabel,
542
- `font-size="${fontSizes.edgeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
543
+ f`font-size="${fontSizes.edgeLabel}" text-anchor="middle" font-weight="${FONT_WEIGHTS.edgeLabel}" fill="var(--_text-muted)"`,
543
544
  )}` +
544
545
  `\n</g>`
545
546
  )
package/src/layout.ts DELETED
@@ -1,8 +0,0 @@
1
- /**
2
- * Layout module for flowchart and state diagrams.
3
- *
4
- * Uses ELK.js for graph layout — battle-tested, full subgraph support,
5
- * orthogonal edge routing, and direction overrides.
6
- */
7
-
8
- export { layoutGraphSync } from './layout-engine.ts'