@pylonsync/create-pylon 0.19.1 → 0.21.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 (696) hide show
  1. package/bin/create-pylon.js +30 -7
  2. package/package.json +1 -1
  3. package/templates/_root/AGENTS.md +1 -1
  4. package/templates/agency/.env.example +7 -0
  5. package/templates/agency/AGENTS.md +1 -1
  6. package/templates/agency/README.md +28 -3
  7. package/templates/agency/app/(marketing)/contact-form.tsx +20 -18
  8. package/templates/agency/app/(marketing)/page.tsx +3 -3
  9. package/templates/agency/app/(marketing)/work/page.tsx +3 -3
  10. package/templates/agency/app/auth-form.tsx +4 -4
  11. package/templates/agency/app/dashboard/dashboard-client.tsx +110 -64
  12. package/templates/agency/app/dashboard/page.tsx +14 -16
  13. package/templates/agency/app/icon.svg +3 -0
  14. package/templates/agency/app/login/page.tsx +12 -14
  15. package/templates/agency/components/marketing.tsx +12 -5
  16. package/templates/agency/functions/seedStudioBackoffice.ts +42 -11
  17. package/templates/agency/lib/agency.ts +22 -0
  18. package/templates/agency/lib/demo.ts +118 -0
  19. package/templates/agency/lib/site.config.ts +19 -13
  20. package/templates/agency/public/favicon.ico +0 -0
  21. package/templates/agency/tests/agency.test.ts +57 -0
  22. package/templates/ai-chat/.env.example +13 -27
  23. package/templates/ai-chat/AGENTS.md +1 -1
  24. package/templates/ai-chat/README.md +53 -60
  25. package/templates/ai-chat/app/(chat)/c/[id]/page.tsx +19 -0
  26. package/templates/ai-chat/app/(chat)/chat-client.tsx +657 -622
  27. package/templates/ai-chat/app/(chat)/layout.tsx +6 -43
  28. package/templates/ai-chat/app/(chat)/not-found.tsx +6 -10
  29. package/templates/ai-chat/app/(chat)/page.tsx +7 -11
  30. package/templates/ai-chat/app/auth-form.tsx +45 -38
  31. package/templates/ai-chat/app/error.tsx +9 -12
  32. package/templates/ai-chat/app/globals.css +464 -102
  33. package/templates/ai-chat/app/layout.tsx +11 -19
  34. package/templates/ai-chat/app/login/page.tsx +39 -24
  35. package/templates/ai-chat/app.ts +118 -43
  36. package/templates/ai-chat/components/chat/composer.tsx +193 -0
  37. package/templates/ai-chat/components/chat/empty-state.tsx +65 -0
  38. package/templates/ai-chat/components/chat/icons.tsx +198 -0
  39. package/templates/ai-chat/components/chat/markdown.tsx +164 -0
  40. package/templates/ai-chat/components/chat/message.tsx +183 -0
  41. package/templates/ai-chat/components/chat/setup.tsx +71 -0
  42. package/templates/ai-chat/components/chat/sidebar.tsx +288 -0
  43. package/templates/ai-chat/functions/_beginTurn.ts +145 -0
  44. package/templates/ai-chat/functions/_finishTurn.ts +55 -0
  45. package/templates/ai-chat/functions/assistantStatus.ts +13 -0
  46. package/templates/ai-chat/functions/claimGuestHistory.ts +39 -0
  47. package/templates/ai-chat/functions/createConversation.ts +28 -0
  48. package/templates/ai-chat/functions/deleteConversation.ts +15 -20
  49. package/templates/ai-chat/functions/prepareGuestClaim.ts +32 -0
  50. package/templates/ai-chat/functions/renameConversation.ts +19 -0
  51. package/templates/ai-chat/functions/respond.ts +88 -0
  52. package/templates/ai-chat/functions/seedExamples.ts +46 -0
  53. package/templates/ai-chat/functions/stopResponse.ts +30 -0
  54. package/templates/ai-chat/lib/brand-icon.ts +7 -0
  55. package/templates/ai-chat/lib/chat.ts +277 -0
  56. package/templates/ai-chat/lib/claim.ts +15 -0
  57. package/templates/ai-chat/lib/examples.ts +179 -0
  58. package/templates/ai-chat/lib/highlight.ts +107 -0
  59. package/templates/ai-chat/lib/markdown.ts +317 -0
  60. package/templates/ai-chat/lib/provider.ts +38 -0
  61. package/templates/ai-chat/lib/site.config.ts +55 -39
  62. package/templates/ai-chat/tests/chat.test.ts +174 -0
  63. package/templates/ai-chat/tests/components.test.tsx +57 -0
  64. package/templates/ai-chat/tests/markdown.test.ts +88 -0
  65. package/templates/ai-chat/tests/provider.test.ts +32 -0
  66. package/templates/ai-studio/AGENTS.md +1 -1
  67. package/templates/backend/consumer/apps/api/app/layout.tsx +33 -0
  68. package/templates/backend/consumer/apps/api/app/page.tsx +54 -0
  69. package/templates/backend/consumer/apps/api/app.ts +166 -0
  70. package/templates/backend/consumer/apps/api/functions/addComment.ts +24 -0
  71. package/templates/backend/consumer/apps/api/functions/createPost.ts +19 -36
  72. package/templates/backend/consumer/apps/api/functions/deletePost.ts +15 -13
  73. package/templates/backend/consumer/apps/api/functions/myProfile.ts +5 -8
  74. package/templates/backend/consumer/apps/api/functions/seedDemo.ts +95 -0
  75. package/templates/backend/consumer/apps/api/functions/setFollow.ts +36 -0
  76. package/templates/backend/consumer/apps/api/functions/setLike.ts +36 -0
  77. package/templates/backend/consumer/apps/api/functions/upsertProfile.ts +43 -37
  78. package/templates/backend/consumer/apps/api/lib/seed.ts +89 -0
  79. package/templates/backend/consumer/apps/api/lib/social.ts +50 -0
  80. package/templates/backend/consumer/apps/api/package.json +8 -2
  81. package/templates/backend/consumer/apps/api/public/images/posts/arch.jpg +0 -0
  82. package/templates/backend/consumer/apps/api/public/images/posts/coffee.jpg +0 -0
  83. package/templates/backend/consumer/apps/api/public/images/posts/dog.jpg +0 -0
  84. package/templates/backend/consumer/apps/api/public/images/posts/lisbon.jpg +0 -0
  85. package/templates/backend/consumer/apps/api/public/images/posts/ramen.jpg +0 -0
  86. package/templates/backend/consumer/apps/api/public/images/posts/run.jpg +0 -0
  87. package/templates/backend/consumer/apps/api/public/images/posts/sunset.jpg +0 -0
  88. package/templates/backend/consumer/apps/api/tsconfig.json +12 -2
  89. package/templates/barebones/AGENTS.md +1 -1
  90. package/templates/chat/AGENTS.md +1 -1
  91. package/templates/chat/README.md +43 -21
  92. package/templates/chat/app/chat-client.tsx +495 -85
  93. package/templates/chat/app/error.tsx +1 -1
  94. package/templates/chat/app/globals.css +170 -54
  95. package/templates/chat/app/layout.tsx +12 -9
  96. package/templates/chat/app/not-found.tsx +4 -2
  97. package/templates/chat/app/page.tsx +9 -19
  98. package/templates/chat/app.ts +96 -17
  99. package/templates/chat/components/chat/avatar.tsx +45 -0
  100. package/templates/chat/components/chat/composer.tsx +110 -0
  101. package/templates/chat/components/chat/message-list.tsx +285 -0
  102. package/templates/chat/components/chat/sidebar.tsx +190 -0
  103. package/templates/chat/functions/joinChat.ts +83 -0
  104. package/templates/chat/functions/renameMember.ts +25 -0
  105. package/templates/chat/lib/chat.ts +188 -0
  106. package/templates/chat/lib/seed.ts +78 -0
  107. package/templates/chat/tests/chat.test.ts +159 -0
  108. package/templates/chat/tests/composer.test.tsx +54 -0
  109. package/templates/consumer/AGENTS.md +1 -1
  110. package/templates/consumer/README.md +3 -3
  111. package/templates/consumer/app/globals.css +10 -3
  112. package/templates/consumer/app/icon.svg +1 -0
  113. package/templates/consumer/components/social/avatar.tsx +25 -11
  114. package/templates/consumer/functions/seedFeed.ts +3 -3
  115. package/templates/consumer/lib/seed.ts +43 -85
  116. package/templates/consumer/public/favicon.ico +0 -0
  117. package/templates/consumer/public/images/posts/arch.jpg +0 -0
  118. package/templates/consumer/public/images/posts/coffee.jpg +0 -0
  119. package/templates/consumer/public/images/posts/dog.jpg +0 -0
  120. package/templates/consumer/public/images/posts/lisbon.jpg +0 -0
  121. package/templates/consumer/public/images/posts/ramen.jpg +0 -0
  122. package/templates/consumer/public/images/posts/run.jpg +0 -0
  123. package/templates/consumer/public/images/posts/sunset.jpg +0 -0
  124. package/templates/consumer/tests/seed.test.ts +39 -0
  125. package/templates/creator/.env.example +7 -0
  126. package/templates/creator/AGENTS.md +1 -1
  127. package/templates/creator/README.md +24 -5
  128. package/templates/creator/app/(marketing)/newsletter-signup.tsx +33 -12
  129. package/templates/creator/app/auth-form.tsx +7 -7
  130. package/templates/creator/app/dashboard/dashboard-client.tsx +137 -100
  131. package/templates/creator/app/dashboard/page.tsx +18 -18
  132. package/templates/creator/app/icon.svg +2 -0
  133. package/templates/creator/app/login/page.tsx +14 -14
  134. package/templates/creator/app.ts +4 -4
  135. package/templates/creator/functions/seedDemo.ts +32 -0
  136. package/templates/creator/functions/subscribe.ts +4 -15
  137. package/templates/creator/lib/demo.ts +94 -0
  138. package/templates/creator/lib/site.config.ts +15 -10
  139. package/templates/creator/lib/stats.ts +35 -0
  140. package/templates/creator/public/favicon.ico +0 -0
  141. package/templates/creator/tests/counter.test.ts +92 -0
  142. package/templates/creator/tests/live-count.test.tsx +45 -0
  143. package/templates/crm/AGENTS.md +1 -1
  144. package/templates/crm/README.md +22 -1
  145. package/templates/crm/app/apple-icon.png +0 -0
  146. package/templates/crm/app/companies/companies-view.tsx +32 -14
  147. package/templates/crm/app/companies/page.tsx +2 -1
  148. package/templates/crm/app/contacts/contacts-view.tsx +34 -10
  149. package/templates/crm/app/contacts/page.tsx +2 -1
  150. package/templates/crm/app/deals/[id]/deal-view.tsx +137 -98
  151. package/templates/crm/app/deals/[id]/page.tsx +2 -1
  152. package/templates/crm/app/globals.css +29 -14
  153. package/templates/crm/app/icon.svg +1 -0
  154. package/templates/crm/app/layout.tsx +7 -5
  155. package/templates/crm/app/page.tsx +2 -1
  156. package/templates/crm/app/pipeline-view.tsx +5 -5
  157. package/templates/crm/app/workspace.tsx +29 -17
  158. package/templates/crm/app.ts +5 -5
  159. package/templates/crm/components/activity-timeline.tsx +14 -4
  160. package/templates/crm/components/app-shell.tsx +94 -0
  161. package/templates/crm/components/auth-form.tsx +35 -5
  162. package/templates/crm/components/avatar.tsx +5 -1
  163. package/templates/crm/components/board-skeleton.tsx +5 -5
  164. package/templates/crm/components/brand-mark.tsx +25 -0
  165. package/templates/crm/components/command-palette.tsx +17 -17
  166. package/templates/crm/components/data-table.tsx +97 -36
  167. package/templates/crm/components/deal-card.tsx +23 -27
  168. package/templates/crm/components/filter-tabs.tsx +101 -0
  169. package/templates/crm/components/metrics-bar.tsx +6 -6
  170. package/templates/crm/components/nav.tsx +15 -0
  171. package/templates/crm/components/page-header.tsx +43 -7
  172. package/templates/crm/components/pipeline-board.tsx +16 -10
  173. package/templates/crm/components/require-auth.tsx +24 -26
  174. package/templates/crm/components/sidebar.tsx +54 -49
  175. package/templates/crm/components/stage-badge.tsx +4 -2
  176. package/templates/crm/components/ui/button.tsx +5 -4
  177. package/templates/crm/components/ui/dialog.tsx +1 -1
  178. package/templates/crm/components/ui/input.tsx +1 -1
  179. package/templates/crm/components/ui/select.tsx +1 -1
  180. package/templates/crm/components/ui/textarea.tsx +1 -1
  181. package/templates/crm/functions/seedWorkspace.ts +32 -17
  182. package/templates/crm/lib/brand.ts +9 -0
  183. package/templates/crm/lib/demo-team.ts +51 -0
  184. package/templates/crm/lib/pipeline.ts +37 -0
  185. package/templates/crm/lib/search.ts +4 -3
  186. package/templates/crm/lib/seed.ts +53 -8
  187. package/templates/crm/public/favicon.ico +0 -0
  188. package/templates/crm/tests/components.test.tsx +5 -5
  189. package/templates/crm/tests/demo-team.test.ts +18 -0
  190. package/templates/crm/tests/pipeline.test.ts +59 -3
  191. package/templates/directory/.env.example +7 -0
  192. package/templates/directory/AGENTS.md +1 -1
  193. package/templates/directory/README.md +22 -3
  194. package/templates/directory/app/(marketing)/directory-browse.tsx +30 -12
  195. package/templates/directory/app/(marketing)/page.tsx +6 -4
  196. package/templates/directory/app/auth-form.tsx +4 -4
  197. package/templates/directory/app/dashboard/dashboard-client.tsx +177 -90
  198. package/templates/directory/app/dashboard/page.tsx +16 -14
  199. package/templates/directory/app/icon.svg +4 -0
  200. package/templates/directory/app/login/page.tsx +16 -15
  201. package/templates/directory/functions/seedDemo.ts +43 -0
  202. package/templates/directory/lib/demo.ts +145 -0
  203. package/templates/directory/lib/directory.ts +27 -0
  204. package/templates/directory/lib/site.config.ts +21 -16
  205. package/templates/directory/public/favicon.ico +0 -0
  206. package/templates/directory/tests/directory.test.ts +46 -0
  207. package/templates/expo/consumer/apps/expo/App.tsx +360 -288
  208. package/templates/expo/consumer/apps/expo/app.json +9 -1
  209. package/templates/expo/consumer/apps/expo/package.json +2 -1
  210. package/templates/helpdesk/AGENTS.md +1 -1
  211. package/templates/helpdesk/README.md +21 -0
  212. package/templates/helpdesk/app/apple-icon.png +0 -0
  213. package/templates/helpdesk/app/customers/customers-view.tsx +23 -7
  214. package/templates/helpdesk/app/customers/page.tsx +2 -1
  215. package/templates/helpdesk/app/globals.css +29 -14
  216. package/templates/helpdesk/app/icon.svg +1 -0
  217. package/templates/helpdesk/app/inbox-view.tsx +66 -47
  218. package/templates/helpdesk/app/layout.tsx +7 -5
  219. package/templates/helpdesk/app/page.tsx +4 -7
  220. package/templates/helpdesk/app/tickets/[id]/page.tsx +2 -1
  221. package/templates/helpdesk/app/tickets/[id]/ticket-view.tsx +134 -97
  222. package/templates/helpdesk/app/workspace.tsx +33 -22
  223. package/templates/helpdesk/app.ts +2 -2
  224. package/templates/helpdesk/components/app-shell.tsx +94 -0
  225. package/templates/helpdesk/components/auth-form.tsx +35 -5
  226. package/templates/helpdesk/components/avatar.tsx +5 -1
  227. package/templates/helpdesk/components/brand-mark.tsx +28 -0
  228. package/templates/helpdesk/components/command-palette.tsx +17 -17
  229. package/templates/helpdesk/components/data-table.tsx +97 -36
  230. package/templates/helpdesk/components/filter-tabs.tsx +101 -0
  231. package/templates/helpdesk/components/nav.tsx +14 -0
  232. package/templates/helpdesk/components/page-header.tsx +43 -7
  233. package/templates/helpdesk/components/priority-badge.tsx +49 -6
  234. package/templates/helpdesk/components/require-auth.tsx +24 -26
  235. package/templates/helpdesk/components/sidebar.tsx +54 -48
  236. package/templates/helpdesk/components/sla-indicator.tsx +5 -8
  237. package/templates/helpdesk/components/thread.tsx +7 -7
  238. package/templates/helpdesk/components/ticket-list.tsx +79 -19
  239. package/templates/helpdesk/components/ui/button.tsx +5 -4
  240. package/templates/helpdesk/components/ui/dialog.tsx +1 -1
  241. package/templates/helpdesk/components/ui/input.tsx +1 -1
  242. package/templates/helpdesk/components/ui/select.tsx +1 -1
  243. package/templates/helpdesk/components/ui/textarea.tsx +1 -1
  244. package/templates/helpdesk/functions/seedWorkspace.ts +25 -15
  245. package/templates/helpdesk/lib/brand.ts +9 -0
  246. package/templates/helpdesk/lib/demo-team.ts +51 -0
  247. package/templates/helpdesk/lib/search.ts +4 -3
  248. package/templates/helpdesk/lib/seed.ts +43 -2
  249. package/templates/helpdesk/lib/tickets.ts +68 -20
  250. package/templates/helpdesk/public/favicon.ico +0 -0
  251. package/templates/helpdesk/tests/components.test.tsx +10 -0
  252. package/templates/helpdesk/tests/demo-team.test.ts +18 -0
  253. package/templates/helpdesk/tests/tickets.test.ts +70 -0
  254. package/templates/imessage/.env.example +49 -0
  255. package/templates/imessage/AGENTS.md +153 -0
  256. package/templates/imessage/CLAUDE.md +7 -0
  257. package/templates/imessage/README.md +187 -0
  258. package/templates/imessage/app/console.tsx +382 -0
  259. package/templates/imessage/app/contacts/page.tsx +13 -0
  260. package/templates/imessage/app/error.tsx +16 -0
  261. package/templates/imessage/app/gate.tsx +31 -0
  262. package/templates/imessage/app/globals.css +183 -0
  263. package/templates/imessage/app/layout.tsx +17 -0
  264. package/templates/imessage/app/login/login-form.tsx +115 -0
  265. package/templates/imessage/app/login/page.tsx +30 -0
  266. package/templates/imessage/app/not-found.tsx +13 -0
  267. package/templates/imessage/app/page.tsx +14 -0
  268. package/templates/imessage/app/robots.ts +6 -0
  269. package/templates/imessage/app/setup/page.tsx +13 -0
  270. package/templates/imessage/app/sign-out.tsx +20 -0
  271. package/templates/imessage/app.ts +299 -0
  272. package/templates/imessage/bunfig +4 -0
  273. package/templates/imessage/components/avatar.tsx +46 -0
  274. package/templates/imessage/components/contact-panel.tsx +263 -0
  275. package/templates/imessage/components/contacts-view.tsx +172 -0
  276. package/templates/imessage/components/conversation-list.tsx +129 -0
  277. package/templates/imessage/components/nav-rail.tsx +67 -0
  278. package/templates/imessage/components/setup-view.tsx +408 -0
  279. package/templates/imessage/components/thread.tsx +229 -0
  280. package/templates/imessage/components/ui/badge.tsx +37 -0
  281. package/templates/imessage/components/ui/button.tsx +56 -0
  282. package/templates/imessage/components/ui/card.tsx +90 -0
  283. package/templates/imessage/components/ui/dialog.tsx +110 -0
  284. package/templates/imessage/components/ui/input.tsx +30 -0
  285. package/templates/imessage/components/ui/label.tsx +21 -0
  286. package/templates/imessage/components/ui/select.tsx +39 -0
  287. package/templates/imessage/components/ui/table.tsx +124 -0
  288. package/templates/imessage/components/ui/textarea.tsx +24 -0
  289. package/templates/imessage/components.json +20 -0
  290. package/templates/imessage/functions/answerNow.ts +34 -0
  291. package/templates/imessage/functions/assistant.ts +96 -0
  292. package/templates/imessage/functions/beginTurn.ts +101 -0
  293. package/templates/imessage/functions/cancelReminder.ts +13 -0
  294. package/templates/imessage/functions/claimOutbound.ts +44 -0
  295. package/templates/imessage/functions/clearDemo.ts +18 -0
  296. package/templates/imessage/functions/deleteNote.ts +11 -0
  297. package/templates/imessage/functions/deliverOutbound.ts +55 -0
  298. package/templates/imessage/functions/finishTurn.ts +84 -0
  299. package/templates/imessage/functions/fireReminder.ts +42 -0
  300. package/templates/imessage/functions/ingestInbound.ts +22 -0
  301. package/templates/imessage/functions/ownerAccount.ts +13 -0
  302. package/templates/imessage/functions/processTurn.ts +74 -0
  303. package/templates/imessage/functions/relayApply.ts +102 -0
  304. package/templates/imessage/functions/relaySync.ts +63 -0
  305. package/templates/imessage/functions/releaseOutbound.ts +18 -0
  306. package/templates/imessage/functions/resolveTurn.ts +25 -0
  307. package/templates/imessage/functions/saveContact.ts +27 -0
  308. package/templates/imessage/functions/seedDemo.ts +70 -0
  309. package/templates/imessage/functions/sendOwnerMessage.ts +28 -0
  310. package/templates/imessage/functions/sendblueWebhook.ts +49 -0
  311. package/templates/imessage/functions/setContactStatus.ts +17 -0
  312. package/templates/imessage/functions/settleOutbound.ts +35 -0
  313. package/templates/imessage/functions/setupStatus.ts +33 -0
  314. package/templates/imessage/functions/sweep.ts +58 -0
  315. package/templates/imessage/functions/toolCancelReminder.ts +17 -0
  316. package/templates/imessage/functions/toolForgetNote.ts +15 -0
  317. package/templates/imessage/functions/toolListNotes.ts +16 -0
  318. package/templates/imessage/functions/toolListReminders.ts +17 -0
  319. package/templates/imessage/functions/toolSaveNote.ts +22 -0
  320. package/templates/imessage/functions/toolSetReminder.ts +55 -0
  321. package/templates/imessage/functions/updateSettings.ts +48 -0
  322. package/templates/imessage/gitignore +11 -0
  323. package/templates/imessage/lib/agent-call.ts +251 -0
  324. package/templates/imessage/lib/assistant.ts +74 -0
  325. package/templates/imessage/lib/chunk.ts +135 -0
  326. package/templates/imessage/lib/demo.ts +68 -0
  327. package/templates/imessage/lib/format.ts +170 -0
  328. package/templates/imessage/lib/gate.ts +104 -0
  329. package/templates/imessage/lib/handles.ts +67 -0
  330. package/templates/imessage/lib/provider.ts +38 -0
  331. package/templates/imessage/lib/relay-protocol.ts +95 -0
  332. package/templates/imessage/lib/reminders.ts +41 -0
  333. package/templates/imessage/lib/secrets.ts +59 -0
  334. package/templates/imessage/lib/sendblue.ts +173 -0
  335. package/templates/imessage/lib/store.ts +407 -0
  336. package/templates/imessage/lib/transport.ts +135 -0
  337. package/templates/imessage/lib/utils.ts +10 -0
  338. package/templates/imessage/package.json +40 -0
  339. package/templates/imessage/relay/.env.example +14 -0
  340. package/templates/imessage/relay/applescript.ts +70 -0
  341. package/templates/imessage/relay/chatdb.ts +165 -0
  342. package/templates/imessage/relay/client.ts +41 -0
  343. package/templates/imessage/relay/main.ts +137 -0
  344. package/templates/imessage/relay/state.ts +35 -0
  345. package/templates/imessage/tests/agent-call.test.ts +112 -0
  346. package/templates/imessage/tests/applescript.test.ts +88 -0
  347. package/templates/imessage/tests/chatdb.test.ts +139 -0
  348. package/templates/imessage/tests/chunk.test.ts +61 -0
  349. package/templates/imessage/tests/fake-ctx.ts +88 -0
  350. package/templates/imessage/tests/ingest.test.ts +141 -0
  351. package/templates/imessage/tests/lib.test.ts +99 -0
  352. package/templates/imessage/tests/review-fixes.test.ts +138 -0
  353. package/templates/imessage/tests/secrets.test.ts +36 -0
  354. package/templates/imessage/tests/sendblue.test.ts +143 -0
  355. package/templates/imessage/tests/setup.ts +5 -0
  356. package/templates/imessage/tsconfig.json +18 -0
  357. package/templates/inventory/AGENTS.md +1 -1
  358. package/templates/inventory/README.md +21 -0
  359. package/templates/inventory/app/apple-icon.png +0 -0
  360. package/templates/inventory/app/globals.css +29 -14
  361. package/templates/inventory/app/icon.svg +1 -0
  362. package/templates/inventory/app/layout.tsx +7 -5
  363. package/templates/inventory/app/movements/movements-view.tsx +32 -10
  364. package/templates/inventory/app/movements/page.tsx +2 -1
  365. package/templates/inventory/app/page.tsx +4 -6
  366. package/templates/inventory/app/products/[id]/page.tsx +2 -1
  367. package/templates/inventory/app/products/[id]/product-view.tsx +55 -25
  368. package/templates/inventory/app/products-view.tsx +74 -33
  369. package/templates/inventory/app/workspace.tsx +34 -16
  370. package/templates/inventory/app.ts +10 -2
  371. package/templates/inventory/components/app-shell.tsx +94 -0
  372. package/templates/inventory/components/auth-form.tsx +35 -5
  373. package/templates/inventory/components/avatar.tsx +5 -1
  374. package/templates/inventory/components/brand-mark.tsx +25 -0
  375. package/templates/inventory/components/category-tile.tsx +49 -0
  376. package/templates/inventory/components/command-palette.tsx +17 -17
  377. package/templates/inventory/components/data-table.tsx +97 -36
  378. package/templates/inventory/components/filter-tabs.tsx +101 -0
  379. package/templates/inventory/components/nav.tsx +14 -0
  380. package/templates/inventory/components/page-header.tsx +43 -7
  381. package/templates/inventory/components/require-auth.tsx +24 -26
  382. package/templates/inventory/components/sidebar.tsx +54 -48
  383. package/templates/inventory/components/stock-level.tsx +30 -14
  384. package/templates/inventory/components/summary-bar.tsx +8 -15
  385. package/templates/inventory/components/ui/button.tsx +5 -4
  386. package/templates/inventory/components/ui/dialog.tsx +1 -1
  387. package/templates/inventory/components/ui/input.tsx +1 -1
  388. package/templates/inventory/components/ui/select.tsx +1 -1
  389. package/templates/inventory/components/ui/textarea.tsx +1 -1
  390. package/templates/inventory/functions/seedWorkspace.ts +18 -8
  391. package/templates/inventory/lib/brand.ts +9 -0
  392. package/templates/inventory/lib/demo-team.ts +51 -0
  393. package/templates/inventory/lib/search.ts +4 -3
  394. package/templates/inventory/lib/seed.ts +29 -2
  395. package/templates/inventory/lib/stock.ts +32 -0
  396. package/templates/inventory/public/favicon.ico +0 -0
  397. package/templates/inventory/tests/demo-team.test.ts +18 -0
  398. package/templates/inventory/tests/stock.test.ts +29 -0
  399. package/templates/invoices/AGENTS.md +1 -1
  400. package/templates/invoices/README.md +16 -0
  401. package/templates/invoices/app/apple-icon.png +0 -0
  402. package/templates/invoices/app/clients/clients-view.tsx +18 -6
  403. package/templates/invoices/app/clients/page.tsx +2 -1
  404. package/templates/invoices/app/globals.css +29 -14
  405. package/templates/invoices/app/icon.svg +1 -0
  406. package/templates/invoices/app/invoices/[id]/invoice-view.tsx +72 -48
  407. package/templates/invoices/app/invoices/[id]/page.tsx +2 -1
  408. package/templates/invoices/app/invoices-view.tsx +81 -32
  409. package/templates/invoices/app/layout.tsx +7 -5
  410. package/templates/invoices/app/page.tsx +4 -6
  411. package/templates/invoices/app/workspace.tsx +26 -16
  412. package/templates/invoices/app.ts +2 -2
  413. package/templates/invoices/components/app-shell.tsx +94 -0
  414. package/templates/invoices/components/auth-form.tsx +35 -5
  415. package/templates/invoices/components/avatar.tsx +5 -1
  416. package/templates/invoices/components/brand-mark.tsx +26 -0
  417. package/templates/invoices/components/command-palette.tsx +17 -17
  418. package/templates/invoices/components/data-table.tsx +97 -36
  419. package/templates/invoices/components/filter-tabs.tsx +101 -0
  420. package/templates/invoices/components/line-items.tsx +19 -14
  421. package/templates/invoices/components/nav.tsx +14 -0
  422. package/templates/invoices/components/page-header.tsx +43 -7
  423. package/templates/invoices/components/require-auth.tsx +24 -26
  424. package/templates/invoices/components/sidebar.tsx +54 -48
  425. package/templates/invoices/components/status-badge.tsx +7 -7
  426. package/templates/invoices/components/summary-bar.tsx +5 -5
  427. package/templates/invoices/components/ui/button.tsx +5 -4
  428. package/templates/invoices/components/ui/dialog.tsx +1 -1
  429. package/templates/invoices/components/ui/input.tsx +1 -1
  430. package/templates/invoices/components/ui/select.tsx +1 -1
  431. package/templates/invoices/components/ui/textarea.tsx +1 -1
  432. package/templates/invoices/functions/recordPayment.ts +3 -0
  433. package/templates/invoices/lib/billing.ts +40 -0
  434. package/templates/invoices/lib/brand.ts +9 -0
  435. package/templates/invoices/lib/search.ts +4 -3
  436. package/templates/invoices/public/favicon.ico +0 -0
  437. package/templates/invoices/tests/billing.test.ts +27 -0
  438. package/templates/ios/consumer/apps/ios/Package.swift +4 -3
  439. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ActivityView.swift +107 -0
  440. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AppModel.swift +171 -0
  441. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AuthView.swift +182 -0
  442. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/CommentsView.swift +136 -0
  443. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ComposeView.swift +152 -0
  444. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ExploreView.swift +117 -0
  445. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/FeedView.swift +175 -159
  446. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/MainTabView.swift +54 -0
  447. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Media.swift +271 -0
  448. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +101 -4
  449. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/PostCard.swift +245 -0
  450. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileEditorView.swift +208 -0
  451. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileView.swift +183 -0
  452. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/RootView.swift +25 -31
  453. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/SocialStore.swift +285 -0
  454. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/StoryViewer.swift +155 -0
  455. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Theme.swift +82 -0
  456. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +4 -47
  457. package/templates/ios/consumer/apps/ios/project.yml +13 -9
  458. package/templates/local-service/.env.example +7 -0
  459. package/templates/local-service/AGENTS.md +1 -1
  460. package/templates/local-service/README.md +26 -2
  461. package/templates/local-service/app/(marketing)/booking-widget.tsx +48 -9
  462. package/templates/local-service/app/(marketing)/page.tsx +66 -42
  463. package/templates/local-service/app/auth-form.tsx +8 -8
  464. package/templates/local-service/app/dashboard/dashboard-client.tsx +119 -92
  465. package/templates/local-service/app/dashboard/page.tsx +14 -14
  466. package/templates/local-service/app/icon.svg +2 -0
  467. package/templates/local-service/app/login/page.tsx +15 -15
  468. package/templates/local-service/functions/seedDemo.ts +49 -0
  469. package/templates/local-service/lib/demo.ts +135 -0
  470. package/templates/local-service/lib/hours.ts +44 -0
  471. package/templates/local-service/lib/site.config.ts +8 -3
  472. package/templates/local-service/lib/slots.ts +3 -0
  473. package/templates/local-service/public/favicon.ico +0 -0
  474. package/templates/local-service/tests/booking.test.ts +104 -0
  475. package/templates/marketplace/AGENTS.md +1 -1
  476. package/templates/marketplace/README.md +23 -15
  477. package/templates/marketplace/app/globals.css +172 -114
  478. package/templates/marketplace/app/layout.tsx +75 -30
  479. package/templates/marketplace/app/listing/[id]/page.tsx +210 -123
  480. package/templates/marketplace/app/me/page.tsx +4 -4
  481. package/templates/marketplace/app/page.tsx +221 -298
  482. package/templates/marketplace/app/sell/page.tsx +8 -11
  483. package/templates/marketplace/app.ts +32 -7
  484. package/templates/marketplace/client/ActivityFeed.tsx +311 -0
  485. package/templates/marketplace/client/AuthNav.tsx +24 -16
  486. package/templates/marketplace/client/ListingCard.tsx +118 -0
  487. package/templates/marketplace/client/LiveGrid.tsx +92 -0
  488. package/templates/marketplace/client/LiveListingStatus.tsx +32 -24
  489. package/templates/marketplace/client/LoginCard.tsx +114 -136
  490. package/templates/marketplace/client/MarketProvider.tsx +3 -5
  491. package/templates/marketplace/client/MyMarket.tsx +371 -185
  492. package/templates/marketplace/client/OfferPanel.tsx +421 -287
  493. package/templates/marketplace/client/SellForm.tsx +271 -192
  494. package/templates/marketplace/client/ThemeToggle.tsx +1 -0
  495. package/templates/marketplace/client/WatchButton.tsx +16 -16
  496. package/templates/marketplace/client/market.ts +61 -14
  497. package/templates/marketplace/functions/seedMarket.ts +143 -36
  498. package/templates/marketplace/functions/seedOffers.ts +101 -0
  499. package/templates/marketplace/lib/catalog.ts +72 -1
  500. package/templates/marketplace/public/images/listings/ceramic-lamp-640.webp +0 -0
  501. package/templates/marketplace/public/images/listings/ceramic-lamp.webp +0 -0
  502. package/templates/marketplace/public/images/listings/duffel-640.webp +0 -0
  503. package/templates/marketplace/public/images/listings/duffel.webp +0 -0
  504. package/templates/marketplace/public/images/listings/film-camera-640.webp +0 -0
  505. package/templates/marketplace/public/images/listings/film-camera.webp +0 -0
  506. package/templates/marketplace/public/images/listings/lounge-chair-640.webp +0 -0
  507. package/templates/marketplace/public/images/listings/lounge-chair.webp +0 -0
  508. package/templates/marketplace/public/images/listings/road-bike-640.webp +0 -0
  509. package/templates/marketplace/public/images/listings/road-bike.webp +0 -0
  510. package/templates/marketplace/public/images/listings/slipper-chair-640.webp +0 -0
  511. package/templates/marketplace/public/images/listings/slipper-chair.webp +0 -0
  512. package/templates/marketplace/public/images/listings/turntable-640.webp +0 -0
  513. package/templates/marketplace/public/images/listings/turntable.webp +0 -0
  514. package/templates/marketplace/public/images/listings/wardrobe-640.webp +0 -0
  515. package/templates/marketplace/public/images/listings/wardrobe.webp +0 -0
  516. package/templates/marketplace/public/images/listings/watch-640.webp +0 -0
  517. package/templates/marketplace/public/images/listings/watch.webp +0 -0
  518. package/templates/marketplace/public/images/listings/weekender-bag-640.webp +0 -0
  519. package/templates/marketplace/public/images/listings/weekender-bag.webp +0 -0
  520. package/templates/marketplace/tests/example.test.ts +64 -2
  521. package/templates/marketplace/tests/listing-card.test.tsx +61 -0
  522. package/templates/projects/AGENTS.md +1 -1
  523. package/templates/projects/README.md +24 -3
  524. package/templates/projects/app/apple-icon.png +0 -0
  525. package/templates/projects/app/clients/clients-view.tsx +20 -6
  526. package/templates/projects/app/clients/page.tsx +2 -1
  527. package/templates/projects/app/globals.css +29 -14
  528. package/templates/projects/app/icon.svg +1 -0
  529. package/templates/projects/app/layout.tsx +7 -5
  530. package/templates/projects/app/page.tsx +3 -5
  531. package/templates/projects/app/projects/[id]/page.tsx +2 -1
  532. package/templates/projects/app/projects/[id]/project-view.tsx +45 -30
  533. package/templates/projects/app/projects-view.tsx +105 -52
  534. package/templates/projects/app/workspace.tsx +41 -20
  535. package/templates/projects/app.ts +2 -2
  536. package/templates/projects/components/app-shell.tsx +94 -0
  537. package/templates/projects/components/auth-form.tsx +35 -5
  538. package/templates/projects/components/avatar.tsx +5 -1
  539. package/templates/projects/components/board-skeleton.tsx +3 -3
  540. package/templates/projects/components/brand-mark.tsx +25 -0
  541. package/templates/projects/components/command-palette.tsx +17 -17
  542. package/templates/projects/components/data-table.tsx +97 -36
  543. package/templates/projects/components/filter-tabs.tsx +101 -0
  544. package/templates/projects/components/meter.tsx +48 -0
  545. package/templates/projects/components/nav.tsx +14 -0
  546. package/templates/projects/components/page-header.tsx +43 -7
  547. package/templates/projects/components/require-auth.tsx +24 -26
  548. package/templates/projects/components/sidebar.tsx +54 -48
  549. package/templates/projects/components/summary-bar.tsx +49 -0
  550. package/templates/projects/components/task-board.tsx +10 -9
  551. package/templates/projects/components/time-dialog.tsx +29 -5
  552. package/templates/projects/components/ui/button.tsx +5 -4
  553. package/templates/projects/components/ui/dialog.tsx +1 -1
  554. package/templates/projects/components/ui/input.tsx +1 -1
  555. package/templates/projects/components/ui/select.tsx +1 -1
  556. package/templates/projects/components/ui/textarea.tsx +1 -1
  557. package/templates/projects/functions/seedWorkspace.ts +26 -12
  558. package/templates/projects/lib/brand.ts +9 -0
  559. package/templates/projects/lib/demo-team.ts +51 -0
  560. package/templates/projects/lib/search.ts +4 -3
  561. package/templates/projects/lib/seed.ts +35 -1
  562. package/templates/projects/lib/work.ts +63 -0
  563. package/templates/projects/public/favicon.ico +0 -0
  564. package/templates/projects/tests/demo-team.test.ts +18 -0
  565. package/templates/projects/tests/work.test.ts +65 -0
  566. package/templates/restaurant/.env.example +7 -0
  567. package/templates/restaurant/AGENTS.md +1 -1
  568. package/templates/restaurant/README.md +29 -3
  569. package/templates/restaurant/app/(marketing)/layout.tsx +4 -2
  570. package/templates/restaurant/app/(marketing)/page.tsx +47 -23
  571. package/templates/restaurant/app/(marketing)/reservation-widget.tsx +37 -7
  572. package/templates/restaurant/app/auth-form.tsx +8 -8
  573. package/templates/restaurant/app/dashboard/dashboard-client.tsx +241 -109
  574. package/templates/restaurant/app/dashboard/page.tsx +14 -14
  575. package/templates/restaurant/app/icon.svg +4 -0
  576. package/templates/restaurant/app/login/page.tsx +15 -15
  577. package/templates/restaurant/functions/seedDemo.ts +44 -0
  578. package/templates/restaurant/lib/demo.ts +144 -0
  579. package/templates/restaurant/lib/hours.ts +45 -0
  580. package/templates/restaurant/lib/site.config.ts +3 -3
  581. package/templates/restaurant/public/favicon.ico +0 -0
  582. package/templates/restaurant/tests/reservations.test.ts +67 -0
  583. package/templates/saas/AGENTS.md +1 -1
  584. package/templates/saas/README.md +54 -35
  585. package/templates/saas/app/(auth)/auth-form.tsx +25 -23
  586. package/templates/saas/app/(auth)/forgot-password/page.tsx +1 -1
  587. package/templates/saas/app/(auth)/layout.tsx +27 -33
  588. package/templates/saas/app/(auth)/login/page.tsx +1 -1
  589. package/templates/saas/app/(auth)/reset-password/page.tsx +1 -1
  590. package/templates/saas/app/(auth)/signup/page.tsx +1 -1
  591. package/templates/saas/app/(marketing)/layout.tsx +127 -253
  592. package/templates/saas/app/(marketing)/page.tsx +133 -372
  593. package/templates/saas/app/(marketing)/pricing/page.tsx +21 -19
  594. package/templates/saas/app/(marketing)/products/[slug]/page.tsx +40 -95
  595. package/templates/saas/app/dashboard/board-client.tsx +416 -0
  596. package/templates/saas/app/dashboard/dashboard-client.tsx +30 -615
  597. package/templates/saas/app/dashboard/layout.tsx +27 -13
  598. package/templates/saas/app/dashboard/overview-client.tsx +332 -0
  599. package/templates/saas/app/dashboard/page.tsx +29 -35
  600. package/templates/saas/app/dashboard/projects/[id]/page.tsx +29 -0
  601. package/templates/saas/app/dashboard/projects/page.tsx +11 -7
  602. package/templates/saas/app/dashboard/projects-client.tsx +398 -0
  603. package/templates/saas/app/globals.css +5 -4
  604. package/templates/saas/app/icon.svg +1 -0
  605. package/templates/saas/app/onboarding/onboarding-client.tsx +93 -26
  606. package/templates/saas/app/onboarding/page.tsx +6 -2
  607. package/templates/saas/app.ts +68 -13
  608. package/templates/saas/components/dashboard-shell.tsx +212 -132
  609. package/templates/saas/components/marketing.tsx +137 -274
  610. package/templates/saas/components/pricing-table.tsx +12 -11
  611. package/templates/saas/components/setup-checklist.tsx +31 -29
  612. package/templates/saas/components/task-ui.tsx +199 -0
  613. package/templates/saas/functions/createProject.ts +7 -13
  614. package/templates/saas/functions/createTask.ts +67 -0
  615. package/templates/saas/functions/deleteProject.ts +17 -0
  616. package/templates/saas/functions/deleteTask.ts +14 -0
  617. package/templates/saas/functions/seedWorkspace.ts +46 -0
  618. package/templates/saas/functions/setProjectStatus.ts +41 -0
  619. package/templates/saas/functions/updateProfile.ts +13 -0
  620. package/templates/saas/functions/updateProject.ts +4 -5
  621. package/templates/saas/functions/updateTask.ts +88 -0
  622. package/templates/saas/lib/members.ts +53 -0
  623. package/templates/saas/lib/names.ts +53 -0
  624. package/templates/saas/lib/plans.ts +11 -0
  625. package/templates/saas/lib/sample-workspace.ts +117 -0
  626. package/templates/saas/lib/site.config.ts +200 -463
  627. package/templates/saas/lib/tasks.ts +101 -0
  628. package/templates/saas/lib/use-live.ts +25 -0
  629. package/templates/saas/public/favicon.ico +0 -0
  630. package/templates/saas/public/screenshots/board.png +0 -0
  631. package/templates/saas/public/screenshots/members.png +0 -0
  632. package/templates/saas/public/screenshots/overview.png +0 -0
  633. package/templates/saas/public/screenshots/projects.png +0 -0
  634. package/templates/saas/scripts/capture-screenshots.mjs +156 -0
  635. package/templates/saas/tests/names.test.ts +35 -0
  636. package/templates/saas/tests/plans.test.ts +12 -0
  637. package/templates/saas/tests/sample-workspace.test.ts +46 -0
  638. package/templates/saas/tests/tasks.test.ts +81 -0
  639. package/templates/shop/AGENTS.md +1 -1
  640. package/templates/shop/README.md +25 -13
  641. package/templates/shop/app/(marketing)/layout.tsx +94 -67
  642. package/templates/shop/app/(marketing)/not-found.tsx +13 -12
  643. package/templates/shop/app/(marketing)/page.tsx +120 -59
  644. package/templates/shop/app/(marketing)/shop-client.tsx +364 -237
  645. package/templates/shop/app/(marketing)/success/page.tsx +75 -22
  646. package/templates/shop/app/globals.css +94 -1
  647. package/templates/shop/app/layout.tsx +4 -4
  648. package/templates/shop/app/login/page.tsx +4 -4
  649. package/templates/shop/app.ts +18 -6
  650. package/templates/shop/components/cart-button.tsx +33 -0
  651. package/templates/shop/components/marketing.tsx +17 -87
  652. package/templates/shop/components/shelf-count.tsx +36 -0
  653. package/templates/shop/functions/checkout.ts +17 -1
  654. package/templates/shop/lib/cart-store.ts +57 -0
  655. package/templates/shop/lib/shelf-store.ts +35 -0
  656. package/templates/shop/lib/shop.ts +27 -0
  657. package/templates/shop/lib/site.config.ts +108 -98
  658. package/templates/shop/public/images/hero.jpg +0 -0
  659. package/templates/shop/public/images/products/cedar-smoke.jpg +0 -0
  660. package/templates/shop/public/images/products/fig-leaf.jpg +0 -0
  661. package/templates/shop/public/images/products/linen.jpg +0 -0
  662. package/templates/shop/public/images/products/sea-salt.jpg +0 -0
  663. package/templates/shop/public/images/products/tobacco-oak.jpg +0 -0
  664. package/templates/shop/tests/shop.test.ts +64 -0
  665. package/templates/todo/AGENTS.md +1 -1
  666. package/templates/waitlist/.env.example +7 -0
  667. package/templates/waitlist/AGENTS.md +1 -1
  668. package/templates/waitlist/README.md +29 -13
  669. package/templates/waitlist/app/(marketing)/page.tsx +9 -7
  670. package/templates/waitlist/app/(marketing)/waitlist-hero.tsx +64 -31
  671. package/templates/waitlist/app/auth-form.tsx +8 -8
  672. package/templates/waitlist/app/dashboard/dashboard-client.tsx +132 -95
  673. package/templates/waitlist/app/dashboard/page.tsx +17 -16
  674. package/templates/waitlist/app/icon.svg +2 -0
  675. package/templates/waitlist/app/login/page.tsx +14 -14
  676. package/templates/waitlist/app.ts +6 -6
  677. package/templates/waitlist/functions/joinWaitlist.ts +6 -18
  678. package/templates/waitlist/functions/seedDemo.ts +32 -0
  679. package/templates/waitlist/lib/demo.ts +94 -0
  680. package/templates/waitlist/lib/launch.ts +19 -0
  681. package/templates/waitlist/lib/site.config.ts +14 -9
  682. package/templates/waitlist/lib/stats.ts +35 -0
  683. package/templates/waitlist/public/favicon.ico +0 -0
  684. package/templates/waitlist/tests/counter.test.ts +107 -0
  685. package/templates/waitlist/tests/live-count.test.tsx +48 -0
  686. package/templates/ai-chat/tests/example.test.ts +0 -12
  687. package/templates/backend/consumer/apps/api/functions/feed.ts +0 -57
  688. package/templates/backend/consumer/apps/api/functions/profilePosts.ts +0 -17
  689. package/templates/backend/consumer/apps/api/functions/toggleLike.ts +0 -48
  690. package/templates/backend/consumer/apps/api/schema.ts +0 -143
  691. package/templates/chat/functions/_keep.ts +0 -13
  692. package/templates/chat/tests/example.test.ts +0 -12
  693. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +0 -68
  694. package/templates/marketplace/client/LiveTicker.tsx +0 -116
  695. package/templates/marketplace/client/ScrollToListingsLink.tsx +0 -29
  696. package/templates/marketplace/public/images/marketplace-hero.webp +0 -0
@@ -1,21 +1,26 @@
1
- // Business-specific copy and settings live here. The landing page and layout
2
- // read this file. The product list, including starting stock, seeds the Product table on
3
- // first visit; after that, stock lives in the database and updates live.
1
+ // Business-specific copy and settings live here. The storefront pages and
2
+ // layout read this file. The product list, including starting stock, seeds the
3
+ // Product table on first visit; after that, stock lives in the database and
4
+ // updates live.
4
5
  //
5
6
  // Colors live here (applied as CSS variables on <html> in app/layout.tsx).
6
7
  // Fictional demo copy. Replace the values and keep the shape.
7
8
 
8
9
  /* ----------------------------- types ----------------------------- */
9
10
 
11
+ import type { ShippingTerms } from "./shop";
12
+
10
13
  export type Social = { label: string; href: string; path: string };
11
14
 
12
15
  export type BaseConfig = {
13
16
  brand: {
14
17
  name: string;
15
- letter: string;
18
+ letter: string; // the monogram on the sign-in and dashboard screens
16
19
  domain: string;
17
20
  email: string;
21
+ announcement: string;
18
22
  footerBlurb: string;
23
+ address: string[];
19
24
  copyrightName: string;
20
25
  socials: Social[];
21
26
  };
@@ -28,21 +33,29 @@ export type ProductItem = {
28
33
  name: string;
29
34
  priceCents: number;
30
35
  description?: string;
31
- image: string; // a product photo URL/path → shown as the photo; or an emoji stand-in (tagged "sample image")
32
- stock: number; // STARTING stock — seeded once, then live in the DB
36
+ size?: string; // one short line under the name, e.g. "8 oz, 60-hour burn"
37
+ image: string; // a product photo URL or /public path (4:5 portrait works best)
38
+ stock: number; // STARTING stock, seeded once, then live in the DB
33
39
  };
34
40
 
35
- export type ValueProp = { title: string; body: string; icon?: string };
36
- export type Review = { quote: string; name: string; rating?: number };
41
+ export type Fact = { value: string; label: string };
42
+ export type Review = { quote: string; name: string; product?: string };
37
43
  export type Policy = { title: string; body: string };
38
44
 
39
45
  export type ShopConfig = BaseConfig & {
40
- hero: { tagline: string; headline: string; subcopy: string; ctaLabel: string };
41
- products: { eyebrow: string; headline: string; items: ProductItem[] };
42
- checkout: { confirmationMessage: string };
43
- valueProps: { eyebrow: string; headline: string; items: ValueProp[] };
44
- reviews?: { eyebrow: string; headline: string; items: Review[] };
45
- policies: { eyebrow: string; headline: string; items: Policy[] };
46
+ hero: {
47
+ headline: string;
48
+ subcopy: string;
49
+ ctaLabel: string;
50
+ secondaryLabel: string;
51
+ image: string;
52
+ imageAlt: string;
53
+ };
54
+ products: { headline: string; subcopy: string; items: ProductItem[] };
55
+ studio: { headline: string; body: string[]; facts: Fact[] };
56
+ checkout: { shipping: ShippingTerms; reservedMessage: string; paidMessage: string; image: string };
57
+ reviews?: { headline: string; items: Review[] };
58
+ policies: { headline: string; items: Policy[] };
46
59
  };
47
60
 
48
61
  /* ----------------------------- config ---------------------------- */
@@ -53,145 +66,142 @@ export const siteConfig: ShopConfig = {
53
66
  letter: "E",
54
67
  domain: "embergoods.com",
55
68
  email: "hello@embergoods.example",
56
- footerBlurb:
57
- "Hand-poured candles and small-batch home goods, made in Dallas. Slow, simple, and built to last. Free shipping over $50.",
69
+ announcement: "Free shipping on orders over $50. Ships from Dallas in two business days.",
70
+ footerBlurb: "Candles poured by hand in small batches in Dallas, Texas.",
71
+ address: ["2809 Elm Street", "Dallas, TX 75226", "Studio open Saturdays, 10 to 4"],
58
72
  copyrightName: "Ember Goods",
59
73
  socials: [
60
74
  {
61
75
  label: "Instagram",
62
76
  href: "https://instagram.com",
63
- path: "M12 2.2c3.2 0 3.6 0 4.85.07 1.17.05 1.8.25 2.23.41.56.22.96.48 1.38.9.42.42.68.82.9 1.38.16.42.36 1.06.41 2.23.06 1.27.07 1.65.07 4.85s0 3.58-.07 4.85c-.05 1.17-.25 1.8-.41 2.23-.22.56-.48.96-.9 1.38-.42.42-.82.68-1.38.9-.42.16-1.06.36-2.23.41-1.27.06-1.65.07-4.85.07s-3.58 0-4.85-.07c-1.17-.05-1.8-.25-2.23-.41a3.7 3.7 0 0 1-1.38-.9 3.7 3.7 0 0 1-.9-1.38c-.16-.42-.36-1.06-.41-2.23C2.2 15.58 2.2 15.2 2.2 12s0-3.58.07-4.85c.05-1.17.25-1.8.41-2.23.22-.56.48-.96.9-1.38.42-.42.82-.68 1.38-.9.42-.16 1.06-.36 2.23-.41C8.42 2.2 8.8 2.2 12 2.2zm0 1.8c-3.15 0-3.5 0-4.74.07-.9.04-1.38.19-1.7.32-.43.16-.74.36-1.06.68-.32.32-.52.63-.68 1.06-.13.32-.28.8-.32 1.7C3.8 8.5 3.8 8.85 3.8 12s0 3.5.07 4.74c.04.9.19 1.38.32 1.7.16.43.36.74.68 1.06.32.32.63.52 1.06.68.32.13.8.28 1.7.32 1.24.07 1.59.07 4.74.07s3.5 0 4.74-.07c.9-.04 1.38-.19 1.7-.32.43-.16.74-.36 1.06-.68.32-.32.52-.63.68-1.06.13-.32.28-.8.32-1.7.07-1.24.07-1.59.07-4.74s0-3.5-.07-4.74c-.04-.9-.19-1.38-.32-1.7a2.85 2.85 0 0 0-.68-1.06 2.85 2.85 0 0 0-1.06-.68c-.32-.13-.8-.28-1.7-.32C15.5 4 15.15 4 12 4zm0 3.06A4.94 4.94 0 1 0 12 16.94 4.94 4.94 0 0 0 12 7.06zm0 8.15A3.21 3.21 0 1 1 12 8.8a3.21 3.21 0 0 1 0 6.4zm6.3-8.35a1.15 1.15 0 1 1-2.3 0 1.15 1.15 0 0 1 2.3 0z",
64
- },
77
+ path: "M12 2.2c3.2 0 3.6 0 4.85.07 1.17.05 1.8.25 2.23.41.56.22.96.48 1.38.9.42.42.68.82.9 1.38.16.42.36 1.06.41 2.23.06 1.27.07 1.65.07 4.85s0 3.58-.07 4.85c-.05 1.17-.25 1.8-.41 2.23-.22.56-.48.96-.9 1.38-.42.42-.82.68-1.38.9-.42.16-1.06.36-2.23.41-1.27.06-1.65.07-4.85.07s-3.58 0-4.85-.07c-1.17-.05-1.8-.25-2.23-.41a3.7 3.7 0 0 1-1.38-.9 3.7 3.7 0 0 1-.9-1.38c-.16-.42-.36-1.06-.41-2.23C2.2 15.58 2.2 15.2 2.2 12s0-3.58.07-4.85c.05-1.17.25-1.8.41-2.23.22-.56.48-.96.9-1.38.42-.42.82-.68 1.38-.9.42-.16 1.06-.36 2.23-.41C8.42 2.2 8.8 2.2 12 2.2zm0 1.8c-3.15 0-3.5 0-4.74.07-.9.04-1.38.19-1.7.32-.43.16-.74.36-1.06.68-.32.32-.52.63-.68 1.06-.13.32-.28.8-.32 1.7C3.8 8.5 3.8 8.85 3.8 12s0 3.5.07 4.74c.04.9.19 1.38.32 1.7.16.43.36.74.68 1.06.32.32.63.52 1.06.68.32.13.8.28 1.7.32 1.24.07 1.59.07 4.74.07s3.5 0 4.74-.07c.9-.04 1.38-.19 1.7-.32.43-.16.74-.36 1.06-.68.32-.32.52-.63.68-1.06.13-.32.28-.8.32-1.7.07-1.24.07-1.59.07-4.74s0-3.5-.07-4.74c-.04-.9-.19-1.38-.32-1.7a2.85 2.85 0 0 0-.68-1.06 2.85 2.85 0 0 0-1.06-.68c-.32-.13-.8-.28-1.7-.32C15.5 4 15.15 4 12 4zm0 3.06A4.94 4.94 0 1 0 12 16.94 4.94 4.94 0 0 0 12 7.06zm0 8.15A3.21 3.21 0 1 1 12 8.8a3.21 3.21 0 0 1 0 6.4zm6.3-8.35a1.15 1.15 0 1 1-2.3 0 1.15 1.15 0 0 1 2.3 0z", },
65
78
  ],
66
79
  },
67
80
 
68
- colors: { brand: "#9a3412", brandSoft: "#ffedd5", paper: "#faf8f5" },
81
+ colors: { brand: "#8b4a2b", brandSoft: "#efe3d6", paper: "#e9e1d5" },
69
82
 
70
83
  seo: {
71
- title: "Ember Goods — hand-poured candles, made in Dallas.",
84
+ title: "Ember Goods: candles poured by hand in Dallas",
72
85
  description:
73
- "Small-batch candles and home goods, hand-poured in Dallas. Stock counts match the studio shelf. Free shipping over $50.",
86
+ "Small-batch candles poured in Dallas. Soy and coconut wax, cotton wicks, 60-hour burn. Stock counts match the studio shelf. Free shipping over $50.",
74
87
  },
75
88
 
76
89
  hero: {
77
- tagline: "Small batch · Made in Dallas",
78
- headline: "Hand-poured candles, made to last.",
90
+ headline: "Candles poured by hand in Dallas.",
79
91
  subcopy:
80
- "We pour everything by hand in small batches, so quantities are real and limited. The stock you see below is exactly what's on the studio shelf right now.",
81
- ctaLabel: "Shop the shelf",
92
+ "Soy and coconut wax, cotton wicks, and scents we blend ourselves. We pour forty at a time, so the stock counts are the real shelf.",
93
+ ctaLabel: "Shop candles",
94
+ secondaryLabel: "See the studio",
95
+ image: "/images/hero.jpg",
96
+ imageAlt: "Three amber glass candles on a travertine shelf, the middle one lit, in late afternoon light.",
82
97
  },
83
98
 
84
99
  products: {
85
- eyebrow: "The shelf",
86
- headline: "What's in the studio right now.",
100
+ headline: "The shelf",
101
+ subcopy: "Stock updates live. When a batch sells out, it is gone until the next pour.",
87
102
  items: [
103
+ {
104
+ slug: "tobacco-oak",
105
+ name: "Tobacco & Oak",
106
+ priceCents: 4200,
107
+ description: "Cured tobacco leaf, oak barrel, and amber. Our darkest scent, poured once a season.",
108
+ size: "8 oz, 60-hour burn",
109
+ image: "/images/products/tobacco-oak.jpg",
110
+ stock: 2,
111
+ },
88
112
  {
89
113
  slug: "cedar-smoke",
90
114
  name: "Cedar & Smoke",
91
- priceCents: 3200,
92
- description: "Cedarwood, smoked vanilla, a little leather. 60-hour burn.",
93
- image: "🕯️",
115
+ priceCents: 3800,
116
+ description: "Cedarwood, smoked vanilla, and a little leather.",
117
+ size: "8 oz, 60-hour burn",
118
+ image: "/images/products/cedar-smoke.jpg",
94
119
  stock: 8,
95
120
  },
96
121
  {
97
122
  slug: "linen",
98
- name: "Fresh Linen",
99
- priceCents: 3200,
100
- description: "Clean cotton and white musk. The everyday one.",
101
- image: "🤍",
123
+ name: "Linen",
124
+ priceCents: 3800,
125
+ description: "Clean cotton, white musk, and iris.",
126
+ size: "8 oz, 60-hour burn",
127
+ image: "/images/products/linen.jpg",
102
128
  stock: 3,
103
129
  },
104
130
  {
105
- slug: "brass-holder",
106
- name: "Brass matchstick holder",
107
- priceCents: 2400,
108
- description: "Solid brass, striker on the base. Ages beautifully.",
109
- image: "🟫",
110
- stock: 12,
131
+ slug: "fig-leaf",
132
+ name: "Fig Leaf",
133
+ priceCents: 3800,
134
+ description: "Green fig, cedar, and coconut.",
135
+ size: "8 oz, 60-hour burn",
136
+ image: "/images/products/fig-leaf.jpg",
137
+ stock: 11,
111
138
  },
112
139
  {
113
- slug: "ceramic-vessel",
114
- name: "Ceramic vessel candle",
115
- priceCents: 4800,
116
- description: "Hand-thrown stoneware you'll keep long after the wax.",
117
- image: "🏺",
118
- stock: 5,
119
- },
120
- {
121
- slug: "wick-trimmer",
122
- name: "Wick trimmer",
123
- priceCents: 1800,
124
- description: "The one tool that makes a candle last. Matte black.",
125
- image: "✂️",
126
- stock: 0,
127
- },
128
- {
129
- slug: "gift-set",
130
- name: "The trio gift set",
131
- priceCents: 8400,
132
- description: "Three minis, boxed and ready to give.",
133
- image: "🎁",
134
- stock: 6,
140
+ slug: "sea-salt",
141
+ name: "Sea Salt & Sage",
142
+ priceCents: 3800,
143
+ description: "Salt air, sage, and driftwood.",
144
+ size: "8 oz, 60-hour burn",
145
+ image: "/images/products/sea-salt.jpg",
146
+ stock: 9,
135
147
  },
136
148
  ],
137
149
  },
138
150
 
139
- checkout: {
140
- confirmationMessage:
141
- "Order placed! We'll email you a payment link and ship within two business days.",
151
+ studio: {
152
+ headline: "Poured in small batches, cured for two weeks.",
153
+ body: [
154
+ "Every candle is poured, cured, and trimmed by hand in our studio in Deep Ellum. We use a soy and coconut wax blend, cotton wicks, and phthalate-free fragrance oils.",
155
+ "Each batch cures for fourteen days before it goes on the shelf, so the scent carries across the room from the first burn.",
156
+ ],
157
+ facts: [
158
+ { value: "40", label: "candles per batch" },
159
+ { value: "14 days", label: "cure before sale" },
160
+ { value: "60 hr", label: "burn time, 8 oz" },
161
+ ],
142
162
  },
143
163
 
144
- valueProps: {
145
- eyebrow: "Why Ember",
146
- headline: "Made slow, on purpose.",
147
- items: [
148
- {
149
- icon: "◍",
150
- title: "Hand-poured",
151
- body: "Every candle is poured, wicked, and labelled by hand in our Dallas studio.",
152
- },
153
- {
154
- icon: "◇",
155
- title: "Real small batch",
156
- body: "We make what we can do well. When the shelf says 3 left, there are 3 left.",
157
- },
158
- {
159
- icon: "✦",
160
- title: "Free shipping over $50",
161
- body: "Carbon-neutral shipping, plastic-free packaging, and easy returns.",
162
- },
163
- ],
164
+ checkout: {
165
+ shipping: { flatCents: 600, freeOverCents: 5000 },
166
+ reservedMessage:
167
+ "We are holding your items and will email a payment link within one business day. Orders ship from Dallas within two business days of payment.",
168
+ paidMessage:
169
+ "Your payment went through and the receipt is on its way to your inbox. Orders ship from Dallas within two business days.",
170
+ image: "/images/products/tobacco-oak.jpg", // shown on /success
164
171
  },
165
172
 
166
173
  reviews: {
167
- eyebrow: "Reviews",
168
- headline: "What people say.",
174
+ headline: "Reviews",
169
175
  items: [
170
176
  {
171
- quote: "Cedar & Smoke is the best candle I've ever bought, full stop. The brass holder is gorgeous too.",
177
+ quote: "Cedar & Smoke fills the whole living room without being loud about it. On my third one.",
172
178
  name: "Maya C.",
173
- rating: 5,
179
+ product: "Cedar & Smoke",
174
180
  },
175
181
  {
176
- quote: "Ordered the gift set for my mom. Beautiful packaging and it arrived in two days. Will reorder.",
182
+ quote: "Bought Linen for my mom. It arrived in two days, packed in paper, no plastic anywhere.",
177
183
  name: "Daniel R.",
178
- rating: 5,
184
+ product: "Linen",
179
185
  },
180
186
  {
181
- quote: "You can tell these are made by hand. The ceramic vessel is on my shelf forever now.",
187
+ quote: "Fig Leaf burned evenly all the way to the bottom of the jar. I kept the jar for pens.",
182
188
  name: "Hannah K.",
183
- rating: 5,
189
+ product: "Fig Leaf",
184
190
  },
185
191
  ],
186
192
  },
187
193
 
188
194
  policies: {
189
- eyebrow: "Good to know",
190
- headline: "Shipping & returns.",
195
+ headline: "Shipping and returns",
191
196
  items: [
192
- { title: "Shipping", body: "Ships in 2 business days from Dallas. Free over $50, $6 flat otherwise." },
193
- { title: "Returns", body: "Unused and unburned? Return within 30 days for a full refund." },
194
- { title: "Wholesale", body: "Run a shop? Email us for the line sheet and wholesale pricing." },
197
+ { title: "Shipping", body: "Ships from Dallas in two business days. Free over $50, $6 flat otherwise." },
198
+ { title: "Returns", body: "Return unused and unburned items within 30 days for a full refund." },
199
+ { title: "Local pickup", body: "Pick up at the studio on Saturdays. Reply to your order email to switch to pickup." },
200
+ { title: "Wholesale", body: "Email us for the line sheet and wholesale pricing." },
195
201
  ],
196
202
  },
197
203
  };
204
+
205
+ export function productBySlug(slug: string): ProductItem | undefined {
206
+ return siteConfig.products.items.find((p) => p.slug === slug);
207
+ }
@@ -0,0 +1,64 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { formatPrice, freeShippingRemaining, shippingCents, stockStatus } from "../lib/shop";
5
+ import { productBySlug, siteConfig } from "../lib/site.config";
6
+
7
+ const terms = { flatCents: 600, freeOverCents: 5000 };
8
+
9
+ describe("stockStatus", () => {
10
+ test("zero or less is sold out", () => {
11
+ expect(stockStatus(0)).toEqual({ kind: "sold_out", label: "Sold out" });
12
+ expect(stockStatus(-2).kind).toBe("sold_out");
13
+ });
14
+
15
+ test("three or fewer is low", () => {
16
+ expect(stockStatus(1)).toEqual({ kind: "low", label: "Last one" });
17
+ expect(stockStatus(3)).toEqual({ kind: "low", label: "Only 3 left" });
18
+ });
19
+
20
+ test("more than three shows the count", () => {
21
+ expect(stockStatus(4)).toEqual({ kind: "in_stock", label: "4 in stock" });
22
+ });
23
+ });
24
+
25
+ describe("shipping", () => {
26
+ test("flat rate below the threshold", () => {
27
+ expect(shippingCents(4999, terms)).toBe(600);
28
+ expect(freeShippingRemaining(3800, terms)).toBe(1200);
29
+ });
30
+
31
+ test("free at and above the threshold", () => {
32
+ expect(shippingCents(5000, terms)).toBe(0);
33
+ expect(freeShippingRemaining(8400, terms)).toBe(0);
34
+ });
35
+ });
36
+
37
+ test("formatPrice drops zero cents", () => {
38
+ expect(formatPrice(3800)).toBe("$38");
39
+ expect(formatPrice(1850)).toBe("$18.50");
40
+ });
41
+
42
+ describe("catalog config", () => {
43
+ const items = siteConfig.products.items;
44
+
45
+ test("slugs are unique", () => {
46
+ expect(new Set(items.map((p) => p.slug)).size).toBe(items.length);
47
+ });
48
+
49
+ test("productBySlug finds a product and misses an unknown slug", () => {
50
+ expect(productBySlug(items[0].slug)?.name).toBe(items[0].name);
51
+ expect(productBySlug("nope")).toBeUndefined();
52
+ });
53
+
54
+ // Every local image the storefront references must ship in public/.
55
+ test("every local image exists in public/", () => {
56
+ const paths = [
57
+ siteConfig.hero.image,
58
+ siteConfig.checkout.image,
59
+ ...items.map((p) => p.image),
60
+ ].filter((src) => src.startsWith("/"));
61
+ const missing = paths.filter((src) => !existsSync(join(import.meta.dir, "..", "public", src)));
62
+ expect(missing).toEqual([]);
63
+ });
64
+ });
@@ -62,7 +62,7 @@ this pass before the user has to ask.
62
62
  - **`serverData` (SSR) is READ-ONLY.** No write methods; the runtime rejects write frames (`SSR_WRITE_FORBIDDEN`). Mutations belong in actions/functions, never in a page render.
63
63
  - **`response.*` / `response.redirect()` / `response.notFound()` must fire in the synchronous shell render**, before any `await` / `<Suspense>`. The HTTP head commits when the shell is ready; status/headers/cookies set from a suspended subtree are lost, and `redirect`/`notFound` thrown below a Suspense boundary are swallowed.
64
64
  - **`ctx.llm`, `ctx.rooms`, and `ctx.connections` are on mutation + action only, NOT query** (reactive purity). `action` has no direct `ctx.db`; use `ctx.runQuery` / `ctx.runMutation`.
65
- - **`ctx.llm.stream(request, onEvent)` streams tokens as they generate** and still resolves with the full response, so `stop_reason === "tool_use"` drives an agent tool loop. Events are `text_delta` / `tool_use_start` / `tool_input_delta` / `done`. Same auth + model-allowlist gating as `ctx.llm.complete`. Streaming does NOT extend the call deadline (`PYLON_FN_CALL_TIMEOUT`, 30s default) — set `timeout: <secs>` on the def for a long run.
65
+ - **`ctx.llm.stream(request, onEvent)` streams tokens as they generate** and still resolves with the full response, so `stop_reason === "tool_use"` drives an agent tool loop. Events are `text_delta` / `tool_use_start` / `tool_input_delta` / `done`. Same auth + model-allowlist gating as `ctx.llm.complete`. The call timeout is an idle timeout (`PYLON_FN_CALL_TIMEOUT`, 30s default; Stack0 Cloud 300s): each stream event resets it, and the total run is capped at 10× the timeout. Set `timeout: <secs>` on the def when a step (a slow tool call) can be silent longer than that.
66
66
  - **`ctx.stream.write(text)` streams to the caller — and every fn stream is RESUMABLE**: the server buffers frames under a stream id (`streamFn`'s `onStreamId`), so a dropped connection or closed tab catches up via `resumeStream(id)`, including the final result after the handler finished. `ctx.rooms.broadcast(room, topic, data)` fans out to every CURRENTLY-CONNECTED subscriber (second device, second tab) but does not replay missed messages — use the stream id for anything that must survive a gap. Broadcast resolves `{ delivered: false }` when the room has no members — a no-op, not an error. Clients receive with `useRoomMessages(room, cb)` from `@pylonsync/react`; `useRoom` is the send side.
67
67
  - **It's `db.useQueryOne`, not `useOne`.** Validators and field types have aliases: `v.bool`/`v.boolean`, `v.float`/`v.number`.
68
68
  - **Use the supported file and scheduling APIs.** Files go through `<FileUpload>` and `/api/files/*`; an upload is readable by its uploader only, so pass `visibility: "public"` to `uploadFile` or `<FileUpload>` for files every visitor sees; there is no `ctx.files`. One-shot work uses `ctx.scheduler.runAfter`, `runAt`, or `cancel`; there is no `defineWorkflow` or `defineJob`. Recurring work uses `cron("0 * * * *", "fnName")` in `buildManifest({ crons: [...] })`, imported from `@pylonsync/sdk`. Make the target function `internal: true`. It runs with anonymous auth, but its own `ctx.db.*` calls are server-side and bypass policies. Use `ctx.auth.elevate({ admin: true, reason: "..." })`, with a mandatory reason, only when chaining another internal function through `ctx.scheduler`.
@@ -10,3 +10,10 @@ PYLON_OWNER_EMAIL=you@yourbusiness.com
10
10
  # ── Site URL (optional) ──────────────────────────────────────────────────────
11
11
  # Used by robots.txt + sitemap.xml. Point it at your real domain in production.
12
12
  # SITE_URL=https://yourbusiness.com
13
+
14
+ # ── Demo data ────────────────────────────────────────────────────────────────
15
+ # `pylon dev` fills an empty waitlist with fictional signups. Seeding runs only
16
+ # under `pylon dev` (it checks PYLON_DEV_MODE and PYLON_DEV_WATCH_DIR), so
17
+ # `pylon start`, Docker, and Pylon Cloud deploys never seed. Set 0 to start
18
+ # empty in development. Never set 1 on a deploy.
19
+ # PYLON_DEMO_DATA=0
@@ -62,7 +62,7 @@ this pass before the user has to ask.
62
62
  - **`serverData` (SSR) is READ-ONLY.** No write methods; the runtime rejects write frames (`SSR_WRITE_FORBIDDEN`). Mutations belong in actions/functions, never in a page render.
63
63
  - **`response.*` / `response.redirect()` / `response.notFound()` must fire in the synchronous shell render**, before any `await` / `<Suspense>`. The HTTP head commits when the shell is ready; status/headers/cookies set from a suspended subtree are lost, and `redirect`/`notFound` thrown below a Suspense boundary are swallowed.
64
64
  - **`ctx.llm`, `ctx.rooms`, and `ctx.connections` are on mutation + action only, NOT query** (reactive purity). `action` has no direct `ctx.db`; use `ctx.runQuery` / `ctx.runMutation`.
65
- - **`ctx.llm.stream(request, onEvent)` streams tokens as they generate** and still resolves with the full response, so `stop_reason === "tool_use"` drives an agent tool loop. Events are `text_delta` / `tool_use_start` / `tool_input_delta` / `done`. Same auth + model-allowlist gating as `ctx.llm.complete`. Streaming does NOT extend the call deadline (`PYLON_FN_CALL_TIMEOUT`, 30s default) — set `timeout: <secs>` on the def for a long run.
65
+ - **`ctx.llm.stream(request, onEvent)` streams tokens as they generate** and still resolves with the full response, so `stop_reason === "tool_use"` drives an agent tool loop. Events are `text_delta` / `tool_use_start` / `tool_input_delta` / `done`. Same auth + model-allowlist gating as `ctx.llm.complete`. The call timeout is an idle timeout (`PYLON_FN_CALL_TIMEOUT`, 30s default; Stack0 Cloud 300s): each stream event resets it, and the total run is capped at 10× the timeout. Set `timeout: <secs>` on the def when a step (a slow tool call) can be silent longer than that.
66
66
  - **`ctx.stream.write(text)` streams to the caller — and every fn stream is RESUMABLE**: the server buffers frames under a stream id (`streamFn`'s `onStreamId`), so a dropped connection or closed tab catches up via `resumeStream(id)`, including the final result after the handler finished. `ctx.rooms.broadcast(room, topic, data)` fans out to every CURRENTLY-CONNECTED subscriber (second device, second tab) but does not replay missed messages — use the stream id for anything that must survive a gap. Broadcast resolves `{ delivered: false }` when the room has no members — a no-op, not an error. Clients receive with `useRoomMessages(room, cb)` from `@pylonsync/react`; `useRoom` is the send side.
67
67
  - **It's `db.useQueryOne`, not `useOne`.** Validators and field types have aliases: `v.bool`/`v.boolean`, `v.float`/`v.number`.
68
68
  - **Use the supported file and scheduling APIs.** Files go through `<FileUpload>` and `/api/files/*`; an upload is readable by its uploader only, so pass `visibility: "public"` to `uploadFile` or `<FileUpload>` for files every visitor sees; there is no `ctx.files`. One-shot work uses `ctx.scheduler.runAfter`, `runAt`, or `cancel`; there is no `defineWorkflow` or `defineJob`. Recurring work uses `cron("0 * * * *", "fnName")` in `buildManifest({ crons: [...] })`, imported from `@pylonsync/sdk`. Make the target function `internal: true`. It runs with anonymous auth, but its own `ctx.db.*` calls are server-side and bypass policies. Use `ctx.auth.elevate({ admin: true, reason: "..." })`, with a mandatory reason, only when chaining another internal function through `ctx.scheduler`.
@@ -18,14 +18,24 @@ increment in another without refreshing.
18
18
  ## How the realtime works
19
19
 
20
20
  - `functions/joinWaitlist.ts`: a public **mutation** that validates, lowercases,
21
- and dedupes the email, then inserts one `Signup` row. The insert fires a
22
- change event.
23
- - `functions/waitlistCount.ts`: a public **query** the landing page subscribes
24
- to with `db.useReactiveQuery`. The server records that it read the `Signup`
25
- table, so every new signup re-runs it and pushes the fresh count to every open
26
- tab. No polling.
27
- - The counter island (`app/waitlist-hero.tsx`) is wrapped in `<EnsureGuest>`,
28
- which mints an anonymous session so the live WebSocket can connect.
21
+ and dedupes the email, inserts one `Signup` row, and rewrites the PII-free
22
+ `WaitlistStat` row with the new count.
23
+ - The landing page reads `WaitlistStat` with `db.useQuery`. The row syncs to
24
+ every open tab, so the counter moves as soon as anyone joins. No polling.
25
+ - The counter island (`app/(marketing)/waitlist-hero.tsx`) is wrapped in
26
+ `<EnsureGuest>`, which mints an anonymous session so the live connection can
27
+ open.
28
+
29
+ The counter is the `Signup` table's row count. `counter.importedCount` in
30
+ `lib/site.config.ts` adds signups you collected somewhere else before this site
31
+ (an old form, a spreadsheet). It ships as 0.
32
+
33
+ ## Demo data
34
+
35
+ In `pylon dev`, an empty waitlist gets about 180 fictional signups from the last
36
+ 30 days, so the counter, chart, and list have data (`functions/seedDemo.ts`,
37
+ `lib/demo.ts`). Seeding runs only when `PYLON_DEMO_DATA` is on, or when it is unset and the app runs under `pylon dev`, and only while the `Signup` table is empty. `pylon start`, Docker, and Pylon Cloud deploys do not seed, because only `pylon dev` sets `PYLON_DEV_WATCH_DIR`. Set `PYLON_DEMO_DATA=0` to start
38
+ empty in development. Delete both files once you have real signups.
29
39
 
30
40
  ## Privacy
31
41
 
@@ -47,22 +57,28 @@ account can see signups; anyone else gets a locked screen.
47
57
 
48
58
  ## Rebrand it
49
59
 
50
- Brand, colors, hero copy, value props, social proof, and FAQ content live in
60
+ Brand, colors, hero copy, the launch date, value props, and FAQ content live in
51
61
  **`lib/site.config.ts`**. Edit or generate that file to update the page without
52
62
  changing JSX or CSS.
53
63
 
64
+ The tab icon is `app/icon.svg` (with `public/favicon.ico` for browsers that
65
+ request `/favicon.ico`). Both carry this brand's letter and colors; replace
66
+ them when you rename the brand.
67
+
54
68
  ## Layout
55
69
 
56
70
  ```
57
71
  app.ts data model + manifest (Signup, User, policies, auth)
58
72
  lib/site.config.ts ALL business copy + brand + colors (edit this)
59
73
  lib/owner.ts owner-email gate (PYLON_OWNER_EMAIL)
60
- lib/stats.ts shared dashboard-stats types
74
+ lib/stats.ts dashboard types + the public count
75
+ lib/launch.ts the launch line from hero.launchDate
76
+ lib/demo.ts demo signups + the dev-only gate
61
77
  functions/joinWaitlist.ts public mutation: validate + dedupe + insert
62
- functions/waitlistCount.ts public reactive query: the live counter
78
+ functions/seedDemo.ts dev-only demo signups (see Demo data)
63
79
  functions/waitlistStats.ts owner-only reactive query: total + chart + list
64
- app/page.tsx the landing page (server-rendered)
65
- app/waitlist-hero.tsx client island: signup form + live counter
80
+ app/(marketing)/page.tsx the landing page (server-rendered)
81
+ app/(marketing)/waitlist-hero.tsx client island: signup form + live counter
66
82
  app/login/page.tsx owner sign-in
67
83
  app/dashboard/ owner dashboard (auth-gated, live)
68
84
  app/globals.css Tailwind entrypoint (compiled by Pylon)
@@ -3,6 +3,7 @@ import { type Metadata } from "@pylonsync/react";
3
3
  import { WRAP, FactList, ProductMock } from "@/components/marketing";
4
4
  import { SignupForm, LiveCount } from "./waitlist-hero";
5
5
  import { siteConfig } from "@/lib/site.config";
6
+ import { launchLabel } from "@/lib/launch";
6
7
 
7
8
  // SEO metadata: server-rendered into <head>, so the page is fully indexable.
8
9
  // All copy lives in lib/site.config.ts; edit it there to rebrand.
@@ -25,6 +26,7 @@ export const metadata: Metadata = {
25
26
  // Signed-in state (the nav's "Dashboard" link) is resolved in the layout.
26
27
  export default function LandingPage() {
27
28
  const { hero, counter, mock, facts, faq } = siteConfig;
29
+ const launch = launchLabel(hero.launchDate, Date.now());
28
30
 
29
31
  return (
30
32
  <div>
@@ -36,9 +38,14 @@ export default function LandingPage() {
36
38
  </h1>
37
39
  <p className="mt-6 max-w-md text-[17px] leading-relaxed text-chalk-2">{hero.subcopy}</p>
38
40
  <div className="mt-8 max-w-md">
39
- <SignupForm hero={hero} />
41
+ <SignupForm hero={hero} showCountNote={counter.enabled} />
42
+ </div>
43
+ <div className="mt-8 flex flex-col gap-2 border-t border-line pt-5">
44
+ {counter.enabled ? (
45
+ <LiveCount importedCount={counter.importedCount} label={counter.label} labelOne={counter.labelOne} />
46
+ ) : null}
47
+ {launch ? <p className="font-mono-ui text-[13px] text-brand">{launch}</p> : null}
40
48
  </div>
41
- <p className="font-mono-ui mt-6 text-[13px] text-brand">{hero.launchNote}</p>
42
49
  </div>
43
50
  <ProductMock
44
51
  windowTitle={mock.windowTitle}
@@ -56,11 +63,6 @@ export default function LandingPage() {
56
63
  <div className="mt-8">
57
64
  <FactList items={facts.items} />
58
65
  </div>
59
- {counter.enabled ? (
60
- <div className="mt-12 border-t border-line pt-5">
61
- <LiveCount seed={counter.seedCount ?? 0} label={counter.label} />
62
- </div>
63
- ) : null}
64
66
  </section>
65
67
 
66
68
  {/* ============================== FAQ ============================= */}