@agent-native/core 0.145.5 → 0.145.6

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 (387) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +18 -0
  3. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  4. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +17 -1
  5. package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  6. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +1 -0
  7. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +22 -3
  8. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +76 -23
  9. package/corpus/templates/analytics/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  10. package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  11. package/corpus/templates/analytics/AGENTS.md +2 -1
  12. package/corpus/templates/analytics/actions/account-deep-dive.ts +83 -47
  13. package/corpus/templates/analytics/actions/create-dashboard-folder.ts +27 -0
  14. package/corpus/templates/analytics/actions/gong-calls.ts +3 -3
  15. package/corpus/templates/analytics/actions/list-dashboard-folders.ts +26 -0
  16. package/corpus/templates/analytics/actions/list-sql-dashboards.ts +1 -0
  17. package/corpus/templates/analytics/actions/set-dashboard-folder.ts +26 -0
  18. package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +18 -3
  19. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +6 -4
  20. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  21. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +12 -2
  22. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +35 -20
  23. package/corpus/templates/analytics/app/global.css +17 -0
  24. package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +29 -15
  25. package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +15 -3
  26. package/corpus/templates/analytics/app/i18n-data.ts +296 -0
  27. package/corpus/templates/analytics/app/lib/prefetch-keys.ts +23 -2
  28. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +642 -0
  29. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +66 -31
  30. package/corpus/templates/analytics/app/root.tsx +4 -0
  31. package/corpus/templates/analytics/app/routes/dashboards._index.tsx +5 -1
  32. package/corpus/templates/analytics/changelog/2026-08-07-added-a-dashboards-overview-with-personal-and-shared-folders.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-08-07-analytics-tracking-and-chat-startup-no-longer-emit-avoidable.md +6 -0
  34. package/corpus/templates/analytics/changelog/2026-08-07-dashboard-panels-now-load-custom-api-data-programs-reliably.md +6 -0
  35. package/corpus/templates/analytics/changelog/2026-08-07-gong-searches-conserve-api-quota-and-report-incomplete-coverage.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-08-07-searchable-dashboards-make-personal-and-shared-folders-easie.md +6 -0
  37. package/corpus/templates/analytics/netlify.toml +3 -0
  38. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +112 -0
  39. package/corpus/templates/analytics/server/db/index.ts +12 -0
  40. package/corpus/templates/analytics/server/db/schema.ts +14 -0
  41. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +4 -0
  42. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +1 -0
  43. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +85 -15
  44. package/corpus/templates/analytics/server/lib/dashboard-folders-store.ts +168 -0
  45. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +9 -4
  46. package/corpus/templates/analytics/server/lib/dashboards-store.ts +116 -1
  47. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +4 -8
  48. package/corpus/templates/analytics/server/lib/gong.ts +139 -78
  49. package/corpus/templates/analytics/server/plugins/db.ts +55 -15
  50. package/corpus/templates/analytics/server/plugins/session-replay-retention-jobs.ts +6 -2
  51. package/corpus/templates/analytics/server/routes/api/session-replay/retention.post.ts +65 -0
  52. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +18 -0
  53. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  54. package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  55. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +1 -0
  56. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +76 -23
  57. package/corpus/templates/assets/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  58. package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  59. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +18 -0
  60. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  61. package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  62. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +1 -0
  63. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +76 -23
  64. package/corpus/templates/brain/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  65. package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  66. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +18 -0
  67. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  68. package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  69. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +1 -0
  70. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +76 -23
  71. package/corpus/templates/calendar/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  72. package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  73. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +18 -0
  74. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  75. package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  76. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +1 -0
  77. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +76 -23
  78. package/corpus/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  79. package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  80. package/corpus/templates/chat/.env.example +3 -0
  81. package/corpus/templates/chat/AGENTS.md +4 -0
  82. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +18 -0
  83. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  84. package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  85. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +1 -0
  86. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +76 -23
  87. package/corpus/templates/clips/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  88. package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  89. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +4 -0
  90. package/corpus/templates/clips/actions/request-transcript.ts +52 -3
  91. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -10
  92. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -0
  93. package/corpus/templates/clips/changelog/2026-08-07-screen-recordings-now-capture-tab-and-system-audio-even-when.md +6 -0
  94. package/corpus/templates/clips/changelog/2026-08-07-transcription-now-retries-transient-cloud-failures-automatic.md +6 -0
  95. package/corpus/templates/clips/server/db/schema.ts +7 -0
  96. package/corpus/templates/clips/server/plugins/db.ts +8 -0
  97. package/corpus/templates/clips/shared/recording-capture.ts +38 -0
  98. package/corpus/templates/clips/shared/transcript-failure.ts +89 -0
  99. package/corpus/templates/content/.agents/skills/actions/SKILL.md +18 -0
  100. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  101. package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  102. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +1 -0
  103. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +76 -23
  104. package/corpus/templates/content/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  105. package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  106. package/corpus/templates/content/.env.example +9 -3
  107. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -0
  108. package/corpus/templates/content/changelog/2026-08-07-collapsed-sidebar-utility-buttons-stay-anchored-at-the-botto.md +6 -0
  109. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  110. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +76 -23
  111. package/corpus/templates/crm/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  112. package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  113. package/corpus/templates/design/.agents/skills/actions/SKILL.md +18 -0
  114. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  115. package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  116. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +1 -0
  117. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +76 -23
  118. package/corpus/templates/design/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  119. package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  120. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +18 -0
  121. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  122. package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  123. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +1 -0
  124. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +76 -23
  125. package/corpus/templates/dispatch/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  126. package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  127. package/corpus/templates/factory/.agents/skills/actions/SKILL.md +18 -0
  128. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  129. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  130. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +1 -0
  131. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +76 -23
  132. package/corpus/templates/factory/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  133. package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  134. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +279 -0
  135. package/corpus/templates/factory/actions/get-factory-automation-health.ts +23 -4
  136. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +1 -1
  137. package/corpus/templates/factory/app/routes/factory.tsx +21 -3
  138. package/corpus/templates/factory/changelog/2026-08-07-factory-can-keep-builder-bot-pull-requests-moving-by-request.md +6 -0
  139. package/corpus/templates/factory/server/lib/require-factory-automation.ts +1 -0
  140. package/corpus/templates/factory/server/plugins/agent-chat.ts +4 -1
  141. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +81 -12
  142. package/corpus/templates/factory/server/triage/github-client.ts +31 -0
  143. package/corpus/templates/factory/server/triage/pr-babysit.ts +75 -1
  144. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +18 -0
  145. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  146. package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  147. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +1 -0
  148. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +76 -23
  149. package/corpus/templates/forms/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  150. package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  151. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  152. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +1 -0
  153. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +76 -23
  154. package/corpus/templates/macros/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  155. package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  156. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +18 -0
  157. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  158. package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  159. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +1 -0
  160. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +76 -23
  161. package/corpus/templates/mail/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  162. package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  163. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +18 -0
  164. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  165. package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  166. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +1 -0
  167. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +76 -23
  168. package/corpus/templates/plan/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  169. package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  170. package/corpus/templates/plan/app/pages/PlansPage.tsx +2 -1
  171. package/corpus/templates/plan/changelog/2026-08-07-inline-help-icons-now-stay-visually-balanced-with-nearby-tex.md +6 -0
  172. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +18 -0
  173. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  174. package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  175. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +1 -0
  176. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +76 -23
  177. package/corpus/templates/slides/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  178. package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  179. package/corpus/templates/slides/.env.example +5 -0
  180. package/corpus/templates/slides/changelog/2026-08-07-fixed-generated-slides-briefly-disappearing-while-they-are-b.md +6 -0
  181. package/corpus/templates/slides/server/plugins/collab.ts +20 -8
  182. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +18 -0
  183. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  184. package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  185. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +1 -0
  186. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +76 -23
  187. package/corpus/templates/tasks/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  188. package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  189. package/corpus/templates/tasks/.env.example +3 -0
  190. package/dist/agent/production-agent.js +24 -3
  191. package/dist/client/AgentPanel.d.ts +6 -0
  192. package/dist/client/AgentPanel.js +176 -30
  193. package/dist/client/AssistantChat.d.ts +2 -0
  194. package/dist/client/AssistantChat.js +4 -1
  195. package/dist/client/ConnectBuilderCard.js +6 -11
  196. package/dist/client/MultiTabAssistantChat.js +1 -1
  197. package/dist/client/RuntimeConfigNotice.d.ts +1 -0
  198. package/dist/client/RuntimeConfigNotice.js +94 -0
  199. package/dist/client/agent-chat/index.d.ts +1 -1
  200. package/dist/client/agent-chat/index.js +1 -1
  201. package/dist/client/agent-chat-adapter.js +85 -4
  202. package/dist/client/agent-page/AgentTabsPage.js +1 -1
  203. package/dist/client/agent-sidebar-state.d.ts +6 -0
  204. package/dist/client/agent-sidebar-state.js +25 -0
  205. package/dist/client/app-providers.js +2 -1
  206. package/dist/client/assistant-ui-recovery.js +1 -1
  207. package/dist/client/chat/index.d.ts +1 -1
  208. package/dist/client/chat/index.js +1 -1
  209. package/dist/client/chat/markdown-renderer.d.ts +2 -1
  210. package/dist/client/chat/markdown-renderer.js +48 -2
  211. package/dist/client/chat/message-components.d.ts +6 -0
  212. package/dist/client/chat/message-components.js +182 -53
  213. package/dist/client/chat/tool-call-display.d.ts +16 -4
  214. package/dist/client/chat/tool-call-display.js +225 -18
  215. package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
  216. package/dist/client/frame-protocol.d.ts +4 -0
  217. package/dist/client/i18n.js +9 -0
  218. package/dist/client/integrations/IntegrationsPanel.js +2 -5
  219. package/dist/client/onboarding/FirstRunOnboarding.js +2 -3
  220. package/dist/client/org/TeamPage.js +49 -6
  221. package/dist/client/org/hooks.d.ts +7 -2
  222. package/dist/client/org/hooks.js +11 -4
  223. package/dist/client/resources/McpConnectionSuggestion.js +2 -3
  224. package/dist/client/resources/McpIntegrationDialog.js +2 -16
  225. package/dist/client/resources/McpIntegrationLogo.d.ts +11 -0
  226. package/dist/client/resources/McpIntegrationLogo.js +14 -0
  227. package/dist/client/resources/ResourceTree.js +1 -1
  228. package/dist/client/resources/mcp-integration-logos.d.ts +1 -0
  229. package/dist/client/resources/mcp-integration-logos.js +47 -18
  230. package/dist/client/settings/useBuilderStatus.js +6 -0
  231. package/dist/client/ui/index.d.ts +1 -0
  232. package/dist/client/ui/index.js +1 -0
  233. package/dist/collab/routes.d.ts +1 -1
  234. package/dist/config.d.ts +26 -3
  235. package/dist/config.js +106 -14
  236. package/dist/db/ddl-guard.d.ts +15 -2
  237. package/dist/db/ddl-guard.js +53 -5
  238. package/dist/jobs/scheduler.js +7 -0
  239. package/dist/localization/default-messages.d.ts +17 -0
  240. package/dist/localization/default-messages.js +17 -0
  241. package/dist/mcp/oauth-client-metadata.js +5 -1
  242. package/dist/mcp/oauth-route.d.ts +5 -4
  243. package/dist/mcp/oauth-route.js +25 -9
  244. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  245. package/dist/mcp/server.d.ts +1 -1
  246. package/dist/mcp/server.js +9 -10
  247. package/dist/notifications/routes.d.ts +3 -3
  248. package/dist/org/handlers.d.ts +1 -0
  249. package/dist/org/handlers.js +17 -2
  250. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  251. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  252. package/dist/provider-api/corpus-jobs.d.ts +12 -2
  253. package/dist/provider-api/corpus-jobs.js +31 -1
  254. package/dist/provider-api/index.d.ts +2 -0
  255. package/dist/provider-api/index.js +7 -1
  256. package/dist/resources/handlers.d.ts +1 -1
  257. package/dist/secrets/routes.d.ts +3 -3
  258. package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +1 -1
  259. package/dist/server/agent-chat/recurring-jobs-runtime.js +14 -2
  260. package/dist/server/agent-chat/script-entries.js +12 -1
  261. package/dist/server/agent-chat-plugin.js +2 -2
  262. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  263. package/dist/server/auth.d.ts +1 -0
  264. package/dist/server/auth.js +85 -1
  265. package/dist/server/better-auth-instance.d.ts +9 -0
  266. package/dist/server/better-auth-instance.js +25 -11
  267. package/dist/server/core-routes-plugin.d.ts +7 -0
  268. package/dist/server/core-routes-plugin.js +51 -5
  269. package/dist/server/create-server.js +23 -2
  270. package/dist/server/db-pressure.d.ts +38 -0
  271. package/dist/server/db-pressure.js +147 -0
  272. package/dist/server/index.d.ts +1 -0
  273. package/dist/server/index.js +1 -0
  274. package/dist/server/legacy-auth-migration.d.ts +10 -0
  275. package/dist/server/legacy-auth-migration.js +37 -0
  276. package/dist/server/onboarding-html.js +206 -2
  277. package/dist/server/poll.d.ts +26 -0
  278. package/dist/server/poll.js +108 -13
  279. package/dist/server/ssr-handler.js +4 -1
  280. package/dist/shared/index.d.ts +1 -0
  281. package/dist/shared/index.js +1 -0
  282. package/dist/shared/runtime-config.d.ts +44 -0
  283. package/dist/shared/runtime-config.js +284 -0
  284. package/dist/styles/agent-native.css +48 -1
  285. package/dist/templates/chat/.agents/skills/actions/SKILL.md +18 -0
  286. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  287. package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  288. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +1 -0
  289. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +76 -23
  290. package/dist/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  291. package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  292. package/dist/templates/chat/.env.example +3 -0
  293. package/dist/templates/chat/AGENTS.md +4 -0
  294. package/dist/templates/default/.agents/skills/actions/SKILL.md +18 -0
  295. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  296. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  297. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +1 -0
  298. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +76 -23
  299. package/dist/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  300. package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  301. package/dist/templates/default/AGENTS.md +6 -1
  302. package/dist/templates/default/app/i18n/ar-SA.ts +6 -0
  303. package/dist/templates/default/app/i18n/de-DE.ts +6 -0
  304. package/dist/templates/default/app/i18n/en-US.ts +6 -0
  305. package/dist/templates/default/app/i18n/es-ES.ts +6 -0
  306. package/dist/templates/default/app/i18n/fr-FR.ts +6 -0
  307. package/dist/templates/default/app/i18n/hi-IN.ts +6 -0
  308. package/dist/templates/default/app/i18n/ja-JP.ts +6 -0
  309. package/dist/templates/default/app/i18n/ko-KR.ts +6 -0
  310. package/dist/templates/default/app/i18n/pt-BR.ts +6 -0
  311. package/dist/templates/default/app/i18n/zh-CN.ts +6 -0
  312. package/dist/templates/default/app/i18n/zh-TW.ts +6 -0
  313. package/dist/templates/headless/.agents/skills/actions/SKILL.md +18 -0
  314. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  315. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +76 -23
  316. package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  317. package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  318. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +18 -0
  319. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  320. package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  321. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +1 -0
  322. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +76 -23
  323. package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  324. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  325. package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +4 -0
  326. package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +8 -0
  327. package/dist/templates/workspace-core/AGENTS.md +7 -2
  328. package/dist/templates/workspace-root/AGENTS.md +5 -0
  329. package/dist/vite/client.d.ts +3 -3
  330. package/dist/vite/client.js +47 -1
  331. package/dist/vite/index.d.ts +1 -1
  332. package/docs/content/agent-native-config.mdx +240 -139
  333. package/docs/content/authentication.mdx +17 -0
  334. package/docs/content/deployment.mdx +19 -14
  335. package/docs/content/drop-in-agent.mdx +19 -2
  336. package/docs/content/environment-variables.mdx +63 -38
  337. package/docs/content/getting-started-actions.mdx +15 -6
  338. package/docs/content/getting-started-database.mdx +14 -10
  339. package/docs/content/getting-started-pages.mdx +32 -35
  340. package/docs/content/getting-started.mdx +7 -6
  341. package/package.json +1 -1
  342. package/src/templates/chat/.agents/skills/actions/SKILL.md +18 -0
  343. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  344. package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  345. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +1 -0
  346. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +76 -23
  347. package/src/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  348. package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  349. package/src/templates/chat/.env.example +3 -0
  350. package/src/templates/chat/AGENTS.md +4 -0
  351. package/src/templates/default/.agents/skills/actions/SKILL.md +18 -0
  352. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  353. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  354. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +1 -0
  355. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +76 -23
  356. package/src/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  357. package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  358. package/src/templates/default/AGENTS.md +6 -1
  359. package/src/templates/default/app/i18n/ar-SA.ts +6 -0
  360. package/src/templates/default/app/i18n/de-DE.ts +6 -0
  361. package/src/templates/default/app/i18n/en-US.ts +6 -0
  362. package/src/templates/default/app/i18n/es-ES.ts +6 -0
  363. package/src/templates/default/app/i18n/fr-FR.ts +6 -0
  364. package/src/templates/default/app/i18n/hi-IN.ts +6 -0
  365. package/src/templates/default/app/i18n/ja-JP.ts +6 -0
  366. package/src/templates/default/app/i18n/ko-KR.ts +6 -0
  367. package/src/templates/default/app/i18n/pt-BR.ts +6 -0
  368. package/src/templates/default/app/i18n/zh-CN.ts +6 -0
  369. package/src/templates/default/app/i18n/zh-TW.ts +6 -0
  370. package/src/templates/headless/.agents/skills/actions/SKILL.md +18 -0
  371. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  372. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +76 -23
  373. package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  374. package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  375. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +18 -0
  376. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  377. package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  378. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +1 -0
  379. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +76 -23
  380. package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  381. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  382. package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +4 -0
  383. package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +8 -0
  384. package/src/templates/workspace-core/AGENTS.md +7 -2
  385. package/src/templates/workspace-root/AGENTS.md +5 -0
  386. package/corpus/templates/dispatch/app/routes/admin.apps.$appId.tsx +0 -1
  387. package/corpus/templates/dispatch/app/routes/admin.apps.tsx +0 -1
@@ -0,0 +1,642 @@
1
+ import {
2
+ callAction,
3
+ useActionMutation,
4
+ useChangeVersions,
5
+ } from "@agent-native/core/client/hooks";
6
+ import { useT } from "@agent-native/core/client/i18n";
7
+ import {
8
+ IconCheck,
9
+ IconDots,
10
+ IconFolder,
11
+ IconFolderPlus,
12
+ IconLayoutDashboard,
13
+ IconLock,
14
+ IconPlus,
15
+ IconSearch,
16
+ IconUsers,
17
+ } from "@tabler/icons-react";
18
+ import { useQuery } from "@tanstack/react-query";
19
+ import { useEffect, useMemo, useState } from "react";
20
+ import { Link } from "react-router";
21
+ import { toast } from "sonner";
22
+
23
+ import { useAuth } from "@/components/auth/AuthProvider";
24
+ import { Badge } from "@/components/ui/badge";
25
+ import { Button } from "@/components/ui/button";
26
+ import {
27
+ Card,
28
+ CardContent,
29
+ CardDescription,
30
+ CardHeader,
31
+ CardTitle,
32
+ } from "@/components/ui/card";
33
+ import {
34
+ Dialog,
35
+ DialogContent,
36
+ DialogFooter,
37
+ DialogHeader,
38
+ DialogTitle,
39
+ DialogTrigger,
40
+ } from "@/components/ui/dialog";
41
+ import {
42
+ DropdownMenu,
43
+ DropdownMenuContent,
44
+ DropdownMenuItem,
45
+ DropdownMenuLabel,
46
+ DropdownMenuSeparator,
47
+ DropdownMenuTrigger,
48
+ } from "@/components/ui/dropdown-menu";
49
+ import { Input } from "@/components/ui/input";
50
+ import { Label } from "@/components/ui/label";
51
+ import {
52
+ Select,
53
+ SelectContent,
54
+ SelectItem,
55
+ SelectTrigger,
56
+ SelectValue,
57
+ } from "@/components/ui/select";
58
+ import { Skeleton } from "@/components/ui/skeleton";
59
+ import { dashboardCacheScope } from "@/lib/prefetch-keys";
60
+
61
+ import { NewDashboardDialog } from "../components/layout/NewDashboardDialog";
62
+
63
+ type Scope = "personal" | "shared";
64
+ type Dashboard = {
65
+ id: string;
66
+ name?: string;
67
+ title?: string;
68
+ visibility?: string;
69
+ folderId?: string | null;
70
+ };
71
+ type Folder = {
72
+ id: string;
73
+ name: string;
74
+ scope: Scope;
75
+ visibility?: string;
76
+ };
77
+
78
+ const createFolderAction = "create-dashboard-folder" as Parameters<
79
+ typeof useActionMutation
80
+ >[0];
81
+ const setFolderAction = "set-dashboard-folder" as Parameters<
82
+ typeof useActionMutation
83
+ >[0];
84
+
85
+ export default function DashboardOverview() {
86
+ const t = useT();
87
+ const { auth } = useAuth();
88
+ const dashboardScope = dashboardCacheScope(auth);
89
+ const [folderDialogOpen, setFolderDialogOpen] = useState(false);
90
+ const [folderName, setFolderName] = useState("");
91
+ const [folderScope, setFolderScope] = useState<Scope>("personal");
92
+ const [search, setSearch] = useState("");
93
+ const [folderOverrides, setFolderOverrides] = useState<
94
+ Record<string, string | null>
95
+ >({});
96
+ const foldersSync = useChangeVersions([
97
+ "dashboard-folders",
98
+ "dashboards",
99
+ "action",
100
+ ]);
101
+
102
+ useEffect(() => {
103
+ setFolderOverrides({});
104
+ }, [dashboardScope]);
105
+
106
+ const dashboardsQuery = useQuery<Dashboard[]>({
107
+ queryKey: ["dashboards-overview", dashboardScope, foldersSync],
108
+ queryFn: async () => {
109
+ const rows = await callAction(
110
+ "list-sql-dashboards",
111
+ {},
112
+ { method: "GET" },
113
+ );
114
+ if (!Array.isArray(rows)) return [];
115
+
116
+ const validDashboards: Dashboard[] = [];
117
+ for (const dashboard of rows) {
118
+ if (
119
+ !dashboard ||
120
+ typeof dashboard !== "object" ||
121
+ typeof dashboard.id !== "string" ||
122
+ (dashboard.visibility !== "private" &&
123
+ dashboard.visibility !== "org" &&
124
+ dashboard.visibility !== "public")
125
+ ) {
126
+ continue;
127
+ }
128
+ validDashboards.push({
129
+ id: dashboard.id,
130
+ name: dashboard.name,
131
+ visibility: dashboard.visibility,
132
+ folderId: dashboard.folderId ?? null,
133
+ });
134
+ }
135
+ return validDashboards;
136
+ },
137
+ staleTime: 30_000,
138
+ });
139
+ const foldersQuery = useQuery<{ folders: Folder[] }>({
140
+ queryKey: ["dashboard-folders-overview", dashboardScope, foldersSync],
141
+ queryFn: async () => {
142
+ const result = await callAction(
143
+ "list-dashboard-folders",
144
+ {},
145
+ { method: "GET" },
146
+ );
147
+ return (result ?? { folders: [] }) as { folders: Folder[] };
148
+ },
149
+ staleTime: 30_000,
150
+ });
151
+ const createFolder = useActionMutation<
152
+ { folder: Folder },
153
+ { name: string; scope: Scope }
154
+ >(createFolderAction);
155
+ const setFolder = useActionMutation<
156
+ unknown,
157
+ { dashboardId: string; folderId: string | null }
158
+ >(setFolderAction);
159
+
160
+ const dashboards = dashboardsQuery.data ?? [];
161
+ const folders = foldersQuery.data?.folders ?? [];
162
+ const folderById = useMemo(
163
+ () => new Map(folders.map((folder) => [folder.id, folder])),
164
+ [folders],
165
+ );
166
+ const filteredDashboards = useMemo(() => {
167
+ const searchTerm = search.trim().toLowerCase();
168
+ if (!searchTerm) return dashboards;
169
+
170
+ return dashboards.filter((dashboard) => {
171
+ const folderId =
172
+ dashboard.id in folderOverrides
173
+ ? folderOverrides[dashboard.id]
174
+ : dashboard.folderId;
175
+ const folderName = folderId ? folderById.get(folderId)?.name : "";
176
+ const dashboardName = dashboard.name || dashboard.title || "";
177
+ return `${dashboardName} ${folderName ?? ""}`
178
+ .toLowerCase()
179
+ .includes(searchTerm);
180
+ });
181
+ }, [dashboards, folderById, folderOverrides, search]);
182
+ const grouped = useMemo(() => {
183
+ const result = new Map<string, Dashboard[]>();
184
+ const visibleFolderIds = new Set(folders.map((folder) => folder.id));
185
+ for (const dashboard of filteredDashboards) {
186
+ const folderId =
187
+ dashboard.id in folderOverrides
188
+ ? folderOverrides[dashboard.id]
189
+ : dashboard.folderId;
190
+ const id =
191
+ folderId && visibleFolderIds.has(folderId) ? folderId : "unfiled";
192
+ result.set(id, [...(result.get(id) ?? []), dashboard]);
193
+ }
194
+ return result;
195
+ }, [filteredDashboards, folderOverrides, folders]);
196
+
197
+ const createNewFolder = async () => {
198
+ const name = folderName.trim();
199
+ if (!name) return;
200
+ try {
201
+ await createFolder.mutateAsync({ name, scope: folderScope });
202
+ setFolderName("");
203
+ setFolderDialogOpen(false);
204
+ await foldersQuery.refetch();
205
+ } catch (error) {
206
+ toast.error(
207
+ error instanceof Error
208
+ ? error.message
209
+ : t("dashboardOverview.folderCreateFailed"),
210
+ );
211
+ }
212
+ };
213
+
214
+ const moveDashboard = async (
215
+ dashboard: Dashboard,
216
+ folderId: string | null,
217
+ ) => {
218
+ const previous =
219
+ dashboard.id in folderOverrides
220
+ ? folderOverrides[dashboard.id]
221
+ : (dashboard.folderId ?? null);
222
+ setFolderOverrides((current) => ({ ...current, [dashboard.id]: folderId }));
223
+ try {
224
+ await setFolder.mutateAsync({ dashboardId: dashboard.id, folderId });
225
+ await dashboardsQuery.refetch();
226
+ setFolderOverrides((current) => {
227
+ const next = { ...current };
228
+ delete next[dashboard.id];
229
+ return next;
230
+ });
231
+ } catch (error) {
232
+ setFolderOverrides((current) => ({
233
+ ...current,
234
+ [dashboard.id]: previous,
235
+ }));
236
+ toast.error(
237
+ error instanceof Error
238
+ ? error.message
239
+ : t("dashboardOverview.moveFailed"),
240
+ );
241
+ }
242
+ };
243
+
244
+ if (dashboardsQuery.isLoading || foldersQuery.isLoading) {
245
+ return <DashboardOverviewSkeleton />;
246
+ }
247
+ if (dashboardsQuery.isError || foldersQuery.isError) {
248
+ return (
249
+ <div className="dashboard-overview-layout mx-auto max-w-5xl">
250
+ <Card>
251
+ <CardHeader>
252
+ <CardTitle>{t("dashboardOverview.loadFailed")}</CardTitle>
253
+ <CardDescription>
254
+ {t("dashboardOverview.loadFailedDescription")}
255
+ </CardDescription>
256
+ </CardHeader>
257
+ <CardContent>
258
+ <Button
259
+ variant="outline"
260
+ onClick={() =>
261
+ void Promise.all([
262
+ dashboardsQuery.refetch(),
263
+ foldersQuery.refetch(),
264
+ ])
265
+ }
266
+ >
267
+ {t("sidebar.retry")}
268
+ </Button>
269
+ </CardContent>
270
+ </Card>
271
+ </div>
272
+ );
273
+ }
274
+
275
+ return (
276
+ <div className="dashboard-overview-layout mx-auto max-w-5xl space-y-8">
277
+ <div className="space-y-1">
278
+ <h1 className="text-xl font-semibold tracking-tight md:hidden">
279
+ {t("dashboardOverview.title")}
280
+ </h1>
281
+ <p className="max-w-2xl text-sm text-muted-foreground">
282
+ {t("dashboardOverview.description")}
283
+ </p>
284
+ </div>
285
+
286
+ <div className="dashboard-overview-toolbar">
287
+ <div className="relative min-w-0 flex-1">
288
+ <IconSearch className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
289
+ <Input
290
+ value={search}
291
+ onChange={(event) => setSearch(event.target.value)}
292
+ placeholder={t("dashboardOverview.searchPlaceholder")}
293
+ aria-label={t("sidebar.search")}
294
+ className="pl-9"
295
+ />
296
+ </div>
297
+ <div className="flex shrink-0 gap-2">
298
+ <NewDashboardDialog triggerClassName="w-auto flex-1 justify-center rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground hover:bg-primary/90 sm:flex-none" />
299
+ <Dialog open={folderDialogOpen} onOpenChange={setFolderDialogOpen}>
300
+ <DialogTrigger asChild>
301
+ <Button variant="outline" className="flex-1 sm:flex-none">
302
+ <IconFolderPlus />
303
+ {t("dashboardOverview.newFolder")}
304
+ </Button>
305
+ </DialogTrigger>
306
+ <DialogContent className="max-h-[90vh] w-[calc(100vw-2rem)] overflow-y-auto sm:max-w-md">
307
+ <DialogHeader>
308
+ <DialogTitle>{t("dashboardOverview.newFolder")}</DialogTitle>
309
+ </DialogHeader>
310
+ <div className="space-y-4 py-2">
311
+ <div className="space-y-2">
312
+ <Label htmlFor="folder-name">
313
+ {t("dashboardOverview.folderName")}
314
+ </Label>
315
+ <Input
316
+ id="folder-name"
317
+ value={folderName}
318
+ onChange={(event) => setFolderName(event.target.value)}
319
+ autoFocus
320
+ />
321
+ </div>
322
+ <div className="space-y-2">
323
+ <Label>{t("dashboardOverview.folderScope")}</Label>
324
+ <Select
325
+ value={folderScope}
326
+ onValueChange={(value) => setFolderScope(value as Scope)}
327
+ >
328
+ <SelectTrigger>
329
+ <SelectValue />
330
+ </SelectTrigger>
331
+ <SelectContent>
332
+ <SelectItem value="personal">
333
+ {t("dashboardOverview.personal")}
334
+ </SelectItem>
335
+ <SelectItem value="shared">
336
+ {t("dashboardOverview.shared")}
337
+ </SelectItem>
338
+ </SelectContent>
339
+ </Select>
340
+ </div>
341
+ </div>
342
+ <DialogFooter className="flex-col gap-2 sm:flex-row">
343
+ <Button
344
+ variant="outline"
345
+ onClick={() => setFolderDialogOpen(false)}
346
+ >
347
+ {t("sidebar.cancel")}
348
+ </Button>
349
+ <Button
350
+ onClick={() => void createNewFolder()}
351
+ disabled={!folderName.trim() || createFolder.isPending}
352
+ >
353
+ <IconPlus />
354
+ {t("dashboardOverview.createFolder")}
355
+ </Button>
356
+ </DialogFooter>
357
+ </DialogContent>
358
+ </Dialog>
359
+ </div>
360
+ </div>
361
+
362
+ {dashboards.length === 0 ? (
363
+ <Card className="border-border/50">
364
+ <CardContent className="flex items-start gap-3 p-5">
365
+ <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
366
+ <IconLayoutDashboard className="size-5" />
367
+ </div>
368
+ <div className="space-y-1">
369
+ <p className="text-sm font-medium">
370
+ {t("dashboardOverview.empty")}
371
+ </p>
372
+ <p className="text-sm text-muted-foreground">
373
+ {t("dashboardOverview.emptyDescription")}
374
+ </p>
375
+ </div>
376
+ </CardContent>
377
+ </Card>
378
+ ) : filteredDashboards.length === 0 ? (
379
+ <Card className="border-border/50">
380
+ <CardContent className="flex items-start gap-3 p-5">
381
+ <IconSearch className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
382
+ <p className="text-sm text-muted-foreground">
383
+ {t("dashboardOverview.noDashboards")}
384
+ </p>
385
+ </CardContent>
386
+ </Card>
387
+ ) : (
388
+ <div className="overflow-hidden rounded-lg border border-border/50 bg-card">
389
+ {(["personal", "shared"] as Scope[]).map((scope) => (
390
+ <DashboardScopeSection
391
+ key={scope}
392
+ scope={scope}
393
+ dashboards={filteredDashboards}
394
+ folders={folders}
395
+ grouped={grouped}
396
+ onMove={moveDashboard}
397
+ t={t}
398
+ />
399
+ ))}
400
+ </div>
401
+ )}
402
+ </div>
403
+ );
404
+ }
405
+
406
+ function DashboardOverviewSkeleton() {
407
+ return (
408
+ <div
409
+ className="dashboard-overview-layout mx-auto max-w-5xl space-y-8"
410
+ aria-busy="true"
411
+ >
412
+ <div className="flex items-center justify-between gap-4">
413
+ <Skeleton className="h-4 w-72" />
414
+ <Skeleton className="h-9 w-32" />
415
+ </div>
416
+ <Skeleton className="h-10 w-full" />
417
+ <div className="space-y-3">
418
+ <Skeleton className="h-5 w-28" />
419
+ <Skeleton className="h-24 w-full" />
420
+ </div>
421
+ </div>
422
+ );
423
+ }
424
+
425
+ function DashboardScopeSection({
426
+ scope,
427
+ dashboards,
428
+ folders,
429
+ grouped,
430
+ onMove,
431
+ t,
432
+ }: {
433
+ scope: Scope;
434
+ dashboards: Dashboard[];
435
+ folders: Folder[];
436
+ grouped: Map<string, Dashboard[]>;
437
+ onMove: (dashboard: Dashboard, folderId: string | null) => void;
438
+ t: ReturnType<typeof useT>;
439
+ }) {
440
+ const scopeFolders = folders.filter((folder) => folder.scope === scope);
441
+ const scopeDashboards = dashboards.filter((dashboard) =>
442
+ isDashboardInScope(dashboard, scope),
443
+ );
444
+ const Icon = scope === "personal" ? IconLock : IconUsers;
445
+
446
+ return (
447
+ <section className="border-t border-border/50 first:border-t-0">
448
+ <div className="flex items-center justify-between gap-3 bg-muted/20 px-4 py-3">
449
+ <div className="flex min-w-0 items-center gap-2.5">
450
+ <Icon className="size-4 shrink-0 text-muted-foreground" />
451
+ <h2 className="text-sm font-semibold">
452
+ {scope === "personal"
453
+ ? t("dashboardOverview.personal")
454
+ : t("dashboardOverview.shared")}
455
+ </h2>
456
+ </div>
457
+ <Badge variant="secondary" className="rounded-md px-2 font-normal">
458
+ {scopeDashboards.length}
459
+ </Badge>
460
+ </div>
461
+
462
+ {scopeDashboards.length === 0 && scopeFolders.length === 0 ? (
463
+ <div className="flex items-start gap-3 p-5">
464
+ <IconFolder className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
465
+ <p className="text-sm text-muted-foreground">
466
+ {t("dashboardOverview.noDashboards")}
467
+ </p>
468
+ </div>
469
+ ) : (
470
+ <>
471
+ {scopeFolders.map((folder) => (
472
+ <FolderSection
473
+ key={folder.id}
474
+ folder={folder}
475
+ dashboards={grouped.get(folder.id) ?? []}
476
+ folders={folders}
477
+ scope={scope}
478
+ onMove={onMove}
479
+ t={t}
480
+ />
481
+ ))}
482
+ <FolderSection
483
+ folder={null}
484
+ dashboards={grouped.get("unfiled") ?? []}
485
+ folders={folders}
486
+ scope={scope}
487
+ onMove={onMove}
488
+ t={t}
489
+ hideWhenEmpty
490
+ />
491
+ </>
492
+ )}
493
+ </section>
494
+ );
495
+ }
496
+
497
+ function FolderSection({
498
+ folder,
499
+ dashboards,
500
+ folders,
501
+ scope,
502
+ onMove,
503
+ t,
504
+ hideWhenEmpty = false,
505
+ }: {
506
+ folder: Folder | null;
507
+ dashboards: Dashboard[];
508
+ folders: Folder[];
509
+ scope: Scope;
510
+ onMove: (dashboard: Dashboard, folderId: string | null) => void;
511
+ t: ReturnType<typeof useT>;
512
+ hideWhenEmpty?: boolean;
513
+ }) {
514
+ const scopedDashboards = dashboards.filter((dashboard) =>
515
+ isDashboardInScope(dashboard, scope),
516
+ );
517
+ if (hideWhenEmpty && scopedDashboards.length === 0) return null;
518
+
519
+ return (
520
+ <section className="border-t border-border/50 first:border-t-0">
521
+ <div className="flex items-center gap-2 px-4 py-3">
522
+ <IconFolder className="size-4 shrink-0 text-muted-foreground" />
523
+ <h3 className="min-w-0 truncate text-sm font-medium">
524
+ {folder?.name ?? t("dashboardOverview.unfiled")}
525
+ </h3>
526
+ <span className="text-xs text-muted-foreground">
527
+ {scopedDashboards.length}
528
+ </span>
529
+ </div>
530
+ {scopedDashboards.length === 0 ? (
531
+ <p className="border-t border-border/50 px-4 py-4 text-sm text-muted-foreground">
532
+ {t("dashboardOverview.noDashboards")}
533
+ </p>
534
+ ) : (
535
+ <div className="divide-y divide-border/50 border-t border-border/50">
536
+ {scopedDashboards.map((dashboard) => (
537
+ <DashboardRow
538
+ key={dashboard.id}
539
+ dashboard={dashboard}
540
+ folders={folders}
541
+ currentFolderId={folder?.id ?? null}
542
+ onMove={onMove}
543
+ t={t}
544
+ />
545
+ ))}
546
+ </div>
547
+ )}
548
+ </section>
549
+ );
550
+ }
551
+
552
+ function DashboardRow({
553
+ dashboard,
554
+ folders,
555
+ currentFolderId,
556
+ onMove,
557
+ t,
558
+ }: {
559
+ dashboard: Dashboard;
560
+ folders: Folder[];
561
+ currentFolderId: string | null;
562
+ onMove: (dashboard: Dashboard, folderId: string | null) => void;
563
+ t: ReturnType<typeof useT>;
564
+ }) {
565
+ const name =
566
+ dashboard.name || dashboard.title || t("sidebar.untitledDashboard");
567
+ const candidateFolders = folders.filter((folder) => {
568
+ if (dashboard.visibility === "private") {
569
+ return folder.scope === "personal";
570
+ }
571
+ return dashboard.visibility === "org" && folder.scope === "shared";
572
+ });
573
+ const canOrganize = dashboard.visibility !== "public";
574
+
575
+ return (
576
+ <div className="group flex items-center gap-3 px-4 py-3 transition-colors hover:bg-muted/30">
577
+ <Link
578
+ className="flex min-w-0 flex-1 items-center gap-3 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring/50"
579
+ to={`/dashboards/${dashboard.id}`}
580
+ >
581
+ <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
582
+ <IconLayoutDashboard className="size-4" />
583
+ </div>
584
+ <div className="min-w-0">
585
+ <p className="truncate text-sm font-medium group-hover:text-primary">
586
+ {name}
587
+ </p>
588
+ <p className="truncate text-xs text-muted-foreground">
589
+ {dashboard.visibility === "private"
590
+ ? t("dashboardOverview.personal")
591
+ : t("dashboardOverview.shared")}
592
+ </p>
593
+ </div>
594
+ </Link>
595
+ {canOrganize ? (
596
+ <DropdownMenu>
597
+ <DropdownMenuTrigger asChild>
598
+ <Button
599
+ variant="ghost"
600
+ size="icon"
601
+ aria-label={t("dashboardOverview.moveDashboard")}
602
+ className="size-8 shrink-0 text-muted-foreground hover:text-foreground"
603
+ >
604
+ <IconDots className="size-4" />
605
+ </Button>
606
+ </DropdownMenuTrigger>
607
+ <DropdownMenuContent align="end">
608
+ <DropdownMenuLabel>
609
+ {t("dashboardOverview.moveDashboard")}
610
+ </DropdownMenuLabel>
611
+ {candidateFolders.map((candidate) => (
612
+ <DropdownMenuItem
613
+ key={candidate.id}
614
+ onSelect={() => onMove(dashboard, candidate.id)}
615
+ >
616
+ <IconFolder className="size-4 text-muted-foreground" />
617
+ {candidate.name}
618
+ {currentFolderId === candidate.id ? (
619
+ <IconCheck className="ml-auto size-4" />
620
+ ) : null}
621
+ </DropdownMenuItem>
622
+ ))}
623
+ {candidateFolders.length > 0 ? <DropdownMenuSeparator /> : null}
624
+ <DropdownMenuItem onSelect={() => onMove(dashboard, null)}>
625
+ <IconFolder className="size-4 text-muted-foreground" />
626
+ {t("dashboardOverview.unfiled")}
627
+ {currentFolderId === null ? (
628
+ <IconCheck className="ml-auto size-4" />
629
+ ) : null}
630
+ </DropdownMenuItem>
631
+ </DropdownMenuContent>
632
+ </DropdownMenu>
633
+ ) : null}
634
+ </div>
635
+ );
636
+ }
637
+
638
+ function isDashboardInScope(dashboard: Dashboard, scope: Scope): boolean {
639
+ return scope === "personal"
640
+ ? dashboard.visibility === "private"
641
+ : dashboard.visibility === "org" || dashboard.visibility === "public";
642
+ }