@pylonsync/create-pylon 0.11.0 → 0.11.3

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 (154) hide show
  1. package/bin/create-pylon.js +1 -1
  2. package/package.json +1 -1
  3. package/templates/_root/AGENTS.md +31 -1
  4. package/templates/agency/AGENTS.md +1 -1
  5. package/templates/agency/app/(marketing)/contact-form.tsx +22 -32
  6. package/templates/agency/app/(marketing)/layout.tsx +36 -64
  7. package/templates/agency/app/(marketing)/not-found.tsx +6 -8
  8. package/templates/agency/app/(marketing)/page.tsx +93 -144
  9. package/templates/agency/app/(marketing)/work/[slug]/page.tsx +69 -71
  10. package/templates/agency/app/(marketing)/work/page.tsx +20 -17
  11. package/templates/agency/app/globals.css +15 -6
  12. package/templates/agency/app.ts +10 -0
  13. package/templates/agency/components/marketing.tsx +40 -104
  14. package/templates/agency/lib/site.config.ts +34 -52
  15. package/templates/agency/public/images/hero.jpg +0 -0
  16. package/templates/agency/public/images/team/claire-donovan.jpg +0 -0
  17. package/templates/agency/public/images/team/dana-okafor.jpg +0 -0
  18. package/templates/agency/public/images/team/marcus-lee.jpg +0 -0
  19. package/templates/agency/public/images/work/atlas-health.jpg +0 -0
  20. package/templates/agency/public/images/work/cohort.jpg +0 -0
  21. package/templates/agency/public/images/work/ledger.jpg +0 -0
  22. package/templates/agency/public/images/work/vela.jpg +0 -0
  23. package/templates/ai-chat/AGENTS.md +1 -1
  24. package/templates/ai-studio/AGENTS.md +1 -1
  25. package/templates/barebones/AGENTS.md +1 -1
  26. package/templates/chat/AGENTS.md +1 -1
  27. package/templates/consumer/AGENTS.md +1 -1
  28. package/templates/consumer/README.md +39 -23
  29. package/templates/consumer/app/(app)/explore/page.tsx +13 -0
  30. package/templates/consumer/app/(app)/layout.tsx +17 -0
  31. package/templates/consumer/app/(app)/p/[id]/page.tsx +13 -0
  32. package/templates/consumer/app/(app)/page.tsx +15 -0
  33. package/templates/consumer/app/(app)/u/[username]/page.tsx +14 -0
  34. package/templates/consumer/app/error.tsx +1 -1
  35. package/templates/consumer/app/globals.css +25 -0
  36. package/templates/consumer/app/layout.tsx +3 -10
  37. package/templates/consumer/app/llms.ts +10 -10
  38. package/templates/consumer/app/not-found.tsx +9 -22
  39. package/templates/consumer/app/robots.ts +3 -4
  40. package/templates/consumer/app/sitemap.ts +7 -21
  41. package/templates/consumer/app.ts +152 -33
  42. package/templates/consumer/components/social/app-chrome.tsx +109 -0
  43. package/templates/consumer/components/social/avatar.tsx +50 -0
  44. package/templates/consumer/components/social/comment-form.tsx +51 -0
  45. package/templates/consumer/components/social/create-post.tsx +155 -0
  46. package/templates/consumer/components/social/edit-profile.tsx +103 -0
  47. package/templates/consumer/components/social/explore-screen.tsx +23 -0
  48. package/templates/consumer/components/social/home-screen.tsx +161 -0
  49. package/templates/consumer/components/social/post-card.tsx +77 -0
  50. package/templates/consumer/components/social/post-grid.tsx +46 -0
  51. package/templates/consumer/components/social/post-parts.tsx +146 -0
  52. package/templates/consumer/components/social/post-screen.tsx +173 -0
  53. package/templates/consumer/components/social/profile-screen.tsx +146 -0
  54. package/templates/consumer/components/social/rich-text.tsx +22 -0
  55. package/templates/consumer/components/social/session.tsx +43 -0
  56. package/templates/consumer/components/social/use-social.ts +86 -0
  57. package/templates/consumer/functions/addComment.ts +27 -0
  58. package/templates/consumer/functions/createPost.ts +39 -0
  59. package/templates/consumer/functions/ensureProfile.ts +39 -0
  60. package/templates/consumer/functions/seedFeed.ts +38 -0
  61. package/templates/consumer/functions/updateProfile.ts +61 -0
  62. package/templates/consumer/lib/seed.ts +192 -0
  63. package/templates/consumer/lib/site.ts +6 -0
  64. package/templates/consumer/lib/social.ts +245 -0
  65. package/templates/consumer/tests/social.test.ts +157 -0
  66. package/templates/creator/AGENTS.md +1 -1
  67. package/templates/creator/app/(marketing)/layout.tsx +35 -69
  68. package/templates/creator/app/(marketing)/newsletter-signup.tsx +15 -26
  69. package/templates/creator/app/(marketing)/not-found.tsx +8 -6
  70. package/templates/creator/app/(marketing)/page.tsx +90 -106
  71. package/templates/creator/app/globals.css +14 -0
  72. package/templates/creator/app/layout.tsx +1 -0
  73. package/templates/creator/app.ts +10 -0
  74. package/templates/creator/components/marketing.tsx +28 -89
  75. package/templates/creator/lib/site.config.ts +51 -29
  76. package/templates/creator/public/images/headshot.jpg +0 -0
  77. package/templates/crm/AGENTS.md +1 -1
  78. package/templates/crm/lib/seed.ts +153 -21
  79. package/templates/directory/AGENTS.md +1 -1
  80. package/templates/directory/app/(marketing)/directory-browse.tsx +99 -137
  81. package/templates/directory/app/(marketing)/layout.tsx +37 -88
  82. package/templates/directory/app/(marketing)/not-found.tsx +11 -10
  83. package/templates/directory/app/(marketing)/page.tsx +12 -33
  84. package/templates/directory/app/(marketing)/submit/page.tsx +9 -10
  85. package/templates/directory/app/(marketing)/submit-form.tsx +26 -26
  86. package/templates/directory/app/globals.css +15 -4
  87. package/templates/directory/app.ts +10 -0
  88. package/templates/directory/components/marketing.tsx +27 -91
  89. package/templates/directory/lib/site.config.ts +13 -24
  90. package/templates/expo/mobile/apps/expo/README.md +1 -1
  91. package/templates/expo/mobile/apps/expo/package.json +55 -54
  92. package/templates/expo/mobile/apps/expo/src/entitlements.ts +24 -6
  93. package/templates/helpdesk/AGENTS.md +1 -1
  94. package/templates/helpdesk/lib/seed.ts +188 -104
  95. package/templates/inventory/AGENTS.md +1 -1
  96. package/templates/inventory/lib/seed.ts +175 -44
  97. package/templates/invoices/AGENTS.md +1 -1
  98. package/templates/invoices/lib/seed.ts +74 -89
  99. package/templates/local-service/AGENTS.md +1 -1
  100. package/templates/local-service/app/(marketing)/booking-widget.tsx +31 -31
  101. package/templates/local-service/app/(marketing)/layout.tsx +25 -37
  102. package/templates/local-service/app/(marketing)/not-found.tsx +4 -4
  103. package/templates/local-service/app/(marketing)/page.tsx +145 -139
  104. package/templates/local-service/app/globals.css +11 -0
  105. package/templates/local-service/app/layout.tsx +2 -0
  106. package/templates/local-service/app.ts +9 -0
  107. package/templates/local-service/components/marketing.tsx +14 -39
  108. package/templates/local-service/lib/site.config.ts +16 -23
  109. package/templates/local-service/public/images/hero.jpg +0 -0
  110. package/templates/marketplace/AGENTS.md +1 -1
  111. package/templates/marketplace/client/SellForm.tsx +2 -0
  112. package/templates/projects/AGENTS.md +1 -1
  113. package/templates/projects/lib/seed.ts +103 -32
  114. package/templates/restaurant/AGENTS.md +1 -1
  115. package/templates/restaurant/app/(marketing)/layout.tsx +26 -48
  116. package/templates/restaurant/app/(marketing)/page.tsx +146 -123
  117. package/templates/restaurant/app/globals.css +11 -0
  118. package/templates/restaurant/app/layout.tsx +1 -0
  119. package/templates/restaurant/app.ts +10 -0
  120. package/templates/restaurant/components/marketing.tsx +10 -0
  121. package/templates/restaurant/lib/site.config.ts +16 -14
  122. package/templates/restaurant/public/images/bar.jpg +0 -0
  123. package/templates/restaurant/public/images/branzino.jpg +0 -0
  124. package/templates/restaurant/public/images/hearth.jpg +0 -0
  125. package/templates/restaurant/public/images/hero.jpg +0 -0
  126. package/templates/saas/AGENTS.md +1 -1
  127. package/templates/shop/AGENTS.md +1 -1
  128. package/templates/shop/public/images/products/cedar-smoke.jpg +0 -0
  129. package/templates/shop/public/images/products/linen.jpg +0 -0
  130. package/templates/todo/AGENTS.md +1 -1
  131. package/templates/waitlist/AGENTS.md +1 -1
  132. package/templates/waitlist/app/(marketing)/layout.tsx +39 -85
  133. package/templates/waitlist/app/(marketing)/not-found.tsx +8 -9
  134. package/templates/waitlist/app/(marketing)/page.tsx +51 -86
  135. package/templates/waitlist/app/(marketing)/waitlist-hero.tsx +43 -83
  136. package/templates/waitlist/app/globals.css +23 -0
  137. package/templates/waitlist/app/layout.tsx +1 -0
  138. package/templates/waitlist/app.ts +18 -0
  139. package/templates/waitlist/components/marketing.tsx +67 -81
  140. package/templates/waitlist/lib/site.config.ts +52 -59
  141. package/templates/agency/app/not-found.tsx +0 -44
  142. package/templates/ai-chat/app/not-found.tsx +0 -44
  143. package/templates/ai-studio/app/not-found.tsx +0 -44
  144. package/templates/consumer/app/feed-client.tsx +0 -159
  145. package/templates/consumer/app/page.tsx +0 -27
  146. package/templates/consumer/functions/_keep.ts +0 -13
  147. package/templates/consumer/functions/seedPosts.ts +0 -56
  148. package/templates/consumer/tests/example.test.ts +0 -12
  149. package/templates/creator/app/not-found.tsx +0 -44
  150. package/templates/directory/app/not-found.tsx +0 -44
  151. package/templates/local-service/app/not-found.tsx +0 -44
  152. package/templates/restaurant/app/not-found.tsx +0 -44
  153. package/templates/shop/app/not-found.tsx +0 -44
  154. package/templates/waitlist/app/not-found.tsx +0 -44
@@ -1,159 +0,0 @@
1
- "use client";
2
-
3
- import React, { useEffect, useState } from "react";
4
- import { db, callFn } from "@pylonsync/react";
5
- import { EnsureGuest, useAuth } from "@pylonsync/client";
6
- import { Button } from "@/components/ui/button";
7
-
8
- export interface Post {
9
- id: string;
10
- authorId: string;
11
- text: string;
12
- createdAt: string;
13
- }
14
-
15
- export interface Like {
16
- id: string;
17
- userId: string;
18
- postId: string;
19
- }
20
-
21
- // `<EnsureGuest>` mints a guest session so anyone can post + like with no
22
- // login. Inside it, `useAuth()` exposes the guest's `userId` — that's how we
23
- // know which posts the current visitor has already liked.
24
- export function Feed() {
25
- return (
26
- <EnsureGuest fallback={<FeedSkeleton />}>
27
- <FeedInner />
28
- </EnsureGuest>
29
- );
30
- }
31
-
32
- function FeedInner() {
33
- const { userId } = useAuth();
34
- const [text, setText] = useState("");
35
- // Seed a few demo posts on first visit so the feed isn't empty. No-op once
36
- // any post exists; safe to call every mount.
37
- useEffect(() => {
38
- void callFn("seedPosts", {});
39
- }, []);
40
- const { data: posts, loading } = db.useQuery<Post>("Post");
41
- // Public-read, so this is every like in the system — we count + match them
42
- // client-side. db.useQuery is live, so counts update the instant anyone likes.
43
- const { data: likes } = db.useQuery<Like>("Like");
44
-
45
- async function addPost(e: React.FormEvent) {
46
- e.preventDefault();
47
- const value = text.trim();
48
- if (!value) return;
49
- setText("");
50
- // No authorId sent — field.owner() stamps it from the session.
51
- await db.insert("Post", { text: value });
52
- }
53
-
54
- function toggleLike(postId: string) {
55
- const mine = likes.find((l) => l.postId === postId && l.userId === userId);
56
- if (mine) {
57
- void db.delete("Like", mine.id);
58
- } else {
59
- void db.insert("Like", { postId });
60
- }
61
- }
62
-
63
- const ordered = [...posts].sort((a, b) =>
64
- b.createdAt.localeCompare(a.createdAt),
65
- );
66
-
67
- return (
68
- <div className="space-y-6">
69
- <form onSubmit={addPost} className="space-y-2">
70
- <textarea
71
- value={text}
72
- onChange={(e) => setText(e.target.value)}
73
- placeholder="What's happening?"
74
- aria-label="Post"
75
- rows={2}
76
- className="w-full resize-none rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm outline-none focus-visible:ring-1 focus-visible:ring-ring"
77
- />
78
- <div className="flex justify-end">
79
- <Button type="submit" disabled={!text.trim()}>
80
- Post
81
- </Button>
82
- </div>
83
- </form>
84
-
85
- {loading && posts.length === 0 ? (
86
- <FeedSkeleton />
87
- ) : ordered.length === 0 ? (
88
- <p className="text-sm text-muted-foreground">
89
- No posts yet — say something above. It appears instantly and syncs;
90
- open a second tab to watch the feed update live.
91
- </p>
92
- ) : (
93
- <ul className="space-y-3">
94
- {ordered.map((post) => {
95
- const count = likes.filter((l) => l.postId === post.id).length;
96
- const liked = likes.some(
97
- (l) => l.postId === post.id && l.userId === userId,
98
- );
99
- const mine = post.authorId === userId;
100
- return (
101
- <li key={post.id} className="rounded-lg border px-4 py-3">
102
- <div className="flex items-center justify-between text-xs text-muted-foreground">
103
- <span className="font-mono">
104
- {mine ? "you" : shortId(post.authorId)}
105
- </span>
106
- {mine && (
107
- <button
108
- type="button"
109
- aria-label="Delete post"
110
- onClick={() => db.delete("Post", post.id)}
111
- className="hover:text-red-600"
112
- >
113
- Delete
114
- </button>
115
- )}
116
- </div>
117
- <p className="mt-1 whitespace-pre-wrap text-sm">{post.text}</p>
118
- <div className="mt-2">
119
- <button
120
- type="button"
121
- onClick={() => toggleLike(post.id)}
122
- aria-pressed={liked}
123
- className={
124
- "inline-flex items-center gap-1.5 text-xs transition-colors " +
125
- (liked
126
- ? "text-rose-600"
127
- : "text-muted-foreground hover:text-foreground")
128
- }
129
- >
130
- <span>{liked ? "♥" : "♡"}</span>
131
- <span>{count}</span>
132
- </button>
133
- </div>
134
- </li>
135
- );
136
- })}
137
- </ul>
138
- )}
139
- </div>
140
- );
141
- }
142
-
143
- // Guest ids look like `guest_<hex>`; show a short, stable handle.
144
- function shortId(id: string) {
145
- return "@" + id.replace(/^guest_/, "").slice(0, 6);
146
- }
147
-
148
- function FeedSkeleton() {
149
- return (
150
- <ul className="space-y-3" aria-hidden>
151
- {[0, 1, 2].map((i) => (
152
- <li key={i} className="rounded-lg border px-4 py-3">
153
- <span className="block h-3 w-16 rounded bg-muted" />
154
- <span className="mt-2 block h-4 w-3/4 rounded bg-muted" />
155
- </li>
156
- ))}
157
- </ul>
158
- );
159
- }
@@ -1,27 +0,0 @@
1
- import React from "react";
2
- import { type Metadata } from "@pylonsync/react";
3
- import { Feed } from "./feed-client";
4
-
5
- export const metadata: Metadata = {
6
- title: "__APP_NAME__ — a live social feed on Pylon",
7
- description:
8
- "A public feed with optimistic posts and likes, server-rendered over one Pylon backend. Open two tabs and watch it sync.",
9
- };
10
-
11
- // `app/page.tsx` → `/`. The intro is server-rendered; `<Feed>` is a client
12
- // island that mints a guest session and runs the live query + optimistic
13
- // posts/likes in the browser.
14
- export default function IndexPage() {
15
- return (
16
- <div>
17
- <header className="mb-6">
18
- <h1 className="text-2xl font-semibold tracking-tight">Feed</h1>
19
- <p className="mt-1 text-sm text-muted-foreground">
20
- Post something — it appears instantly and syncs to every tab. Likes
21
- are live too.
22
- </p>
23
- </header>
24
- <Feed />
25
- </div>
26
- );
27
- }
@@ -1,13 +0,0 @@
1
- // Server functions go here. Each file in this directory that exports a
2
- // query() or action() becomes a typed RPC endpoint, callable from your
3
- // pages and client with full type inference. Delete this placeholder when
4
- // you add your first one.
5
- //
6
- // Example (functions/notes.ts):
7
- //
8
- // import { query } from "@pylonsync/functions";
9
- //
10
- // export const listNotes = query(async (ctx) => {
11
- // return ctx.db.list("Note");
12
- // });
13
- export {};
@@ -1,56 +0,0 @@
1
- import { mutation } from "@pylonsync/functions";
2
-
3
- // A few demo posts so the feed isn't an empty shell on first visit. The feed
4
- // calls this on mount; it's a no-op once any post exists (the lock guards
5
- // against a double-seed from two concurrent first-visits). Public so an
6
- // anonymous first visitor seeds it — it only writes demo content.
7
- //
8
- // `unsafe.insert` sets the demo `authorId` + a backdated `createdAt` directly
9
- // (bypassing the `field.owner()` stamp + policies) so the seed reads like a few
10
- // different people already posted, instead of everything attributed to the
11
- // first visitor.
12
- const DEMO_POSTS: { author: string; text: string }[] = [
13
- {
14
- author: "guest_ada",
15
- text: "just shipped my first Pylon app — one binary doing SSR + sync + auth. wild.",
16
- },
17
- {
18
- author: "guest_lin",
19
- text: "the feed updates across tabs with zero websocket code I wrote. open a second tab 👀",
20
- },
21
- {
22
- author: "guest_rey",
23
- text: "likes are just rows — delete the row to unlike. local-first, so it's instant.",
24
- },
25
- {
26
- author: "guest_max",
27
- text: "no separate backend to deploy. `pylon deploy` and it's live. that's the whole thing.",
28
- },
29
- ];
30
-
31
- export default mutation<
32
- Record<string, never>,
33
- { seeded: boolean; count: number }
34
- >({
35
- auth: "public",
36
- async handler(ctx) {
37
- await ctx.db.advisoryLock("consumer_seed_posts");
38
- const existing = await ctx.db.unsafe.list("Post");
39
- if (existing.length > 0) return { seeded: false, count: existing.length };
40
-
41
- const now = Date.now();
42
- for (let i = 0; i < DEMO_POSTS.length; i++) {
43
- const p = DEMO_POSTS[i];
44
- // Backdate a minute apart so they sort into a natural order.
45
- const createdAt = new Date(
46
- now - (DEMO_POSTS.length - i) * 60_000,
47
- ).toISOString();
48
- await ctx.db.unsafe.insert("Post", {
49
- authorId: p.author,
50
- text: p.text,
51
- createdAt,
52
- });
53
- }
54
- return { seeded: true, count: DEMO_POSTS.length };
55
- },
56
- });
@@ -1,12 +0,0 @@
1
- import { expect, test } from "bun:test";
2
-
3
- // Your first test. Run the suite with: pylon test (or: npm test)
4
- //
5
- // `pylon test` discovers every *.test.ts file under tests/ (or functions/) and
6
- // runs it with Bun's test runner against an in-memory Pylon. Replace this
7
- // placeholder with tests for your functions and helpers — see AGENTS.md →
8
- // Testing for the pure-logic and HTTP-against-`pylon dev` patterns.
9
-
10
- test("placeholder — replace me with a real test", () => {
11
- expect(true).toBe(true);
12
- });
@@ -1,44 +0,0 @@
1
- import React from "react";
2
- import { Link, type NotFoundProps } from "@pylonsync/react";
3
- import { Button } from "@/components/ui/button";
4
-
5
- // `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
6
- // page calls `response.notFound()`.
7
- //
8
- // Give it real links. This boundary is also what an agent reads when it guesses
9
- // a URL wrong: a markdown request for a missing page renders THIS file as
10
- // markdown at 404, so the links below are how it finds its way back.
11
- export default function NotFound(_props: NotFoundProps) {
12
- return (
13
- <div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
14
- <section>
15
- <h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
16
- <p className="mt-2 text-muted-foreground">
17
- That URL does not exist. Try one of these:
18
- </p>
19
- </section>
20
- <ul className="space-y-2 text-sm">
21
- <li>
22
- <Link className="underline underline-offset-4" href="/">
23
- Home
24
- </Link>
25
- </li>
26
- <li>
27
- <a className="underline underline-offset-4" href="/sitemap.xml">
28
- Sitemap
29
- </a>
30
- </li>
31
- <li>
32
- <a className="underline underline-offset-4" href="/llms.txt">
33
- llms.txt
34
- </a>
35
- </li>
36
- </ul>
37
- <div>
38
- <Button asChild>
39
- <Link href="/">Back to home</Link>
40
- </Button>
41
- </div>
42
- </div>
43
- );
44
- }
@@ -1,44 +0,0 @@
1
- import React from "react";
2
- import { Link, type NotFoundProps } from "@pylonsync/react";
3
- import { Button } from "@/components/ui/button";
4
-
5
- // `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
6
- // page calls `response.notFound()`.
7
- //
8
- // Give it real links. This boundary is also what an agent reads when it guesses
9
- // a URL wrong: a markdown request for a missing page renders THIS file as
10
- // markdown at 404, so the links below are how it finds its way back.
11
- export default function NotFound(_props: NotFoundProps) {
12
- return (
13
- <div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
14
- <section>
15
- <h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
16
- <p className="mt-2 text-muted-foreground">
17
- That URL does not exist. Try one of these:
18
- </p>
19
- </section>
20
- <ul className="space-y-2 text-sm">
21
- <li>
22
- <Link className="underline underline-offset-4" href="/">
23
- Home
24
- </Link>
25
- </li>
26
- <li>
27
- <a className="underline underline-offset-4" href="/sitemap.xml">
28
- Sitemap
29
- </a>
30
- </li>
31
- <li>
32
- <a className="underline underline-offset-4" href="/llms.txt">
33
- llms.txt
34
- </a>
35
- </li>
36
- </ul>
37
- <div>
38
- <Button asChild>
39
- <Link href="/">Back to home</Link>
40
- </Button>
41
- </div>
42
- </div>
43
- );
44
- }
@@ -1,44 +0,0 @@
1
- import React from "react";
2
- import { Link, type NotFoundProps } from "@pylonsync/react";
3
- import { Button } from "@/components/ui/button";
4
-
5
- // `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
6
- // page calls `response.notFound()`.
7
- //
8
- // Give it real links. This boundary is also what an agent reads when it guesses
9
- // a URL wrong: a markdown request for a missing page renders THIS file as
10
- // markdown at 404, so the links below are how it finds its way back.
11
- export default function NotFound(_props: NotFoundProps) {
12
- return (
13
- <div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
14
- <section>
15
- <h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
16
- <p className="mt-2 text-muted-foreground">
17
- That URL does not exist. Try one of these:
18
- </p>
19
- </section>
20
- <ul className="space-y-2 text-sm">
21
- <li>
22
- <Link className="underline underline-offset-4" href="/">
23
- Home
24
- </Link>
25
- </li>
26
- <li>
27
- <a className="underline underline-offset-4" href="/sitemap.xml">
28
- Sitemap
29
- </a>
30
- </li>
31
- <li>
32
- <a className="underline underline-offset-4" href="/llms.txt">
33
- llms.txt
34
- </a>
35
- </li>
36
- </ul>
37
- <div>
38
- <Button asChild>
39
- <Link href="/">Back to home</Link>
40
- </Button>
41
- </div>
42
- </div>
43
- );
44
- }
@@ -1,44 +0,0 @@
1
- import React from "react";
2
- import { Link, type NotFoundProps } from "@pylonsync/react";
3
- import { Button } from "@/components/ui/button";
4
-
5
- // `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
6
- // page calls `response.notFound()`.
7
- //
8
- // Give it real links. This boundary is also what an agent reads when it guesses
9
- // a URL wrong: a markdown request for a missing page renders THIS file as
10
- // markdown at 404, so the links below are how it finds its way back.
11
- export default function NotFound(_props: NotFoundProps) {
12
- return (
13
- <div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
14
- <section>
15
- <h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
16
- <p className="mt-2 text-muted-foreground">
17
- That URL does not exist. Try one of these:
18
- </p>
19
- </section>
20
- <ul className="space-y-2 text-sm">
21
- <li>
22
- <Link className="underline underline-offset-4" href="/">
23
- Home
24
- </Link>
25
- </li>
26
- <li>
27
- <a className="underline underline-offset-4" href="/sitemap.xml">
28
- Sitemap
29
- </a>
30
- </li>
31
- <li>
32
- <a className="underline underline-offset-4" href="/llms.txt">
33
- llms.txt
34
- </a>
35
- </li>
36
- </ul>
37
- <div>
38
- <Button asChild>
39
- <Link href="/">Back to home</Link>
40
- </Button>
41
- </div>
42
- </div>
43
- );
44
- }
@@ -1,44 +0,0 @@
1
- import React from "react";
2
- import { Link, type NotFoundProps } from "@pylonsync/react";
3
- import { Button } from "@/components/ui/button";
4
-
5
- // `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
6
- // page calls `response.notFound()`.
7
- //
8
- // Give it real links. This boundary is also what an agent reads when it guesses
9
- // a URL wrong: a markdown request for a missing page renders THIS file as
10
- // markdown at 404, so the links below are how it finds its way back.
11
- export default function NotFound(_props: NotFoundProps) {
12
- return (
13
- <div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
14
- <section>
15
- <h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
16
- <p className="mt-2 text-muted-foreground">
17
- That URL does not exist. Try one of these:
18
- </p>
19
- </section>
20
- <ul className="space-y-2 text-sm">
21
- <li>
22
- <Link className="underline underline-offset-4" href="/">
23
- Home
24
- </Link>
25
- </li>
26
- <li>
27
- <a className="underline underline-offset-4" href="/sitemap.xml">
28
- Sitemap
29
- </a>
30
- </li>
31
- <li>
32
- <a className="underline underline-offset-4" href="/llms.txt">
33
- llms.txt
34
- </a>
35
- </li>
36
- </ul>
37
- <div>
38
- <Button asChild>
39
- <Link href="/">Back to home</Link>
40
- </Button>
41
- </div>
42
- </div>
43
- );
44
- }
@@ -1,44 +0,0 @@
1
- import React from "react";
2
- import { Link, type NotFoundProps } from "@pylonsync/react";
3
- import { Button } from "@/components/ui/button";
4
-
5
- // `app/not-found.tsx` → rendered at HTTP 404 for any unmatched URL, and when a
6
- // page calls `response.notFound()`.
7
- //
8
- // Give it real links. This boundary is also what an agent reads when it guesses
9
- // a URL wrong: a markdown request for a missing page renders THIS file as
10
- // markdown at 404, so the links below are how it finds its way back.
11
- export default function NotFound(_props: NotFoundProps) {
12
- return (
13
- <div className="mx-auto flex min-h-[60vh] max-w-lg flex-col justify-center gap-6 px-4">
14
- <section>
15
- <h1 className="text-2xl font-semibold tracking-tight">Page not found</h1>
16
- <p className="mt-2 text-muted-foreground">
17
- That URL does not exist. Try one of these:
18
- </p>
19
- </section>
20
- <ul className="space-y-2 text-sm">
21
- <li>
22
- <Link className="underline underline-offset-4" href="/">
23
- Home
24
- </Link>
25
- </li>
26
- <li>
27
- <a className="underline underline-offset-4" href="/sitemap.xml">
28
- Sitemap
29
- </a>
30
- </li>
31
- <li>
32
- <a className="underline underline-offset-4" href="/llms.txt">
33
- llms.txt
34
- </a>
35
- </li>
36
- </ul>
37
- <div>
38
- <Button asChild>
39
- <Link href="/">Back to home</Link>
40
- </Button>
41
- </div>
42
- </div>
43
- );
44
- }