create-swift-rust 0.4.0 → 0.7.0

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 (65) hide show
  1. package/dist/index.js +74 -938
  2. package/dist/index.js.map +1 -1
  3. package/package.json +6 -4
  4. package/templates/default/README.md +29 -0
  5. package/templates/default/_env.example +5 -0
  6. package/templates/default/_env.local +5 -0
  7. package/templates/default/_gitignore +14 -0
  8. package/templates/default/biome.json +12 -0
  9. package/templates/default/globals.d.ts +19 -0
  10. package/templates/default/package.json +30 -0
  11. package/templates/default/postcss.config.mjs +7 -0
  12. package/templates/default/public/blog/introducing-swift-rust.svg +12 -0
  13. package/templates/default/public/og-image.svg +25 -0
  14. package/templates/default/public/samples/axelborg-towers-modern-architecture.jpg +0 -0
  15. package/templates/default/public/samples/brown-high-skyscrapers.jpg +0 -0
  16. package/templates/default/public/samples/close-up-portrait-gorgeous-young-woman.jpg +0 -0
  17. package/templates/default/public/samples/full-shot-woman-taking-selfie.jpg +0 -0
  18. package/templates/default/public/samples/happy-black-parents-with-kids-making-video-call-smart-phone-home.jpg +0 -0
  19. package/templates/default/public/samples/metaverse-concept-collage-design.jpg +0 -0
  20. package/templates/default/src/app/about/layout.tsx +5 -0
  21. package/templates/default/src/app/about/page.tsx +74 -0
  22. package/templates/default/src/app/about/team/page.tsx +88 -0
  23. package/templates/default/src/app/api/contact/route.ts +15 -0
  24. package/templates/default/src/app/api/health/route.ts +7 -0
  25. package/templates/default/src/app/api/posts/[id]/route.ts +33 -0
  26. package/templates/default/src/app/api/posts/route.ts +29 -0
  27. package/templates/default/src/app/api/search/route.ts +15 -0
  28. package/templates/default/src/app/api/stats/route.ts +16 -0
  29. package/templates/default/src/app/blog/[slug]/page.tsx +116 -0
  30. package/templates/default/src/app/blog/page.tsx +101 -0
  31. package/templates/default/src/app/blog/tag/[tag]/page.tsx +82 -0
  32. package/templates/default/src/app/contact/page.tsx +131 -0
  33. package/templates/default/src/app/dashboard/analytics/page.tsx +103 -0
  34. package/templates/default/src/app/dashboard/layout.tsx +11 -0
  35. package/templates/default/src/app/dashboard/loading.tsx +18 -0
  36. package/templates/default/src/app/dashboard/page.tsx +181 -0
  37. package/templates/default/src/app/dashboard/posts/[id]/not-found.tsx +23 -0
  38. package/templates/default/src/app/dashboard/posts/[id]/page.tsx +87 -0
  39. package/templates/default/src/app/dashboard/posts/new/page.tsx +103 -0
  40. package/templates/default/src/app/dashboard/posts/page.tsx +81 -0
  41. package/templates/default/src/app/dashboard/settings/page.tsx +119 -0
  42. package/templates/default/src/app/error.tsx +42 -0
  43. package/templates/default/src/app/favicon.ico +0 -0
  44. package/templates/default/src/app/favicon.svg +8 -0
  45. package/templates/default/src/app/fonts/page.tsx +610 -0
  46. package/templates/default/src/app/globals.css +238 -0
  47. package/templates/default/src/app/images/page.tsx +56 -0
  48. package/templates/default/src/app/layout.tsx +44 -0
  49. package/templates/default/src/app/loading.tsx +13 -0
  50. package/templates/default/src/app/not-found.tsx +25 -0
  51. package/templates/default/src/app/page.tsx +410 -0
  52. package/templates/default/src/app/videos/page.tsx +182 -0
  53. package/templates/default/src/components/font-studio.tsx +378 -0
  54. package/templates/default/src/components/footer.tsx +75 -0
  55. package/templates/default/src/components/logo.tsx +26 -0
  56. package/templates/default/src/components/nav.tsx +102 -0
  57. package/templates/default/src/components/sidebar.tsx +109 -0
  58. package/templates/default/src/lib/blur.ts +5 -0
  59. package/templates/default/src/lib/format.ts +31 -0
  60. package/templates/default/src/lib/posts.ts +132 -0
  61. package/templates/default/src/lib/site.config.ts +39 -0
  62. package/templates/default/src/lib/stats.ts +25 -0
  63. package/templates/default/swift-rust.config.json +8 -0
  64. package/templates/default/tsconfig.json +29 -0
  65. package/templates/default/vercel.json +23 -0
@@ -0,0 +1,181 @@
1
+ import { formatNumber, formatPercent, relativeTime } from "@/lib/format";
2
+ import { posts } from "@/lib/posts";
3
+ import { stats, trafficMax, trafficSeries } from "@/lib/stats";
4
+ import type { Metadata } from "swift-rust";
5
+ import { Link } from "swift-rust";
6
+
7
+ export const metadata: Metadata = { title: "Dashboard" };
8
+
9
+ const ACTIVITY = [
10
+ {
11
+ who: "alex@swift-rust.dev",
12
+ what: "published",
13
+ target: "Streaming SSR in Rust",
14
+ when: "2026-06-01T08:32:00Z",
15
+ },
16
+ {
17
+ who: "maria@swift-rust.dev",
18
+ what: "updated",
19
+ target: "Geist font spec",
20
+ when: "2026-05-31T15:10:00Z",
21
+ },
22
+ {
23
+ who: "jordan@swift-rust.dev",
24
+ what: "deployed",
25
+ target: "v0.0.9-rc3",
26
+ when: "2026-05-31T09:45:00Z",
27
+ },
28
+ {
29
+ who: "priya@swift-rust.dev",
30
+ what: "merged",
31
+ target: "feat: edge streaming",
32
+ when: "2026-05-30T18:22:00Z",
33
+ },
34
+ {
35
+ who: "sam@swift-rust.dev",
36
+ what: "commented on",
37
+ target: "binary size regression",
38
+ when: "2026-05-30T11:08:00Z",
39
+ },
40
+ ];
41
+
42
+ export default function DashboardOverviewPage() {
43
+ return (
44
+ <div className="px-6 py-10 sm:px-10">
45
+ <div className="flex flex-wrap items-end justify-between gap-4">
46
+ <div>
47
+ <p className="text-[0.75rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
48
+ Overview
49
+ </p>
50
+ <h1 className="mt-1 text-3xl font-semibold tracking-tight">Welcome back, Alex</h1>
51
+ <p className="mt-1 text-[var(--color-fg-muted)]">
52
+ Here&apos;s what happened across your projects in the last 30 days.
53
+ </p>
54
+ </div>
55
+ <div className="flex items-center gap-2">
56
+ <Link href="/dashboard/posts/new" className="btn btn-outline btn-sm">
57
+ New post
58
+ </Link>
59
+ <Link href="/dashboard/settings" className="btn btn-primary btn-sm">
60
+ Settings
61
+ </Link>
62
+ </div>
63
+ </div>
64
+
65
+ <div className="mt-10 grid gap-px overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-border)] sm:grid-cols-2 lg:grid-cols-4">
66
+ {stats.map((s) => (
67
+ <div key={s.label} className="bg-[var(--color-surface)] p-6">
68
+ <p className="text-[0.8125rem] text-[var(--color-fg-muted)]">{s.label}</p>
69
+ <p className="mt-2 text-3xl font-semibold tracking-tight">{s.value}</p>
70
+ <div className="mt-3 flex items-center gap-2 text-[0.75rem]">
71
+ <span
72
+ className={`inline-flex items-center gap-0.5 font-medium ${
73
+ s.delta >= 0 ? "text-[#16a34a]" : "text-[#dc2626]"
74
+ }`}
75
+ >
76
+ {s.delta >= 0 ? "↑" : "↓"} {formatPercent(Math.abs(s.delta))}
77
+ </span>
78
+ <span className="text-[var(--color-fg-subtle)]">{s.context}</span>
79
+ </div>
80
+ </div>
81
+ ))}
82
+ </div>
83
+
84
+ <div className="mt-6 grid gap-6 lg:grid-cols-[1.4fr_1fr]">
85
+ <div className="card p-6">
86
+ <div className="flex items-center justify-between">
87
+ <div>
88
+ <h2 className="text-[0.95rem] font-semibold">Traffic</h2>
89
+ <p className="text-[0.8125rem] text-[var(--color-fg-muted)]">Last 7 days</p>
90
+ </div>
91
+ <div className="flex items-center gap-1 text-[0.75rem]">
92
+ <span className="badge badge-dot badge-success">Healthy</span>
93
+ </div>
94
+ </div>
95
+ <div className="mt-6 flex h-48 items-end gap-2">
96
+ {trafficSeries.map((d) => (
97
+ <div key={d.day} className="flex flex-1 flex-col items-center gap-2">
98
+ <div
99
+ className="w-full rounded-t-md bg-[var(--color-accent)]/80 transition-colors hover:bg-[var(--color-accent)]"
100
+ style={{ height: `${(d.requests / trafficMax) * 100}%` }}
101
+ title={`${d.requests.toLocaleString()} requests`}
102
+ />
103
+ <span className="text-[0.7rem] font-medium text-[var(--color-fg-subtle)]">
104
+ {d.day}
105
+ </span>
106
+ </div>
107
+ ))}
108
+ </div>
109
+ <div className="mt-4 flex items-center justify-between border-t border-[var(--color-border)] pt-4 text-[0.8125rem]">
110
+ <span className="text-[var(--color-fg-muted)]">Total</span>
111
+ <span className="font-mono font-medium">
112
+ {formatNumber(trafficSeries.reduce((s, d) => s + d.requests, 0))}
113
+ </span>
114
+ </div>
115
+ </div>
116
+
117
+ <div className="card p-6">
118
+ <h2 className="text-[0.95rem] font-semibold">Recent activity</h2>
119
+ <ul className="mt-4 -mx-2 divide-y divide-[var(--color-border)]">
120
+ {ACTIVITY.map((a, i) => (
121
+ <li key={i} className="flex items-start gap-3 px-2 py-3">
122
+ <div className="mt-0.5 h-2 w-2 shrink-0 rounded-full bg-[var(--color-accent)]" />
123
+ <div className="min-w-0 flex-1">
124
+ <p className="text-[0.875rem] leading-snug text-[var(--color-fg)]">
125
+ <span className="font-medium">{a.who}</span>{" "}
126
+ <span className="text-[var(--color-fg-muted)]">{a.what}</span>{" "}
127
+ <span className="font-medium">{a.target}</span>
128
+ </p>
129
+ <p className="mt-0.5 text-[0.75rem] text-[var(--color-fg-subtle)]">
130
+ {relativeTime(a.when)}
131
+ </p>
132
+ </div>
133
+ </li>
134
+ ))}
135
+ </ul>
136
+ </div>
137
+ </div>
138
+
139
+ <div className="mt-6 card p-6">
140
+ <div className="flex items-center justify-between">
141
+ <h2 className="text-[0.95rem] font-semibold">Latest posts</h2>
142
+ <Link
143
+ href="/dashboard/posts"
144
+ className="text-[0.8125rem] font-medium text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
145
+ >
146
+ View all
147
+ </Link>
148
+ </div>
149
+ <table className="mt-4 w-full text-left text-[0.875rem]">
150
+ <thead>
151
+ <tr className="border-b border-[var(--color-border)] text-[0.7rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
152
+ <th className="pb-2 font-semibold">Title</th>
153
+ <th className="pb-2 font-semibold">Author</th>
154
+ <th className="pb-2 font-semibold">Status</th>
155
+ <th className="pb-2 text-right font-semibold">Date</th>
156
+ </tr>
157
+ </thead>
158
+ <tbody>
159
+ {posts.slice(0, 4).map((p) => (
160
+ <tr key={p.slug} className="border-b border-[var(--color-border)] last:border-0">
161
+ <td className="py-3 pr-4">
162
+ <Link
163
+ href={`/blog/${p.slug}`}
164
+ className="font-medium text-[var(--color-fg)] hover:text-[var(--color-accent)]"
165
+ >
166
+ {p.title}
167
+ </Link>
168
+ </td>
169
+ <td className="py-3 pr-4 text-[var(--color-fg-muted)]">{p.author.name}</td>
170
+ <td className="py-3 pr-4">
171
+ <span className="badge badge-success badge-dot">Published</span>
172
+ </td>
173
+ <td className="py-3 text-right text-[var(--color-fg-subtle)]">{p.date}</td>
174
+ </tr>
175
+ ))}
176
+ </tbody>
177
+ </table>
178
+ </div>
179
+ </div>
180
+ );
181
+ }
@@ -0,0 +1,23 @@
1
+ import { Link } from "swift-rust";
2
+
3
+ export default function PostNotFound() {
4
+ return (
5
+ <div className="mx-auto max-w-md px-6 py-20 text-center sm:px-10">
6
+ <p className="text-[0.75rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
7
+ 404
8
+ </p>
9
+ <h1 className="mt-2 text-3xl font-semibold tracking-tight">Post not found</h1>
10
+ <p className="mt-3 text-[var(--color-fg-muted)]">
11
+ That post doesn&apos;t exist, was unpublished, or you don&apos;t have access.
12
+ </p>
13
+ <div className="mt-6 flex justify-center gap-2">
14
+ <Link href="/dashboard/posts" className="btn btn-primary btn-sm">
15
+ Back to posts
16
+ </Link>
17
+ <Link href="/dashboard" className="btn btn-outline btn-sm">
18
+ Dashboard
19
+ </Link>
20
+ </div>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,87 @@
1
+ import { formatDate } from "@/lib/format";
2
+ import { getPost, posts } from "@/lib/posts";
3
+ import type { Metadata } from "swift-rust";
4
+ import { Link } from "swift-rust";
5
+ import { notFound } from "swift-rust/router";
6
+
7
+ export function generateStaticParams() {
8
+ return posts.map((p) => ({ id: p.slug }));
9
+ }
10
+
11
+ export function generateMetadata({ params }: { params: { id: string } }): Metadata {
12
+ const post = getPost(params.id);
13
+ if (!post) return { title: "Post not found" };
14
+ return { title: `Edit: ${post.title}` };
15
+ }
16
+
17
+ export default function DashboardPostPage({ params }: { params: { id: string } }) {
18
+ const post = getPost(params.id);
19
+ if (!post) notFound();
20
+
21
+ return (
22
+ <div className="mx-auto max-w-3xl px-6 py-10 sm:px-10">
23
+ <Link
24
+ href="/dashboard/posts"
25
+ className="inline-flex items-center gap-1.5 text-[0.875rem] text-[var(--color-fg-muted)] hover:text-[var(--color-fg)]"
26
+ >
27
+ <svg
28
+ viewBox="0 0 24 24"
29
+ className="h-3.5 w-3.5"
30
+ fill="none"
31
+ stroke="currentColor"
32
+ strokeWidth="2"
33
+ >
34
+ <path d="M19 12H5M11 18l-6-6 6-6" strokeLinecap="round" strokeLinejoin="round" />
35
+ </svg>
36
+ All posts
37
+ </Link>
38
+
39
+ <div className="mt-8 flex items-start justify-between gap-4">
40
+ <div>
41
+ <p className="text-[0.75rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
42
+ Post
43
+ </p>
44
+ <h1 className="mt-1 text-3xl font-semibold tracking-tight">{post.title}</h1>
45
+ <p className="mt-2 text-[0.875rem] text-[var(--color-fg-muted)]">
46
+ By {post.author.name} · {formatDate(post.date)} · {post.readingTime}
47
+ </p>
48
+ </div>
49
+ <div className="flex items-center gap-2">
50
+ <span className="badge badge-success badge-dot">Published</span>
51
+ <Link href={`/blog/${post.slug}`} className="btn btn-outline btn-sm">
52
+ View live
53
+ </Link>
54
+ </div>
55
+ </div>
56
+
57
+ <div className="mt-8 card p-6">
58
+ <h2 className="text-[0.95rem] font-semibold">Excerpt</h2>
59
+ <p className="mt-2 text-[0.9375rem] leading-relaxed text-[var(--color-fg-muted)]">
60
+ {post.excerpt}
61
+ </p>
62
+ </div>
63
+
64
+ <div className="mt-6 card p-6">
65
+ <h2 className="text-[0.95rem] font-semibold">Tags</h2>
66
+ <div className="mt-3 flex flex-wrap gap-1.5">
67
+ {post.tags.map((t) => (
68
+ <span key={t} className="badge badge-accent">
69
+ {t}
70
+ </span>
71
+ ))}
72
+ </div>
73
+ </div>
74
+
75
+ <div className="mt-6 card p-6">
76
+ <h2 className="text-[0.95rem] font-semibold">Body preview</h2>
77
+ <div className="mt-3 max-h-72 overflow-y-auto text-[0.875rem] leading-relaxed text-[var(--color-fg-muted)]">
78
+ {post.body.split("\n\n").map((p, i) => (
79
+ <p key={i} className="mb-3">
80
+ {p}
81
+ </p>
82
+ ))}
83
+ </div>
84
+ </div>
85
+ </div>
86
+ );
87
+ }
@@ -0,0 +1,103 @@
1
+ import type { Metadata } from "swift-rust";
2
+ import { Link } from "swift-rust";
3
+
4
+ export const metadata: Metadata = { title: "New post" };
5
+
6
+ export default function NewPostPage() {
7
+ return (
8
+ <div className="mx-auto max-w-2xl px-6 py-10 sm:px-10">
9
+ <Link
10
+ href="/dashboard/posts"
11
+ className="inline-flex items-center gap-1.5 text-[0.875rem] text-[var(--color-fg-muted)] hover:text-[var(--color-fg)]"
12
+ >
13
+ <svg
14
+ viewBox="0 0 24 24"
15
+ className="h-3.5 w-3.5"
16
+ fill="none"
17
+ stroke="currentColor"
18
+ strokeWidth="2"
19
+ >
20
+ <path d="M19 12H5M11 18l-6-6 6-6" strokeLinecap="round" strokeLinejoin="round" />
21
+ </svg>
22
+ All posts
23
+ </Link>
24
+
25
+ <h1 className="mt-6 text-3xl font-semibold tracking-tight">New post</h1>
26
+ <p className="mt-1 text-[var(--color-fg-muted)]">
27
+ Compose a draft. Save without publishing, or hit publish when you&apos;re ready.
28
+ </p>
29
+
30
+ <form className="mt-8 space-y-6">
31
+ <div>
32
+ <label
33
+ htmlFor="title"
34
+ className="block text-[0.8125rem] font-medium text-[var(--color-fg)]"
35
+ >
36
+ Title
37
+ </label>
38
+ <input
39
+ id="title"
40
+ name="title"
41
+ type="text"
42
+ placeholder="A title for your post"
43
+ className="input mt-2"
44
+ />
45
+ </div>
46
+ <div>
47
+ <label
48
+ htmlFor="slug"
49
+ className="block text-[0.8125rem] font-medium text-[var(--color-fg)]"
50
+ >
51
+ Slug
52
+ </label>
53
+ <input
54
+ id="slug"
55
+ name="slug"
56
+ type="text"
57
+ placeholder="a-slug-for-your-post"
58
+ className="input mt-2 font-mono"
59
+ />
60
+ </div>
61
+ <div>
62
+ <label
63
+ htmlFor="excerpt"
64
+ className="block text-[0.8125rem] font-medium text-[var(--color-fg)]"
65
+ >
66
+ Excerpt
67
+ </label>
68
+ <textarea
69
+ id="excerpt"
70
+ name="excerpt"
71
+ placeholder="A short summary that shows up on the blog index."
72
+ className="textarea mt-2"
73
+ />
74
+ </div>
75
+ <div>
76
+ <label
77
+ htmlFor="body"
78
+ className="block text-[0.8125rem] font-medium text-[var(--color-fg)]"
79
+ >
80
+ Body
81
+ </label>
82
+ <textarea
83
+ id="body"
84
+ name="body"
85
+ placeholder="Write your post here. Markdown is supported."
86
+ className="textarea mt-2 min-h-[16rem] font-mono"
87
+ />
88
+ </div>
89
+ <div className="flex items-center justify-end gap-2 border-t border-[var(--color-border)] pt-6">
90
+ <Link href="/dashboard/posts" className="btn btn-outline">
91
+ Cancel
92
+ </Link>
93
+ <button type="button" className="btn btn-ghost">
94
+ Save draft
95
+ </button>
96
+ <button type="submit" className="btn btn-accent">
97
+ Publish
98
+ </button>
99
+ </div>
100
+ </form>
101
+ </div>
102
+ );
103
+ }
@@ -0,0 +1,81 @@
1
+ import { formatDate } from "@/lib/format";
2
+ import { posts } from "@/lib/posts";
3
+ import type { Metadata } from "swift-rust";
4
+ import { Link } from "swift-rust";
5
+
6
+ export const metadata: Metadata = { title: "Posts" };
7
+
8
+ export default function PostsPage() {
9
+ return (
10
+ <div className="px-6 py-10 sm:px-10">
11
+ <div className="flex flex-wrap items-end justify-between gap-4">
12
+ <div>
13
+ <p className="text-[0.75rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
14
+ Dashboard
15
+ </p>
16
+ <h1 className="mt-1 text-3xl font-semibold tracking-tight">Posts</h1>
17
+ <p className="mt-1 text-[var(--color-fg-muted)]">{posts.length} total</p>
18
+ </div>
19
+ <Link href="/dashboard/posts/new" className="btn btn-primary btn-sm">
20
+ <svg
21
+ viewBox="0 0 24 24"
22
+ className="h-4 w-4"
23
+ fill="none"
24
+ stroke="currentColor"
25
+ strokeWidth="2"
26
+ >
27
+ <path d="M12 5v14M5 12h14" strokeLinecap="round" />
28
+ </svg>
29
+ New post
30
+ </Link>
31
+ </div>
32
+
33
+ <div className="mt-8 card overflow-hidden">
34
+ <table className="w-full text-left text-[0.875rem]">
35
+ <thead>
36
+ <tr className="border-b border-[var(--color-border)] bg-[var(--color-surface-2)] text-[0.7rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
37
+ <th className="px-5 py-3 font-semibold">Title</th>
38
+ <th className="px-5 py-3 font-semibold">Author</th>
39
+ <th className="px-5 py-3 font-semibold">Status</th>
40
+ <th className="px-5 py-3 font-semibold">Tags</th>
41
+ <th className="px-5 py-3 text-right font-semibold">Date</th>
42
+ </tr>
43
+ </thead>
44
+ <tbody>
45
+ {posts.map((p) => (
46
+ <tr
47
+ key={p.slug}
48
+ className="border-b border-[var(--color-border)] last:border-0 hover:bg-[var(--color-surface-2)]"
49
+ >
50
+ <td className="px-5 py-3.5">
51
+ <Link
52
+ href={`/dashboard/posts/${p.slug}`}
53
+ className="font-medium text-[var(--color-fg)] hover:text-[var(--color-accent)]"
54
+ >
55
+ {p.title}
56
+ </Link>
57
+ </td>
58
+ <td className="px-5 py-3.5 text-[var(--color-fg-muted)]">{p.author.name}</td>
59
+ <td className="px-5 py-3.5">
60
+ <span className="badge badge-success badge-dot">Published</span>
61
+ </td>
62
+ <td className="px-5 py-3.5">
63
+ <div className="flex flex-wrap gap-1">
64
+ {p.tags.map((t) => (
65
+ <span key={t} className="badge">
66
+ {t}
67
+ </span>
68
+ ))}
69
+ </div>
70
+ </td>
71
+ <td className="px-5 py-3.5 text-right text-[var(--color-fg-subtle)]">
72
+ <time dateTime={p.date}>{formatDate(p.date)}</time>
73
+ </td>
74
+ </tr>
75
+ ))}
76
+ </tbody>
77
+ </table>
78
+ </div>
79
+ </div>
80
+ );
81
+ }
@@ -0,0 +1,119 @@
1
+ import type { Metadata } from "swift-rust";
2
+
3
+ export const metadata: Metadata = { title: "Settings" };
4
+
5
+ export default function SettingsPage() {
6
+ return (
7
+ <div className="mx-auto max-w-2xl px-6 py-10 sm:px-10">
8
+ <p className="text-[0.75rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
9
+ Dashboard
10
+ </p>
11
+ <h1 className="mt-1 text-3xl font-semibold tracking-tight">Settings</h1>
12
+ <p className="mt-1 text-[var(--color-fg-muted)]">Manage your project preferences.</p>
13
+
14
+ <div className="mt-10 space-y-10">
15
+ <section>
16
+ <h2 className="text-[0.95rem] font-semibold">Profile</h2>
17
+ <p className="mt-1 text-[0.875rem] text-[var(--color-fg-muted)]">
18
+ Information that shows up on posts you author.
19
+ </p>
20
+ <div className="mt-5 space-y-4">
21
+ <div>
22
+ <label htmlFor="name" className="block text-[0.8125rem] font-medium">
23
+ Name
24
+ </label>
25
+ <input id="name" type="text" defaultValue="Alex Chen" className="input mt-1.5" />
26
+ </div>
27
+ <div>
28
+ <label htmlFor="email" className="block text-[0.8125rem] font-medium">
29
+ Email
30
+ </label>
31
+ <input
32
+ id="email"
33
+ type="email"
34
+ defaultValue="alex@swift-rust.dev"
35
+ className="input mt-1.5"
36
+ />
37
+ </div>
38
+ <div>
39
+ <label htmlFor="bio" className="block text-[0.8125rem] font-medium">
40
+ Bio
41
+ </label>
42
+ <textarea
43
+ id="bio"
44
+ className="textarea mt-1.5"
45
+ defaultValue="Founding engineer at Swift Rust. Likes Rust, dislikes webpack."
46
+ />
47
+ </div>
48
+ </div>
49
+ </section>
50
+
51
+ <section className="border-t border-[var(--color-border)] pt-8">
52
+ <h2 className="text-[0.95rem] font-semibold">Rendering</h2>
53
+ <p className="mt-1 text-[0.875rem] text-[var(--color-fg-muted)]">
54
+ How the framework renders pages for this project.
55
+ </p>
56
+ <div className="mt-5 grid gap-2">
57
+ {[
58
+ {
59
+ id: "ssr-wasm",
60
+ label: "SSR + WASM",
61
+ hint: "Default. Server-rendered HTML, WASM-hydrated.",
62
+ },
63
+ { id: "ssr", label: "SSR only", hint: "No client JavaScript." },
64
+ { id: "ssr-htmx", label: "SSR + HTMX", hint: "Progressive enhancement." },
65
+ { id: "wasm", label: "Full WASM SPA", hint: "Single-page app in WebAssembly." },
66
+ ].map((opt, i) => (
67
+ <label
68
+ key={opt.id}
69
+ className="flex cursor-pointer items-start gap-3 rounded-lg border border-[var(--color-border)] p-4 transition-colors hover:bg-[var(--color-surface-2)] has-[:checked]:border-[var(--color-accent)] has-[:checked]:bg-[var(--color-accent-soft)]"
70
+ >
71
+ <input
72
+ type="radio"
73
+ name="renderer"
74
+ defaultChecked={i === 0}
75
+ className="mt-1 h-4 w-4 cursor-pointer accent-[var(--color-accent)]"
76
+ />
77
+ <div>
78
+ <p className="font-medium">{opt.label}</p>
79
+ <p className="text-[0.8125rem] text-[var(--color-fg-muted)]">{opt.hint}</p>
80
+ </div>
81
+ </label>
82
+ ))}
83
+ </div>
84
+ </section>
85
+
86
+ <section className="border-t border-[var(--color-border)] pt-8">
87
+ <h2 className="text-[0.95rem] font-semibold">Danger zone</h2>
88
+ <p className="mt-1 text-[0.875rem] text-[var(--color-fg-muted)]">
89
+ Irreversible actions. Be sure before proceeding.
90
+ </p>
91
+ <div className="mt-5 flex items-center justify-between rounded-lg border border-[#fecaca] bg-[#fef2f2] p-4">
92
+ <div>
93
+ <p className="font-medium text-[#991b1b]">Delete project</p>
94
+ <p className="text-[0.8125rem] text-[#b91c1c]">
95
+ All posts, settings, and data will be permanently removed.
96
+ </p>
97
+ </div>
98
+ <button
99
+ type="button"
100
+ className="btn btn-sm"
101
+ style={{ background: "#dc2626", color: "white" }}
102
+ >
103
+ Delete
104
+ </button>
105
+ </div>
106
+ </section>
107
+
108
+ <div className="flex justify-end gap-2 border-t border-[var(--color-border)] pt-8">
109
+ <button type="button" className="btn btn-outline">
110
+ Cancel
111
+ </button>
112
+ <button type="submit" className="btn btn-primary">
113
+ Save changes
114
+ </button>
115
+ </div>
116
+ </div>
117
+ </div>
118
+ );
119
+ }
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { useEffect } from "react";
3
+ import { Link } from "swift-rust";
4
+
5
+ export default function GlobalError({
6
+ error,
7
+ reset,
8
+ }: {
9
+ error: Error & { digest?: string };
10
+ reset: () => void;
11
+ }) {
12
+ useEffect(() => {
13
+ console.error(error);
14
+ }, [error]);
15
+
16
+ return (
17
+ <div className="container-page py-24">
18
+ <div className="mx-auto max-w-xl text-center">
19
+ <span className="badge badge-dot" style={{ background: "#fef2f2", color: "#dc2626" }}>
20
+ Runtime error
21
+ </span>
22
+ <h1 className="mt-5 text-4xl font-semibold tracking-tight">Something went wrong.</h1>
23
+ <p className="mt-4 text-[var(--color-fg-muted)]">
24
+ We logged the error. You can try again, or head back to the homepage.
25
+ </p>
26
+ {error.digest ? (
27
+ <p className="mt-2 font-mono text-[0.75rem] text-[var(--color-fg-subtle)]">
28
+ digest: {error.digest}
29
+ </p>
30
+ ) : null}
31
+ <div className="mt-8 flex justify-center gap-2">
32
+ <button type="button" onClick={reset} className="btn btn-primary">
33
+ Try again
34
+ </button>
35
+ <Link href="/" className="btn btn-outline">
36
+ Home
37
+ </Link>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ );
42
+ }
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
2
+ <rect width="32" height="32" rx="7" fill="#0a0a0a"/>
3
+ <g stroke="#ffffff" stroke-width="1.9" stroke-linecap="round" fill="none">
4
+ <circle cx="16" cy="16" r="6.4"/>
5
+ <path d="M16 4.4v2.6M16 25v2.6M4.4 16h2.6M25 16h2.6M7.9 7.9l1.8 1.8M22.3 22.3l1.8 1.8M7.9 24.1l1.8-1.8M22.3 9.7l1.8-1.8"/>
6
+ </g>
7
+ <path d="M17.8 9.3 L11.6 17 H15.2 L14.2 22.8 L20.4 15 H16.8 Z" fill="#fb923c"/>
8
+ </svg>