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.
Files changed (119) hide show
  1. package/README.md +2 -0
  2. package/build/cli/.tsbuildinfo +1 -1
  3. package/build/cli/configure.js +10 -4
  4. package/build/cli/create.js +58 -30
  5. package/build/cli/diagnostics.d.ts +55 -0
  6. package/build/cli/diagnostics.js +333 -0
  7. package/build/cli/doctor.d.ts +6 -0
  8. package/build/cli/doctor.js +249 -0
  9. package/build/cli/index.js +26 -0
  10. package/build/cli/proc.d.ts +5 -0
  11. package/build/cli/proc.js +20 -0
  12. package/build/cli/ui.d.ts +1 -0
  13. package/build/cli/ui.js +1 -0
  14. package/build/cli/update.d.ts +7 -0
  15. package/build/cli/update.js +117 -0
  16. package/build/cli/updates.d.ts +10 -0
  17. package/build/cli/updates.js +45 -0
  18. package/build/client/.tsbuildinfo +1 -1
  19. package/build/client/dev/error-overlay.js +1 -1
  20. package/build/client/head/metadata.js +3 -1
  21. package/build/client/index.d.ts +5 -1
  22. package/build/client/index.js +2 -0
  23. package/build/client/navigation/navigation.js +1 -1
  24. package/build/client/routing/Router.js +2 -2
  25. package/build/client/search/search.d.ts +26 -0
  26. package/build/client/search/search.js +101 -0
  27. package/build/client/search/use-page-search.d.ts +8 -0
  28. package/build/client/search/use-page-search.js +21 -0
  29. package/build/compiler/.tsbuildinfo +1 -1
  30. package/build/compiler/generate.js +26 -23
  31. package/build/compiler/index.d.ts +2 -0
  32. package/build/compiler/index.js +1 -0
  33. package/build/compiler/pages.d.ts +8 -0
  34. package/build/compiler/pages.js +37 -0
  35. package/build/compiler/plugin.js +3 -1
  36. package/build/compiler/prerender.d.ts +1 -0
  37. package/build/compiler/prerender.js +11 -5
  38. package/build/compiler/seo.js +10 -3
  39. package/build/io/.tsbuildinfo +1 -1
  40. package/examples/basic/client/components/Header.tsx +43 -41
  41. package/examples/basic/client/components/HoneycombBackground.tsx +223 -230
  42. package/examples/basic/client/public/index.html +18 -16
  43. package/examples/basic/client/routes/(legal)/privacy.tsx +18 -19
  44. package/examples/basic/client/routes/(legal)/terms.tsx +15 -16
  45. package/examples/basic/client/routes/about.tsx +21 -22
  46. package/examples/basic/client/routes/blog/[id].tsx +26 -18
  47. package/examples/basic/client/routes/features/actions.tsx +67 -67
  48. package/examples/basic/client/routes/features/error/index.tsx +27 -27
  49. package/examples/basic/client/routes/features/head.tsx +38 -38
  50. package/examples/basic/client/routes/features/index.tsx +83 -75
  51. package/examples/basic/client/routes/features/realtime.tsx +34 -32
  52. package/examples/basic/client/routes/features/script.tsx +31 -31
  53. package/examples/basic/client/routes/features/seo.tsx +39 -39
  54. package/examples/basic/client/routes/features/template/index.tsx +20 -20
  55. package/examples/basic/client/routes/features/template/template.tsx +16 -18
  56. package/examples/basic/client/routes/gallery/@modal/(.)photo/[id].tsx +23 -23
  57. package/examples/basic/client/routes/gallery/index.tsx +42 -42
  58. package/examples/basic/client/routes/gallery/photo/[id].tsx +18 -18
  59. package/examples/basic/client/routes/get-started.tsx +157 -84
  60. package/examples/basic/client/routes/index.tsx +137 -96
  61. package/examples/basic/client/routes/loader-demo/index.tsx +59 -52
  62. package/examples/basic/client/routes/search.tsx +61 -0
  63. package/examples/basic/client/routes/test.tsx +7 -8
  64. package/examples/basic/client/styles/main.css +624 -552
  65. package/package.json +2 -2
  66. package/presets/eslint.js +10 -3
  67. package/src/cli/configure.ts +363 -353
  68. package/src/cli/create.ts +563 -530
  69. package/src/cli/diagnostics.ts +421 -0
  70. package/src/cli/doctor.ts +318 -0
  71. package/src/cli/features.ts +166 -160
  72. package/src/cli/index.ts +242 -211
  73. package/src/cli/proc.ts +30 -0
  74. package/src/cli/ui.ts +111 -103
  75. package/src/cli/update.ts +150 -0
  76. package/src/cli/updates.ts +69 -0
  77. package/src/client/components/Image.tsx +91 -89
  78. package/src/client/dev/error-overlay.tsx +193 -197
  79. package/src/client/head/metadata.ts +94 -92
  80. package/src/client/index.ts +79 -64
  81. package/src/client/navigation/Link.tsx +94 -100
  82. package/src/client/navigation/navigation.ts +215 -218
  83. package/src/client/routing/Router.tsx +210 -193
  84. package/src/client/routing/hooks.ts +110 -114
  85. package/src/client/routing/lazy.ts +77 -81
  86. package/src/client/search/search.ts +189 -0
  87. package/src/client/search/use-page-search.ts +73 -0
  88. package/src/compiler/config.ts +173 -171
  89. package/src/compiler/fonts.ts +89 -87
  90. package/src/compiler/generate.ts +378 -373
  91. package/src/compiler/image-report.ts +88 -85
  92. package/src/compiler/index.ts +2 -0
  93. package/src/compiler/pages.ts +70 -0
  94. package/src/compiler/plugin.ts +51 -47
  95. package/src/compiler/prerender.ts +152 -130
  96. package/src/compiler/routes.ts +132 -131
  97. package/src/compiler/seo.ts +381 -356
  98. package/src/compiler/vite.ts +155 -145
  99. package/src/io/FastSet.ts +99 -96
  100. package/test/configure.test.ts +94 -90
  101. package/test/doctor.test.ts +140 -0
  102. package/test/dom/Image.test.tsx +73 -46
  103. package/test/dom/Script.test.tsx +48 -45
  104. package/test/dom/action.test.tsx +146 -129
  105. package/test/dom/error-overlay.test.tsx +44 -44
  106. package/test/dom/loader.test.tsx +2 -2
  107. package/test/dom/revalidate.test.tsx +1 -1
  108. package/test/dom/route-head.test.tsx +1 -2
  109. package/test/dom/slot.test.tsx +131 -109
  110. package/test/dom/view-transitions.test.tsx +53 -51
  111. package/test/features.test.ts +149 -142
  112. package/test/fonts.test.ts +28 -26
  113. package/test/head.test.ts +45 -35
  114. package/test/metadata.test.ts +42 -41
  115. package/test/pages.test.ts +105 -0
  116. package/test/prerender.test.ts +54 -46
  117. package/test/search.test.ts +114 -0
  118. package/test/seo.test.ts +164 -142
  119. package/test/update.test.ts +44 -0
@@ -1,39 +1,39 @@
1
- // Route metadata: the declarative way to set this page's <title>, description, OpenGraph, and more.
2
- // The router applies it before paint and the build bakes it into static HTML for crawlers.
3
- //
4
- // This route sets its OWN `titleTemplate: '%s'`, which overrides the layout's "%s | ToilJS" template,
5
- // so the tab reads exactly "useReducer | React Hooks" with no site suffix. Drop the titleTemplate
6
- // line and the same title renders as "useReducer | React Hooks | ToilJS".
7
- export const metadata: Toil.Metadata = {
8
- title: 'useReducer | React Hooks',
9
- titleTemplate: '%s',
10
- description: 'Manage complex state transitions with a reducer function using the useReducer hook.',
11
- keywords: ['react', 'hooks', 'useReducer', 'state'],
12
- canonical: 'https://toil.example/features/seo',
13
- openGraph: {
14
- title: 'useReducer | React Hooks',
15
- description: 'Manage complex state transitions with a reducer.',
16
- type: 'website',
17
- },
18
- };
19
-
20
- export default function SeoDemo() {
21
- return (
22
- <main>
23
- <h1>Route metadata</h1>
24
- <p>
25
- The browser tab now reads <strong>useReducer | React Hooks</strong>, set entirely by
26
- the <code>metadata</code> export in <code>client/routes/features/seo.tsx</code>, with
27
- no <code>useEffect</code> and no title suffix.
28
- </p>
29
- <p>
30
- It also emitted <code>&lt;meta name="description"&gt;</code>, keywords, a canonical
31
- link, and the <code>og:*</code> tags, all from that one object.
32
- </p>
33
- <p>
34
- <Toil.Link href="/features/head">Prefer the imperative API?</Toil.Link>{' '}
35
- <Toil.Link href="/features">Back to features</Toil.Link>
36
- </p>
37
- </main>
38
- );
39
- }
1
+ // Route metadata: the declarative way to set this page's <title>, description, OpenGraph, and more.
2
+ // The router applies it before paint and the build bakes it into static HTML for crawlers.
3
+ //
4
+ // This route sets its OWN `titleTemplate: '%s'`, which overrides the layout's "%s | ToilJS" template,
5
+ // so the tab reads exactly "useReducer | React Hooks" with no site suffix. Drop the titleTemplate
6
+ // line and the same title renders as "useReducer | React Hooks | ToilJS".
7
+ export const metadata: Toil.Metadata = {
8
+ title: 'useReducer | React Hooks',
9
+ titleTemplate: '%s',
10
+ description: 'Manage complex state transitions with a reducer function using the useReducer hook.',
11
+ keywords: ['react', 'hooks', 'useReducer', 'state'],
12
+ canonical: 'https://toil.example/features/seo',
13
+ openGraph: {
14
+ title: 'useReducer | React Hooks',
15
+ description: 'Manage complex state transitions with a reducer.',
16
+ type: 'website'
17
+ }
18
+ };
19
+
20
+ export default function SeoDemo() {
21
+ return (
22
+ <main>
23
+ <h1>Route metadata</h1>
24
+ <p>
25
+ The browser tab now reads <strong>useReducer | React Hooks</strong>, set entirely by the{' '}
26
+ <code>metadata</code> export in <code>client/routes/features/seo.tsx</code>, with no{' '}
27
+ <code>useEffect</code> and no title suffix.
28
+ </p>
29
+ <p>
30
+ It also emitted <code>&lt;meta name="description"&gt;</code>, keywords, a canonical link, and the{' '}
31
+ <code>og:*</code> tags, all from that one object.
32
+ </p>
33
+ <p>
34
+ <Toil.Link href="/features/head">Prefer the imperative API?</Toil.Link>{' '}
35
+ <Toil.Link href="/features">Back to features</Toil.Link>
36
+ </p>
37
+ </main>
38
+ );
39
+ }
@@ -1,20 +1,20 @@
1
- export const metadata: Toil.Metadata = { title: 'Templates' };
2
-
3
- export default function TemplateDemo() {
4
- return (
5
- <main>
6
- <h1>Templates</h1>
7
- <p>
8
- The line above is rendered by <code>template.tsx</code>. Bounce between these two links
9
- and watch the mount number climb, the template remounts on every navigation.
10
- </p>
11
- <p>
12
- <Toil.Link href="/features/template">This page</Toil.Link>{' '}
13
- <Toil.Link href="/features/template/b">Sibling page</Toil.Link>
14
- </p>
15
- <p>
16
- <Toil.Link href="/features">Back to features</Toil.Link>
17
- </p>
18
- </main>
19
- );
20
- }
1
+ export const metadata: Toil.Metadata = { title: 'Templates' };
2
+
3
+ export default function TemplateDemo() {
4
+ return (
5
+ <main>
6
+ <h1>Templates</h1>
7
+ <p>
8
+ The line above is rendered by <code>template.tsx</code>. Bounce between these two links and watch the
9
+ mount number climb, the template remounts on every navigation.
10
+ </p>
11
+ <p>
12
+ <Toil.Link href="/features/template">This page</Toil.Link>{' '}
13
+ <Toil.Link href="/features/template/b">Sibling page</Toil.Link>
14
+ </p>
15
+ <p>
16
+ <Toil.Link href="/features">Back to features</Toil.Link>
17
+ </p>
18
+ </main>
19
+ );
20
+ }
@@ -1,18 +1,16 @@
1
- import { useState, type ReactNode } from 'react';
2
-
3
- // A template wraps a segment like a layout, but RE-MOUNTS on every navigation within it (a layout
4
- // persists). This counter increments each time the template mounts, so navigating between the two
5
- // child links below bumps it, proving the remount. Swap this file for `layout.tsx` and the number
6
- // would hold steady instead.
7
- let mounts = 0;
8
- export default function PlaygroundTemplate({ children }: { children?: ReactNode }) {
9
- const [mountId] = useState(() => ++mounts);
10
- return (
11
- <div>
12
- <p style={{ opacity: 0.6 }}>
13
- template mount #{mountId} (it increments on every navigation here)
14
- </p>
15
- {children}
16
- </div>
17
- );
18
- }
1
+ import { useState, type ReactNode } from 'react';
2
+
3
+ // A template wraps a segment like a layout, but RE-MOUNTS on every navigation within it (a layout
4
+ // persists). This counter increments each time the template mounts, so navigating between the two
5
+ // child links below bumps it, proving the remount. Swap this file for `layout.tsx` and the number
6
+ // would hold steady instead.
7
+ let mounts = 0;
8
+ export default function PlaygroundTemplate({ children }: { children?: ReactNode }) {
9
+ const [mountId] = useState(() => ++mounts);
10
+ return (
11
+ <div>
12
+ <p style={{ opacity: 0.6 }}>template mount #{mountId} (it increments on every navigation here)</p>
13
+ {children}
14
+ </div>
15
+ );
16
+ }
@@ -1,23 +1,23 @@
1
- // Intercepting route: `(.)photo/[id]` inside the `@modal` slot catches a soft navigation to
2
- // /gallery/photo/:id and renders here instead, as an overlay, while the gallery stays mounted behind
3
- // it. On a hard reload the interception does not apply and the full photo/[id].tsx page renders.
4
- export default function PhotoModal() {
5
- const { id } = Toil.useParams();
6
- return (
7
- <div
8
- style={{
9
- position: 'fixed',
10
- inset: 0,
11
- background: 'rgba(0,0,0,0.6)',
12
- display: 'grid',
13
- placeItems: 'center',
14
- zIndex: 50,
15
- }}>
16
- <div style={{ background: 'var(--bg, #0b0f14)', padding: 24, borderRadius: 12, minWidth: 240 }}>
17
- <h2>Photo {id}</h2>
18
- <p>This is the intercepted modal view (soft navigation).</p>
19
- <Toil.Link href="/gallery">Close</Toil.Link>
20
- </div>
21
- </div>
22
- );
23
- }
1
+ // Intercepting route: `(.)photo/[id]` inside the `@modal` slot catches a soft navigation to
2
+ // /gallery/photo/:id and renders here instead, as an overlay, while the gallery stays mounted behind
3
+ // it. On a hard reload the interception does not apply and the full photo/[id].tsx page renders.
4
+ export default function PhotoModal() {
5
+ const { id } = Toil.useParams();
6
+ return (
7
+ <div
8
+ style={{
9
+ position: 'fixed',
10
+ inset: 0,
11
+ background: 'rgba(0,0,0,0.6)',
12
+ display: 'grid',
13
+ placeItems: 'center',
14
+ zIndex: 50
15
+ }}>
16
+ <div style={{ background: 'var(--bg, #0b0f14)', padding: 24, borderRadius: 12, minWidth: 240 }}>
17
+ <h2>Photo {id}</h2>
18
+ <p>This is the intercepted modal view (soft navigation).</p>
19
+ <Toil.Link href="/gallery">Close</Toil.Link>
20
+ </div>
21
+ </div>
22
+ );
23
+ }
@@ -1,42 +1,42 @@
1
- export const metadata: Toil.Metadata = {
2
- title: 'Gallery',
3
- description: 'Parallel routes and intercepting routes: a photo opens as a modal on soft nav.',
4
- };
5
-
6
- const photos = [1, 2, 3, 4];
7
-
8
- // Clicking a photo soft-navigates to /gallery/photo/:id. The intercepting route @modal/(.)photo/[id]
9
- // catches that on soft nav and shows it as a modal over this grid. A hard reload of the same URL
10
- // renders the full page (photo/[id].tsx) instead, deep links still work.
11
- export default function Gallery() {
12
- return (
13
- <main>
14
- <h1>Gallery</h1>
15
- <p>
16
- Click a photo, it opens as a modal (intercepting route). Reload that URL and you get
17
- the full page. Same URL, two presentations.
18
- </p>
19
- <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
20
- {photos.map((id) => (
21
- <Toil.Link
22
- key={id}
23
- href={`/gallery/photo/${id}`}
24
- style={{
25
- width: 88,
26
- height: 88,
27
- display: 'grid',
28
- placeItems: 'center',
29
- border: '1px solid currentColor',
30
- borderRadius: 8,
31
- fontWeight: 700,
32
- }}>
33
- {id}
34
- </Toil.Link>
35
- ))}
36
- </div>
37
- <p style={{ marginTop: 16 }}>
38
- <Toil.Link href="/features">Back to features</Toil.Link>
39
- </p>
40
- </main>
41
- );
42
- }
1
+ export const metadata: Toil.Metadata = {
2
+ title: 'Gallery',
3
+ description: 'Parallel routes and intercepting routes: a photo opens as a modal on soft nav.'
4
+ };
5
+
6
+ const photos = [1, 2, 3, 4];
7
+
8
+ // Clicking a photo soft-navigates to /gallery/photo/:id. The intercepting route @modal/(.)photo/[id]
9
+ // catches that on soft nav and shows it as a modal over this grid. A hard reload of the same URL
10
+ // renders the full page (photo/[id].tsx) instead, deep links still work.
11
+ export default function Gallery() {
12
+ return (
13
+ <main>
14
+ <h1>Gallery</h1>
15
+ <p>
16
+ Click a photo, it opens as a modal (intercepting route). Reload that URL and you get the full page. Same
17
+ URL, two presentations.
18
+ </p>
19
+ <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
20
+ {photos.map((id) => (
21
+ <Toil.Link
22
+ key={id}
23
+ href={`/gallery/photo/${id}`}
24
+ style={{
25
+ width: 88,
26
+ height: 88,
27
+ display: 'grid',
28
+ placeItems: 'center',
29
+ border: '1px solid currentColor',
30
+ borderRadius: 8,
31
+ fontWeight: 700
32
+ }}>
33
+ {id}
34
+ </Toil.Link>
35
+ ))}
36
+ </div>
37
+ <p style={{ marginTop: 16 }}>
38
+ <Toil.Link href="/features">Back to features</Toil.Link>
39
+ </p>
40
+ </main>
41
+ );
42
+ }
@@ -1,18 +1,18 @@
1
- export const generateMetadata: Toil.GenerateMetadata = ({ params }) => ({
2
- title: `Photo ${params.id}`,
3
- });
4
-
5
- // The full page for a photo, shown on a hard load or deep link to /gallery/photo/:id.
6
- export default function PhotoPage() {
7
- const { id } = Toil.useParams();
8
- return (
9
- <main>
10
- <h1>Photo {id}</h1>
11
- <p>
12
- Full page at <code>gallery/photo/[id].tsx</code>. Reached directly (reload or deep
13
- link), not intercepted.
14
- </p>
15
- <Toil.Link href="/gallery">Back to gallery</Toil.Link>
16
- </main>
17
- );
18
- }
1
+ export const generateMetadata: Toil.GenerateMetadata = ({ params }) => ({
2
+ title: `Photo ${params.id}`
3
+ });
4
+
5
+ // The full page for a photo, shown on a hard load or deep link to /gallery/photo/:id.
6
+ export default function PhotoPage() {
7
+ const { id } = Toil.useParams();
8
+ return (
9
+ <main>
10
+ <h1>Photo {id}</h1>
11
+ <p>
12
+ Full page at <code>gallery/photo/[id].tsx</code>. Reached directly (reload or deep link), not
13
+ intercepted.
14
+ </p>
15
+ <Toil.Link href="/gallery">Back to gallery</Toil.Link>
16
+ </main>
17
+ );
18
+ }
@@ -1,84 +1,157 @@
1
- export default function GetStarted() {
2
- return (
3
- <div className="gs-page">
4
-
5
- {/* Hero */}
6
- <div className="gs-hero">
7
- <h1 className="gs-title">Get Started</h1>
8
- <p className="gs-desc">Everything you need to build your first ToilJS app.</p>
9
- </div>
10
-
11
-
12
- {/* Info grid */}
13
- <section className="gs-section">
14
- <h2 className="gs-section-title">Project Structure</h2>
15
- <div className="gs-grid">
16
-
17
- <div className="gs-card gs-card--accent1">
18
- <div className="gs-card-icon">
19
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
20
- </div>
21
- <h3>File-based Routing</h3>
22
- <p>Every <code>.tsx</code> file in <code>client/routes/</code> becomes a route. No config required.</p>
23
- <pre><code>{`index.tsx → /
24
- about.tsx → /about
25
- [id].tsx → /:id
26
- [...slug].tsx → /*`}</code></pre>
27
- </div>
28
-
29
- <div className="gs-card gs-card--accent2">
30
- <div className="gs-card-icon">
31
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
32
- </div>
33
- <h3>Public Folder</h3>
34
- <p>Files in <code>public/</code> are copied as-is to the build root. Reference them with an absolute path.</p>
35
- <pre><code>{`public/images/logo.svg
36
- /images/logo.svg`}</code></pre>
37
- </div>
38
-
39
- <div className="gs-card gs-card--accent3">
40
- <div className="gs-card-icon">
41
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
42
- </div>
43
- <h3>Layout</h3>
44
- <p><code>client/layout.tsx</code> wraps every page. Use it for your nav, footer, providers, and global styles.</p>
45
- </div>
46
-
47
- <div className="gs-card gs-card--accent4">
48
- <div className="gs-card-icon">
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
- </div>
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>
53
- </div>
54
-
55
- </div>
56
- </section>
57
-
58
- {/* Navigation section */}
59
- <section className="gs-section">
60
- <h2 className="gs-section-title">Navigation</h2>
61
- <div className="gs-card gs-card--flat">
62
- <div className="gs-card-icon">
63
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
64
- </div>
65
- <h3>Use <code>{'<Toil.Link>'}</code> for client-side navigation</h3>
66
- <p>Avoids full page reloads and keeps transitions instant. Use a regular <code>{'<a>'}</code> only for external links.</p>
67
- <pre><code>{`// Internal navigation
68
- <Toil.Link href="/about">About</Toil.Link>
69
-
70
- // ✅ External link
71
- <a href="https://toil.org" target="_blank">Docs</a>`}</code></pre>
72
- </div>
73
- </section>
74
-
75
- <div className="gs-actions">
76
- <Toil.Link href="/" className="btn btn-secondary">← Back home</Toil.Link>
77
- <a href="https://toil.org/docs" target="_blank" rel="noopener noreferrer" className="btn btn-primary">
78
- Read the Docs
79
- </a>
80
- </div>
81
-
82
- </div>
83
- );
84
- }
1
+ export default function GetStarted() {
2
+ return (
3
+ <div className="gs-page">
4
+ {/* Hero */}
5
+ <div className="gs-hero">
6
+ <h1 className="gs-title">Get Started</h1>
7
+ <p className="gs-desc">Everything you need to build your first ToilJS app.</p>
8
+ </div>
9
+
10
+ {/* Info grid */}
11
+ <section className="gs-section">
12
+ <h2 className="gs-section-title">Project Structure</h2>
13
+ <div className="gs-grid">
14
+ <div className="gs-card gs-card--accent1">
15
+ <div className="gs-card-icon">
16
+ <svg
17
+ width="22"
18
+ height="22"
19
+ viewBox="0 0 24 24"
20
+ fill="none"
21
+ stroke="currentColor"
22
+ strokeWidth="2"
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round">
25
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
26
+ </svg>
27
+ </div>
28
+ <h3>File-based Routing</h3>
29
+ <p>
30
+ Every <code>.tsx</code> file in <code>client/routes/</code> becomes a route. No config
31
+ required.
32
+ </p>
33
+ <pre>
34
+ <code>{`index.tsx → /
35
+ about.tsx → /about
36
+ [id].tsx /:id
37
+ [...slug].tsx → /*`}</code>
38
+ </pre>
39
+ </div>
40
+
41
+ <div className="gs-card gs-card--accent2">
42
+ <div className="gs-card-icon">
43
+ <svg
44
+ width="22"
45
+ height="22"
46
+ viewBox="0 0 24 24"
47
+ fill="none"
48
+ stroke="currentColor"
49
+ strokeWidth="2"
50
+ strokeLinecap="round"
51
+ strokeLinejoin="round">
52
+ <rect x="2" y="3" width="20" height="14" rx="2" />
53
+ <path d="M8 21h8M12 17v4" />
54
+ </svg>
55
+ </div>
56
+ <h3>Public Folder</h3>
57
+ <p>
58
+ Files in <code>public/</code> are copied as-is to the build root. Reference them with an
59
+ absolute path.
60
+ </p>
61
+ <pre>
62
+ <code>{`public/images/logo.svg
63
+ → /images/logo.svg`}</code>
64
+ </pre>
65
+ </div>
66
+
67
+ <div className="gs-card gs-card--accent3">
68
+ <div className="gs-card-icon">
69
+ <svg
70
+ width="22"
71
+ height="22"
72
+ viewBox="0 0 24 24"
73
+ fill="none"
74
+ stroke="currentColor"
75
+ strokeWidth="2"
76
+ strokeLinecap="round"
77
+ strokeLinejoin="round">
78
+ <rect x="3" y="3" width="18" height="18" rx="2" />
79
+ <path d="M3 9h18M9 21V9" />
80
+ </svg>
81
+ </div>
82
+ <h3>Layout</h3>
83
+ <p>
84
+ <code>client/layout.tsx</code> wraps every page. Use it for your nav, footer, providers, and
85
+ global styles.
86
+ </p>
87
+ </div>
88
+
89
+ <div className="gs-card gs-card--accent4">
90
+ <div className="gs-card-icon">
91
+ <svg
92
+ width="22"
93
+ height="22"
94
+ viewBox="0 0 24 24"
95
+ fill="none"
96
+ stroke="currentColor"
97
+ strokeWidth="2"
98
+ strokeLinecap="round"
99
+ strokeLinejoin="round">
100
+ <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
101
+ </svg>
102
+ </div>
103
+ <h3>Entry Point</h3>
104
+ <p>
105
+ <code>client/toil.tsx</code> is the app entry. Import global CSS and call{' '}
106
+ <code>Toil.mount()</code>, runs once on startup.
107
+ </p>
108
+ </div>
109
+ </div>
110
+ </section>
111
+
112
+ {/* Navigation section */}
113
+ <section className="gs-section">
114
+ <h2 className="gs-section-title">Navigation</h2>
115
+ <div className="gs-card gs-card--flat">
116
+ <div className="gs-card-icon">
117
+ <svg
118
+ width="22"
119
+ height="22"
120
+ viewBox="0 0 24 24"
121
+ fill="none"
122
+ stroke="currentColor"
123
+ strokeWidth="2"
124
+ strokeLinecap="round"
125
+ strokeLinejoin="round">
126
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
127
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
128
+ </svg>
129
+ </div>
130
+ <h3>
131
+ Use <code>{'<Toil.Link>'}</code> for client-side navigation
132
+ </h3>
133
+ <p>
134
+ Avoids full page reloads and keeps transitions instant. Use a regular <code>{'<a>'}</code> only
135
+ for external links.
136
+ </p>
137
+ <pre>
138
+ <code>{`// ✅ Internal navigation
139
+ <Toil.Link href="/about">About</Toil.Link>
140
+
141
+ // ✅ External link
142
+ <a href="https://toil.org" target="_blank">Docs</a>`}</code>
143
+ </pre>
144
+ </div>
145
+ </section>
146
+
147
+ <div className="gs-actions">
148
+ <Toil.Link href="/" className="btn btn-secondary">
149
+ ← Back home
150
+ </Toil.Link>
151
+ <a href="https://toil.org/docs" target="_blank" rel="noopener noreferrer" className="btn btn-primary">
152
+ Read the Docs
153
+ </a>
154
+ </div>
155
+ </div>
156
+ );
157
+ }