toiljs 0.0.11 → 0.0.12
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/README.md +2 -0
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/configure.js +10 -4
- package/build/cli/create.js +58 -30
- package/build/cli/diagnostics.d.ts +55 -0
- package/build/cli/diagnostics.js +333 -0
- package/build/cli/doctor.d.ts +6 -0
- package/build/cli/doctor.js +249 -0
- package/build/cli/index.js +26 -0
- package/build/cli/proc.d.ts +5 -0
- package/build/cli/proc.js +20 -0
- package/build/cli/ui.d.ts +1 -0
- package/build/cli/ui.js +1 -0
- package/build/cli/update.d.ts +7 -0
- package/build/cli/update.js +117 -0
- package/build/cli/updates.d.ts +10 -0
- package/build/cli/updates.js +45 -0
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/dev/error-overlay.js +1 -1
- package/build/client/head/metadata.js +3 -1
- package/build/client/index.d.ts +5 -1
- package/build/client/index.js +2 -0
- package/build/client/navigation/navigation.js +1 -1
- package/build/client/routing/Router.js +2 -2
- package/build/client/search/search.d.ts +26 -0
- package/build/client/search/search.js +101 -0
- package/build/client/search/use-page-search.d.ts +8 -0
- package/build/client/search/use-page-search.js +21 -0
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/generate.js +26 -23
- package/build/compiler/index.d.ts +2 -0
- package/build/compiler/index.js +1 -0
- package/build/compiler/pages.d.ts +8 -0
- package/build/compiler/pages.js +37 -0
- package/build/compiler/plugin.js +3 -1
- package/build/compiler/prerender.d.ts +1 -0
- package/build/compiler/prerender.js +11 -5
- package/build/compiler/seo.js +10 -3
- package/build/io/.tsbuildinfo +1 -1
- package/examples/basic/client/components/Header.tsx +43 -41
- package/examples/basic/client/components/HoneycombBackground.tsx +223 -230
- package/examples/basic/client/public/index.html +18 -16
- package/examples/basic/client/routes/(legal)/privacy.tsx +18 -19
- package/examples/basic/client/routes/(legal)/terms.tsx +15 -16
- package/examples/basic/client/routes/about.tsx +21 -22
- package/examples/basic/client/routes/blog/[id].tsx +26 -18
- package/examples/basic/client/routes/features/actions.tsx +67 -67
- package/examples/basic/client/routes/features/error/index.tsx +27 -27
- package/examples/basic/client/routes/features/head.tsx +38 -38
- package/examples/basic/client/routes/features/index.tsx +83 -75
- package/examples/basic/client/routes/features/realtime.tsx +34 -32
- package/examples/basic/client/routes/features/script.tsx +31 -31
- package/examples/basic/client/routes/features/seo.tsx +39 -39
- package/examples/basic/client/routes/features/template/index.tsx +20 -20
- package/examples/basic/client/routes/features/template/template.tsx +16 -18
- package/examples/basic/client/routes/gallery/@modal/(.)photo/[id].tsx +23 -23
- package/examples/basic/client/routes/gallery/index.tsx +42 -42
- package/examples/basic/client/routes/gallery/photo/[id].tsx +18 -18
- package/examples/basic/client/routes/get-started.tsx +157 -84
- package/examples/basic/client/routes/index.tsx +137 -96
- package/examples/basic/client/routes/loader-demo/index.tsx +59 -52
- package/examples/basic/client/routes/search.tsx +61 -0
- package/examples/basic/client/routes/test.tsx +7 -8
- package/examples/basic/client/styles/main.css +624 -552
- package/package.json +2 -2
- package/presets/eslint.js +10 -3
- package/src/cli/configure.ts +363 -353
- package/src/cli/create.ts +563 -530
- package/src/cli/diagnostics.ts +421 -0
- package/src/cli/doctor.ts +318 -0
- package/src/cli/features.ts +166 -160
- package/src/cli/index.ts +242 -211
- package/src/cli/proc.ts +30 -0
- package/src/cli/ui.ts +111 -103
- package/src/cli/update.ts +150 -0
- package/src/cli/updates.ts +69 -0
- package/src/client/components/Image.tsx +91 -89
- package/src/client/dev/error-overlay.tsx +193 -197
- package/src/client/head/metadata.ts +94 -92
- package/src/client/index.ts +79 -64
- package/src/client/navigation/Link.tsx +94 -100
- package/src/client/navigation/navigation.ts +215 -218
- package/src/client/routing/Router.tsx +210 -193
- package/src/client/routing/hooks.ts +110 -114
- package/src/client/routing/lazy.ts +77 -81
- package/src/client/search/search.ts +189 -0
- package/src/client/search/use-page-search.ts +73 -0
- package/src/compiler/config.ts +173 -171
- package/src/compiler/fonts.ts +89 -87
- package/src/compiler/generate.ts +378 -373
- package/src/compiler/image-report.ts +88 -85
- package/src/compiler/index.ts +2 -0
- package/src/compiler/pages.ts +70 -0
- package/src/compiler/plugin.ts +51 -47
- package/src/compiler/prerender.ts +152 -130
- package/src/compiler/routes.ts +132 -131
- package/src/compiler/seo.ts +381 -356
- package/src/compiler/vite.ts +155 -145
- package/src/io/FastSet.ts +99 -96
- package/test/configure.test.ts +94 -90
- package/test/doctor.test.ts +140 -0
- package/test/dom/Image.test.tsx +73 -46
- package/test/dom/Script.test.tsx +48 -45
- package/test/dom/action.test.tsx +146 -129
- package/test/dom/error-overlay.test.tsx +44 -44
- package/test/dom/loader.test.tsx +2 -2
- package/test/dom/revalidate.test.tsx +1 -1
- package/test/dom/route-head.test.tsx +1 -2
- package/test/dom/slot.test.tsx +131 -109
- package/test/dom/view-transitions.test.tsx +53 -51
- package/test/features.test.ts +149 -142
- package/test/fonts.test.ts +28 -26
- package/test/head.test.ts +45 -35
- package/test/metadata.test.ts +42 -41
- package/test/pages.test.ts +105 -0
- package/test/prerender.test.ts +54 -46
- package/test/search.test.ts +114 -0
- package/test/seo.test.ts +164 -142
- package/test/update.test.ts +44 -0
|
@@ -1,230 +1,223 @@
|
|
|
1
|
-
import { useRef, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
const HEX_R = 34;
|
|
4
|
-
const GAP = 3;
|
|
5
|
-
const DRAW_R = HEX_R - GAP;
|
|
6
|
-
const GLOW_DIST = 140;
|
|
7
|
-
const LOGO_SRC = '/images/logo.svg';
|
|
8
|
-
|
|
9
|
-
function tracePath(ctx: CanvasRenderingContext2D, cx: number, cy: number, r: number) {
|
|
10
|
-
ctx.beginPath();
|
|
11
|
-
|
|
12
|
-
for (let i = 0; i < 6; i++) {
|
|
13
|
-
const a = (Math.PI / 3) * i - Math.PI / 6;
|
|
14
|
-
const x = cx + r * Math.cos(a);
|
|
15
|
-
const y = cy + r * Math.sin(a);
|
|
16
|
-
|
|
17
|
-
if (i === 0) {
|
|
18
|
-
ctx.moveTo(x, y);
|
|
19
|
-
} else {
|
|
20
|
-
ctx.lineTo(x, y);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
ctx.closePath();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function buildGrid(w: number, h: number): Array<{ x: number; y: number }> {
|
|
28
|
-
const colW = Math.sqrt(3) * HEX_R;
|
|
29
|
-
|
|
30
|
-
const rowH = HEX_R * 1.5;
|
|
31
|
-
const cols = Math.ceil(w / colW) + 2;
|
|
32
|
-
const rows = Math.ceil(h / rowH) + 2;
|
|
33
|
-
const hexes: Array<{ x: number; y: number }> = [];
|
|
34
|
-
|
|
35
|
-
for (let row = -1; row < rows; row++) {
|
|
36
|
-
for (let col = -1; col < cols; col++) {
|
|
37
|
-
hexes.push({
|
|
38
|
-
x: col * colW + (row % 2 !== 0 ? colW / 2 : 0),
|
|
39
|
-
y: row * rowH
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return hexes;
|
|
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
|
-
|
|
82
|
-
export default function HoneycombBackground() {
|
|
83
|
-
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
84
|
-
const mouse = useRef({ x: -9999, y: -9999 });
|
|
85
|
-
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
const canvas = canvasRef.current;
|
|
88
|
-
|
|
89
|
-
if (!canvas) return;
|
|
90
|
-
|
|
91
|
-
const ctx = canvas.getContext('2d');
|
|
92
|
-
|
|
93
|
-
if (!ctx) return;
|
|
94
|
-
|
|
95
|
-
const dpr = window.devicePixelRatio || 1;
|
|
96
|
-
let hexes: Array<{ x: number; y: number }> = [];
|
|
97
|
-
let hexColors: Array<[number, number, number]> = [];
|
|
98
|
-
let raf: number;
|
|
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
|
-
|
|
112
|
-
function resize() {
|
|
113
|
-
if (!canvas || !ctx) return;
|
|
114
|
-
|
|
115
|
-
const w = window.innerWidth;
|
|
116
|
-
const h = window.innerHeight;
|
|
117
|
-
|
|
118
|
-
canvas.width = w * dpr;
|
|
119
|
-
canvas.height = h * dpr;
|
|
120
|
-
canvas.style.width = `${w}px`;
|
|
121
|
-
canvas.style.height = `${h}px`;
|
|
122
|
-
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
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
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function draw() {
|
|
136
|
-
if (!ctx) return;
|
|
137
|
-
|
|
138
|
-
const w = window.innerWidth;
|
|
139
|
-
const h = window.innerHeight;
|
|
140
|
-
|
|
141
|
-
ctx.clearRect(0, 0, w, h);
|
|
142
|
-
|
|
143
|
-
const mx = mouse.current.x;
|
|
144
|
-
const my = mouse.current.y;
|
|
145
|
-
|
|
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;
|
|
152
|
-
const dist = Math.hypot(x - mx, y - my);
|
|
153
|
-
const t = Math.max(0, 1 - dist / GLOW_DIST);
|
|
154
|
-
const ease = t * t * (3 - 2 * t);
|
|
155
|
-
|
|
156
|
-
// Base fill
|
|
157
|
-
tracePath(ctx, x, y, DRAW_R);
|
|
158
|
-
ctx.fillStyle = 'rgba(255,255,255,0.018)';
|
|
159
|
-
ctx.fill();
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
ctx.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
ctx.
|
|
183
|
-
ctx.
|
|
184
|
-
ctx.
|
|
185
|
-
ctx.
|
|
186
|
-
ctx.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return (
|
|
225
|
-
<canvas
|
|
226
|
-
ref={canvasRef}
|
|
227
|
-
className="honeycomb-canvas"
|
|
228
|
-
/>
|
|
229
|
-
);
|
|
230
|
-
}
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const HEX_R = 34;
|
|
4
|
+
const GAP = 3;
|
|
5
|
+
const DRAW_R = HEX_R - GAP;
|
|
6
|
+
const GLOW_DIST = 140;
|
|
7
|
+
const LOGO_SRC = '/images/logo.svg';
|
|
8
|
+
|
|
9
|
+
function tracePath(ctx: CanvasRenderingContext2D, cx: number, cy: number, r: number) {
|
|
10
|
+
ctx.beginPath();
|
|
11
|
+
|
|
12
|
+
for (let i = 0; i < 6; i++) {
|
|
13
|
+
const a = (Math.PI / 3) * i - Math.PI / 6;
|
|
14
|
+
const x = cx + r * Math.cos(a);
|
|
15
|
+
const y = cy + r * Math.sin(a);
|
|
16
|
+
|
|
17
|
+
if (i === 0) {
|
|
18
|
+
ctx.moveTo(x, y);
|
|
19
|
+
} else {
|
|
20
|
+
ctx.lineTo(x, y);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ctx.closePath();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildGrid(w: number, h: number): Array<{ x: number; y: number }> {
|
|
28
|
+
const colW = Math.sqrt(3) * HEX_R;
|
|
29
|
+
|
|
30
|
+
const rowH = HEX_R * 1.5;
|
|
31
|
+
const cols = Math.ceil(w / colW) + 2;
|
|
32
|
+
const rows = Math.ceil(h / rowH) + 2;
|
|
33
|
+
const hexes: Array<{ x: number; y: number }> = [];
|
|
34
|
+
|
|
35
|
+
for (let row = -1; row < rows; row++) {
|
|
36
|
+
for (let col = -1; col < cols; col++) {
|
|
37
|
+
hexes.push({
|
|
38
|
+
x: col * colW + (row % 2 !== 0 ? colW / 2 : 0),
|
|
39
|
+
y: row * rowH
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return hexes;
|
|
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
|
+
|
|
82
|
+
export default function HoneycombBackground() {
|
|
83
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
84
|
+
const mouse = useRef({ x: -9999, y: -9999 });
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
const canvas = canvasRef.current;
|
|
88
|
+
|
|
89
|
+
if (!canvas) return;
|
|
90
|
+
|
|
91
|
+
const ctx = canvas.getContext('2d');
|
|
92
|
+
|
|
93
|
+
if (!ctx) return;
|
|
94
|
+
|
|
95
|
+
const dpr = window.devicePixelRatio || 1;
|
|
96
|
+
let hexes: Array<{ x: number; y: number }> = [];
|
|
97
|
+
let hexColors: Array<[number, number, number]> = [];
|
|
98
|
+
let raf: number;
|
|
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
|
+
|
|
112
|
+
function resize() {
|
|
113
|
+
if (!canvas || !ctx) return;
|
|
114
|
+
|
|
115
|
+
const w = window.innerWidth;
|
|
116
|
+
const h = window.innerHeight;
|
|
117
|
+
|
|
118
|
+
canvas.width = w * dpr;
|
|
119
|
+
canvas.height = h * dpr;
|
|
120
|
+
canvas.style.width = `${w}px`;
|
|
121
|
+
canvas.style.height = `${h}px`;
|
|
122
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
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
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function draw() {
|
|
136
|
+
if (!ctx) return;
|
|
137
|
+
|
|
138
|
+
const w = window.innerWidth;
|
|
139
|
+
const h = window.innerHeight;
|
|
140
|
+
|
|
141
|
+
ctx.clearRect(0, 0, w, h);
|
|
142
|
+
|
|
143
|
+
const mx = mouse.current.x;
|
|
144
|
+
const my = mouse.current.y;
|
|
145
|
+
|
|
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;
|
|
152
|
+
const dist = Math.hypot(x - mx, y - my);
|
|
153
|
+
const t = Math.max(0, 1 - dist / GLOW_DIST);
|
|
154
|
+
const ease = t * t * (3 - 2 * t);
|
|
155
|
+
|
|
156
|
+
// Base fill
|
|
157
|
+
tracePath(ctx, x, y, DRAW_R);
|
|
158
|
+
ctx.fillStyle = 'rgba(255,255,255,0.018)';
|
|
159
|
+
ctx.fill();
|
|
160
|
+
|
|
161
|
+
// Base border
|
|
162
|
+
tracePath(ctx, x, y, DRAW_R);
|
|
163
|
+
ctx.strokeStyle = 'rgba(255,255,255,0.055)';
|
|
164
|
+
ctx.lineWidth = 1;
|
|
165
|
+
ctx.stroke();
|
|
166
|
+
|
|
167
|
+
// Glow border using logo-sampled colour
|
|
168
|
+
if (ease > 0) {
|
|
169
|
+
const col = hexColors[i];
|
|
170
|
+
const r = col ? col[0] : 120;
|
|
171
|
+
const g = col ? col[1] : 180;
|
|
172
|
+
const b = col ? col[2] : 255;
|
|
173
|
+
|
|
174
|
+
// If the logo has colour here, use it; otherwise fall back to a soft white
|
|
175
|
+
const bright = r + g + b;
|
|
176
|
+
const fr = bright > 30 ? r : 120;
|
|
177
|
+
const fg = bright > 30 ? g : 180;
|
|
178
|
+
const fb = bright > 30 ? b : 255;
|
|
179
|
+
|
|
180
|
+
ctx.save();
|
|
181
|
+
ctx.shadowColor = `rgba(${fr},${fg},${fb},${ease * 0.25})`;
|
|
182
|
+
ctx.shadowBlur = 8 * ease;
|
|
183
|
+
ctx.strokeStyle = `rgba(${fr},${fg},${fb},${ease * 0.18})`;
|
|
184
|
+
ctx.lineWidth = 1 + ease * 0.5;
|
|
185
|
+
ctx.stroke();
|
|
186
|
+
ctx.restore();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
raf = requestAnimationFrame(draw);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
resize();
|
|
194
|
+
draw();
|
|
195
|
+
|
|
196
|
+
const onResize = () => {
|
|
197
|
+
cancelAnimationFrame(raf);
|
|
198
|
+
resize();
|
|
199
|
+
draw();
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const onMove = (e: MouseEvent) => {
|
|
203
|
+
mouse.current = { x: e.clientX, y: e.clientY };
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const onLeave = () => {
|
|
207
|
+
mouse.current = { x: -9999, y: -9999 };
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
window.addEventListener('resize', onResize);
|
|
211
|
+
window.addEventListener('mousemove', onMove);
|
|
212
|
+
document.addEventListener('mouseleave', onLeave);
|
|
213
|
+
|
|
214
|
+
return () => {
|
|
215
|
+
cancelAnimationFrame(raf);
|
|
216
|
+
window.removeEventListener('resize', onResize);
|
|
217
|
+
window.removeEventListener('mousemove', onMove);
|
|
218
|
+
document.removeEventListener('mouseleave', onLeave);
|
|
219
|
+
};
|
|
220
|
+
}, []);
|
|
221
|
+
|
|
222
|
+
return <canvas ref={canvasRef} className="honeycomb-canvas" />;
|
|
223
|
+
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<meta name="theme-color" content="#080D11" />
|
|
7
|
-
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
8
|
-
<title>ToilJS</title>
|
|
9
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
10
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
11
|
-
<link
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="theme-color" content="#080D11" />
|
|
7
|
+
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
8
|
+
<title>ToilJS</title>
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
11
|
+
<link
|
|
12
|
+
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@700;800;900&display=swap"
|
|
13
|
+
rel="stylesheet" />
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div id="root"></div>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
export const metadata: Toil.Metadata = { title: 'Privacy' };
|
|
2
|
-
|
|
3
|
-
// Route group: the `(legal)` folder organizes files without adding a URL segment, so this route is
|
|
4
|
-
// served at `/privacy`, not `/legal/privacy`. Groups can carry their own layout too.
|
|
5
|
-
export default function Privacy() {
|
|
6
|
-
return (
|
|
7
|
-
<main>
|
|
8
|
-
<h1>Privacy</h1>
|
|
9
|
-
<p>
|
|
10
|
-
Served at <code>/privacy</code> from <code>routes/(legal)/privacy.tsx</code>. The
|
|
11
|
-
<code> (legal)</code> group adds no URL segment.
|
|
12
|
-
</p>
|
|
13
|
-
<p>
|
|
14
|
-
<Toil.Link href="/terms">Terms</Toil.Link>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
1
|
+
export const metadata: Toil.Metadata = { title: 'Privacy' };
|
|
2
|
+
|
|
3
|
+
// Route group: the `(legal)` folder organizes files without adding a URL segment, so this route is
|
|
4
|
+
// served at `/privacy`, not `/legal/privacy`. Groups can carry their own layout too.
|
|
5
|
+
export default function Privacy() {
|
|
6
|
+
return (
|
|
7
|
+
<main>
|
|
8
|
+
<h1>Privacy</h1>
|
|
9
|
+
<p>
|
|
10
|
+
Served at <code>/privacy</code> from <code>routes/(legal)/privacy.tsx</code>. The
|
|
11
|
+
<code> (legal)</code> group adds no URL segment.
|
|
12
|
+
</p>
|
|
13
|
+
<p>
|
|
14
|
+
<Toil.Link href="/terms">Terms</Toil.Link> <Toil.Link href="/features">Back to features</Toil.Link>
|
|
15
|
+
</p>
|
|
16
|
+
</main>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
export const metadata: Toil.Metadata = { title: 'Terms' };
|
|
2
|
-
|
|
3
|
-
export default function Terms() {
|
|
4
|
-
return (
|
|
5
|
-
<main>
|
|
6
|
-
<h1>Terms</h1>
|
|
7
|
-
<p>
|
|
8
|
-
Also in the <code>(legal)</code> group, served at <code>/terms</code>.
|
|
9
|
-
</p>
|
|
10
|
-
<p>
|
|
11
|
-
<Toil.Link href="/privacy">Privacy</Toil.Link>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
1
|
+
export const metadata: Toil.Metadata = { title: 'Terms' };
|
|
2
|
+
|
|
3
|
+
export default function Terms() {
|
|
4
|
+
return (
|
|
5
|
+
<main>
|
|
6
|
+
<h1>Terms</h1>
|
|
7
|
+
<p>
|
|
8
|
+
Also in the <code>(legal)</code> group, served at <code>/terms</code>.
|
|
9
|
+
</p>
|
|
10
|
+
<p>
|
|
11
|
+
<Toil.Link href="/privacy">Privacy</Toil.Link> <Toil.Link href="/features">Back to features</Toil.Link>
|
|
12
|
+
</p>
|
|
13
|
+
</main>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
// Declarative per-route SEO, resolved by the router into <title> + <meta>/<link> tags, and baked
|
|
2
|
-
// into static HTML at build (see build/client/about/index.html). The layout's titleTemplate wraps
|
|
3
|
-
// this title, so the tab reads "About | ToilJS"; component-level useHead/<Head> can override.
|
|
4
|
-
export const metadata: Toil.Metadata = {
|
|
5
|
-
title: 'About',
|
|
6
|
-
description: 'About the ToilJS example app.',
|
|
7
|
-
openGraph: { title: 'About, ToilJS', type: 'website' }
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default function About() {
|
|
11
|
-
return (
|
|
12
|
-
<main>
|
|
13
|
-
<h1>About</h1>
|
|
14
|
-
<p>
|
|
15
|
-
This page is served by <code>client/routes/about.tsx</code>. Its tab title comes from
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
</
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
1
|
+
// Declarative per-route SEO, resolved by the router into <title> + <meta>/<link> tags, and baked
|
|
2
|
+
// into static HTML at build (see build/client/about/index.html). The layout's titleTemplate wraps
|
|
3
|
+
// this title, so the tab reads "About | ToilJS"; component-level useHead/<Head> can override.
|
|
4
|
+
export const metadata: Toil.Metadata = {
|
|
5
|
+
title: 'About',
|
|
6
|
+
description: 'About the ToilJS example app.',
|
|
7
|
+
openGraph: { title: 'About, ToilJS', type: 'website' }
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function About() {
|
|
11
|
+
return (
|
|
12
|
+
<main>
|
|
13
|
+
<h1>About</h1>
|
|
14
|
+
<p>
|
|
15
|
+
This page is served by <code>client/routes/about.tsx</code>. Its tab title comes from the{' '}
|
|
16
|
+
<code>metadata</code> export above, wrapped by the layout template into <code>About | ToilJS</code>.
|
|
17
|
+
</p>
|
|
18
|
+
<Toil.Link href="/features">See every feature</Toil.Link>
|
|
19
|
+
</main>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
// Dynamic metadata from the route param, so the tab reads "Blog post 42 | ToilJS".
|
|
2
|
-
export const generateMetadata: Toil.GenerateMetadata = ({ params }) => ({
|
|
3
|
-
title: `Blog post ${params.id}`,
|
|
4
|
-
description: `Reading blog post ${params.id}
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
// Dynamic metadata from the route param, so the tab reads "Blog post 42 | ToilJS".
|
|
2
|
+
export const generateMetadata: Toil.GenerateMetadata = ({ params }) => ({
|
|
3
|
+
title: `Blog post ${params.id}`,
|
|
4
|
+
description: `Reading blog post ${params.id}.`
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
// The per-post title above is dynamic, so it can't be statically indexed for site search. These
|
|
8
|
+
// static hints make the blog discoverable from the /search page anyway (see usePageSearch).
|
|
9
|
+
export const searchHints: Toil.SearchHints = {
|
|
10
|
+
title: 'Blog',
|
|
11
|
+
description: 'Articles and updates from the ToilJS example app.',
|
|
12
|
+
keywords: ['blog', 'posts', 'articles']
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default function BlogPost() {
|
|
16
|
+
const { id } = Toil.useParams();
|
|
17
|
+
return (
|
|
18
|
+
<main>
|
|
19
|
+
<h1>Blog post {id}</h1>
|
|
20
|
+
<p>
|
|
21
|
+
Dynamic route from <code>client/routes/blog/[id].tsx</code>.
|
|
22
|
+
</p>
|
|
23
|
+
<Toil.Link href="/features">See every feature</Toil.Link>
|
|
24
|
+
</main>
|
|
25
|
+
);
|
|
26
|
+
}
|