@useatlas/create 0.0.2 → 0.0.4

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 (296) hide show
  1. package/README.md +4 -18
  2. package/index.ts +191 -31
  3. package/package.json +1 -1
  4. package/templates/docker/.env.example +3 -3
  5. package/templates/docker/Dockerfile.sidecar +28 -0
  6. package/templates/docker/bin/__tests__/plugin-cli.test.ts +9 -9
  7. package/templates/docker/bin/atlas.ts +108 -44
  8. package/templates/docker/data/demo-semantic/catalog.yml +51 -27
  9. package/templates/docker/data/demo-semantic/entities/accounts.yml +95 -103
  10. package/templates/docker/data/demo-semantic/entities/companies.yml +88 -152
  11. package/templates/docker/data/demo-semantic/entities/people.yml +82 -95
  12. package/templates/docker/data/demo-semantic/glossary.yml +104 -8
  13. package/templates/docker/data/demo-semantic/metrics/accounts.yml +62 -23
  14. package/templates/docker/data/demo-semantic/metrics/companies.yml +52 -78
  15. package/templates/docker/docker-compose.yml +1 -1
  16. package/templates/docker/docs/deploy.md +2 -39
  17. package/templates/docker/package.json +17 -1
  18. package/templates/docker/semantic/catalog.yml +62 -3
  19. package/templates/docker/semantic/entities/accounts.yml +162 -0
  20. package/templates/docker/semantic/entities/companies.yml +143 -0
  21. package/templates/docker/semantic/entities/people.yml +132 -0
  22. package/templates/docker/semantic/glossary.yml +116 -4
  23. package/templates/docker/semantic/metrics/accounts.yml +77 -0
  24. package/templates/docker/semantic/metrics/companies.yml +63 -0
  25. package/templates/docker/sidecar/Dockerfile +5 -6
  26. package/templates/docker/sidecar/railway.json +1 -2
  27. package/templates/docker/src/api/__tests__/admin.test.ts +7 -7
  28. package/templates/docker/src/api/__tests__/health-plugin.test.ts +7 -0
  29. package/templates/docker/src/api/__tests__/health.test.ts +30 -8
  30. package/templates/docker/src/api/routes/admin.ts +549 -8
  31. package/templates/docker/src/api/routes/chat.ts +5 -20
  32. package/templates/docker/src/api/routes/health.ts +39 -27
  33. package/templates/docker/src/api/routes/openapi.ts +1329 -74
  34. package/templates/docker/src/api/routes/query.ts +2 -1
  35. package/templates/docker/src/api/server.ts +27 -0
  36. package/templates/docker/src/app/api/[...route]/route.ts +2 -2
  37. package/templates/docker/src/app/globals.css +13 -12
  38. package/templates/docker/src/app/layout.tsx +9 -2
  39. package/templates/docker/src/components/ui/alert-dialog.tsx +196 -0
  40. package/templates/docker/src/components/ui/badge.tsx +48 -0
  41. package/templates/docker/src/components/ui/button.tsx +64 -0
  42. package/templates/docker/src/components/ui/card.tsx +92 -0
  43. package/templates/docker/src/components/ui/collapsible.tsx +33 -0
  44. package/templates/docker/src/components/ui/command.tsx +184 -0
  45. package/templates/docker/src/components/ui/dialog.tsx +158 -0
  46. package/templates/docker/src/components/ui/dropdown-menu.tsx +257 -0
  47. package/templates/docker/src/components/ui/input.tsx +21 -0
  48. package/templates/docker/src/components/ui/scroll-area.tsx +58 -0
  49. package/templates/docker/src/components/ui/select.tsx +190 -0
  50. package/templates/docker/src/components/ui/separator.tsx +28 -0
  51. package/templates/docker/src/components/ui/sheet.tsx +143 -0
  52. package/templates/docker/src/components/ui/sidebar.tsx +726 -0
  53. package/templates/docker/src/components/ui/skeleton.tsx +13 -0
  54. package/templates/docker/src/components/ui/table.tsx +116 -0
  55. package/templates/docker/src/components/ui/tabs.tsx +91 -0
  56. package/templates/docker/src/components/ui/toggle-group.tsx +83 -0
  57. package/templates/docker/src/components/ui/toggle.tsx +47 -0
  58. package/templates/docker/src/components/ui/tooltip.tsx +57 -0
  59. package/templates/docker/src/hooks/use-mobile.ts +19 -0
  60. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +2 -0
  61. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +17 -0
  62. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
  63. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +2 -0
  64. package/templates/docker/src/lib/__tests__/config.test.ts +69 -19
  65. package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
  66. package/templates/docker/src/lib/__tests__/providers.test.ts +32 -1
  67. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +9 -0
  68. package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +429 -0
  69. package/templates/docker/src/lib/__tests__/startup.test.ts +5 -0
  70. package/templates/docker/src/lib/agent-query.ts +5 -23
  71. package/templates/docker/src/lib/agent.ts +32 -112
  72. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +5 -3
  73. package/templates/docker/src/lib/auth/middleware.ts +30 -4
  74. package/templates/docker/src/lib/auth/migrate.ts +97 -0
  75. package/templates/docker/src/lib/auth/server.ts +12 -1
  76. package/templates/docker/src/lib/config.ts +37 -39
  77. package/templates/docker/src/lib/db/__tests__/connection.test.ts +89 -14
  78. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +1 -18
  79. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
  80. package/templates/docker/src/lib/db/__tests__/registry.test.ts +11 -208
  81. package/templates/docker/src/lib/db/connection.ts +87 -265
  82. package/templates/docker/src/lib/db/internal.ts +6 -1
  83. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
  84. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +2 -2
  85. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +355 -1
  86. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +32 -5
  87. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +228 -14
  88. package/templates/docker/src/lib/plugins/index.ts +4 -1
  89. package/templates/docker/src/lib/plugins/migrate.ts +103 -0
  90. package/templates/docker/src/lib/plugins/registry.ts +12 -6
  91. package/templates/docker/src/lib/plugins/wiring.ts +113 -4
  92. package/templates/docker/src/lib/providers.ts +6 -1
  93. package/templates/docker/src/lib/security.ts +24 -0
  94. package/templates/docker/src/lib/semantic.ts +2 -0
  95. package/templates/docker/src/lib/sidecar-types.ts +12 -1
  96. package/templates/docker/src/lib/startup.ts +71 -101
  97. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
  98. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
  99. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
  100. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  101. package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  102. package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  103. package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  104. package/templates/docker/src/lib/tools/__tests__/python.test.ts +331 -0
  105. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  106. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +38 -31
  107. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  108. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  109. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  110. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +5 -308
  111. package/templates/docker/src/lib/tools/explore-nsjail.ts +17 -12
  112. package/templates/docker/src/lib/tools/explore-sidecar.ts +25 -0
  113. package/templates/docker/src/lib/tools/explore.ts +28 -32
  114. package/templates/docker/src/lib/tools/python-nsjail.ts +396 -0
  115. package/templates/docker/src/lib/tools/python-sandbox.ts +476 -0
  116. package/templates/docker/src/lib/tools/python-sidecar.ts +150 -0
  117. package/templates/docker/src/lib/tools/python.ts +367 -0
  118. package/templates/docker/src/lib/tools/registry.ts +49 -22
  119. package/templates/docker/src/lib/tools/sql.ts +88 -88
  120. package/templates/docker/src/types/vercel-sandbox.d.ts +7 -0
  121. package/templates/docker/src/ui/components/admin/admin-layout.tsx +77 -8
  122. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +25 -17
  123. package/templates/docker/src/ui/components/admin/change-password-dialog.tsx +128 -0
  124. package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -3
  125. package/templates/docker/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  126. package/templates/docker/src/ui/components/atlas-chat.tsx +64 -12
  127. package/templates/docker/src/ui/components/chart/result-chart.tsx +25 -15
  128. package/templates/docker/src/ui/components/chat/markdown.tsx +88 -42
  129. package/templates/docker/src/ui/components/chat/python-result-card.tsx +244 -0
  130. package/templates/docker/src/ui/components/chat/sql-block.tsx +39 -15
  131. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +6 -1
  132. package/templates/docker/src/ui/components/chat/tool-part.tsx +12 -3
  133. package/templates/docker/src/ui/components/chat/typing-indicator.tsx +5 -2
  134. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +25 -20
  135. package/templates/docker/src/ui/context.tsx +1 -1
  136. package/templates/docker/src/ui/hooks/use-conversations.ts +3 -3
  137. package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -10
  138. package/templates/docker/tsconfig.json +2 -2
  139. package/templates/nextjs-standalone/.env.example +1 -1
  140. package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +9 -9
  141. package/templates/nextjs-standalone/bin/atlas.ts +108 -44
  142. package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +51 -27
  143. package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +95 -103
  144. package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +88 -152
  145. package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +82 -95
  146. package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +104 -8
  147. package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +62 -23
  148. package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +52 -78
  149. package/templates/nextjs-standalone/docs/deploy.md +2 -39
  150. package/templates/nextjs-standalone/package.json +11 -2
  151. package/templates/nextjs-standalone/scripts/migrate-auth.ts +25 -0
  152. package/templates/nextjs-standalone/scripts/seed-demo.ts +94 -0
  153. package/templates/nextjs-standalone/semantic/catalog.yml +62 -3
  154. package/templates/nextjs-standalone/semantic/entities/accounts.yml +162 -0
  155. package/templates/nextjs-standalone/semantic/entities/companies.yml +143 -0
  156. package/templates/nextjs-standalone/semantic/entities/people.yml +132 -0
  157. package/templates/nextjs-standalone/semantic/glossary.yml +116 -4
  158. package/templates/nextjs-standalone/semantic/metrics/accounts.yml +77 -0
  159. package/templates/nextjs-standalone/semantic/metrics/companies.yml +63 -0
  160. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +7 -7
  161. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +7 -0
  162. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +30 -8
  163. package/templates/nextjs-standalone/src/api/routes/admin.ts +549 -8
  164. package/templates/nextjs-standalone/src/api/routes/chat.ts +5 -20
  165. package/templates/nextjs-standalone/src/api/routes/health.ts +39 -27
  166. package/templates/nextjs-standalone/src/api/routes/openapi.ts +1329 -74
  167. package/templates/nextjs-standalone/src/api/routes/query.ts +2 -1
  168. package/templates/nextjs-standalone/src/api/server.ts +27 -0
  169. package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +2 -2
  170. package/templates/nextjs-standalone/src/app/globals.css +13 -12
  171. package/templates/nextjs-standalone/src/app/layout.tsx +9 -2
  172. package/templates/nextjs-standalone/src/components/ui/alert-dialog.tsx +196 -0
  173. package/templates/nextjs-standalone/src/components/ui/badge.tsx +48 -0
  174. package/templates/nextjs-standalone/src/components/ui/button.tsx +64 -0
  175. package/templates/nextjs-standalone/src/components/ui/card.tsx +92 -0
  176. package/templates/nextjs-standalone/src/components/ui/collapsible.tsx +33 -0
  177. package/templates/nextjs-standalone/src/components/ui/command.tsx +184 -0
  178. package/templates/nextjs-standalone/src/components/ui/dialog.tsx +158 -0
  179. package/templates/nextjs-standalone/src/components/ui/dropdown-menu.tsx +257 -0
  180. package/templates/nextjs-standalone/src/components/ui/input.tsx +21 -0
  181. package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +58 -0
  182. package/templates/nextjs-standalone/src/components/ui/select.tsx +190 -0
  183. package/templates/nextjs-standalone/src/components/ui/separator.tsx +28 -0
  184. package/templates/nextjs-standalone/src/components/ui/sheet.tsx +143 -0
  185. package/templates/nextjs-standalone/src/components/ui/sidebar.tsx +726 -0
  186. package/templates/nextjs-standalone/src/components/ui/skeleton.tsx +13 -0
  187. package/templates/nextjs-standalone/src/components/ui/table.tsx +116 -0
  188. package/templates/nextjs-standalone/src/components/ui/tabs.tsx +91 -0
  189. package/templates/nextjs-standalone/src/components/ui/toggle-group.tsx +83 -0
  190. package/templates/nextjs-standalone/src/components/ui/toggle.tsx +47 -0
  191. package/templates/nextjs-standalone/src/components/ui/tooltip.tsx +57 -0
  192. package/templates/nextjs-standalone/src/hooks/use-mobile.ts +19 -0
  193. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +2 -0
  194. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +17 -0
  195. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
  196. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +2 -0
  197. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +69 -19
  198. package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
  199. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +32 -1
  200. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +9 -0
  201. package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +429 -0
  202. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +5 -0
  203. package/templates/nextjs-standalone/src/lib/agent-query.ts +5 -23
  204. package/templates/nextjs-standalone/src/lib/agent.ts +32 -112
  205. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +5 -3
  206. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +30 -4
  207. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +97 -0
  208. package/templates/nextjs-standalone/src/lib/auth/server.ts +12 -1
  209. package/templates/nextjs-standalone/src/lib/config.ts +37 -39
  210. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +89 -14
  211. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +1 -18
  212. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
  213. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +11 -208
  214. package/templates/nextjs-standalone/src/lib/db/connection.ts +87 -265
  215. package/templates/nextjs-standalone/src/lib/db/internal.ts +6 -1
  216. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
  217. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +2 -2
  218. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +355 -1
  219. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +32 -5
  220. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +228 -14
  221. package/templates/nextjs-standalone/src/lib/plugins/index.ts +4 -1
  222. package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +103 -0
  223. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +12 -6
  224. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +113 -4
  225. package/templates/nextjs-standalone/src/lib/providers.ts +6 -1
  226. package/templates/nextjs-standalone/src/lib/security.ts +24 -0
  227. package/templates/nextjs-standalone/src/lib/semantic.ts +2 -0
  228. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +12 -1
  229. package/templates/nextjs-standalone/src/lib/startup.ts +71 -101
  230. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
  231. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
  232. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
  233. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  234. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  235. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  236. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  237. package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +331 -0
  238. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  239. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +38 -31
  240. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  241. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  242. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  243. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +5 -308
  244. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +17 -12
  245. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +25 -0
  246. package/templates/nextjs-standalone/src/lib/tools/explore.ts +28 -32
  247. package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +396 -0
  248. package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +476 -0
  249. package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +150 -0
  250. package/templates/nextjs-standalone/src/lib/tools/python.ts +367 -0
  251. package/templates/nextjs-standalone/src/lib/tools/registry.ts +49 -22
  252. package/templates/nextjs-standalone/src/lib/tools/sql.ts +88 -88
  253. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +77 -8
  254. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +25 -17
  255. package/templates/nextjs-standalone/src/ui/components/admin/change-password-dialog.tsx +128 -0
  256. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -3
  257. package/templates/nextjs-standalone/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  258. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +64 -12
  259. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +25 -15
  260. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +88 -42
  261. package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +244 -0
  262. package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +39 -15
  263. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +6 -1
  264. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +12 -3
  265. package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +5 -2
  266. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +25 -20
  267. package/templates/nextjs-standalone/src/ui/context.tsx +1 -1
  268. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +3 -3
  269. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -10
  270. package/templates/nextjs-standalone/tsconfig.json +0 -1
  271. package/templates/nextjs-standalone/vercel.json +4 -1
  272. package/templates/docker/render.yaml +0 -34
  273. package/templates/docker/semantic/entities/.gitkeep +0 -0
  274. package/templates/docker/semantic/metrics/.gitkeep +0 -0
  275. package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +0 -141
  276. package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +0 -339
  277. package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +0 -217
  278. package/templates/docker/src/lib/db/duckdb.ts +0 -122
  279. package/templates/docker/src/lib/db/salesforce.ts +0 -342
  280. package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  281. package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  282. package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  283. package/templates/docker/src/lib/tools/salesforce.ts +0 -138
  284. package/templates/docker/src/lib/tools/soql-validation.ts +0 -172
  285. package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
  286. package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
  287. package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +0 -141
  288. package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +0 -339
  289. package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +0 -217
  290. package/templates/nextjs-standalone/src/lib/db/duckdb.ts +0 -122
  291. package/templates/nextjs-standalone/src/lib/db/salesforce.ts +0 -342
  292. package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  293. package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  294. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  295. package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +0 -138
  296. package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +0 -172
@@ -0,0 +1,244 @@
1
+ "use client";
2
+
3
+ import { Component, type ReactNode, type ErrorInfo, useContext, useState } from "react";
4
+ import { getToolArgs, getToolResult, isToolComplete } from "../../lib/helpers";
5
+ import { DarkModeContext } from "../../hooks/use-dark-mode";
6
+ import dynamic from "next/dynamic";
7
+ import { LoadingCard } from "./loading-card";
8
+ import { DataTable } from "./data-table";
9
+ import type { ChartDetectionResult } from "../chart/chart-detection";
10
+
11
+ const ResultChart = dynamic(
12
+ () => import("../chart/result-chart").then((m) => ({ default: m.ResultChart })),
13
+ { ssr: false, loading: () => <div className="h-64 animate-pulse rounded-lg bg-zinc-100 dark:bg-zinc-800" /> },
14
+ );
15
+
16
+ interface RechartsChartConfig {
17
+ type: "line" | "bar" | "pie";
18
+ data: Record<string, unknown>[];
19
+ categoryKey: string;
20
+ valueKeys: string[];
21
+ }
22
+
23
+ interface PythonChart {
24
+ base64: string;
25
+ mimeType: "image/png";
26
+ }
27
+
28
+ const ALLOWED_IMAGE_MIME = new Set(["image/png", "image/jpeg"]);
29
+
30
+ /* ------------------------------------------------------------------ */
31
+ /* Error boundary */
32
+ /* ------------------------------------------------------------------ */
33
+
34
+ class PythonErrorBoundary extends Component<
35
+ { children: ReactNode },
36
+ { hasError: boolean; error?: Error }
37
+ > {
38
+ constructor(props: { children: ReactNode }) {
39
+ super(props);
40
+ this.state = { hasError: false };
41
+ }
42
+
43
+ static getDerivedStateFromError(error: Error) {
44
+ return { hasError: true, error };
45
+ }
46
+
47
+ componentDidCatch(error: Error, info: ErrorInfo) {
48
+ console.error("PythonResultCard rendering failed:", error, info.componentStack);
49
+ }
50
+
51
+ render() {
52
+ if (this.state.hasError) {
53
+ return (
54
+ <div className="my-2 rounded-lg border border-red-300 bg-red-50 px-3 py-2 text-xs text-red-700 dark:border-red-900/50 dark:bg-red-950/20 dark:text-red-400">
55
+ Python result could not be rendered: {this.state.error?.message ?? "unknown error"}
56
+ </div>
57
+ );
58
+ }
59
+ return this.props.children;
60
+ }
61
+ }
62
+
63
+ /* ------------------------------------------------------------------ */
64
+ /* Main component */
65
+ /* ------------------------------------------------------------------ */
66
+
67
+ export function PythonResultCard({ part }: { part: unknown }) {
68
+ return (
69
+ <PythonErrorBoundary>
70
+ <PythonResultCardInner part={part} />
71
+ </PythonErrorBoundary>
72
+ );
73
+ }
74
+
75
+ function PythonResultCardInner({ part }: { part: unknown }) {
76
+ const dark = useContext(DarkModeContext);
77
+ const args = getToolArgs(part);
78
+ const raw = getToolResult(part);
79
+ const done = isToolComplete(part);
80
+ const [open, setOpen] = useState(true);
81
+
82
+ if (!done) return <LoadingCard label="Running Python..." />;
83
+
84
+ // Structural validation — result must be a non-null object
85
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
86
+ return (
87
+ <div className="my-2 rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400">
88
+ Python executed but returned an unexpected result format.
89
+ </div>
90
+ );
91
+ }
92
+
93
+ const result = raw as Record<string, unknown>;
94
+
95
+ if (!result.success) {
96
+ return (
97
+ <div className="my-2 overflow-hidden rounded-lg border border-red-300 bg-red-50 dark:border-red-900/50 dark:bg-red-950/20">
98
+ <div className="px-3 py-2 text-xs font-medium text-red-700 dark:text-red-400">
99
+ Python execution failed
100
+ </div>
101
+ <pre className="border-t border-red-200 px-3 py-2 text-xs whitespace-pre-wrap text-red-600 dark:border-red-900/50 dark:text-red-300">
102
+ {String(result.error ?? "Unknown error")}
103
+ </pre>
104
+ {!!result.output && (
105
+ <pre className="border-t border-red-200 px-3 py-2 text-xs whitespace-pre-wrap text-red-500 dark:border-red-900/50 dark:text-red-400">
106
+ {String(result.output)}
107
+ </pre>
108
+ )}
109
+ </div>
110
+ );
111
+ }
112
+
113
+ const output = result.output ? String(result.output) : null;
114
+ const table = result.table as { columns: string[]; rows: unknown[][] } | undefined;
115
+ const charts = Array.isArray(result.charts) ? (result.charts as PythonChart[]) : undefined;
116
+ const rechartsCharts = Array.isArray(result.rechartsCharts) ? (result.rechartsCharts as RechartsChartConfig[]) : undefined;
117
+
118
+ const hasTable = table && Array.isArray(table.columns) && Array.isArray(table.rows)
119
+ && table.columns.length > 0 && table.rows.length > 0;
120
+ const hasCharts = charts && charts.length > 0;
121
+ const hasRechartsCharts = rechartsCharts && rechartsCharts.length > 0;
122
+
123
+ // Filter charts to only safe image MIME types
124
+ const safeCharts = hasCharts
125
+ ? charts.filter((c) => c.base64 && ALLOWED_IMAGE_MIME.has(c.mimeType))
126
+ : [];
127
+
128
+ return (
129
+ <div className="my-2 overflow-hidden rounded-lg border border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
130
+ <button
131
+ onClick={() => setOpen(!open)}
132
+ className="flex w-full items-center gap-2 px-3 py-2 text-left text-xs transition-colors hover:bg-zinc-100/60 dark:hover:bg-zinc-800/60"
133
+ >
134
+ <span className="rounded bg-emerald-100 px-1.5 py-0.5 font-medium text-emerald-700 dark:bg-emerald-600/20 dark:text-emerald-400">
135
+ Python
136
+ </span>
137
+ <span className="flex-1 truncate text-zinc-500 dark:text-zinc-400">
138
+ {String(args.explanation ?? "Python result")}
139
+ </span>
140
+ <span className="text-zinc-400 dark:text-zinc-600">{open ? "\u25BE" : "\u25B8"}</span>
141
+ </button>
142
+
143
+ {open && (
144
+ <div className="space-y-2 border-t border-zinc-100 px-3 py-2 dark:border-zinc-800">
145
+ {output && (
146
+ <pre className="rounded-md bg-zinc-100 px-3 py-2 text-xs whitespace-pre-wrap text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
147
+ {output}
148
+ </pre>
149
+ )}
150
+
151
+ {hasTable && <DataTable columns={table.columns} rows={table.rows} />}
152
+
153
+ {hasRechartsCharts &&
154
+ rechartsCharts.map((chart, i) => (
155
+ <RechartsChartSection key={i} chart={chart} dark={dark} />
156
+ ))}
157
+
158
+ {safeCharts.length > 0 &&
159
+ safeCharts.map((chart, i) => (
160
+ <ChartImage key={i} chart={chart} index={i} />
161
+ ))}
162
+ </div>
163
+ )}
164
+ </div>
165
+ );
166
+ }
167
+
168
+ /* ------------------------------------------------------------------ */
169
+ /* Chart image with error handling */
170
+ /* ------------------------------------------------------------------ */
171
+
172
+ function ChartImage({ chart, index }: { chart: PythonChart; index: number }) {
173
+ const [failed, setFailed] = useState(false);
174
+
175
+ if (failed) {
176
+ return (
177
+ <div className="rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400">
178
+ Chart {index + 1} failed to render.
179
+ </div>
180
+ );
181
+ }
182
+
183
+ return (
184
+ // eslint-disable-next-line @next/next/no-img-element -- base64 data URL, cannot use next/image optimization
185
+ <img
186
+ src={`data:${chart.mimeType};base64,${chart.base64}`}
187
+ alt={`Python chart ${index + 1}`}
188
+ className="max-w-full rounded-lg border border-zinc-200 dark:border-zinc-700"
189
+ onError={() => setFailed(true)}
190
+ />
191
+ );
192
+ }
193
+
194
+ /* ------------------------------------------------------------------ */
195
+ /* Recharts section — bypasses auto-detection with synthetic result */
196
+ /* ------------------------------------------------------------------ */
197
+
198
+ function RechartsChartSection({ chart, dark }: { chart: RechartsChartConfig; dark: boolean }) {
199
+ if (!chart.categoryKey || !Array.isArray(chart.valueKeys) || !Array.isArray(chart.data)) {
200
+ return (
201
+ <div className="rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400">
202
+ Chart data is incomplete or malformed.
203
+ </div>
204
+ );
205
+ }
206
+
207
+ const headers = [chart.categoryKey, ...chart.valueKeys];
208
+ const rows: string[][] = chart.data.map((row) =>
209
+ headers.map((key) => (row[key] == null ? "" : String(row[key]))),
210
+ );
211
+
212
+ // Build a synthetic detection result so ResultChart uses the backend's
213
+ // chart config directly, bypassing auto-detection that might reject it.
214
+ const detectionResult: ChartDetectionResult = {
215
+ chartable: true,
216
+ columns: headers.map((h, i) => ({
217
+ header: h,
218
+ type: i === 0 ? "categorical" as const : "numeric" as const,
219
+ index: i,
220
+ uniqueCount: i === 0 ? chart.data.length : 0,
221
+ })),
222
+ recommendations: [{
223
+ type: chart.type,
224
+ categoryColumn: { header: chart.categoryKey, type: "categorical" as const, index: 0, uniqueCount: chart.data.length },
225
+ valueColumns: chart.valueKeys.map((k, i) => ({
226
+ header: k,
227
+ type: "numeric" as const,
228
+ index: i + 1,
229
+ uniqueCount: 0,
230
+ })) as [{ header: string; type: "numeric"; index: number; uniqueCount: number }, ...{ header: string; type: "numeric"; index: number; uniqueCount: number }[]],
231
+ reason: "Python-generated chart",
232
+ }],
233
+ data: chart.data.map((row) => {
234
+ const out: Record<string, string | number> = {};
235
+ for (const key of headers) {
236
+ const val = row[key];
237
+ out[key] = typeof val === "number" ? val : String(val ?? "");
238
+ }
239
+ return out;
240
+ }),
241
+ };
242
+
243
+ return <ResultChart headers={headers} rows={rows} dark={dark} detectionResult={detectionResult} />;
244
+ }
@@ -1,27 +1,51 @@
1
1
  "use client";
2
2
 
3
- import { useContext } from "react";
4
- import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
5
- import { oneDark, oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
3
+ import { useContext, useState, useEffect } from "react";
6
4
  import { DarkModeContext } from "../../hooks/use-dark-mode";
7
5
  import { CopyButton } from "./copy-button";
8
6
 
7
+ type SyntaxHighlighterModule = typeof import("react-syntax-highlighter");
8
+ type StyleModule = typeof import("react-syntax-highlighter/dist/esm/styles/prism");
9
+
10
+ let _cache: { Prism: SyntaxHighlighterModule["Prism"]; oneDark: StyleModule["oneDark"]; oneLight: StyleModule["oneLight"] } | null = null;
11
+
12
+ const SQL_BLOCK_STYLE = {
13
+ margin: 0,
14
+ borderRadius: "0.5rem",
15
+ fontSize: "0.75rem",
16
+ padding: "0.75rem 1rem",
17
+ } as const;
18
+
9
19
  export function SQLBlock({ sql }: { sql: string }) {
10
20
  const dark = useContext(DarkModeContext);
21
+ const [mod, setMod] = useState(_cache);
22
+
23
+ useEffect(() => {
24
+ if (_cache) return;
25
+ Promise.all([
26
+ import("react-syntax-highlighter"),
27
+ import("react-syntax-highlighter/dist/esm/styles/prism"),
28
+ ]).then(([sh, styles]) => {
29
+ _cache = { Prism: sh.Prism, oneDark: styles.oneDark, oneLight: styles.oneLight };
30
+ setMod(_cache);
31
+ });
32
+ }, []);
33
+
11
34
  return (
12
35
  <div className="relative">
13
- <SyntaxHighlighter
14
- language="sql"
15
- style={dark ? oneDark : oneLight}
16
- customStyle={{
17
- margin: 0,
18
- borderRadius: "0.5rem",
19
- fontSize: "0.75rem",
20
- padding: "0.75rem 1rem",
21
- }}
22
- >
23
- {sql}
24
- </SyntaxHighlighter>
36
+ {mod ? (
37
+ <mod.Prism
38
+ language="sql"
39
+ style={dark ? mod.oneDark : mod.oneLight}
40
+ customStyle={SQL_BLOCK_STYLE}
41
+ >
42
+ {sql}
43
+ </mod.Prism>
44
+ ) : (
45
+ <pre className="overflow-x-auto rounded-lg bg-zinc-100 p-3 text-xs dark:bg-zinc-800">
46
+ <code>{sql}</code>
47
+ </pre>
48
+ )}
25
49
  <div className="absolute right-2 top-2">
26
50
  <CopyButton text={sql} label="Copy SQL" />
27
51
  </div>
@@ -4,7 +4,12 @@ import { useContext, useMemo, useState } from "react";
4
4
  import { getToolArgs, getToolResult, isToolComplete, downloadCSV, toCsvString } from "../../lib/helpers";
5
5
  import { DarkModeContext } from "../../hooks/use-dark-mode";
6
6
  import { detectCharts } from "../chart/chart-detection";
7
- import { ResultChart } from "../chart/result-chart";
7
+ import dynamic from "next/dynamic";
8
+
9
+ const ResultChart = dynamic(
10
+ () => import("../chart/result-chart").then((m) => ({ default: m.ResultChart })),
11
+ { ssr: false, loading: () => <div className="h-64 animate-pulse rounded-lg bg-zinc-100 dark:bg-zinc-800" /> },
12
+ );
8
13
  import { LoadingCard } from "./loading-card";
9
14
  import { DataTable } from "./data-table";
10
15
  import { SQLBlock } from "./sql-block";
@@ -1,13 +1,15 @@
1
1
  "use client";
2
2
 
3
+ import { memo } from "react";
3
4
  import { getToolName } from "ai";
4
- import { getToolResult } from "../../lib/helpers";
5
+ import { getToolResult, isToolComplete } from "../../lib/helpers";
5
6
  import { isActionToolResult } from "../../lib/action-types";
6
7
  import { ExploreCard } from "./explore-card";
7
8
  import { SQLResultCard } from "./sql-result-card";
8
9
  import { ActionApprovalCard } from "../actions/action-approval-card";
10
+ import { PythonResultCard } from "./python-result-card";
9
11
 
10
- export function ToolPart({ part }: { part: unknown }) {
12
+ export const ToolPart = memo(function ToolPart({ part }: { part: unknown }) {
11
13
  let name: string;
12
14
  try {
13
15
  name = getToolName(part as Parameters<typeof getToolName>[0]);
@@ -25,6 +27,8 @@ export function ToolPart({ part }: { part: unknown }) {
25
27
  return <ExploreCard part={part} />;
26
28
  case "executeSQL":
27
29
  return <SQLResultCard part={part} />;
30
+ case "executePython":
31
+ return <PythonResultCard part={part} />;
28
32
  default: {
29
33
  const result = getToolResult(part);
30
34
  if (isActionToolResult(result)) {
@@ -37,4 +41,9 @@ export function ToolPart({ part }: { part: unknown }) {
37
41
  );
38
42
  }
39
43
  }
40
- }
44
+ }, (prev, next) => {
45
+ // Once a tool part is complete, its output won't change — skip re-renders.
46
+ // This prevents the Recharts render tree from contributing to React's update depth limit.
47
+ if (isToolComplete(prev.part) && isToolComplete(next.part)) return true;
48
+ return false;
49
+ });
@@ -1,5 +1,8 @@
1
1
  "use client";
2
2
 
3
+ const DELAY_1 = { animationDelay: "150ms" } as const;
4
+ const DELAY_2 = { animationDelay: "300ms" } as const;
5
+
3
6
  export function TypingIndicator() {
4
7
  return (
5
8
  <div className="flex justify-start">
@@ -7,11 +10,11 @@ export function TypingIndicator() {
7
10
  <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-zinc-400 dark:bg-zinc-500" />
8
11
  <span
9
12
  className="h-1.5 w-1.5 animate-bounce rounded-full bg-zinc-400 dark:bg-zinc-500"
10
- style={{ animationDelay: "150ms" }}
13
+ style={DELAY_1}
11
14
  />
12
15
  <span
13
16
  className="h-1.5 w-1.5 animate-bounce rounded-full bg-zinc-400 dark:bg-zinc-500"
14
- style={{ animationDelay: "300ms" }}
17
+ style={DELAY_2}
15
18
  />
16
19
  </div>
17
20
  </div>
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { useState } from "react";
4
+ import { Star, Trash2 } from "lucide-react";
5
+ import { Button } from "@/components/ui/button";
4
6
  import type { Conversation } from "../../lib/types";
5
7
  import { DeleteConfirmation } from "./delete-confirmation";
6
8
 
@@ -52,7 +54,6 @@ export function ConversationItem({
52
54
  if (success) {
53
55
  setConfirmDelete(false);
54
56
  }
55
- // On failure, keep confirmation dialog open so user sees something is wrong
56
57
  }}
57
58
  />
58
59
  </div>
@@ -60,9 +61,17 @@ export function ConversationItem({
60
61
  }
61
62
 
62
63
  return (
63
- <button
64
+ <div
65
+ role="button"
66
+ tabIndex={0}
64
67
  onClick={onSelect}
65
- className={`group flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm transition-colors ${
68
+ onKeyDown={(e) => {
69
+ if (e.key === "Enter" || e.key === " ") {
70
+ e.preventDefault();
71
+ onSelect();
72
+ }
73
+ }}
74
+ className={`group flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-left text-sm transition-colors ${
66
75
  isActive
67
76
  ? "bg-blue-50 text-blue-700 dark:bg-blue-600/10 dark:text-blue-400"
68
77
  : "text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"
@@ -77,7 +86,9 @@ export function ConversationItem({
77
86
  </p>
78
87
  </div>
79
88
  <div className="flex shrink-0 items-center gap-0.5">
80
- <button
89
+ <Button
90
+ variant="ghost"
91
+ size="icon"
81
92
  onClick={async (e) => {
82
93
  e.stopPropagation();
83
94
  if (starPending) return;
@@ -86,35 +97,29 @@ export function ConversationItem({
86
97
  setStarPending(false);
87
98
  }}
88
99
  disabled={starPending}
89
- className={`rounded p-1 transition-all ${
100
+ className={`h-6 w-6 transition-all ${
90
101
  conversation.starred
91
102
  ? "text-amber-400 opacity-100 hover:text-amber-500 dark:text-amber-400 dark:hover:text-amber-300"
92
103
  : "text-zinc-400 opacity-0 hover:text-amber-400 group-hover:opacity-100 dark:hover:text-amber-400"
93
104
  } ${starPending ? "opacity-50" : ""}`}
94
105
  aria-label={conversation.starred ? "Unstar conversation" : "Star conversation"}
95
106
  >
96
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" className="h-3.5 w-3.5">
97
- {conversation.starred ? (
98
- <path fillRule="evenodd" d="M8 1.75a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.75.75 0 0 1-1.088.791L8 13.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 7.874a.75.75 0 0 1 .416-1.28l4.21-.611L7.327 2.17A.75.75 0 0 1 8 1.75Z" clipRule="evenodd" />
99
- ) : (
100
- <path fillRule="evenodd" d="M8 1.75a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.75.75 0 0 1-1.088.791L8 13.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 7.874a.75.75 0 0 1 .416-1.28l4.21-.611L7.327 2.17A.75.75 0 0 1 8 1.75Zm0 2.445L6.615 7.05a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 4.196Z" clipRule="evenodd" />
101
- )}
102
- </svg>
103
- </button>
104
- <button
107
+ <Star className="h-3.5 w-3.5" fill={conversation.starred ? "currentColor" : "none"} />
108
+ </Button>
109
+ <Button
110
+ variant="ghost"
111
+ size="icon"
105
112
  onClick={(e) => {
106
113
  e.stopPropagation();
107
114
  setConfirmDelete(true);
108
115
  }}
109
116
  disabled={deleting}
110
- className="shrink-0 rounded p-1 text-zinc-400 opacity-0 transition-all hover:bg-red-50 hover:text-red-500 group-hover:opacity-100 dark:hover:bg-red-950/20 dark:hover:text-red-400"
117
+ className="h-6 w-6 shrink-0 text-zinc-400 opacity-0 transition-all hover:bg-red-50 hover:text-red-500 group-hover:opacity-100 dark:hover:bg-red-950/20 dark:hover:text-red-400"
111
118
  aria-label="Delete conversation"
112
119
  >
113
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" className="h-3.5 w-3.5">
114
- <path fillRule="evenodd" d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.286a1.5 1.5 0 0 0 1.492-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5A.75.75 0 0 1 9.95 6Z" clipRule="evenodd" />
115
- </svg>
116
- </button>
120
+ <Trash2 className="h-3.5 w-3.5" />
121
+ </Button>
117
122
  </div>
118
- </button>
123
+ </div>
119
124
  );
120
125
  }
@@ -14,7 +14,7 @@ export interface AtlasAuthClient {
14
14
  email: (opts: { email: string; password: string; name: string }) => Promise<{ error?: { message?: string } | null }>;
15
15
  };
16
16
  signOut: () => Promise<unknown>;
17
- useSession: () => { data?: { user?: { email?: string } } | null };
17
+ useSession: () => { data?: { user?: { email?: string } } | null; isPending?: boolean };
18
18
  }
19
19
 
20
20
  export interface AtlasUIConfig {
@@ -64,12 +64,12 @@ export function useConversations(opts: UseConversationsOptions): UseConversation
64
64
  }
65
65
 
66
66
  if (!res.ok) {
67
- const body = await res.json().catch(() => null);
68
- if (body?.code === "not_available") {
67
+ const errorBody = await res.json().catch(() => null);
68
+ if (errorBody?.code === "not_available") {
69
69
  setAvailable(false);
70
70
  return;
71
71
  }
72
- console.warn(`fetchList: HTTP ${res.status}`, body);
72
+ console.warn(`fetchList: HTTP ${res.status}`, errorBody);
73
73
  return;
74
74
  }
75
75
 
@@ -1,17 +1,24 @@
1
1
  "use client";
2
2
 
3
- import { createContext, useState, useEffect } from "react";
3
+ import { createContext, useSyncExternalStore } from "react";
4
4
 
5
5
  export const DarkModeContext = createContext(false);
6
6
 
7
+ /** SSR-safe snapshot: reads current prefers-color-scheme without hydration flicker. */
8
+ function subscribeToColorScheme(onChange: () => void) {
9
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
10
+ mq.addEventListener("change", onChange);
11
+ return () => mq.removeEventListener("change", onChange);
12
+ }
13
+
14
+ function getSnapshot() {
15
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
16
+ }
17
+
18
+ function getServerSnapshot() {
19
+ return false;
20
+ }
21
+
7
22
  export function useDarkMode() {
8
- const [dark, setDark] = useState(false);
9
- useEffect(() => {
10
- const mq = window.matchMedia("(prefers-color-scheme: dark)");
11
- setDark(mq.matches);
12
- const handler = (e: MediaQueryListEvent) => setDark(e.matches);
13
- mq.addEventListener("change", handler);
14
- return () => mq.removeEventListener("change", handler);
15
- }, []);
16
- return dark;
23
+ return useSyncExternalStore(subscribeToColorScheme, getSnapshot, getServerSnapshot);
17
24
  }
@@ -30,8 +30,8 @@
30
30
  },
31
31
  "include": [
32
32
  "next-env.d.ts",
33
- "**/*.ts",
34
- "**/*.tsx",
33
+ "src/**/*.ts",
34
+ "src/**/*.tsx",
35
35
  ".next/types/**/*.ts",
36
36
  ".next/dev/types/**/*.ts"
37
37
  ],
@@ -50,7 +50,7 @@
50
50
  # ATLAS_QUERY_TIMEOUT=30000 # Default: 30s in milliseconds
51
51
 
52
52
  # === Production Deployment ===
53
- # Required for production (Railway, Render, etc.):
53
+ # Required for production (Railway, etc.):
54
54
  # ATLAS_PROVIDER + its API key (e.g. ANTHROPIC_API_KEY)
55
55
  # ATLAS_DATASOURCE_URL=postgresql://user:pass@host:5432/dbname
56
56
  #
@@ -19,7 +19,7 @@ describe("pluginTemplate", () => {
19
19
  const result = pluginTemplate("my-source", "datasource");
20
20
  expect(result).toContain('import { definePlugin } from "@useatlas/plugin-sdk"');
21
21
  expect(result).toContain('id: "my-source"');
22
- expect(result).toContain('type: "datasource"');
22
+ expect(result).toContain('types: ["datasource"]');
23
23
  expect(result).toContain("satisfies AtlasDatasourcePlugin");
24
24
  expect(result).toContain("connection:");
25
25
  expect(result).toContain("dbType:");
@@ -28,7 +28,7 @@ describe("pluginTemplate", () => {
28
28
  test("generates context template with contextProvider", () => {
29
29
  const result = pluginTemplate("my-ctx", "context");
30
30
  expect(result).toContain('id: "my-ctx"');
31
- expect(result).toContain('type: "context"');
31
+ expect(result).toContain('types: ["context"]');
32
32
  expect(result).toContain("contextProvider:");
33
33
  expect(result).toContain("satisfies AtlasContextPlugin");
34
34
  });
@@ -36,7 +36,7 @@ describe("pluginTemplate", () => {
36
36
  test("generates interaction template with routes", () => {
37
37
  const result = pluginTemplate("my-interaction", "interaction");
38
38
  expect(result).toContain('id: "my-interaction"');
39
- expect(result).toContain('type: "interaction"');
39
+ expect(result).toContain('types: ["interaction"]');
40
40
  expect(result).toContain("routes(app:");
41
41
  expect(result).toContain("satisfies AtlasInteractionPlugin");
42
42
  });
@@ -44,7 +44,7 @@ describe("pluginTemplate", () => {
44
44
  test("generates action template with actions array", () => {
45
45
  const result = pluginTemplate("my-action", "action");
46
46
  expect(result).toContain('id: "my-action"');
47
- expect(result).toContain('type: "action"');
47
+ expect(result).toContain('types: ["action"]');
48
48
  expect(result).toContain("actions:");
49
49
  expect(result).toContain("satisfies AtlasActionPlugin");
50
50
  expect(result).toContain('import { tool } from "ai"');
@@ -60,7 +60,7 @@ describe("pluginTestTemplate", () => {
60
60
  test("generates test with correct plugin id assertion", () => {
61
61
  const result = pluginTestTemplate("test-plugin", "datasource");
62
62
  expect(result).toContain('expect(plugin.id).toBe("test-plugin")');
63
- expect(result).toContain('expect(plugin.type).toBe("datasource")');
63
+ expect(result).toContain('expect(plugin.types).toContain("datasource")');
64
64
  expect(result).toContain("healthCheck");
65
65
  });
66
66
  });
@@ -125,7 +125,7 @@ describe("handlePluginCreate", () => {
125
125
  expect(fs.existsSync(path.join(pluginDir, "tsconfig.json"))).toBe(true);
126
126
 
127
127
  const indexContent = fs.readFileSync(path.join(pluginDir, "src", "index.ts"), "utf-8");
128
- expect(indexContent).toContain('type: "datasource"');
128
+ expect(indexContent).toContain('types: ["datasource"]');
129
129
  expect(indexContent).toContain('id: "test-ds"');
130
130
 
131
131
  const pkgJson = JSON.parse(fs.readFileSync(path.join(pluginDir, "package.json"), "utf-8"));
@@ -139,7 +139,7 @@ describe("handlePluginCreate", () => {
139
139
  expect(fs.existsSync(path.join(pluginDir, "src", "index.ts"))).toBe(true);
140
140
 
141
141
  const indexContent = fs.readFileSync(path.join(pluginDir, "src", "index.ts"), "utf-8");
142
- expect(indexContent).toContain('type: "action"');
142
+ expect(indexContent).toContain('types: ["action"]');
143
143
  expect(indexContent).toContain("actions:");
144
144
  });
145
145
 
@@ -148,7 +148,7 @@ describe("handlePluginCreate", () => {
148
148
 
149
149
  const pluginDir = path.join(tmpDir, "plugins", "my-ctx");
150
150
  const indexContent = fs.readFileSync(path.join(pluginDir, "src", "index.ts"), "utf-8");
151
- expect(indexContent).toContain('type: "context"');
151
+ expect(indexContent).toContain('types: ["context"]');
152
152
  expect(indexContent).toContain("contextProvider:");
153
153
  });
154
154
 
@@ -157,7 +157,7 @@ describe("handlePluginCreate", () => {
157
157
 
158
158
  const pluginDir = path.join(tmpDir, "plugins", "my-web");
159
159
  const indexContent = fs.readFileSync(path.join(pluginDir, "src", "index.ts"), "utf-8");
160
- expect(indexContent).toContain('type: "interaction"');
160
+ expect(indexContent).toContain('types: ["interaction"]');
161
161
  expect(indexContent).toContain("routes(app:");
162
162
  });
163
163