@diagrammo/dgmo 0.44.1 → 0.46.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.
@@ -1,24 +1,40 @@
1
1
  /**
2
- * Static CSS for `@diagrammo/dgmo/auto`.
2
+ * Static CSS for `@diagrammo/dgmo/auto` (and `./element`, which shares the
3
+ * same injector).
3
4
  *
4
- * The string is a literal — no template interpolation of user-supplied
5
- * values. Theme/palette differences are applied via class hooks
6
- * (`.dgmo-theme-light`, `.dgmo-theme-dark`) on the rendered wrapper, so
7
- * a malicious theme name can never be smuggled into the stylesheet text.
5
+ * As of BL-114 the diagram + source chrome is the canonical standard block
6
+ * from `src/embed` — its styles ship in `BLOCK_CSS` and are appended below.
7
+ * This file only keeps the auto-surface-specific rules: anti-flash hiding,
8
+ * the `.dgmo-rendered` container reset, and the loading hint.
9
+ *
10
+ * `BLOCK_CSS` keys its dark-mode overrides off `[data-theme="dark"]`; the
11
+ * browser surfaces resolve the theme themselves and stamp a
12
+ * `.dgmo-theme-dark` class on the wrapper, so a re-scoped copy of the dark
13
+ * rules is appended too (same adapter pattern as fumadocs/nextra's
14
+ * build-css.mjs, done here at module init).
8
15
  *
9
- * All selectors are prefixed `.dgmo-` to avoid collisions with embedder
10
- * styles. Class names are part of the SemVer-stable surface — see
16
+ * The string is a literal — no template interpolation of user-supplied
17
+ * values. All selectors are prefixed `.dgmo-` to avoid collisions with
18
+ * embedder styles. Class names are part of the SemVer-stable surface — see
11
19
  * "SemVer policy" in the README HTML section.
20
+ *
21
+ * IMPORTANT: keep AUTO_BASE_CSS a single pure template literal — tsup's
22
+ * emitAutoCss extracts it (plus BLOCK_CSS) with a regex to build
23
+ * `dist/auto.css`, mirroring the concatenation done at the bottom of this
24
+ * file. Update tsup.config.ts if the assembly here changes.
12
25
  */
13
- export const CSS: string = `
26
+
27
+ import { BLOCK_CSS } from '../embed/css';
28
+
29
+ const AUTO_BASE_CSS: string = `
14
30
  /* anti-flash: hide source elements until either render swaps them out
15
- or the error path explicitly un-hides them */
31
+ or the error path replaces them with the standard error card */
16
32
  pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
17
- .dgmo:not(svg *):not(.dgmo-rendered):not(.dgmo-rendered *) {
33
+ .dgmo:not(svg *):not(.dgmo-rendered):not(.dgmo-rendered *):not(.dgmo--error):not(.dgmo--error *) {
18
34
  visibility: hidden;
19
35
  }
20
36
  .dgmo-rendered, .dgmo-rendered *,
21
- .dgmo-error-banner, .dgmo-error-banner * {
37
+ .dgmo--error, .dgmo--error * {
22
38
  visibility: visible !important;
23
39
  }
24
40
 
@@ -32,197 +48,26 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
32
48
  line-height: 1.4;
33
49
  color-scheme: light dark;
34
50
  }
35
- .dgmo-rendered svg {
36
- display: block;
37
- max-width: 100%;
38
- height: auto;
39
- }
40
-
41
- .dgmo-source-panel {
42
- margin-top: 4px;
43
- position: relative;
44
- }
45
-
46
- .dgmo-source-toggle {
47
- display: inline-flex;
48
- align-items: center;
49
- gap: 4px;
50
- /* Visual size is small; the 28px hit area + padding still meets
51
- the 44 × 44 minimum touch target across most stylesheets, and
52
- we widen padding on coarse pointers below. */
53
- min-height: 28px;
54
- padding: 4px 6px;
55
- background: transparent;
56
- border: 0;
57
- font: inherit;
58
- font-size: 11px;
59
- font-weight: 500;
60
- letter-spacing: 0.02em;
61
- text-align: left;
62
- cursor: pointer;
63
- color: inherit;
64
- opacity: 0.45;
65
- border-radius: 3px;
66
- transition: opacity 120ms ease, background 120ms ease;
67
- }
68
- .dgmo-source-toggle:hover {
69
- opacity: 0.85;
70
- background: rgba(127, 127, 127, 0.08);
71
- }
72
- .dgmo-source-toggle:focus-visible {
73
- opacity: 1;
74
- outline: 2px solid currentColor;
75
- outline-offset: 1px;
76
- }
77
- .dgmo-source-toggle[aria-expanded="true"] {
78
- opacity: 0.7;
79
- }
80
- .dgmo-source-toggle .dgmo-chevron {
81
- display: inline-block;
82
- width: 8px;
83
- height: 8px;
84
- font-size: 8px;
85
- line-height: 1;
86
- transition: transform 150ms ease-out;
87
- }
88
- .dgmo-source-toggle[aria-expanded="true"] .dgmo-chevron {
89
- transform: rotate(90deg);
90
- }
91
- @media (pointer: coarse) {
92
- /* Larger hit area on touch devices without making the visible
93
- toggle bigger on the desktop. */
94
- .dgmo-source-toggle {
95
- min-height: 44px;
96
- padding: 12px 10px;
97
- }
98
- }
99
-
100
- .dgmo-source-body {
101
- overflow: hidden;
102
- max-height: 0;
103
- transition: max-height 150ms ease-out;
104
- position: relative;
105
- }
106
- .dgmo-source-body.dgmo-open {
107
- max-height: 60vh;
108
- overflow: auto;
109
- margin-top: 2px;
110
- border-radius: 4px;
111
- background: rgba(127, 127, 127, 0.04);
112
- }
113
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-body.dgmo-open {
114
- background: rgba(255, 255, 255, 0.03);
115
- }
116
- .dgmo-source-pre {
117
- margin: 0;
118
- /* Right padding leaves room for the floating action icons. */
119
- padding: 10px 56px 10px 14px;
120
- font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
121
- font-size: 12px;
122
- line-height: 1.45;
123
- white-space: pre;
124
- overflow: auto;
125
- background: transparent;
126
- color: inherit;
127
- }
128
-
129
- .dgmo-source-actions {
130
- position: absolute;
131
- top: 6px;
132
- right: 6px;
133
- display: flex;
134
- gap: 2px;
135
- /* Inherit visibility:hidden when collapsed; pointer-events:none keeps
136
- them off the tab order until the panel opens. */
137
- pointer-events: none;
138
- }
139
- .dgmo-source-body.dgmo-open .dgmo-source-actions {
140
- pointer-events: auto;
141
- }
142
- .dgmo-btn {
143
- display: inline-flex;
144
- align-items: center;
145
- justify-content: center;
146
- width: 26px;
147
- height: 26px;
148
- padding: 0;
149
- background: transparent;
150
- color: inherit;
151
- border: 0;
152
- border-radius: 3px;
153
- cursor: pointer;
154
- text-decoration: none;
155
- opacity: 0.4;
156
- transition: opacity 120ms ease, background 120ms ease, color 120ms ease;
157
- }
158
- .dgmo-btn:hover {
159
- opacity: 1;
160
- background: rgba(127, 127, 127, 0.15);
161
- }
162
- .dgmo-btn:focus-visible {
163
- opacity: 1;
164
- outline: 2px solid currentColor;
165
- outline-offset: 1px;
166
- }
167
- .dgmo-btn[aria-disabled="true"] {
168
- opacity: 0.25;
169
- cursor: not-allowed;
170
- }
171
- .dgmo-btn[aria-disabled="true"]:hover {
172
- background: transparent;
173
- }
174
- .dgmo-btn-copied {
175
- opacity: 1 !important;
176
- color: rgb(120, 200, 120);
177
- }
178
- .dgmo-btn svg {
179
- width: 14px;
180
- height: 14px;
181
- display: block;
182
- }
183
51
 
184
- .dgmo-error-banner {
185
- display: block;
186
- margin: 0.5em 0;
187
- padding: 10px 14px;
188
- border-left: 4px solid #c44;
189
- background: rgba(204, 68, 68, 0.08);
190
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
191
- font-size: 13px;
192
- line-height: 1.4;
193
- color: inherit;
194
- border-radius: 0 4px 4px 0;
195
- }
196
- .dgmo-error-banner-title {
197
- font-weight: 600;
198
- margin-bottom: 2px;
199
- }
200
- .dgmo-error-banner-loc {
201
- opacity: 0.7;
52
+ .dgmo--loading {
53
+ opacity: 0.6;
202
54
  font-size: 12px;
203
55
  font-family: ui-monospace, monospace;
204
56
  }
57
+ `;
205
58
 
206
- /* Token roles — fallback colors used when theme classes don't override */
207
- .dgmo-source-pre .dgmo-tok-keyword { color: #5e81ac; font-weight: 600; }
208
- .dgmo-source-pre .dgmo-tok-controlKeyword { color: #b48ead; font-weight: 600; }
209
- .dgmo-source-pre .dgmo-tok-definitionKeyword { color: #5e81ac; font-weight: 600; }
210
- .dgmo-source-pre .dgmo-tok-modifier { color: #b48ead; }
211
- .dgmo-source-pre .dgmo-tok-chartType { color: #d08770; font-weight: 600; }
212
- .dgmo-source-pre .dgmo-tok-operator { color: #bf616a; font-weight: 600; }
213
- .dgmo-source-pre .dgmo-tok-number { color: #b48ead; }
214
- .dgmo-source-pre .dgmo-tok-comment { color: #6c7a96; font-style: italic; }
215
- .dgmo-source-pre .dgmo-tok-heading { color: #d08770; font-weight: 600; }
216
- .dgmo-source-pre .dgmo-tok-bracket { color: #5e81ac; }
217
- .dgmo-source-pre .dgmo-tok-separator { color: #88c0d0; }
218
- .dgmo-source-pre .dgmo-tok-url { color: #88c0d0; text-decoration: underline; }
219
- .dgmo-source-pre .dgmo-tok-colorAnnotation { color: #d08770; font-style: italic; }
220
- .dgmo-source-pre .dgmo-tok-punctuation { color: #6c7a96; }
221
- .dgmo-source-pre .dgmo-tok-noteContent { color: #6c7a96; font-style: italic; }
59
+ /** Re-scope BLOCK_CSS's \`[data-theme="dark"]\` rules to `.dgmo-theme-dark`. */
60
+ function darkScopedBlockCss(css: string): string {
61
+ const rules = css.match(/\[data-theme="dark"\][^{]*\{[^}]*\}/g) ?? [];
62
+ return rules
63
+ .map((rule) => rule.replace(/\[data-theme="dark"\]/g, '.dgmo-theme-dark'))
64
+ .join('\n');
65
+ }
222
66
 
223
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-keyword,
224
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-definitionKeyword { color: #81a1c1; }
225
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-comment,
226
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-noteContent,
227
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-punctuation { color: #616e88; }
228
- `;
67
+ export const CSS: string =
68
+ AUTO_BASE_CSS +
69
+ '\n' +
70
+ BLOCK_CSS +
71
+ '\n' +
72
+ darkScopedBlockCss(BLOCK_CSS) +
73
+ '\n';
@@ -622,7 +622,7 @@ export function renderC4Context(
622
622
  .attr('transform', `translate(0, ${DIAGRAM_PADDING + titleHeight})`)
623
623
  : contentG.append('g').attr('class', 'c4-legend');
624
624
  if (activeTagGroup) {
625
- legendParent.attr('data-legend-active', activeTagGroup.toLowerCase());
625
+ legendParent.attr('data-legend-active', tagAttrKey(activeTagGroup));
626
626
  }
627
627
  renderLegend(
628
628
  legendParent as GSelection,
@@ -1804,7 +1804,7 @@ export function renderC4Containers(
1804
1804
  .attr('transform', `translate(0, ${DIAGRAM_PADDING + titleHeight})`)
1805
1805
  : contentG.append('g').attr('class', 'c4-legend');
1806
1806
  if (activeTagGroup) {
1807
- legendParent.attr('data-legend-active', activeTagGroup.toLowerCase());
1807
+ legendParent.attr('data-legend-active', tagAttrKey(activeTagGroup));
1808
1808
  }
1809
1809
  renderLegend(
1810
1810
  legendParent as GSelection,
@@ -500,6 +500,40 @@ export const NORD_ROLE_STYLES: Record<string, Record<string, string>> = {
500
500
  default: {},
501
501
  };
502
502
 
503
+ // ============================================================
504
+ // LIGHT_ROLE_STYLES — slate-light companion to NORD_ROLE_STYLES
505
+ // ============================================================
506
+
507
+ /**
508
+ * Light-background counterpart of NORD_ROLE_STYLES for static contexts
509
+ * (marketing site, light-mode source panels). Same role keys — a parity test
510
+ * (tests/block.test.ts) guards the two maps and the `.dgmo-tok-*` rules in
511
+ * block/css.ts against drift. Colors are darker AA-targeting tones tuned for
512
+ * a light code panel (bg ~#f3f5f8). Moved here from the site repo's
513
+ * hand-maintained mirror (BL-114).
514
+ */
515
+ export const LIGHT_ROLE_STYLES: Record<string, Record<string, string>> = {
516
+ keyword: { color: '#3b6ea5', fontWeight: 'bold' }, // slate blue
517
+ controlKeyword: { color: '#7d5ba6', fontWeight: 'bold' }, // purple
518
+ definitionKeyword: { color: '#2f5a86', fontWeight: 'bold' }, // deep blue
519
+ modifier: { color: '#7d5ba6' },
520
+ chartType: { color: '#b8691f', fontWeight: 'bold' }, // amber
521
+ operator: { color: '#c0504d', fontWeight: 'bold' }, // red
522
+ number: { color: '#7d5ba6' },
523
+ comment: { color: '#677483', fontStyle: 'italic' }, // muted gray
524
+ heading: { color: '#b8691f', fontWeight: 'bold' },
525
+ bracket: { color: '#5b6672' },
526
+ separator: { color: '#2d7268' }, // deep teal
527
+ deprecatedSyntax: { color: '#c0504d', textDecoration: 'line-through' },
528
+ url: { color: '#3b6ea5', textDecoration: 'underline' },
529
+ colorAnnotation: { color: '#b8691f', fontStyle: 'italic' },
530
+ punctuation: { color: '#677483' },
531
+ propertyName: { color: '#2d7268' },
532
+ string: { color: '#3f7a3b' }, // forest green
533
+ noteContent: { color: '#677483', fontStyle: 'italic' },
534
+ default: {},
535
+ };
536
+
503
537
  // ============================================================
504
538
  // ROLE_TO_ANSI — ANSI escape codes for CLI output
505
539
  // ============================================================
@@ -45,8 +45,8 @@ import {
45
45
  resolveTheme,
46
46
  sanitizeSvgInPlace,
47
47
  deriveAriaLabel,
48
- buildSourcePanel,
49
- buildErrorBanner,
48
+ buildRenderedBlock,
49
+ buildErrorBlock,
50
50
  buildShareUrl,
51
51
  sharedWarn,
52
52
  type ThemePreference,
@@ -275,7 +275,9 @@ export class DgmoDiagram extends HTMLElement {
275
275
  line?: number;
276
276
  column?: number;
277
277
  }): void {
278
- this.mount(buildErrorBanner(opts), 'dgmo dgmo--error');
278
+ // The standard error card carries `.dgmo.dgmo--error` on its own root,
279
+ // so the container itself stays class-free.
280
+ this.mount(buildErrorBlock({ ...opts, source: this.source ?? '' }), '');
279
281
  }
280
282
 
281
283
  private async rerender(): Promise<void> {
@@ -289,9 +291,8 @@ export class DgmoDiagram extends HTMLElement {
289
291
 
290
292
  // Loading state (visible only while an async render/fetch is in flight).
291
293
  const loading = document.createElement('div');
292
- loading.className = 'dgmo-error-banner-loc';
293
294
  loading.textContent = 'Rendering diagram…';
294
- this.mount(loading, 'dgmo dgmo--loading');
295
+ this.mount(loading, 'dgmo--loading');
295
296
 
296
297
  const themePref = this.themePreference();
297
298
  const resolvedTheme =
@@ -382,22 +383,29 @@ export class DgmoDiagram extends HTMLElement {
382
383
  ? 'dgmo-theme-transparent'
383
384
  : 'dgmo-theme-light';
384
385
 
385
- const frag = document.createDocumentFragment();
386
- frag.appendChild(svgEl);
387
-
388
- // Showcase chrome: collapsible source panel + Copy + Open-in-editor.
389
- if (this.isShowcase()) {
390
- const shareUrl = buildShareUrl(src, {
391
- palette,
392
- theme: resolvedTheme === 'dark' ? 'dark' : 'light',
393
- editorBase: this.editorBase(),
394
- campaign: VERSION,
395
- utmSource: 'element-embed',
396
- });
397
- frag.appendChild(buildSourcePanel(src, shareUrl, true));
398
- }
399
-
400
- this.mount(frag, `dgmo-rendered ${themeClass} dgmo--rendered`);
386
+ // Showcase chrome: standard embed block (BL-114) with the hover-reveal
387
+ // toolbar (view-source toggle + Copy + Open-in-editor).
388
+ const showcase = this.isShowcase();
389
+ const shareUrl = showcase
390
+ ? buildShareUrl(src, {
391
+ palette,
392
+ theme: resolvedTheme === 'dark' ? 'dark' : 'light',
393
+ editorBase: this.editorBase(),
394
+ campaign: VERSION,
395
+ utmSource: 'element-embed',
396
+ })
397
+ : null;
398
+
399
+ const wrapper = buildRenderedBlock({
400
+ source: src,
401
+ svgEl,
402
+ themeClass,
403
+ showSource: showcase,
404
+ showEditorLink: showcase,
405
+ shareUrl,
406
+ });
407
+
408
+ this.mount(wrapper, 'dgmo--rendered');
401
409
  }
402
410
  }
403
411