jamdesk 1.1.96 → 1.1.97
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.97",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import dynamic from 'next/dynamic';
|
|
4
|
-
import { useMemo, useState } from 'react';
|
|
4
|
+
import { useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { readCachedHeight } from './mermaidCache';
|
|
6
6
|
import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect';
|
|
7
7
|
|
|
8
8
|
// Neutral floor reserved in the SSR markup itself (present on the browser's
|
|
9
9
|
// first paint, before any JS) so a cache miss / pre-hydration window shows
|
|
10
10
|
// correctly-sized empty space instead of a collapse or a grey pulse. Roughly
|
|
11
|
-
// the prior skeleton footprint;
|
|
12
|
-
//
|
|
11
|
+
// the prior skeleton footprint; a cache hit refines it to the exact diagram
|
|
12
|
+
// height pre-paint, and once the diagram renders the floor is released
|
|
13
|
+
// entirely (see the layout effect below).
|
|
13
14
|
const DEFAULT_MERMAID_RESERVE_PX = 192;
|
|
14
15
|
|
|
16
|
+
// Sentinel: floor released, MermaidInner's own min-height governs the box.
|
|
17
|
+
const FLOOR_RELEASED_PX = 0;
|
|
18
|
+
|
|
19
|
+
// A rendered diagram (or the error box) is always taller than this; the empty
|
|
20
|
+
// pre-render container is 0px. Distinguishes "content has painted" from "still
|
|
21
|
+
// reserving the gap" so the floor is released only once it is dead weight.
|
|
22
|
+
const RENDERED_FLOOR_RELEASE_PX = 8;
|
|
23
|
+
|
|
15
24
|
const MermaidInner = dynamic(
|
|
16
25
|
() => import('./MermaidInner').then(mod => ({ default: mod.MermaidInner })),
|
|
17
26
|
{ ssr: false, loading: () => null }
|
|
@@ -25,21 +34,49 @@ interface MermaidProps {
|
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
export function Mermaid({ children, className, minWidth }: MermaidProps) {
|
|
37
|
+
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
38
|
+
|
|
28
39
|
// Server + first (hydration) render emit the constant default — identical
|
|
29
40
|
// markup, so no hydration mismatch. The sessionStorage read happens only
|
|
30
41
|
// in the post-hydration layout effect, never in this initializer.
|
|
31
42
|
const [reservedPx, setReservedPx] = useState(DEFAULT_MERMAID_RESERVE_PX);
|
|
32
43
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// exact diagram height so the SVG paints into already-exact space.
|
|
37
|
-
// Distinct from the reverted spike: one pre-paint settle before injection,
|
|
38
|
-
// never a re-render around an already-injected node. minWidth still flows
|
|
39
|
-
// to MermaidInner unchanged; the wrapper owns height only.
|
|
44
|
+
// The reserved min-height only bridges the pre-render gap; it must never
|
|
45
|
+
// outlive the diagram or it leaves dead space under any diagram shorter
|
|
46
|
+
// than the floor (the cache-miss path never refines it down).
|
|
40
47
|
useIsomorphicLayoutEffect(() => {
|
|
41
|
-
const
|
|
42
|
-
|
|
48
|
+
const wrapper = wrapperRef.current;
|
|
49
|
+
|
|
50
|
+
// Relies on MermaidInner rendering exactly one HTMLElement root (the
|
|
51
|
+
// diagram container or the error box); if that contract changes, this
|
|
52
|
+
// height probe silently targets the wrong node.
|
|
53
|
+
const releaseIfRendered = (): boolean => {
|
|
54
|
+
const content = wrapper?.firstElementChild as HTMLElement | null;
|
|
55
|
+
if (content && content.clientHeight > RENDERED_FLOOR_RELEASE_PX) {
|
|
56
|
+
setReservedPx(FLOOR_RELEASED_PX);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Already painted (warm soft-nav: SVG hydrated synchronously from cache) —
|
|
63
|
+
// the floor is already dead weight, skip the cache refine entirely.
|
|
64
|
+
if (releaseIfRendered()) return;
|
|
65
|
+
|
|
66
|
+
// Not yet painted: cache hit → reserve the exact height pre-paint (zero
|
|
67
|
+
// shift); cache miss / new diagram → hold the default floor for its gap.
|
|
68
|
+
const cached = readCachedHeight(children);
|
|
69
|
+
setReservedPx(cached > 0 ? cached : DEFAULT_MERMAID_RESERVE_PX);
|
|
70
|
+
|
|
71
|
+
if (!wrapper || typeof MutationObserver === 'undefined') return;
|
|
72
|
+
// childList only — no `attributes`: MermaidInner's post-commit style pass
|
|
73
|
+
// mutates SVG child styles heavily; observing attributes would fire a
|
|
74
|
+
// callback storm. The SVG-insert is a childList mutation, all we need.
|
|
75
|
+
const mo = new MutationObserver(() => {
|
|
76
|
+
if (releaseIfRendered()) mo.disconnect();
|
|
77
|
+
});
|
|
78
|
+
mo.observe(wrapper, { childList: true, subtree: true });
|
|
79
|
+
return () => mo.disconnect();
|
|
43
80
|
}, [children]);
|
|
44
81
|
|
|
45
82
|
// Stable element identity across reservedPx changes. The pre-paint
|
|
@@ -63,7 +100,7 @@ export function Mermaid({ children, className, minWidth }: MermaidProps) {
|
|
|
63
100
|
);
|
|
64
101
|
|
|
65
102
|
return (
|
|
66
|
-
<div data-testid="mermaid-wrapper" style={{ minHeight: reservedPx }}>
|
|
103
|
+
<div ref={wrapperRef} data-testid="mermaid-wrapper" style={{ minHeight: reservedPx }}>
|
|
67
104
|
{inner}
|
|
68
105
|
</div>
|
|
69
106
|
);
|