@helping-ai-workflow/md2doc 3.13.0 → 3.14.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/lib/editor/client.js +2768 -202
- package/lib/editor/inline-md.js +66 -1
- package/lib/editor/list-md.js +8 -1
- package/lib/editor/toolbar-model.js +11 -1
- package/lib/md2doc.js +453 -83
- package/lib/theme/runtime.js +279 -27
- package/lib/theme/tokens.js +3 -0
- package/package.json +1 -1
package/lib/theme/runtime.js
CHANGED
|
@@ -38,6 +38,9 @@
|
|
|
38
38
|
}
|
|
39
39
|
btn.addEventListener('click', function () {
|
|
40
40
|
var dark = !isDark();
|
|
41
|
+
// Measured while the light layout is still clean: see prepareGeometry. A
|
|
42
|
+
// failure there only costs that speed-up; the toggle must still work.
|
|
43
|
+
if (dark) { try { prepareGeometry(); } catch (e) { /* recolourSvg measures lazily instead */ } }
|
|
41
44
|
if (dark) root.setAttribute('data-md2doc-theme', 'dark'); else root.removeAttribute('data-md2doc-theme');
|
|
42
45
|
save(dark);
|
|
43
46
|
paint();
|
|
@@ -277,10 +280,208 @@
|
|
|
277
280
|
return /^rect$/i.test(el.tagName) && next !== null && /^text$/i.test(next.tagName) &&
|
|
278
281
|
el.closest('[id^="wavearcs"]') !== null;
|
|
279
282
|
}
|
|
283
|
+
function contrast(a, b) {
|
|
284
|
+
var x = lum(a), y = lum(b);
|
|
285
|
+
return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05);
|
|
286
|
+
}
|
|
287
|
+
// ── Where a text sits (R7 option A) ──
|
|
288
|
+
// Geometry is kept apart from colour: a text's STACK is every shape before it in
|
|
289
|
+
// document order (= paint order) whose fill AREA covers the text's centre, bottom
|
|
290
|
+
// to top, whatever its fill. That is the same in light and dark, so the toggle
|
|
291
|
+
// measures it BEFORE the theme attribute flips, while the page layout is still
|
|
292
|
+
// clean (prepareGeometry), and keeps it per svg; a read after the flip would force
|
|
293
|
+
// a synchronous layout of the whole restyled page. Colour is applied afterwards:
|
|
294
|
+
// the stack's FINAL fills are composited (fill alpha x fill-opacity x opacity of
|
|
295
|
+
// the shape and its ancestors) over the page, so a semi-transparent cell counts as
|
|
296
|
+
// the colour it really shows.
|
|
297
|
+
var PAINTED = 'polygon, ellipse, path, rect, circle';
|
|
298
|
+
var NOT_PAINTED = 'defs, marker, clipPath, mask, pattern, symbol, foreignObject';
|
|
299
|
+
var geoCache = typeof WeakMap === 'function' ? new WeakMap() : null;
|
|
300
|
+
// A mermaid XHTML label is measured by its foreignObject.
|
|
301
|
+
function geoOf(el) { return el.namespaceURI === XHTML ? el.closest('foreignObject') : el; }
|
|
302
|
+
function centreOf(g) {
|
|
303
|
+
if (!g || typeof g.getBBox !== 'function') return null;
|
|
304
|
+
var b, m;
|
|
305
|
+
try { b = g.getBBox(); m = g.getCTM(); } catch (e) { return null; }
|
|
306
|
+
if (!m || (!b.width && !b.height)) return null;
|
|
307
|
+
return new DOMPoint(b.x + b.width / 2, b.y + b.height / 2).matrixTransform(m);
|
|
308
|
+
}
|
|
309
|
+
function geomShapes(svg) {
|
|
310
|
+
var all = svg.querySelectorAll(PAINTED), list = [];
|
|
311
|
+
for (var i = 0; i < all.length; i++) {
|
|
312
|
+
var s = all[i], b, m;
|
|
313
|
+
if (s.closest(NOT_PAINTED) || typeof s.isPointInFill !== 'function') continue;
|
|
314
|
+
try { b = s.getBBox(); m = s.getCTM(); } catch (e) { continue; }
|
|
315
|
+
if (!m) continue;
|
|
316
|
+
// Its box in viewport units, for a cheap reject before isPointInFill.
|
|
317
|
+
var xs = [], ys = [], cs = [[b.x, b.y], [b.x + b.width, b.y], [b.x, b.y + b.height], [b.x + b.width, b.y + b.height]];
|
|
318
|
+
for (var c = 0; c < 4; c++) { var q = new DOMPoint(cs[c][0], cs[c][1]).matrixTransform(m); xs.push(q.x); ys.push(q.y); }
|
|
319
|
+
list.push({ el: s, m: m, vp: s.nearestViewportElement,
|
|
320
|
+
box: [Math.min.apply(null, xs), Math.min.apply(null, ys), Math.max.apply(null, xs), Math.max.apply(null, ys)] });
|
|
321
|
+
}
|
|
322
|
+
return list;
|
|
323
|
+
}
|
|
324
|
+
// null when the element has no geometry (detached, display:none).
|
|
325
|
+
function stackAt(g, shapes) {
|
|
326
|
+
var p = centreOf(g);
|
|
327
|
+
if (!p) return null;
|
|
328
|
+
var vp = g.nearestViewportElement, stack = [];
|
|
329
|
+
for (var i = 0; i < shapes.length; i++) {
|
|
330
|
+
var s = shapes[i];
|
|
331
|
+
if (s.vp !== vp || p.x < s.box[0] || p.x > s.box[2] || p.y < s.box[1] || p.y > s.box[3]) continue;
|
|
332
|
+
if (!(s.el.compareDocumentPosition(g) & 4)) break; // document order: every later shape follows g too
|
|
333
|
+
if (s.el.isPointInFill(p.matrixTransform(s.m.inverse()))) stack.push(s.el);
|
|
334
|
+
}
|
|
335
|
+
return stack;
|
|
336
|
+
}
|
|
337
|
+
// A mermaid svg is rebuilt from markup on every theme swap (new elements, same
|
|
338
|
+
// drawing), so its stacks are ALSO kept per host and per render kind (light / live
|
|
339
|
+
// dark) as indices: the n-th text or foreignObject over the m-th shapes. A swapped-in
|
|
340
|
+
// svg with the same counts adopts them without reading geometry.
|
|
341
|
+
var GEO_EL = 'text, tspan, foreignObject';
|
|
342
|
+
var hostGeo = typeof WeakMap === 'function' ? new WeakMap() : null;
|
|
343
|
+
function hostEntry(svg, make) {
|
|
344
|
+
var host = hostGeo && svg.closest('.mermaid');
|
|
345
|
+
if (!host) return null;
|
|
346
|
+
var kind = svg.hasAttribute('data-md2doc-live') ? 'live' : 'light';
|
|
347
|
+
var all = hostGeo.get(host) || {};
|
|
348
|
+
var nT = svg.querySelectorAll(GEO_EL).length, nS = svg.querySelectorAll(PAINTED).length, e = all[kind];
|
|
349
|
+
if (!e || e.nT !== nT || e.nS !== nS) {
|
|
350
|
+
if (!make) return null;
|
|
351
|
+
e = all[kind] = { nT: nT, nS: nS, map: {} };
|
|
352
|
+
hostGeo.set(host, all);
|
|
353
|
+
}
|
|
354
|
+
return e;
|
|
355
|
+
}
|
|
356
|
+
function adopt(svg) {
|
|
357
|
+
var e = geoCache && hostEntry(svg, false);
|
|
358
|
+
if (!e) return;
|
|
359
|
+
var keep = geoCache.get(svg) || new Map(), gl = svg.querySelectorAll(GEO_EL), sl = svg.querySelectorAll(PAINTED);
|
|
360
|
+
for (var t in e.map) {
|
|
361
|
+
var g = gl[+t];
|
|
362
|
+
if (g && !keep.has(g)) keep.set(g, e.map[t].map(function (n) { return sl[n]; }));
|
|
363
|
+
}
|
|
364
|
+
if (keep.size) geoCache.set(svg, keep);
|
|
365
|
+
}
|
|
366
|
+
// An element with no geometry (empty label, display:none) is remembered for the
|
|
367
|
+
// current toggle only (geoGen): it may be shown before the next one.
|
|
368
|
+
var noGeo = typeof WeakMap === 'function' ? new WeakMap() : null, geoGen = 0;
|
|
369
|
+
// Measures the given geometry elements of one svg and remembers each stack.
|
|
370
|
+
function measure(svg, gs) {
|
|
371
|
+
var shapes = null, keep = geoCache ? geoCache.get(svg) || new Map() : new Map(), out = new Map();
|
|
372
|
+
for (var i = 0; i < gs.length; i++) {
|
|
373
|
+
var g = gs[i];
|
|
374
|
+
if (!g || out.has(g)) continue;
|
|
375
|
+
if (shapes === null) shapes = geomShapes(svg);
|
|
376
|
+
var st = stackAt(g, shapes);
|
|
377
|
+
out.set(g, st);
|
|
378
|
+
if (st) keep.set(g, st); else if (noGeo) noGeo.set(g, geoGen);
|
|
379
|
+
}
|
|
380
|
+
if (geoCache && keep.size) geoCache.set(svg, keep);
|
|
381
|
+
var e = keep.size ? hostEntry(svg, true) : null;
|
|
382
|
+
if (e) {
|
|
383
|
+
var gl = [].slice.call(svg.querySelectorAll(GEO_EL)), sl = [].slice.call(svg.querySelectorAll(PAINTED));
|
|
384
|
+
out.forEach(function (st, g) {
|
|
385
|
+
if (st) e.map[gl.indexOf(g)] = st.map(function (s) { return sl.indexOf(s); });
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return out;
|
|
389
|
+
}
|
|
390
|
+
// Called by the toggle before it flips to dark. A light-drawn mermaid svg is
|
|
391
|
+
// measured too (it is recoloured until the live dark render arrives) unless that
|
|
392
|
+
// dark render is cached: then the swap replaces it before anything is recoloured.
|
|
393
|
+
function prepareGeometry() {
|
|
394
|
+
if (!geoCache) return;
|
|
395
|
+
geoGen++;
|
|
396
|
+
var swapsNow = hasMermaid && !inFlight && cached('dark');
|
|
397
|
+
var svgs = document.querySelectorAll(RECOLOUR);
|
|
398
|
+
for (var i = 0; i < svgs.length; i++) {
|
|
399
|
+
var svg = svgs[i], gs = [];
|
|
400
|
+
if (swapsNow && svg.closest('.mermaid')) continue;
|
|
401
|
+
adopt(svg);
|
|
402
|
+
var known = geoCache.get(svg), ts = svg.querySelectorAll('text, tspan, foreignObject *');
|
|
403
|
+
for (var k = 0; k < ts.length; k++) {
|
|
404
|
+
var g = geoOf(ts[k]);
|
|
405
|
+
// Every text, not only the dark ones: an inherited colour can turn dark (or
|
|
406
|
+
// light) with the page once the theme flips.
|
|
407
|
+
if (g && !(known && known.has(g)) && !(noGeo && noGeo.get(g) === geoGen)) gs.push(g);
|
|
408
|
+
}
|
|
409
|
+
if (gs.length) measure(svg, gs);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function num(v) { var n = parseFloat(v); return isNaN(n) ? 1 : n; }
|
|
413
|
+
// A shape's final paint as [r, g, b, alpha], or null when it shows nothing.
|
|
414
|
+
function paintOf(s, svg, memo) {
|
|
415
|
+
if (memo.has(s)) return memo.get(s);
|
|
416
|
+
var cs = getComputedStyle(s), f = rgba(cs.fill), out = null;
|
|
417
|
+
if (f) {
|
|
418
|
+
var a = f[3] * num(cs.fillOpacity) * num(cs.opacity);
|
|
419
|
+
for (var p = s.parentNode; a > 0 && p && p !== svg && p.nodeType === 1; p = p.parentNode) a *= num(getComputedStyle(p).opacity);
|
|
420
|
+
if (a > 0) out = [f[0], f[1], f[2], Math.min(a, 1)];
|
|
421
|
+
}
|
|
422
|
+
memo.set(s, out);
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
425
|
+
// The colour under a text: its stack composited over the page; null when nothing
|
|
426
|
+
// in the stack paints (the text then keeps the old rule).
|
|
427
|
+
function composite(stack, page, svg, memo) {
|
|
428
|
+
var c = null;
|
|
429
|
+
for (var i = 0; i < stack.length; i++) {
|
|
430
|
+
var p = paintOf(stack[i], svg, memo);
|
|
431
|
+
if (!p) continue;
|
|
432
|
+
if (!c) c = page.slice(0, 3);
|
|
433
|
+
c = [p[0] * p[3] + c[0] * (1 - p[3]), p[1] * p[3] + c[1] * (1 - p[3]), p[2] * p[3] + c[2] * (1 - p[3])];
|
|
434
|
+
}
|
|
435
|
+
return c ? [c[0], c[1], c[2], 1] : null;
|
|
436
|
+
}
|
|
437
|
+
// No geometry at recolour time (a diagram hidden while the theme flips): the text
|
|
438
|
+
// could sit on any shape of its own node or cluster, so it is judged against all
|
|
439
|
+
// of them: each shape's paint composited over the page, or the page where a shape
|
|
440
|
+
// paints nothing (a table cell without a bgcolor). Text in no node keeps the old rule.
|
|
441
|
+
function nodeFallback(el, page, svg, memo) {
|
|
442
|
+
var n = el.closest('g.node, g.cluster');
|
|
443
|
+
if (!n) return null;
|
|
444
|
+
var pg = [page[0], page[1], page[2], 1], all = n.querySelectorAll(PAINTED), out = [], seen = {};
|
|
445
|
+
for (var i = 0; i < all.length; i++) {
|
|
446
|
+
if (all[i].closest('foreignObject')) continue;
|
|
447
|
+
var c = composite([all[i]], page, svg, memo) || pg;
|
|
448
|
+
if (!seen[c.join()]) { seen[c.join()] = 1; out.push(c); }
|
|
449
|
+
}
|
|
450
|
+
return out.length ? out : [pg];
|
|
451
|
+
}
|
|
452
|
+
// R7 option A: a dark text keeps its author colour unless the rule's replacement
|
|
453
|
+
// (a toned colour for saturated text) or #e3e3e3 reads better on what is under it.
|
|
454
|
+
// `unders` is the colour under it (one entry) or, with no geometry, every colour it
|
|
455
|
+
// might sit on: each option is scored by its WORST contrast over them, the best
|
|
456
|
+
// score wins, ties keep the author's then the toned colour. null = keep the
|
|
457
|
+
// author's colour. Over no shape (unders null): the old rule (the replacement).
|
|
458
|
+
// Shapes are never changed here.
|
|
459
|
+
function textFor(own, unders) {
|
|
460
|
+
var cand = hsl(own)[1] > 0.25 ? tone(own, 0.78, 0.7) : TEXT;
|
|
461
|
+
if (!unders) return cand;
|
|
462
|
+
var opts = cand === TEXT ? [null, TEXT] : [null, cand, TEXT], best = null, bestC = -1;
|
|
463
|
+
for (var i = 0; i < opts.length; i++) {
|
|
464
|
+
var col = opts[i] === null ? own : rgba(opts[i]), worst = Infinity;
|
|
465
|
+
for (var u = 0; u < unders.length; u++) worst = Math.min(worst, contrast(col, unders[u]));
|
|
466
|
+
if (worst > bestC) { best = opts[i]; bestC = worst; }
|
|
467
|
+
}
|
|
468
|
+
return best;
|
|
469
|
+
}
|
|
470
|
+
function decide(own, unders, memo) {
|
|
471
|
+
var key = own.join() + '|' + (unders ? unders.map(function (u) { return u.join(); }).join(';') : '');
|
|
472
|
+
if (!(key in memo)) memo[key] = textFor(own, unders);
|
|
473
|
+
return memo[key];
|
|
474
|
+
}
|
|
475
|
+
function restyle(el, ov) {
|
|
476
|
+
if (!ov) return;
|
|
477
|
+
var base = el.getAttribute('data-md2doc-style');
|
|
478
|
+
el.setAttribute('style', (base ? base.replace(/;?\s*$/, ';') : '') + ov);
|
|
479
|
+
}
|
|
280
480
|
function recolourSvg(svg, dark, opts) {
|
|
281
481
|
var backing = (opts && opts.backing) || 'var(--md-bg)';
|
|
282
482
|
var els = svg.querySelectorAll('polygon, ellipse, path, polyline, rect, line, circle, text, tspan, foreignObject *');
|
|
283
483
|
var live = svg.hasAttribute('data-md2doc-live');
|
|
484
|
+
var texts = [];
|
|
284
485
|
for (var i = 0; i < els.length; i++) {
|
|
285
486
|
var el = els[i];
|
|
286
487
|
if (el.hasAttribute('data-md2doc-style')) {
|
|
@@ -297,43 +498,94 @@
|
|
|
297
498
|
el.setAttribute('data-md2doc-style', el.getAttribute('style') || '');
|
|
298
499
|
}
|
|
299
500
|
if (!dark) continue;
|
|
501
|
+
// Texts and mermaid's XHTML labels wait for the second pass, so each one
|
|
502
|
+
// is paired with the FINAL fill of its own shape whatever the document order.
|
|
503
|
+
if (el.namespaceURI === XHTML || /^(text|tspan)$/i.test(el.tagName)) { texts.push(el); continue; }
|
|
300
504
|
var cs = getComputedStyle(el);
|
|
301
505
|
var ov = '';
|
|
302
|
-
if (el.namespaceURI === XHTML) {
|
|
303
|
-
// Mermaid flowchart labels: HTML inside foreignObject, coloured by CSS.
|
|
304
|
-
var fg = rgba(cs.color), bgc = rgba(cs.backgroundColor);
|
|
305
|
-
if (fg && fg[3] > 0 && lum(fg) < 0.35) ov += 'color:' + (hsl(fg)[1] > 0.25 ? tone(fg, 0.78, 0.7) : TEXT) + ' !important;';
|
|
306
|
-
if (bgc && bgc[3] > 0) {
|
|
307
|
-
var BL = lum(bgc);
|
|
308
|
-
if (BL > 0.85) ov += 'background-color:transparent !important;';
|
|
309
|
-
else if (BL > 0.3) ov += 'background-color:' + tone(bgc, 0.26, 0.55) + ' !important;';
|
|
310
|
-
}
|
|
311
|
-
if (ov) {
|
|
312
|
-
var hb = el.getAttribute('data-md2doc-style');
|
|
313
|
-
el.setAttribute('style', (hb ? hb.replace(/;?\s*$/, ';') : '') + ov);
|
|
314
|
-
}
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
506
|
var fill = rgba(cs.fill), stroke = rgba(cs.stroke);
|
|
318
507
|
// Overrides are written once, as attribute text, never through el.style:
|
|
319
508
|
// setProperty leaves style="" on the element after the restore removes
|
|
320
509
|
// the attribute (Chromium re-serialises it), so light would not be
|
|
321
510
|
// byte-exact.
|
|
322
|
-
if (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (fill
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
511
|
+
if (fill && fill[3] > 0) {
|
|
512
|
+
var L = lum(fill);
|
|
513
|
+
if (L > 0.85) ov += isWaveLabelBacking(el) ? 'fill:' + backing + ' !important;' : 'fill:transparent !important;';
|
|
514
|
+
else if (L > 0.3) ov += 'fill:' + tone(fill, 0.26, 0.55) + ' !important;';
|
|
515
|
+
else if (el.closest('g.edge, marker')) ov += 'fill:' + lineColour(fill) + ' !important;';
|
|
516
|
+
}
|
|
517
|
+
if (stroke && stroke[3] > 0 && lum(stroke) < 0.35) ov += 'stroke:' + lineColour(stroke) + ' !important;';
|
|
518
|
+
restyle(el, ov);
|
|
519
|
+
}
|
|
520
|
+
// Only a dark text has a choice to make. It uses the stack measured before the
|
|
521
|
+
// toggle flipped the theme when there is one. Otherwise geometry is read only
|
|
522
|
+
// when the choice can differ between what the text might sit on (each opaque
|
|
523
|
+
// paint, or none; any translucent paint always measures): reading it forces a
|
|
524
|
+
// synchronous layout, which a freshly drawn svg (the wave editor redraws on every
|
|
525
|
+
// edit) has not had yet. An svg text's own fill is read here once and reused
|
|
526
|
+
// below: the writes below would otherwise force a style recalc before every read.
|
|
527
|
+
var memo = {}, paints = new Map(), unders = [], owns = [], need = [], fills = null, translucent = false;
|
|
528
|
+
if (dark) adopt(svg);
|
|
529
|
+
var known = geoCache && geoCache.get(svg), page = null;
|
|
530
|
+
function pageColour() {
|
|
531
|
+
if (page) return page;
|
|
532
|
+
var bk = opts && opts.backing;
|
|
533
|
+
page = (bk && /^rgba?\(/.test(bk) ? rgba(bk) : null) || rgba(getComputedStyle(document.body).backgroundColor) || [255, 255, 255, 1];
|
|
534
|
+
return page;
|
|
535
|
+
}
|
|
536
|
+
for (var k = 0; k < texts.length; k++) {
|
|
537
|
+
var kc = owns[k] = rgba(texts[k].namespaceURI === XHTML ? getComputedStyle(texts[k]).color : getComputedStyle(texts[k]).fill);
|
|
538
|
+
if (!kc || kc[3] === 0 || lum(kc) >= 0.35) continue;
|
|
539
|
+
var st = known && known.get(geoOf(texts[k]));
|
|
540
|
+
if (st && st.every(function (s) { return svg.contains(s); })) { var cu = composite(st, pageColour(), svg, paints); unders[k] = cu ? [cu] : null; continue; }
|
|
541
|
+
var kg = geoOf(texts[k]);
|
|
542
|
+
if (noGeo && kg && noGeo.get(kg) === geoGen) { unders[k] = nodeFallback(texts[k], pageColour(), svg, paints); continue; }
|
|
543
|
+
if (fills === null) {
|
|
544
|
+
fills = [];
|
|
545
|
+
var all = svg.querySelectorAll(PAINTED), seen = {};
|
|
546
|
+
for (var q = 0; q < all.length; q++) {
|
|
547
|
+
if (all[q].closest(NOT_PAINTED)) continue;
|
|
548
|
+
var pq = paintOf(all[q], svg, paints);
|
|
549
|
+
if (!pq) continue;
|
|
550
|
+
if (pq[3] < 1) translucent = true;
|
|
551
|
+
else if (!seen[pq.join()]) { seen[pq.join()] = 1; fills.push(pq); }
|
|
330
552
|
}
|
|
331
|
-
if (stroke && stroke[3] > 0 && lum(stroke) < 0.35) ov += 'stroke:' + lineColour(stroke) + ' !important;';
|
|
332
553
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
554
|
+
var first = decide(kc, null, memo), differs = translucent;
|
|
555
|
+
for (var w = 0; w < fills.length && !differs; w++) differs = decide(kc, [fills[w]], memo) !== first;
|
|
556
|
+
if (differs) need.push(k);
|
|
557
|
+
}
|
|
558
|
+
if (need.length) {
|
|
559
|
+
var got = measure(svg, need.map(function (n) { return geoOf(texts[n]); }));
|
|
560
|
+
for (var r = 0; r < need.length; r++) {
|
|
561
|
+
var ns = got.get(geoOf(texts[need[r]]));
|
|
562
|
+
var nc = ns ? composite(ns, pageColour(), svg, paints) : null;
|
|
563
|
+
unders[need[r]] = ns ? (nc ? [nc] : null) : nodeFallback(texts[need[r]], pageColour(), svg, paints);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
for (var j = 0; j < texts.length; j++) {
|
|
567
|
+
var t = texts[j], tov = '';
|
|
568
|
+
if (t.namespaceURI === XHTML) {
|
|
569
|
+
// Mermaid flowchart labels: HTML inside foreignObject, coloured by CSS.
|
|
570
|
+
// Read again: a label inherits the colour its parent was just given.
|
|
571
|
+
var tcs = getComputedStyle(t), fg = rgba(tcs.color), bgc = rgba(tcs.backgroundColor);
|
|
572
|
+
if (fg && fg[3] > 0 && lum(fg) < 0.35) {
|
|
573
|
+
var fc = decide(fg, unders[j], memo);
|
|
574
|
+
if (fc) tov += 'color:' + fc + ' !important;';
|
|
575
|
+
}
|
|
576
|
+
if (bgc && bgc[3] > 0) {
|
|
577
|
+
var BL = lum(bgc);
|
|
578
|
+
if (BL > 0.85) tov += 'background-color:transparent !important;';
|
|
579
|
+
else if (BL > 0.3) tov += 'background-color:' + tone(bgc, 0.26, 0.55) + ' !important;';
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
var tf = owns[j];
|
|
583
|
+
if (tf && tf[3] > 0 && lum(tf) < 0.35) {
|
|
584
|
+
var tc = decide(tf, unders[j], memo);
|
|
585
|
+
if (tc) tov += 'fill:' + tc + ' !important;';
|
|
586
|
+
}
|
|
336
587
|
}
|
|
588
|
+
restyle(t, tov);
|
|
337
589
|
}
|
|
338
590
|
if (dark) svg.setAttribute('data-md2doc-recoloured', ''); else svg.removeAttribute('data-md2doc-recoloured');
|
|
339
591
|
}
|
package/lib/theme/tokens.js
CHANGED
|
@@ -163,6 +163,9 @@ function themeCss() {
|
|
|
163
163
|
' ' + D + ' .content a:not(.heading-anchor), ' + D + ' .content a:not(.heading-anchor):visited { color: ' + LINK_DARK + '; }',
|
|
164
164
|
' ' + D + ' .content img { background: #ffffff; border-radius: 4px; box-shadow: 0 0 0 10px #ffffff; }',
|
|
165
165
|
' ' + D + ' .content .drawio { background: #ffffff; border-radius: 6px; box-shadow: 0 0 0 10px #ffffff; }',
|
|
166
|
+
// R7 甲: a Markdown-native inline svg (wrapped in .inline-svg by the renderer) gets the
|
|
167
|
+
// same white plate; its ink stays the light text colour because the plate is always white.
|
|
168
|
+
' ' + D + ' .content .inline-svg > svg, ' + D + ' .content .ed-block[data-svg-plate="1"] > svg, ' + D + ' .content .ed-block[data-svg-plate="1"] > p > svg { background: #ffffff; color: #1f2328; border-radius: 4px; box-shadow: 0 0 0 10px #ffffff; }',
|
|
166
169
|
' ' + D + ' .lightbox-canvas > svg[data-md2doc-recoloured] { background: var(--md-bg); }',
|
|
167
170
|
' }',
|
|
168
171
|
' .md2doc-theme-toggle { position: fixed; right: 20px; bottom: 20px; z-index: 60; width: 40px; height: 40px;',
|