raffel 1.1.16 → 1.1.18

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.
Files changed (29) hide show
  1. package/dist/docs/ui/assets/raffel-docs.css +192 -1
  2. package/dist/docs/ui/assets/raffel-docs.js +229 -3
  3. package/dist/docs/ui/client-script/index.d.ts +1 -1
  4. package/dist/docs/ui/client-script/index.d.ts.map +1 -1
  5. package/dist/docs/ui/client-script/index.js +3 -2
  6. package/dist/docs/ui/client-script/index.js.map +1 -1
  7. package/dist/docs/ui/html-builder.d.ts.map +1 -1
  8. package/dist/docs/ui/html-builder.js +26 -1
  9. package/dist/docs/ui/html-builder.js.map +1 -1
  10. package/dist/docs/ui/runtime/index.js +229 -3
  11. package/dist/docs/ui/runtime/index.js.map +1 -1
  12. package/dist/docs/ui/style-sections/content.d.ts +1 -1
  13. package/dist/docs/ui/style-sections/content.d.ts.map +1 -1
  14. package/dist/docs/ui/style-sections/content.js +162 -1
  15. package/dist/docs/ui/style-sections/content.js.map +1 -1
  16. package/dist/docs/ui/style-sections/shell.d.ts.map +1 -1
  17. package/dist/docs/ui/style-sections/shell.js +30 -0
  18. package/dist/docs/ui/style-sections/shell.js.map +1 -1
  19. package/dist/docs/ui/types.d.ts +25 -0
  20. package/dist/docs/ui/types.d.ts.map +1 -1
  21. package/dist/ui/docs/ui/client-script/index.d.ts +1 -1
  22. package/dist/ui/docs/ui/client-script/index.d.ts.map +1 -1
  23. package/dist/ui/docs/ui/html-builder.d.ts.map +1 -1
  24. package/dist/ui/docs/ui/style-sections/content.d.ts +1 -1
  25. package/dist/ui/docs/ui/style-sections/content.d.ts.map +1 -1
  26. package/dist/ui/docs/ui/style-sections/shell.d.ts.map +1 -1
  27. package/dist/ui/docs/ui/types.d.ts +25 -0
  28. package/dist/ui/docs/ui/types.d.ts.map +1 -1
  29. package/package.json +1 -1
@@ -31,6 +31,8 @@ const docsAssetBasePath = String(data.docsAssetBasePath ?? '');
31
31
  const footerMarkdown = data.footerMarkdown ?? null;
32
32
  const tocConfig = data.tocConfig ?? {};
33
33
  const markdownConfig = data.markdownConfig ?? {};
34
+ const mermaidConfig = data.mermaidConfig ?? { enabled: true, src: 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js', viewer: true };
35
+ let mermaidLoadPromise = null;
34
36
  const docsRepoConfig = (data.docsRepoConfig ?? null);
35
37
  const breadcrumbsConfig = (data.breadcrumbsConfig && typeof data.breadcrumbsConfig === 'object')
36
38
  ? data.breadcrumbsConfig
@@ -1490,15 +1492,52 @@ function scrollToActiveHeading() {
1490
1492
  function scrollToEndpoint(id) {
1491
1493
  byId(id)?.scrollIntoView?.({ behavior: 'smooth', block: 'start' });
1492
1494
  }
1493
- function renderMermaidDiagrams(root = doc) {
1494
- const mermaid = win.mermaid;
1495
+ /**
1496
+ * Lazy-load the Mermaid renderer the first time a page with diagrams is
1497
+ * visited. Cached: subsequent route transitions inside the SPA reuse the
1498
+ * same `<script>` injection. Pages without `.mermaid` blocks never trigger
1499
+ * the network request, so the ~3MB library stays off the critical path
1500
+ * for the 95% of docs pages that have no diagrams.
1501
+ */
1502
+ function loadMermaidLibrary(src) {
1503
+ if (win.mermaid)
1504
+ return Promise.resolve(win.mermaid);
1505
+ if (mermaidLoadPromise)
1506
+ return mermaidLoadPromise;
1507
+ mermaidLoadPromise = new Promise((resolve, reject) => {
1508
+ const script = doc.createElement('script');
1509
+ script.src = src;
1510
+ script.defer = true;
1511
+ script.onload = () => resolve(win.mermaid);
1512
+ script.onerror = () => {
1513
+ mermaidLoadPromise = null;
1514
+ reject(new Error(`Failed to load Mermaid from ${src}`));
1515
+ };
1516
+ doc.head?.appendChild(script);
1517
+ });
1518
+ return mermaidLoadPromise;
1519
+ }
1520
+ async function renderMermaidDiagrams(root = doc) {
1495
1521
  const diagrams = Array.from(root?.querySelectorAll?.('.mermaid:not([data-mermaid-rendered])') ?? []);
1496
1522
  if (diagrams.length === 0)
1497
1523
  return;
1498
- if (!mermaid) {
1524
+ if (mermaidConfig.enabled === false) {
1499
1525
  diagrams.forEach(diagram => diagram.classList.add('mermaid-fallback'));
1500
1526
  return;
1501
1527
  }
1528
+ let mermaid = win.mermaid;
1529
+ if (!mermaid) {
1530
+ try {
1531
+ mermaid = await loadMermaidLibrary(mermaidConfig.src);
1532
+ }
1533
+ catch (error) {
1534
+ diagrams.forEach(diagram => {
1535
+ diagram.classList.add('mermaid-fallback', 'mermaid-error');
1536
+ diagram.setAttribute('title', error instanceof Error ? error.message : 'Unable to load Mermaid');
1537
+ });
1538
+ return;
1539
+ }
1540
+ }
1502
1541
  mermaid.initialize?.({ startOnLoad: false, securityLevel: 'strict' });
1503
1542
  diagrams.forEach((diagram, index) => {
1504
1543
  const source = diagram.getAttribute('data-mermaid-source') || diagram.textContent || '';
@@ -1509,6 +1548,8 @@ function renderMermaidDiagrams(root = doc) {
1509
1548
  result?.bindFunctions?.(diagram);
1510
1549
  diagram.dataset.mermaidRendered = 'true';
1511
1550
  diagram.classList.remove('mermaid-fallback', 'mermaid-error');
1551
+ if (mermaidConfig.viewer !== false)
1552
+ mountMermaidViewer(diagram);
1512
1553
  })
1513
1554
  .catch((error) => {
1514
1555
  diagram.dataset.mermaidRendered = 'error';
@@ -1517,6 +1558,191 @@ function renderMermaidDiagrams(root = doc) {
1517
1558
  });
1518
1559
  });
1519
1560
  }
1561
+ /**
1562
+ * Wrap a freshly rendered Mermaid SVG in a viewer overlay:
1563
+ * • toolbar (zoom in/out/reset/fullscreen) — fades in on hover
1564
+ * • drag-to-pan when zoomed in (scale > 1)
1565
+ * • wheel-zoom with Ctrl/⌘ pressed (so vertical scroll still works normally)
1566
+ * • fullscreen via <dialog>, with same controls
1567
+ *
1568
+ * Idempotent: skips diagrams already wrapped (data-mermaid-viewer-mounted).
1569
+ */
1570
+ function mountMermaidViewer(diagram) {
1571
+ if (diagram.dataset.mermaidViewerMounted)
1572
+ return;
1573
+ const svg = diagram.querySelector?.('svg');
1574
+ if (!svg)
1575
+ return;
1576
+ const viewport = doc.createElement('div');
1577
+ viewport.className = 'mermaid-viewport';
1578
+ diagram.insertBefore(viewport, svg);
1579
+ viewport.appendChild(svg);
1580
+ const toolbar = doc.createElement('div');
1581
+ toolbar.className = 'mermaid-toolbar';
1582
+ toolbar.innerHTML = [
1583
+ '<button class="mermaid-btn" data-mermaid-action="zoom-in" aria-label="Zoom in" title="Zoom in">+</button>',
1584
+ '<button class="mermaid-btn" data-mermaid-action="zoom-out" aria-label="Zoom out" title="Zoom out">−</button>',
1585
+ '<button class="mermaid-btn" data-mermaid-action="reset" aria-label="Reset view" title="Reset view">⟲</button>',
1586
+ '<button class="mermaid-btn" data-mermaid-action="fullscreen" aria-label="Open fullscreen" title="Fullscreen">⛶</button>',
1587
+ ].join('');
1588
+ diagram.appendChild(toolbar);
1589
+ const state = { scale: 1, tx: 0, ty: 0 };
1590
+ function apply() {
1591
+ svg.style.transform = `translate(${state.tx}px, ${state.ty}px) scale(${state.scale})`;
1592
+ svg.style.transformOrigin = 'center center';
1593
+ svg.style.transition = 'transform 0.18s cubic-bezier(0.16, 1, 0.3, 1)';
1594
+ viewport.style.cursor = state.scale > 1 ? 'grab' : '';
1595
+ }
1596
+ function reset() { state.scale = 1; state.tx = 0; state.ty = 0; apply(); }
1597
+ function zoomBy(factor) {
1598
+ state.scale = Math.max(0.5, Math.min(state.scale * factor, 8));
1599
+ if (state.scale === 1) {
1600
+ state.tx = 0;
1601
+ state.ty = 0;
1602
+ }
1603
+ apply();
1604
+ }
1605
+ toolbar.addEventListener('click', (ev) => {
1606
+ const action = ev.target?.dataset?.mermaidAction;
1607
+ if (action === 'zoom-in')
1608
+ zoomBy(1.25);
1609
+ else if (action === 'zoom-out')
1610
+ zoomBy(1 / 1.25);
1611
+ else if (action === 'reset')
1612
+ reset();
1613
+ else if (action === 'fullscreen')
1614
+ openMermaidFullscreen(svg.cloneNode(true));
1615
+ });
1616
+ let panning = false;
1617
+ let panStartX = 0;
1618
+ let panStartY = 0;
1619
+ let panStartTx = 0;
1620
+ let panStartTy = 0;
1621
+ viewport.addEventListener('mousedown', (ev) => {
1622
+ if (ev.button !== 0 || state.scale <= 1)
1623
+ return;
1624
+ panning = true;
1625
+ panStartX = ev.clientX;
1626
+ panStartY = ev.clientY;
1627
+ panStartTx = state.tx;
1628
+ panStartTy = state.ty;
1629
+ viewport.style.cursor = 'grabbing';
1630
+ ev.preventDefault();
1631
+ });
1632
+ doc.addEventListener('mousemove', (ev) => {
1633
+ if (!panning)
1634
+ return;
1635
+ state.tx = panStartTx + (ev.clientX - panStartX);
1636
+ state.ty = panStartTy + (ev.clientY - panStartY);
1637
+ svg.style.transform = `translate(${state.tx}px, ${state.ty}px) scale(${state.scale})`;
1638
+ });
1639
+ doc.addEventListener('mouseup', () => {
1640
+ if (!panning)
1641
+ return;
1642
+ panning = false;
1643
+ viewport.style.cursor = state.scale > 1 ? 'grab' : '';
1644
+ });
1645
+ viewport.addEventListener('wheel', (ev) => {
1646
+ if (!ev.ctrlKey && !ev.metaKey)
1647
+ return;
1648
+ ev.preventDefault();
1649
+ zoomBy(ev.deltaY < 0 ? 1.1 : 1 / 1.1);
1650
+ });
1651
+ diagram.dataset.mermaidViewerMounted = 'true';
1652
+ }
1653
+ function openMermaidFullscreen(svg) {
1654
+ const dialog = doc.createElement('dialog');
1655
+ dialog.className = 'mermaid-fullscreen-dialog';
1656
+ const close = doc.createElement('button');
1657
+ close.className = 'mermaid-fullscreen-close';
1658
+ close.setAttribute('aria-label', 'Close');
1659
+ close.textContent = '✕';
1660
+ close.addEventListener('click', () => dialog.close());
1661
+ const stage = doc.createElement('div');
1662
+ stage.className = 'mermaid-fullscreen-stage';
1663
+ svg.removeAttribute('style');
1664
+ stage.appendChild(svg);
1665
+ const fsState = { scale: 1, tx: 0, ty: 0 };
1666
+ function fsApply() {
1667
+ svg.style.transform = `translate(${fsState.tx}px, ${fsState.ty}px) scale(${fsState.scale})`;
1668
+ svg.style.transformOrigin = 'center center';
1669
+ stage.style.cursor = fsState.scale > 1 ? 'grab' : '';
1670
+ }
1671
+ function fsZoom(factor) {
1672
+ fsState.scale = Math.max(0.5, Math.min(fsState.scale * factor, 8));
1673
+ if (fsState.scale === 1) {
1674
+ fsState.tx = 0;
1675
+ fsState.ty = 0;
1676
+ }
1677
+ fsApply();
1678
+ }
1679
+ const toolbar = doc.createElement('div');
1680
+ toolbar.className = 'mermaid-toolbar mermaid-toolbar-fullscreen';
1681
+ toolbar.innerHTML = [
1682
+ '<button class="mermaid-btn" data-mermaid-action="zoom-in" aria-label="Zoom in" title="Zoom in">+</button>',
1683
+ '<button class="mermaid-btn" data-mermaid-action="zoom-out" aria-label="Zoom out" title="Zoom out">−</button>',
1684
+ '<button class="mermaid-btn" data-mermaid-action="reset" aria-label="Reset view" title="Reset view">⟲</button>',
1685
+ ].join('');
1686
+ toolbar.addEventListener('click', (ev) => {
1687
+ const action = ev.target?.dataset?.mermaidAction;
1688
+ if (action === 'zoom-in')
1689
+ fsZoom(1.25);
1690
+ else if (action === 'zoom-out')
1691
+ fsZoom(1 / 1.25);
1692
+ else if (action === 'reset') {
1693
+ fsState.scale = 1;
1694
+ fsState.tx = 0;
1695
+ fsState.ty = 0;
1696
+ fsApply();
1697
+ }
1698
+ });
1699
+ let fsPan = false;
1700
+ let fsStartX = 0;
1701
+ let fsStartY = 0;
1702
+ let fsStartTx = 0;
1703
+ let fsStartTy = 0;
1704
+ stage.addEventListener('mousedown', (ev) => {
1705
+ if (ev.button !== 0 || fsState.scale <= 1)
1706
+ return;
1707
+ fsPan = true;
1708
+ fsStartX = ev.clientX;
1709
+ fsStartY = ev.clientY;
1710
+ fsStartTx = fsState.tx;
1711
+ fsStartTy = fsState.ty;
1712
+ stage.style.cursor = 'grabbing';
1713
+ ev.preventDefault();
1714
+ });
1715
+ doc.addEventListener('mousemove', (ev) => {
1716
+ if (!fsPan)
1717
+ return;
1718
+ fsState.tx = fsStartTx + (ev.clientX - fsStartX);
1719
+ fsState.ty = fsStartTy + (ev.clientY - fsStartY);
1720
+ fsApply();
1721
+ });
1722
+ doc.addEventListener('mouseup', () => {
1723
+ if (!fsPan)
1724
+ return;
1725
+ fsPan = false;
1726
+ stage.style.cursor = fsState.scale > 1 ? 'grab' : '';
1727
+ });
1728
+ stage.addEventListener('wheel', (ev) => {
1729
+ ev.preventDefault();
1730
+ fsZoom(ev.deltaY < 0 ? 1.1 : 1 / 1.1);
1731
+ });
1732
+ dialog.appendChild(close);
1733
+ dialog.appendChild(toolbar);
1734
+ dialog.appendChild(stage);
1735
+ doc.body.appendChild(dialog);
1736
+ dialog.addEventListener('close', () => { try {
1737
+ doc.body.removeChild(dialog);
1738
+ }
1739
+ catch { } });
1740
+ dialog.addEventListener('keydown', (ev) => {
1741
+ if (ev.key === 'Escape')
1742
+ dialog.close();
1743
+ });
1744
+ dialog.showModal?.();
1745
+ }
1520
1746
  function parseComponentProps(raw) {
1521
1747
  const source = String(raw ?? '').trim();
1522
1748
  if (!source)