executable-stories-react 0.8.0 → 0.9.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/index.cjs +36 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +37 -6
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +178 -60
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +25 -3
- package/dist/interactive.d.ts +25 -3
- package/dist/interactive.js +169 -51
- package/dist/interactive.js.map +1 -1
- package/dist/report-island.global.js +24 -24
- package/dist/tailwind.css +1 -1
- package/package.json +1 -1
package/dist/interactive.d.cts
CHANGED
|
@@ -372,12 +372,29 @@ interface ReportSearchProps {
|
|
|
372
372
|
}
|
|
373
373
|
declare const ReportSearch: react.ForwardRefExoticComponent<ReportSearchProps & react.RefAttributes<HTMLInputElement>>;
|
|
374
374
|
|
|
375
|
+
/**
|
|
376
|
+
* View state in the URL, so a filtered report survives a refresh and can be
|
|
377
|
+
* shared as a link.
|
|
378
|
+
*
|
|
379
|
+
* It lives in the HASH, not the query string, because the flagship surface is a
|
|
380
|
+
* single HTML file opened from disk. A `file://` document has an opaque origin,
|
|
381
|
+
* and Chrome refuses `history.pushState`/`replaceState` when the URL argument
|
|
382
|
+
* changes anything but the fragment there. The fragment is the one part every
|
|
383
|
+
* browser lets us rewrite in place.
|
|
384
|
+
*
|
|
385
|
+
* Format: `#<anchor>?<params>` — the anchor half stays exactly what it has
|
|
386
|
+
* always been, a scenario id deep link, so existing permalinks keep working and
|
|
387
|
+
* a link with no filters is unchanged.
|
|
388
|
+
*/
|
|
389
|
+
type UrlStatusFilter = "all" | "passed" | "failed" | "skipped" | "pending";
|
|
390
|
+
|
|
375
391
|
/**
|
|
376
392
|
* Pure functions for filtering a StoryReport by a search query.
|
|
377
393
|
* Used by <ReportInteractive>. Kept side-effect-free so it can be unit-tested.
|
|
378
394
|
*/
|
|
379
395
|
|
|
380
|
-
|
|
396
|
+
/** Defined with the URL codec that has to validate it, so the two can't drift. */
|
|
397
|
+
type StatusFilter = UrlStatusFilter;
|
|
381
398
|
interface FilterCriteria {
|
|
382
399
|
query?: string;
|
|
383
400
|
status?: StatusFilter;
|
|
@@ -443,8 +460,13 @@ interface KeyboardShortcutHandlers {
|
|
|
443
460
|
declare function useKeyboardShortcuts(handlers: KeyboardShortcutHandlers): void;
|
|
444
461
|
|
|
445
462
|
/**
|
|
446
|
-
* Scrolls to a scenario by URL hash on mount and whenever the
|
|
447
|
-
* No-op in SSR (only runs in useEffect). Honours
|
|
463
|
+
* Scrolls to a scenario by URL hash on mount and whenever the deep link
|
|
464
|
+
* changes. No-op in SSR (only runs in useEffect). Honours
|
|
465
|
+
* `prefers-reduced-motion`.
|
|
466
|
+
*
|
|
467
|
+
* The fragment also carries filter state (`#<anchor>?q=...`), so only the
|
|
468
|
+
* anchor half is read — and a hashchange that merely rewrote the filters must
|
|
469
|
+
* not yank the page back to the last anchor, hence the last-anchor guard.
|
|
448
470
|
*/
|
|
449
471
|
declare function useDeepLinkScroll(): void;
|
|
450
472
|
|
package/dist/interactive.d.ts
CHANGED
|
@@ -372,12 +372,29 @@ interface ReportSearchProps {
|
|
|
372
372
|
}
|
|
373
373
|
declare const ReportSearch: react.ForwardRefExoticComponent<ReportSearchProps & react.RefAttributes<HTMLInputElement>>;
|
|
374
374
|
|
|
375
|
+
/**
|
|
376
|
+
* View state in the URL, so a filtered report survives a refresh and can be
|
|
377
|
+
* shared as a link.
|
|
378
|
+
*
|
|
379
|
+
* It lives in the HASH, not the query string, because the flagship surface is a
|
|
380
|
+
* single HTML file opened from disk. A `file://` document has an opaque origin,
|
|
381
|
+
* and Chrome refuses `history.pushState`/`replaceState` when the URL argument
|
|
382
|
+
* changes anything but the fragment there. The fragment is the one part every
|
|
383
|
+
* browser lets us rewrite in place.
|
|
384
|
+
*
|
|
385
|
+
* Format: `#<anchor>?<params>` — the anchor half stays exactly what it has
|
|
386
|
+
* always been, a scenario id deep link, so existing permalinks keep working and
|
|
387
|
+
* a link with no filters is unchanged.
|
|
388
|
+
*/
|
|
389
|
+
type UrlStatusFilter = "all" | "passed" | "failed" | "skipped" | "pending";
|
|
390
|
+
|
|
375
391
|
/**
|
|
376
392
|
* Pure functions for filtering a StoryReport by a search query.
|
|
377
393
|
* Used by <ReportInteractive>. Kept side-effect-free so it can be unit-tested.
|
|
378
394
|
*/
|
|
379
395
|
|
|
380
|
-
|
|
396
|
+
/** Defined with the URL codec that has to validate it, so the two can't drift. */
|
|
397
|
+
type StatusFilter = UrlStatusFilter;
|
|
381
398
|
interface FilterCriteria {
|
|
382
399
|
query?: string;
|
|
383
400
|
status?: StatusFilter;
|
|
@@ -443,8 +460,13 @@ interface KeyboardShortcutHandlers {
|
|
|
443
460
|
declare function useKeyboardShortcuts(handlers: KeyboardShortcutHandlers): void;
|
|
444
461
|
|
|
445
462
|
/**
|
|
446
|
-
* Scrolls to a scenario by URL hash on mount and whenever the
|
|
447
|
-
* No-op in SSR (only runs in useEffect). Honours
|
|
463
|
+
* Scrolls to a scenario by URL hash on mount and whenever the deep link
|
|
464
|
+
* changes. No-op in SSR (only runs in useEffect). Honours
|
|
465
|
+
* `prefers-reduced-motion`.
|
|
466
|
+
*
|
|
467
|
+
* The fragment also carries filter state (`#<anchor>?q=...`), so only the
|
|
468
|
+
* anchor half is read — and a hashchange that merely rewrote the filters must
|
|
469
|
+
* not yank the page back to the last anchor, hence the last-anchor guard.
|
|
448
470
|
*/
|
|
449
471
|
declare function useDeepLinkScroll(): void;
|
|
450
472
|
|
package/dist/interactive.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/interactive/ReportInteractive.tsx
|
|
4
4
|
import {
|
|
5
|
-
useCallback as
|
|
5
|
+
useCallback as useCallback5,
|
|
6
6
|
useDeferredValue,
|
|
7
7
|
useId as useId2,
|
|
8
8
|
useMemo as useMemo3,
|
|
9
|
-
useRef as
|
|
10
|
-
useState as
|
|
9
|
+
useRef as useRef5,
|
|
10
|
+
useState as useState8
|
|
11
11
|
} from "react";
|
|
12
12
|
|
|
13
13
|
// src/result.ts
|
|
@@ -973,11 +973,11 @@ function flakinessOf(entries) {
|
|
|
973
973
|
if (transitionScore > 0.2 || failureRate > 0.3) return "unstable";
|
|
974
974
|
return "stable";
|
|
975
975
|
}
|
|
976
|
-
function diffSinceLastRun(
|
|
976
|
+
function diffSinceLastRun(history, scenarios) {
|
|
977
977
|
let hasPreviousRun = false;
|
|
978
978
|
const delta = { newlyFailing: [], newlyPassing: [], added: [] };
|
|
979
979
|
for (const scenario of scenarios) {
|
|
980
|
-
const entries =
|
|
980
|
+
const entries = history[scenario.id];
|
|
981
981
|
if (!entries || entries.length === 0) continue;
|
|
982
982
|
const last = entries[entries.length - 1];
|
|
983
983
|
const prev = entries[entries.length - 2];
|
|
@@ -1007,8 +1007,8 @@ function dotTitle(e) {
|
|
|
1007
1007
|
return parts.join(" \xB7 ");
|
|
1008
1008
|
}
|
|
1009
1009
|
function ScenarioRunHistory({ scenarioId }) {
|
|
1010
|
-
const
|
|
1011
|
-
const entries =
|
|
1010
|
+
const history = useScenarioHistory();
|
|
1011
|
+
const entries = history?.[scenarioId];
|
|
1012
1012
|
if (!entries || entries.length < 2) return null;
|
|
1013
1013
|
const recent = entries.slice(-MAX_DOTS);
|
|
1014
1014
|
const flaky = flakinessOf(recent) === "flaky";
|
|
@@ -1975,6 +1975,54 @@ var ReportSearch = forwardRef(
|
|
|
1975
1975
|
// src/interactive/ReportFailureBanner.tsx
|
|
1976
1976
|
import { useCallback as useCallback2 } from "react";
|
|
1977
1977
|
|
|
1978
|
+
// src/lib/hash-state.ts
|
|
1979
|
+
var DEFAULT_URL_STATE = {
|
|
1980
|
+
query: "",
|
|
1981
|
+
status: "all",
|
|
1982
|
+
tags: [],
|
|
1983
|
+
detail: "full"
|
|
1984
|
+
};
|
|
1985
|
+
var STATUSES = ["all", "passed", "failed", "skipped", "pending"];
|
|
1986
|
+
function splitHash(hash) {
|
|
1987
|
+
const raw = hash.replace(/^#/, "");
|
|
1988
|
+
const split = raw.indexOf("?");
|
|
1989
|
+
if (split === -1) return { anchor: raw, params: new URLSearchParams() };
|
|
1990
|
+
return { anchor: raw.slice(0, split), params: new URLSearchParams(raw.slice(split + 1)) };
|
|
1991
|
+
}
|
|
1992
|
+
function decodeUrlState(params) {
|
|
1993
|
+
const status = params.get("status");
|
|
1994
|
+
const tags = params.get("tags");
|
|
1995
|
+
return {
|
|
1996
|
+
query: params.get("q") ?? DEFAULT_URL_STATE.query,
|
|
1997
|
+
status: STATUSES.includes(status) ? status : DEFAULT_URL_STATE.status,
|
|
1998
|
+
tags: tags ? tags.split(",").filter(Boolean) : [],
|
|
1999
|
+
detail: params.get("docs") === "0" ? "minimal" : "full"
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
function encodeUrlState(state) {
|
|
2003
|
+
const params = new URLSearchParams();
|
|
2004
|
+
if (state.query) params.set("q", state.query);
|
|
2005
|
+
if (state.status !== "all") params.set("status", state.status);
|
|
2006
|
+
if (state.tags.length > 0) params.set("tags", state.tags.join(","));
|
|
2007
|
+
if (state.detail === "minimal") params.set("docs", "0");
|
|
2008
|
+
return params.toString();
|
|
2009
|
+
}
|
|
2010
|
+
function formatHash(anchor, params) {
|
|
2011
|
+
if (params) return `#${anchor}?${params}`;
|
|
2012
|
+
return anchor ? `#${anchor}` : "";
|
|
2013
|
+
}
|
|
2014
|
+
function writeHash(anchor, params) {
|
|
2015
|
+
if (typeof window === "undefined") return;
|
|
2016
|
+
const next = formatHash(anchor, params);
|
|
2017
|
+
if (next === window.location.hash) return;
|
|
2018
|
+
const url = next || window.location.href.split("#")[0];
|
|
2019
|
+
try {
|
|
2020
|
+
window.history.replaceState(window.history.state, "", url);
|
|
2021
|
+
} catch {
|
|
2022
|
+
window.location.hash = next.slice(1);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
|
|
1978
2026
|
// src/lib/scroll.ts
|
|
1979
2027
|
function prefersReducedMotion() {
|
|
1980
2028
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
@@ -1990,8 +2038,8 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
1990
2038
|
behavior: prefersReducedMotion() ? "auto" : "smooth",
|
|
1991
2039
|
block: "start"
|
|
1992
2040
|
});
|
|
1993
|
-
if (options.updateHash !== false
|
|
1994
|
-
|
|
2041
|
+
if (options.updateHash !== false) {
|
|
2042
|
+
writeHash(scenarioId, splitHash(window.location.hash).params.toString());
|
|
1995
2043
|
}
|
|
1996
2044
|
}
|
|
1997
2045
|
|
|
@@ -2093,12 +2141,12 @@ function ScenarioLinks({ scenarios, tone }) {
|
|
|
2093
2141
|
] });
|
|
2094
2142
|
}
|
|
2095
2143
|
function ReportLastRunDelta({
|
|
2096
|
-
history
|
|
2144
|
+
history,
|
|
2097
2145
|
report
|
|
2098
2146
|
}) {
|
|
2099
|
-
if (!
|
|
2147
|
+
if (!history) return null;
|
|
2100
2148
|
const scenarios = report.features.flatMap((f) => f.scenarios.map((s) => ({ id: s.id, title: s.title })));
|
|
2101
|
-
const delta = diffSinceLastRun(
|
|
2149
|
+
const delta = diffSinceLastRun(history, scenarios);
|
|
2102
2150
|
if (!delta) return null;
|
|
2103
2151
|
const { newlyFailing, newlyPassing, added } = delta;
|
|
2104
2152
|
if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
|
|
@@ -2282,14 +2330,16 @@ function useKeyboardShortcuts(handlers) {
|
|
|
2282
2330
|
}
|
|
2283
2331
|
|
|
2284
2332
|
// src/interactive/use-deep-link-scroll.ts
|
|
2285
|
-
import { useEffect as useEffect4 } from "react";
|
|
2333
|
+
import { useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
2286
2334
|
function useDeepLinkScroll() {
|
|
2335
|
+
const lastAnchor = useRef3(null);
|
|
2287
2336
|
useEffect4(() => {
|
|
2288
2337
|
function scrollToHash() {
|
|
2289
|
-
const
|
|
2290
|
-
if (!
|
|
2338
|
+
const { anchor } = splitHash(window.location.hash);
|
|
2339
|
+
if (!anchor || anchor === lastAnchor.current) return;
|
|
2340
|
+
lastAnchor.current = anchor;
|
|
2291
2341
|
requestAnimationFrame(() => {
|
|
2292
|
-
scrollToScenarioId(
|
|
2342
|
+
scrollToScenarioId(anchor, { updateHash: false });
|
|
2293
2343
|
});
|
|
2294
2344
|
}
|
|
2295
2345
|
scrollToHash();
|
|
@@ -2655,6 +2705,34 @@ function useTheme() {
|
|
|
2655
2705
|
return { pref, setPref };
|
|
2656
2706
|
}
|
|
2657
2707
|
|
|
2708
|
+
// src/interactive/use-url-state.ts
|
|
2709
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef4, useState as useState7 } from "react";
|
|
2710
|
+
var WRITE_DELAY_MS = 250;
|
|
2711
|
+
function useUrlState() {
|
|
2712
|
+
const [state, setState] = useState7(DEFAULT_URL_STATE);
|
|
2713
|
+
const ready = useRef4(false);
|
|
2714
|
+
useEffect7(() => {
|
|
2715
|
+
const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
|
|
2716
|
+
read();
|
|
2717
|
+
ready.current = true;
|
|
2718
|
+
window.addEventListener("hashchange", read);
|
|
2719
|
+
return () => window.removeEventListener("hashchange", read);
|
|
2720
|
+
}, []);
|
|
2721
|
+
useEffect7(() => {
|
|
2722
|
+
if (!ready.current) return;
|
|
2723
|
+
const timer = setTimeout(
|
|
2724
|
+
() => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
|
|
2725
|
+
WRITE_DELAY_MS
|
|
2726
|
+
);
|
|
2727
|
+
return () => clearTimeout(timer);
|
|
2728
|
+
}, [state]);
|
|
2729
|
+
const update = useCallback4(
|
|
2730
|
+
(patch) => setState((prev) => ({ ...prev, ...patch })),
|
|
2731
|
+
[]
|
|
2732
|
+
);
|
|
2733
|
+
return [state, update];
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2658
2736
|
// src/interactive/ReportInteractive.tsx
|
|
2659
2737
|
import { Fragment as Fragment13, jsx as jsx46, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2660
2738
|
function ReportInteractive(props) {
|
|
@@ -2697,13 +2775,20 @@ function ReportInteractiveView({
|
|
|
2697
2775
|
staleAfterDays = 7,
|
|
2698
2776
|
scenarioHistory
|
|
2699
2777
|
}) {
|
|
2700
|
-
const [
|
|
2701
|
-
const
|
|
2702
|
-
const
|
|
2703
|
-
const
|
|
2704
|
-
|
|
2778
|
+
const [urlState, setUrlState] = useUrlState();
|
|
2779
|
+
const { query, status: statusFilter, tags: activeTags, detail } = urlState;
|
|
2780
|
+
const setQuery = useCallback5((next) => setUrlState({ query: next }), [setUrlState]);
|
|
2781
|
+
const setStatusFilter = useCallback5(
|
|
2782
|
+
(next) => setUrlState({ status: next }),
|
|
2783
|
+
[setUrlState]
|
|
2784
|
+
);
|
|
2785
|
+
const setDetail = useCallback5(
|
|
2786
|
+
(next) => setUrlState({ detail: next }),
|
|
2787
|
+
[setUrlState]
|
|
2788
|
+
);
|
|
2789
|
+
const [helpOpen, setHelpOpen] = useState8(false);
|
|
2705
2790
|
const { pref: themePref, setPref: setThemePref } = useTheme();
|
|
2706
|
-
const searchRef =
|
|
2791
|
+
const searchRef = useRef5(null);
|
|
2707
2792
|
const expandId = useId2();
|
|
2708
2793
|
const docsId = useId2();
|
|
2709
2794
|
const deferredQuery = useDeferredValue(query);
|
|
@@ -2727,18 +2812,20 @@ function ReportInteractiveView({
|
|
|
2727
2812
|
return opts;
|
|
2728
2813
|
}, [report]);
|
|
2729
2814
|
const tagOptions = useMemo3(() => allTags(report), [report]);
|
|
2730
|
-
const toggleTag =
|
|
2731
|
-
(tag) =>
|
|
2732
|
-
|
|
2815
|
+
const toggleTag = useCallback5(
|
|
2816
|
+
(tag) => setUrlState({
|
|
2817
|
+
tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
|
|
2818
|
+
}),
|
|
2819
|
+
[activeTags, setUrlState]
|
|
2733
2820
|
);
|
|
2734
|
-
const [toast, setToast] =
|
|
2735
|
-
const toastTimer =
|
|
2736
|
-
const showToast =
|
|
2821
|
+
const [toast, setToast] = useState8(null);
|
|
2822
|
+
const toastTimer = useRef5(null);
|
|
2823
|
+
const showToast = useCallback5((msg) => {
|
|
2737
2824
|
setToast(msg);
|
|
2738
2825
|
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
2739
2826
|
toastTimer.current = setTimeout(() => setToast(null), 1800);
|
|
2740
2827
|
}, []);
|
|
2741
|
-
const copy =
|
|
2828
|
+
const copy = useCallback5(
|
|
2742
2829
|
(text, msg) => {
|
|
2743
2830
|
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
2744
2831
|
navigator.clipboard.writeText(text).then(() => showToast(msg), () => showToast("Copy failed"));
|
|
@@ -2754,15 +2841,15 @@ function ReportInteractiveView({
|
|
|
2754
2841
|
}),
|
|
2755
2842
|
[copy]
|
|
2756
2843
|
);
|
|
2757
|
-
const failureIndexRef =
|
|
2844
|
+
const failureIndexRef = useRef5(0);
|
|
2758
2845
|
const allCollapsibleIds = useMemo3(
|
|
2759
2846
|
() => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
|
|
2760
2847
|
[report]
|
|
2761
2848
|
);
|
|
2762
|
-
const collapse = useCollapseState(
|
|
2763
|
-
const collapseAll =
|
|
2764
|
-
const [expandedAll, setExpandedAll] =
|
|
2765
|
-
const setExpanded =
|
|
2849
|
+
const collapse = useCollapseState(useCallback5(() => defaultCollapsedIds(report), [report]));
|
|
2850
|
+
const collapseAll = useCallback5(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
|
|
2851
|
+
const [expandedAll, setExpandedAll] = useState8(() => report.summary.failed === 0);
|
|
2852
|
+
const setExpanded = useCallback5(
|
|
2766
2853
|
(expanded) => {
|
|
2767
2854
|
if (expanded) collapse.expandAll();
|
|
2768
2855
|
else collapseAll();
|
|
@@ -2770,10 +2857,10 @@ function ReportInteractiveView({
|
|
|
2770
2857
|
},
|
|
2771
2858
|
[collapse, collapseAll]
|
|
2772
2859
|
);
|
|
2773
|
-
const focusSearch =
|
|
2860
|
+
const focusSearch = useCallback5(() => {
|
|
2774
2861
|
searchRef.current?.focus();
|
|
2775
2862
|
}, []);
|
|
2776
|
-
const stepFailure =
|
|
2863
|
+
const stepFailure = useCallback5(
|
|
2777
2864
|
(direction) => {
|
|
2778
2865
|
if (failures.length === 0) return;
|
|
2779
2866
|
failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
|
|
@@ -2782,10 +2869,10 @@ function ReportInteractiveView({
|
|
|
2782
2869
|
},
|
|
2783
2870
|
[failures]
|
|
2784
2871
|
);
|
|
2785
|
-
const toggleHelp =
|
|
2872
|
+
const toggleHelp = useCallback5(() => {
|
|
2786
2873
|
setHelpOpen((v) => !v);
|
|
2787
2874
|
}, []);
|
|
2788
|
-
const escape =
|
|
2875
|
+
const escape = useCallback5(() => {
|
|
2789
2876
|
if (helpOpen) setHelpOpen(false);
|
|
2790
2877
|
else if (query !== "") setQuery("");
|
|
2791
2878
|
}, [helpOpen, query]);
|
|
@@ -2925,11 +3012,11 @@ function ReportInteractiveView({
|
|
|
2925
3012
|
import { useMemo as useMemo4 } from "react";
|
|
2926
3013
|
|
|
2927
3014
|
// src/interactive/island-renderers.tsx
|
|
2928
|
-
import { createElement, useEffect as
|
|
3015
|
+
import { createElement, useEffect as useEffect9, useState as useState10 } from "react";
|
|
2929
3016
|
|
|
2930
3017
|
// src/components/doc/MermaidView.tsx
|
|
2931
|
-
import { useEffect as
|
|
2932
|
-
import { jsx as jsx47, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3018
|
+
import { useEffect as useEffect8, useRef as useRef6, useState as useState9 } from "react";
|
|
3019
|
+
import { Fragment as Fragment14, jsx as jsx47, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2933
3020
|
function prefersDark(el) {
|
|
2934
3021
|
const themed = el?.closest("[data-theme]");
|
|
2935
3022
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -2937,23 +3024,40 @@ function prefersDark(el) {
|
|
|
2937
3024
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
2938
3025
|
}
|
|
2939
3026
|
function MermaidView({ entry, load }) {
|
|
2940
|
-
const ref =
|
|
2941
|
-
const [svg, setSvg] =
|
|
2942
|
-
|
|
3027
|
+
const ref = useRef6(null);
|
|
3028
|
+
const [svg, setSvg] = useState9(null);
|
|
3029
|
+
const [error, setError] = useState9(null);
|
|
3030
|
+
useEffect8(() => {
|
|
2943
3031
|
let cancelled = false;
|
|
2944
3032
|
const draw = async () => {
|
|
3033
|
+
let mermaid;
|
|
3034
|
+
try {
|
|
3035
|
+
mermaid = await load();
|
|
3036
|
+
} catch {
|
|
3037
|
+
if (!cancelled) {
|
|
3038
|
+
setSvg(null);
|
|
3039
|
+
setError(null);
|
|
3040
|
+
}
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
2945
3043
|
try {
|
|
2946
|
-
const mermaid = await load();
|
|
2947
3044
|
mermaid.initialize({
|
|
2948
3045
|
startOnLoad: false,
|
|
2949
3046
|
securityLevel: "strict",
|
|
2950
3047
|
theme: prefersDark(ref.current) ? "dark" : "default"
|
|
2951
3048
|
});
|
|
3049
|
+
await mermaid.parse(entry.code);
|
|
2952
3050
|
const id = `es-mermaid-${Math.random().toString(36).slice(2)}`;
|
|
2953
3051
|
const { svg: rendered } = await mermaid.render(id, entry.code);
|
|
2954
|
-
if (!cancelled)
|
|
2955
|
-
|
|
2956
|
-
|
|
3052
|
+
if (!cancelled) {
|
|
3053
|
+
setSvg(rendered);
|
|
3054
|
+
setError(null);
|
|
3055
|
+
}
|
|
3056
|
+
} catch (err) {
|
|
3057
|
+
if (!cancelled) {
|
|
3058
|
+
setSvg(null);
|
|
3059
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
3060
|
+
}
|
|
2957
3061
|
}
|
|
2958
3062
|
};
|
|
2959
3063
|
void draw();
|
|
@@ -2979,7 +3083,21 @@ function MermaidView({ entry, load }) {
|
|
|
2979
3083
|
)
|
|
2980
3084
|
] });
|
|
2981
3085
|
}
|
|
2982
|
-
return /* @__PURE__ */
|
|
3086
|
+
return /* @__PURE__ */ jsxs31(Fragment14, { children: [
|
|
3087
|
+
error ? /* @__PURE__ */ jsxs31(
|
|
3088
|
+
"p",
|
|
3089
|
+
{
|
|
3090
|
+
role: "alert",
|
|
3091
|
+
"data-es-mermaid-error": true,
|
|
3092
|
+
className: "mt-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
|
|
3093
|
+
children: [
|
|
3094
|
+
"Diagram failed to render: ",
|
|
3095
|
+
error
|
|
3096
|
+
]
|
|
3097
|
+
}
|
|
3098
|
+
) : null,
|
|
3099
|
+
/* @__PURE__ */ jsx47(MermaidSource, { entry, ref })
|
|
3100
|
+
] });
|
|
2983
3101
|
}
|
|
2984
3102
|
|
|
2985
3103
|
// src/interactive/island-renderers.tsx
|
|
@@ -3000,8 +3118,8 @@ function cachedCdnModule(url) {
|
|
|
3000
3118
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
3001
3119
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
3002
3120
|
function CdnHighlightedCode({ entry }) {
|
|
3003
|
-
const [html, setHtml] =
|
|
3004
|
-
|
|
3121
|
+
const [html, setHtml] = useState10(null);
|
|
3122
|
+
useEffect9(() => {
|
|
3005
3123
|
let cancelled = false;
|
|
3006
3124
|
void (async () => {
|
|
3007
3125
|
try {
|