@diagrammo/dgmo 0.1.3 → 0.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/cli.ts CHANGED
@@ -4,6 +4,7 @@ import { JSDOM } from 'jsdom';
4
4
  import { Resvg } from '@resvg/resvg-js';
5
5
  import { renderD3ForExport } from './d3';
6
6
  import { getPalette } from './palettes/registry';
7
+ import { DEFAULT_FONT_NAME } from './fonts';
7
8
 
8
9
  const PALETTES = [
9
10
  'nord',
@@ -128,6 +129,11 @@ function svgToPng(svg: string, background?: string): Buffer {
128
129
  const resvg = new Resvg(svg, {
129
130
  fitTo: { mode: 'zoom', value: 2 },
130
131
  ...(background ? { background } : {}),
132
+ font: {
133
+ loadSystemFonts: true,
134
+ defaultFontFamily: DEFAULT_FONT_NAME,
135
+ sansSerifFamily: DEFAULT_FONT_NAME,
136
+ },
131
137
  });
132
138
  const rendered = resvg.render();
133
139
  return rendered.asPng();
package/src/d3.ts CHANGED
@@ -3,6 +3,7 @@ import * as d3Selection from 'd3-selection';
3
3
  import * as d3Shape from 'd3-shape';
4
4
  import * as d3Array from 'd3-array';
5
5
  import cloud from 'd3-cloud';
6
+ import { FONT_FAMILY } from './fonts';
6
7
 
7
8
  // ============================================================
8
9
  // Types
@@ -3770,13 +3771,13 @@ export function renderWordCloud(
3770
3771
  .padding(4)
3771
3772
  .rotate(rotateFn)
3772
3773
  .fontSize((d) => d.size!)
3773
- .font('system-ui, -apple-system, sans-serif')
3774
+ .font(FONT_FAMILY)
3774
3775
  .on('end', (layoutWords) => {
3775
3776
  g.selectAll('text')
3776
3777
  .data(layoutWords)
3777
3778
  .join('text')
3778
3779
  .style('font-size', (d) => `${d.size}px`)
3779
- .style('font-family', 'system-ui, -apple-system, sans-serif')
3780
+ .style('font-family', FONT_FAMILY)
3780
3781
  .style('font-weight', '600')
3781
3782
  .style('fill', (_d, i) => colors[i % colors.length])
3782
3783
  .style('cursor', (d) =>
@@ -3874,13 +3875,13 @@ function renderWordCloudAsync(
3874
3875
  .padding(4)
3875
3876
  .rotate(rotateFn)
3876
3877
  .fontSize((d) => d.size!)
3877
- .font('system-ui, -apple-system, sans-serif')
3878
+ .font(FONT_FAMILY)
3878
3879
  .on('end', (layoutWords) => {
3879
3880
  g.selectAll('text')
3880
3881
  .data(layoutWords)
3881
3882
  .join('text')
3882
3883
  .style('font-size', (d) => `${d.size}px`)
3883
- .style('font-family', 'system-ui, -apple-system, sans-serif')
3884
+ .style('font-family', FONT_FAMILY)
3884
3885
  .style('font-weight', '600')
3885
3886
  .style('fill', (_d, i) => colors[i % colors.length])
3886
3887
  .attr('text-anchor', 'middle')
@@ -5013,6 +5014,7 @@ export async function renderD3ForExport(
5013
5014
 
5014
5015
  // Add xmlns for standalone SVG
5015
5016
  svgEl.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
5017
+ svgEl.style.fontFamily = FONT_FAMILY;
5016
5018
 
5017
5019
  return svgEl.outerHTML;
5018
5020
  } finally {
package/src/echarts.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { EChartsOption } from 'echarts';
2
+ import { FONT_FAMILY } from './fonts';
2
3
 
3
4
  // ============================================================
4
5
  // Types
@@ -407,7 +408,7 @@ export function buildEChartsOption(
407
408
  color: textColor,
408
409
  fontSize: 18,
409
410
  fontWeight: 'bold' as const,
410
- fontFamily: 'system-ui, -apple-system, sans-serif',
411
+ fontFamily: FONT_FAMILY,
411
412
  },
412
413
  }
413
414
  : undefined;
package/src/fonts.ts ADDED
@@ -0,0 +1,2 @@
1
+ export const FONT_FAMILY = 'Inter, system-ui, Avenir, Helvetica, Arial, sans-serif';
2
+ export const DEFAULT_FONT_NAME = 'Helvetica';
@@ -5,6 +5,7 @@
5
5
  import * as d3Selection from 'd3-selection';
6
6
  import type { PaletteColors } from '../palettes';
7
7
  import { resolveColor } from '../colors';
8
+ import { FONT_FAMILY } from '../fonts';
8
9
  import type {
9
10
  ParsedSequenceDgmo,
10
11
  SequenceElement,
@@ -29,9 +30,21 @@ const MESSAGE_START_OFFSET = 30;
29
30
  const LIFELINE_TAIL = 30;
30
31
  const ARROWHEAD_SIZE = 8;
31
32
 
33
+ // Mix two hex colors in sRGB: pct% of a, rest of b
34
+ function mix(a: string, b: string, pct: number): string {
35
+ const parse = (h: string) => {
36
+ const r = h.replace('#', '');
37
+ const f = r.length === 3 ? r[0]+r[0]+r[1]+r[1]+r[2]+r[2] : r;
38
+ return [parseInt(f.substring(0,2),16), parseInt(f.substring(2,4),16), parseInt(f.substring(4,6),16)];
39
+ };
40
+ const [ar,ag,ab] = parse(a), [br,bg,bb] = parse(b), t = pct/100;
41
+ const c = (x: number, y: number) => Math.round(x*t + y*(1-t)).toString(16).padStart(2,'0');
42
+ return `#${c(ar,br)}${c(ag,bg)}${c(ab,bb)}`;
43
+ }
44
+
32
45
  // Shared fill/stroke helpers
33
46
  const fill = (palette: PaletteColors, isDark: boolean): string =>
34
- `color-mix(in srgb, ${palette.primary} ${isDark ? '15%' : '30%'}, ${isDark ? palette.surface : palette.bg})`;
47
+ mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
35
48
  const stroke = (palette: PaletteColors): string => palette.textMuted;
36
49
  const SW = 1.5;
37
50
  const W = PARTICIPANT_BOX_WIDTH;
@@ -746,10 +759,15 @@ export function renderSequenceDiagram(
746
759
  // Build render sequence with stack-based return placement
747
760
  // Run on ALL messages first (preserves call stack correctness), then filter
748
761
  const allRenderSteps = buildRenderSequence(messages);
749
- const renderSteps =
762
+ let renderSteps =
750
763
  hiddenMsgIndices.size > 0
751
764
  ? allRenderSteps.filter((s) => !hiddenMsgIndices.has(s.messageIndex))
752
765
  : allRenderSteps;
766
+ // Drop unlabeled returns — they add visual noise without conveying information.
767
+ // Labeled returns (explicit <- value) are kept.
768
+ renderSteps = renderSteps.filter(
769
+ (s) => s.type === 'call' || s.label
770
+ );
753
771
  const activations = activationsOff ? [] : computeActivations(renderSteps);
754
772
  const stepSpacing = 35;
755
773
 
@@ -1020,10 +1038,7 @@ export function renderSequenceDiagram(
1020
1038
  .attr('viewBox', `0 0 ${svgWidth} ${totalHeight}`)
1021
1039
  .attr('preserveAspectRatio', 'xMidYMin meet')
1022
1040
  .attr('class', 'sequence-diagram')
1023
- .style(
1024
- 'font-family',
1025
- 'Inter, system-ui, Avenir, Helvetica, Arial, sans-serif'
1026
- );
1041
+ .style('font-family', FONT_FAMILY);
1027
1042
 
1028
1043
  // Define arrowhead markers
1029
1044
  const defs = svg.append('defs');
@@ -1119,7 +1134,7 @@ export function renderSequenceDiagram(
1119
1134
  ? resolveColor(group.color, palette)
1120
1135
  : undefined;
1121
1136
  const fillColor = resolvedGroupColor
1122
- ? `color-mix(in srgb, ${resolvedGroupColor} 10%, ${isDark ? palette.surface : palette.bg})`
1137
+ ? mix(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10)
1123
1138
  : isDark
1124
1139
  ? palette.surface
1125
1140
  : palette.bg;
@@ -1352,7 +1367,7 @@ export function renderSequenceDiagram(
1352
1367
  .attr('height', y2 - y1)
1353
1368
  .attr('fill', isDark ? palette.surface : palette.bg);
1354
1369
 
1355
- const actFill = `color-mix(in srgb, ${palette.primary} ${isDark ? '15%' : '30%'}, ${isDark ? palette.surface : palette.bg})`;
1370
+ const actFill = mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
1356
1371
  svg
1357
1372
  .append('rect')
1358
1373
  .attr('x', x)