@pyreon/zero 0.11.5 → 0.11.6

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/src/theme.tsx CHANGED
@@ -1,6 +1,6 @@
1
- import type { VNodeChild } from "@pyreon/core"
2
- import { onMount, onUnmount } from "@pyreon/core"
3
- import { effect, signal } from "@pyreon/reactivity"
1
+ import type { VNodeChild } from '@pyreon/core'
2
+ import { onMount, onUnmount } from '@pyreon/core'
3
+ import { effect, signal } from '@pyreon/reactivity'
4
4
 
5
5
  // ─── Theme system ───────────────────────────────────────────────────────────
6
6
  //
@@ -10,19 +10,19 @@ import { effect, signal } from "@pyreon/reactivity"
10
10
  // - No flash of wrong theme (inline script in HTML)
11
11
  // - Reactive theme signal for components
12
12
 
13
- export type Theme = "light" | "dark" | "system"
13
+ export type Theme = 'light' | 'dark' | 'system'
14
14
 
15
- const STORAGE_KEY = "zero-theme"
15
+ const STORAGE_KEY = 'zero-theme'
16
16
 
17
17
  /** Reactive theme signal. */
18
- export const theme = signal<Theme>("system")
18
+ export const theme = signal<Theme>('system')
19
19
 
20
20
  /** Computed resolved theme (what's actually applied). */
21
- export function resolvedTheme(): "light" | "dark" {
21
+ export function resolvedTheme(): 'light' | 'dark' {
22
22
  const t = theme()
23
- if (t === "system") {
24
- if (typeof window === "undefined") return "dark"
25
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
23
+ if (t === 'system') {
24
+ if (typeof window === 'undefined') return 'dark'
25
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
26
26
  }
27
27
  return t
28
28
  }
@@ -30,13 +30,13 @@ export function resolvedTheme(): "light" | "dark" {
30
30
  /** Toggle between light and dark. */
31
31
  export function toggleTheme() {
32
32
  const current = resolvedTheme()
33
- setTheme(current === "dark" ? "light" : "dark")
33
+ setTheme(current === 'dark' ? 'light' : 'dark')
34
34
  }
35
35
 
36
36
  /** Set theme explicitly. */
37
37
  export function setTheme(t: Theme) {
38
38
  theme.set(t)
39
- if (typeof document !== "undefined") {
39
+ if (typeof document !== 'undefined') {
40
40
  document.documentElement.dataset.theme = resolvedTheme()
41
41
  try {
42
42
  localStorage.setItem(STORAGE_KEY, t)
@@ -55,7 +55,7 @@ export function initTheme() {
55
55
  // Read persisted preference
56
56
  try {
57
57
  const stored = localStorage.getItem(STORAGE_KEY) as Theme | null
58
- if (stored === "light" || stored === "dark" || stored === "system") {
58
+ if (stored === 'light' || stored === 'dark' || stored === 'system') {
59
59
  theme.set(stored)
60
60
  }
61
61
  } catch {
@@ -66,14 +66,14 @@ export function initTheme() {
66
66
  document.documentElement.dataset.theme = resolvedTheme()
67
67
 
68
68
  // Watch for system preference changes
69
- const mq = window.matchMedia("(prefers-color-scheme: dark)")
69
+ const mq = window.matchMedia('(prefers-color-scheme: dark)')
70
70
  function onChange() {
71
- if (theme() === "system") {
71
+ if (theme() === 'system') {
72
72
  document.documentElement.dataset.theme = resolvedTheme()
73
73
  }
74
74
  }
75
- mq.addEventListener("change", onChange)
76
- onUnmount(() => mq.removeEventListener("change", onChange))
75
+ mq.addEventListener('change', onChange)
76
+ onUnmount(() => mq.removeEventListener('change', onChange))
77
77
 
78
78
  // Re-apply when theme signal changes
79
79
  const dispose = effect(() => {
@@ -105,7 +105,7 @@ export function ThemeToggle(props: { class?: string; style?: string }): VNodeChi
105
105
  type="button"
106
106
  >
107
107
  {() =>
108
- resolvedTheme() === "dark" ? (
108
+ resolvedTheme() === 'dark' ? (
109
109
  <svg
110
110
  width="18"
111
111
  height="18"
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { ComponentFn } from "@pyreon/core"
2
- import type { NavigationGuard } from "@pyreon/router"
3
- import type { Middleware } from "@pyreon/server"
1
+ import type { ComponentFn } from '@pyreon/core'
2
+ import type { NavigationGuard } from '@pyreon/router'
3
+ import type { Middleware } from '@pyreon/server'
4
4
 
5
5
  // ─── Route module conventions ────────────────────────────────────────────────
6
6
 
@@ -43,7 +43,7 @@ export interface RouteMeta {
43
43
 
44
44
  // ─── Rendering modes ─────────────────────────────────────────────────────────
45
45
 
46
- export type RenderMode = "ssr" | "ssg" | "spa" | "isr"
46
+ export type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr'
47
47
 
48
48
  export interface ISRConfig {
49
49
  /** Revalidation interval in seconds. */
@@ -62,7 +62,7 @@ export interface ZeroConfig {
62
62
  /** SSR options. */
63
63
  ssr?: {
64
64
  /** Streaming mode. Default: "string" */
65
- mode?: "string" | "stream"
65
+ mode?: 'string' | 'stream'
66
66
  }
67
67
 
68
68
  /** SSG options — only used when mode is "ssg". */
@@ -75,7 +75,7 @@ export interface ZeroConfig {
75
75
  isr?: ISRConfig
76
76
 
77
77
  /** Deploy adapter. Default: "node" */
78
- adapter?: "node" | "bun" | "static"
78
+ adapter?: 'node' | 'bun' | 'static'
79
79
 
80
80
  /** Base URL path. Default: "/" */
81
81
  base?: string
@@ -1,4 +1,4 @@
1
- import { onMount, onUnmount } from "@pyreon/core"
1
+ import { onMount, onUnmount } from '@pyreon/core'
2
2
 
3
3
  /**
4
4
  * Observes an element and calls `onIntersect` once it enters the viewport.
@@ -11,7 +11,7 @@ import { onMount, onUnmount } from "@pyreon/core"
11
11
  export function useIntersectionObserver(
12
12
  getElement: () => HTMLElement | undefined,
13
13
  onIntersect: () => void,
14
- rootMargin = "200px",
14
+ rootMargin = '200px',
15
15
  ) {
16
16
  onMount(() => {
17
17
  const el = getElement()
@@ -1,17 +1,17 @@
1
- import type { Plugin } from "vite"
2
- import { generateApiRouteModule } from "./api-routes"
3
- import { resolveConfig } from "./config"
4
- import { renderErrorOverlay } from "./error-overlay"
5
- import { generateMiddlewareModule, generateRouteModule, scanRouteFiles } from "./fs-router"
6
- import type { ZeroConfig } from "./types"
7
-
8
- const VIRTUAL_ROUTES_ID = "virtual:zero/routes"
1
+ import type { Plugin } from 'vite'
2
+ import { generateApiRouteModule } from './api-routes'
3
+ import { resolveConfig } from './config'
4
+ import { renderErrorOverlay } from './error-overlay'
5
+ import { generateMiddlewareModule, generateRouteModule, scanRouteFiles } from './fs-router'
6
+ import type { ZeroConfig } from './types'
7
+
8
+ const VIRTUAL_ROUTES_ID = 'virtual:zero/routes'
9
9
  const RESOLVED_VIRTUAL_ROUTES_ID = `\0${VIRTUAL_ROUTES_ID}`
10
10
 
11
- const VIRTUAL_MIDDLEWARE_ID = "virtual:zero/route-middleware"
11
+ const VIRTUAL_MIDDLEWARE_ID = 'virtual:zero/route-middleware'
12
12
  const RESOLVED_VIRTUAL_MIDDLEWARE_ID = `\0${VIRTUAL_MIDDLEWARE_ID}`
13
13
 
14
- const VIRTUAL_API_ROUTES_ID = "virtual:zero/api-routes"
14
+ const VIRTUAL_API_ROUTES_ID = 'virtual:zero/api-routes'
15
15
  const RESOLVED_VIRTUAL_API_ROUTES_ID = `\0${VIRTUAL_API_ROUTES_ID}`
16
16
 
17
17
  /**
@@ -33,8 +33,8 @@ export function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {
33
33
  let root: string
34
34
 
35
35
  const plugin: Plugin & { _zeroConfig: ZeroConfig } = {
36
- name: "pyreon-zero",
37
- enforce: "pre",
36
+ name: 'pyreon-zero',
37
+ enforce: 'pre',
38
38
  _zeroConfig: userConfig,
39
39
 
40
40
  configResolved(resolvedConfig) {
@@ -82,8 +82,8 @@ export function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {
82
82
  // This runs as a late middleware (return function) so it wraps
83
83
  // Vite's own SSR handling and catches rendering failures.
84
84
  server.middlewares.use((req, res, next) => {
85
- const accept = req.headers.accept ?? ""
86
- if (!accept.includes("text/html")) return next()
85
+ const accept = req.headers.accept ?? ''
86
+ if (!accept.includes('text/html')) return next()
87
87
 
88
88
  // Monkey-patch res.end to catch errors from SSR rendering
89
89
  const originalEnd = res.end.bind(res)
@@ -96,12 +96,12 @@ export function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {
96
96
  server.ssrFixStacktrace(error)
97
97
  const html = renderErrorOverlay(error)
98
98
  res.statusCode = 500
99
- res.setHeader("Content-Type", "text/html; charset=utf-8")
100
- res.setHeader("Content-Length", Buffer.byteLength(html))
99
+ res.setHeader('Content-Type', 'text/html; charset=utf-8')
100
+ res.setHeader('Content-Length', Buffer.byteLength(html))
101
101
  originalEnd(html)
102
102
  }
103
103
 
104
- res.on("error", handleError)
104
+ res.on('error', handleError)
105
105
 
106
106
  // Wrap next() in try/catch to handle synchronous errors
107
107
  try {
@@ -115,8 +115,8 @@ export function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {
115
115
  server.watcher.add(`${routesDir}/**/*.{tsx,jsx,ts,js}`)
116
116
 
117
117
  // Invalidate virtual modules when route files change
118
- server.watcher.on("all", (event, path) => {
119
- if (path.startsWith(routesDir) && (event === "add" || event === "unlink")) {
118
+ server.watcher.on('all', (event, path) => {
119
+ if (path.startsWith(routesDir) && (event === 'add' || event === 'unlink')) {
120
120
  for (const resolvedId of [
121
121
  RESOLVED_VIRTUAL_ROUTES_ID,
122
122
  RESOLVED_VIRTUAL_MIDDLEWARE_ID,
@@ -125,7 +125,7 @@ export function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {
125
125
  const mod = server.moduleGraph.getModuleById(resolvedId)
126
126
  if (mod) server.moduleGraph.invalidateModule(mod)
127
127
  }
128
- server.ws.send({ type: "full-reload" })
128
+ server.ws.send({ type: 'full-reload' })
129
129
  }
130
130
  })
131
131
  },
@@ -133,7 +133,7 @@ export function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {
133
133
  config() {
134
134
  return {
135
135
  resolve: {
136
- conditions: ["bun"],
136
+ conditions: ['bun'],
137
137
  },
138
138
  server: {
139
139
  port: config.port,