create-zudo-doc 0.2.20 → 0.2.22

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 (74) hide show
  1. package/dist/features/image-enlarge.d.ts +10 -2
  2. package/dist/features/image-enlarge.js +10 -2
  3. package/dist/scaffold.js +22 -22
  4. package/dist/zfb-config-gen.d.ts +16 -10
  5. package/dist/zfb-config-gen.js +34 -239
  6. package/package.json +1 -1
  7. package/templates/base/pages/lib/_body-end-islands.tsx +6 -13
  8. package/templates/base/pages/lib/_category-nav.tsx +30 -115
  9. package/templates/base/pages/lib/_category-tree-nav.tsx +38 -65
  10. package/templates/base/pages/lib/_compose-meta-title.ts +2 -6
  11. package/templates/base/pages/lib/_doc-body-end.tsx +3 -35
  12. package/templates/base/pages/lib/_doc-content-header.tsx +10 -111
  13. package/templates/base/pages/lib/_doc-history-area.tsx +19 -211
  14. package/templates/base/pages/lib/_doc-metainfo-area.tsx +10 -113
  15. package/templates/base/pages/lib/_doc-page-renderer.tsx +22 -183
  16. package/templates/base/pages/lib/_doc-page-shell.tsx +17 -251
  17. package/templates/base/pages/lib/_doc-pager.tsx +4 -74
  18. package/templates/base/pages/lib/_doc-route-entries.ts +43 -177
  19. package/templates/base/pages/lib/_doc-route-paths.ts +16 -101
  20. package/templates/base/pages/lib/_doc-tags-area.tsx +14 -77
  21. package/templates/base/pages/lib/_footer-with-defaults.tsx +37 -225
  22. package/templates/base/pages/lib/_frontmatter-preview-data.ts +5 -31
  23. package/templates/base/pages/lib/_head-with-defaults.tsx +13 -138
  24. package/templates/base/pages/lib/_header-with-defaults.tsx +24 -324
  25. package/templates/base/pages/lib/_inline-version-switcher.tsx +16 -78
  26. package/templates/base/pages/lib/_nav-data-prep.ts +32 -51
  27. package/templates/base/pages/lib/_nav-source-cache.ts +12 -57
  28. package/templates/base/pages/lib/_nav-source-docs.ts +33 -233
  29. package/templates/base/pages/lib/_search-widget-script.ts +2 -470
  30. package/templates/base/pages/lib/_search-widget.tsx +9 -195
  31. package/templates/base/pages/lib/_sidebar-prepaint.tsx +6 -47
  32. package/templates/base/pages/lib/_sidebar-with-defaults.tsx +16 -118
  33. package/templates/base/pages/lib/_site-tree-nav.tsx +29 -80
  34. package/templates/base/pages/lib/doc-page-props.ts +26 -44
  35. package/templates/base/pages/lib/locale-merge.ts +32 -145
  36. package/templates/base/pages/lib/route-enumerators.ts +52 -286
  37. package/templates/base/pages/robots.txt.tsx +2 -26
  38. package/templates/base/src/components/ai-chat-modal.tsx +9 -8
  39. package/templates/base/src/components/doc-history.tsx +9 -8
  40. package/templates/base/src/components/image-enlarge.tsx +11 -8
  41. package/templates/base/src/components/sidebar-toggle.tsx +6 -170
  42. package/templates/base/src/components/sidebar-tree.tsx +6 -548
  43. package/templates/base/src/components/site-tree-nav.tsx +6 -220
  44. package/templates/base/src/config/color-scheme-utils.ts +34 -158
  45. package/templates/base/src/config/i18n.ts +9 -0
  46. package/templates/base/src/config/z-index-tokens.ts +5 -4
  47. package/templates/base/src/styles/global.css +5 -579
  48. package/templates/base/src/utils/base.ts +1 -1
  49. package/templates/base/src/utils/docs.ts +47 -16
  50. package/templates/base/src/utils/github.ts +12 -9
  51. package/templates/base/src/utils/nav-scope.ts +13 -42
  52. package/templates/base/src/utils/sidebar.ts +18 -86
  53. package/templates/base/src/utils/slug.ts +10 -53
  54. package/templates/base/src/utils/smart-break.tsx +12 -120
  55. package/templates/base/src/utils/tags.ts +25 -68
  56. package/templates/features/bodyFootUtil/files/src/utils/github.ts +12 -9
  57. package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +13 -39
  58. package/templates/features/docHistory/files/src/components/doc-history.tsx +8 -636
  59. package/templates/features/docHistory/files/src/types/doc-history.ts +7 -23
  60. package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +35 -201
  61. package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +8 -269
  62. package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +6 -99
  63. package/templates/features/tagGovernance/files/scripts/tags-audit.ts +67 -515
  64. package/templates/features/tauri/files/src-tauri/capabilities/default.json +1 -4
  65. package/templates/features/tauri/files/src-tauri/tauri.conf.json +1 -1
  66. package/templates/features/tauriDev/files/src-tauri-dev/capabilities/default.json +1 -1
  67. package/templates/features/versioning/files/pages/lib/_versions-page.tsx +21 -73
  68. package/templates/base/plugins/connect-adapter.mjs +0 -169
  69. package/templates/base/plugins/search-index-plugin.mjs +0 -66
  70. package/templates/base/scripts/gen-z-index.mjs +0 -157
  71. package/templates/base/src/components/mermaid-enlarge.tsx +0 -490
  72. package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +0 -47
  73. package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +0 -111
  74. package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +0 -93
@@ -42,6 +42,11 @@
42
42
  above (line 11); the safelist import above generates the component utilities
43
43
  its headings/links/etc. depend on. */
44
44
  @import "@takazudo/zudo-doc/content.css";
45
+ /* Non-island feature CSS (code block buttons, syntect light-dark token rule,
46
+ .zd-html-preview-code, KaTeX, sidebar toggle geometry, view-transition chrome,
47
+ .ai-chat-md, .zd-enlarge*, .zd-mermaid*).
48
+ Shipped by @takazudo/zudo-doc as single source of truth. See zudolab/zudo-doc#2331. */
49
+ @import "@takazudo/zudo-doc/features.css";
45
50
  /* @slot:global-css:imports */
46
51
 
47
52
  /* ========================================
@@ -308,234 +313,6 @@ body {
308
313
  }
309
314
  }
310
315
 
311
- /* ========================================
312
- * Code block buttons (copy + word wrap)
313
- * ======================================== */
314
-
315
- .code-block-wrapper {
316
- position: relative;
317
- }
318
-
319
- pre[class^="syntect-"].word-wrap {
320
- overflow-x: hidden;
321
- }
322
-
323
- pre[class^="syntect-"].word-wrap code {
324
- white-space: pre-wrap;
325
- overflow-wrap: anywhere;
326
- }
327
-
328
- .code-block-sr-announce {
329
- position: absolute;
330
- width: 1px;
331
- height: 1px;
332
- overflow: hidden;
333
- clip: rect(0, 0, 0, 0);
334
- white-space: nowrap;
335
- }
336
-
337
- .code-buttons {
338
- position: absolute;
339
- top: var(--spacing-hsp-sm);
340
- right: var(--spacing-hsp-sm);
341
- display: flex;
342
- gap: var(--spacing-hsp-2xs);
343
- opacity: 0;
344
- transition: opacity 0.15s;
345
- z-index: var(--z-index-local-1);
346
- }
347
-
348
- .code-block-wrapper:hover .code-buttons,
349
- .code-block-wrapper:focus-within .code-buttons {
350
- opacity: 1;
351
- }
352
-
353
- .code-btn {
354
- display: flex;
355
- align-items: center;
356
- justify-content: center;
357
- width: var(--spacing-hsp-2xl);
358
- height: var(--spacing-hsp-2xl);
359
- padding: 0;
360
- border: 1px solid var(--color-muted);
361
- border-radius: var(--radius-lg);
362
- background-color: var(--color-surface);
363
- color: var(--color-fg);
364
- cursor: pointer;
365
- transition:
366
- color 0.15s,
367
- border-color 0.15s,
368
- background-color 0.15s;
369
- }
370
-
371
- .code-btn:hover {
372
- color: var(--color-fg);
373
- border-color: var(--color-fg);
374
- }
375
-
376
- .code-btn .code-icon {
377
- width: var(--spacing-hsp-lg);
378
- height: var(--spacing-hsp-lg);
379
- }
380
-
381
- /* Copy: icon swap */
382
- .code-btn-copy .code-icon-check {
383
- display: none;
384
- }
385
- .code-btn-copy .code-icon-copy {
386
- display: block;
387
- }
388
- .code-btn-copy.copied .code-icon-check {
389
- display: block;
390
- color: var(--color-success);
391
- }
392
- .code-btn-copy.copied .code-icon-copy {
393
- display: none;
394
- }
395
-
396
- /* Word wrap active state */
397
- .code-btn-wrap.active {
398
- color: var(--color-accent);
399
- border-color: var(--color-accent);
400
- }
401
-
402
- @media (scripting: none) {
403
- .code-buttons {
404
- display: none;
405
- }
406
- }
407
-
408
- /* ========================================
409
- * Code-block syntax-highlight token color rule
410
- * ======================================== */
411
-
412
- /* Build-time: zfb's Rust pipeline runs SyntectPlugin and emits
413
- * <pre class="syntect-..."> with inline style="color:#hex" on every token
414
- * span. The inline hex is the desired token color — this rule must NOT
415
- * override it (so: NO !important on `color`).
416
- *
417
- * Runtime (design-token-tweak panel, when enabled): Shiki re-highlights on
418
- * demand and emits spans with inline style="--shiki-light:#X;--shiki-dark:#Y"
419
- * — CSS custom properties only, no `color` declaration — depending on this
420
- * rule to resolve light-dark(var(--shiki-light), var(--shiki-dark)) to the
421
- * active theme. Shiki spans have no inline `color`, so the rule applies
422
- * without needing !important. */
423
- [data-theme] .shiki,
424
- [data-theme] .shiki span,
425
- [data-theme] pre[class^="syntect-"],
426
- [data-theme] pre[class^="syntect-"] span {
427
- color: light-dark(var(--shiki-light), var(--shiki-dark));
428
- background-color: light-dark(var(--shiki-light-bg), var(--shiki-dark-bg));
429
- font-style: light-dark(
430
- var(--shiki-light-font-style, inherit),
431
- var(--shiki-dark-font-style, inherit)
432
- );
433
- }
434
-
435
- /* HtmlPreview component — Shiki code blocks
436
- * Token colors inherit from the global [data-theme] .shiki rule above.
437
- * These rules only adjust layout and background for the preview context. */
438
- .zd-html-preview-code pre.shiki {
439
- margin: 0;
440
- padding: 0.75rem;
441
- font-size: 0.875rem;
442
- line-height: 1.6;
443
- overflow-x: auto;
444
- }
445
-
446
- .zd-html-preview-code pre.shiki code {
447
- font-family: var(--font-mono);
448
- white-space: pre;
449
- }
450
-
451
- /* ========================================
452
- * Code block line highlighting
453
- * ======================================== */
454
-
455
- /* Line highlighting via {1,3-5} meta syntax.
456
- * zfb emits data-line-highlight="true" on the <span class="line"> element
457
- * (verified empirically at next.13). The .highlighted class form is kept for
458
- * forward-compat in case a future zfb version emits both. */
459
- pre[class^="syntect-"] .line.highlighted,
460
- pre[class^="syntect-"] [data-line-highlight="true"] {
461
- background-color: color-mix(in srgb, var(--color-accent) 15%, transparent);
462
- }
463
-
464
- /* Word highlighting via /word/ meta syntax */
465
- pre[class^="syntect-"] .line .highlighted-word {
466
- background-color: color-mix(in srgb, var(--color-accent) 25%, transparent);
467
- border-bottom: 1px solid var(--color-accent);
468
- }
469
-
470
- /* ========================================
471
- * Code block titles
472
- * ======================================== */
473
-
474
- .code-block-container {
475
- margin-top: 1em;
476
- margin-bottom: 1em;
477
- }
478
-
479
- .code-block-title {
480
- font-family: var(--font-mono);
481
- font-size: var(--text-small);
482
- color: var(--color-muted);
483
- background-color: var(--color-code-bg);
484
- border: 1px solid var(--color-muted);
485
- border-bottom: none;
486
- padding: var(--spacing-vsp-2xs) var(--spacing-hsp-lg);
487
- }
488
-
489
- .code-block-container pre[class^="syntect-"] {
490
- margin-top: 0 !important;
491
- border-top-left-radius: 0;
492
- border-top-right-radius: 0;
493
- }
494
-
495
- /* ========================================
496
- * CodeGroup raw-code panels (codeTabs Option A)
497
- *
498
- * zfb's :::code-group emits <CodeGroup tabs={[...]}> with one
499
- * <pre data-lang="…">{RAW text}</pre> child per fence. These <pre> elements
500
- * are NOT syntect-highlighted (the Rust pipeline doesn't highlight inside
501
- * code-group fences), so they get no `.syntect-*` class. Apply explicit
502
- * code-block visual treatment via tokens to match the syntect-highlighted
503
- * code blocks surrounding them.
504
- * ======================================== */
505
-
506
- .code-group-panel pre[data-lang] {
507
- font-family: var(--font-mono);
508
- font-size: var(--text-small);
509
- line-height: var(--leading-relaxed);
510
- background-color: var(--color-code-bg);
511
- color: var(--color-code-fg);
512
- padding: var(--spacing-vsp-xs) var(--spacing-hsp-lg);
513
- overflow-x: auto;
514
- border-radius: 0 0 var(--radius-DEFAULT) var(--radius-DEFAULT);
515
- margin: 0;
516
- }
517
-
518
- /* KaTeX math equations */
519
- .katex {
520
- font-size: 1.1em;
521
- }
522
-
523
- .katex-display {
524
- margin: var(--spacing-vsp-md) 0;
525
- overflow-x: auto;
526
- overflow-y: hidden;
527
- padding: var(--spacing-vsp-xs) 0;
528
- }
529
-
530
- .katex-display > .katex {
531
- white-space: nowrap;
532
- }
533
-
534
- /* Make KaTeX respect our color scheme */
535
- .katex .base {
536
- color: var(--zd-fg);
537
- }
538
-
539
316
  /* ── Search highlight ── */
540
317
  [data-search-results] mark {
541
318
  background-color: var(--color-matched-keyword-bg);
@@ -544,30 +321,6 @@ pre[class^="syntect-"] .line .highlighted-word {
544
321
  border-radius: 2px;
545
322
  }
546
323
 
547
- /* ========================================
548
- * Page transition animations
549
- * ======================================== */
550
-
551
- @keyframes contentFadeIn {
552
- from {
553
- opacity: 0;
554
- transform: translateY(-0.75rem);
555
- }
556
- to {
557
- opacity: 1;
558
- transform: translateY(0);
559
- }
560
- }
561
-
562
- @keyframes contentFadeOut {
563
- from {
564
- opacity: 1;
565
- }
566
- to {
567
- opacity: 0;
568
- }
569
- }
570
-
571
324
  /* Version-switcher responsive visibility (moved out of the component to
572
325
  * avoid <style>-inside-<div> HTML5 content-model violation; required when
573
326
  * the i18nVersion feature ships a <VersionSwitcher> wrapped in
@@ -579,61 +332,6 @@ pre[class^="syntect-"] .line .highlighted-word {
579
332
  }
580
333
  }
581
334
 
582
- /* ========================================
583
- * Desktop sidebar toggle (W7A: unconditional — feature gates at runtime)
584
- * ======================================== */
585
-
586
- @media (min-width: 1024px) {
587
- #desktop-sidebar {
588
- transition:
589
- transform var(--zd-transition-slow) ease-in-out,
590
- visibility var(--zd-transition-slow);
591
- }
592
-
593
- html[data-sidebar-hidden] #desktop-sidebar {
594
- transform: translateX(calc(-1 * var(--zd-sidebar-w)));
595
- visibility: hidden;
596
- }
597
-
598
- .zd-sidebar-content-wrapper {
599
- transition: margin-left var(--zd-transition-slow) ease-in-out;
600
- }
601
-
602
- html[data-sidebar-hidden] .zd-sidebar-content-wrapper {
603
- margin-left: 0;
604
- }
605
-
606
- /* When hidden via the toggle, narrow the content band to the
607
- * hide_sidebar frontmatter width so it centers (the flex parent already
608
- * applies justify-content: center) instead of leaving a dead gap where
609
- * the sidebar was. 80rem must match the `max-w-[80rem]` hide_sidebar
610
- * branch in doc-layout.tsx. These rules are unlayered, so they win over
611
- * the Tailwind `max-w-[clamp(...)]` utility (utilities layer). (#2002) */
612
- .zd-doc-content-band {
613
- transition: max-width var(--zd-transition-slow) ease-in-out;
614
- }
615
-
616
- html[data-sidebar-hidden] .zd-doc-content-band {
617
- max-width: 80rem;
618
- }
619
- }
620
-
621
- /* Sidebar toggle button — left position uses CSS variable, needs global rule */
622
- @media (min-width: 1024px) {
623
- .zd-desktop-sidebar-toggle {
624
- left: var(--zd-sidebar-w);
625
- }
626
-
627
- html[data-sidebar-hidden] .zd-desktop-sidebar-toggle {
628
- left: 0;
629
- }
630
- }
631
-
632
- /* The SPA-swap flash guard (`data-sidebar-no-transition`) was removed once
633
- * doc-layout adopted <ClientRouter preserveHtmlAttrs={["data-sidebar-hidden",
634
- * ...]} /> (zfb-runtime >= 0.1.0-next.52): the attribute now survives the swap
635
- * before paint, so no sidebar geometry transition fires mid-swap. */
636
-
637
335
  /* ── Find-in-page highlight (W7A: unconditional — Tauri feature gates at runtime) ── */
638
336
  .find-match {
639
337
  background-color: color-mix(in oklch, var(--color-warning) 40%, transparent);
@@ -645,276 +343,4 @@ pre[class^="syntect-"] .line .highlighted-word {
645
343
  outline: 2px solid color-mix(in oklch, var(--color-warning) 90%, transparent);
646
344
  }
647
345
 
648
- /* ========================================
649
- * Image enlarge (.zd-enlargeable) — W7A: unconditional, runtime-gated
650
- * ======================================== */
651
-
652
- .zd-enlargeable {
653
- position: relative;
654
- display: block;
655
- margin-inline: 0;
656
- margin-block-end: 0;
657
- border: 1px solid transparent;
658
- transition: border-color var(--default-transition-duration);
659
- }
660
-
661
- /* Eligible images (expand-btn visible): the entire image becomes the click target.
662
- * Show a subtle border as the interactivity affordance, accent on hover. */
663
- .zd-enlargeable:has(.zd-enlarge-btn:not([hidden])) {
664
- border-color: var(--color-muted);
665
- cursor: pointer;
666
- }
667
-
668
- .zd-enlargeable:has(.zd-enlarge-btn:not([hidden])):hover {
669
- border-color: var(--color-accent);
670
- }
671
-
672
- .zd-enlargeable img {
673
- display: block;
674
- max-width: 100%;
675
- height: auto;
676
- }
677
-
678
- .zd-enlarge-btn {
679
- position: absolute;
680
- top: var(--spacing-image-overlay-inset);
681
- right: var(--spacing-image-overlay-inset);
682
- display: flex;
683
- align-items: center;
684
- justify-content: center;
685
- padding: var(--spacing-image-overlay-inset);
686
- border: none;
687
- background: transparent;
688
- cursor: pointer;
689
- z-index: var(--z-index-local-1);
690
- transition: opacity var(--default-transition-duration);
691
- }
692
-
693
- .zd-enlarge-btn::before {
694
- content: "";
695
- position: absolute;
696
- inset: 0;
697
- background: color-mix(in oklch, var(--color-image-overlay-bg) 80%, transparent);
698
- z-index: var(--z-index-local-1);
699
- }
700
-
701
- .zd-enlarge-btn > svg {
702
- position: relative;
703
- z-index: var(--z-index-local-2);
704
- width: var(--spacing-icon-sm);
705
- height: var(--spacing-icon-sm);
706
- color: var(--color-image-overlay-fg);
707
- fill: currentColor;
708
- }
709
-
710
- .zd-enlarge-btn:hover {
711
- opacity: 0.8;
712
- }
713
-
714
- .zd-enlarge-btn[hidden] {
715
- display: none !important;
716
- }
717
-
718
- dialog.zd-enlarge-dialog::backdrop {
719
- background: color-mix(in oklch, var(--color-overlay) 80%, transparent);
720
- }
721
-
722
- /* Close button is positioned to the VIEWPORT corner (not the dialog) so it
723
- * stays visually distinct against any image, regardless of the dialog's size.
724
- * This relies on the dialog being transform-free (see DIALOG_STYLE in
725
- * image-enlarge.tsx) — a transform on the dialog would re-anchor this fixed
726
- * button to the dialog corner. */
727
- .zd-enlarge-dialog-close {
728
- position: fixed;
729
- top: 4px;
730
- right: 4px;
731
- display: flex;
732
- align-items: center;
733
- justify-content: center;
734
- padding: var(--spacing-image-overlay-inset);
735
- border: none;
736
- background: transparent;
737
- cursor: pointer;
738
- z-index: var(--z-index-local-1);
739
- transition: opacity var(--default-transition-duration);
740
- }
741
-
742
- .zd-enlarge-dialog-close:hover {
743
- opacity: 0.8;
744
- }
745
-
746
- .zd-enlarge-dialog-close::before {
747
- content: "";
748
- position: absolute;
749
- inset: 0;
750
- background: color-mix(in oklch, var(--color-image-overlay-bg) 80%, transparent);
751
- z-index: var(--z-index-local-1);
752
- }
753
-
754
- .zd-enlarge-dialog-close > svg {
755
- position: relative;
756
- z-index: var(--z-index-local-2);
757
- width: var(--spacing-icon-lg);
758
- height: var(--spacing-icon-lg);
759
- color: var(--color-image-overlay-fg);
760
- fill: currentColor;
761
- }
762
-
763
- /* Wide viewport: ~2× larger close button to read clearly against any image. */
764
- @media (min-width: 1024px) {
765
- .zd-enlarge-dialog-close {
766
- top: 10px;
767
- right: 10px;
768
- }
769
-
770
- .zd-enlarge-dialog-close > svg {
771
- width: calc(var(--spacing-icon-lg) * 2);
772
- height: calc(var(--spacing-icon-lg) * 2);
773
- }
774
- }
775
-
776
- /* ========================================
777
- * Mermaid enlarge (.zd-mermaid-enlargeable) — issue #2176 / #2178
778
- *
779
- * Unlike images (SSR-wrapped in figure.zd-enlargeable), mermaid diagrams render
780
- * client-side, so the mermaid-enlarge island injects the `.zd-enlarge-btn` into
781
- * each rendered `.mermaid` container and adds `.zd-mermaid-enlargeable` (which
782
- * makes the container position:relative so the button anchors to it). The
783
- * button + close-button visuals are SHARED with image-enlarge above
784
- * (`.zd-enlarge-btn`, `.zd-enlarge-dialog-close`).
785
- * ======================================== */
786
-
787
- .zd-mermaid-enlargeable {
788
- position: relative;
789
- border: 1px solid transparent;
790
- border-radius: var(--radius-DEFAULT);
791
- transition: border-color var(--default-transition-duration);
792
- }
793
-
794
- /* The diagram is always enlargeable once the button is injected — show a subtle
795
- * border affordance, accent on hover (mirrors the image-enlarge affordance). */
796
- .zd-mermaid-enlargeable:has(.zd-enlarge-btn) {
797
- cursor: pointer;
798
- border-color: var(--color-muted);
799
- }
800
-
801
- .zd-mermaid-enlargeable:has(.zd-enlarge-btn):hover {
802
- border-color: var(--color-accent);
803
- }
804
-
805
- /* Zoom/pan dialog. The `<dialog>` stays transform-free (see DIALOG_STYLE in
806
- * mermaid-enlarge.tsx); the transform lives on .zd-mermaid-transform. */
807
- dialog.zd-mermaid-dialog::backdrop {
808
- background: color-mix(in oklch, var(--color-overlay) 80%, transparent);
809
- }
810
-
811
- /* Pan viewport — fills the dialog and clips the (possibly zoomed) diagram.
812
- * touch-action:none so pointer-drag pans on touch without the browser
813
- * intercepting the gesture for scrolling. */
814
- .zd-mermaid-viewport {
815
- width: 100%;
816
- height: 100%;
817
- overflow: hidden;
818
- display: flex;
819
- align-items: center;
820
- justify-content: center;
821
- touch-action: none;
822
- }
823
-
824
- .zd-mermaid-viewport[data-pan-active] {
825
- cursor: grab;
826
- /* In pan mode a drag should pan, not select diagram text. Scoped to
827
- * [data-pan-active] so text stays selectable when pan mode is off. */
828
- -webkit-user-select: none;
829
- user-select: none;
830
- }
831
-
832
- .zd-mermaid-viewport[data-pan-active]:active {
833
- cursor: grabbing;
834
- }
835
-
836
- .zd-mermaid-transform {
837
- width: 100%;
838
- height: 100%;
839
- display: flex;
840
- align-items: center;
841
- justify-content: center;
842
- will-change: transform;
843
- }
844
-
845
- /* SVG sizing — fit the cloned mermaid svg inside the dialog like
846
- * object-fit:contain. The svg fills the viewport box (100% x 100%) and its
847
- * viewBox content is letterboxed/centered by the default
848
- * preserveAspectRatio="xMidYMid meet". A TALL diagram (e.g. a state diagram) no
849
- * longer gets forced to full dialog width with `height:auto` driving the height
850
- * far past the viewport via its aspect ratio — it now shrinks to fit the height
851
- * instead. max-width/max-height !important neutralize the inline px sizing
852
- * mermaid bakes onto the svg (without it the diagram renders tiny). Scoped to
853
- * .zd-mermaid-transform (the cloned-diagram wrapper) so it does NOT hit the
854
- * toolbar / close-button icon svgs that also live inside the dialog. */
855
- .zd-mermaid-transform svg {
856
- max-width: 100% !important;
857
- max-height: 100% !important;
858
- width: 100%;
859
- height: 100%;
860
- }
861
-
862
- /* Edge labels (e.g. "Yes"/"No") render inside a tightly-sized <foreignObject>;
863
- * when the cloned diagram is upscaled to fill the dialog, sub-pixel overflow
864
- * makes the browser clip the last glyph at the foreignObject's right edge.
865
- * Letting the foreignObject overflow render keeps the full label visible. */
866
- .zd-mermaid-transform svg foreignObject {
867
- overflow: visible;
868
- }
869
-
870
- /* Toolbar — bottom-right pill of zoom/pan controls (2× sized buttons). */
871
- .zd-mermaid-toolbar {
872
- position: absolute;
873
- bottom: var(--spacing-image-overlay-inset);
874
- right: var(--spacing-image-overlay-inset);
875
- display: flex;
876
- gap: var(--spacing-hsp-xs);
877
- padding: var(--spacing-hsp-xs);
878
- border: 1px solid var(--color-muted);
879
- border-radius: var(--radius-DEFAULT);
880
- background: color-mix(in oklch, var(--color-surface) 92%, transparent);
881
- z-index: var(--z-index-local-2);
882
- }
883
-
884
- .zd-mermaid-tool-btn {
885
- display: flex;
886
- align-items: center;
887
- justify-content: center;
888
- width: calc(var(--spacing-icon-lg) * 2);
889
- height: calc(var(--spacing-icon-lg) * 2);
890
- padding: var(--spacing-vsp-2xs);
891
- border: none;
892
- border-radius: var(--radius-DEFAULT);
893
- background: transparent;
894
- color: var(--color-fg);
895
- cursor: pointer;
896
- transition:
897
- background-color var(--default-transition-duration),
898
- opacity var(--default-transition-duration);
899
- }
900
-
901
- .zd-mermaid-tool-btn:hover:not([disabled]) {
902
- background: color-mix(in oklch, var(--color-muted) 25%, transparent);
903
- }
904
-
905
- .zd-mermaid-tool-btn[aria-pressed="true"] {
906
- background: color-mix(in oklch, var(--color-accent) 30%, transparent);
907
- color: var(--color-accent);
908
- }
909
-
910
- .zd-mermaid-tool-btn[disabled] {
911
- opacity: 0.4;
912
- cursor: default;
913
- }
914
-
915
- .zd-mermaid-tool-btn > svg {
916
- width: calc(var(--spacing-icon-md) * 2);
917
- height: calc(var(--spacing-icon-md) * 2);
918
- }
919
-
920
346
  /* @slot:global-css:feature-styles */
@@ -156,7 +156,7 @@ export function isDefaultLocaleOnlyPath(path: string): boolean {
156
156
  }
157
157
 
158
158
  /** Build a versioned docs URL for the given slug, version, and lang. */
159
- export function versionedDocsUrl(slug: string, versionSlug: string, lang: Locale = defaultLocale): string {
159
+ export function versionedDocsUrl(slug: string, versionSlug: string, lang: Locale | string = defaultLocale): string {
160
160
  const path = lang === defaultLocale
161
161
  ? `/v/${versionSlug}/docs/${slug}`
162
162
  : `/v/${versionSlug}/${lang}/docs/${slug}`;