@diagrammo/dgmo 0.2.7 → 0.2.9

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/dist/index.d.cts CHANGED
@@ -171,6 +171,7 @@ interface ChartDataPoint {
171
171
  interface ParsedChart {
172
172
  type: ChartType;
173
173
  title?: string;
174
+ titleLineNumber?: number;
174
175
  series?: string;
175
176
  xlabel?: string;
176
177
  ylabel?: string;
@@ -235,6 +236,7 @@ interface ParsedHeatmapRow {
235
236
  interface ParsedEChart {
236
237
  type: EChartsChartType;
237
238
  title?: string;
239
+ titleLineNumber?: number;
238
240
  series?: string;
239
241
  seriesNames?: string[];
240
242
  seriesNameColors?: (string | undefined)[];
@@ -384,6 +386,7 @@ interface D3ExportDimensions {
384
386
  interface ParsedD3 {
385
387
  type: D3ChartType | null;
386
388
  title: string | null;
389
+ titleLineNumber: number | null;
387
390
  orientation: 'horizontal' | 'vertical';
388
391
  periods: string[];
389
392
  data: D3DataItem[];
@@ -546,6 +549,7 @@ interface SequenceNote {
546
549
  position: 'right' | 'left';
547
550
  participantId: string;
548
551
  lineNumber: number;
552
+ endLineNumber: number;
549
553
  }
550
554
  type SequenceElement = SequenceMessage | SequenceBlock | SequenceSection | SequenceNote;
551
555
  declare function isSequenceBlock(el: SequenceElement): el is SequenceBlock;
@@ -564,6 +568,7 @@ interface SequenceGroup {
564
568
  */
565
569
  interface ParsedSequenceDgmo {
566
570
  title: string | null;
571
+ titleLineNumber: number | null;
567
572
  participants: SequenceParticipant[];
568
573
  messages: SequenceMessage[];
569
574
  elements: SequenceElement[];
@@ -660,6 +665,7 @@ interface GraphGroup {
660
665
  interface ParsedGraph {
661
666
  type: 'flowchart';
662
667
  title?: string;
668
+ titleLineNumber?: number;
663
669
  direction: GraphDirection;
664
670
  nodes: GraphNode[];
665
671
  edges: GraphEdge[];
package/dist/index.d.ts CHANGED
@@ -171,6 +171,7 @@ interface ChartDataPoint {
171
171
  interface ParsedChart {
172
172
  type: ChartType;
173
173
  title?: string;
174
+ titleLineNumber?: number;
174
175
  series?: string;
175
176
  xlabel?: string;
176
177
  ylabel?: string;
@@ -235,6 +236,7 @@ interface ParsedHeatmapRow {
235
236
  interface ParsedEChart {
236
237
  type: EChartsChartType;
237
238
  title?: string;
239
+ titleLineNumber?: number;
238
240
  series?: string;
239
241
  seriesNames?: string[];
240
242
  seriesNameColors?: (string | undefined)[];
@@ -384,6 +386,7 @@ interface D3ExportDimensions {
384
386
  interface ParsedD3 {
385
387
  type: D3ChartType | null;
386
388
  title: string | null;
389
+ titleLineNumber: number | null;
387
390
  orientation: 'horizontal' | 'vertical';
388
391
  periods: string[];
389
392
  data: D3DataItem[];
@@ -546,6 +549,7 @@ interface SequenceNote {
546
549
  position: 'right' | 'left';
547
550
  participantId: string;
548
551
  lineNumber: number;
552
+ endLineNumber: number;
549
553
  }
550
554
  type SequenceElement = SequenceMessage | SequenceBlock | SequenceSection | SequenceNote;
551
555
  declare function isSequenceBlock(el: SequenceElement): el is SequenceBlock;
@@ -564,6 +568,7 @@ interface SequenceGroup {
564
568
  */
565
569
  interface ParsedSequenceDgmo {
566
570
  title: string | null;
571
+ titleLineNumber: number | null;
567
572
  participants: SequenceParticipant[];
568
573
  messages: SequenceMessage[];
569
574
  elements: SequenceElement[];
@@ -660,6 +665,7 @@ interface GraphGroup {
660
665
  interface ParsedGraph {
661
666
  type: 'flowchart';
662
667
  title?: string;
668
+ titleLineNumber?: number;
663
669
  direction: GraphDirection;
664
670
  nodes: GraphNode[];
665
671
  edges: GraphEdge[];
package/dist/index.js CHANGED
@@ -196,6 +196,7 @@ function measureIndent(line3) {
196
196
  function parseSequenceDgmo(content) {
197
197
  const result = {
198
198
  title: null,
199
+ titleLineNumber: null,
199
200
  participants: [],
200
201
  messages: [],
201
202
  elements: [],
@@ -299,6 +300,7 @@ function parseSequenceDgmo(content) {
299
300
  }
300
301
  if (key === "title") {
301
302
  result.title = value;
303
+ result.titleLineNumber = lineNumber;
302
304
  continue;
303
305
  }
304
306
  result.options[key] = value;
@@ -528,22 +530,19 @@ function parseSequenceDgmo(content) {
528
530
  const notePosition = noteSingleMatch[1]?.toLowerCase() || "right";
529
531
  let noteParticipant = noteSingleMatch[2] || null;
530
532
  if (!noteParticipant) {
531
- if (!lastMsgFrom) {
532
- result.error = `Line ${lineNumber}: note requires a preceding message`;
533
- return result;
534
- }
533
+ if (!lastMsgFrom) continue;
535
534
  noteParticipant = lastMsgFrom;
536
535
  }
537
536
  if (!result.participants.some((p) => p.id === noteParticipant)) {
538
- result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
539
- return result;
537
+ continue;
540
538
  }
541
539
  const note = {
542
540
  kind: "note",
543
541
  text: noteSingleMatch[3].trim(),
544
542
  position: notePosition,
545
543
  participantId: noteParticipant,
546
- lineNumber
544
+ lineNumber,
545
+ endLineNumber: lineNumber
547
546
  };
548
547
  currentContainer().push(note);
549
548
  continue;
@@ -553,15 +552,11 @@ function parseSequenceDgmo(content) {
553
552
  const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
554
553
  let noteParticipant = noteMultiMatch[2] || null;
555
554
  if (!noteParticipant) {
556
- if (!lastMsgFrom) {
557
- result.error = `Line ${lineNumber}: note requires a preceding message`;
558
- return result;
559
- }
555
+ if (!lastMsgFrom) continue;
560
556
  noteParticipant = lastMsgFrom;
561
557
  }
562
558
  if (!result.participants.some((p) => p.id === noteParticipant)) {
563
- result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
564
- return result;
559
+ continue;
565
560
  }
566
561
  const noteLines = [];
567
562
  while (i + 1 < lines.length) {
@@ -573,16 +568,15 @@ function parseSequenceDgmo(content) {
573
568
  noteLines.push(nextTrimmed);
574
569
  i++;
575
570
  }
576
- if (noteLines.length === 0) {
577
- result.error = `Line ${lineNumber}: multi-line note has no content \u2014 add indented lines or use 'note: text'`;
578
- return result;
579
- }
571
+ if (noteLines.length === 0) continue;
580
572
  const note = {
581
573
  kind: "note",
582
574
  text: noteLines.join("\n"),
583
575
  position: notePosition,
584
576
  participantId: noteParticipant,
585
- lineNumber
577
+ lineNumber,
578
+ endLineNumber: i + 1
579
+ // i has advanced past the body lines (1-based)
586
580
  };
587
581
  currentContainer().push(note);
588
582
  continue;
@@ -630,7 +624,7 @@ var init_parser = __esm({
630
624
  ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
631
625
  UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
632
626
  NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
633
- NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*$/i;
627
+ NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+([^\s:]+))?\s*:?\s*$/i;
634
628
  }
635
629
  });
636
630
 
@@ -975,6 +969,7 @@ function parseFlowchart(content, palette) {
975
969
  }
976
970
  if (key === "title") {
977
971
  result.title = value;
972
+ result.titleLineNumber = lineNumber;
978
973
  continue;
979
974
  }
980
975
  if (key === "direction") {
@@ -2413,7 +2408,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
2413
2408
  const scaledH = diagramH * scale;
2414
2409
  const offsetX = (width - scaledW) / 2;
2415
2410
  const offsetY = (height - scaledH) / 2;
2416
- const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
2411
+ const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("font-family", FONT_FAMILY);
2417
2412
  const defs = svg.append("defs");
2418
2413
  defs.append("marker").attr("id", "fc-arrow").attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", ARROWHEAD_W).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("orient", "auto").append("polygon").attr("points", `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`).attr("fill", palette.textMuted);
2419
2414
  const edgeColors = /* @__PURE__ */ new Set();
@@ -2426,7 +2421,17 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
2426
2421
  }
2427
2422
  const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
2428
2423
  if (graph.title) {
2429
- mainG.append("text").attr("x", diagramW / 2).attr("y", TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", "bold").attr("class", "fc-title").text(graph.title);
2424
+ const titleEl = mainG.append("text").attr("x", diagramW / 2).attr("y", TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", "bold").attr("class", "fc-title chart-title").style("cursor", onClickItem && graph.titleLineNumber ? "pointer" : "default").text(graph.title);
2425
+ if (graph.titleLineNumber) {
2426
+ titleEl.attr("data-line-number", graph.titleLineNumber);
2427
+ if (onClickItem) {
2428
+ titleEl.on("click", () => onClickItem(graph.titleLineNumber)).on("mouseenter", function() {
2429
+ d3Selection.select(this).attr("opacity", 0.7);
2430
+ }).on("mouseleave", function() {
2431
+ d3Selection.select(this).attr("opacity", 1);
2432
+ });
2433
+ }
2434
+ }
2430
2435
  }
2431
2436
  const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
2432
2437
  for (const group of layout.groups) {
@@ -2460,7 +2465,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
2460
2465
  }
2461
2466
  }
2462
2467
  for (const node of layout.nodes) {
2463
- const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
2468
+ const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
2464
2469
  if (onClickItem) {
2465
2470
  nodeG.style("cursor", "pointer").on("click", () => {
2466
2471
  onClickItem(node.lineNumber);
@@ -2543,14 +2548,16 @@ __export(renderer_exports, {
2543
2548
  import * as d3Selection2 from "d3-selection";
2544
2549
  function parseInlineMarkdown(text) {
2545
2550
  const spans = [];
2546
- const regex = /\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*`[]+)/g;
2551
+ const regex = /\*\*(.+?)\*\*|__(.+?)__|\*(.+?)\*|_(.+?)_|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*_`[]+)/g;
2547
2552
  let match;
2548
2553
  while ((match = regex.exec(text)) !== null) {
2549
2554
  if (match[1]) spans.push({ text: match[1], bold: true });
2550
- else if (match[2]) spans.push({ text: match[2], italic: true });
2551
- else if (match[3]) spans.push({ text: match[3], code: true });
2552
- else if (match[4]) spans.push({ text: match[4], href: match[5] });
2553
- else if (match[6]) spans.push({ text: match[6] });
2555
+ else if (match[2]) spans.push({ text: match[2], bold: true });
2556
+ else if (match[3]) spans.push({ text: match[3], italic: true });
2557
+ else if (match[4]) spans.push({ text: match[4], italic: true });
2558
+ else if (match[5]) spans.push({ text: match[5], code: true });
2559
+ else if (match[6]) spans.push({ text: match[6], href: match[7] });
2560
+ else if (match[8]) spans.push({ text: match[8] });
2554
2561
  }
2555
2562
  return spans;
2556
2563
  }
@@ -2949,6 +2956,34 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2949
2956
  if (elements && elements.length > 0) {
2950
2957
  markBlockSpacing(elements);
2951
2958
  }
2959
+ const NOTE_OFFSET_BELOW = 16;
2960
+ const computeNoteHeight = (text) => {
2961
+ const lines = wrapTextLines(text, NOTE_CHARS_PER_LINE);
2962
+ return lines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
2963
+ };
2964
+ const markNoteSpacing = (els) => {
2965
+ for (let i = 0; i < els.length; i++) {
2966
+ const el = els[i];
2967
+ if (isSequenceNote(el)) {
2968
+ const noteH = computeNoteHeight(el.text);
2969
+ const nextIdx = i + 1 < els.length ? findFirstMsgIndex([els[i + 1]]) : -1;
2970
+ if (nextIdx >= 0) {
2971
+ addExtra(nextIdx, noteH + NOTE_OFFSET_BELOW);
2972
+ }
2973
+ } else if (isSequenceBlock(el)) {
2974
+ markNoteSpacing(el.children);
2975
+ if (el.elseIfBranches) {
2976
+ for (const branch of el.elseIfBranches) {
2977
+ markNoteSpacing(branch.children);
2978
+ }
2979
+ }
2980
+ markNoteSpacing(el.elseChildren);
2981
+ }
2982
+ }
2983
+ };
2984
+ if (elements && elements.length > 0) {
2985
+ markNoteSpacing(elements);
2986
+ }
2952
2987
  const preSectionMsgIndices = [];
2953
2988
  const sectionRegions = [];
2954
2989
  {
@@ -3121,7 +3156,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
3121
3156
  `0,0 ${ARROWHEAD_SIZE},${ARROWHEAD_SIZE / 2} 0,${ARROWHEAD_SIZE}`
3122
3157
  ).attr("fill", "none").attr("stroke", palette.text).attr("stroke-width", 1.2);
3123
3158
  if (title) {
3124
- svg.append("text").attr("x", svgWidth / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 20).attr("font-weight", "bold").text(title);
3159
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", svgWidth / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 20).attr("font-weight", "bold").text(title);
3160
+ if (parsed.titleLineNumber) {
3161
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
3162
+ }
3125
3163
  }
3126
3164
  for (const group of groups) {
3127
3165
  if (group.participantIds.length === 0) continue;
@@ -3460,8 +3498,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
3460
3498
  );
3461
3499
  const isRight = el.position === "right";
3462
3500
  const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
3463
- const noteTopY = noteY - noteH / 2;
3464
- const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber));
3501
+ const noteTopY = noteY + NOTE_OFFSET_BELOW;
3502
+ const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber)).attr("data-line-end", String(el.endLineNumber));
3465
3503
  noteG.append("path").attr(
3466
3504
  "d",
3467
3505
  [
@@ -3481,13 +3519,16 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
3481
3519
  `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
3482
3520
  ].join(" ")
3483
3521
  ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
3484
- const connectorNoteX = isRight ? noteX : noteX + noteW;
3485
- const connectorLifeX = isRight ? px + ACTIVATION_WIDTH / 2 : px - ACTIVATION_WIDTH / 2;
3486
- noteG.append("line").attr("x1", connectorNoteX).attr("y1", noteY).attr("x2", connectorLifeX).attr("y2", noteY).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("stroke-dasharray", "3 2").attr("class", "note-connector");
3487
3522
  wrappedLines.forEach((line3, li) => {
3488
3523
  const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
3489
- const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
3490
- const spans = parseInlineMarkdown(line3);
3524
+ const isBullet = line3.startsWith("- ");
3525
+ const bulletIndent = isBullet ? 10 : 0;
3526
+ const displayLine = isBullet ? line3.slice(2) : line3;
3527
+ const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H + bulletIndent).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
3528
+ if (isBullet) {
3529
+ noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
3530
+ }
3531
+ const spans = parseInlineMarkdown(displayLine);
3491
3532
  for (const span of spans) {
3492
3533
  if (span.href) {
3493
3534
  const a = textEl.append("a").attr("href", span.href);
@@ -3637,6 +3678,7 @@ function parseChart(content, palette) {
3637
3678
  }
3638
3679
  if (key === "title") {
3639
3680
  result.title = value;
3681
+ result.titleLineNumber = lineNumber;
3640
3682
  continue;
3641
3683
  }
3642
3684
  if (key === "xlabel") {
@@ -3778,6 +3820,7 @@ function parseEChart(content, palette) {
3778
3820
  }
3779
3821
  if (key === "title") {
3780
3822
  result.title = value;
3823
+ result.titleLineNumber = lineNumber;
3781
3824
  continue;
3782
3825
  }
3783
3826
  if (key === "series") {
@@ -5139,6 +5182,7 @@ function parseD3(content, palette) {
5139
5182
  const result = {
5140
5183
  type: null,
5141
5184
  title: null,
5185
+ titleLineNumber: null,
5142
5186
  orientation: "horizontal",
5143
5187
  periods: [],
5144
5188
  data: [],
@@ -5400,6 +5444,7 @@ function parseD3(content, palette) {
5400
5444
  }
5401
5445
  if (key === "title") {
5402
5446
  result.title = line3.substring(colonIndex + 1).trim();
5447
+ result.titleLineNumber = lineNumber;
5403
5448
  if (result.type === "quadrant") {
5404
5449
  result.quadrantTitleLineNumber = lineNumber;
5405
5450
  }
@@ -5762,7 +5807,17 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
5762
5807
  const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
5763
5808
  const tooltip = createTooltip(container, palette, isDark);
5764
5809
  if (title) {
5765
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
5810
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
5811
+ if (parsed.titleLineNumber) {
5812
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
5813
+ if (onClickItem) {
5814
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
5815
+ d3Selection3.select(this).attr("opacity", 0.7);
5816
+ }).on("mouseleave", function() {
5817
+ d3Selection3.select(this).attr("opacity", 1);
5818
+ });
5819
+ }
5820
+ }
5766
5821
  }
5767
5822
  for (const period of periods) {
5768
5823
  const x = xScale(period);
@@ -5971,7 +6026,17 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
5971
6026
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5972
6027
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5973
6028
  if (title) {
5974
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
6029
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
6030
+ if (parsed.titleLineNumber) {
6031
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
6032
+ if (onClickItem) {
6033
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
6034
+ d3Selection3.select(this).attr("opacity", 0.7);
6035
+ }).on("mouseleave", function() {
6036
+ d3Selection3.select(this).attr("opacity", 1);
6037
+ });
6038
+ }
6039
+ }
5975
6040
  }
5976
6041
  const neighbors = /* @__PURE__ */ new Map();
5977
6042
  for (const node of nodes) neighbors.set(node, /* @__PURE__ */ new Set());
@@ -6523,7 +6588,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
6523
6588
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6524
6589
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
6525
6590
  if (title) {
6526
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
6591
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
6592
+ if (parsed.titleLineNumber) {
6593
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
6594
+ if (onClickItem) {
6595
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
6596
+ d3Selection3.select(this).attr("opacity", 0.7);
6597
+ }).on("mouseleave", function() {
6598
+ d3Selection3.select(this).attr("opacity", 1);
6599
+ });
6600
+ }
6601
+ }
6527
6602
  }
6528
6603
  renderEras(
6529
6604
  g,
@@ -6617,7 +6692,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
6617
6692
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6618
6693
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
6619
6694
  if (title) {
6620
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
6695
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
6696
+ if (parsed.titleLineNumber) {
6697
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
6698
+ if (onClickItem) {
6699
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
6700
+ d3Selection3.select(this).attr("opacity", 0.7);
6701
+ }).on("mouseleave", function() {
6702
+ d3Selection3.select(this).attr("opacity", 1);
6703
+ });
6704
+ }
6705
+ }
6621
6706
  }
6622
6707
  renderEras(
6623
6708
  g,
@@ -6740,7 +6825,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
6740
6825
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6741
6826
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
6742
6827
  if (title) {
6743
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
6828
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
6829
+ if (parsed.titleLineNumber) {
6830
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
6831
+ if (onClickItem) {
6832
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
6833
+ d3Selection3.select(this).attr("opacity", 0.7);
6834
+ }).on("mouseleave", function() {
6835
+ d3Selection3.select(this).attr("opacity", 1);
6836
+ });
6837
+ }
6838
+ }
6744
6839
  }
6745
6840
  renderEras(
6746
6841
  g,
@@ -6885,7 +6980,17 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
6885
6980
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6886
6981
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
6887
6982
  if (title) {
6888
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
6983
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
6984
+ if (parsed.titleLineNumber) {
6985
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
6986
+ if (onClickItem) {
6987
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
6988
+ d3Selection3.select(this).attr("opacity", 0.7);
6989
+ }).on("mouseleave", function() {
6990
+ d3Selection3.select(this).attr("opacity", 1);
6991
+ });
6992
+ }
6993
+ }
6889
6994
  }
6890
6995
  renderEras(
6891
6996
  g,
@@ -7036,7 +7141,17 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
7036
7141
  const rotateFn = getRotateFn(cloudOptions.rotate);
7037
7142
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7038
7143
  if (title) {
7039
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
7144
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
7145
+ if (parsed.titleLineNumber) {
7146
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
7147
+ if (onClickItem) {
7148
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
7149
+ d3Selection3.select(this).attr("opacity", 0.7);
7150
+ }).on("mouseleave", function() {
7151
+ d3Selection3.select(this).attr("opacity", 1);
7152
+ });
7153
+ }
7154
+ }
7040
7155
  }
7041
7156
  const g = svg.append("g").attr(
7042
7157
  "transform",
@@ -7086,7 +7201,10 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
7086
7201
  const rotateFn = getRotateFn(cloudOptions.rotate);
7087
7202
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7088
7203
  if (title) {
7089
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
7204
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
7205
+ if (parsed.titleLineNumber) {
7206
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
7207
+ }
7090
7208
  }
7091
7209
  const g = svg.append("g").attr(
7092
7210
  "transform",
@@ -7296,7 +7414,17 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
7296
7414
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7297
7415
  const tooltip = createTooltip(container, palette, isDark);
7298
7416
  if (title) {
7299
- svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
7417
+ const titleEl = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style("cursor", onClickItem && parsed.titleLineNumber ? "pointer" : "default").text(title);
7418
+ if (parsed.titleLineNumber) {
7419
+ titleEl.attr("data-line-number", parsed.titleLineNumber);
7420
+ if (onClickItem) {
7421
+ titleEl.on("click", () => onClickItem(parsed.titleLineNumber)).on("mouseenter", function() {
7422
+ d3Selection3.select(this).attr("opacity", 0.7);
7423
+ }).on("mouseleave", function() {
7424
+ d3Selection3.select(this).attr("opacity", 1);
7425
+ });
7426
+ }
7427
+ }
7300
7428
  }
7301
7429
  const defs = svg.append("defs");
7302
7430
  const pad = 20;
@@ -7474,10 +7602,13 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
7474
7602
  const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7475
7603
  const tooltip = createTooltip(container, palette, isDark);
7476
7604
  if (title) {
7477
- const titleText = svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style(
7605
+ const titleText = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style(
7478
7606
  "cursor",
7479
7607
  onClickItem && quadrantTitleLineNumber ? "pointer" : "default"
7480
7608
  ).text(title);
7609
+ if (quadrantTitleLineNumber) {
7610
+ titleText.attr("data-line-number", quadrantTitleLineNumber);
7611
+ }
7481
7612
  if (onClickItem && quadrantTitleLineNumber) {
7482
7613
  titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
7483
7614
  d3Selection3.select(this).attr("opacity", 0.7);