@kamod-ch/preactpress 1.0.2 → 2.0.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.
Files changed (42) hide show
  1. package/dist/client/app.d.ts.map +1 -1
  2. package/dist/client/app.js +54 -7
  3. package/dist/client/app.js.map +1 -1
  4. package/dist/client/loadPage.d.ts +1 -0
  5. package/dist/client/loadPage.d.ts.map +1 -1
  6. package/dist/client/loadPage.js +3 -0
  7. package/dist/client/loadPage.js.map +1 -1
  8. package/dist/client/mermaid.d.ts +2 -0
  9. package/dist/client/mermaid.d.ts.map +1 -0
  10. package/dist/client/mermaid.js +68 -0
  11. package/dist/client/mermaid.js.map +1 -0
  12. package/dist/client/theme-default/Layout.d.ts.map +1 -1
  13. package/dist/client/theme-default/Layout.js +43 -0
  14. package/dist/client/theme-default/Layout.js.map +1 -1
  15. package/dist/node/markdown.d.ts.map +1 -1
  16. package/dist/node/markdown.js +24 -6
  17. package/dist/node/markdown.js.map +1 -1
  18. package/dist/shared/scrollRestoration.d.ts +11 -0
  19. package/dist/shared/scrollRestoration.d.ts.map +1 -0
  20. package/dist/shared/scrollRestoration.js +88 -0
  21. package/dist/shared/scrollRestoration.js.map +1 -0
  22. package/dist/shared/search.d.ts.map +1 -1
  23. package/dist/shared/search.js +4 -1
  24. package/dist/shared/search.js.map +1 -1
  25. package/package.json +2 -1
  26. package/src/client/app.tsx +59 -7
  27. package/src/client/loadPage.ts +4 -0
  28. package/src/client/mermaid.ts +74 -0
  29. package/src/client/theme-default/Layout.tsx +43 -0
  30. package/src/client/theme-default/styles.css +181 -5
  31. package/src/shared/scrollRestoration.ts +101 -0
  32. package/src/shared/search.ts +5 -1
  33. package/templates/docs/.preactpress/config.ts +5 -0
  34. package/templates/docs/examples/cloudflare-pages.md +117 -0
  35. package/templates/docs/examples/custom-theme.md +266 -0
  36. package/templates/docs/examples/mermaid.md +53 -0
  37. package/templates/docs/examples/preact-signals.md +102 -0
  38. package/templates/docs/examples/rss.md +104 -0
  39. package/templates/docs/guide/custom-themes.md +1 -1
  40. package/templates/docs/guide/deploy.md +2 -0
  41. package/templates/docs/guide/markdown-and-mdx.md +1 -1
  42. package/templates/docs/markdown-examples.md +10 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrollRestoration.d.ts","sourceRoot":"","sources":["../../src/shared/scrollRestoration.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAI5C,wBAAgB,sBAAsB,IAAI,MAAM,IAAI,CAgBnD;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAM5C;AAED,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAItD;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAE5D;AAuCD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,wBAAgB,8BAA8B,IAAI,OAAO,CAMxD"}
@@ -0,0 +1,88 @@
1
+ export const SCROLL_STATE_KEY = "ppScrollY";
2
+ let skipScrollRestoreOnce = false;
3
+ export function setupScrollRestoration() {
4
+ if (typeof window === "undefined")
5
+ return () => { };
6
+ if ("scrollRestoration" in window.history) {
7
+ window.history.scrollRestoration = "manual";
8
+ }
9
+ const onScroll = () => {
10
+ persistScrollPosition();
11
+ };
12
+ window.addEventListener("scroll", onScroll, { passive: true });
13
+ window.addEventListener("pagehide", persistScrollPosition);
14
+ return () => {
15
+ window.removeEventListener("scroll", onScroll);
16
+ window.removeEventListener("pagehide", persistScrollPosition);
17
+ };
18
+ }
19
+ export function persistScrollPosition() {
20
+ if (typeof window === "undefined")
21
+ return;
22
+ const scrollY = window.scrollY;
23
+ const state = window.history.state ?? {};
24
+ if (state[SCROLL_STATE_KEY] === scrollY)
25
+ return;
26
+ window.history.replaceState({ ...state, [SCROLL_STATE_KEY]: scrollY }, "");
27
+ }
28
+ export function saveScrollPositionBeforeNavigation() {
29
+ persistScrollPosition();
30
+ }
31
+ export function readScrollPositionFromHistory() {
32
+ if (typeof window === "undefined")
33
+ return 0;
34
+ const scrollY = window.history.state?.[SCROLL_STATE_KEY];
35
+ return typeof scrollY === "number" && Number.isFinite(scrollY) ? scrollY : 0;
36
+ }
37
+ export function restoreScrollPosition() {
38
+ if (typeof window === "undefined")
39
+ return;
40
+ window.scrollTo({ top: readScrollPositionFromHistory(), left: 0, behavior: "auto" });
41
+ }
42
+ export async function restoreScrollPositionAfterLayout() {
43
+ await scrollAfterLayout(readScrollPositionFromHistory());
44
+ }
45
+ export async function scrollToTopAfterLayout() {
46
+ await scrollAfterLayout(0);
47
+ }
48
+ async function scrollAfterLayout(top) {
49
+ if (typeof window === "undefined")
50
+ return;
51
+ await nextAnimationFrame();
52
+ await nextAnimationFrame();
53
+ await waitForImagesToSettle();
54
+ await nextAnimationFrame();
55
+ const html = document.documentElement;
56
+ const previous = html.style.scrollBehavior;
57
+ html.style.scrollBehavior = "auto";
58
+ window.scrollTo({ top, left: 0, behavior: "auto" });
59
+ html.style.scrollBehavior = previous;
60
+ }
61
+ function nextAnimationFrame() {
62
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
63
+ }
64
+ async function waitForImagesToSettle(timeoutMs = 1500) {
65
+ if (typeof document === "undefined")
66
+ return;
67
+ const images = Array.from(document.images).filter((image) => !image.complete);
68
+ if (images.length === 0)
69
+ return;
70
+ await Promise.race([
71
+ Promise.allSettled(images.map((image) => new Promise((resolve) => {
72
+ image.addEventListener("load", () => resolve(), { once: true });
73
+ image.addEventListener("error", () => resolve(), { once: true });
74
+ }))),
75
+ new Promise((resolve) => setTimeout(resolve, timeoutMs)),
76
+ ]);
77
+ }
78
+ export function skipNextScrollRestore() {
79
+ skipScrollRestoreOnce = true;
80
+ }
81
+ export function consumeScrollRestoreOnPopstate() {
82
+ if (skipScrollRestoreOnce) {
83
+ skipScrollRestoreOnce = false;
84
+ return false;
85
+ }
86
+ return true;
87
+ }
88
+ //# sourceMappingURL=scrollRestoration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrollRestoration.js","sourceRoot":"","sources":["../../src/shared/scrollRestoration.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE5C,IAAI,qBAAqB,GAAG,KAAK,CAAC;AAElC,MAAM,UAAU,sBAAsB;IACpC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAEnD,IAAI,mBAAmB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC1C,MAAM,CAAC,OAAO,CAAC,iBAAiB,GAAG,QAAQ,CAAC;IAC9C,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,qBAAqB,EAAE,CAAC;IAC1B,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;IAC3D,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/C,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;IAChE,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IACzC,IAAI,KAAK,CAAC,gBAAgB,CAAC,KAAK,OAAO;QAAE,OAAO;IAChD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,kCAAkC;IAChD,qBAAqB,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,gBAAgB,CAAC,CAAC;IACzD,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,6BAA6B,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gCAAgC;IACpD,MAAM,iBAAiB,CAAC,6BAA6B,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC1C,MAAM,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,GAAW;IAC1C,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,qBAAqB,EAAE,CAAC;IAC9B,MAAM,kBAAkB,EAAE,CAAC;IAE3B,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC;IAC3C,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,MAAM,CAAC;IACnC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,QAAQ,CAAC;AACvC,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,SAAS,GAAG,IAAI;IACnD,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEhC,MAAM,OAAO,CAAC,IAAI,CAAC;QACjB,OAAO,CAAC,UAAU,CAChB,MAAM,CAAC,GAAG,CACR,CAAC,KAAK,EAAE,EAAE,CACR,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC5B,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAChE,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC,CAAC,CACL,CACF;QACD,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;KAC/D,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,qBAAqB,GAAG,IAAI,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC5C,IAAI,qBAAqB,EAAE,CAAC;QAC1B,qBAAqB,GAAG,KAAK,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/shared/search.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,oBAAoB,CAAC;CAC/B;AAED,oFAAoF;AACpF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAE7E,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,cAAc,GAAG,KAAK,CAInF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAEnE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAErE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,oBAAoB,GAAG,SAAS,CAGhG;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,oBAAoB,CAQtB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,oBAAoB,GAAG;IACzE,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAUA;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,UAAO,GAAG,MAAM,CAS3F;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAc3E"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/shared/search.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,oBAAoB,CAAC;CAC/B;AAED,oFAAoF;AACpF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAE7E,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,cAAc,GAAG,KAAK,CAInF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAEnE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAErE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,oBAAoB,GAAG,SAAS,CAGhG;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,oBAAoB,CAQtB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,oBAAoB,GAAG;IACzE,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAUA;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,UAAO,GAAG,MAAM,CAS3F;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAgB3E"}
@@ -1,3 +1,4 @@
1
+ import { saveScrollPositionBeforeNavigation, skipNextScrollRestore } from "./scrollRestoration.js";
1
2
  export function resolveSearchProvider(search) {
2
3
  if (search === true)
3
4
  return "local";
@@ -56,8 +57,10 @@ export function navigateDocSearchResult(itemUrl, base) {
56
57
  window.location.assign(href);
57
58
  return;
58
59
  }
60
+ saveScrollPositionBeforeNavigation();
59
61
  window.history.pushState({}, "", href);
62
+ skipNextScrollRestore();
60
63
  window.dispatchEvent(new PopStateEvent("popstate"));
61
- window.scrollTo({ top: 0 });
64
+ window.scrollTo({ top: 0, behavior: "auto" });
62
65
  }
63
66
  //# sourceMappingURL=search.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/shared/search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/shared/search.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kamod-ch/preactpress",
3
- "version": "1.0.2",
3
+ "version": "2.0.0",
4
4
  "description": "Vite and Preact powered static site generator",
5
5
  "keywords": [
6
6
  "markdown",
@@ -103,6 +103,7 @@
103
103
  "markdown-it": "^14.2.0",
104
104
  "markdown-it-emoji": "^3.0.0",
105
105
  "markdown-it-mathjax3": "^5.2.0",
106
+ "mermaid": "^11.16.0",
106
107
  "minimist": "^1.2.8",
107
108
  "node-html-parser": "^7.1.0",
108
109
  "picocolors": "^1.1.1",
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from "preact/hooks";
1
+ import { useEffect, useRef, useState } from "preact/hooks";
2
2
  import Layout from "virtual:preactpress-layout";
3
3
  import { pagesMeta, routes } from "virtual:preactpress-pages";
4
4
  import { i18n, mpa, site, themeConfig } from "virtual:preactpress-site";
@@ -6,7 +6,15 @@ import type { PageView } from "./types.js";
6
6
  import type { ResolvedLocale } from "../node/siteConfig.js";
7
7
  import { usePageHead } from "./usePageHead.js";
8
8
  import { normalizeRoute, routeFromPathname } from "../shared/route.js";
9
- import { loadPage, prefetchPage, seedPage } from "./loadPage.js";
9
+ import {
10
+ consumeScrollRestoreOnPopstate,
11
+ restoreScrollPositionAfterLayout,
12
+ saveScrollPositionBeforeNavigation,
13
+ scrollToTopAfterLayout,
14
+ setupScrollRestoration,
15
+ } from "../shared/scrollRestoration.js";
16
+ import { getCachedPage, loadPage, prefetchPage, seedPage } from "./loadPage.js";
17
+ import { renderMermaidDiagrams } from "./mermaid.js";
10
18
  import { setupViewportPrefetch } from "./prefetchLinks.js";
11
19
  import {
12
20
  localeFromRoute,
@@ -55,6 +63,10 @@ function loadingPage(route: string): PageView {
55
63
  export function App({ routePath, initialPage }: { routePath: string; initialPage?: PageView }) {
56
64
  const [currentRoute, setCurrentRoute] = useState(() => normalizeRoute(routePath));
57
65
  const [page, setPage] = useState<PageView>(() => initialPage ?? loadingPage(routePath));
66
+ const pendingScrollRestore = useRef(false);
67
+ const pendingScrollTop = useRef(false);
68
+ const [scrollRestoreNonce, setScrollRestoreNonce] = useState(0);
69
+ const [scrollTopNonce, setScrollTopNonce] = useState(0);
58
70
  const availableRoutes = new Set(routes);
59
71
  const activeLocale = localeFromRoute(currentRoute, i18n);
60
72
  const activeSite = siteForRoute(site, currentRoute, i18n);
@@ -66,10 +78,20 @@ export function App({ routePath, initialPage }: { routePath: string; initialPage
66
78
 
67
79
  useEffect(() => {
68
80
  let cancelled = false;
69
- setPage(loadingPage(currentRoute));
81
+ const cached = getCachedPage(currentRoute);
82
+ if (!cached) setPage(loadingPage(currentRoute));
70
83
  void loadPage(currentRoute, site.base)
71
84
  .then((loaded) => {
72
- if (!cancelled) setPage(loaded);
85
+ if (!cancelled) {
86
+ setPage(loaded);
87
+ if (pendingScrollRestore.current) {
88
+ pendingScrollRestore.current = false;
89
+ setScrollRestoreNonce((nonce) => nonce + 1);
90
+ } else if (pendingScrollTop.current) {
91
+ pendingScrollTop.current = false;
92
+ setScrollTopNonce((nonce) => nonce + 1);
93
+ }
94
+ }
73
95
  })
74
96
  .catch(() => {
75
97
  if (!cancelled) {
@@ -81,6 +103,13 @@ export function App({ routePath, initialPage }: { routePath: string; initialPage
81
103
  meta: {},
82
104
  headings: [],
83
105
  });
106
+ if (pendingScrollRestore.current) {
107
+ pendingScrollRestore.current = false;
108
+ setScrollRestoreNonce((nonce) => nonce + 1);
109
+ } else if (pendingScrollTop.current) {
110
+ pendingScrollTop.current = false;
111
+ setScrollTopNonce((nonce) => nonce + 1);
112
+ }
84
113
  }
85
114
  });
86
115
  return () => {
@@ -88,6 +117,16 @@ export function App({ routePath, initialPage }: { routePath: string; initialPage
88
117
  };
89
118
  }, [activeSite.description, currentRoute]);
90
119
 
120
+ useEffect(() => {
121
+ if (scrollRestoreNonce === 0) return;
122
+ void restoreScrollPositionAfterLayout();
123
+ }, [scrollRestoreNonce]);
124
+
125
+ useEffect(() => {
126
+ if (scrollTopNonce === 0) return;
127
+ void scrollToTopAfterLayout();
128
+ }, [scrollTopNonce]);
129
+
91
130
  useEffect(() => {
92
131
  const prefetch = (route: string) => prefetchPage(route, site.base);
93
132
  const stopViewportPrefetch = setupViewportPrefetch(routeFromHref, prefetch);
@@ -95,7 +134,13 @@ export function App({ routePath, initialPage }: { routePath: string; initialPage
95
134
  return () => stopViewportPrefetch();
96
135
  }
97
136
 
98
- const onPopState = () => setCurrentRoute(routeFromLocation());
137
+ const stopScrollRestoration = setupScrollRestoration();
138
+ const onPopState = () => {
139
+ if (consumeScrollRestoreOnPopstate()) {
140
+ pendingScrollRestore.current = true;
141
+ }
142
+ setCurrentRoute(routeFromLocation());
143
+ };
99
144
  const onClick = (event: MouseEvent) => {
100
145
  if (
101
146
  event.defaultPrevented ||
@@ -117,9 +162,11 @@ export function App({ routePath, initialPage }: { routePath: string; initialPage
117
162
  return;
118
163
  }
119
164
  event.preventDefault();
165
+ saveScrollPositionBeforeNavigation();
166
+ pendingScrollTop.current = true;
120
167
  window.history.pushState({}, "", url);
121
168
  setCurrentRoute(route);
122
- window.scrollTo({ top: 0 });
169
+ window.scrollTo({ top: 0, left: 0, behavior: "auto" });
123
170
  };
124
171
  const onMouseEnter = (event: MouseEvent) => {
125
172
  const link = anchorFromEvent(event);
@@ -131,6 +178,7 @@ export function App({ routePath, initialPage }: { routePath: string; initialPage
131
178
  document.addEventListener("click", onClick);
132
179
  document.addEventListener("mouseenter", onMouseEnter, true);
133
180
  return () => {
181
+ stopScrollRestoration();
134
182
  stopViewportPrefetch();
135
183
  window.removeEventListener("popstate", onPopState);
136
184
  document.removeEventListener("click", onClick);
@@ -138,9 +186,13 @@ export function App({ routePath, initialPage }: { routePath: string; initialPage
138
186
  };
139
187
  }, [currentRoute, mpa]);
140
188
 
189
+ useEffect(() => {
190
+ void renderMermaidDiagrams();
191
+ }, [currentRoute, page]);
192
+
141
193
  useEffect(() => {
142
194
  if (currentRoute !== normalizeRoute(routePath)) {
143
- document.getElementById("content")?.focus();
195
+ document.getElementById("content")?.focus({ preventScroll: true });
144
196
  }
145
197
  }, [currentRoute, routePath]);
146
198
 
@@ -23,6 +23,10 @@ export function seedPage(route: string, page: PageView | undefined): void {
23
23
  if (page) cache.set(route, page);
24
24
  }
25
25
 
26
+ export function getCachedPage(route: string): PageView | undefined {
27
+ return cache.get(route);
28
+ }
29
+
26
30
  async function loadMarkdownPage(route: string, base: string): Promise<PageView> {
27
31
  const meta = pagesMeta[route];
28
32
  const url = import.meta.env.DEV
@@ -0,0 +1,74 @@
1
+ let renderRun = 0;
2
+
3
+ function isDarkTheme(): boolean {
4
+ if (typeof document === "undefined" || typeof window === "undefined") return false;
5
+ const explicit = document.documentElement.dataset.theme;
6
+ if (explicit === "dark") return true;
7
+ if (explicit === "light") return false;
8
+ return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
9
+ }
10
+
11
+ function mermaidSourceFromElement(element: HTMLElement): string {
12
+ if (element.dataset.mermaidSource) return element.dataset.mermaidSource;
13
+ return element.textContent ?? "";
14
+ }
15
+
16
+ function normalizeMermaidBlocks(): HTMLElement[] {
17
+ const blocks = Array.from(document.querySelectorAll<HTMLElement>(".pp-mermaid"));
18
+
19
+ for (const code of document.querySelectorAll<HTMLElement>(
20
+ 'pre > code.language-mermaid, pre > code[class*="language-mermaid"]',
21
+ )) {
22
+ const pre = code.parentElement;
23
+ if (!pre || pre.dataset.mermaidNormalized === "true") continue;
24
+
25
+ const block = document.createElement("div");
26
+ block.className = "pp-mermaid";
27
+ block.dataset.mermaidSource = code.textContent ?? "";
28
+ block.textContent = code.textContent ?? "";
29
+ pre.replaceWith(block);
30
+ pre.dataset.mermaidNormalized = "true";
31
+ blocks.push(block);
32
+ }
33
+
34
+ return blocks;
35
+ }
36
+
37
+ export async function renderMermaidDiagrams(): Promise<void> {
38
+ if (typeof document === "undefined") return;
39
+
40
+ const blocks = normalizeMermaidBlocks().filter(
41
+ (block) => block.dataset.mermaidRendered !== "true",
42
+ );
43
+ if (blocks.length === 0) return;
44
+
45
+ const run = ++renderRun;
46
+ const { default: mermaid } = await import("mermaid");
47
+ if (run !== renderRun) return;
48
+
49
+ mermaid.initialize({
50
+ startOnLoad: false,
51
+ securityLevel: "strict",
52
+ theme: isDarkTheme() ? "dark" : "default",
53
+ });
54
+
55
+ await Promise.all(
56
+ blocks.map(async (block, index) => {
57
+ const source = mermaidSourceFromElement(block).trim();
58
+ if (!source) return;
59
+
60
+ try {
61
+ const id = `pp-mermaid-${run}-${index}`;
62
+ const { svg } = await mermaid.render(id, source);
63
+ if (run !== renderRun) return;
64
+ block.innerHTML = svg;
65
+ block.dataset.mermaidRendered = "true";
66
+ block.dataset.mermaidSource = source;
67
+ } catch (error) {
68
+ block.dataset.mermaidError = "true";
69
+ block.textContent = source;
70
+ console.warn("Failed to render Mermaid diagram", error);
71
+ }
72
+ }),
73
+ );
74
+ }
@@ -150,6 +150,49 @@ const Layout: FunctionalComponent<LayoutProps> = ({
150
150
  setMobileMenuOpen(false);
151
151
  }, [routePath]);
152
152
 
153
+ useEffect(() => {
154
+ const copyText = async (text: string) => {
155
+ if (navigator.clipboard?.writeText) {
156
+ await navigator.clipboard.writeText(text);
157
+ return;
158
+ }
159
+ const textarea = document.createElement("textarea");
160
+ textarea.value = text;
161
+ textarea.setAttribute("readonly", "");
162
+ textarea.style.position = "fixed";
163
+ textarea.style.top = "-9999px";
164
+ document.body.append(textarea);
165
+ textarea.select();
166
+ document.execCommand("copy");
167
+ textarea.remove();
168
+ };
169
+
170
+ const onClick = (event: MouseEvent) => {
171
+ const target = event.target;
172
+ const candidate = target instanceof Element ? target.closest(".pp-code-copy") : null;
173
+ if (!(candidate instanceof HTMLButtonElement)) return;
174
+ const button = candidate;
175
+ const code = button.closest(".pp-code-block")?.querySelector("code")?.textContent ?? "";
176
+ if (!code) return;
177
+
178
+ event.preventDefault();
179
+ void copyText(code).then(() => {
180
+ const icon = button.querySelector(".pp-code-copy-icon");
181
+ if (icon && button.dataset.checkIcon) icon.innerHTML = button.dataset.checkIcon;
182
+ button.classList.add("copied");
183
+ button.setAttribute("aria-label", button.dataset.copiedLabel ?? "Copied");
184
+ window.setTimeout(() => {
185
+ if (icon && button.dataset.copyIcon) icon.innerHTML = button.dataset.copyIcon;
186
+ button.classList.remove("copied");
187
+ button.setAttribute("aria-label", "Copy code");
188
+ }, 2000);
189
+ });
190
+ };
191
+
192
+ document.addEventListener("click", onClick);
193
+ return () => document.removeEventListener("click", onClick);
194
+ }, []);
195
+
153
196
  useEffect(() => {
154
197
  if (!mobileMenuOpen) return;
155
198
 
@@ -759,14 +759,188 @@ body::selection {
759
759
  margin: 16px 0;
760
760
  }
761
761
 
762
+ .pp-doc-content .pp-mermaid {
763
+ margin: 1.25rem 0;
764
+ padding: 1.25rem;
765
+ overflow: auto;
766
+ border: 1px solid color-mix(in srgb, var(--pp-border) 82%, transparent);
767
+ border-radius: 14px;
768
+ background: var(--pp-code-bg);
769
+ color: var(--pp-code-text);
770
+ text-align: center;
771
+ }
772
+
773
+ .pp-doc-content .pp-mermaid[data-mermaid-error="true"] {
774
+ text-align: left;
775
+ white-space: pre;
776
+ }
777
+
778
+ .pp-doc-content .pp-mermaid svg {
779
+ max-width: 100%;
780
+ height: auto;
781
+ }
782
+
783
+ .pp-doc-content .pp-code-block {
784
+ position: relative;
785
+ margin: 1.25rem 0;
786
+ border: 1px solid color-mix(in srgb, var(--pp-border) 82%, transparent);
787
+ border-radius: 14px;
788
+ background: var(--pp-code-bg);
789
+ box-shadow:
790
+ 0 14px 34px color-mix(in srgb, #000 8%, transparent),
791
+ inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);
792
+ overflow: hidden;
793
+ }
794
+
762
795
  .pp-doc-content pre {
763
- padding: 16px;
764
- border-radius: 8px;
796
+ padding: 16px 18px;
797
+ border-radius: 0;
765
798
  overflow: auto;
766
- border: 1px solid var(--pp-border);
799
+ border: 0;
767
800
  background: var(--pp-code-bg);
768
801
  color: var(--pp-code-text);
769
- line-height: 1.55;
802
+ line-height: 1.38;
803
+ }
804
+
805
+ .pp-doc-content .pp-code-block pre {
806
+ margin: 0;
807
+ max-height: min(68vh, 720px);
808
+ padding-top: 2.7rem;
809
+ padding-right: 3.75rem;
810
+ line-height: 0;
811
+ scrollbar-width: thin;
812
+ scrollbar-color: color-mix(in srgb, var(--pp-muted) 40%, transparent) transparent;
813
+ }
814
+
815
+ .pp-doc-content .pp-code-block .shiki.shiki-themes.vitesse-light.vitesse-dark {
816
+ padding-bottom: 1rem;
817
+ }
818
+
819
+ .pp-doc-content .pp-code-block pre code {
820
+ display: block;
821
+ min-width: max-content;
822
+ font-size: 0.9rem;
823
+ line-height: 1.32;
824
+ tab-size: 2;
825
+ }
826
+
827
+ .pp-doc-content .pp-code-block .line {
828
+ display: block;
829
+ line-height: 0;
830
+ counter-increment: pp-code-line;
831
+ }
832
+
833
+ .pp-doc-content .pp-code-block code {
834
+ counter-reset: pp-code-line;
835
+ }
836
+
837
+ .pp-doc-content .pp-code-block .line::before {
838
+ content: counter(pp-code-line);
839
+ display: inline-block;
840
+ width: 2rem;
841
+ margin-right: 1rem;
842
+ color: color-mix(in srgb, var(--pp-muted) 42%, transparent);
843
+ font-variant-numeric: tabular-nums;
844
+ text-align: right;
845
+ user-select: none;
846
+ }
847
+
848
+ .pp-doc-content .pp-code-block[data-language="plaintext"] pre {
849
+ padding-top: 18px;
850
+ }
851
+
852
+ .pp-doc-content .pp-code-lang {
853
+ position: absolute;
854
+ top: 12px;
855
+ left: 14px;
856
+ z-index: 2;
857
+ max-width: calc(100% - 4.75rem);
858
+ overflow: hidden;
859
+ border: 1px solid color-mix(in srgb, var(--pp-border) 72%, transparent);
860
+ border-radius: 999px;
861
+ padding: 0.18rem 0.55rem;
862
+ background: color-mix(in srgb, var(--pp-bg) 72%, transparent);
863
+ color: var(--pp-muted);
864
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
865
+ font-size: 0.72rem;
866
+ line-height: 1.35;
867
+ text-overflow: ellipsis;
868
+ text-transform: uppercase;
869
+ letter-spacing: 0.04em;
870
+ backdrop-filter: blur(12px);
871
+ }
872
+
873
+ .pp-doc-content .shiki,
874
+ .pp-doc-content .shiki span {
875
+ color: var(--shiki-light, var(--pp-code-text)) !important;
876
+ background-color: var(--shiki-light-bg, var(--pp-code-bg)) !important;
877
+ }
878
+
879
+ @media (prefers-color-scheme: dark) {
880
+ :root:not([data-theme]) .pp-doc-content .shiki,
881
+ :root:not([data-theme]) .pp-doc-content .shiki span {
882
+ color: var(--shiki-dark, var(--pp-code-text)) !important;
883
+ background-color: var(--shiki-dark-bg, var(--pp-code-bg)) !important;
884
+ }
885
+ }
886
+
887
+ :root[data-theme="dark"] .pp-doc-content .shiki,
888
+ :root[data-theme="dark"] .pp-doc-content .shiki span {
889
+ color: var(--shiki-dark, var(--pp-code-text)) !important;
890
+ background-color: var(--shiki-dark-bg, var(--pp-code-bg)) !important;
891
+ }
892
+
893
+ .pp-doc-content .pp-code-copy {
894
+ position: absolute;
895
+ top: 10px;
896
+ right: 10px;
897
+ z-index: 2;
898
+ display: grid;
899
+ place-items: center;
900
+ width: 34px;
901
+ height: 34px;
902
+ padding: 0;
903
+ border: 1px solid color-mix(in srgb, var(--pp-border) 72%, transparent);
904
+ border-radius: 10px;
905
+ background: color-mix(in srgb, var(--pp-bg) 72%, transparent);
906
+ color: var(--pp-muted);
907
+ opacity: 0;
908
+ transform: translateY(-3px) scale(0.96);
909
+ cursor: pointer;
910
+ backdrop-filter: blur(12px);
911
+ transition:
912
+ opacity 0.18s ease,
913
+ transform 0.18s ease,
914
+ color 0.18s ease,
915
+ border-color 0.18s ease,
916
+ background 0.18s ease;
917
+ }
918
+
919
+ .pp-doc-content .pp-code-block:hover .pp-code-copy,
920
+ .pp-doc-content .pp-code-copy:focus-visible,
921
+ .pp-doc-content .pp-code-copy.copied {
922
+ opacity: 1;
923
+ transform: translateY(0) scale(1);
924
+ }
925
+
926
+ .pp-doc-content .pp-code-copy:hover,
927
+ .pp-doc-content .pp-code-copy.copied {
928
+ color: var(--pp-accent);
929
+ border-color: color-mix(in srgb, var(--pp-accent) 44%, var(--pp-border));
930
+ background: color-mix(in srgb, var(--pp-bg) 88%, var(--pp-accent) 12%);
931
+ }
932
+
933
+ .pp-doc-content .pp-code-copy svg {
934
+ width: 17px;
935
+ height: 17px;
936
+ fill: currentColor;
937
+ }
938
+
939
+ .pp-doc-content.pp-code-wrap .pp-code-block pre code,
940
+ .pp-doc-content .pp-code-block.pp-code-wrap pre code {
941
+ min-width: 0;
942
+ white-space: pre-wrap;
943
+ overflow-wrap: anywhere;
770
944
  }
771
945
 
772
946
  .pp-doc-content :not(pre) > code {
@@ -970,9 +1144,11 @@ body::selection {
970
1144
  display: block;
971
1145
  }
972
1146
 
973
- .pp-doc-content .pp-code-group-block .shiki {
1147
+ .pp-doc-content .pp-code-group-block .pp-code-block {
974
1148
  margin: 0;
1149
+ border: 0;
975
1150
  border-radius: 0;
1151
+ box-shadow: none;
976
1152
  }
977
1153
 
978
1154
  .pp-doc-content table {