@pyreon/zero 0.12.13 → 0.12.15
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/lib/client.js +41 -5
- package/lib/client.js.map +1 -1
- package/lib/env.js +6 -6
- package/lib/env.js.map +1 -1
- package/lib/favicon.js +2 -2
- package/lib/favicon.js.map +1 -1
- package/lib/font.js +2 -2
- package/lib/font.js.map +1 -1
- package/lib/i18n-routing.js.map +1 -1
- package/lib/image-plugin.js +1 -1
- package/lib/image-plugin.js.map +1 -1
- package/lib/index.js +39 -10
- package/lib/index.js.map +1 -1
- package/lib/link.js +12 -4
- package/lib/link.js.map +1 -1
- package/lib/meta.js.map +1 -1
- package/lib/og-image.js +2 -2
- package/lib/og-image.js.map +1 -1
- package/lib/script.js +1 -0
- package/lib/script.js.map +1 -1
- package/lib/server.js +132 -12
- package/lib/server.js.map +1 -1
- package/lib/theme.js +27 -7
- package/lib/theme.js.map +1 -1
- package/lib/types/client.d.ts +26 -0
- package/lib/types/client.d.ts.map +1 -1
- package/lib/types/config.d.ts +7 -0
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/index.d.ts +13 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/link.d.ts.map +1 -1
- package/lib/types/server.d.ts +14 -0
- package/lib/types/server.d.ts.map +1 -1
- package/lib/types/theme.d.ts +6 -1
- package/lib/types/theme.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/adapters/index.ts +1 -1
- package/src/adapters/validate.ts +2 -2
- package/src/client.ts +84 -6
- package/src/env.ts +6 -6
- package/src/favicon.ts +3 -3
- package/src/font.ts +2 -2
- package/src/i18n-routing.ts +1 -1
- package/src/image-plugin.ts +1 -1
- package/src/isr.ts +34 -2
- package/src/link.tsx +21 -5
- package/src/og-image.ts +2 -2
- package/src/script.tsx +4 -0
- package/src/theme.tsx +33 -11
- package/src/types.ts +7 -0
- package/src/vite-plugin.ts +204 -2
package/lib/theme.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { onMount
|
|
1
|
+
import { onMount } from "@pyreon/core";
|
|
2
2
|
import { effect, signal } from "@pyreon/reactivity";
|
|
3
3
|
|
|
4
4
|
//#region ../../core/core/lib/jsx-runtime.js
|
|
@@ -55,6 +55,18 @@ const jsxs = jsx;
|
|
|
55
55
|
const STORAGE_KEY = "zero-theme";
|
|
56
56
|
/** Reactive theme signal. */
|
|
57
57
|
const theme = signal("system");
|
|
58
|
+
/**
|
|
59
|
+
* Reactive signal tracking the OS color-scheme preference. Updated by the
|
|
60
|
+
* `matchMedia('(prefers-color-scheme: dark)').change` event registered in
|
|
61
|
+
* `initTheme`. Components reading `resolvedTheme()` subscribe to BOTH
|
|
62
|
+
* `theme` and this signal, so a user toggling dark mode at the OS level
|
|
63
|
+
* re-renders everything reactively — not just the `<html data-theme>`
|
|
64
|
+
* attribute.
|
|
65
|
+
*
|
|
66
|
+
* SSR default is `_ssrDefault` (mutable via `setSSRThemeDefault`) so the
|
|
67
|
+
* server-rendered theme can differ from the client's OS preference.
|
|
68
|
+
*/
|
|
69
|
+
const _osPrefersDark = signal(false);
|
|
58
70
|
/** SSR fallback when system preference can't be detected. Default: 'light'. */
|
|
59
71
|
let _ssrDefault = "light";
|
|
60
72
|
/**
|
|
@@ -64,12 +76,17 @@ let _ssrDefault = "light";
|
|
|
64
76
|
function setSSRThemeDefault(value) {
|
|
65
77
|
_ssrDefault = value;
|
|
66
78
|
}
|
|
67
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Reactive read of the resolved theme. Subscribes to `theme` (explicit
|
|
81
|
+
* user choice) and — when `theme === 'system'` — to `_osPrefersDark`
|
|
82
|
+
* (OS color-scheme preference). Components using `resolvedTheme()`
|
|
83
|
+
* inside JSX / effects / computeds re-render when either changes.
|
|
84
|
+
*/
|
|
68
85
|
function resolvedTheme() {
|
|
69
86
|
const t = theme();
|
|
70
87
|
if (t === "system") {
|
|
71
88
|
if (typeof window === "undefined") return _ssrDefault;
|
|
72
|
-
return
|
|
89
|
+
return _osPrefersDark() ? "dark" : "light";
|
|
73
90
|
}
|
|
74
91
|
return t;
|
|
75
92
|
}
|
|
@@ -99,18 +116,21 @@ function initTheme() {
|
|
|
99
116
|
} catch {}
|
|
100
117
|
document.documentElement.dataset.theme = resolvedTheme();
|
|
101
118
|
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
102
|
-
|
|
103
|
-
|
|
119
|
+
_osPrefersDark.set(mq.matches);
|
|
120
|
+
function onChange(e) {
|
|
121
|
+
_osPrefersDark.set(e.matches);
|
|
104
122
|
}
|
|
105
123
|
mq.addEventListener("change", onChange);
|
|
106
|
-
onUnmount(() => mq.removeEventListener("change", onChange));
|
|
107
124
|
const dispose = effect(() => {
|
|
108
125
|
const mode = resolvedTheme();
|
|
109
126
|
document.documentElement.dataset.theme = mode;
|
|
110
127
|
const faviconLinks = document.querySelectorAll("[data-favicon-theme]");
|
|
111
128
|
for (const link of faviconLinks) link.media = link.dataset.faviconTheme === mode ? "" : "not all";
|
|
112
129
|
});
|
|
113
|
-
|
|
130
|
+
return () => {
|
|
131
|
+
mq.removeEventListener("change", onChange);
|
|
132
|
+
dispose?.dispose();
|
|
133
|
+
};
|
|
114
134
|
});
|
|
115
135
|
}
|
|
116
136
|
/**
|
package/lib/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","names":[],"sources":["../../../core/core/lib/jsx-runtime.js","../src/theme.tsx"],"sourcesContent":["//#region src/h.ts\n/** Marker for fragment nodes — renders children without a wrapper element */\nconst Fragment = Symbol(\"Pyreon.Fragment\");\n/**\n* Hyperscript function — the compiled output of JSX.\n* `<div class=\"x\">hello</div>` → `h(\"div\", { class: \"x\" }, \"hello\")`\n*\n* Generic on P so TypeScript validates props match the component's signature\n* at the call site, then stores the result in the loosely-typed VNode.\n*/\n/** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */\nconst EMPTY_PROPS = {};\nfunction h(type, props, ...children) {\n\treturn {\n\t\ttype,\n\t\tprops: props ?? EMPTY_PROPS,\n\t\tchildren: normalizeChildren(children),\n\t\tkey: props?.key ?? null\n\t};\n}\nfunction normalizeChildren(children) {\n\tfor (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);\n\treturn children;\n}\nfunction flattenChildren(children) {\n\tconst result = [];\n\tfor (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));\n\telse result.push(child);\n\treturn result;\n}\n\n//#endregion\n//#region src/jsx-runtime.ts\n/**\n* JSX automatic runtime.\n*\n* When tsconfig has `\"jsxImportSource\": \"@pyreon/core\"`, the TS/bundler compiler\n* rewrites JSX to imports from this file automatically:\n* <div class=\"x\" /> → jsx(\"div\", { class: \"x\" })\n*/\nfunction jsx(type, props, key) {\n\tconst { children, ...rest } = props;\n\tconst propsWithKey = key != null ? {\n\t\t...rest,\n\t\tkey\n\t} : rest;\n\tif (typeof type === \"function\") return h(type, children !== void 0 ? {\n\t\t...propsWithKey,\n\t\tchildren\n\t} : propsWithKey);\n\treturn h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);\n}\nconst jsxs = jsx;\n\n//#endregion\nexport { Fragment, jsx, jsxs };\n//# sourceMappingURL=jsx-runtime.js.map","import type { VNodeChild } from '@pyreon/core'\nimport { onMount, onUnmount } from '@pyreon/core'\nimport { effect, signal } from '@pyreon/reactivity'\n\n// ─── Theme system ───────────────────────────────────────────────────────────\n//\n// Provides dark/light/system theme support with:\n// - System preference detection via matchMedia\n// - Persistent preference via localStorage\n// - No flash of wrong theme (inline script in HTML)\n// - Reactive theme signal for components\n\nexport type Theme = 'light' | 'dark' | 'system'\n\nconst STORAGE_KEY = 'zero-theme'\n\n/** Reactive theme signal. */\nexport const theme = signal<Theme>('system')\n\n/** SSR fallback when system preference can't be detected. Default: 'light'. */\nlet _ssrDefault: 'light' | 'dark' = 'light'\n\n/**\n * Set the default theme for SSR (when `matchMedia` is unavailable).\n * Call once at server startup before rendering.\n */\nexport function setSSRThemeDefault(value: 'light' | 'dark'): void {\n _ssrDefault = value\n}\n\n/** Computed resolved theme (what's actually applied). */\nexport function resolvedTheme(): 'light' | 'dark' {\n const t = theme()\n if (t === 'system') {\n if (typeof window === 'undefined') return _ssrDefault\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n }\n return t\n}\n\n/** Toggle between light and dark. */\nexport function toggleTheme() {\n const current = resolvedTheme()\n setTheme(current === 'dark' ? 'light' : 'dark')\n}\n\n/** Set theme explicitly. */\nexport function setTheme(t: Theme) {\n theme.set(t)\n if (typeof document !== 'undefined') {\n document.documentElement.dataset.theme = resolvedTheme()\n try {\n localStorage.setItem(STORAGE_KEY, t)\n } catch {\n // localStorage may not be available (SSR, private browsing)\n }\n }\n}\n\n/**\n * Initialize the theme system. Call once in your app entry or layout.\n * Reads from localStorage, listens for system preference changes.\n */\nexport function initTheme() {\n onMount(() => {\n // Read persisted preference\n try {\n const stored = localStorage.getItem(STORAGE_KEY) as Theme | null\n if (stored === 'light' || stored === 'dark' || stored === 'system') {\n theme.set(stored)\n }\n } catch {\n // localStorage may not be available\n }\n\n // Apply to document\n document.documentElement.dataset.theme = resolvedTheme()\n\n // Watch for system preference changes\n const mq = window.matchMedia('(prefers-color-scheme: dark)')\n function onChange() {\n if (theme() === 'system') {\n document.documentElement.dataset.theme = resolvedTheme()\n }\n }\n mq.addEventListener('change', onChange)\n onUnmount(() => mq.removeEventListener('change', onChange))\n\n // Re-apply when theme signal changes — updates data-theme + favicons\n const dispose = effect(() => {\n const mode = resolvedTheme()\n document.documentElement.dataset.theme = mode\n\n // Swap favicon variants (if dual-variant favicons are present)\n const faviconLinks = document.querySelectorAll<HTMLLinkElement>('[data-favicon-theme]')\n for (const link of faviconLinks) {\n link.media = link.dataset.faviconTheme === mode ? '' : 'not all'\n }\n })\n if (dispose) onUnmount(() => dispose.dispose())\n\n return undefined\n })\n}\n\n/**\n * Theme toggle button component.\n *\n * @example\n * import { ThemeToggle } from \"@pyreon/zero/theme\"\n * <ThemeToggle />\n */\nexport function ThemeToggle(props: { class?: string; style?: string }): VNodeChild {\n initTheme()\n\n return (\n <button\n class={props.class}\n style={props.style}\n onClick={toggleTheme}\n aria-label=\"Toggle theme\"\n title=\"Toggle theme\"\n type=\"button\"\n >\n {() =>\n resolvedTheme() === 'dark' ? (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"5\" />\n <line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"3\" />\n <line x1=\"12\" y1=\"21\" x2=\"12\" y2=\"23\" />\n <line x1=\"4.22\" y1=\"4.22\" x2=\"5.64\" y2=\"5.64\" />\n <line x1=\"18.36\" y1=\"18.36\" x2=\"19.78\" y2=\"19.78\" />\n <line x1=\"1\" y1=\"12\" x2=\"3\" y2=\"12\" />\n <line x1=\"21\" y1=\"12\" x2=\"23\" y2=\"12\" />\n <line x1=\"4.22\" y1=\"19.78\" x2=\"5.64\" y2=\"18.36\" />\n <line x1=\"18.36\" y1=\"5.64\" x2=\"19.78\" y2=\"4.22\" />\n </svg>\n ) : (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n )\n }\n </button>\n )\n}\n\n/**\n * Inline script to prevent flash of wrong theme.\n * Include this in your index.html <head> BEFORE any stylesheets.\n *\n * @example\n * // index.html\n * <head>\n * <script>{themeScript}</script>\n * ...\n * </head>\n */\nexport const themeScript = `(function(){try{var t=localStorage.getItem(\"${STORAGE_KEY}\");var r=t===\"light\"?\"light\":t===\"dark\"?\"dark\":window.matchMedia(\"(prefers-color-scheme:dark)\").matches?\"dark\":\"light\";document.documentElement.dataset.theme=r;document.querySelectorAll(\"[data-favicon-theme]\").forEach(function(l){l.media=l.dataset.faviconTheme===r?\"\":\"not all\"})}catch(e){}})()`\n"],"mappings":";;;;;;;;;;;;AAWA,MAAM,cAAc,EAAE;AACtB,SAAS,EAAE,MAAM,OAAO,GAAG,UAAU;AACpC,QAAO;EACN;EACA,OAAO,SAAS;EAChB,UAAU,kBAAkB,SAAS;EACrC,KAAK,OAAO,OAAO;EACnB;;AAEF,SAAS,kBAAkB,UAAU;AACpC,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAK,KAAI,MAAM,QAAQ,SAAS,GAAG,CAAE,QAAO,gBAAgB,SAAS;AAC1G,QAAO;;AAER,SAAS,gBAAgB,UAAU;CAClC,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,SAAS,SAAU,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,KAAK,GAAG,gBAAgB,MAAM,CAAC;KACzF,QAAO,KAAK,MAAM;AACvB,QAAO;;;;;;;;;AAYR,SAAS,IAAI,MAAM,OAAO,KAAK;CAC9B,MAAM,EAAE,UAAU,GAAG,SAAS;CAC9B,MAAM,eAAe,OAAO,OAAO;EAClC,GAAG;EACH;EACA,GAAG;AACJ,KAAI,OAAO,SAAS,WAAY,QAAO,EAAE,MAAM,aAAa,KAAK,IAAI;EACpE,GAAG;EACH;EACA,GAAG,aAAa;AACjB,QAAO,EAAE,MAAM,cAAc,GAAG,aAAa,KAAK,IAAI,EAAE,GAAG,MAAM,QAAQ,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;;AAE5G,MAAM,OAAO;;;;ACtCb,MAAM,cAAc;;AAGpB,MAAa,QAAQ,OAAc,SAAS;;AAG5C,IAAI,cAAgC;;;;;AAMpC,SAAgB,mBAAmB,OAA+B;AAChE,eAAc;;;AAIhB,SAAgB,gBAAkC;CAChD,MAAM,IAAI,OAAO;AACjB,KAAI,MAAM,UAAU;AAClB,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,WAAW,+BAA+B,CAAC,UAAU,SAAS;;AAE9E,QAAO;;;AAIT,SAAgB,cAAc;AAE5B,UADgB,eAAe,KACV,SAAS,UAAU,OAAO;;;AAIjD,SAAgB,SAAS,GAAU;AACjC,OAAM,IAAI,EAAE;AACZ,KAAI,OAAO,aAAa,aAAa;AACnC,WAAS,gBAAgB,QAAQ,QAAQ,eAAe;AACxD,MAAI;AACF,gBAAa,QAAQ,aAAa,EAAE;UAC9B;;;;;;;AAUZ,SAAgB,YAAY;AAC1B,eAAc;AAEZ,MAAI;GACF,MAAM,SAAS,aAAa,QAAQ,YAAY;AAChD,OAAI,WAAW,WAAW,WAAW,UAAU,WAAW,SACxD,OAAM,IAAI,OAAO;UAEb;AAKR,WAAS,gBAAgB,QAAQ,QAAQ,eAAe;EAGxD,MAAM,KAAK,OAAO,WAAW,+BAA+B;EAC5D,SAAS,WAAW;AAClB,OAAI,OAAO,KAAK,SACd,UAAS,gBAAgB,QAAQ,QAAQ,eAAe;;AAG5D,KAAG,iBAAiB,UAAU,SAAS;AACvC,kBAAgB,GAAG,oBAAoB,UAAU,SAAS,CAAC;EAG3D,MAAM,UAAU,aAAa;GAC3B,MAAM,OAAO,eAAe;AAC5B,YAAS,gBAAgB,QAAQ,QAAQ;GAGzC,MAAM,eAAe,SAAS,iBAAkC,uBAAuB;AACvF,QAAK,MAAM,QAAQ,aACjB,MAAK,QAAQ,KAAK,QAAQ,iBAAiB,OAAO,KAAK;IAEzD;AACF,MAAI,QAAS,iBAAgB,QAAQ,SAAS,CAAC;GAG/C;;;;;;;;;AAUJ,SAAgB,YAAY,OAAuD;AACjF,YAAW;AAEX,QACE,oBAAC,UAAD;EACE,OAAO,MAAM;EACb,OAAO,MAAM;EACb,SAAS;EACT,cAAW;EACX,OAAM;EACN,MAAK;kBAGH,eAAe,KAAK,SAClB,qBAAC,OAAD;GACE,OAAM;GACN,QAAO;GACP,SAAQ;GACR,MAAK;GACL,QAAO;GACP,gBAAa;GACb,kBAAe;GACf,mBAAgB;GAChB,eAAY;aATd;IAWE,oBAAC,UAAD;KAAQ,IAAG;KAAK,IAAG;KAAK,GAAE;KAAM;IAChC,oBAAC,QAAD;KAAM,IAAG;KAAK,IAAG;KAAI,IAAG;KAAK,IAAG;KAAM;IACtC,oBAAC,QAAD;KAAM,IAAG;KAAK,IAAG;KAAK,IAAG;KAAK,IAAG;KAAO;IACxC,oBAAC,QAAD;KAAM,IAAG;KAAO,IAAG;KAAO,IAAG;KAAO,IAAG;KAAS;IAChD,oBAAC,QAAD;KAAM,IAAG;KAAQ,IAAG;KAAQ,IAAG;KAAQ,IAAG;KAAU;IACpD,oBAAC,QAAD;KAAM,IAAG;KAAI,IAAG;KAAK,IAAG;KAAI,IAAG;KAAO;IACtC,oBAAC,QAAD;KAAM,IAAG;KAAK,IAAG;KAAK,IAAG;KAAK,IAAG;KAAO;IACxC,oBAAC,QAAD;KAAM,IAAG;KAAO,IAAG;KAAQ,IAAG;KAAO,IAAG;KAAU;IAClD,oBAAC,QAAD;KAAM,IAAG;KAAQ,IAAG;KAAO,IAAG;KAAQ,IAAG;KAAS;IAC9C;OAEN,oBAAC,OAAD;GACE,OAAM;GACN,QAAO;GACP,SAAQ;GACR,MAAK;GACL,QAAO;GACP,gBAAa;GACb,kBAAe;GACf,mBAAgB;GAChB,eAAY;aAEZ,oBAAC,QAAD,EAAM,GAAE,mDAAoD;GACxD;EAGH;;;;;;;;;;;;;AAeb,MAAa,cAAc,+CAA+C,YAAY"}
|
|
1
|
+
{"version":3,"file":"theme.js","names":[],"sources":["../../../core/core/lib/jsx-runtime.js","../src/theme.tsx"],"sourcesContent":["//#region src/h.ts\n/** Marker for fragment nodes — renders children without a wrapper element */\nconst Fragment = Symbol(\"Pyreon.Fragment\");\n/**\n* Hyperscript function — the compiled output of JSX.\n* `<div class=\"x\">hello</div>` → `h(\"div\", { class: \"x\" }, \"hello\")`\n*\n* Generic on P so TypeScript validates props match the component's signature\n* at the call site, then stores the result in the loosely-typed VNode.\n*/\n/** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */\nconst EMPTY_PROPS = {};\nfunction h(type, props, ...children) {\n\treturn {\n\t\ttype,\n\t\tprops: props ?? EMPTY_PROPS,\n\t\tchildren: normalizeChildren(children),\n\t\tkey: props?.key ?? null\n\t};\n}\nfunction normalizeChildren(children) {\n\tfor (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);\n\treturn children;\n}\nfunction flattenChildren(children) {\n\tconst result = [];\n\tfor (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));\n\telse result.push(child);\n\treturn result;\n}\n\n//#endregion\n//#region src/jsx-runtime.ts\n/**\n* JSX automatic runtime.\n*\n* When tsconfig has `\"jsxImportSource\": \"@pyreon/core\"`, the TS/bundler compiler\n* rewrites JSX to imports from this file automatically:\n* <div class=\"x\" /> → jsx(\"div\", { class: \"x\" })\n*/\nfunction jsx(type, props, key) {\n\tconst { children, ...rest } = props;\n\tconst propsWithKey = key != null ? {\n\t\t...rest,\n\t\tkey\n\t} : rest;\n\tif (typeof type === \"function\") return h(type, children !== void 0 ? {\n\t\t...propsWithKey,\n\t\tchildren\n\t} : propsWithKey);\n\treturn h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);\n}\nconst jsxs = jsx;\n\n//#endregion\nexport { Fragment, jsx, jsxs };\n//# sourceMappingURL=jsx-runtime.js.map","import type { VNodeChild } from '@pyreon/core'\nimport { onMount } from '@pyreon/core'\nimport { effect, signal } from '@pyreon/reactivity'\n\n// ─── Theme system ───────────────────────────────────────────────────────────\n//\n// Provides dark/light/system theme support with:\n// - System preference detection via matchMedia\n// - Persistent preference via localStorage\n// - No flash of wrong theme (inline script in HTML)\n// - Reactive theme signal for components\n\nexport type Theme = 'light' | 'dark' | 'system'\n\nconst STORAGE_KEY = 'zero-theme'\n\n/** Reactive theme signal. */\nexport const theme = signal<Theme>('system')\n\n/**\n * Reactive signal tracking the OS color-scheme preference. Updated by the\n * `matchMedia('(prefers-color-scheme: dark)').change` event registered in\n * `initTheme`. Components reading `resolvedTheme()` subscribe to BOTH\n * `theme` and this signal, so a user toggling dark mode at the OS level\n * re-renders everything reactively — not just the `<html data-theme>`\n * attribute.\n *\n * SSR default is `_ssrDefault` (mutable via `setSSRThemeDefault`) so the\n * server-rendered theme can differ from the client's OS preference.\n */\nconst _osPrefersDark = signal<boolean>(false)\n\n/** SSR fallback when system preference can't be detected. Default: 'light'. */\nlet _ssrDefault: 'light' | 'dark' = 'light'\n\n/**\n * Set the default theme for SSR (when `matchMedia` is unavailable).\n * Call once at server startup before rendering.\n */\nexport function setSSRThemeDefault(value: 'light' | 'dark'): void {\n _ssrDefault = value\n}\n\n/**\n * Reactive read of the resolved theme. Subscribes to `theme` (explicit\n * user choice) and — when `theme === 'system'` — to `_osPrefersDark`\n * (OS color-scheme preference). Components using `resolvedTheme()`\n * inside JSX / effects / computeds re-render when either changes.\n */\nexport function resolvedTheme(): 'light' | 'dark' {\n const t = theme()\n if (t === 'system') {\n if (typeof window === 'undefined') return _ssrDefault\n return _osPrefersDark() ? 'dark' : 'light'\n }\n return t\n}\n\n/** Toggle between light and dark. */\nexport function toggleTheme() {\n const current = resolvedTheme()\n setTheme(current === 'dark' ? 'light' : 'dark')\n}\n\n/** Set theme explicitly. */\nexport function setTheme(t: Theme) {\n theme.set(t)\n if (typeof document !== 'undefined') {\n document.documentElement.dataset.theme = resolvedTheme()\n try {\n localStorage.setItem(STORAGE_KEY, t)\n } catch {\n // localStorage may not be available (SSR, private browsing)\n }\n }\n}\n\n/**\n * Initialize the theme system. Call once in your app entry or layout.\n * Reads from localStorage, listens for system preference changes.\n */\nexport function initTheme() {\n onMount(() => {\n // Read persisted preference\n try {\n const stored = localStorage.getItem(STORAGE_KEY) as Theme | null\n if (stored === 'light' || stored === 'dark' || stored === 'system') {\n theme.set(stored)\n }\n } catch {\n // localStorage may not be available\n }\n\n // Apply to document\n document.documentElement.dataset.theme = resolvedTheme()\n\n // Watch for system preference changes. Seed the signal from the\n // current media-query state, then update reactively on each OS\n // preference flip. Components reading `resolvedTheme()` pick up the\n // change automatically (they subscribe to `_osPrefersDark` when\n // `theme === 'system'`).\n const mq = window.matchMedia('(prefers-color-scheme: dark)')\n _osPrefersDark.set(mq.matches)\n function onChange(e: MediaQueryListEvent) {\n _osPrefersDark.set(e.matches)\n }\n mq.addEventListener('change', onChange)\n\n // Re-apply when theme signal changes — updates data-theme + favicons\n const dispose = effect(() => {\n const mode = resolvedTheme()\n document.documentElement.dataset.theme = mode\n\n // Swap favicon variants (if dual-variant favicons are present)\n const faviconLinks = document.querySelectorAll<HTMLLinkElement>('[data-favicon-theme]')\n for (const link of faviconLinks) {\n link.media = link.dataset.faviconTheme === mode ? '' : 'not all'\n }\n })\n\n return () => {\n mq.removeEventListener('change', onChange)\n dispose?.dispose()\n }\n })\n}\n\n/**\n * Theme toggle button component.\n *\n * @example\n * import { ThemeToggle } from \"@pyreon/zero/theme\"\n * <ThemeToggle />\n */\nexport function ThemeToggle(props: { class?: string; style?: string }): VNodeChild {\n initTheme()\n\n return (\n <button\n class={props.class}\n style={props.style}\n onClick={toggleTheme}\n aria-label=\"Toggle theme\"\n title=\"Toggle theme\"\n type=\"button\"\n >\n {() =>\n resolvedTheme() === 'dark' ? (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"5\" />\n <line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"3\" />\n <line x1=\"12\" y1=\"21\" x2=\"12\" y2=\"23\" />\n <line x1=\"4.22\" y1=\"4.22\" x2=\"5.64\" y2=\"5.64\" />\n <line x1=\"18.36\" y1=\"18.36\" x2=\"19.78\" y2=\"19.78\" />\n <line x1=\"1\" y1=\"12\" x2=\"3\" y2=\"12\" />\n <line x1=\"21\" y1=\"12\" x2=\"23\" y2=\"12\" />\n <line x1=\"4.22\" y1=\"19.78\" x2=\"5.64\" y2=\"18.36\" />\n <line x1=\"18.36\" y1=\"5.64\" x2=\"19.78\" y2=\"4.22\" />\n </svg>\n ) : (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n )\n }\n </button>\n )\n}\n\n/**\n * Inline script to prevent flash of wrong theme.\n * Include this in your index.html <head> BEFORE any stylesheets.\n *\n * @example\n * // index.html\n * <head>\n * <script>{themeScript}</script>\n * ...\n * </head>\n */\nexport const themeScript = `(function(){try{var t=localStorage.getItem(\"${STORAGE_KEY}\");var r=t===\"light\"?\"light\":t===\"dark\"?\"dark\":window.matchMedia(\"(prefers-color-scheme:dark)\").matches?\"dark\":\"light\";document.documentElement.dataset.theme=r;document.querySelectorAll(\"[data-favicon-theme]\").forEach(function(l){l.media=l.dataset.faviconTheme===r?\"\":\"not all\"})}catch(e){}})()`\n"],"mappings":"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"}
|
package/lib/types/client.d.ts
CHANGED
|
@@ -11,6 +11,32 @@ interface StartClientOptions {
|
|
|
11
11
|
/**
|
|
12
12
|
* Start the client-side app — hydrates SSR content or mounts fresh for SPA.
|
|
13
13
|
*
|
|
14
|
+
* ## Loader data flow
|
|
15
|
+
*
|
|
16
|
+
* Direct navigation to a route with a `loader` function needs data to be
|
|
17
|
+
* available on the VERY FIRST render. This is handled in two modes:
|
|
18
|
+
*
|
|
19
|
+
* - **SSR mode (zero's default)**: the server pre-runs loaders, renders the
|
|
20
|
+
* HTML with loader data already applied, and embeds a JSON blob in the
|
|
21
|
+
* HTML as `window.__PYREON_LOADER_DATA__`. On the client we read that
|
|
22
|
+
* blob and call `hydrateLoaderData(router, data)` BEFORE hydrating — so
|
|
23
|
+
* the hydration pass sees the same data the SSR render produced
|
|
24
|
+
* (avoids hydration mismatches and the flash of "not found" fallback).
|
|
25
|
+
*
|
|
26
|
+
* - **SPA cold start (no SSR content)**: no `__PYREON_LOADER_DATA__` was
|
|
27
|
+
* embedded, so we call `router.replace(currentPath)` after mount to
|
|
28
|
+
* trigger the loader pipeline for the initial route. The first render
|
|
29
|
+
* shows whatever the component displays for `useLoaderData() === undefined`
|
|
30
|
+
* (typically a loading state or fallback); once loaders resolve, the
|
|
31
|
+
* reactive `useLoaderData` re-renders with the data. This matches
|
|
32
|
+
* standard SPA loading behavior.
|
|
33
|
+
*
|
|
34
|
+
* Without this wiring, direct URL navigation to a loader-backed route
|
|
35
|
+
* (e.g. `/posts/3`) showed the "Post not found" fallback indefinitely
|
|
36
|
+
* because `useLoaderData()` returned `undefined` forever. The router
|
|
37
|
+
* only ran loaders on in-app navigation (push/replace), not on initial
|
|
38
|
+
* mount.
|
|
39
|
+
*
|
|
14
40
|
* @example
|
|
15
41
|
* import { routes } from "virtual:zero/routes"
|
|
16
42
|
* import { startClient } from "@pyreon/zero/client"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client2.d.ts","names":[],"sources":["../../../src/client.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"client2.d.ts","names":[],"sources":["../../../src/client.ts"],"mappings":";;;;UASiB,kBAAA;;EAEf,MAAA,EAAQ,WAAA;EAFyB;EAIjC,MAAA,GAAS,WAAA;AAAA;;;;;;;AAsCX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,WAAA,CAAY,OAAA,EAAS,kBAAA"}
|
package/lib/types/config.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr';
|
|
|
5
5
|
interface ISRConfig {
|
|
6
6
|
/** Revalidation interval in seconds. */
|
|
7
7
|
revalidate: number;
|
|
8
|
+
/**
|
|
9
|
+
* Maximum number of distinct URL paths to keep in the in-memory cache.
|
|
10
|
+
* Oldest-first LRU eviction once the cap is reached. Default: `1000`.
|
|
11
|
+
* Set higher for SSG-heavy sites, lower for routes with unbounded URL
|
|
12
|
+
* space (e.g. `/user/:id` where `:id` is free-form).
|
|
13
|
+
*/
|
|
14
|
+
maxEntries?: number;
|
|
8
15
|
}
|
|
9
16
|
interface ZeroConfig {
|
|
10
17
|
/** Default rendering mode. Default: "ssr" */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config2.d.ts","names":[],"sources":["../../../src/types.ts","../../../src/config.ts"],"mappings":";;;KA6CY,UAAA;AAAA,UAEK,SAAA;
|
|
1
|
+
{"version":3,"file":"config2.d.ts","names":[],"sources":["../../../src/types.ts","../../../src/config.ts"],"mappings":";;;KA6CY,UAAA;AAAA,UAEK,SAAA;EA2CF;EAzCb,UAAA;EA4CI;;;;;AC9EN;EDyCE,UAAA;AAAA;AAAA,UAKe,UAAA;EC9CoB;EDgDnC,IAAA,GAAO,UAAA;EChDyC;EDmDhD,IAAA,GAAO,MAAA;ECnDmD;EDsD1D,GAAA;ICjD2B,wCDmDzB,IAAA;EAAA;ECjDa;EDqDf,GAAA;ICrDC,wDDuDC,KAAA,gCAAqC,OAAA;EAAA;ECvDuC;ED2D9E,GAAA,GAAM,SAAA;EC5DN;ED+DA,OAAA;EC9DU;EDiEV,IAAA;ECjEoE;EDoEpE,UAAA,GAAa,UAAA;ECpEiE;EDuE9E,IAAA;AAAA;;;;;;;AAhDF;;;;;AAEA;;;;iBChCgB,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;AD8ClD;AAAA,iBCzCgB,aAAA,CACd,UAAA,GAAY,UAAA,GACX,QAAA,CAAS,IAAA,CAAK,UAAA,2CAAqD,UAAA"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -454,7 +454,12 @@ declare const theme: _pyreon_reactivity0.Signal<Theme>;
|
|
|
454
454
|
* Call once at server startup before rendering.
|
|
455
455
|
*/
|
|
456
456
|
declare function setSSRThemeDefault(value: 'light' | 'dark'): void;
|
|
457
|
-
/**
|
|
457
|
+
/**
|
|
458
|
+
* Reactive read of the resolved theme. Subscribes to `theme` (explicit
|
|
459
|
+
* user choice) and — when `theme === 'system'` — to `_osPrefersDark`
|
|
460
|
+
* (OS color-scheme preference). Components using `resolvedTheme()`
|
|
461
|
+
* inside JSX / effects / computeds re-render when either changes.
|
|
462
|
+
*/
|
|
458
463
|
declare function resolvedTheme(): 'light' | 'dark';
|
|
459
464
|
/** Toggle between light and dark. */
|
|
460
465
|
declare function toggleTheme(): void;
|
|
@@ -528,6 +533,13 @@ type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr';
|
|
|
528
533
|
interface ISRConfig {
|
|
529
534
|
/** Revalidation interval in seconds. */
|
|
530
535
|
revalidate: number;
|
|
536
|
+
/**
|
|
537
|
+
* Maximum number of distinct URL paths to keep in the in-memory cache.
|
|
538
|
+
* Oldest-first LRU eviction once the cap is reached. Default: `1000`.
|
|
539
|
+
* Set higher for SSG-heavy sites, lower for routes with unbounded URL
|
|
540
|
+
* space (e.g. `/user/:id` where `:id` is free-form).
|
|
541
|
+
*/
|
|
542
|
+
maxEntries?: number;
|
|
531
543
|
}
|
|
532
544
|
interface ZeroConfig {
|
|
533
545
|
/** Default rendering mode. Default: "ssr" */
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/image-plugin.ts","../../../src/image.tsx","../../../src/link.tsx","../../../src/script.tsx","../../../src/i18n-routing.ts","../../../src/meta.tsx","../../../src/theme.tsx","../../../src/types.ts","../../../src/index.ts"],"mappings":";;;;;;;;UAgHiB,YAAA;EE/EA;EFiFf,IAAA;EEjFyB;EFmFzB,MAAA;AAAA;;;UCpGe,UAAA;;EAEf,GAAA;;EAEA,GAAA;;EAEA,KAAA;ED0F2B;ECxF3B,MAAA;ED0FA;ECxFA,KAAA;;EAEA,MAAA,YAAkB,WAAA;;EAElB,OAAA,GAAU,YAAA;EAde;EAgBzB,OAAA;EAFsB;EAItB,QAAA;EAdA;EAgBA,WAAA;EAZA;EAcA,KAAA;EAVA;EAYA,KAAA;EAVA;EAYA,GAAA;EAVA;EAYA,QAAA;EARA;;;;;EAcA,GAAA;AAAA;AAAA,UAGe,WAAA;EACf,GAAA;EACA,KAAA;AAAA;;;AAgBF;;;;;;;;;;;iBAAgB,KAAA,CAAM,KAAA,EAAO,UAAA,GAAa,UAAA;;;UC1DzB,SAAA;;EAEf,IAAA;;EAEA,QAAA;;EAEA,KAAA;;EAEA,WAAA;EF2F2B;EEzF3B,gBAAA;EF2FA;EEzFA,QAAA;;EAEA,QAAA;;EAEA,KAAA;EDbyB;ECezB,YAAA;EDDsB;ECGtB,OAAA,KAAY,CAAA,EAAG,UAAA;AAAA;;UAIA,eAAA;EACf,IAAA;EACA,GAAA,EAAiD,aAAA,CAArB,GAAA,CAAI,iBAAA;EAChC,OAAA,GAAU,CAAA,EAAG,UAAA;EACb,YAAA;EACA,YAAA;EACA,QAAA;EACA,aAAA;EDRA;ECUA,KAAA;EACA,KAAA;EACA,MAAA;EACA,GAAA;EACA,YAAA;EACA,QAAA;AAAA;ADEF;AAAA,UCEiB,aAAA;;EAEf,GAAA,EAAiD,aAAA,CAArB,GAAA,CAAI,iBAAA;EDF3B;ECIL,WAAA,GAAc,CAAA,EAAG,UAAA;EDYE;ECVnB,gBAAA;EDUkD;ECRlD,gBAAA;EDQoB;ECNpB,QAAA;EDMkD;ECJlD,aAAA;;EAEA,OAAA;AAAA;AAxDF;;;;;;;;AAAA,iBA+
|
|
1
|
+
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/image-plugin.ts","../../../src/image.tsx","../../../src/link.tsx","../../../src/script.tsx","../../../src/i18n-routing.ts","../../../src/meta.tsx","../../../src/theme.tsx","../../../src/types.ts","../../../src/index.ts"],"mappings":"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"}
|
package/lib/types/link.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link2.d.ts","names":[],"sources":["../../../src/link.tsx"],"mappings":";;;UAaiB,SAAA;;EAEf,IAAA;EAFe;EAIf,QAAA;;EAEA,KAAA;EAJA;EAMA,WAAA;EAFA;EAIA,gBAAA;EAAA;EAEA,QAAA;EAEA;EAAA,QAAA;EAIA;EAFA,KAAA;EAIe;EAFf,YAAA;EAEyB;EAAzB,OAAA,KAAY,CAAA,EAAG,UAAA;AAAA;;UAIA,eAAA;EACf,IAAA;EACA,GAAA,EAAiD,aAAA,CAArB,GAAA,CAAI,iBAAA;EAChC,OAAA,GAAU,CAAA,EAAG,UAAA;EACb,YAAA;EACA,YAAA;EACA,QAAA;EACA,aAAA;EALG;EAOH,KAAA;EACA,KAAA;EACA,MAAA;EACA,GAAA;EACA,YAAA;EACA,QAAA;AAAA;;UAIe,aAAA;EARf;EAUA,GAAA,EAAiD,aAAA,CAArB,GAAA,CAAI,iBAAA;EARhC;EAUA,WAAA,GAAc,CAAA,EAAG,UAAA;EARjB;EAUA,gBAAA;EAVQ;EAYR,gBAAA;EAR4B;EAU5B,QAAA;EARgC;EAUhC,aAAA;EARiB;EAUjB,OAAA;AAAA;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"link2.d.ts","names":[],"sources":["../../../src/link.tsx"],"mappings":";;;UAaiB,SAAA;;EAEf,IAAA;EAFe;EAIf,QAAA;;EAEA,KAAA;EAJA;EAMA,WAAA;EAFA;EAIA,gBAAA;EAAA;EAEA,QAAA;EAEA;EAAA,QAAA;EAIA;EAFA,KAAA;EAIe;EAFf,YAAA;EAEyB;EAAzB,OAAA,KAAY,CAAA,EAAG,UAAA;AAAA;;UAIA,eAAA;EACf,IAAA;EACA,GAAA,EAAiD,aAAA,CAArB,GAAA,CAAI,iBAAA;EAChC,OAAA,GAAU,CAAA,EAAG,UAAA;EACb,YAAA;EACA,YAAA;EACA,QAAA;EACA,aAAA;EALG;EAOH,KAAA;EACA,KAAA;EACA,MAAA;EACA,GAAA;EACA,YAAA;EACA,QAAA;AAAA;;UAIe,aAAA;EARf;EAUA,GAAA,EAAiD,aAAA,CAArB,GAAA,CAAI,iBAAA;EARhC;EAUA,WAAA,GAAc,CAAA,EAAG,UAAA;EARjB;EAUA,gBAAA;EAVQ;EAYR,gBAAA;EAR4B;EAU5B,QAAA;EARgC;EAUhC,aAAA;EARiB;EAUjB,OAAA;AAAA;;;;;;;;;iBAuDc,aAAA,CAAc,IAAA;;;;;AAA9B;;;;;AAoBA;;;;;;;iBAAgB,OAAA,CAAQ,KAAA,EAAO,SAAA,GAAY,aAAA;;AAiH3C;;;;;;;;;;;AA8BA;;;;;;;;;;;;;;;;;;;;;;;iBA9BgB,UAAA,CAAW,SAAA,GAAY,KAAA,EAAO,eAAA,YAA2B,KAAA,EAAO,SAAA;;;;;;;;cA8BnE,IAAA,GAAI,KAAA,EA9B+D,SAAA"}
|
package/lib/types/server.d.ts
CHANGED
|
@@ -66,6 +66,13 @@ type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr';
|
|
|
66
66
|
interface ISRConfig {
|
|
67
67
|
/** Revalidation interval in seconds. */
|
|
68
68
|
revalidate: number;
|
|
69
|
+
/**
|
|
70
|
+
* Maximum number of distinct URL paths to keep in the in-memory cache.
|
|
71
|
+
* Oldest-first LRU eviction once the cap is reached. Default: `1000`.
|
|
72
|
+
* Set higher for SSG-heavy sites, lower for routes with unbounded URL
|
|
73
|
+
* space (e.g. `/user/:id` where `:id` is free-form).
|
|
74
|
+
*/
|
|
75
|
+
maxEntries?: number;
|
|
69
76
|
}
|
|
70
77
|
interface ZeroConfig {
|
|
71
78
|
/** Default rendering mode. Default: "ssr" */
|
|
@@ -288,6 +295,13 @@ declare function scanRouteFiles(routesDir: string): Promise<string[]>;
|
|
|
288
295
|
*
|
|
289
296
|
* Wraps an SSR handler and caches responses per URL path.
|
|
290
297
|
* Serves stale content immediately while revalidating in the background.
|
|
298
|
+
*
|
|
299
|
+
* Bounded by `config.maxEntries` (default: 1000) with LRU eviction. The
|
|
300
|
+
* `Map` preserves insertion order, so re-inserting an entry on every
|
|
301
|
+
* serve (touching it) keeps the LRU order correct. Without the cap,
|
|
302
|
+
* unbounded URL spaces like `/user/:id` would grow cache memory without
|
|
303
|
+
* limit over the server's lifetime — a real leak in long-running
|
|
304
|
+
* deployments.
|
|
291
305
|
*/
|
|
292
306
|
declare function createISRHandler(handler: (req: Request) => Promise<Response>, config: ISRConfig): (req: Request) => Promise<Response>;
|
|
293
307
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server2.d.ts","names":[],"sources":["../../../src/app.ts","../../../src/api-routes.ts","../../../src/types.ts","../../../src/entry-server.ts","../../../src/config.ts","../../../src/fs-router.ts","../../../src/isr.ts","../../../src/adapters/bun.ts","../../../src/adapters/cloudflare.ts","../../../src/adapters/netlify.ts","../../../src/adapters/node.ts","../../../src/adapters/static.ts","../../../src/adapters/vercel.ts","../../../src/adapters/index.ts","../../../src/not-found.ts","../../../src/middleware.ts","../../../src/vite-plugin.ts","../../../src/favicon.ts","../../../src/seo.ts","../../../src/og-image.ts","../../../src/ai.ts","../../../src/i18n-routing.ts"],"mappings":";;;;;;;UAQiB,gBAAA;;EAEf,MAAA,EAAQ,WAAA;;EAGR,UAAA;;EAGA,GAAA;EARe;EAWf,MAAA,GAAS,WAAA;;EAGT,cAAA,GAAiB,WAAA;AAAA;;;;;;iBAQH,SAAA,CAAU,OAAA,EAAS,gBAAA;aAAgB,aAAA,CAAA,KAAA;;;;;;UCtBlC,UAAA;;EAEf,OAAA,EAAS,OAAA;;EAET,GAAA,EAAK,GAAA;EDJU;ECMf,IAAA;;EAEA,MAAA,EAAQ,MAAA;EDGC;ECDT,OAAA,EAAS,OAAA;AAAA;;KAIC,UAAA,IAAc,GAAA,EAAK,UAAA,KAAe,QAAA,GAAW,OAAA,CAAQ,QAAA;;UAGhD,cAAA;EACf,GAAA,GAAM,UAAA;EACN,IAAA,GAAO,UAAA;EACP,GAAA,GAAM,UAAA;EACN,KAAA,GAAQ,UAAA;EACR,MAAA,GAAS,UAAA;EACT,IAAA,GAAO,UAAA;EACP,OAAA,GAAU,UAAA;AAAA;;UAIK,aAAA;EDNkB;ECQjC,OAAA;;EAEA,MAAA,EAAQ,cAAA;AAAA;;;KCKE,UAAA;AAAA,UAEK,SAAA;;EAEf,UAAA;AAAA;AAAA,UAKe,UAAA
|
|
1
|
+
{"version":3,"file":"server2.d.ts","names":[],"sources":["../../../src/app.ts","../../../src/api-routes.ts","../../../src/types.ts","../../../src/entry-server.ts","../../../src/config.ts","../../../src/fs-router.ts","../../../src/isr.ts","../../../src/adapters/bun.ts","../../../src/adapters/cloudflare.ts","../../../src/adapters/netlify.ts","../../../src/adapters/node.ts","../../../src/adapters/static.ts","../../../src/adapters/vercel.ts","../../../src/adapters/index.ts","../../../src/not-found.ts","../../../src/middleware.ts","../../../src/vite-plugin.ts","../../../src/favicon.ts","../../../src/seo.ts","../../../src/og-image.ts","../../../src/ai.ts","../../../src/i18n-routing.ts"],"mappings":"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"}
|
package/lib/types/theme.d.ts
CHANGED
|
@@ -10,7 +10,12 @@ declare const theme: _pyreon_reactivity0.Signal<Theme>;
|
|
|
10
10
|
* Call once at server startup before rendering.
|
|
11
11
|
*/
|
|
12
12
|
declare function setSSRThemeDefault(value: 'light' | 'dark'): void;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Reactive read of the resolved theme. Subscribes to `theme` (explicit
|
|
15
|
+
* user choice) and — when `theme === 'system'` — to `_osPrefersDark`
|
|
16
|
+
* (OS color-scheme preference). Components using `resolvedTheme()`
|
|
17
|
+
* inside JSX / effects / computeds re-render when either changes.
|
|
18
|
+
*/
|
|
14
19
|
declare function resolvedTheme(): 'light' | 'dark';
|
|
15
20
|
/** Toggle between light and dark. */
|
|
16
21
|
declare function toggleTheme(): void;
|
package/lib/types/theme.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme2.d.ts","names":[],"sources":["../../../src/theme.tsx"],"mappings":";;;;KAYY,KAAA;;cAKC,KAAA,EAAK,mBAAA,CAAA,MAAA,CAAA,KAAA;AALlB;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"theme2.d.ts","names":[],"sources":["../../../src/theme.tsx"],"mappings":";;;;KAYY,KAAA;;cAKC,KAAA,EAAK,mBAAA,CAAA,MAAA,CAAA,KAAA;AALlB;;;;AAAA,iBA2BgB,kBAAA,CAAmB,KAAA;AAtBnC;;;;;AAsBA;AAtBA,iBAgCgB,aAAA,CAAA;;iBAUA,WAAA,CAAA;;iBAMA,QAAA,CAAS,CAAA,EAAG,KAAA;;;;;iBAgBZ,SAAA,CAAA;;;;;AAhBhB;;;iBAqEgB,WAAA,CAAY,KAAA;EAAS,KAAA;EAAgB,KAAA;AAAA,IAAmB,UAAA;;;;AAAxE;;;;;;;;cAkEa,WAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/zero",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.15",
|
|
4
4
|
"description": "Pyreon Zero — zero-config full-stack framework powered by Pyreon and Vite",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Vit Bokisch",
|
|
@@ -166,18 +166,18 @@
|
|
|
166
166
|
"lint": "oxlint ."
|
|
167
167
|
},
|
|
168
168
|
"dependencies": {
|
|
169
|
-
"@pyreon/core": "^0.12.
|
|
170
|
-
"@pyreon/head": "^0.12.
|
|
171
|
-
"@pyreon/meta": "^0.12.
|
|
172
|
-
"@pyreon/router": "^0.12.
|
|
173
|
-
"@pyreon/runtime-dom": "^0.12.
|
|
174
|
-
"@pyreon/runtime-server": "^0.12.
|
|
175
|
-
"@pyreon/server": "^0.12.
|
|
176
|
-
"@pyreon/vite-plugin": "^0.12.
|
|
169
|
+
"@pyreon/core": "^0.12.15",
|
|
170
|
+
"@pyreon/head": "^0.12.15",
|
|
171
|
+
"@pyreon/meta": "^0.12.15",
|
|
172
|
+
"@pyreon/router": "^0.12.15",
|
|
173
|
+
"@pyreon/runtime-dom": "^0.12.15",
|
|
174
|
+
"@pyreon/runtime-server": "^0.12.15",
|
|
175
|
+
"@pyreon/server": "^0.12.15",
|
|
176
|
+
"@pyreon/vite-plugin": "^0.12.15",
|
|
177
177
|
"vite": "^8.0.0"
|
|
178
178
|
},
|
|
179
179
|
"peerDependencies": {
|
|
180
|
-
"@pyreon/reactivity": "^0.12.
|
|
180
|
+
"@pyreon/reactivity": "^0.12.15",
|
|
181
181
|
"sharp": "^0.33.0"
|
|
182
182
|
},
|
|
183
183
|
"peerDependenciesMeta": {
|
package/src/adapters/index.ts
CHANGED
|
@@ -34,6 +34,6 @@ export function resolveAdapter(config: ZeroConfig): Adapter {
|
|
|
34
34
|
case 'netlify':
|
|
35
35
|
return netlifyAdapter()
|
|
36
36
|
default:
|
|
37
|
-
throw new Error(`[
|
|
37
|
+
throw new Error(`[Pyreon] Unknown adapter: "${name}". Use "node", "bun", "static", "vercel", "cloudflare", or "netlify".`)
|
|
38
38
|
}
|
|
39
39
|
}
|
package/src/adapters/validate.ts
CHANGED
|
@@ -8,9 +8,9 @@ import type { AdapterBuildOptions } from '../types'
|
|
|
8
8
|
export async function validateBuildInputs(options: AdapterBuildOptions): Promise<void> {
|
|
9
9
|
const { existsSync } = await import('node:fs')
|
|
10
10
|
if (!existsSync(options.clientOutDir)) {
|
|
11
|
-
throw new Error(`[
|
|
11
|
+
throw new Error(`[Pyreon] Client build output not found: ${options.clientOutDir}. Run "vite build" first.`)
|
|
12
12
|
}
|
|
13
13
|
if (!existsSync(options.serverEntry)) {
|
|
14
|
-
throw new Error(`[
|
|
14
|
+
throw new Error(`[Pyreon] Server entry not found: ${options.serverEntry}. Run "vite build --ssr" first.`)
|
|
15
15
|
}
|
|
16
16
|
}
|
package/src/client.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ComponentFn } from '@pyreon/core'
|
|
2
2
|
import { h } from '@pyreon/core'
|
|
3
3
|
import type { RouteRecord } from '@pyreon/router'
|
|
4
|
+
import { hydrateLoaderData } from '@pyreon/router'
|
|
4
5
|
import { hydrateRoot, mount } from '@pyreon/runtime-dom'
|
|
5
6
|
import { createApp } from './app'
|
|
6
7
|
|
|
@@ -16,6 +17,32 @@ export interface StartClientOptions {
|
|
|
16
17
|
/**
|
|
17
18
|
* Start the client-side app — hydrates SSR content or mounts fresh for SPA.
|
|
18
19
|
*
|
|
20
|
+
* ## Loader data flow
|
|
21
|
+
*
|
|
22
|
+
* Direct navigation to a route with a `loader` function needs data to be
|
|
23
|
+
* available on the VERY FIRST render. This is handled in two modes:
|
|
24
|
+
*
|
|
25
|
+
* - **SSR mode (zero's default)**: the server pre-runs loaders, renders the
|
|
26
|
+
* HTML with loader data already applied, and embeds a JSON blob in the
|
|
27
|
+
* HTML as `window.__PYREON_LOADER_DATA__`. On the client we read that
|
|
28
|
+
* blob and call `hydrateLoaderData(router, data)` BEFORE hydrating — so
|
|
29
|
+
* the hydration pass sees the same data the SSR render produced
|
|
30
|
+
* (avoids hydration mismatches and the flash of "not found" fallback).
|
|
31
|
+
*
|
|
32
|
+
* - **SPA cold start (no SSR content)**: no `__PYREON_LOADER_DATA__` was
|
|
33
|
+
* embedded, so we call `router.replace(currentPath)` after mount to
|
|
34
|
+
* trigger the loader pipeline for the initial route. The first render
|
|
35
|
+
* shows whatever the component displays for `useLoaderData() === undefined`
|
|
36
|
+
* (typically a loading state or fallback); once loaders resolve, the
|
|
37
|
+
* reactive `useLoaderData` re-renders with the data. This matches
|
|
38
|
+
* standard SPA loading behavior.
|
|
39
|
+
*
|
|
40
|
+
* Without this wiring, direct URL navigation to a loader-backed route
|
|
41
|
+
* (e.g. `/posts/3`) showed the "Post not found" fallback indefinitely
|
|
42
|
+
* because `useLoaderData()` returned `undefined` forever. The router
|
|
43
|
+
* only ran loaders on in-app navigation (push/replace), not on initial
|
|
44
|
+
* mount.
|
|
45
|
+
*
|
|
19
46
|
* @example
|
|
20
47
|
* import { routes } from "virtual:zero/routes"
|
|
21
48
|
* import { startClient } from "@pyreon/zero/client"
|
|
@@ -23,21 +50,72 @@ export interface StartClientOptions {
|
|
|
23
50
|
* startClient({ routes })
|
|
24
51
|
*/
|
|
25
52
|
export function startClient(options: StartClientOptions) {
|
|
53
|
+
// `startClient` is the browser entry point — only ever called from a
|
|
54
|
+
// user's `client.ts` mounted in the browser. Explicit guard documents
|
|
55
|
+
// that contract and gives a clearer error than `document is not defined`.
|
|
56
|
+
if (typeof document === 'undefined') {
|
|
57
|
+
throw new Error('[Pyreon] startClient() can only be called in the browser.')
|
|
58
|
+
}
|
|
26
59
|
const container = document.getElementById('app')
|
|
27
|
-
if (!container) throw new Error('[
|
|
60
|
+
if (!container) throw new Error('[Pyreon] Missing #app container element')
|
|
28
61
|
|
|
29
|
-
const { App } = createApp({
|
|
62
|
+
const { App, router } = createApp({
|
|
30
63
|
routes: options.routes,
|
|
31
64
|
routerMode: 'history',
|
|
32
65
|
...(options.layout ? { layout: options.layout } : {}),
|
|
33
66
|
})
|
|
34
67
|
|
|
68
|
+
// ── Loader data hydration (SSR path) ───────────────────────────────────────
|
|
69
|
+
// If the server embedded loader data, hydrate it BEFORE mounting so the
|
|
70
|
+
// initial render sees the same data the SSR pass produced. This avoids
|
|
71
|
+
// hydration mismatches and eliminates the flash-of-fallback.
|
|
72
|
+
const ssrLoaderData = (window as unknown as Record<string, unknown>)
|
|
73
|
+
.__PYREON_LOADER_DATA__
|
|
74
|
+
const hasSSRLoaderData =
|
|
75
|
+
ssrLoaderData !== undefined &&
|
|
76
|
+
typeof ssrLoaderData === 'object' &&
|
|
77
|
+
ssrLoaderData !== null
|
|
78
|
+
if (hasSSRLoaderData) {
|
|
79
|
+
// `router` is the public Router<> type; hydrateLoaderData uses the
|
|
80
|
+
// internal RouterInstance shape. The cast is safe because they're
|
|
81
|
+
// the same object at runtime — just narrower/wider type views.
|
|
82
|
+
hydrateLoaderData(router as never, ssrLoaderData as Record<string, unknown>)
|
|
83
|
+
}
|
|
84
|
+
|
|
35
85
|
const vnode = h(App, null)
|
|
36
86
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
87
|
+
// ── Mount vs hydrate ───────────────────────────────────────────────────────
|
|
88
|
+
const hasSSRContent = container.childNodes.length > 0
|
|
89
|
+
const cleanup = hasSSRContent ? hydrateRoot(container, vnode) : mount(vnode, container)
|
|
90
|
+
|
|
91
|
+
// ── Loader run (SPA cold-start path) ───────────────────────────────────────
|
|
92
|
+
// If we had no SSR loader data AND no SSR content, this is a true SPA
|
|
93
|
+
// cold start. Trigger the router's loader pipeline for the current route
|
|
94
|
+
// via `replace()` with the same path — doesn't change the URL, just kicks
|
|
95
|
+
// off the loader batch. Guards, middleware, and redirects run too, which
|
|
96
|
+
// matches what any other route navigation would do.
|
|
97
|
+
//
|
|
98
|
+
// If we DID have SSR content but NO loader data — that's an unusual case
|
|
99
|
+
// (SSR disabled for this route but loader defined). Run loaders anyway so
|
|
100
|
+
// the client catches up.
|
|
101
|
+
if (!hasSSRLoaderData) {
|
|
102
|
+
const currentPath = router.currentRoute().path
|
|
103
|
+
router.replace(currentPath).catch((err: unknown) => {
|
|
104
|
+
// Loader failures are already reported via the route's error handling
|
|
105
|
+
// pipeline. We swallow the promise rejection here to prevent unhandled
|
|
106
|
+
// rejection warnings — the route's `errorComponent` (if any) already
|
|
107
|
+
// handled the display.
|
|
108
|
+
// @ts-ignore — `import.meta.env.DEV` is provided by Vite/Rolldown at build time
|
|
109
|
+
if (import.meta.env?.DEV === true) {
|
|
110
|
+
// oxlint-disable-next-line no-console
|
|
111
|
+
console.warn(
|
|
112
|
+
'[Pyreon] Initial loader run failed for route:',
|
|
113
|
+
currentPath,
|
|
114
|
+
err,
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
})
|
|
40
118
|
}
|
|
41
119
|
|
|
42
|
-
return
|
|
120
|
+
return cleanup
|
|
43
121
|
}
|
package/src/env.ts
CHANGED
|
@@ -160,7 +160,7 @@ export function oneOf<T extends string>(
|
|
|
160
160
|
class EnvError extends Error {
|
|
161
161
|
constructor(key: string, message: string, description?: string) {
|
|
162
162
|
const desc = description ? ` (${description})` : ''
|
|
163
|
-
super(`[
|
|
163
|
+
super(`[Pyreon] ${key}${desc}: ${message}`)
|
|
164
164
|
this.name = 'EnvError'
|
|
165
165
|
}
|
|
166
166
|
}
|
|
@@ -193,7 +193,7 @@ function toValidator(value: unknown): EnvValidator<unknown> {
|
|
|
193
193
|
if (typeof value === 'boolean') return bool({ default: value })
|
|
194
194
|
if (typeof value === 'string') return str({ default: value })
|
|
195
195
|
|
|
196
|
-
throw new Error(`[
|
|
196
|
+
throw new Error(`[Pyreon] Invalid schema value: ${String(value)}. Use a default value, String/Number/Boolean, or a validator like url().`)
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
// ─── Type inference ─────────────────────────────────────────────────────────
|
|
@@ -261,8 +261,8 @@ export function validateEnv<T extends Record<string, SchemaEntry>>(
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
if (errors.length > 0) {
|
|
264
|
-
const header = `\n[
|
|
265
|
-
const body = errors.map((e) => ` ✗ ${e.replace('[
|
|
264
|
+
const header = `\n[Pyreon] Environment validation failed (${errors.length} error${errors.length > 1 ? 's' : ''}):\n`
|
|
265
|
+
const body = errors.map((e) => ` ✗ ${e.replace('[Pyreon] ', '')}`).join('\n')
|
|
266
266
|
throw new Error(header + body + '\n')
|
|
267
267
|
}
|
|
268
268
|
|
|
@@ -331,13 +331,13 @@ export function schema<T>(parse: (raw: string) => T): EnvValidator<T> {
|
|
|
331
331
|
defaultValue: undefined,
|
|
332
332
|
parse(raw: string | undefined, key: string) {
|
|
333
333
|
if (raw === undefined || raw === '') {
|
|
334
|
-
throw new Error(`[
|
|
334
|
+
throw new Error(`[Pyreon] ${key}: is required but not set`)
|
|
335
335
|
}
|
|
336
336
|
try {
|
|
337
337
|
return parse(raw)
|
|
338
338
|
} catch (e) {
|
|
339
339
|
const msg = e instanceof Error ? e.message : String(e)
|
|
340
|
-
throw new Error(`[
|
|
340
|
+
throw new Error(`[Pyreon] ${key}: ${msg}`)
|
|
341
341
|
}
|
|
342
342
|
},
|
|
343
343
|
}
|
package/src/favicon.ts
CHANGED
|
@@ -9,7 +9,7 @@ function warnSharpMissing() {
|
|
|
9
9
|
sharpWarned = true
|
|
10
10
|
// oxlint-disable-next-line no-console
|
|
11
11
|
console.warn(
|
|
12
|
-
'\n[
|
|
12
|
+
'\n[Pyreon] sharp not installed — favicons will not be generated. Install for full support: bun add -D sharp\n',
|
|
13
13
|
)
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -25,7 +25,7 @@ function warnSharpMissing() {
|
|
|
25
25
|
//
|
|
26
26
|
// Usage:
|
|
27
27
|
// import { faviconPlugin } from "@pyreon/zero"
|
|
28
|
-
// export default { plugins: [
|
|
28
|
+
// export default { plugins: [Pyreon] }
|
|
29
29
|
|
|
30
30
|
export interface FaviconLocaleConfig {
|
|
31
31
|
/** Locale-specific source icon (SVG or PNG). */
|
|
@@ -404,7 +404,7 @@ async function generateFaviconSet(
|
|
|
404
404
|
const sourcePath = join(rootDir, source)
|
|
405
405
|
if (!existsSync(sourcePath)) {
|
|
406
406
|
// oxlint-disable-next-line no-console
|
|
407
|
-
console.warn(`[
|
|
407
|
+
console.warn(`[Pyreon] Source not found: ${sourcePath}`)
|
|
408
408
|
return
|
|
409
409
|
}
|
|
410
410
|
|
package/src/font.ts
CHANGED
|
@@ -282,7 +282,7 @@ async function downloadGoogleFontsCSS(url: string): Promise<string> {
|
|
|
282
282
|
},
|
|
283
283
|
})
|
|
284
284
|
if (!response.ok) {
|
|
285
|
-
throw new Error(`Failed to fetch Google Fonts CSS: ${response.status}`)
|
|
285
|
+
throw new Error(`[Pyreon] Failed to fetch Google Fonts CSS: ${response.status}`)
|
|
286
286
|
}
|
|
287
287
|
return response.text()
|
|
288
288
|
}
|
|
@@ -292,7 +292,7 @@ async function downloadGoogleFontsCSS(url: string): Promise<string> {
|
|
|
292
292
|
*/
|
|
293
293
|
async function downloadFontFile(url: string): Promise<Buffer> {
|
|
294
294
|
const response = await fetch(url)
|
|
295
|
-
if (!response.ok) throw new Error(`Failed to download font: ${url}`)
|
|
295
|
+
if (!response.ok) throw new Error(`[Pyreon] Failed to download font: ${url}`)
|
|
296
296
|
const arrayBuffer = await response.arrayBuffer()
|
|
297
297
|
return Buffer.from(arrayBuffer)
|
|
298
298
|
}
|