@diagrammo/dgmo 0.45.0 → 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.
- package/dist/advanced.cjs +31 -25
- package/dist/advanced.js +31 -25
- package/dist/auto.cjs +500 -341
- package/dist/auto.css +342 -159
- package/dist/auto.d.cts +10 -8
- package/dist/auto.d.ts +10 -8
- package/dist/auto.js +373 -265
- package/dist/auto.mjs +500 -341
- package/dist/block.cjs +31 -25
- package/dist/block.js +31 -25
- package/dist/cli.cjs +112 -112
- package/dist/element.cjs +492 -325
- package/dist/element.js +367 -259
- package/dist/element.mjs +492 -325
- package/dist/index.cjs +31 -25
- package/dist/index.js +31 -25
- package/package.json +1 -1
- package/src/auto/index.ts +55 -57
- package/src/auto/shared.ts +142 -142
- package/src/auto/styles.ts +44 -199
- package/src/c4/renderer.ts +2 -2
- package/src/element/index.ts +29 -21
- package/src/infra/renderer.ts +1 -1
- package/src/render.ts +34 -14
- package/src/sitemap/renderer.ts +1 -1
- package/src/timeline/renderer.ts +1 -1
- package/src/utils/hover-styles.ts +6 -1
- package/src/utils/legend-d3.ts +1 -4
- package/src/utils/legend-svg.ts +1 -1
package/src/auto/shared.ts
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* Shared browser-embed helpers for the `@diagrammo/dgmo` client-side
|
|
3
3
|
* drop-ins (`./auto` script-tag renderer and `./element` custom element).
|
|
4
4
|
*
|
|
5
|
-
* These are the SVG sanitizer, theme resolver,
|
|
6
|
-
*
|
|
7
|
-
* and share-URL builder — factored
|
|
8
|
-
*
|
|
9
|
-
* public `window.dgmo` surface is
|
|
5
|
+
* These are the SVG sanitizer, theme resolver, the standard-block DOM
|
|
6
|
+
* assembler (canonical chrome from `src/embed` — BL-114), the error card,
|
|
7
|
+
* aria-label derivation, style injection, and share-URL builder — factored
|
|
8
|
+
* out of `auto/index.ts` so the two entries share one implementation rather
|
|
9
|
+
* than diverging copies. The `auto` entry's public `window.dgmo` surface is
|
|
10
|
+
* unaffected; it imports from here.
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import { encodeDiagramUrl } from '../sharing';
|
|
13
|
-
import {
|
|
14
|
+
import { buildDgmoBlockHtml, errorBlockHtml } from '../embed';
|
|
14
15
|
import { CSS } from './styles';
|
|
15
16
|
import { safeHref } from '../utils/safe-href';
|
|
16
17
|
|
|
@@ -22,10 +23,10 @@ export const VERSION: string =
|
|
|
22
23
|
(typeof __DGMO_VERSION__ === 'string' && __DGMO_VERSION__) || 'dev';
|
|
23
24
|
|
|
24
25
|
/** Default hosted editor used by "Open in editor" share links. */
|
|
25
|
-
export
|
|
26
|
+
export { EDITOR_BASE_URL } from '../embed';
|
|
26
27
|
|
|
27
28
|
const ARIA_LABEL_MAX = 200;
|
|
28
|
-
const COPIED_INTERACTION_MS =
|
|
29
|
+
const COPIED_INTERACTION_MS = 1500;
|
|
29
30
|
const LOG_PREFIX = '[dgmo]';
|
|
30
31
|
|
|
31
32
|
// Bidi override + control char strip for aria-label sanitization. The regex
|
|
@@ -148,156 +149,149 @@ export function deriveAriaLabel(source: string): string {
|
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
// ============================================================
|
|
151
|
-
//
|
|
152
|
+
// Standard block assembly (canonical chrome from src/embed — BL-114)
|
|
152
153
|
// ============================================================
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return frag;
|
|
155
|
+
export interface RenderedBlockOptions {
|
|
156
|
+
/** Trimmed DGMO source (copy payload + highlighted panel content). */
|
|
157
|
+
source: string;
|
|
158
|
+
/** Already-rendered AND sanitized SVG element to mount as the diagram. */
|
|
159
|
+
svgEl: Element;
|
|
160
|
+
/** `dgmo-theme-light` / `dgmo-theme-dark` / `dgmo-theme-transparent`. */
|
|
161
|
+
themeClass: string;
|
|
162
|
+
/** Emit the source disclosure + toolbar (showcase) or diagram-only. */
|
|
163
|
+
showSource: boolean;
|
|
164
|
+
/** Emit the open-in-editor toolbar link at all. */
|
|
165
|
+
showEditorLink: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Pre-built share URL (with the surface's UTM params) for the
|
|
168
|
+
* open-in-editor link. `null` (encode failed / too large) drops the link.
|
|
169
|
+
*/
|
|
170
|
+
shareUrl: string | null;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Assemble the standard DGMO embed block (`figure.dgmo` + hover-reveal icon
|
|
175
|
+
* toolbar + native `<details>` source panel) around an already-sanitized SVG
|
|
176
|
+
* node, and bind the toolbar's copy/open click handling.
|
|
177
|
+
*
|
|
178
|
+
* The markup comes from the canonical `buildDgmoBlockHtml` in `src/embed`;
|
|
179
|
+
* this wrapper only (1) mounts the live SVG node into the `.dgmo-svg` slot,
|
|
180
|
+
* (2) swaps the open-in-editor href for the surface's UTM-tagged share URL,
|
|
181
|
+
* and (3) attaches the click handler that `<details>` can't provide (copy).
|
|
182
|
+
*/
|
|
183
|
+
export function buildRenderedBlock(opts: RenderedBlockOptions): HTMLElement {
|
|
184
|
+
const html = buildDgmoBlockHtml(opts.source, '<div class="dgmo-svg"></div>', {
|
|
185
|
+
mode: opts.showSource ? 'showcase' : 'diagram',
|
|
186
|
+
showOpenInEditor: opts.showSource && opts.showEditorLink,
|
|
187
|
+
// Keep the legacy `.dgmo-rendered` + theme hooks so the anti-flash CSS,
|
|
188
|
+
// idempotency filters, and theme-scoped token colors keep working.
|
|
189
|
+
legacyClassNames: ['dgmo-rendered', opts.themeClass],
|
|
190
|
+
});
|
|
191
|
+
const holder = document.createElement('div');
|
|
192
|
+
// buildDgmoBlockHtml output is author-controlled markup with all
|
|
193
|
+
// user-supplied strings escaped; the SVG slot is empty at this point.
|
|
194
|
+
holder.innerHTML = html;
|
|
195
|
+
const wrapper = holder.firstElementChild as HTMLElement;
|
|
196
|
+
|
|
197
|
+
// Mount the sanitized SVG node directly (no serialize/re-parse round trip).
|
|
198
|
+
wrapper.querySelector('.dgmo-svg')?.appendChild(opts.svgEl);
|
|
199
|
+
|
|
200
|
+
// The canonical builder encodes a bare share URL; the browser surfaces
|
|
201
|
+
// carry UTM attribution, so swap in the pre-built URL (or drop the link
|
|
202
|
+
// when encoding failed — copy remains as the fallback affordance).
|
|
203
|
+
const open = wrapper.querySelector<HTMLAnchorElement>('a.dgmo-open');
|
|
204
|
+
if (open) {
|
|
205
|
+
if (opts.shareUrl) open.href = opts.shareUrl;
|
|
206
|
+
else open.remove();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
bindBlockToolbar(wrapper);
|
|
210
|
+
return wrapper;
|
|
184
211
|
}
|
|
185
212
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
chevron.setAttribute('aria-hidden', 'true');
|
|
201
|
-
chevron.textContent = '▸';
|
|
202
|
-
toggle.appendChild(chevron);
|
|
203
|
-
const label = document.createElement('span');
|
|
204
|
-
label.textContent = 'DGMO source';
|
|
205
|
-
toggle.appendChild(label);
|
|
206
|
-
|
|
207
|
-
const body = document.createElement('div');
|
|
208
|
-
body.className = 'dgmo-source-body';
|
|
209
|
-
const pre = document.createElement('pre');
|
|
210
|
-
pre.className = 'dgmo-source-pre';
|
|
211
|
-
pre.appendChild(buildHighlightedSource(source));
|
|
212
|
-
body.appendChild(pre);
|
|
213
|
-
|
|
214
|
-
const actions = document.createElement('div');
|
|
215
|
-
actions.className = 'dgmo-source-actions';
|
|
216
|
-
|
|
217
|
-
const copyBtn = document.createElement('button');
|
|
218
|
-
copyBtn.type = 'button';
|
|
219
|
-
copyBtn.className = 'dgmo-btn dgmo-btn-copy';
|
|
220
|
-
copyBtn.setAttribute('aria-label', 'Copy DGMO source');
|
|
221
|
-
copyBtn.title = 'Copy source';
|
|
222
|
-
setIcon(copyBtn, COPY_ICON_SVG);
|
|
223
|
-
copyBtn.addEventListener('click', () => {
|
|
224
|
-
void copySource(source, copyBtn);
|
|
213
|
+
/**
|
|
214
|
+
* Click handling for the standard block toolbar (mirrors remark-dgmo's
|
|
215
|
+
* `bindDgmo` reference implementation, but bound per-wrapper because these
|
|
216
|
+
* surfaces build their DOM imperatively).
|
|
217
|
+
*
|
|
218
|
+
* The toolbar is the `<summary>` of the source `<details>`: clicks on the
|
|
219
|
+
* `</>` toggle keep the native default (toggling), while clicks on
|
|
220
|
+
* `.dgmo-toolbar-btn` (copy / open) must preventDefault so they don't ALSO
|
|
221
|
+
* toggle — which cancels an anchor's navigation, hence the manual
|
|
222
|
+
* `window.open`.
|
|
223
|
+
*/
|
|
224
|
+
function bindBlockToolbar(wrapper: HTMLElement): void {
|
|
225
|
+
wrapper.addEventListener('click', (e) => {
|
|
226
|
+
void handleToolbarClick(e);
|
|
225
227
|
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function handleToolbarClick(e: Event): Promise<void> {
|
|
231
|
+
const target = e.target as Element | null;
|
|
232
|
+
if (!target || typeof target.closest !== 'function') return;
|
|
233
|
+
const btn = target.closest('.dgmo-toolbar-btn') as HTMLElement | null;
|
|
234
|
+
if (!btn) return;
|
|
235
|
+
|
|
236
|
+
const insideSummary = !!btn.closest('summary');
|
|
237
|
+
if (insideSummary) e.preventDefault();
|
|
238
|
+
|
|
239
|
+
if (btn.matches('button.dgmo-copy')) {
|
|
240
|
+
const copied = await copyText(btn.dataset['dgmoSource'] ?? '');
|
|
241
|
+
if (copied) {
|
|
242
|
+
btn.classList.add('dgmo-copy--success');
|
|
243
|
+
setTimeout(
|
|
244
|
+
() => btn.classList.remove('dgmo-copy--success'),
|
|
245
|
+
COPIED_INTERACTION_MS
|
|
246
|
+
);
|
|
238
247
|
} else {
|
|
239
|
-
|
|
240
|
-
editorBtn.title =
|
|
241
|
-
'Diagram too large for share link; copy source and paste into editor';
|
|
242
|
-
editorBtn.addEventListener('click', (e) => e.preventDefault());
|
|
248
|
+
sharedWarn('clipboard write failed');
|
|
243
249
|
}
|
|
244
|
-
|
|
250
|
+
return;
|
|
245
251
|
}
|
|
246
252
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
253
|
+
if (insideSummary && btn.matches('a.dgmo-open')) {
|
|
254
|
+
const anchor = btn as HTMLAnchorElement;
|
|
255
|
+
if (anchor.href) {
|
|
256
|
+
window.open(
|
|
257
|
+
anchor.href,
|
|
258
|
+
anchor.target || '_blank',
|
|
259
|
+
'noopener,noreferrer'
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
257
263
|
}
|
|
258
264
|
|
|
259
|
-
async function
|
|
260
|
-
source: string,
|
|
261
|
-
btn: HTMLButtonElement
|
|
262
|
-
): Promise<void> {
|
|
263
|
-
let copied = false;
|
|
265
|
+
async function copyText(text: string): Promise<boolean> {
|
|
264
266
|
try {
|
|
265
267
|
if (navigator?.clipboard?.writeText) {
|
|
266
|
-
await navigator.clipboard.writeText(
|
|
267
|
-
|
|
268
|
+
await navigator.clipboard.writeText(text);
|
|
269
|
+
return true;
|
|
268
270
|
}
|
|
269
271
|
} catch {
|
|
270
272
|
// fallthrough to execCommand
|
|
271
273
|
}
|
|
272
|
-
if (
|
|
274
|
+
if (typeof document !== 'undefined') {
|
|
273
275
|
try {
|
|
274
276
|
const ta = document.createElement('textarea');
|
|
275
|
-
ta.value =
|
|
277
|
+
ta.value = text;
|
|
276
278
|
ta.setAttribute('readonly', '');
|
|
277
279
|
ta.style.position = 'absolute';
|
|
278
280
|
ta.style.left = '-9999px';
|
|
279
281
|
document.body.appendChild(ta);
|
|
280
282
|
ta.select();
|
|
281
|
-
copied = document.execCommand('copy');
|
|
283
|
+
const copied = document.execCommand('copy');
|
|
282
284
|
document.body.removeChild(ta);
|
|
285
|
+
return copied;
|
|
283
286
|
} catch {
|
|
284
|
-
|
|
287
|
+
return false;
|
|
285
288
|
}
|
|
286
289
|
}
|
|
287
|
-
|
|
288
|
-
setIcon(btn, CHECK_ICON_SVG);
|
|
289
|
-
btn.classList.add('dgmo-btn-copied');
|
|
290
|
-
setTimeout(() => {
|
|
291
|
-
setIcon(btn, COPY_ICON_SVG);
|
|
292
|
-
btn.classList.remove('dgmo-btn-copied');
|
|
293
|
-
}, COPIED_INTERACTION_MS);
|
|
294
|
-
} else {
|
|
295
|
-
sharedWarn('clipboard write failed');
|
|
296
|
-
}
|
|
290
|
+
return false;
|
|
297
291
|
}
|
|
298
292
|
|
|
299
293
|
// ============================================================
|
|
300
|
-
// Error
|
|
294
|
+
// Error card (canonical errorBlockHtml from src/embed)
|
|
301
295
|
// ============================================================
|
|
302
296
|
|
|
303
297
|
export interface BannerOptions {
|
|
@@ -307,26 +301,32 @@ export interface BannerOptions {
|
|
|
307
301
|
column?: number;
|
|
308
302
|
}
|
|
309
303
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Build the standard `.dgmo--error` card as a live DOM element. Message +
|
|
306
|
+
* location are folded into one line; the offending source is shown inside
|
|
307
|
+
* the card (canonical shape — same as remark-dgmo and the other adopters).
|
|
308
|
+
*/
|
|
309
|
+
export function buildErrorBlock(
|
|
310
|
+
opts: BannerOptions & { source: string }
|
|
311
|
+
): HTMLElement {
|
|
312
|
+
let message =
|
|
313
|
+
opts.severity && opts.severity !== 'error'
|
|
314
|
+
? `${opts.severity}: ${opts.message}`
|
|
315
|
+
: opts.message;
|
|
320
316
|
if (opts.line !== undefined && opts.line > 0) {
|
|
321
|
-
|
|
322
|
-
loc.className = 'dgmo-error-banner-loc';
|
|
323
|
-
loc.textContent =
|
|
317
|
+
message +=
|
|
324
318
|
opts.column !== undefined
|
|
325
|
-
? `at ${opts.line}:${opts.column}`
|
|
326
|
-
: `at line ${opts.line}`;
|
|
327
|
-
banner.appendChild(loc);
|
|
319
|
+
? ` (at ${opts.line}:${opts.column})`
|
|
320
|
+
: ` (at line ${opts.line})`;
|
|
328
321
|
}
|
|
329
|
-
|
|
322
|
+
const holder = document.createElement('div');
|
|
323
|
+
// errorBlockHtml escapes both message and source.
|
|
324
|
+
holder.innerHTML = errorBlockHtml(new Error(message), opts.source);
|
|
325
|
+
const card = holder.firstElementChild as HTMLElement;
|
|
326
|
+
// Mark processed so the auto surface's selectors never re-scan the card
|
|
327
|
+
// (its root carries the `.dgmo` class the scanner matches on).
|
|
328
|
+
card.setAttribute('data-dgmo-processed', 'true');
|
|
329
|
+
return card;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
// ============================================================
|
package/src/auto/styles.ts
CHANGED
|
@@ -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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
10
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
185
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.
|
|
211
|
-
.
|
|
212
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
67
|
+
export const CSS: string =
|
|
68
|
+
AUTO_BASE_CSS +
|
|
69
|
+
'\n' +
|
|
70
|
+
BLOCK_CSS +
|
|
71
|
+
'\n' +
|
|
72
|
+
darkScopedBlockCss(BLOCK_CSS) +
|
|
73
|
+
'\n';
|
package/src/c4/renderer.ts
CHANGED
|
@@ -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
|
|
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
|
|
1807
|
+
legendParent.attr('data-legend-active', tagAttrKey(activeTagGroup));
|
|
1808
1808
|
}
|
|
1809
1809
|
renderLegend(
|
|
1810
1810
|
legendParent as GSelection,
|