@useatlas/create 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,159 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { ChevronRight, File, Folder } from "lucide-react";
5
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
6
+ import { ScrollArea } from "@/components/ui/scroll-area";
7
+ import { cn } from "@/lib/utils";
8
+
9
+ export type SemanticSelection =
10
+ | { type: "catalog" }
11
+ | { type: "glossary" }
12
+ | { type: "entity"; name: string }
13
+ | { type: "metrics"; file?: string }
14
+ | null;
15
+
16
+ interface SemanticFileTreeProps {
17
+ entityNames: string[];
18
+ metricFileNames: string[];
19
+ hasCatalog: boolean;
20
+ hasGlossary: boolean;
21
+ selection: SemanticSelection;
22
+ onSelect: (selection: SemanticSelection) => void;
23
+ className?: string;
24
+ }
25
+
26
+ function isSelected(selection: SemanticSelection, target: SemanticSelection): boolean {
27
+ if (!selection || !target) return false;
28
+ if (selection.type !== target.type) return false;
29
+ if (selection.type === "entity" && target.type === "entity") return selection.name === target.name;
30
+ if (selection.type === "metrics" && target.type === "metrics") return selection.file === target.file;
31
+ return true;
32
+ }
33
+
34
+ function FileItem({
35
+ name,
36
+ selected,
37
+ onClick,
38
+ indent = 0,
39
+ }: {
40
+ name: string;
41
+ selected: boolean;
42
+ onClick: () => void;
43
+ indent?: number;
44
+ }) {
45
+ return (
46
+ <button
47
+ onClick={onClick}
48
+ className={cn(
49
+ "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors",
50
+ selected ? "bg-accent text-accent-foreground" : "hover:bg-muted text-muted-foreground hover:text-foreground",
51
+ )}
52
+ style={{ paddingLeft: `${8 + indent * 16}px` }}
53
+ >
54
+ <File className="size-4 shrink-0 opacity-60" />
55
+ <span className="truncate">{name}</span>
56
+ </button>
57
+ );
58
+ }
59
+
60
+ function FolderSection({
61
+ name,
62
+ children,
63
+ defaultOpen = true,
64
+ indent = 0,
65
+ }: {
66
+ name: string;
67
+ children: React.ReactNode;
68
+ defaultOpen?: boolean;
69
+ indent?: number;
70
+ }) {
71
+ const [open, setOpen] = useState(defaultOpen);
72
+
73
+ return (
74
+ <Collapsible open={open} onOpenChange={setOpen}>
75
+ <CollapsibleTrigger
76
+ className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm font-medium hover:bg-muted transition-colors"
77
+ style={{ paddingLeft: `${8 + indent * 16}px` }}
78
+ >
79
+ <ChevronRight
80
+ className={cn("size-3.5 shrink-0 transition-transform", open && "rotate-90")}
81
+ />
82
+ <Folder className="size-4 shrink-0 opacity-60" />
83
+ <span className="truncate">{name}</span>
84
+ </CollapsibleTrigger>
85
+ <CollapsibleContent>{children}</CollapsibleContent>
86
+ </Collapsible>
87
+ );
88
+ }
89
+
90
+ export function SemanticFileTree({
91
+ entityNames,
92
+ metricFileNames,
93
+ hasCatalog,
94
+ hasGlossary,
95
+ selection,
96
+ onSelect,
97
+ className,
98
+ }: SemanticFileTreeProps) {
99
+ return (
100
+ <div className={cn("flex flex-col", className)}>
101
+ <div className="border-b px-4 py-3">
102
+ <div className="flex items-center gap-2">
103
+ <Folder className="size-4 text-muted-foreground" />
104
+ <span className="text-sm font-semibold">semantic/</span>
105
+ </div>
106
+ </div>
107
+ <ScrollArea className="flex-1">
108
+ <div className="space-y-0.5 p-2">
109
+ {hasCatalog && (
110
+ <FileItem
111
+ name="catalog.yml"
112
+ selected={isSelected(selection, { type: "catalog" })}
113
+ onClick={() => onSelect({ type: "catalog" })}
114
+ />
115
+ )}
116
+ {hasGlossary && (
117
+ <FileItem
118
+ name="glossary.yml"
119
+ selected={isSelected(selection, { type: "glossary" })}
120
+ onClick={() => onSelect({ type: "glossary" })}
121
+ />
122
+ )}
123
+
124
+ <FolderSection name="entities">
125
+ {entityNames.length === 0 ? (
126
+ <p className="py-2 text-xs text-muted-foreground" style={{ paddingLeft: "40px" }}>
127
+ No entities
128
+ </p>
129
+ ) : (
130
+ entityNames.map((name) => (
131
+ <FileItem
132
+ key={name}
133
+ name={`${name}.yml`}
134
+ selected={isSelected(selection, { type: "entity", name })}
135
+ onClick={() => onSelect({ type: "entity", name })}
136
+ indent={1}
137
+ />
138
+ ))
139
+ )}
140
+ </FolderSection>
141
+
142
+ {metricFileNames.length > 0 && (
143
+ <FolderSection name="metrics">
144
+ {metricFileNames.map((file) => (
145
+ <FileItem
146
+ key={file}
147
+ name={`${file}.yml`}
148
+ selected={isSelected(selection, { type: "metrics", file })}
149
+ onClick={() => onSelect({ type: "metrics", file })}
150
+ indent={1}
151
+ />
152
+ ))}
153
+ </FolderSection>
154
+ )}
155
+ </div>
156
+ </ScrollArea>
157
+ </div>
158
+ );
159
+ }
@@ -16,22 +16,39 @@ import { ToolPart } from "./chat/tool-part";
16
16
  import { Markdown } from "./chat/markdown";
17
17
  import { STARTER_PROMPTS } from "./chat/starter-prompts";
18
18
  import { ConversationSidebar } from "./conversations/conversation-sidebar";
19
+ import { ChangePasswordDialog } from "./admin/change-password-dialog";
19
20
 
20
21
  const API_KEY_STORAGE_KEY = "atlas-api-key";
21
22
 
23
+ /* Static SVG icons — hoisted to avoid recreation on every render */
24
+ const MenuIcon = (
25
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-5 w-5">
26
+ <path fillRule="evenodd" d="M2 4.75A.75.75 0 0 1 2.75 4h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 4.75ZM2 10a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 10Zm0 5.25a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1-.75-.75Z" clipRule="evenodd" />
27
+ </svg>
28
+ );
29
+
30
+ const AtlasLogo = (
31
+ <svg viewBox="0 0 256 256" fill="none" className="h-7 w-7 shrink-0 text-primary" aria-hidden="true">
32
+ <path d="M128 24 L232 208 L24 208 Z" stroke="currentColor" strokeWidth="14" fill="none" strokeLinejoin="round"/>
33
+ <circle cx="128" cy="28" r="16" fill="currentColor"/>
34
+ </svg>
35
+ );
36
+
22
37
  export function AtlasChat() {
23
38
  const { apiUrl, isCrossOrigin, authClient } = useAtlasConfig();
24
39
  const dark = useDarkMode();
25
40
  const [input, setInput] = useState("");
26
- const [authMode, setAuthMode] = useState<AuthMode>("none");
41
+ const [authMode, setAuthMode] = useState<AuthMode | null>(null);
27
42
  const [healthWarning, setHealthWarning] = useState("");
28
43
  const [apiKey, setApiKey] = useState("");
29
44
  const [conversationId, setConversationId] = useState<string | null>(null);
30
45
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
31
46
  const [loadingConversation, setLoadingConversation] = useState(false);
47
+ const [passwordChangeRequired, setPasswordChangeRequired] = useState(false);
32
48
  const scrollRef = useRef<HTMLDivElement>(null);
33
49
 
34
50
  const managedSession = authClient.useSession();
51
+ const authResolved = authMode !== null;
35
52
  const isManaged = authMode === "managed";
36
53
  const isSignedIn = isManaged && !!managedSession.data?.user;
37
54
 
@@ -55,6 +72,9 @@ export function AtlasChat() {
55
72
  const refreshConvosRef = useRef(convos.refresh);
56
73
  refreshConvosRef.current = convos.refresh;
57
74
 
75
+ const conversationIdRef = useRef(conversationId);
76
+ conversationIdRef.current = conversationId;
77
+
58
78
  // Load API key from sessionStorage on mount + fetch auth mode + conversations
59
79
  useEffect(() => {
60
80
  try {
@@ -76,6 +96,7 @@ export function AtlasChat() {
76
96
  return fetchHealth(attempt + 1);
77
97
  }
78
98
  setHealthWarning("Health check failed — check server logs. Try refreshing the page.");
99
+ setAuthMode("none");
79
100
  return;
80
101
  }
81
102
  const data = await res.json();
@@ -90,6 +111,7 @@ export function AtlasChat() {
90
111
  return fetchHealth(attempt + 1);
91
112
  }
92
113
  setHealthWarning("Unable to reach the API server. Try refreshing the page.");
114
+ setAuthMode("none");
93
115
  }
94
116
  }
95
117
  fetchHealth(1);
@@ -100,6 +122,25 @@ export function AtlasChat() {
100
122
  convos.fetchList();
101
123
  }, [authMode, convos.fetchList]);
102
124
 
125
+ // Check if managed auth user needs to change their default password
126
+ useEffect(() => {
127
+ if (!isManaged || !managedSession.data?.user) return;
128
+
129
+ async function checkPasswordStatus() {
130
+ try {
131
+ const res = await fetch(`${apiUrl}/api/v1/admin/me/password-status`, {
132
+ credentials: isCrossOrigin ? "include" : "same-origin",
133
+ });
134
+ if (!res.ok) return;
135
+ const data = await res.json();
136
+ if (data.passwordChangeRequired) setPasswordChangeRequired(true);
137
+ } catch {
138
+ // Non-critical — skip silently
139
+ }
140
+ }
141
+ checkPasswordStatus();
142
+ }, [isManaged, managedSession.data?.user, apiUrl, isCrossOrigin]);
143
+
103
144
  const handleSaveApiKey = useCallback((key: string) => {
104
145
  setApiKey(key);
105
146
  try {
@@ -109,7 +150,9 @@ export function AtlasChat() {
109
150
  }
110
151
  }, []);
111
152
 
112
- // Dynamic transport — captures x-conversation-id from response
153
+ // Dynamic transport — captures x-conversation-id from response.
154
+ // conversationId is accessed via ref to avoid recreating the transport mid-stream
155
+ // (which causes an infinite re-render loop in useChat).
113
156
  const transport = useMemo(() => {
114
157
  const headers: Record<string, string> = {};
115
158
  if (apiKey) {
@@ -119,11 +162,11 @@ export function AtlasChat() {
119
162
  api: `${apiUrl}/api/chat`,
120
163
  headers,
121
164
  credentials: isCrossOrigin ? "include" : undefined,
122
- body: conversationId ? { conversationId } : undefined,
165
+ body: () => (conversationIdRef.current ? { conversationId: conversationIdRef.current } : {}),
123
166
  fetch: (async (input, init) => {
124
167
  const response = await globalThis.fetch(input, init);
125
168
  const convId = response.headers.get("x-conversation-id");
126
- if (convId && convId !== conversationId) {
169
+ if (convId && convId !== conversationIdRef.current) {
127
170
  setConversationId(convId);
128
171
  setTimeout(() => {
129
172
  refreshConvosRef.current().catch((err) => {
@@ -134,7 +177,7 @@ export function AtlasChat() {
134
177
  return response;
135
178
  }) as typeof fetch,
136
179
  });
137
- }, [apiKey, authMode, apiUrl, isCrossOrigin, conversationId]);
180
+ }, [apiKey, authMode, apiUrl, isCrossOrigin]);
138
181
 
139
182
  const { messages, setMessages, sendMessage, status, error } = useChat({ transport });
140
183
 
@@ -186,6 +229,16 @@ export function AtlasChat() {
186
229
  setMobileMenuOpen(false);
187
230
  }
188
231
 
232
+ // Wait for auth mode detection before rendering — prevents flash of chat UI
233
+ // when managed auth is active but session hasn't been checked yet.
234
+ if (!authResolved || (isManaged && managedSession.isPending)) {
235
+ return (
236
+ <DarkModeContext.Provider value={dark}>
237
+ <div className="flex h-dvh items-center justify-center bg-white dark:bg-zinc-950" />
238
+ </DarkModeContext.Provider>
239
+ );
240
+ }
241
+
189
242
  return (
190
243
  <DarkModeContext.Provider value={dark}>
191
244
  <div className="flex h-dvh">
@@ -214,16 +267,11 @@ export function AtlasChat() {
214
267
  className="rounded p-1 text-zinc-400 hover:text-zinc-700 md:hidden dark:hover:text-zinc-200"
215
268
  aria-label="Open conversation history"
216
269
  >
217
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-5 w-5">
218
- <path fillRule="evenodd" d="M2 4.75A.75.75 0 0 1 2.75 4h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 4.75ZM2 10a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 10Zm0 5.25a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1-.75-.75Z" clipRule="evenodd" />
219
- </svg>
270
+ {MenuIcon}
220
271
  </button>
221
272
  )}
222
273
  <div className="flex items-center gap-2.5">
223
- <svg viewBox="0 0 256 256" fill="none" className="h-7 w-7 shrink-0" aria-hidden="true">
224
- <path d="M128 24 L232 208 L24 208 Z" stroke="#23CE9E" strokeWidth="14" fill="none" strokeLinejoin="round"/>
225
- <circle cx="128" cy="28" r="16" fill="#23CE9E"/>
226
- </svg>
274
+ {AtlasLogo}
227
275
  <div>
228
276
  <h1 className="text-xl font-semibold tracking-tight">Atlas</h1>
229
277
  <p className="text-sm text-zinc-500">Ask your data anything</p>
@@ -365,6 +413,10 @@ export function AtlasChat() {
365
413
  </div>
366
414
  </main>
367
415
  </div>
416
+ <ChangePasswordDialog
417
+ open={passwordChangeRequired}
418
+ onComplete={() => setPasswordChangeRequired(false)}
419
+ />
368
420
  </DarkModeContext.Provider>
369
421
  );
370
422
  }
@@ -100,6 +100,26 @@ function truncateLabel(label: unknown, maxLen = 12): string {
100
100
  /* Tooltip */
101
101
  /* ------------------------------------------------------------------ */
102
102
 
103
+ const TOOLTIP_LABEL_STYLE = { fontWeight: 600, marginBottom: 4 } as const;
104
+
105
+ const tooltipStyleCache = new Map<boolean, React.CSSProperties>();
106
+ function getTooltipStyle(dark: boolean): React.CSSProperties {
107
+ let style = tooltipStyleCache.get(dark);
108
+ if (!style) {
109
+ const t = themeTokens(dark);
110
+ style = {
111
+ background: t.tooltipBg,
112
+ border: `1px solid ${t.tooltipBorder}`,
113
+ borderRadius: 6,
114
+ padding: "8px 12px",
115
+ fontSize: 12,
116
+ color: t.tooltipText,
117
+ };
118
+ tooltipStyleCache.set(dark, style);
119
+ }
120
+ return style;
121
+ }
122
+
103
123
  function ChartTooltip({ active, payload, label, dark }: {
104
124
  active?: boolean;
105
125
  payload?: Array<{ name: string; value: number; color: string }>;
@@ -107,19 +127,9 @@ function ChartTooltip({ active, payload, label, dark }: {
107
127
  dark: boolean;
108
128
  }) {
109
129
  if (!active || !payload?.length) return null;
110
- const t = themeTokens(dark);
111
130
  return (
112
- <div
113
- style={{
114
- background: t.tooltipBg,
115
- border: `1px solid ${t.tooltipBorder}`,
116
- borderRadius: 6,
117
- padding: "8px 12px",
118
- fontSize: 12,
119
- color: t.tooltipText,
120
- }}
121
- >
122
- <p style={{ fontWeight: 600, marginBottom: 4 }}>{label}</p>
131
+ <div style={getTooltipStyle(dark)}>
132
+ <p style={TOOLTIP_LABEL_STYLE}>{label}</p>
123
133
  {payload.map((entry, i) => (
124
134
  <p key={i} style={{ color: entry.color }}>
125
135
  {entry.name}: {typeof entry.value === "number" ? formatNumber(entry.value) : entry.value}
@@ -365,9 +375,9 @@ export function ResultChart({
365
375
  </div>
366
376
  <ChartErrorBoundary key={currentType}>
367
377
  <div className="p-2">
368
- {currentType === "bar" && <BarChartView data={chartData} rec={currentRec} dark={dark} />}
369
- {currentType === "line" && <LineChartView data={chartData} rec={currentRec} dark={dark} />}
370
- {currentType === "pie" && <PieChartView data={chartData} rec={currentRec} dark={dark} />}
378
+ {currentType === "bar" ? <BarChartView data={chartData} rec={currentRec} dark={dark} />
379
+ : currentType === "line" ? <LineChartView data={chartData} rec={currentRec} dark={dark} />
380
+ : <PieChartView data={chartData} rec={currentRec} dark={dark} />}
371
381
  </div>
372
382
  </ChartErrorBoundary>
373
383
  </div>
@@ -1,58 +1,104 @@
1
1
  "use client";
2
2
 
3
- import { useContext } from "react";
3
+ import { memo, useContext, useState, useEffect, type ReactNode } from "react";
4
4
  import ReactMarkdown from "react-markdown";
5
- import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
6
- import { oneDark, oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
7
5
  import { DarkModeContext } from "../../hooks/use-dark-mode";
8
6
 
9
- export function Markdown({ content }: { content: string }) {
7
+ /* ------------------------------------------------------------------ */
8
+ /* Lazy-loaded syntax highlighter (~300KB) */
9
+ /* ------------------------------------------------------------------ */
10
+
11
+ type SyntaxHighlighterModule = typeof import("react-syntax-highlighter");
12
+ type StyleModule = typeof import("react-syntax-highlighter/dist/esm/styles/prism");
13
+
14
+ let _highlighterCache: { Prism: SyntaxHighlighterModule["Prism"]; oneDark: StyleModule["oneDark"]; oneLight: StyleModule["oneLight"] } | null = null;
15
+
16
+ function LazyCodeBlock({ language, dark, children }: { language: string; dark: boolean; children: string }) {
17
+ const [mod, setMod] = useState(_highlighterCache);
18
+
19
+ useEffect(() => {
20
+ if (_highlighterCache) return;
21
+ Promise.all([
22
+ import("react-syntax-highlighter"),
23
+ import("react-syntax-highlighter/dist/esm/styles/prism"),
24
+ ]).then(([sh, styles]) => {
25
+ _highlighterCache = { Prism: sh.Prism, oneDark: styles.oneDark, oneLight: styles.oneLight };
26
+ setMod(_highlighterCache);
27
+ });
28
+ }, []);
29
+
30
+ if (!mod) {
31
+ return (
32
+ <pre className="my-2 overflow-x-auto rounded-lg bg-zinc-100 p-3 text-xs dark:bg-zinc-800">
33
+ <code>{children}</code>
34
+ </pre>
35
+ );
36
+ }
37
+
38
+ return (
39
+ <mod.Prism
40
+ language={language}
41
+ style={dark ? mod.oneDark : mod.oneLight}
42
+ customStyle={CODE_BLOCK_STYLE}
43
+ >
44
+ {children}
45
+ </mod.Prism>
46
+ );
47
+ }
48
+
49
+ const CODE_BLOCK_STYLE = {
50
+ margin: "0.5rem 0",
51
+ borderRadius: "0.5rem",
52
+ fontSize: "0.75rem",
53
+ } as const;
54
+
55
+ /* ------------------------------------------------------------------ */
56
+ /* Static markdown renderers — hoisted outside component */
57
+ /* ------------------------------------------------------------------ */
58
+
59
+ const mdComponents = {
60
+ p: ({ children }: { children?: ReactNode }) => (
61
+ <p className="mb-3 leading-relaxed last:mb-0">{children}</p>
62
+ ),
63
+ h1: ({ children }: { children?: ReactNode }) => (
64
+ <h1 className="mb-2 mt-4 text-lg font-bold first:mt-0">{children}</h1>
65
+ ),
66
+ h2: ({ children }: { children?: ReactNode }) => (
67
+ <h2 className="mb-2 mt-3 text-base font-semibold first:mt-0">{children}</h2>
68
+ ),
69
+ h3: ({ children }: { children?: ReactNode }) => (
70
+ <h3 className="mb-1 mt-2 font-semibold first:mt-0">{children}</h3>
71
+ ),
72
+ ul: ({ children }: { children?: ReactNode }) => (
73
+ <ul className="mb-3 list-disc space-y-1 pl-4">{children}</ul>
74
+ ),
75
+ ol: ({ children }: { children?: ReactNode }) => (
76
+ <ol className="mb-3 list-decimal space-y-1 pl-4">{children}</ol>
77
+ ),
78
+ strong: ({ children }: { children?: ReactNode }) => (
79
+ <strong className="font-semibold text-zinc-900 dark:text-zinc-50">{children}</strong>
80
+ ),
81
+ blockquote: ({ children }: { children?: ReactNode }) => (
82
+ <blockquote className="my-2 border-l-2 border-zinc-300 pl-3 text-zinc-500 dark:border-zinc-600 dark:text-zinc-400">
83
+ {children}
84
+ </blockquote>
85
+ ),
86
+ pre: ({ children }: { children?: ReactNode }) => <>{children}</>,
87
+ };
88
+
89
+ export const Markdown = memo(function Markdown({ content }: { content: string }) {
10
90
  const dark = useContext(DarkModeContext);
11
91
  return (
12
92
  <ReactMarkdown
13
93
  components={{
14
- p: ({ children }) => (
15
- <p className="mb-3 leading-relaxed last:mb-0">{children}</p>
16
- ),
17
- h1: ({ children }) => (
18
- <h1 className="mb-2 mt-4 text-lg font-bold first:mt-0">{children}</h1>
19
- ),
20
- h2: ({ children }) => (
21
- <h2 className="mb-2 mt-3 text-base font-semibold first:mt-0">{children}</h2>
22
- ),
23
- h3: ({ children }) => (
24
- <h3 className="mb-1 mt-2 font-semibold first:mt-0">{children}</h3>
25
- ),
26
- ul: ({ children }) => (
27
- <ul className="mb-3 list-disc space-y-1 pl-4">{children}</ul>
28
- ),
29
- ol: ({ children }) => (
30
- <ol className="mb-3 list-decimal space-y-1 pl-4">{children}</ol>
31
- ),
32
- strong: ({ children }) => (
33
- <strong className="font-semibold text-zinc-900 dark:text-zinc-50">{children}</strong>
34
- ),
35
- blockquote: ({ children }) => (
36
- <blockquote className="my-2 border-l-2 border-zinc-300 pl-3 text-zinc-500 dark:border-zinc-600 dark:text-zinc-400">
37
- {children}
38
- </blockquote>
39
- ),
40
- pre: ({ children }) => <>{children}</>,
94
+ ...mdComponents,
41
95
  code({ className, children, ...props }) {
42
96
  const match = /language-(\w+)/.exec(className || "");
43
97
  if (match) {
44
98
  return (
45
- <SyntaxHighlighter
46
- language={match[1]}
47
- style={dark ? oneDark : oneLight}
48
- customStyle={{
49
- margin: "0.5rem 0",
50
- borderRadius: "0.5rem",
51
- fontSize: "0.75rem",
52
- }}
53
- >
99
+ <LazyCodeBlock language={match[1]} dark={dark}>
54
100
  {String(children).replace(/\n$/, "")}
55
- </SyntaxHighlighter>
101
+ </LazyCodeBlock>
56
102
  );
57
103
  }
58
104
  return (
@@ -69,4 +115,4 @@ export function Markdown({ content }: { content: string }) {
69
115
  {content}
70
116
  </ReactMarkdown>
71
117
  );
72
- }
118
+ });