toiljs 0.0.7 → 0.0.9

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 (135) hide show
  1. package/build/backend/.tsbuildinfo +1 -1
  2. package/build/cli/.tsbuildinfo +1 -1
  3. package/build/cli/configure.d.ts +1 -0
  4. package/build/cli/configure.js +85 -20
  5. package/build/cli/create.d.ts +1 -0
  6. package/build/cli/create.js +18 -7
  7. package/build/cli/features.d.ts +2 -0
  8. package/build/cli/features.js +22 -0
  9. package/build/cli/index.js +8 -0
  10. package/build/client/.tsbuildinfo +1 -1
  11. package/build/client/components/Form.d.ts +12 -0
  12. package/build/client/components/Form.js +23 -0
  13. package/build/client/components/Image.d.ts +13 -0
  14. package/build/client/components/Image.js +22 -0
  15. package/build/client/components/Script.d.ts +13 -0
  16. package/build/client/components/Script.js +68 -0
  17. package/build/client/components/Slot.d.ts +6 -0
  18. package/build/client/components/Slot.js +6 -0
  19. package/build/client/dev/error-overlay.d.ts +20 -0
  20. package/build/client/dev/error-overlay.js +123 -0
  21. package/build/client/head/head.d.ts +2 -0
  22. package/build/client/head/head.js +17 -2
  23. package/build/client/head/metadata.d.ts +29 -0
  24. package/build/client/head/metadata.js +38 -0
  25. package/build/client/index.d.ts +15 -3
  26. package/build/client/index.js +8 -2
  27. package/build/client/navigation/navigation.d.ts +3 -0
  28. package/build/client/navigation/navigation.js +42 -1
  29. package/build/client/routing/Router.d.ts +1 -0
  30. package/build/client/routing/Router.js +56 -34
  31. package/build/client/routing/action.d.ts +17 -0
  32. package/build/client/routing/action.js +55 -0
  33. package/build/client/routing/hooks.d.ts +1 -0
  34. package/build/client/routing/hooks.js +6 -7
  35. package/build/client/routing/loader.d.ts +10 -2
  36. package/build/client/routing/loader.js +83 -24
  37. package/build/client/routing/mount.d.ts +1 -1
  38. package/build/client/routing/mount.js +12 -4
  39. package/build/client/routing/slot-context.d.ts +2 -0
  40. package/build/client/routing/slot-context.js +2 -0
  41. package/build/client/types.d.ts +1 -0
  42. package/build/compiler/.tsbuildinfo +1 -1
  43. package/build/compiler/config.d.ts +10 -0
  44. package/build/compiler/config.js +5 -1
  45. package/build/compiler/docs.js +26 -26
  46. package/build/compiler/fonts.d.ts +4 -0
  47. package/build/compiler/fonts.js +64 -0
  48. package/build/compiler/generate.js +67 -32
  49. package/build/compiler/image-report.d.ts +2 -0
  50. package/build/compiler/image-report.js +62 -0
  51. package/build/compiler/plugin.js +1 -1
  52. package/build/compiler/prerender.d.ts +7 -0
  53. package/build/compiler/prerender.js +111 -0
  54. package/build/compiler/routes.d.ts +3 -0
  55. package/build/compiler/routes.js +50 -5
  56. package/build/compiler/seo.d.ts +70 -0
  57. package/build/compiler/seo.js +221 -0
  58. package/build/compiler/vite.js +13 -1
  59. package/build/io/.tsbuildinfo +1 -1
  60. package/build/shared/.tsbuildinfo +1 -1
  61. package/examples/basic/client/404.tsx +1 -1
  62. package/examples/basic/client/components/Header.tsx +38 -0
  63. package/examples/basic/client/components/HoneycombBackground.tsx +86 -18
  64. package/examples/basic/client/global-error.tsx +3 -3
  65. package/examples/basic/client/layout.tsx +2 -33
  66. package/examples/basic/client/public/images/test_image.webp +0 -0
  67. package/examples/basic/client/routes/about.tsx +8 -0
  68. package/examples/basic/client/routes/get-started.tsx +1 -1
  69. package/examples/basic/client/routes/index.tsx +8 -1
  70. package/examples/basic/client/routes/io.tsx +1 -1
  71. package/examples/basic/client/routes/loader-demo/index.tsx +29 -1
  72. package/examples/basic/client/routes/test.tsx +8 -0
  73. package/examples/basic/client/styles/main.css +48 -1
  74. package/package.json +8 -6
  75. package/presets/eslint.js +7 -4
  76. package/presets/tsconfig.json +1 -1
  77. package/src/backend/index.ts +1 -1
  78. package/src/cli/configure.ts +102 -21
  79. package/src/cli/create.ts +25 -9
  80. package/src/cli/features.ts +33 -1
  81. package/src/cli/index.ts +10 -1
  82. package/src/cli/ui.ts +1 -1
  83. package/src/cli/validate.ts +1 -1
  84. package/src/client/components/Form.tsx +65 -0
  85. package/src/client/components/Image.tsx +89 -0
  86. package/src/client/components/Script.tsx +113 -0
  87. package/src/client/components/Slot.tsx +21 -0
  88. package/src/client/dev/error-overlay.tsx +197 -0
  89. package/src/client/head/head.ts +28 -3
  90. package/src/client/head/metadata.ts +92 -0
  91. package/src/client/index.ts +20 -3
  92. package/src/client/navigation/Link.tsx +1 -1
  93. package/src/client/navigation/navigation.ts +74 -4
  94. package/src/client/navigation/prefetch.ts +2 -2
  95. package/src/client/routing/Router.tsx +128 -62
  96. package/src/client/routing/action.ts +122 -0
  97. package/src/client/routing/error-boundary.tsx +1 -1
  98. package/src/client/routing/hooks.ts +17 -23
  99. package/src/client/routing/loader.ts +158 -35
  100. package/src/client/routing/mount.tsx +25 -3
  101. package/src/client/routing/slot-context.ts +7 -0
  102. package/src/client/types.ts +6 -4
  103. package/src/compiler/config.ts +40 -3
  104. package/src/compiler/docs.ts +26 -26
  105. package/src/compiler/fonts.ts +87 -0
  106. package/src/compiler/generate.ts +69 -31
  107. package/src/compiler/image-report.ts +85 -0
  108. package/src/compiler/plugin.ts +2 -2
  109. package/src/compiler/prerender.ts +130 -0
  110. package/src/compiler/routes.ts +62 -7
  111. package/src/compiler/seo.ts +356 -0
  112. package/src/compiler/vite.ts +21 -4
  113. package/src/io/FastSet.ts +1 -1
  114. package/src/io/index.ts +1 -1
  115. package/src/io/types.ts +1 -1
  116. package/src/server/index.ts +1 -1
  117. package/src/server/main.ts +1 -1
  118. package/src/shared/index.ts +1 -1
  119. package/test/dom/Image.test.tsx +46 -0
  120. package/test/dom/Script.test.tsx +45 -0
  121. package/test/dom/action.test.tsx +129 -0
  122. package/test/dom/error-overlay.test.tsx +44 -0
  123. package/test/dom/loader.test.tsx +121 -0
  124. package/test/dom/revalidate.test.tsx +38 -0
  125. package/test/dom/route-head.test.tsx +34 -0
  126. package/test/dom/router-loading.test.tsx +44 -0
  127. package/test/dom/slot.test.tsx +109 -0
  128. package/test/dom/view-transitions.test.tsx +51 -0
  129. package/test/features.test.ts +31 -0
  130. package/test/fonts.test.ts +26 -0
  131. package/test/metadata.test.ts +41 -0
  132. package/test/prerender.test.ts +46 -0
  133. package/test/routes.test.ts +20 -1
  134. package/test/seo.test.ts +142 -0
  135. package/examples/basic/client/template.tsx +0 -7
@@ -4,6 +4,7 @@ const HEX_R = 34;
4
4
  const GAP = 3;
5
5
  const DRAW_R = HEX_R - GAP;
6
6
  const GLOW_DIST = 140;
7
+ const LOGO_SRC = '/images/logo.svg';
7
8
 
8
9
  function tracePath(ctx: CanvasRenderingContext2D, cx: number, cy: number, r: number) {
9
10
  ctx.beginPath();
@@ -43,6 +44,41 @@ function buildGrid(w: number, h: number): Array<{ x: number; y: number }> {
43
44
  return hexes;
44
45
  }
45
46
 
47
+ /** Samples logo colours per hex centre for use in border glow. */
48
+ function buildLogoColors(
49
+ img: HTMLImageElement,
50
+ hexes: Array<{ x: number; y: number }>,
51
+ w: number,
52
+ h: number,
53
+ ): Array<[number, number, number]> | null {
54
+ const lc = document.createElement('canvas');
55
+
56
+ lc.width = w;
57
+ lc.height = h;
58
+
59
+ const lctx = lc.getContext('2d');
60
+
61
+ if (!lctx) return null;
62
+
63
+ // Draw logo large + blurred, roughly where the hero logo sits in the viewport
64
+ const size = 700;
65
+ const cx = w / 2;
66
+ const cy = h * 0.42;
67
+
68
+ lctx.filter = 'blur(90px)';
69
+ lctx.drawImage(img, cx - size / 2, cy - size / 2, size, size);
70
+ lctx.filter = 'none';
71
+
72
+ // Sample one pixel per hex centre so we can use logo colours for border glow
73
+ return hexes.map(({ x, y }) => {
74
+ const px = Math.round(Math.max(0, Math.min(w - 1, x)));
75
+ const py = Math.round(Math.max(0, Math.min(h - 1, y)));
76
+ const d = lctx.getImageData(px, py, 1, 1).data;
77
+
78
+ return [d[0], d[1], d[2]] as [number, number, number];
79
+ });
80
+ }
81
+
46
82
  export default function HoneycombBackground() {
47
83
  const canvasRef = useRef<HTMLCanvasElement>(null);
48
84
  const mouse = useRef({ x: -9999, y: -9999 });
@@ -58,19 +94,42 @@ export default function HoneycombBackground() {
58
94
 
59
95
  const dpr = window.devicePixelRatio || 1;
60
96
  let hexes: Array<{ x: number; y: number }> = [];
97
+ let hexColors: Array<[number, number, number]> = [];
61
98
  let raf: number;
62
99
 
100
+ const img = new Image();
101
+
102
+ img.onload = () => {
103
+ const colors = buildLogoColors(img, hexes, window.innerWidth, window.innerHeight);
104
+
105
+ if (colors) {
106
+ hexColors = colors;
107
+ }
108
+ };
109
+
110
+ img.src = LOGO_SRC;
111
+
63
112
  function resize() {
64
113
  if (!canvas || !ctx) return;
65
114
 
66
115
  const w = window.innerWidth;
67
116
  const h = window.innerHeight;
117
+
68
118
  canvas.width = w * dpr;
69
119
  canvas.height = h * dpr;
70
120
  canvas.style.width = `${w}px`;
71
121
  canvas.style.height = `${h}px`;
72
122
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
73
123
  hexes = buildGrid(w, h);
124
+
125
+ // Rebuild logo colours if image is already loaded
126
+ if (img.complete && img.naturalWidth > 0) {
127
+ const colors = buildLogoColors(img, hexes, w, h);
128
+
129
+ if (colors) {
130
+ hexColors = colors;
131
+ }
132
+ }
74
133
  }
75
134
 
76
135
  function draw() {
@@ -78,36 +137,53 @@ export default function HoneycombBackground() {
78
137
 
79
138
  const w = window.innerWidth;
80
139
  const h = window.innerHeight;
140
+
81
141
  ctx.clearRect(0, 0, w, h);
82
142
 
83
143
  const mx = mouse.current.x;
84
144
  const my = mouse.current.y;
85
145
 
86
- for (const { x, y } of hexes) {
146
+ for (let i = 0; i < hexes.length; i++) {
147
+ const hex = hexes[i];
148
+
149
+ if (!hex) continue;
150
+
151
+ const { x, y } = hex;
87
152
  const dist = Math.hypot(x - mx, y - my);
88
153
  const t = Math.max(0, 1 - dist / GLOW_DIST);
89
154
  const ease = t * t * (3 - 2 * t);
90
155
 
156
+ // Base fill
91
157
  tracePath(ctx, x, y, DRAW_R);
92
-
93
158
  ctx.fillStyle = 'rgba(255,255,255,0.018)';
94
159
  ctx.fill();
95
160
 
96
- if (ease > 0) {
97
- ctx.fillStyle = `rgba(72,148,255,${ease * 0.025})`;
98
- ctx.fill();
99
- }
100
161
 
162
+
163
+ // Base border
164
+ tracePath(ctx, x, y, DRAW_R);
101
165
  ctx.strokeStyle = 'rgba(255,255,255,0.055)';
102
166
  ctx.lineWidth = 1;
103
167
  ctx.stroke();
104
168
 
169
+ // Glow border using logo-sampled colour
105
170
  if (ease > 0) {
171
+ const col = hexColors[i];
172
+ const r = col ? col[0] : 120;
173
+ const g = col ? col[1] : 180;
174
+ const b = col ? col[2] : 255;
175
+
176
+ // If the logo has colour here, use it; otherwise fall back to a soft white
177
+ const bright = r + g + b;
178
+ const fr = bright > 30 ? r : 120;
179
+ const fg = bright > 30 ? g : 180;
180
+ const fb = bright > 30 ? b : 255;
181
+
106
182
  ctx.save();
107
- ctx.shadowColor = `rgba(72,148,255,${ease * 0.2})`;
183
+ ctx.shadowColor = `rgba(${fr},${fg},${fb},${ease * 0.25})`;
108
184
  ctx.shadowBlur = 8 * ease;
109
- ctx.strokeStyle = `rgba(120,180,255,${ease * 0.2})`;
110
- ctx.lineWidth = 1 + ease * 0.4;
185
+ ctx.strokeStyle = `rgba(${fr},${fg},${fb},${ease * 0.18})`;
186
+ ctx.lineWidth = 1 + ease * 0.5;
111
187
  ctx.stroke();
112
188
  ctx.restore();
113
189
  }
@@ -148,15 +224,7 @@ export default function HoneycombBackground() {
148
224
  return (
149
225
  <canvas
150
226
  ref={canvasRef}
151
- style={{
152
- position: 'fixed',
153
- inset: 0,
154
- width: '100%',
155
- height: '100%',
156
- pointerEvents: 'none',
157
- zIndex: 0,
158
- }}
227
+ className="honeycomb-canvas"
159
228
  />
160
229
  );
161
230
  }
162
-
@@ -1,10 +1,10 @@
1
1
  // Root error boundary. Unlike a route's `error.tsx`, this sits *outside* the root layout, so it
2
- // also catches errors thrown while rendering the layout itself the last line of defense.
2
+ // also catches errors thrown while rendering the layout itself, the last line of defense.
3
3
  export default function GlobalError({ error, reset }: Toil.RouteErrorProps) {
4
4
  return (
5
- <main style={{ padding: 48, fontFamily: 'system-ui', textAlign: 'center' }}>
5
+ <main className="global-error">
6
6
  <h1>Something went wrong</h1>
7
- <p style={{ opacity: 0.7 }}>{error.message}</p>
7
+ <p className="global-error-message">{error.message}</p>
8
8
  <button type="button" onClick={reset}>
9
9
  Try again
10
10
  </button>
@@ -1,13 +1,8 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import Footer from './components/Footer';
3
+ import Header from './components/Header';
3
4
  import HoneycombBackground from './components/HoneycombBackground';
4
5
 
5
- const GitHubIcon = () => (
6
- <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
7
- <path d="M12 2C6.477 2 2 6.477 2 12c0 4.418 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.009-.868-.013-1.703-2.782.604-3.369-1.341-3.369-1.341-.454-1.154-1.11-1.462-1.11-1.462-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0 1 12 6.836a9.59 9.59 0 0 1 2.504.337c1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.202 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.579.688.481C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z" />
8
- </svg>
9
- );
10
-
11
6
  export default function Layout({ children }: { children?: ReactNode }) {
12
7
  return (
13
8
  <div className="app">
@@ -17,33 +12,7 @@ export default function Layout({ children }: { children?: ReactNode }) {
17
12
  title="ToilJS"
18
13
  meta={[{ name: 'description', content: 'The most performant React framework.' }]}
19
14
  />
20
- <header className="nav">
21
- <Toil.Link href="/" className="nav-logo">
22
- <img src="images/logo.svg" alt="ToilJS" width={28} height={28} />
23
- <span>ToilJS</span>
24
- </Toil.Link>
25
-
26
- <nav className="nav-center">
27
- <Toil.NavLink href="/" end className="nav-center-link">
28
- Home
29
- </Toil.NavLink>
30
- <Toil.NavLink href="/get-started" className="nav-center-link">
31
- Get Started
32
- </Toil.NavLink>
33
- </nav>
34
-
35
- <nav className="nav-links">
36
- <Toil.Link href="https://toil.org/docs">Docs</Toil.Link>
37
- <a
38
- href="https://github.com/btc-vision/toiljs"
39
- target="_blank"
40
- rel="noopener noreferrer"
41
- className="nav-github">
42
- <GitHubIcon />
43
- GitHub
44
- </a>
45
- </nav>
46
- </header>
15
+ <Header />
47
16
 
48
17
  <main className="content">{children}</main>
49
18
 
@@ -1,3 +1,11 @@
1
+ // Declarative per-route SEO, resolved by the router into <title> + <meta>/<link> tags. The root
2
+ // layout's titleTemplate (if any) still applies; component-level useHead/<Head> can override.
3
+ export const metadata: Toil.Metadata = {
4
+ title: 'About',
5
+ description: 'About the ToilJS example app.',
6
+ openGraph: { title: 'About · ToilJS', type: 'website' },
7
+ };
8
+
1
9
  export default function About() {
2
10
  return (
3
11
  <main>
@@ -49,7 +49,7 @@ about.tsx → /about
49
49
  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
50
50
  </div>
51
51
  <h3>Entry Point</h3>
52
- <p><code>client/toil.tsx</code> is the app entry. Import global CSS and call <code>Toil.mount()</code> runs once on startup.</p>
52
+ <p><code>client/toil.tsx</code> is the app entry. Import global CSS and call <code>Toil.mount()</code>, runs once on startup.</p>
53
53
  </div>
54
54
 
55
55
  </div>
@@ -43,7 +43,14 @@ export default function Home() {
43
43
  <section className="hero">
44
44
  <div className="hero-logo">
45
45
  <img src="images/logo.svg" className="hero-logo-glow" alt="" aria-hidden="true" width={96} height={96} />
46
- <img src="images/logo.svg" className="hero-logo-img" alt="ToilJS" width={96} height={96} />
46
+ <Toil.Image
47
+ src="images/logo.svg"
48
+ className="hero-logo-img"
49
+ alt="ToilJS"
50
+ width={96}
51
+ height={96}
52
+ priority
53
+ />
47
54
  </div>
48
55
 
49
56
  <h1 className="hero-title">ToilJS</h1>
@@ -15,7 +15,7 @@ export default function IoDemo() {
15
15
  <main>
16
16
  <h1>Native IO</h1>
17
17
  <p>
18
- <code>new BinaryWriter()</code> with no import round-tripped {n} and &quot;{s}&quot; through{' '}
18
+ <code>new BinaryWriter()</code> with no import, round-tripped {n} and &quot;{s}&quot; through{' '}
19
19
  {bytes.length} bytes; FastSet size {seen.size}.
20
20
  </p>
21
21
  <Toil.Link href="/">Back home</Toil.Link>
@@ -7,8 +7,19 @@ export const loader = async ({ searchParams }: Toil.LoaderArgs) => {
7
7
  return { loadedAt: new Date().toISOString(), q: searchParams.get('q') };
8
8
  };
9
9
 
10
+ // Cache this route's data for 10s: revisiting within 10s is instant (no 2s wait); after that it
11
+ // refetches on navigation. Use `false` to cache forever, or omit for the default (refetch every nav).
12
+ export const revalidate: Toil.Revalidate = 10;
13
+
14
+ // Dynamic metadata derived from the loader's data (vs the static `metadata` export on /about).
15
+ export const generateMetadata: Toil.GenerateMetadata<Awaited<ReturnType<typeof loader>>> = ({
16
+ data,
17
+ }) => ({ title: `Loader demo, loaded ${data.loadedAt}` });
18
+
10
19
  export default function LoaderDemo() {
11
- const data = Toil.useLoaderData<{ loadedAt: string; q: string | null }>();
20
+ // Pass the loader to infer the data type from its return, no generics, no restating the shape.
21
+ const data = Toil.useLoaderData(loader);
22
+ const router = Toil.useRouter();
12
23
  return (
13
24
  <main>
14
25
  <h1>Loader demo</h1>
@@ -16,6 +27,23 @@ export default function LoaderDemo() {
16
27
  Data loaded before render (no <code>useEffect</code>): <code>{data.loadedAt}</code>
17
28
  {data.q !== null ? ` · q=${data.q}` : ''}
18
29
  </p>
30
+ <p>
31
+ <button type="button" onClick={() => { router.revalidate(); }}>
32
+ Revalidate (refetch)
33
+ </button>
34
+ </p>
35
+ {/* The write half: an action runs on submit, then revalidates this route's loader so
36
+ `loadedAt` above updates, read → write → revalidate, no manual refetch. */}
37
+ <Toil.Form action={async (form) => { await wait(500); console.log('saved', form.get('note')); }}>
38
+ {({ pending }) => (
39
+ <>
40
+ <input name="note" placeholder="Leave a note" disabled={pending} />
41
+ <button type="submit" disabled={pending}>
42
+ {pending ? 'Saving…' : 'Save & revalidate'}
43
+ </button>
44
+ </>
45
+ )}
46
+ </Toil.Form>
19
47
  <Toil.Link href="/">Back home</Toil.Link>
20
48
  </main>
21
49
  );
@@ -0,0 +1,8 @@
1
+ export default function TestPage() {
2
+ return (
3
+ <div className="test-page">
4
+ <img src="/images/test_image.webp" alt="Test" className="test-page-image" />
5
+ </div>
6
+ );
7
+ }
8
+
@@ -291,13 +291,26 @@ a:hover { color: var(--accent3); }
291
291
  background: #131d2e;
292
292
  }
293
293
 
294
+ /* ── Global Error ── */
295
+ .global-error {
296
+ padding: 3rem;
297
+ font-family: system-ui;
298
+ text-align: center;
299
+ }
300
+
301
+ .global-error-message {
302
+ opacity: 0.7;
303
+ }
304
+
294
305
  /* ── Footer ── */
295
306
  .footer {
296
- text-align: center;
297
307
  padding: 1.25rem;
298
308
  font-size: 0.82rem;
299
309
  color: var(--muted);
300
310
  border-top: 1px solid var(--border);
311
+ background: var(--bg);
312
+ position: relative;
313
+ z-index: 1;
301
314
  }
302
315
 
303
316
  /* ── Misc ── */
@@ -503,3 +516,37 @@ code {
503
516
  .spinner { animation-duration: 0s; }
504
517
  }
505
518
 
519
+ /* ── Global Error ── */
520
+ .global-error {
521
+ padding: 3rem;
522
+ font-family: system-ui;
523
+ text-align: center;
524
+ }
525
+
526
+ .global-error-message {
527
+ opacity: 0.7;
528
+ }
529
+
530
+ /* ── Test page ── */
531
+ .test-page {
532
+ display: flex;
533
+ justify-content: center;
534
+ align-items: center;
535
+ min-height: 100%;
536
+ }
537
+
538
+ .test-page-image {
539
+ max-width: 100%;
540
+ max-height: 80vh;
541
+ border-radius: 8px;
542
+ }
543
+
544
+ /* ── Honeycomb background ── */
545
+ .honeycomb-canvas {
546
+ position: fixed;
547
+ inset: 0;
548
+ width: 100%;
549
+ height: 100%;
550
+ pointer-events: none;
551
+ z-index: 0;
552
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "toiljs",
3
3
  "type": "module",
4
- "version": "0.0.7",
4
+ "version": "0.0.9",
5
5
  "author": "Dacely",
6
6
  "description": "todo",
7
7
  "engines": {
@@ -95,16 +95,18 @@
95
95
  "@btc-vision/as-loader": "^0.0.0",
96
96
  "@btc-vision/hyper-express": "^6.17.4",
97
97
  "@clack/prompts": "^1.5.0",
98
- "@eslint-react/eslint-plugin": "^4.2.3",
98
+ "@eslint-react/eslint-plugin": "^5.8.8",
99
99
  "@eslint/js": "^10.0.1",
100
100
  "@typescript-eslint/utils": "^8.60.0",
101
101
  "@vitejs/plugin-react": "^6.0.2",
102
- "eslint-plugin-react-hooks": "^7.1.0-canary-ab18f33d-20260220",
102
+ "eslint-plugin-react-hooks": "^7.1.1",
103
103
  "eslint-plugin-react-refresh": "^0.5.2",
104
104
  "picocolors": "^1.1.1",
105
+ "sharp": "^0.34.5",
105
106
  "toilscript": "^0.1.4",
106
107
  "typescript-eslint": "^8.60.0",
107
108
  "vite": "^8.0.14",
109
+ "vite-imagetools": "^10.0.0",
108
110
  "vite-plugin-node-polyfills": "^0.28.0"
109
111
  },
110
112
  "peerDependencies": {
@@ -131,10 +133,10 @@
131
133
  "@types/react-dom": "^19.2.3",
132
134
  "@vitest/coverage-v8": "^4.1.7",
133
135
  "@vitest/ui": "^4.1.7",
134
- "eslint": "^10.2.0",
135
- "jsdom": "^26.1.0",
136
+ "eslint": "^10.4.1",
137
+ "jsdom": "^29.1.1",
136
138
  "micromatch": "^4.0.8",
137
- "prettier": "^3.8.1",
139
+ "prettier": "^3.8.3",
138
140
  "react": "^19.2.6",
139
141
  "react-dom": "^19.2.6",
140
142
  "typedoc": "^0.28.19",
package/presets/eslint.js CHANGED
@@ -30,12 +30,15 @@ export default tseslint.config(
30
30
  },
31
31
  rules: {
32
32
  ...reactHooks.configs.recommended.rules,
33
- // Route files conventionally export a `loader` alongside the default component; the toil
34
- // compiler consumes it at runtime. Allow it (plus primitive constants) so Fast Refresh
35
- // doesn't flag the pattern.
33
+ // Route files conventionally export `loader` / `revalidate` / `metadata` /
34
+ // `generateMetadata` alongside the default component; the toil compiler consumes them at
35
+ // runtime. Allow them (plus primitive constants) so Fast Refresh doesn't flag the pattern.
36
36
  'react-refresh/only-export-components': [
37
37
  'warn',
38
- { allowConstantExport: true, allowExportNames: ['loader'] },
38
+ {
39
+ allowConstantExport: true,
40
+ allowExportNames: ['loader', 'revalidate', 'metadata', 'generateMetadata'],
41
+ },
39
42
  ],
40
43
  'no-undef': 'off',
41
44
  '@typescript-eslint/no-unused-vars': 'off',
@@ -1,4 +1,4 @@
1
- // toiljs shared client tsconfig opinionated, strict.
1
+ // toiljs shared client tsconfig, opinionated, strict.
2
2
  // Extend it from your project: { "extends": "toiljs/tsconfig", "include": ["client", ".toil"] }
3
3
  {
4
4
  "compilerOptions": {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * toiljs backend the self-host / dev server, built on @btc-vision/hyper-express (uWebSockets.js)
2
+ * toiljs backend, the self-host / dev server, built on @btc-vision/hyper-express (uWebSockets.js)
3
3
  * for very high throughput. It serves the built client (static assets + SPA fallback) and exposes
4
4
  * a WebSocket channel for realtime / live updates.
5
5
  *