@pylonsync/create-pylon 0.19.1 → 0.20.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 (675) hide show
  1. package/bin/create-pylon.js +30 -7
  2. package/package.json +1 -1
  3. package/templates/agency/.env.example +7 -0
  4. package/templates/agency/README.md +28 -3
  5. package/templates/agency/app/(marketing)/contact-form.tsx +20 -18
  6. package/templates/agency/app/(marketing)/page.tsx +3 -3
  7. package/templates/agency/app/(marketing)/work/page.tsx +3 -3
  8. package/templates/agency/app/auth-form.tsx +4 -4
  9. package/templates/agency/app/dashboard/dashboard-client.tsx +110 -64
  10. package/templates/agency/app/dashboard/page.tsx +14 -16
  11. package/templates/agency/app/icon.svg +3 -0
  12. package/templates/agency/app/login/page.tsx +12 -14
  13. package/templates/agency/components/marketing.tsx +12 -5
  14. package/templates/agency/functions/seedStudioBackoffice.ts +42 -11
  15. package/templates/agency/lib/agency.ts +22 -0
  16. package/templates/agency/lib/demo.ts +118 -0
  17. package/templates/agency/lib/site.config.ts +19 -13
  18. package/templates/agency/public/favicon.ico +0 -0
  19. package/templates/agency/tests/agency.test.ts +57 -0
  20. package/templates/ai-chat/.env.example +13 -27
  21. package/templates/ai-chat/README.md +53 -60
  22. package/templates/ai-chat/app/(chat)/c/[id]/page.tsx +19 -0
  23. package/templates/ai-chat/app/(chat)/chat-client.tsx +657 -622
  24. package/templates/ai-chat/app/(chat)/layout.tsx +6 -43
  25. package/templates/ai-chat/app/(chat)/not-found.tsx +6 -10
  26. package/templates/ai-chat/app/(chat)/page.tsx +7 -11
  27. package/templates/ai-chat/app/auth-form.tsx +45 -38
  28. package/templates/ai-chat/app/error.tsx +9 -12
  29. package/templates/ai-chat/app/globals.css +464 -102
  30. package/templates/ai-chat/app/layout.tsx +11 -19
  31. package/templates/ai-chat/app/login/page.tsx +39 -24
  32. package/templates/ai-chat/app.ts +118 -43
  33. package/templates/ai-chat/components/chat/composer.tsx +193 -0
  34. package/templates/ai-chat/components/chat/empty-state.tsx +65 -0
  35. package/templates/ai-chat/components/chat/icons.tsx +198 -0
  36. package/templates/ai-chat/components/chat/markdown.tsx +164 -0
  37. package/templates/ai-chat/components/chat/message.tsx +183 -0
  38. package/templates/ai-chat/components/chat/setup.tsx +71 -0
  39. package/templates/ai-chat/components/chat/sidebar.tsx +288 -0
  40. package/templates/ai-chat/functions/_beginTurn.ts +145 -0
  41. package/templates/ai-chat/functions/_finishTurn.ts +55 -0
  42. package/templates/ai-chat/functions/assistantStatus.ts +13 -0
  43. package/templates/ai-chat/functions/claimGuestHistory.ts +39 -0
  44. package/templates/ai-chat/functions/createConversation.ts +28 -0
  45. package/templates/ai-chat/functions/deleteConversation.ts +15 -20
  46. package/templates/ai-chat/functions/prepareGuestClaim.ts +32 -0
  47. package/templates/ai-chat/functions/renameConversation.ts +19 -0
  48. package/templates/ai-chat/functions/respond.ts +88 -0
  49. package/templates/ai-chat/functions/seedExamples.ts +46 -0
  50. package/templates/ai-chat/functions/stopResponse.ts +30 -0
  51. package/templates/ai-chat/lib/brand-icon.ts +7 -0
  52. package/templates/ai-chat/lib/chat.ts +277 -0
  53. package/templates/ai-chat/lib/claim.ts +15 -0
  54. package/templates/ai-chat/lib/examples.ts +179 -0
  55. package/templates/ai-chat/lib/highlight.ts +107 -0
  56. package/templates/ai-chat/lib/markdown.ts +317 -0
  57. package/templates/ai-chat/lib/provider.ts +38 -0
  58. package/templates/ai-chat/lib/site.config.ts +55 -39
  59. package/templates/ai-chat/tests/chat.test.ts +174 -0
  60. package/templates/ai-chat/tests/components.test.tsx +57 -0
  61. package/templates/ai-chat/tests/markdown.test.ts +88 -0
  62. package/templates/ai-chat/tests/provider.test.ts +32 -0
  63. package/templates/backend/consumer/apps/api/app/layout.tsx +33 -0
  64. package/templates/backend/consumer/apps/api/app/page.tsx +54 -0
  65. package/templates/backend/consumer/apps/api/app.ts +166 -0
  66. package/templates/backend/consumer/apps/api/functions/addComment.ts +24 -0
  67. package/templates/backend/consumer/apps/api/functions/createPost.ts +19 -36
  68. package/templates/backend/consumer/apps/api/functions/deletePost.ts +15 -13
  69. package/templates/backend/consumer/apps/api/functions/myProfile.ts +5 -8
  70. package/templates/backend/consumer/apps/api/functions/seedDemo.ts +95 -0
  71. package/templates/backend/consumer/apps/api/functions/setFollow.ts +36 -0
  72. package/templates/backend/consumer/apps/api/functions/setLike.ts +36 -0
  73. package/templates/backend/consumer/apps/api/functions/upsertProfile.ts +43 -37
  74. package/templates/backend/consumer/apps/api/lib/seed.ts +89 -0
  75. package/templates/backend/consumer/apps/api/lib/social.ts +50 -0
  76. package/templates/backend/consumer/apps/api/package.json +8 -2
  77. package/templates/backend/consumer/apps/api/public/images/posts/arch.jpg +0 -0
  78. package/templates/backend/consumer/apps/api/public/images/posts/coffee.jpg +0 -0
  79. package/templates/backend/consumer/apps/api/public/images/posts/dog.jpg +0 -0
  80. package/templates/backend/consumer/apps/api/public/images/posts/lisbon.jpg +0 -0
  81. package/templates/backend/consumer/apps/api/public/images/posts/ramen.jpg +0 -0
  82. package/templates/backend/consumer/apps/api/public/images/posts/run.jpg +0 -0
  83. package/templates/backend/consumer/apps/api/public/images/posts/sunset.jpg +0 -0
  84. package/templates/backend/consumer/apps/api/tsconfig.json +12 -2
  85. package/templates/chat/README.md +43 -21
  86. package/templates/chat/app/chat-client.tsx +495 -85
  87. package/templates/chat/app/error.tsx +1 -1
  88. package/templates/chat/app/globals.css +170 -54
  89. package/templates/chat/app/layout.tsx +12 -9
  90. package/templates/chat/app/not-found.tsx +4 -2
  91. package/templates/chat/app/page.tsx +9 -19
  92. package/templates/chat/app.ts +96 -17
  93. package/templates/chat/components/chat/avatar.tsx +45 -0
  94. package/templates/chat/components/chat/composer.tsx +110 -0
  95. package/templates/chat/components/chat/message-list.tsx +285 -0
  96. package/templates/chat/components/chat/sidebar.tsx +190 -0
  97. package/templates/chat/functions/joinChat.ts +83 -0
  98. package/templates/chat/functions/renameMember.ts +25 -0
  99. package/templates/chat/lib/chat.ts +188 -0
  100. package/templates/chat/lib/seed.ts +78 -0
  101. package/templates/chat/tests/chat.test.ts +159 -0
  102. package/templates/chat/tests/composer.test.tsx +54 -0
  103. package/templates/consumer/README.md +3 -3
  104. package/templates/consumer/app/globals.css +10 -3
  105. package/templates/consumer/app/icon.svg +1 -0
  106. package/templates/consumer/components/social/avatar.tsx +25 -11
  107. package/templates/consumer/functions/seedFeed.ts +3 -3
  108. package/templates/consumer/lib/seed.ts +43 -85
  109. package/templates/consumer/public/favicon.ico +0 -0
  110. package/templates/consumer/public/images/posts/arch.jpg +0 -0
  111. package/templates/consumer/public/images/posts/coffee.jpg +0 -0
  112. package/templates/consumer/public/images/posts/dog.jpg +0 -0
  113. package/templates/consumer/public/images/posts/lisbon.jpg +0 -0
  114. package/templates/consumer/public/images/posts/ramen.jpg +0 -0
  115. package/templates/consumer/public/images/posts/run.jpg +0 -0
  116. package/templates/consumer/public/images/posts/sunset.jpg +0 -0
  117. package/templates/consumer/tests/seed.test.ts +39 -0
  118. package/templates/creator/.env.example +7 -0
  119. package/templates/creator/README.md +24 -5
  120. package/templates/creator/app/(marketing)/newsletter-signup.tsx +33 -12
  121. package/templates/creator/app/auth-form.tsx +7 -7
  122. package/templates/creator/app/dashboard/dashboard-client.tsx +137 -100
  123. package/templates/creator/app/dashboard/page.tsx +18 -18
  124. package/templates/creator/app/icon.svg +2 -0
  125. package/templates/creator/app/login/page.tsx +14 -14
  126. package/templates/creator/app.ts +4 -4
  127. package/templates/creator/functions/seedDemo.ts +32 -0
  128. package/templates/creator/functions/subscribe.ts +4 -15
  129. package/templates/creator/lib/demo.ts +94 -0
  130. package/templates/creator/lib/site.config.ts +15 -10
  131. package/templates/creator/lib/stats.ts +35 -0
  132. package/templates/creator/public/favicon.ico +0 -0
  133. package/templates/creator/tests/counter.test.ts +92 -0
  134. package/templates/creator/tests/live-count.test.tsx +45 -0
  135. package/templates/crm/README.md +22 -1
  136. package/templates/crm/app/apple-icon.png +0 -0
  137. package/templates/crm/app/companies/companies-view.tsx +32 -14
  138. package/templates/crm/app/companies/page.tsx +2 -1
  139. package/templates/crm/app/contacts/contacts-view.tsx +34 -10
  140. package/templates/crm/app/contacts/page.tsx +2 -1
  141. package/templates/crm/app/deals/[id]/deal-view.tsx +137 -98
  142. package/templates/crm/app/deals/[id]/page.tsx +2 -1
  143. package/templates/crm/app/globals.css +29 -14
  144. package/templates/crm/app/icon.svg +1 -0
  145. package/templates/crm/app/layout.tsx +7 -5
  146. package/templates/crm/app/page.tsx +2 -1
  147. package/templates/crm/app/pipeline-view.tsx +5 -5
  148. package/templates/crm/app/workspace.tsx +29 -17
  149. package/templates/crm/app.ts +5 -5
  150. package/templates/crm/components/activity-timeline.tsx +14 -4
  151. package/templates/crm/components/app-shell.tsx +94 -0
  152. package/templates/crm/components/auth-form.tsx +35 -5
  153. package/templates/crm/components/avatar.tsx +5 -1
  154. package/templates/crm/components/board-skeleton.tsx +5 -5
  155. package/templates/crm/components/brand-mark.tsx +25 -0
  156. package/templates/crm/components/command-palette.tsx +17 -17
  157. package/templates/crm/components/data-table.tsx +97 -36
  158. package/templates/crm/components/deal-card.tsx +23 -27
  159. package/templates/crm/components/filter-tabs.tsx +101 -0
  160. package/templates/crm/components/metrics-bar.tsx +6 -6
  161. package/templates/crm/components/nav.tsx +15 -0
  162. package/templates/crm/components/page-header.tsx +43 -7
  163. package/templates/crm/components/pipeline-board.tsx +16 -10
  164. package/templates/crm/components/require-auth.tsx +24 -26
  165. package/templates/crm/components/sidebar.tsx +54 -49
  166. package/templates/crm/components/stage-badge.tsx +4 -2
  167. package/templates/crm/components/ui/button.tsx +5 -4
  168. package/templates/crm/components/ui/dialog.tsx +1 -1
  169. package/templates/crm/components/ui/input.tsx +1 -1
  170. package/templates/crm/components/ui/select.tsx +1 -1
  171. package/templates/crm/components/ui/textarea.tsx +1 -1
  172. package/templates/crm/functions/seedWorkspace.ts +32 -17
  173. package/templates/crm/lib/brand.ts +9 -0
  174. package/templates/crm/lib/demo-team.ts +51 -0
  175. package/templates/crm/lib/pipeline.ts +37 -0
  176. package/templates/crm/lib/search.ts +4 -3
  177. package/templates/crm/lib/seed.ts +53 -8
  178. package/templates/crm/public/favicon.ico +0 -0
  179. package/templates/crm/tests/components.test.tsx +5 -5
  180. package/templates/crm/tests/demo-team.test.ts +18 -0
  181. package/templates/crm/tests/pipeline.test.ts +59 -3
  182. package/templates/directory/.env.example +7 -0
  183. package/templates/directory/README.md +22 -3
  184. package/templates/directory/app/(marketing)/directory-browse.tsx +30 -12
  185. package/templates/directory/app/(marketing)/page.tsx +6 -4
  186. package/templates/directory/app/auth-form.tsx +4 -4
  187. package/templates/directory/app/dashboard/dashboard-client.tsx +177 -90
  188. package/templates/directory/app/dashboard/page.tsx +16 -14
  189. package/templates/directory/app/icon.svg +4 -0
  190. package/templates/directory/app/login/page.tsx +16 -15
  191. package/templates/directory/functions/seedDemo.ts +43 -0
  192. package/templates/directory/lib/demo.ts +145 -0
  193. package/templates/directory/lib/directory.ts +27 -0
  194. package/templates/directory/lib/site.config.ts +21 -16
  195. package/templates/directory/public/favicon.ico +0 -0
  196. package/templates/directory/tests/directory.test.ts +46 -0
  197. package/templates/expo/consumer/apps/expo/App.tsx +360 -288
  198. package/templates/expo/consumer/apps/expo/app.json +9 -1
  199. package/templates/expo/consumer/apps/expo/package.json +2 -1
  200. package/templates/helpdesk/README.md +21 -0
  201. package/templates/helpdesk/app/apple-icon.png +0 -0
  202. package/templates/helpdesk/app/customers/customers-view.tsx +23 -7
  203. package/templates/helpdesk/app/customers/page.tsx +2 -1
  204. package/templates/helpdesk/app/globals.css +29 -14
  205. package/templates/helpdesk/app/icon.svg +1 -0
  206. package/templates/helpdesk/app/inbox-view.tsx +66 -47
  207. package/templates/helpdesk/app/layout.tsx +7 -5
  208. package/templates/helpdesk/app/page.tsx +4 -7
  209. package/templates/helpdesk/app/tickets/[id]/page.tsx +2 -1
  210. package/templates/helpdesk/app/tickets/[id]/ticket-view.tsx +134 -97
  211. package/templates/helpdesk/app/workspace.tsx +33 -22
  212. package/templates/helpdesk/app.ts +2 -2
  213. package/templates/helpdesk/components/app-shell.tsx +94 -0
  214. package/templates/helpdesk/components/auth-form.tsx +35 -5
  215. package/templates/helpdesk/components/avatar.tsx +5 -1
  216. package/templates/helpdesk/components/brand-mark.tsx +28 -0
  217. package/templates/helpdesk/components/command-palette.tsx +17 -17
  218. package/templates/helpdesk/components/data-table.tsx +97 -36
  219. package/templates/helpdesk/components/filter-tabs.tsx +101 -0
  220. package/templates/helpdesk/components/nav.tsx +14 -0
  221. package/templates/helpdesk/components/page-header.tsx +43 -7
  222. package/templates/helpdesk/components/priority-badge.tsx +49 -6
  223. package/templates/helpdesk/components/require-auth.tsx +24 -26
  224. package/templates/helpdesk/components/sidebar.tsx +54 -48
  225. package/templates/helpdesk/components/sla-indicator.tsx +5 -8
  226. package/templates/helpdesk/components/thread.tsx +7 -7
  227. package/templates/helpdesk/components/ticket-list.tsx +79 -19
  228. package/templates/helpdesk/components/ui/button.tsx +5 -4
  229. package/templates/helpdesk/components/ui/dialog.tsx +1 -1
  230. package/templates/helpdesk/components/ui/input.tsx +1 -1
  231. package/templates/helpdesk/components/ui/select.tsx +1 -1
  232. package/templates/helpdesk/components/ui/textarea.tsx +1 -1
  233. package/templates/helpdesk/functions/seedWorkspace.ts +25 -15
  234. package/templates/helpdesk/lib/brand.ts +9 -0
  235. package/templates/helpdesk/lib/demo-team.ts +51 -0
  236. package/templates/helpdesk/lib/search.ts +4 -3
  237. package/templates/helpdesk/lib/seed.ts +43 -2
  238. package/templates/helpdesk/lib/tickets.ts +68 -20
  239. package/templates/helpdesk/public/favicon.ico +0 -0
  240. package/templates/helpdesk/tests/components.test.tsx +10 -0
  241. package/templates/helpdesk/tests/demo-team.test.ts +18 -0
  242. package/templates/helpdesk/tests/tickets.test.ts +70 -0
  243. package/templates/imessage/.env.example +49 -0
  244. package/templates/imessage/AGENTS.md +153 -0
  245. package/templates/imessage/CLAUDE.md +7 -0
  246. package/templates/imessage/README.md +187 -0
  247. package/templates/imessage/app/console.tsx +382 -0
  248. package/templates/imessage/app/contacts/page.tsx +13 -0
  249. package/templates/imessage/app/error.tsx +16 -0
  250. package/templates/imessage/app/gate.tsx +31 -0
  251. package/templates/imessage/app/globals.css +183 -0
  252. package/templates/imessage/app/layout.tsx +17 -0
  253. package/templates/imessage/app/login/login-form.tsx +115 -0
  254. package/templates/imessage/app/login/page.tsx +30 -0
  255. package/templates/imessage/app/not-found.tsx +13 -0
  256. package/templates/imessage/app/page.tsx +14 -0
  257. package/templates/imessage/app/robots.ts +6 -0
  258. package/templates/imessage/app/setup/page.tsx +13 -0
  259. package/templates/imessage/app/sign-out.tsx +20 -0
  260. package/templates/imessage/app.ts +299 -0
  261. package/templates/imessage/bunfig +4 -0
  262. package/templates/imessage/components/avatar.tsx +46 -0
  263. package/templates/imessage/components/contact-panel.tsx +263 -0
  264. package/templates/imessage/components/contacts-view.tsx +172 -0
  265. package/templates/imessage/components/conversation-list.tsx +129 -0
  266. package/templates/imessage/components/nav-rail.tsx +67 -0
  267. package/templates/imessage/components/setup-view.tsx +408 -0
  268. package/templates/imessage/components/thread.tsx +229 -0
  269. package/templates/imessage/components/ui/badge.tsx +37 -0
  270. package/templates/imessage/components/ui/button.tsx +56 -0
  271. package/templates/imessage/components/ui/card.tsx +90 -0
  272. package/templates/imessage/components/ui/dialog.tsx +110 -0
  273. package/templates/imessage/components/ui/input.tsx +30 -0
  274. package/templates/imessage/components/ui/label.tsx +21 -0
  275. package/templates/imessage/components/ui/select.tsx +39 -0
  276. package/templates/imessage/components/ui/table.tsx +124 -0
  277. package/templates/imessage/components/ui/textarea.tsx +24 -0
  278. package/templates/imessage/components.json +20 -0
  279. package/templates/imessage/functions/answerNow.ts +34 -0
  280. package/templates/imessage/functions/assistant.ts +96 -0
  281. package/templates/imessage/functions/beginTurn.ts +101 -0
  282. package/templates/imessage/functions/cancelReminder.ts +13 -0
  283. package/templates/imessage/functions/claimOutbound.ts +44 -0
  284. package/templates/imessage/functions/clearDemo.ts +18 -0
  285. package/templates/imessage/functions/deleteNote.ts +11 -0
  286. package/templates/imessage/functions/deliverOutbound.ts +55 -0
  287. package/templates/imessage/functions/finishTurn.ts +84 -0
  288. package/templates/imessage/functions/fireReminder.ts +42 -0
  289. package/templates/imessage/functions/ingestInbound.ts +22 -0
  290. package/templates/imessage/functions/ownerAccount.ts +13 -0
  291. package/templates/imessage/functions/processTurn.ts +74 -0
  292. package/templates/imessage/functions/relayApply.ts +102 -0
  293. package/templates/imessage/functions/relaySync.ts +63 -0
  294. package/templates/imessage/functions/releaseOutbound.ts +18 -0
  295. package/templates/imessage/functions/resolveTurn.ts +25 -0
  296. package/templates/imessage/functions/saveContact.ts +27 -0
  297. package/templates/imessage/functions/seedDemo.ts +70 -0
  298. package/templates/imessage/functions/sendOwnerMessage.ts +28 -0
  299. package/templates/imessage/functions/sendblueWebhook.ts +49 -0
  300. package/templates/imessage/functions/setContactStatus.ts +17 -0
  301. package/templates/imessage/functions/settleOutbound.ts +35 -0
  302. package/templates/imessage/functions/setupStatus.ts +33 -0
  303. package/templates/imessage/functions/sweep.ts +58 -0
  304. package/templates/imessage/functions/toolCancelReminder.ts +17 -0
  305. package/templates/imessage/functions/toolForgetNote.ts +15 -0
  306. package/templates/imessage/functions/toolListNotes.ts +16 -0
  307. package/templates/imessage/functions/toolListReminders.ts +17 -0
  308. package/templates/imessage/functions/toolSaveNote.ts +22 -0
  309. package/templates/imessage/functions/toolSetReminder.ts +55 -0
  310. package/templates/imessage/functions/updateSettings.ts +48 -0
  311. package/templates/imessage/gitignore +11 -0
  312. package/templates/imessage/lib/agent-call.ts +251 -0
  313. package/templates/imessage/lib/assistant.ts +74 -0
  314. package/templates/imessage/lib/chunk.ts +135 -0
  315. package/templates/imessage/lib/demo.ts +68 -0
  316. package/templates/imessage/lib/format.ts +170 -0
  317. package/templates/imessage/lib/gate.ts +104 -0
  318. package/templates/imessage/lib/handles.ts +67 -0
  319. package/templates/imessage/lib/provider.ts +38 -0
  320. package/templates/imessage/lib/relay-protocol.ts +95 -0
  321. package/templates/imessage/lib/reminders.ts +41 -0
  322. package/templates/imessage/lib/secrets.ts +59 -0
  323. package/templates/imessage/lib/sendblue.ts +173 -0
  324. package/templates/imessage/lib/store.ts +407 -0
  325. package/templates/imessage/lib/transport.ts +135 -0
  326. package/templates/imessage/lib/utils.ts +10 -0
  327. package/templates/imessage/package.json +40 -0
  328. package/templates/imessage/relay/.env.example +14 -0
  329. package/templates/imessage/relay/applescript.ts +70 -0
  330. package/templates/imessage/relay/chatdb.ts +165 -0
  331. package/templates/imessage/relay/client.ts +41 -0
  332. package/templates/imessage/relay/main.ts +137 -0
  333. package/templates/imessage/relay/state.ts +35 -0
  334. package/templates/imessage/tests/agent-call.test.ts +112 -0
  335. package/templates/imessage/tests/applescript.test.ts +88 -0
  336. package/templates/imessage/tests/chatdb.test.ts +139 -0
  337. package/templates/imessage/tests/chunk.test.ts +61 -0
  338. package/templates/imessage/tests/fake-ctx.ts +88 -0
  339. package/templates/imessage/tests/ingest.test.ts +141 -0
  340. package/templates/imessage/tests/lib.test.ts +99 -0
  341. package/templates/imessage/tests/review-fixes.test.ts +138 -0
  342. package/templates/imessage/tests/secrets.test.ts +36 -0
  343. package/templates/imessage/tests/sendblue.test.ts +143 -0
  344. package/templates/imessage/tests/setup.ts +5 -0
  345. package/templates/imessage/tsconfig.json +18 -0
  346. package/templates/inventory/README.md +21 -0
  347. package/templates/inventory/app/apple-icon.png +0 -0
  348. package/templates/inventory/app/globals.css +29 -14
  349. package/templates/inventory/app/icon.svg +1 -0
  350. package/templates/inventory/app/layout.tsx +7 -5
  351. package/templates/inventory/app/movements/movements-view.tsx +32 -10
  352. package/templates/inventory/app/movements/page.tsx +2 -1
  353. package/templates/inventory/app/page.tsx +4 -6
  354. package/templates/inventory/app/products/[id]/page.tsx +2 -1
  355. package/templates/inventory/app/products/[id]/product-view.tsx +55 -25
  356. package/templates/inventory/app/products-view.tsx +74 -33
  357. package/templates/inventory/app/workspace.tsx +34 -16
  358. package/templates/inventory/app.ts +10 -2
  359. package/templates/inventory/components/app-shell.tsx +94 -0
  360. package/templates/inventory/components/auth-form.tsx +35 -5
  361. package/templates/inventory/components/avatar.tsx +5 -1
  362. package/templates/inventory/components/brand-mark.tsx +25 -0
  363. package/templates/inventory/components/category-tile.tsx +49 -0
  364. package/templates/inventory/components/command-palette.tsx +17 -17
  365. package/templates/inventory/components/data-table.tsx +97 -36
  366. package/templates/inventory/components/filter-tabs.tsx +101 -0
  367. package/templates/inventory/components/nav.tsx +14 -0
  368. package/templates/inventory/components/page-header.tsx +43 -7
  369. package/templates/inventory/components/require-auth.tsx +24 -26
  370. package/templates/inventory/components/sidebar.tsx +54 -48
  371. package/templates/inventory/components/stock-level.tsx +30 -14
  372. package/templates/inventory/components/summary-bar.tsx +8 -15
  373. package/templates/inventory/components/ui/button.tsx +5 -4
  374. package/templates/inventory/components/ui/dialog.tsx +1 -1
  375. package/templates/inventory/components/ui/input.tsx +1 -1
  376. package/templates/inventory/components/ui/select.tsx +1 -1
  377. package/templates/inventory/components/ui/textarea.tsx +1 -1
  378. package/templates/inventory/functions/seedWorkspace.ts +18 -8
  379. package/templates/inventory/lib/brand.ts +9 -0
  380. package/templates/inventory/lib/demo-team.ts +51 -0
  381. package/templates/inventory/lib/search.ts +4 -3
  382. package/templates/inventory/lib/seed.ts +29 -2
  383. package/templates/inventory/lib/stock.ts +32 -0
  384. package/templates/inventory/public/favicon.ico +0 -0
  385. package/templates/inventory/tests/demo-team.test.ts +18 -0
  386. package/templates/inventory/tests/stock.test.ts +29 -0
  387. package/templates/invoices/README.md +16 -0
  388. package/templates/invoices/app/apple-icon.png +0 -0
  389. package/templates/invoices/app/clients/clients-view.tsx +18 -6
  390. package/templates/invoices/app/clients/page.tsx +2 -1
  391. package/templates/invoices/app/globals.css +29 -14
  392. package/templates/invoices/app/icon.svg +1 -0
  393. package/templates/invoices/app/invoices/[id]/invoice-view.tsx +72 -48
  394. package/templates/invoices/app/invoices/[id]/page.tsx +2 -1
  395. package/templates/invoices/app/invoices-view.tsx +81 -32
  396. package/templates/invoices/app/layout.tsx +7 -5
  397. package/templates/invoices/app/page.tsx +4 -6
  398. package/templates/invoices/app/workspace.tsx +26 -16
  399. package/templates/invoices/app.ts +2 -2
  400. package/templates/invoices/components/app-shell.tsx +94 -0
  401. package/templates/invoices/components/auth-form.tsx +35 -5
  402. package/templates/invoices/components/avatar.tsx +5 -1
  403. package/templates/invoices/components/brand-mark.tsx +26 -0
  404. package/templates/invoices/components/command-palette.tsx +17 -17
  405. package/templates/invoices/components/data-table.tsx +97 -36
  406. package/templates/invoices/components/filter-tabs.tsx +101 -0
  407. package/templates/invoices/components/line-items.tsx +19 -14
  408. package/templates/invoices/components/nav.tsx +14 -0
  409. package/templates/invoices/components/page-header.tsx +43 -7
  410. package/templates/invoices/components/require-auth.tsx +24 -26
  411. package/templates/invoices/components/sidebar.tsx +54 -48
  412. package/templates/invoices/components/status-badge.tsx +7 -7
  413. package/templates/invoices/components/summary-bar.tsx +5 -5
  414. package/templates/invoices/components/ui/button.tsx +5 -4
  415. package/templates/invoices/components/ui/dialog.tsx +1 -1
  416. package/templates/invoices/components/ui/input.tsx +1 -1
  417. package/templates/invoices/components/ui/select.tsx +1 -1
  418. package/templates/invoices/components/ui/textarea.tsx +1 -1
  419. package/templates/invoices/functions/recordPayment.ts +3 -0
  420. package/templates/invoices/lib/billing.ts +40 -0
  421. package/templates/invoices/lib/brand.ts +9 -0
  422. package/templates/invoices/lib/search.ts +4 -3
  423. package/templates/invoices/public/favicon.ico +0 -0
  424. package/templates/invoices/tests/billing.test.ts +27 -0
  425. package/templates/ios/consumer/apps/ios/Package.swift +4 -3
  426. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ActivityView.swift +107 -0
  427. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AppModel.swift +171 -0
  428. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AuthView.swift +182 -0
  429. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/CommentsView.swift +136 -0
  430. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ComposeView.swift +152 -0
  431. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ExploreView.swift +117 -0
  432. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/FeedView.swift +175 -159
  433. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/MainTabView.swift +54 -0
  434. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Media.swift +271 -0
  435. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +101 -4
  436. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/PostCard.swift +245 -0
  437. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileEditorView.swift +208 -0
  438. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileView.swift +183 -0
  439. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/RootView.swift +25 -31
  440. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/SocialStore.swift +285 -0
  441. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/StoryViewer.swift +155 -0
  442. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Theme.swift +82 -0
  443. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +4 -47
  444. package/templates/ios/consumer/apps/ios/project.yml +13 -9
  445. package/templates/local-service/.env.example +7 -0
  446. package/templates/local-service/README.md +26 -2
  447. package/templates/local-service/app/(marketing)/booking-widget.tsx +48 -9
  448. package/templates/local-service/app/(marketing)/page.tsx +66 -42
  449. package/templates/local-service/app/auth-form.tsx +8 -8
  450. package/templates/local-service/app/dashboard/dashboard-client.tsx +119 -92
  451. package/templates/local-service/app/dashboard/page.tsx +14 -14
  452. package/templates/local-service/app/icon.svg +2 -0
  453. package/templates/local-service/app/login/page.tsx +15 -15
  454. package/templates/local-service/functions/seedDemo.ts +49 -0
  455. package/templates/local-service/lib/demo.ts +135 -0
  456. package/templates/local-service/lib/hours.ts +44 -0
  457. package/templates/local-service/lib/site.config.ts +8 -3
  458. package/templates/local-service/lib/slots.ts +3 -0
  459. package/templates/local-service/public/favicon.ico +0 -0
  460. package/templates/local-service/tests/booking.test.ts +104 -0
  461. package/templates/marketplace/README.md +23 -15
  462. package/templates/marketplace/app/globals.css +172 -114
  463. package/templates/marketplace/app/layout.tsx +75 -30
  464. package/templates/marketplace/app/listing/[id]/page.tsx +210 -123
  465. package/templates/marketplace/app/me/page.tsx +4 -4
  466. package/templates/marketplace/app/page.tsx +221 -298
  467. package/templates/marketplace/app/sell/page.tsx +8 -11
  468. package/templates/marketplace/app.ts +32 -7
  469. package/templates/marketplace/client/ActivityFeed.tsx +311 -0
  470. package/templates/marketplace/client/AuthNav.tsx +24 -16
  471. package/templates/marketplace/client/ListingCard.tsx +118 -0
  472. package/templates/marketplace/client/LiveGrid.tsx +92 -0
  473. package/templates/marketplace/client/LiveListingStatus.tsx +32 -24
  474. package/templates/marketplace/client/LoginCard.tsx +114 -136
  475. package/templates/marketplace/client/MarketProvider.tsx +3 -5
  476. package/templates/marketplace/client/MyMarket.tsx +371 -185
  477. package/templates/marketplace/client/OfferPanel.tsx +421 -287
  478. package/templates/marketplace/client/SellForm.tsx +271 -192
  479. package/templates/marketplace/client/ThemeToggle.tsx +1 -0
  480. package/templates/marketplace/client/WatchButton.tsx +16 -16
  481. package/templates/marketplace/client/market.ts +61 -14
  482. package/templates/marketplace/functions/seedMarket.ts +143 -36
  483. package/templates/marketplace/functions/seedOffers.ts +101 -0
  484. package/templates/marketplace/lib/catalog.ts +72 -1
  485. package/templates/marketplace/public/images/listings/ceramic-lamp-640.webp +0 -0
  486. package/templates/marketplace/public/images/listings/ceramic-lamp.webp +0 -0
  487. package/templates/marketplace/public/images/listings/duffel-640.webp +0 -0
  488. package/templates/marketplace/public/images/listings/duffel.webp +0 -0
  489. package/templates/marketplace/public/images/listings/film-camera-640.webp +0 -0
  490. package/templates/marketplace/public/images/listings/film-camera.webp +0 -0
  491. package/templates/marketplace/public/images/listings/lounge-chair-640.webp +0 -0
  492. package/templates/marketplace/public/images/listings/lounge-chair.webp +0 -0
  493. package/templates/marketplace/public/images/listings/road-bike-640.webp +0 -0
  494. package/templates/marketplace/public/images/listings/road-bike.webp +0 -0
  495. package/templates/marketplace/public/images/listings/slipper-chair-640.webp +0 -0
  496. package/templates/marketplace/public/images/listings/slipper-chair.webp +0 -0
  497. package/templates/marketplace/public/images/listings/turntable-640.webp +0 -0
  498. package/templates/marketplace/public/images/listings/turntable.webp +0 -0
  499. package/templates/marketplace/public/images/listings/wardrobe-640.webp +0 -0
  500. package/templates/marketplace/public/images/listings/wardrobe.webp +0 -0
  501. package/templates/marketplace/public/images/listings/watch-640.webp +0 -0
  502. package/templates/marketplace/public/images/listings/watch.webp +0 -0
  503. package/templates/marketplace/public/images/listings/weekender-bag-640.webp +0 -0
  504. package/templates/marketplace/public/images/listings/weekender-bag.webp +0 -0
  505. package/templates/marketplace/tests/example.test.ts +64 -2
  506. package/templates/marketplace/tests/listing-card.test.tsx +61 -0
  507. package/templates/projects/README.md +24 -3
  508. package/templates/projects/app/apple-icon.png +0 -0
  509. package/templates/projects/app/clients/clients-view.tsx +20 -6
  510. package/templates/projects/app/clients/page.tsx +2 -1
  511. package/templates/projects/app/globals.css +29 -14
  512. package/templates/projects/app/icon.svg +1 -0
  513. package/templates/projects/app/layout.tsx +7 -5
  514. package/templates/projects/app/page.tsx +3 -5
  515. package/templates/projects/app/projects/[id]/page.tsx +2 -1
  516. package/templates/projects/app/projects/[id]/project-view.tsx +45 -30
  517. package/templates/projects/app/projects-view.tsx +105 -52
  518. package/templates/projects/app/workspace.tsx +41 -20
  519. package/templates/projects/app.ts +2 -2
  520. package/templates/projects/components/app-shell.tsx +94 -0
  521. package/templates/projects/components/auth-form.tsx +35 -5
  522. package/templates/projects/components/avatar.tsx +5 -1
  523. package/templates/projects/components/board-skeleton.tsx +3 -3
  524. package/templates/projects/components/brand-mark.tsx +25 -0
  525. package/templates/projects/components/command-palette.tsx +17 -17
  526. package/templates/projects/components/data-table.tsx +97 -36
  527. package/templates/projects/components/filter-tabs.tsx +101 -0
  528. package/templates/projects/components/meter.tsx +48 -0
  529. package/templates/projects/components/nav.tsx +14 -0
  530. package/templates/projects/components/page-header.tsx +43 -7
  531. package/templates/projects/components/require-auth.tsx +24 -26
  532. package/templates/projects/components/sidebar.tsx +54 -48
  533. package/templates/projects/components/summary-bar.tsx +49 -0
  534. package/templates/projects/components/task-board.tsx +10 -9
  535. package/templates/projects/components/time-dialog.tsx +29 -5
  536. package/templates/projects/components/ui/button.tsx +5 -4
  537. package/templates/projects/components/ui/dialog.tsx +1 -1
  538. package/templates/projects/components/ui/input.tsx +1 -1
  539. package/templates/projects/components/ui/select.tsx +1 -1
  540. package/templates/projects/components/ui/textarea.tsx +1 -1
  541. package/templates/projects/functions/seedWorkspace.ts +26 -12
  542. package/templates/projects/lib/brand.ts +9 -0
  543. package/templates/projects/lib/demo-team.ts +51 -0
  544. package/templates/projects/lib/search.ts +4 -3
  545. package/templates/projects/lib/seed.ts +35 -1
  546. package/templates/projects/lib/work.ts +63 -0
  547. package/templates/projects/public/favicon.ico +0 -0
  548. package/templates/projects/tests/demo-team.test.ts +18 -0
  549. package/templates/projects/tests/work.test.ts +65 -0
  550. package/templates/restaurant/.env.example +7 -0
  551. package/templates/restaurant/README.md +29 -3
  552. package/templates/restaurant/app/(marketing)/layout.tsx +4 -2
  553. package/templates/restaurant/app/(marketing)/page.tsx +47 -23
  554. package/templates/restaurant/app/(marketing)/reservation-widget.tsx +37 -7
  555. package/templates/restaurant/app/auth-form.tsx +8 -8
  556. package/templates/restaurant/app/dashboard/dashboard-client.tsx +241 -109
  557. package/templates/restaurant/app/dashboard/page.tsx +14 -14
  558. package/templates/restaurant/app/icon.svg +4 -0
  559. package/templates/restaurant/app/login/page.tsx +15 -15
  560. package/templates/restaurant/functions/seedDemo.ts +44 -0
  561. package/templates/restaurant/lib/demo.ts +144 -0
  562. package/templates/restaurant/lib/hours.ts +45 -0
  563. package/templates/restaurant/lib/site.config.ts +3 -3
  564. package/templates/restaurant/public/favicon.ico +0 -0
  565. package/templates/restaurant/tests/reservations.test.ts +67 -0
  566. package/templates/saas/README.md +54 -35
  567. package/templates/saas/app/(auth)/auth-form.tsx +25 -23
  568. package/templates/saas/app/(auth)/forgot-password/page.tsx +1 -1
  569. package/templates/saas/app/(auth)/layout.tsx +27 -33
  570. package/templates/saas/app/(auth)/login/page.tsx +1 -1
  571. package/templates/saas/app/(auth)/reset-password/page.tsx +1 -1
  572. package/templates/saas/app/(auth)/signup/page.tsx +1 -1
  573. package/templates/saas/app/(marketing)/layout.tsx +127 -253
  574. package/templates/saas/app/(marketing)/page.tsx +133 -372
  575. package/templates/saas/app/(marketing)/pricing/page.tsx +21 -19
  576. package/templates/saas/app/(marketing)/products/[slug]/page.tsx +40 -95
  577. package/templates/saas/app/dashboard/board-client.tsx +416 -0
  578. package/templates/saas/app/dashboard/dashboard-client.tsx +30 -615
  579. package/templates/saas/app/dashboard/layout.tsx +27 -13
  580. package/templates/saas/app/dashboard/overview-client.tsx +332 -0
  581. package/templates/saas/app/dashboard/page.tsx +29 -35
  582. package/templates/saas/app/dashboard/projects/[id]/page.tsx +29 -0
  583. package/templates/saas/app/dashboard/projects/page.tsx +11 -7
  584. package/templates/saas/app/dashboard/projects-client.tsx +398 -0
  585. package/templates/saas/app/globals.css +5 -4
  586. package/templates/saas/app/icon.svg +1 -0
  587. package/templates/saas/app/onboarding/onboarding-client.tsx +93 -26
  588. package/templates/saas/app/onboarding/page.tsx +6 -2
  589. package/templates/saas/app.ts +68 -13
  590. package/templates/saas/components/dashboard-shell.tsx +212 -132
  591. package/templates/saas/components/marketing.tsx +137 -274
  592. package/templates/saas/components/pricing-table.tsx +12 -11
  593. package/templates/saas/components/setup-checklist.tsx +31 -29
  594. package/templates/saas/components/task-ui.tsx +199 -0
  595. package/templates/saas/functions/createProject.ts +7 -13
  596. package/templates/saas/functions/createTask.ts +67 -0
  597. package/templates/saas/functions/deleteProject.ts +17 -0
  598. package/templates/saas/functions/deleteTask.ts +14 -0
  599. package/templates/saas/functions/seedWorkspace.ts +46 -0
  600. package/templates/saas/functions/setProjectStatus.ts +41 -0
  601. package/templates/saas/functions/updateProfile.ts +13 -0
  602. package/templates/saas/functions/updateProject.ts +4 -5
  603. package/templates/saas/functions/updateTask.ts +88 -0
  604. package/templates/saas/lib/members.ts +53 -0
  605. package/templates/saas/lib/names.ts +53 -0
  606. package/templates/saas/lib/plans.ts +11 -0
  607. package/templates/saas/lib/sample-workspace.ts +117 -0
  608. package/templates/saas/lib/site.config.ts +200 -463
  609. package/templates/saas/lib/tasks.ts +101 -0
  610. package/templates/saas/lib/use-live.ts +25 -0
  611. package/templates/saas/public/favicon.ico +0 -0
  612. package/templates/saas/public/screenshots/board.png +0 -0
  613. package/templates/saas/public/screenshots/members.png +0 -0
  614. package/templates/saas/public/screenshots/overview.png +0 -0
  615. package/templates/saas/public/screenshots/projects.png +0 -0
  616. package/templates/saas/scripts/capture-screenshots.mjs +156 -0
  617. package/templates/saas/tests/names.test.ts +35 -0
  618. package/templates/saas/tests/plans.test.ts +12 -0
  619. package/templates/saas/tests/sample-workspace.test.ts +46 -0
  620. package/templates/saas/tests/tasks.test.ts +81 -0
  621. package/templates/shop/README.md +25 -13
  622. package/templates/shop/app/(marketing)/layout.tsx +94 -67
  623. package/templates/shop/app/(marketing)/not-found.tsx +13 -12
  624. package/templates/shop/app/(marketing)/page.tsx +120 -59
  625. package/templates/shop/app/(marketing)/shop-client.tsx +364 -237
  626. package/templates/shop/app/(marketing)/success/page.tsx +75 -22
  627. package/templates/shop/app/globals.css +94 -1
  628. package/templates/shop/app/layout.tsx +4 -4
  629. package/templates/shop/app/login/page.tsx +4 -4
  630. package/templates/shop/app.ts +18 -6
  631. package/templates/shop/components/cart-button.tsx +33 -0
  632. package/templates/shop/components/marketing.tsx +17 -87
  633. package/templates/shop/components/shelf-count.tsx +36 -0
  634. package/templates/shop/functions/checkout.ts +17 -1
  635. package/templates/shop/lib/cart-store.ts +57 -0
  636. package/templates/shop/lib/shelf-store.ts +35 -0
  637. package/templates/shop/lib/shop.ts +27 -0
  638. package/templates/shop/lib/site.config.ts +108 -98
  639. package/templates/shop/public/images/hero.jpg +0 -0
  640. package/templates/shop/public/images/products/cedar-smoke.jpg +0 -0
  641. package/templates/shop/public/images/products/fig-leaf.jpg +0 -0
  642. package/templates/shop/public/images/products/linen.jpg +0 -0
  643. package/templates/shop/public/images/products/sea-salt.jpg +0 -0
  644. package/templates/shop/public/images/products/tobacco-oak.jpg +0 -0
  645. package/templates/shop/tests/shop.test.ts +64 -0
  646. package/templates/waitlist/.env.example +7 -0
  647. package/templates/waitlist/README.md +29 -13
  648. package/templates/waitlist/app/(marketing)/page.tsx +9 -7
  649. package/templates/waitlist/app/(marketing)/waitlist-hero.tsx +64 -31
  650. package/templates/waitlist/app/auth-form.tsx +8 -8
  651. package/templates/waitlist/app/dashboard/dashboard-client.tsx +132 -95
  652. package/templates/waitlist/app/dashboard/page.tsx +17 -16
  653. package/templates/waitlist/app/icon.svg +2 -0
  654. package/templates/waitlist/app/login/page.tsx +14 -14
  655. package/templates/waitlist/app.ts +6 -6
  656. package/templates/waitlist/functions/joinWaitlist.ts +6 -18
  657. package/templates/waitlist/functions/seedDemo.ts +32 -0
  658. package/templates/waitlist/lib/demo.ts +94 -0
  659. package/templates/waitlist/lib/launch.ts +19 -0
  660. package/templates/waitlist/lib/site.config.ts +14 -9
  661. package/templates/waitlist/lib/stats.ts +35 -0
  662. package/templates/waitlist/public/favicon.ico +0 -0
  663. package/templates/waitlist/tests/counter.test.ts +107 -0
  664. package/templates/waitlist/tests/live-count.test.tsx +48 -0
  665. package/templates/ai-chat/tests/example.test.ts +0 -12
  666. package/templates/backend/consumer/apps/api/functions/feed.ts +0 -57
  667. package/templates/backend/consumer/apps/api/functions/profilePosts.ts +0 -17
  668. package/templates/backend/consumer/apps/api/functions/toggleLike.ts +0 -48
  669. package/templates/backend/consumer/apps/api/schema.ts +0 -143
  670. package/templates/chat/functions/_keep.ts +0 -13
  671. package/templates/chat/tests/example.test.ts +0 -12
  672. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +0 -68
  673. package/templates/marketplace/client/LiveTicker.tsx +0 -116
  674. package/templates/marketplace/client/ScrollToListingsLink.tsx +0 -29
  675. package/templates/marketplace/public/images/marketplace-hero.webp +0 -0
@@ -1,38 +1,53 @@
1
1
  import React from "react";
2
2
  import { Link, type Metadata, type PageProps } from "@pylonsync/react";
3
3
  import { AuthForm } from "../auth-form";
4
+ import { BrandMark } from "@/components/chat/icons";
4
5
  import { siteConfig } from "@/lib/site.config";
6
+ import { brandIcon } from "@/lib/brand-icon";
7
+ import { safeNextPath } from "@/lib/chat";
5
8
 
6
9
  export const metadata: Metadata = {
7
- title: `Sign in — ${siteConfig.brand.name}`,
10
+ title: `Sign in to ${siteConfig.brand.name}`,
8
11
  robots: "noindex",
12
+ icons: { icon: brandIcon },
9
13
  };
10
14
 
11
- // `app/login/page.tsx` → `/login`. Sign-in is required to use the chat (chats
12
- // are tied to your account). It sits outside the `(chat)` route group, so it
13
- // renders bare — no app top bar. Already signed in? Skip back to the
14
- // chat — `response.redirect` in the synchronous shell render is a real 307
15
- // before any HTML is sent.
16
- export default function LoginPage({ auth, response }: PageProps) {
17
- // Already signed in (a real account, not an anonymous guest)? Skip the form.
18
- if (auth.user_id && !auth.user_id.startsWith("guest_")) response.redirect("/");
15
+ // `/login`: optional. Chat works without an account; signing in keeps the
16
+ // history on every device, and the guest's chats move to the account. A
17
+ // signed-in account is sent back to the chat.
18
+ export default function LoginPage({ auth, response, searchParams }: PageProps) {
19
+ const next = safeNextPath(typeof searchParams.next === "string" ? searchParams.next : null);
20
+ if (auth.user_id && !auth.is_guest && !auth.user_id.startsWith("guest_")) response.redirect(next);
19
21
  const { brand } = siteConfig;
22
+ const hasGuestSession = Boolean(auth.user_id);
23
+
20
24
  return (
21
- <div className="flex min-h-screen items-center justify-center bg-white px-6 py-12">
22
- <div className="w-full max-w-[400px] rounded-2xl border border-zinc-200/70 p-8">
23
- <Link href="/" className="inline-flex">
24
- <span className="flex size-9 items-center justify-center rounded-xl bg-zinc-900 text-base font-bold text-white">
25
- {brand.letter}
26
- </span>
27
- </Link>
28
- <h1 className="mt-5 text-[22px] font-semibold tracking-tight text-zinc-900">
29
- Sign in to {brand.name}
30
- </h1>
31
- <p className="mt-1 text-[13px] text-zinc-500">
32
- Create an account or sign in to start chatting.
33
- </p>
34
- <div className="mt-6">
35
- <AuthForm />
25
+ <div className="flex min-h-[100dvh] flex-col bg-paper px-4 py-6 sm:px-6">
26
+ <Link href={next} className="press inline-flex w-max items-center gap-2.5 rounded-xl px-1.5 py-1 hover:bg-hover">
27
+ <BrandMark size={26} />
28
+ <span className="text-[15.5px] font-semibold tracking-[-0.015em] text-ink">{brand.name}</span>
29
+ </Link>
30
+
31
+ <div className="flex flex-1 items-center justify-center py-10">
32
+ <div className="rise w-full max-w-[25rem]">
33
+ <h1 className="text-center font-serif text-[32px] leading-tight tracking-[-0.02em] text-ink">
34
+ Sign in to {brand.name}
35
+ </h1>
36
+ <p className="mx-auto mt-2 max-w-[21rem] text-center text-[14.5px] leading-relaxed text-ink-2">
37
+ {hasGuestSession
38
+ ? "Your chats from this browser move to your account."
39
+ : "Keep your chats on every device."}
40
+ </p>
41
+ <div className="bezel mt-8">
42
+ <div className="bezel-core p-5 sm:p-6">
43
+ <AuthForm next={next} />
44
+ </div>
45
+ </div>
46
+ <p className="mt-6 text-center text-[13px] text-ink-3">
47
+ <Link href={next} className="underline decoration-ink/20 underline-offset-4 hover:text-ink">
48
+ Continue without an account
49
+ </Link>
50
+ </p>
36
51
  </div>
37
52
  </div>
38
53
  </div>
@@ -3,28 +3,27 @@ import {
3
3
  field,
4
4
  policy,
5
5
  auth,
6
+ llm,
6
7
  buildManifest,
7
8
  discoverAppRoutes,
8
9
  discoverFunctions,
9
10
  font,
10
11
  } from "@pylonsync/sdk";
12
+ import { siteConfig } from "./lib/site.config";
11
13
 
12
14
  // ---------------------------------------------------------------------------
13
- // ai-chat — a streaming AI chat app. Tokens stream from the built-in
14
- // `POST /api/ai/stream` endpoint (your PYLON_AI_API_KEY never leaves the
15
- // server); the conversation itself is sync-backed, so your chats stay in sync
16
- // across tabs and devices in realtime.
15
+ // ai-chat: a streaming AI assistant. Every visitor gets a guest session and can
16
+ // chat right away; signing in moves the guest's conversations to the account.
17
17
  //
18
- // Two data entities (+ User):
19
- // • Conversation — a chat thread. Owner-scoped: you only ever see your own.
20
- // • Message — one turn (user or assistant). Owner-scoped too. Both use
21
- // `field.owner()` on userId, so the client can insert with a
22
- // plain optimistic `db.insert` and the id is stamped from the
23
- // session — unspoofable, no server function needed to write.
24
- // • User — the account (email/password is built in).
25
- //
26
- // There's no single "owner"/admin — it's multi-user: every signed-in (or guest)
27
- // visitor gets their own private chats.
18
+ // Conversation a chat thread. Owner-scoped.
19
+ // Message one turn. Assistant replies are written by the `respond`
20
+ // function, which streams tokens from `ctx.llm.stream` and
21
+ // records the resumable stream id on the row, so any other tab
22
+ // can attach to the same reply while it generates.
23
+ // ReplyLog one row per generation, for limits and the busy check.
24
+ // GuestClaim a one-time code that lets a guest's history move to the
25
+ // account they sign in to. Never readable by clients.
26
+ // User the account (email/password is built in).
28
27
  // ---------------------------------------------------------------------------
29
28
 
30
29
  const Conversation = entity(
@@ -32,12 +31,15 @@ const Conversation = entity(
32
31
  {
33
32
  userId: field.string().owner(),
34
33
  title: field.string().default("New chat"),
34
+ // Set on the seeded examples shown when no provider key is configured.
35
+ example: field.boolean().default(false),
35
36
  createdAt: field.datetime().defaultNow(),
37
+ updatedAt: field.datetime().defaultNow(),
36
38
  },
37
39
  {
38
40
  indexes: [
39
41
  { name: "by_user", fields: ["userId"], unique: false },
40
- { name: "by_created", fields: ["createdAt"], unique: false },
42
+ { name: "by_updated", fields: ["updatedAt"], unique: false },
41
43
  ],
42
44
  },
43
45
  );
@@ -49,6 +51,12 @@ const Message = entity(
49
51
  userId: field.string().owner(),
50
52
  role: field.string(), // "user" | "assistant"
51
53
  content: field.string(),
54
+ // "streaming" | "done" | "stopped" | "error"; see lib/chat.ts.
55
+ status: field.string().default("done"),
56
+ errorCode: field.string().optional(),
57
+ // Resumable stream id of the reply while it generates.
58
+ streamId: field.string().optional(),
59
+ model: field.string().optional(),
52
60
  createdAt: field.datetime().defaultNow(),
53
61
  },
54
62
  {
@@ -59,6 +67,39 @@ const Message = entity(
59
67
  },
60
68
  );
61
69
 
70
+ // One row per generation, never deleted by regenerate or conversation
71
+ // delete. The hourly limits count these rows, and `finishedAt` stays null
72
+ // while the provider call runs, so one user cannot run two generations at
73
+ // once. Never readable by clients.
74
+ const ReplyLog = entity(
75
+ "ReplyLog",
76
+ {
77
+ userId: field.string(),
78
+ messageId: field.string(),
79
+ isGuest: field.boolean(),
80
+ model: field.string(),
81
+ createdAt: field.datetime(),
82
+ finishedAt: field.datetime().optional(),
83
+ },
84
+ {
85
+ indexes: [
86
+ { name: "by_user", fields: ["userId"], unique: false },
87
+ { name: "by_created", fields: ["createdAt"], unique: false },
88
+ { name: "by_message", fields: ["messageId"], unique: false },
89
+ ],
90
+ },
91
+ );
92
+
93
+ const GuestClaim = entity(
94
+ "GuestClaim",
95
+ {
96
+ guestId: field.string(),
97
+ codeHash: field.string().serverOnly(),
98
+ expiresAt: field.datetime(),
99
+ },
100
+ { indexes: [{ name: "by_code", fields: ["codeHash"], unique: true }] },
101
+ );
102
+
62
103
  const User = entity(
63
104
  "User",
64
105
  {
@@ -72,26 +113,45 @@ const User = entity(
72
113
  { indexes: [{ name: "by_email", fields: ["email"], unique: true }] },
73
114
  );
74
115
 
75
- // Conversations + messages are PRIVATE: a signed-in (or guest) user can only
76
- // read, create, and modify their OWN rows. `field.owner()` stamps userId from
77
- // the session on insert, so "create your own" is enforced at write time and
78
- // reads are scoped by the same id.
116
+ // Clients read their own rows. Every write goes through a server function:
117
+ // `respond` writes messages, `createConversation`, `renameConversation`, and
118
+ // `deleteConversation` manage threads, and `stopResponse` ends a reply. That
119
+ // keeps a client from writing assistant turns, moving a message to another
120
+ // thread, or setting fields such as `example`.
79
121
  const conversationPolicy = policy({
80
122
  name: "conversation_owner",
81
123
  entity: "Conversation",
82
- allowRead: "auth.userId == data.userId",
83
- allowInsert: "auth.userId != null",
84
- allowUpdate: "auth.userId == data.userId",
85
- allowDelete: "auth.userId == data.userId",
124
+ allowRead: "auth.userId != null && auth.userId == data.userId",
125
+ allowInsert: "false",
126
+ allowUpdate: "false",
127
+ allowDelete: "false",
86
128
  });
87
129
 
88
130
  const messagePolicy = policy({
89
131
  name: "message_owner",
90
132
  entity: "Message",
91
- allowRead: "auth.userId == data.userId",
92
- allowInsert: "auth.userId != null",
93
- allowUpdate: "auth.userId == data.userId",
94
- allowDelete: "auth.userId == data.userId",
133
+ allowRead: "auth.userId != null && auth.userId == data.userId",
134
+ allowInsert: "false",
135
+ allowUpdate: "false",
136
+ allowDelete: "false",
137
+ });
138
+
139
+ const replyLogPolicy = policy({
140
+ name: "reply_log_server_only",
141
+ entity: "ReplyLog",
142
+ allowRead: "false",
143
+ allowInsert: "false",
144
+ allowUpdate: "false",
145
+ allowDelete: "false",
146
+ });
147
+
148
+ const guestClaimPolicy = policy({
149
+ name: "guest_claim_server_only",
150
+ entity: "GuestClaim",
151
+ allowRead: "false",
152
+ allowInsert: "false",
153
+ allowUpdate: "false",
154
+ allowDelete: "false",
95
155
  });
96
156
 
97
157
  const userPolicy = policy({
@@ -103,41 +163,56 @@ const userPolicy = policy({
103
163
  allowDelete: "false",
104
164
  });
105
165
 
106
- // Every non-internal function in functions/ becomes a manifest entry —
107
- // this is what makes them show up in /api/manifest, the OpenAPI spec,
108
- // and `pylon codegen`.
109
166
  const fns = await discoverFunctions();
110
167
 
111
168
  const manifest = buildManifest({
112
169
  name: "__APP_NAME__",
113
170
  version: "0.1.0",
114
- entities: [Conversation, Message, User],
115
- // No custom functions needed: messages are written with optimistic db.insert
116
- // (owner-stamped), and streaming uses the built-in POST /api/ai/stream.
171
+ entities: [Conversation, Message, ReplyLog, GuestClaim, User],
117
172
  queries: fns.queries,
118
173
  actions: fns.actions,
119
- policies: [conversationPolicy, messagePolicy, userPolicy],
174
+ policies: [conversationPolicy, messagePolicy, replyLogPolicy, guestClaimPolicy, userPolicy],
120
175
  auth: auth(),
121
- // Self-hosted Inter (next/font parity): the build fetches the woff2, serves it
122
- // same-origin (no third-party request, no FOUT), preloads it, and synthesizes a
123
- // size-adjusted fallback face so there's no layout shift. globals.css reads it
124
- // via `var(--font-sans, …)`; layout.tsx carries no font <link>.
176
+ // The model list in lib/site.config.ts is the allowlist: ctx.llm refuses any
177
+ // other model id.
178
+ llm: llm({
179
+ defaultModel: siteConfig.chat.defaultModel,
180
+ allowedModels: siteConfig.chat.models.map((m) => m.id),
181
+ }),
182
+ // Self-hosted fonts: the build downloads the woff2 files, serves them from
183
+ // this origin, and adds size-adjusted fallbacks so text does not shift when
184
+ // they load. globals.css maps them to Tailwind's font-sans, font-serif, and font-mono.
125
185
  fonts: [
126
186
  font({
127
- family: "Inter",
128
- variable: "--font-sans",
129
- weights: ["400", "500", "600", "700"],
187
+ family: "Geist",
188
+ variable: "--font-ui",
189
+ weights: ["400", "500", "600"],
190
+ subsets: ["latin"],
191
+ display: "swap",
192
+ preload: true,
193
+ }),
194
+ font({
195
+ family: "Source Serif 4",
196
+ variable: "--font-reading",
197
+ weights: ["400", "600"],
130
198
  subsets: ["latin"],
131
199
  display: "swap",
132
200
  preload: true,
133
201
  }),
202
+ font({
203
+ family: "Geist Mono",
204
+ variable: "--font-code",
205
+ weights: ["400", "500"],
206
+ subsets: ["latin"],
207
+ display: "swap",
208
+ }),
134
209
  ],
135
210
  routes: await discoverAppRoutes(),
136
211
  });
137
212
 
138
213
  // Not a debug leftover: the CLI runs `bun run app.ts` and parses stdout as
139
- // your manifest (see crates/cli/src/bun.rs). Deleting this line breaks every
140
- // pylon command that reads your schema.
214
+ // your manifest. Deleting this line breaks every pylon command that reads your
215
+ // schema.
141
216
  console.log(JSON.stringify(manifest, null, 2));
142
217
 
143
218
  export default manifest;
@@ -0,0 +1,193 @@
1
+ "use client";
2
+
3
+ import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
4
+ import { siteConfig } from "@/lib/site.config";
5
+ import { findModel, modelAllowedFor } from "@/lib/chat";
6
+ import { ArrowUpIcon, CheckIcon, ChevronDownIcon, StopIcon } from "./icons";
7
+
8
+ const useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
9
+
10
+ export interface ComposerProps {
11
+ value: string;
12
+ onChange: (value: string) => void;
13
+ onSubmit: () => void;
14
+ onStop: () => void;
15
+ // A reply is generating: the send button becomes Stop.
16
+ busy: boolean;
17
+ placeholder: string;
18
+ model: string;
19
+ onModelChange: (id: string) => void;
20
+ // Guests see the models outside `guestModels` as needing an account.
21
+ isGuest?: boolean;
22
+ autoFocus?: boolean;
23
+ textareaRef?: React.RefObject<HTMLTextAreaElement | null>;
24
+ }
25
+
26
+ // The message box. Enter sends, Shift+Enter adds a line, Escape stops a reply.
27
+ export function Composer({
28
+ value,
29
+ onChange,
30
+ onSubmit,
31
+ onStop,
32
+ busy,
33
+ placeholder,
34
+ model,
35
+ onModelChange,
36
+ isGuest = false,
37
+ autoFocus,
38
+ textareaRef,
39
+ }: ComposerProps) {
40
+ const ownRef = useRef<HTMLTextAreaElement>(null);
41
+ const ref = textareaRef ?? ownRef;
42
+ const canSend = value.trim().length > 0 && !busy;
43
+ const tooLong = value.length > siteConfig.chat.maxInputChars;
44
+
45
+ useIsoLayoutEffect(() => {
46
+ const el = ref.current;
47
+ if (!el) return;
48
+ el.style.height = "0px";
49
+ el.style.height = `${Math.min(el.scrollHeight, 240)}px`;
50
+ }, [value, ref]);
51
+
52
+ function onKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {
53
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
54
+ e.preventDefault();
55
+ if (canSend && !tooLong) onSubmit();
56
+ } else if (e.key === "Escape" && busy) {
57
+ e.preventDefault();
58
+ onStop();
59
+ }
60
+ }
61
+
62
+ return (
63
+ <form
64
+ className="bezel composer-shell"
65
+ onSubmit={(e) => {
66
+ e.preventDefault();
67
+ if (canSend && !tooLong) onSubmit();
68
+ }}
69
+ >
70
+ <div className="bezel-core flex flex-col px-3 pb-2.5 pt-3 transition-shadow duration-300">
71
+ <textarea
72
+ ref={ref}
73
+ value={value}
74
+ onChange={(e) => onChange(e.target.value)}
75
+ onKeyDown={onKeyDown}
76
+ rows={1}
77
+ autoFocus={autoFocus}
78
+ placeholder={placeholder}
79
+ aria-label="Message"
80
+ className="max-h-60 min-h-[28px] w-full resize-none bg-transparent px-1.5 text-[15.5px] leading-[1.6] text-ink outline-none placeholder:text-ink-3"
81
+ />
82
+ <div className="mt-2 flex items-center justify-between gap-2">
83
+ <ModelPicker value={model} onChange={onModelChange} isGuest={isGuest} />
84
+ <div className="flex items-center gap-3">
85
+ {tooLong ? (
86
+ <span className="text-[12px] text-warn">
87
+ {value.length.toLocaleString()} / {siteConfig.chat.maxInputChars.toLocaleString()}
88
+ </span>
89
+ ) : null}
90
+ {busy ? (
91
+ <button
92
+ type="button"
93
+ onClick={onStop}
94
+ aria-label="Stop"
95
+ title="Stop (Esc)"
96
+ className="press flex size-9 items-center justify-center rounded-full bg-ink text-bg transition-transform duration-200"
97
+ >
98
+ <StopIcon size={11} />
99
+ </button>
100
+ ) : (
101
+ <button
102
+ type="submit"
103
+ disabled={!canSend || tooLong}
104
+ aria-label="Send"
105
+ title="Send (Enter)"
106
+ className="press flex size-9 items-center justify-center rounded-full bg-brand text-white transition-[opacity,transform] duration-200 disabled:bg-ink/15 disabled:text-ink-3"
107
+ >
108
+ <ArrowUpIcon size={18} />
109
+ </button>
110
+ )}
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </form>
115
+ );
116
+ }
117
+
118
+ function ModelPicker({
119
+ value,
120
+ onChange,
121
+ isGuest,
122
+ }: {
123
+ value: string;
124
+ onChange: (id: string) => void;
125
+ isGuest: boolean;
126
+ }) {
127
+ const [open, setOpen] = useState(false);
128
+ const rootRef = useRef<HTMLDivElement>(null);
129
+ const current = findModel(value) ?? siteConfig.chat.models[0];
130
+
131
+ useEffect(() => {
132
+ if (!open) return;
133
+ function onDown(e: MouseEvent) {
134
+ if (!rootRef.current?.contains(e.target as Node)) setOpen(false);
135
+ }
136
+ function onKey(e: KeyboardEvent) {
137
+ if (e.key === "Escape") setOpen(false);
138
+ }
139
+ document.addEventListener("mousedown", onDown);
140
+ document.addEventListener("keydown", onKey);
141
+ return () => {
142
+ document.removeEventListener("mousedown", onDown);
143
+ document.removeEventListener("keydown", onKey);
144
+ };
145
+ }, [open]);
146
+
147
+ return (
148
+ <div ref={rootRef} className="relative">
149
+ <button
150
+ type="button"
151
+ onClick={() => setOpen((o) => !o)}
152
+ aria-haspopup="listbox"
153
+ aria-expanded={open}
154
+ className="press inline-flex h-8 items-center gap-1 rounded-lg px-2 text-[13px] font-medium text-ink-2 transition-colors duration-200 hover:bg-hover hover:text-ink"
155
+ >
156
+ {current.label}
157
+ <ChevronDownIcon size={14} className={"transition-transform duration-300 " + (open ? "rotate-180" : "")} />
158
+ </button>
159
+ {open ? (
160
+ <div
161
+ role="listbox"
162
+ aria-label="Model"
163
+ className="menu-in absolute bottom-10 left-0 z-30 w-[17rem] rounded-2xl border border-line bg-surface p-1.5 shadow-float"
164
+ >
165
+ {siteConfig.chat.models.map((m) => {
166
+ const locked = !modelAllowedFor(m.id, isGuest);
167
+ return (
168
+ <button
169
+ key={m.id}
170
+ type="button"
171
+ role="option"
172
+ aria-selected={m.id === current.id}
173
+ aria-disabled={locked}
174
+ disabled={locked}
175
+ onClick={() => {
176
+ onChange(m.id);
177
+ setOpen(false);
178
+ }}
179
+ className="flex w-full items-center gap-3 rounded-xl px-3 py-2 text-left transition-colors duration-150 enabled:hover:bg-hover disabled:opacity-55"
180
+ >
181
+ <span className="min-w-0 flex-1">
182
+ <span className="block text-[13.5px] font-medium text-ink">{m.label}</span>
183
+ <span className="block text-[12.5px] text-ink-3">{locked ? "Sign in to use this model" : m.note}</span>
184
+ </span>
185
+ {m.id === current.id ? <CheckIcon size={16} className="text-brand" /> : null}
186
+ </button>
187
+ );
188
+ })}
189
+ </div>
190
+ ) : null}
191
+ </div>
192
+ );
193
+ }
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { siteConfig } from "@/lib/site.config";
5
+ import { ProviderSetup } from "./setup";
6
+ import { BookIcon } from "./icons";
7
+
8
+ export interface EmptyStateProps {
9
+ // The composer, placed under the headline.
10
+ composer: React.ReactNode;
11
+ onPick: (prompt: string) => void;
12
+ providerConfigured: boolean | null;
13
+ exampleCount: number;
14
+ onOpenExample?: () => void;
15
+ }
16
+
17
+ // The new-chat screen: headline, composer, starter prompts, and the setup
18
+ // steps while no provider key is configured. Below the md breakpoint the
19
+ // composer is docked at the bottom of the screen instead.
20
+ export function EmptyState({ composer, onPick, providerConfigured, exampleCount, onOpenExample }: EmptyStateProps) {
21
+ const { chat } = siteConfig;
22
+ return (
23
+ <div className="mx-auto flex w-full max-w-[46rem] flex-col px-4 pb-10 pt-[max(10vh,3rem)] sm:px-6 md:pt-[16vh]">
24
+ <h1 className="rise text-center font-serif text-[30px] font-normal leading-tight tracking-[-0.02em] text-ink sm:text-[38px]">
25
+ {chat.emptyHeadline}
26
+ </h1>
27
+
28
+ <div className="rise mt-9 hidden [animation-delay:60ms] md:block">{composer}</div>
29
+
30
+ <div className="rise mt-4 grid grid-cols-2 gap-2 [animation-delay:120ms] max-md:mt-8">
31
+ {chat.suggestions.map((s) => (
32
+ <button
33
+ key={s.title}
34
+ type="button"
35
+ onClick={() => onPick(s.prompt)}
36
+ className="press group/s rounded-2xl px-4 py-3 text-left ring-1 ring-line transition-[background-color,box-shadow] duration-300 hover:bg-surface hover:shadow-soft"
37
+ >
38
+ <span className="block text-[14px] font-medium text-ink">{s.title}</span>
39
+ <span className="mt-0.5 line-clamp-2 text-[13px] text-ink-3 sm:line-clamp-1 transition-colors duration-300 group-hover/s:text-ink-2">
40
+ {s.prompt}
41
+ </span>
42
+ </button>
43
+ ))}
44
+ </div>
45
+
46
+ {providerConfigured === false ? (
47
+ <div className="rise mt-8 space-y-3 [animation-delay:180ms]">
48
+ <ProviderSetup />
49
+ {exampleCount > 0 && onOpenExample ? (
50
+ <button
51
+ type="button"
52
+ onClick={onOpenExample}
53
+ className="press flex w-full items-center gap-3 rounded-2xl px-4 py-3 text-left ring-1 ring-line transition-colors duration-300 hover:bg-surface"
54
+ >
55
+ <BookIcon size={18} className="shrink-0 text-ink-3" />
56
+ <span className="flex-1 text-[13.5px] text-ink-2">
57
+ Open an example conversation to see how replies are laid out.
58
+ </span>
59
+ </button>
60
+ ) : null}
61
+ </div>
62
+ ) : null}
63
+ </div>
64
+ );
65
+ }