@agent-native/core 0.120.1 → 0.120.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 (396) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/docs/content/extensions.mdx +23 -4
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/agent/production-agent.ts +4 -0
  6. package/corpus/core/src/agent/run-manager.ts +36 -10
  7. package/corpus/core/src/agent/types.ts +2 -0
  8. package/corpus/core/src/cli/context-xray-local.ts +1 -1
  9. package/corpus/core/src/cli/index.ts +63 -18
  10. package/corpus/core/src/cli/skills.ts +5 -0
  11. package/corpus/core/src/cli/telemetry.ts +118 -1
  12. package/corpus/core/src/client/AgentPanel.tsx +10 -0
  13. package/corpus/core/src/client/CommandMenu.tsx +13 -1
  14. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +39 -4
  15. package/corpus/core/src/client/analytics.ts +13 -0
  16. package/corpus/core/src/client/error-capture.ts +6 -1
  17. package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +28 -8
  18. package/corpus/core/src/client/extensions/agent-native-extension-runtime.ts +1 -1
  19. package/corpus/core/src/client/navigation/index.ts +2 -0
  20. package/corpus/core/src/client/org/OrgSwitcher.tsx +14 -32
  21. package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  22. package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
  23. package/corpus/core/src/deploy/build.ts +3 -0
  24. package/corpus/core/src/extensions/theme.ts +1 -1
  25. package/corpus/core/src/localization/default-messages.ts +2 -0
  26. package/corpus/core/src/server/analytics.ts +154 -25
  27. package/corpus/core/src/server/core-routes-plugin.ts +10 -1
  28. package/corpus/core/src/server/framework-request-handler.ts +17 -26
  29. package/corpus/core/src/styles/agent-native.css +13 -0
  30. package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +3 -0
  31. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  32. package/corpus/core/src/templates/chat/app/global.css +1 -1
  33. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  34. package/corpus/core/src/templates/chat/netlify.toml +1 -0
  35. package/corpus/core/src/templates/default/app/global.css +1 -1
  36. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  37. package/corpus/core/src/tracking/error-capture.ts +151 -0
  38. package/corpus/core/src/tracking/index.ts +5 -0
  39. package/corpus/core/src/vite/client.ts +6 -0
  40. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
  41. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
  42. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
  43. package/corpus/templates/analytics/AGENTS.md +20 -8
  44. package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
  45. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
  46. package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
  47. package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
  48. package/corpus/templates/analytics/actions/view-screen.ts +1 -1
  49. package/corpus/templates/analytics/app/components/dashboard/CumulativeNetChart.tsx +1 -1
  50. package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +1 -4
  51. package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
  52. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
  53. package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +1 -1
  54. package/corpus/templates/analytics/app/components/dashboard/DateRangePicker.tsx +2 -2
  55. package/corpus/templates/analytics/app/components/dashboard/MetricCard.tsx +1 -1
  56. package/corpus/templates/analytics/app/components/dashboard/RecentActivity.tsx +1 -1
  57. package/corpus/templates/analytics/app/components/dashboard/RevenueChart.tsx +1 -1
  58. package/corpus/templates/analytics/app/components/dashboard/RevenueComparisonChart.tsx +1 -1
  59. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +41 -9
  60. package/corpus/templates/analytics/app/components/dashboard/StatsCard.tsx +1 -1
  61. package/corpus/templates/analytics/app/components/dashboard/TimeSeriesChart.tsx +1 -1
  62. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +2 -0
  63. package/corpus/templates/analytics/app/components/layout/Layout.tsx +28 -14
  64. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  65. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +255 -626
  66. package/corpus/templates/analytics/app/global.css +31 -1
  67. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
  68. package/corpus/templates/analytics/app/i18n-data.ts +80 -0
  69. package/corpus/templates/analytics/app/lib/dashboard-report-capture.ts +42 -0
  70. package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
  71. package/corpus/templates/analytics/app/pages/adhoc/_shared/KpiChart.tsx +1 -1
  72. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/ChartTypePicker.tsx +1 -1
  73. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/DateRangePicker.tsx +1 -1
  74. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventRow.tsx +1 -1
  75. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/SqlPreview.tsx +2 -2
  76. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
  77. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +3 -3
  78. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +2 -2
  79. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
  80. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +4 -54
  81. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +178 -69
  82. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
  83. package/corpus/templates/analytics/app/root.tsx +28 -0
  84. package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
  85. package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
  86. package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
  87. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
  88. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
  89. package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
  90. package/corpus/templates/analytics/changelog/2026-07-24-analytics-charts-now-explain-slow-loads-and-offer-a-direct-r.md +6 -0
  91. package/corpus/templates/analytics/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  92. package/corpus/templates/analytics/changelog/2026-07-24-daily-dashboard-emails-now-load-signed-reports-more-reliably.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-07-24-manage-agent-is-clearly-marked-as-a-link-to-its-dedicated-pa.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  95. package/corpus/templates/analytics/netlify.toml +4 -0
  96. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -1
  97. package/corpus/templates/analytics/server/db/schema.ts +9 -0
  98. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +21 -0
  99. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
  100. package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
  101. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +11 -0
  102. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +54 -0
  103. package/corpus/templates/analytics/server/lib/dashboard-report.ts +235 -26
  104. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +4 -1
  105. package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
  106. package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
  107. package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
  108. package/corpus/templates/analytics/server/plugins/db.ts +19 -0
  109. package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +1 -1
  110. package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
  111. package/corpus/templates/assets/actions/generate-image.ts +28 -1
  112. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +376 -0
  113. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +14 -4
  114. package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
  115. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +164 -82
  116. package/corpus/templates/assets/app/global.css +1 -1
  117. package/corpus/templates/assets/app/i18n-data.ts +10 -0
  118. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +99 -10
  119. package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
  120. package/corpus/templates/assets/app/routes/library.tsx +21 -198
  121. package/corpus/templates/assets/changelog/2026-07-21-unified-asset-preview-click-any-asset-to-open-one-preview-wi.md +6 -0
  122. package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
  123. package/corpus/templates/assets/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  124. package/corpus/templates/assets/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  125. package/corpus/templates/assets/netlify.toml +1 -0
  126. package/corpus/templates/brain/app/components/layout/Layout.tsx +3 -0
  127. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +114 -51
  128. package/corpus/templates/brain/app/global.css +1 -1
  129. package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
  130. package/corpus/templates/brain/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  131. package/corpus/templates/brain/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  132. package/corpus/templates/brain/netlify.toml +1 -0
  133. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +110 -57
  134. package/corpus/templates/calendar/app/global.css +1 -1
  135. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
  136. package/corpus/templates/calendar/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  137. package/corpus/templates/calendar/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  138. package/corpus/templates/calendar/netlify.toml +1 -0
  139. package/corpus/templates/chat/app/components/layout/Layout.tsx +3 -0
  140. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  141. package/corpus/templates/chat/app/global.css +1 -1
  142. package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
  143. package/corpus/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  144. package/corpus/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  145. package/corpus/templates/chat/netlify.toml +1 -0
  146. package/corpus/templates/clips/app/components/library/library-layout.tsx +117 -37
  147. package/corpus/templates/clips/app/components/library/organization-switcher.tsx +2 -2
  148. package/corpus/templates/clips/app/components/player/video-player.tsx +16 -1
  149. package/corpus/templates/clips/app/global.css +1 -1
  150. package/corpus/templates/clips/app/hooks/use-view-tracking.ts +55 -12
  151. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
  152. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +6 -5
  153. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +7 -7
  154. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -5
  155. package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  156. package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
  157. package/corpus/templates/clips/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  158. package/corpus/templates/clips/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  159. package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
  160. package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
  161. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
  162. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
  163. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
  164. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
  165. package/corpus/templates/clips/desktop/src/styles.css +35 -3
  166. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
  167. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
  168. package/corpus/templates/clips/netlify.toml +1 -0
  169. package/corpus/templates/clips/server/plugins/db.ts +113 -68
  170. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +69 -49
  171. package/corpus/templates/content/app/global.css +1 -1
  172. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
  173. package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  174. package/corpus/templates/content/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  175. package/corpus/templates/content/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  176. package/corpus/templates/content/netlify.toml +1 -0
  177. package/corpus/templates/design/app/components/layout/Sidebar.tsx +115 -42
  178. package/corpus/templates/design/app/entry.client.tsx +14 -1
  179. package/corpus/templates/design/app/global.css +1 -1
  180. package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
  181. package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  182. package/corpus/templates/design/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  183. package/corpus/templates/design/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  184. package/corpus/templates/design/netlify.toml +1 -0
  185. package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
  186. package/corpus/templates/dispatch/app/global.css +1 -1
  187. package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  188. package/corpus/templates/dispatch/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  189. package/corpus/templates/dispatch/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  190. package/corpus/templates/dispatch/netlify.toml +1 -0
  191. package/corpus/templates/forms/app/components/layout/Layout.tsx +3 -0
  192. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +113 -82
  193. package/corpus/templates/forms/app/global.css +1 -1
  194. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
  195. package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  196. package/corpus/templates/forms/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  197. package/corpus/templates/forms/changelog/2026-07-24-forms-sidebar-actions-now-keep-search-language-feedback-and-.md +6 -0
  198. package/corpus/templates/forms/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  199. package/corpus/templates/forms/netlify.toml +1 -0
  200. package/corpus/templates/macros/app/components/DailyProgress.tsx +5 -5
  201. package/corpus/templates/macros/app/components/ExerciseCard.tsx +2 -2
  202. package/corpus/templates/macros/app/components/MealCard.tsx +2 -2
  203. package/corpus/templates/macros/app/components/WeightCard.tsx +2 -2
  204. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +92 -34
  205. package/corpus/templates/macros/app/components/ui/button.tsx +1 -2
  206. package/corpus/templates/macros/app/components/ui/card.tsx +1 -1
  207. package/corpus/templates/macros/app/global.css +1 -1
  208. package/corpus/templates/macros/app/routes/_index.tsx +1 -1
  209. package/corpus/templates/macros/app/routes/analytics.tsx +4 -7
  210. package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
  211. package/corpus/templates/macros/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  212. package/corpus/templates/macros/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  213. package/corpus/templates/macros/netlify.toml +1 -0
  214. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +125 -46
  215. package/corpus/templates/mail/app/global.css +1 -1
  216. package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
  217. package/corpus/templates/mail/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  218. package/corpus/templates/mail/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  219. package/corpus/templates/mail/netlify.toml +1 -0
  220. package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -0
  221. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +128 -65
  222. package/corpus/templates/plan/app/global.css +1 -1
  223. package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
  224. package/corpus/templates/plan/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  225. package/corpus/templates/plan/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  226. package/corpus/templates/plan/netlify.toml +1 -0
  227. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +116 -36
  228. package/corpus/templates/slides/app/global.css +1 -1
  229. package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
  230. package/corpus/templates/slides/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  231. package/corpus/templates/slides/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  232. package/corpus/templates/slides/netlify.toml +1 -0
  233. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
  234. package/corpus/templates/tasks/app/global.css +1 -1
  235. package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
  236. package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
  237. package/corpus/templates/tasks/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  238. package/corpus/templates/tasks/netlify.toml +1 -0
  239. package/corpus/toolkit/CHANGELOG.md +8 -0
  240. package/corpus/toolkit/README.md +8 -1
  241. package/corpus/toolkit/agent-native.eject.json +1 -1
  242. package/corpus/toolkit/package.json +1 -1
  243. package/corpus/toolkit/src/app-shell/index.ts +1 -0
  244. package/corpus/toolkit/src/app-shell/sidebar-footer-actions.tsx +48 -0
  245. package/corpus/toolkit/src/chat-history.css +52 -1
  246. package/corpus/toolkit/src/design-system/default-adapter.tsx +1 -1
  247. package/corpus/toolkit/src/ui/button.tsx +1 -2
  248. package/corpus/toolkit/src/ui/card.tsx +1 -1
  249. package/corpus/toolkit/src/ui/table.tsx +4 -11
  250. package/dist/agent/production-agent.d.ts.map +1 -1
  251. package/dist/agent/production-agent.js +4 -0
  252. package/dist/agent/production-agent.js.map +1 -1
  253. package/dist/agent/run-manager.d.ts +9 -0
  254. package/dist/agent/run-manager.d.ts.map +1 -1
  255. package/dist/agent/run-manager.js +25 -10
  256. package/dist/agent/run-manager.js.map +1 -1
  257. package/dist/agent/types.d.ts +2 -2
  258. package/dist/agent/types.d.ts.map +1 -1
  259. package/dist/agent/types.js.map +1 -1
  260. package/dist/cli/context-xray-local.js +1 -1
  261. package/dist/cli/context-xray-local.js.map +1 -1
  262. package/dist/cli/index.js +56 -17
  263. package/dist/cli/index.js.map +1 -1
  264. package/dist/cli/skills.d.ts.map +1 -1
  265. package/dist/cli/skills.js +5 -0
  266. package/dist/cli/skills.js.map +1 -1
  267. package/dist/cli/telemetry.d.ts +7 -0
  268. package/dist/cli/telemetry.d.ts.map +1 -1
  269. package/dist/cli/telemetry.js +91 -1
  270. package/dist/cli/telemetry.js.map +1 -1
  271. package/dist/client/AgentPanel.d.ts +7 -1
  272. package/dist/client/AgentPanel.d.ts.map +1 -1
  273. package/dist/client/AgentPanel.js +2 -2
  274. package/dist/client/AgentPanel.js.map +1 -1
  275. package/dist/client/CommandMenu.d.ts +2 -0
  276. package/dist/client/CommandMenu.d.ts.map +1 -1
  277. package/dist/client/CommandMenu.js +12 -1
  278. package/dist/client/CommandMenu.js.map +1 -1
  279. package/dist/client/agent-page/AgentTabsPage.d.ts +4 -0
  280. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  281. package/dist/client/agent-page/AgentTabsPage.js +8 -4
  282. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  283. package/dist/client/analytics.d.ts.map +1 -1
  284. package/dist/client/analytics.js +7 -0
  285. package/dist/client/analytics.js.map +1 -1
  286. package/dist/client/error-capture.d.ts.map +1 -1
  287. package/dist/client/error-capture.js +8 -1
  288. package/dist/client/error-capture.js.map +1 -1
  289. package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
  290. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  291. package/dist/client/extensions/ExtensionsListPage.js +6 -4
  292. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  293. package/dist/client/extensions/agent-native-extension-runtime.js +1 -1
  294. package/dist/client/extensions/agent-native-extension-runtime.js.map +1 -1
  295. package/dist/client/navigation/index.d.ts +1 -1
  296. package/dist/client/navigation/index.d.ts.map +1 -1
  297. package/dist/client/navigation/index.js +1 -1
  298. package/dist/client/navigation/index.js.map +1 -1
  299. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  300. package/dist/client/org/OrgSwitcher.js +9 -13
  301. package/dist/client/org/OrgSwitcher.js.map +1 -1
  302. package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
  303. package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
  304. package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
  305. package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
  306. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  307. package/dist/client/settings/SettingsPanel.js +10 -1
  308. package/dist/client/settings/SettingsPanel.js.map +1 -1
  309. package/dist/deploy/build.js +1 -0
  310. package/dist/deploy/build.js.map +1 -1
  311. package/dist/extensions/theme.js +1 -1
  312. package/dist/extensions/theme.js.map +1 -1
  313. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  314. package/dist/localization/default-messages.d.ts +2 -0
  315. package/dist/localization/default-messages.d.ts.map +1 -1
  316. package/dist/localization/default-messages.js +2 -0
  317. package/dist/localization/default-messages.js.map +1 -1
  318. package/dist/observability/routes.d.ts +3 -3
  319. package/dist/progress/routes.d.ts +1 -1
  320. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  321. package/dist/provider-api/actions/provider-api.d.ts +9 -9
  322. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  323. package/dist/resources/handlers.d.ts +1 -1
  324. package/dist/server/analytics.d.ts +9 -4
  325. package/dist/server/analytics.d.ts.map +1 -1
  326. package/dist/server/analytics.js +111 -22
  327. package/dist/server/analytics.js.map +1 -1
  328. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  329. package/dist/server/core-routes-plugin.js +8 -1
  330. package/dist/server/core-routes-plugin.js.map +1 -1
  331. package/dist/server/framework-request-handler.d.ts.map +1 -1
  332. package/dist/server/framework-request-handler.js +17 -27
  333. package/dist/server/framework-request-handler.js.map +1 -1
  334. package/dist/server/realtime-token.d.ts +1 -1
  335. package/dist/styles/agent-native.css +13 -0
  336. package/dist/templates/chat/app/components/layout/Layout.tsx +3 -0
  337. package/dist/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  338. package/dist/templates/chat/app/global.css +1 -1
  339. package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
  340. package/dist/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  341. package/dist/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  342. package/dist/templates/chat/netlify.toml +1 -0
  343. package/dist/templates/default/app/global.css +1 -1
  344. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  345. package/dist/tracking/error-capture.d.ts +14 -0
  346. package/dist/tracking/error-capture.d.ts.map +1 -0
  347. package/dist/tracking/error-capture.js +113 -0
  348. package/dist/tracking/error-capture.js.map +1 -0
  349. package/dist/tracking/index.d.ts +1 -0
  350. package/dist/tracking/index.d.ts.map +1 -1
  351. package/dist/tracking/index.js +1 -0
  352. package/dist/tracking/index.js.map +1 -1
  353. package/dist/vite/client.d.ts.map +1 -1
  354. package/dist/vite/client.js +2 -0
  355. package/dist/vite/client.js.map +1 -1
  356. package/docs/content/extensions.mdx +23 -4
  357. package/package.json +2 -2
  358. package/src/agent/production-agent.ts +4 -0
  359. package/src/agent/run-manager.ts +36 -10
  360. package/src/agent/types.ts +2 -0
  361. package/src/cli/context-xray-local.ts +1 -1
  362. package/src/cli/index.ts +63 -18
  363. package/src/cli/skills.ts +5 -0
  364. package/src/cli/telemetry.ts +118 -1
  365. package/src/client/AgentPanel.tsx +10 -0
  366. package/src/client/CommandMenu.tsx +13 -1
  367. package/src/client/agent-page/AgentTabsPage.tsx +39 -4
  368. package/src/client/analytics.ts +13 -0
  369. package/src/client/error-capture.ts +6 -1
  370. package/src/client/extensions/ExtensionsListPage.tsx +28 -8
  371. package/src/client/extensions/agent-native-extension-runtime.ts +1 -1
  372. package/src/client/navigation/index.ts +2 -0
  373. package/src/client/org/OrgSwitcher.tsx +14 -32
  374. package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  375. package/src/client/settings/SettingsPanel.tsx +10 -0
  376. package/src/deploy/build.ts +3 -0
  377. package/src/extensions/theme.ts +1 -1
  378. package/src/localization/default-messages.ts +2 -0
  379. package/src/server/analytics.ts +154 -25
  380. package/src/server/core-routes-plugin.ts +10 -1
  381. package/src/server/framework-request-handler.ts +17 -26
  382. package/src/styles/agent-native.css +13 -0
  383. package/src/templates/chat/app/components/layout/Layout.tsx +3 -0
  384. package/src/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  385. package/src/templates/chat/app/global.css +1 -1
  386. package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  387. package/src/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  388. package/src/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  389. package/src/templates/chat/netlify.toml +1 -0
  390. package/src/templates/default/app/global.css +1 -1
  391. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  392. package/src/tracking/error-capture.ts +151 -0
  393. package/src/tracking/index.ts +5 -0
  394. package/src/vite/client.ts +6 -0
  395. package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
  396. package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +0 -175
@@ -11,7 +11,6 @@ import {
11
11
  IconGripVertical,
12
12
  IconBook2,
13
13
  IconDatabase,
14
- IconReportAnalytics,
15
14
  IconSearch,
16
15
  IconArchive,
17
16
  IconActivity,
@@ -27,6 +26,7 @@ import {
27
26
  IconPlayerPlay,
28
27
  IconLayoutSidebarLeftCollapse,
29
28
  IconLayoutSidebarLeftExpand,
29
+ IconArrowUpRight,
30
30
  } from "@tabler/icons-react";
31
31
  import {
32
32
  useQuery,
@@ -62,7 +62,8 @@ type SidebarDashboard = {
62
62
  id: string;
63
63
  name: string;
64
64
  subviews?: DashboardSubview[];
65
- source: "static" | "sql";
65
+ source: "static" | "sql" | "analysis";
66
+ resourceId?: string;
66
67
  visibility?: Visibility;
67
68
  /** Id of the dashboard this one nests under in the sidebar, if any. */
68
69
  parentId?: string;
@@ -88,15 +89,16 @@ import {
88
89
  } from "@agent-native/core/client/agent-chat";
89
90
  import { appApiPath, appPath } from "@agent-native/core/client/api-path";
90
91
  import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
91
- import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
92
92
  import {
93
93
  callAction,
94
94
  useActionMutation,
95
95
  useChangeVersions,
96
96
  } from "@agent-native/core/client/hooks";
97
97
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
98
+ import { openCommandMenu } from "@agent-native/core/client/navigation";
98
99
  import { OrgSwitcher } from "@agent-native/core/client/org";
99
100
  import { FeedbackButton } from "@agent-native/core/client/ui";
101
+ import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
100
102
  import {
101
103
  ChatHistoryRail,
102
104
  type ChatHistoryItem,
@@ -143,24 +145,18 @@ import { useUserPref } from "@/hooks/use-user-pref";
143
145
  import { shouldRenderDashboardList } from "@/lib/dashboard-list-loading";
144
146
  import { usePopularity, popularityOf } from "@/lib/item-popularity";
145
147
  import {
146
- analysisDetailPrefetchKey,
147
148
  sqlDashboardPrefetchKey,
148
149
  type PrefetchSnapshot,
149
150
  } from "@/lib/prefetch-keys";
150
151
  import type { ResourceAccess } from "@/lib/resource-access";
151
152
 
152
- import { NewAnalysisDialog } from "./NewAnalysisDialog";
153
153
  import { NewDashboardDialog } from "./NewDashboardDialog";
154
154
  import { SidebarLoadError } from "./SidebarLoadError";
155
155
 
156
- type AnalysisHiddenFilter = "visible" | "hidden";
157
-
158
156
  const SIDEBAR_PREVIEW_COUNT = 5;
159
157
  const ASK_OPEN_KEY = "analytics-sidebar-ask-open";
160
158
  const DASHBOARD_SORT_MODE_KEY = "dashboard-sort-mode";
161
- const ANALYSIS_SORT_MODE_KEY = "analysis-sort-mode";
162
159
  const DASHBOARDS_OPEN_KEY = "analytics-sidebar-dashboards-open";
163
- const ANALYSES_OPEN_KEY = "analytics-sidebar-analyses-open";
164
160
  const SIDEBAR_COLLAPSE_KEY = "analytics.sidebar.collapsed";
165
161
  const SIDEBAR_SKELETON_CLASS =
166
162
  "bg-sidebar-foreground/12 dark:bg-sidebar-foreground/10";
@@ -191,6 +187,7 @@ const bottomItems = [
191
187
  icon: IconHierarchy2,
192
188
  labelKey: "settings.agentTitle",
193
189
  href: "/agent",
190
+ showExternalLinkIcon: true,
194
191
  },
195
192
  { icon: IconSettings, labelKey: "navigation.settings", href: "/settings" },
196
193
  ];
@@ -867,7 +864,10 @@ function SortableDashboardItem({
867
864
  visibility: Visibility,
868
865
  ) => Promise<void>;
869
866
  }) {
870
- const href = `/dashboards/${d.id}`;
867
+ const resourceId = d.resourceId ?? d.id;
868
+ const href =
869
+ d.source === "analysis" ? `/analyses/${resourceId}` : `/dashboards/${d.id}`;
870
+ const favoriteKey = d.source === "analysis" ? `analysis:${resourceId}` : d.id;
871
871
  const t = useT();
872
872
  const { mutateAsync: deleteView } = useDeleteDashboardView();
873
873
  const [deletingViewId, setDeletingViewId] = useState<string | null>(null);
@@ -908,7 +908,7 @@ function SortableDashboardItem({
908
908
  return (
909
909
  <SortableRow
910
910
  id={d.id}
911
- favoriteKey={d.id}
911
+ favoriteKey={favoriteKey}
912
912
  name={d.name}
913
913
  href={href}
914
914
  isActive={isActive}
@@ -916,7 +916,9 @@ function SortableDashboardItem({
916
916
  onToggleFavorite={onToggleFavorite}
917
917
  onDelete={() => onDelete(d)}
918
918
  onRename={(name) => onRename(d, name)}
919
- onArchive={onArchive ? () => onArchive(d) : undefined}
919
+ onArchive={
920
+ d.source === "analysis" || !onArchive ? undefined : () => onArchive(d)
921
+ }
920
922
  onPrefetch={() => onPrefetch?.(d)}
921
923
  visibility={d.visibility}
922
924
  onSetVisibility={
@@ -1068,33 +1070,6 @@ function SortableDashboardItem({
1068
1070
  );
1069
1071
  }
1070
1072
 
1071
- // Analyses reuse SortableRow directly — no wrapper component needed.
1072
-
1073
- const ANALYSIS_ORDER_KEY = "analysis-order";
1074
-
1075
- function getAnalysisOrder(): string[] {
1076
- if (typeof window === "undefined") return [];
1077
- try {
1078
- const raw = window.localStorage.getItem(ANALYSIS_ORDER_KEY);
1079
- if (!raw) return [];
1080
- const parsed = JSON.parse(raw);
1081
- return Array.isArray(parsed)
1082
- ? parsed.filter((x) => typeof x === "string")
1083
- : [];
1084
- } catch {
1085
- return [];
1086
- }
1087
- }
1088
-
1089
- function setAnalysisOrder(order: string[]): void {
1090
- if (typeof window === "undefined") return;
1091
- try {
1092
- window.localStorage.setItem(ANALYSIS_ORDER_KEY, JSON.stringify(order));
1093
- } catch {
1094
- // localStorage unavailable / quota — ignore, order is best-effort
1095
- }
1096
- }
1097
-
1098
1073
  const STATIC_DASHBOARD_RENAMES_KEY = "dashboard-name-overrides";
1099
1074
 
1100
1075
  function getStaticDashboardRenames(): Record<string, string> {
@@ -1161,10 +1136,7 @@ async function fetchSqlDashboards(
1161
1136
  }));
1162
1137
  }
1163
1138
 
1164
- async function fetchSidebarAnalyses(
1165
- t: (key: string) => string,
1166
- hidden: AnalysisHiddenFilter = "visible",
1167
- ): Promise<
1139
+ async function fetchSidebarAnalyses(t: (key: string) => string): Promise<
1168
1140
  {
1169
1141
  id: string;
1170
1142
  name: string;
@@ -1172,13 +1144,7 @@ async function fetchSidebarAnalyses(
1172
1144
  hiddenAt: string | null;
1173
1145
  }[]
1174
1146
  > {
1175
- const rows = await callAction(
1176
- "list-analyses",
1177
- {
1178
- ...(hidden === "hidden" ? { hidden: "hidden" } : {}),
1179
- } as Record<string, unknown>,
1180
- { method: "GET" },
1181
- );
1147
+ const rows = await callAction("list-analyses", {}, { method: "GET" });
1182
1148
  return (Array.isArray(rows) ? rows : [])
1183
1149
  .filter((a: any) => a && typeof a.id === "string" && a.id.length > 0)
1184
1150
  .map((a: any) => ({
@@ -1256,16 +1222,6 @@ async function fetchSqlDashboardForPrefetch(
1256
1222
  }
1257
1223
  }
1258
1224
 
1259
- async function fetchAnalysisDetailForPrefetch(id: string): Promise<unknown> {
1260
- try {
1261
- const data = await callAction("get-analysis", { id }, { method: "GET" });
1262
- if (!data || (data as Record<string, unknown>).error) return null;
1263
- return data;
1264
- } catch {
1265
- return null;
1266
- }
1267
- }
1268
-
1269
1225
  const ANALYTICS_ACTIVE_THREAD_KEY = `agent-chat-active-thread:${ANALYTICS_CHAT_STORAGE_KEY}`;
1270
1226
 
1271
1227
  function formatThreadAge(updatedAt: number) {
@@ -1311,7 +1267,13 @@ function persistedAnalyticsThreadId() {
1311
1267
  }
1312
1268
  }
1313
1269
 
1314
- function AnalyticsChatsSection({ isAskRoute }: { isAskRoute: boolean }) {
1270
+ function AnalyticsChatsSection({
1271
+ isAskRoute,
1272
+ open,
1273
+ }: {
1274
+ isAskRoute: boolean;
1275
+ open: boolean;
1276
+ }) {
1315
1277
  const navigate = useNavigate();
1316
1278
  const t = useT();
1317
1279
  const {
@@ -1413,57 +1375,63 @@ function AnalyticsChatsSection({ isAskRoute }: { isAskRoute: boolean }) {
1413
1375
  }
1414
1376
 
1415
1377
  return (
1416
- <div className="ms-4 min-w-0 space-y-0.5">
1417
- {chatsLoading &&
1418
- visibleThreads.length === 0 &&
1419
- Array.from({ length: 3 }).map((_, i) => (
1420
- <div
1421
- key={`chat-skeleton-${i}`}
1422
- className="flex items-center gap-2 px-3 py-1"
1423
- >
1424
- <Skeleton
1425
- className={cn(
1426
- "h-3.5 w-3.5 shrink-0 rounded-sm",
1427
- SIDEBAR_SKELETON_CLASS,
1428
- )}
1429
- />
1430
- <Skeleton
1431
- className={cn("h-3 rounded", SIDEBAR_SKELETON_CLASS)}
1432
- style={{ width: `${60 + ((i * 17) % 30)}%` }}
1433
- />
1434
- </div>
1435
- ))}
1436
- <ChatHistoryRail
1437
- items={chatItems}
1438
- activeId={displayedActiveThreadId}
1439
- onSelect={(threadId) => openThread(threadId)}
1440
- onNewChat={() => void handleNewChat()}
1441
- railLabels={{
1442
- newChat: t("chat.newChat"),
1443
- showMore: t("sidebar.showMore", {
1444
- count: Math.max(0, visibleThreads.length - SIDEBAR_PREVIEW_COUNT),
1445
- }),
1446
- showLess: t("sidebar.showLess"),
1447
- }}
1448
- renameMaxLength={160}
1449
- onTogglePin={(threadId) => {
1450
- const thread = visibleThreads.find((item) => item.id === threadId);
1451
- if (thread) void pinThread(threadId, !thread.pinnedAt);
1452
- }}
1453
- onRename={handleRenameThread}
1454
- onDelete={(threadId) => void handleArchiveThread(threadId)}
1455
- labels={{
1456
- options: (item) =>
1457
- t("chat.optionsFor", { title: item.titleText ?? "" }),
1458
- renameInput: (item) =>
1459
- t("chat.renameThread", { title: item.titleText ?? "" }),
1460
- rename: t("chat.renameChat"),
1461
- pin: t("chat.pinChat"),
1462
- unpin: t("chat.unpinChat"),
1463
- delete: t("chat.archiveChat"),
1464
- }}
1465
- className="min-w-0"
1466
- />
1378
+ <div
1379
+ className="an-chat-history-rail__collapse"
1380
+ data-state={open ? "open" : "closed"}
1381
+ aria-hidden={!open}
1382
+ >
1383
+ <div className="ms-4 min-w-0 space-y-0.5">
1384
+ {chatsLoading &&
1385
+ visibleThreads.length === 0 &&
1386
+ Array.from({ length: 3 }).map((_, i) => (
1387
+ <div
1388
+ key={`chat-skeleton-${i}`}
1389
+ className="flex items-center gap-2 px-3 py-1"
1390
+ >
1391
+ <Skeleton
1392
+ className={cn(
1393
+ "h-3.5 w-3.5 shrink-0 rounded-sm",
1394
+ SIDEBAR_SKELETON_CLASS,
1395
+ )}
1396
+ />
1397
+ <Skeleton
1398
+ className={cn("h-3 rounded", SIDEBAR_SKELETON_CLASS)}
1399
+ style={{ width: `${60 + ((i * 17) % 30)}%` }}
1400
+ />
1401
+ </div>
1402
+ ))}
1403
+ <ChatHistoryRail
1404
+ items={chatItems}
1405
+ activeId={displayedActiveThreadId}
1406
+ onSelect={(threadId) => openThread(threadId)}
1407
+ onNewChat={() => void handleNewChat()}
1408
+ railLabels={{
1409
+ newChat: t("chat.newChat"),
1410
+ showMore: t("sidebar.showMore", {
1411
+ count: Math.max(0, visibleThreads.length - SIDEBAR_PREVIEW_COUNT),
1412
+ }),
1413
+ showLess: t("sidebar.showLess"),
1414
+ }}
1415
+ renameMaxLength={160}
1416
+ onTogglePin={(threadId) => {
1417
+ const thread = visibleThreads.find((item) => item.id === threadId);
1418
+ if (thread) void pinThread(threadId, !thread.pinnedAt);
1419
+ }}
1420
+ onRename={handleRenameThread}
1421
+ onDelete={(threadId) => void handleArchiveThread(threadId)}
1422
+ labels={{
1423
+ options: (item) =>
1424
+ t("chat.optionsFor", { title: item.titleText ?? "" }),
1425
+ renameInput: (item) =>
1426
+ t("chat.renameThread", { title: item.titleText ?? "" }),
1427
+ rename: t("chat.renameChat"),
1428
+ pin: t("chat.pinChat"),
1429
+ unpin: t("chat.unpinChat"),
1430
+ delete: t("chat.archiveChat"),
1431
+ }}
1432
+ className="min-w-0"
1433
+ />
1434
+ </div>
1467
1435
  </div>
1468
1436
  );
1469
1437
  }
@@ -1510,20 +1478,8 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1510
1478
  );
1511
1479
  const [dashShowAll, setDashShowAll] = useState(false);
1512
1480
  const [dashFilter, setDashFilter] = useState<SidebarVisibilityFilter>("all");
1513
- const [analysesOpen, setAnalysesOpen] = useState(
1514
- () =>
1515
- getStoredBooleanPreference(ANALYSES_OPEN_KEY) ??
1516
- activeAnalysisId !== null,
1517
- );
1518
- const [analysesShowAll, setAnalysesShowAll] = useState(false);
1519
- const [analysisFilter, setAnalysisFilter] =
1520
- useState<SidebarVisibilityFilter>("all");
1521
- const [analysisHiddenFilter, setAnalysisHiddenFilter] =
1522
- useState<AnalysisHiddenFilter>("visible");
1523
1481
  const [dashboardSortMode, setDashboardSortModeState] =
1524
1482
  useState<SidebarSortMode>(() => getStoredSortMode(DASHBOARD_SORT_MODE_KEY));
1525
- const [analysisSortMode, setAnalysisSortModeState] =
1526
- useState<SidebarSortMode>(() => getStoredSortMode(ANALYSIS_SORT_MODE_KEY));
1527
1483
  const { data: popularity, isReady: popularityReady } = usePopularity();
1528
1484
 
1529
1485
  useEffect(() => {
@@ -1542,7 +1498,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1542
1498
  const { mutateAsync: renameDashboard } =
1543
1499
  useActionMutation("rename-dashboard");
1544
1500
  const { mutateAsync: renameAnalysis } = useActionMutation("rename-analysis");
1545
- const { mutateAsync: hideAnalysisMut } = useActionMutation("hide-analysis");
1546
1501
  const { mutateAsync: setResourceVisibility } = useActionMutation(
1547
1502
  "set-resource-visibility",
1548
1503
  );
@@ -1580,10 +1535,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1580
1535
  const [dashboardOrderState, setDashboardOrderState] = useState(() =>
1581
1536
  typeof window === "undefined" ? [] : getDashboardOrder(),
1582
1537
  );
1583
- const [analysisOrderState, setAnalysisOrderState] = useState(() =>
1584
- typeof window === "undefined" ? [] : getAnalysisOrder(),
1585
- );
1586
-
1587
1538
  // Server-backed favorites
1588
1539
  const {
1589
1540
  data: favoritesData,
@@ -1614,11 +1565,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1614
1565
  setDashboardSortModeState(mode);
1615
1566
  }, []);
1616
1567
 
1617
- const setAnalysisSortMode = useCallback((mode: SidebarSortMode) => {
1618
- setStoredSortMode(ANALYSIS_SORT_MODE_KEY, mode);
1619
- setAnalysisSortModeState(mode);
1620
- }, []);
1621
-
1622
1568
  useEffect(() => {
1623
1569
  if (getStoredBooleanPreference(ASK_OPEN_KEY) === null) {
1624
1570
  setAskOpen(isAskRoute);
@@ -1631,12 +1577,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1631
1577
  }
1632
1578
  }, [activeDashboardId]);
1633
1579
 
1634
- useEffect(() => {
1635
- if (getStoredBooleanPreference(ANALYSES_OPEN_KEY) === null) {
1636
- setAnalysesOpen(activeAnalysisId !== null);
1637
- }
1638
- }, [activeAnalysisId]);
1639
-
1640
1580
  const toggleAskOpen = useCallback(() => {
1641
1581
  setAskOpen((current) => {
1642
1582
  const next = !current;
@@ -1653,14 +1593,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1653
1593
  });
1654
1594
  }, []);
1655
1595
 
1656
- const toggleAnalysesOpen = useCallback(() => {
1657
- setAnalysesOpen((current) => {
1658
- const next = !current;
1659
- setStoredBoolean(ANALYSES_OPEN_KEY, next);
1660
- return next;
1661
- });
1662
- }, []);
1663
-
1664
1596
  // Fold per-source counters into sidebar list query keys so agent-driven
1665
1597
  // create/rename/archive/delete shows up without a manual refresh. We
1666
1598
  // Domain counters keep these lists targeted. Folding the generic `action`
@@ -1671,9 +1603,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1671
1603
  );
1672
1604
  const analysesSync = useSettledSyncVersion(useChangeVersions(["analyses"]));
1673
1605
  const dashboardsSyncRef = useRef(dashboardsSync);
1674
- const analysesSyncRef = useRef(analysesSync);
1675
1606
  dashboardsSyncRef.current = dashboardsSync;
1676
- analysesSyncRef.current = analysesSync;
1677
1607
 
1678
1608
  const {
1679
1609
  data: sqlDashboards = [],
@@ -1694,52 +1624,12 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1694
1624
  isError: analysesError,
1695
1625
  refetch: refetchAnalyses,
1696
1626
  } = useQuery({
1697
- queryKey: ["analyses-sidebar", analysesSync, analysisHiddenFilter],
1698
- queryFn: () => fetchSidebarAnalyses(t, analysisHiddenFilter),
1627
+ queryKey: ["analyses-sidebar", analysesSync],
1628
+ queryFn: () => fetchSidebarAnalyses(t),
1699
1629
  staleTime: 30_000,
1700
1630
  placeholderData: (prev) => prev,
1701
1631
  });
1702
1632
 
1703
- const sortedAnalyses = useMemo(() => {
1704
- if (analysisSortMode === "alphabetical") {
1705
- return sortByName(analysesList);
1706
- }
1707
- if (analysisSortMode === "manual" && analysisOrderState.length > 0) {
1708
- return applyOrder(analysesList, analysisOrderState);
1709
- }
1710
- return [...analysesList].sort((a, b) => {
1711
- const aFav = favoriteIds.has(`analysis:${a.id}`) ? 0 : 1;
1712
- const bFav = favoriteIds.has(`analysis:${b.id}`) ? 0 : 1;
1713
- if (aFav !== bFav) return aFav - bFav;
1714
- const aPop = popularityOf(popularity, "analysis", a.id);
1715
- const bPop = popularityOf(popularity, "analysis", b.id);
1716
- if (aPop !== bPop) return bPop - aPop;
1717
- return a.name.localeCompare(b.name);
1718
- });
1719
- }, [
1720
- analysesList,
1721
- analysisSortMode,
1722
- analysisOrderState,
1723
- popularity,
1724
- favoriteIds,
1725
- ]);
1726
-
1727
- const filteredAnalyses = useMemo(
1728
- () =>
1729
- sortedAnalyses.filter((analysis) =>
1730
- matchesVisibilityFilter(analysis, analysisFilter),
1731
- ),
1732
- [sortedAnalyses, analysisFilter],
1733
- );
1734
-
1735
- const displayedAnalyses = useMemo(
1736
- () =>
1737
- analysesShowAll
1738
- ? filteredAnalyses
1739
- : filteredAnalyses.slice(0, SIDEBAR_PREVIEW_COUNT),
1740
- [filteredAnalyses, analysesShowAll],
1741
- );
1742
-
1743
1633
  // Only the active dashboard can display saved views in the sidebar, so avoid
1744
1634
  // issuing one request per dashboard on every sidebar mount.
1745
1635
  const { views: activeDashboardViews = [] } = useDashboardViews(
@@ -1772,24 +1662,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1772
1662
  [dashboardsSync, queryClient, t],
1773
1663
  );
1774
1664
 
1775
- const prefetchAnalysis = useCallback(
1776
- (id: string) => {
1777
- const queryKey = analysisDetailPrefetchKey(id);
1778
- const cached =
1779
- queryClient.getQueryData<PrefetchSnapshot<unknown | null>>(queryKey);
1780
- void import("@/pages/analyses/AnalysisDetail");
1781
- void queryClient.prefetchQuery({
1782
- queryKey,
1783
- queryFn: async () => ({
1784
- data: await fetchAnalysisDetailForPrefetch(id),
1785
- syncVersion: analysesSync,
1786
- }),
1787
- staleTime: cached?.syncVersion === analysesSync ? 30_000 : 0,
1788
- });
1789
- },
1790
- [analysesSync, queryClient],
1791
- );
1792
-
1793
1665
  const visibleDashboards = useMemo<SidebarDashboard[]>(() => {
1794
1666
  const staticItems: SidebarDashboard[] = dashboards
1795
1667
  .filter((d) => !hiddenIds.has(d.id))
@@ -1806,7 +1678,14 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1806
1678
  visibility: d.visibility,
1807
1679
  parentId: d.parentId,
1808
1680
  }));
1809
- const all = [...staticItems, ...sqlItems];
1681
+ const analysisItems: SidebarDashboard[] = analysesList.map((a) => ({
1682
+ id: `analysis:${a.id}`,
1683
+ resourceId: a.id,
1684
+ name: a.name,
1685
+ source: "analysis",
1686
+ visibility: a.visibility,
1687
+ }));
1688
+ const all = [...staticItems, ...sqlItems, ...analysisItems];
1810
1689
  if (dashboardSortMode === "alphabetical") {
1811
1690
  return sortByName(all);
1812
1691
  }
@@ -1814,11 +1693,25 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1814
1693
  return applyOrder(all, dashboardOrderState);
1815
1694
  }
1816
1695
  return [...all].sort((a, b) => {
1817
- const aFav = favoriteIds.has(a.id) ? 0 : 1;
1818
- const bFav = favoriteIds.has(b.id) ? 0 : 1;
1696
+ const aResourceId = a.resourceId ?? a.id;
1697
+ const bResourceId = b.resourceId ?? b.id;
1698
+ const aFavoriteKey =
1699
+ a.source === "analysis" ? `analysis:${aResourceId}` : a.id;
1700
+ const bFavoriteKey =
1701
+ b.source === "analysis" ? `analysis:${bResourceId}` : b.id;
1702
+ const aFav = favoriteIds.has(aFavoriteKey) ? 0 : 1;
1703
+ const bFav = favoriteIds.has(bFavoriteKey) ? 0 : 1;
1819
1704
  if (aFav !== bFav) return aFav - bFav;
1820
- const aPop = popularityOf(popularity, "dashboard", a.id);
1821
- const bPop = popularityOf(popularity, "dashboard", b.id);
1705
+ const aPop = popularityOf(
1706
+ popularity,
1707
+ a.source === "analysis" ? "analysis" : "dashboard",
1708
+ aResourceId,
1709
+ );
1710
+ const bPop = popularityOf(
1711
+ popularity,
1712
+ b.source === "analysis" ? "analysis" : "dashboard",
1713
+ bResourceId,
1714
+ );
1822
1715
  if (aPop !== bPop) return bPop - aPop;
1823
1716
  return a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
1824
1717
  });
@@ -1829,6 +1722,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1829
1722
  dashboardSortMode,
1830
1723
  dashboardOrderState,
1831
1724
  sqlDashboards,
1725
+ analysesList,
1832
1726
  popularity,
1833
1727
  ]);
1834
1728
 
@@ -1905,6 +1799,12 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1905
1799
 
1906
1800
  const handleDashboardDelete = useCallback(
1907
1801
  async (d: SidebarDashboard) => {
1802
+ if (d.source === "analysis") {
1803
+ await deleteAnalysisMut({ id: d.resourceId ?? d.id });
1804
+ queryClient.invalidateQueries({ queryKey: ["analyses-sidebar"] });
1805
+ queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
1806
+ return;
1807
+ }
1908
1808
  if (d.source === "static") {
1909
1809
  hideDashboard(d.id);
1910
1810
  setHiddenIds(getHiddenDashboards());
@@ -1931,11 +1831,12 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1931
1831
  throw err;
1932
1832
  }
1933
1833
  },
1934
- [queryClient, deleteSqlDashboard],
1834
+ [deleteAnalysisMut, deleteSqlDashboard, queryClient],
1935
1835
  );
1936
1836
 
1937
1837
  const handleDashboardArchive = useCallback(
1938
1838
  async (d: SidebarDashboard) => {
1839
+ if (d.source === "analysis") return;
1939
1840
  if (d.source === "static") {
1940
1841
  // Static dashboards can only be hidden, not archived; route to delete
1941
1842
  // (which calls hideDashboard for static items).
@@ -1970,6 +1871,16 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1970
1871
  const trimmed = name.trim();
1971
1872
  if (!trimmed || trimmed === d.name) return;
1972
1873
 
1874
+ if (d.source === "analysis") {
1875
+ await renameAnalysis({ id: d.resourceId ?? d.id, name: trimmed });
1876
+ queryClient.invalidateQueries({ queryKey: ["analyses-sidebar"] });
1877
+ queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
1878
+ queryClient.invalidateQueries({
1879
+ queryKey: ["analysis-detail", d.resourceId ?? d.id],
1880
+ });
1881
+ return;
1882
+ }
1883
+
1973
1884
  if (d.source === "static") {
1974
1885
  setStaticDashboardRenamesState((prev) => {
1975
1886
  const next = { ...prev, [d.id]: trimmed };
@@ -2001,38 +1912,27 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2001
1912
  throw err;
2002
1913
  }
2003
1914
  },
2004
- [queryClient, renameDashboard],
2005
- );
2006
-
2007
- const handleAnalysisDelete = useCallback(
2008
- async (a: { id: string; name: string }) => {
2009
- const queryKey = ["analyses-sidebar"] as const;
2010
- const prev = getQuerySnapshots<{ id: string; name: string }[]>(
2011
- queryClient,
2012
- queryKey,
2013
- );
2014
- queryClient.setQueriesData<{ id: string; name: string }[]>(
2015
- { queryKey },
2016
- (old) => (old ?? []).filter((item) => item.id !== a.id),
2017
- );
2018
- try {
2019
- await deleteAnalysisMut({ id: a.id });
2020
- queryClient.removeQueries({
2021
- queryKey: analysisDetailPrefetchKey(a.id),
2022
- });
2023
- queryClient.invalidateQueries({ queryKey });
2024
- queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
2025
- } catch (err) {
2026
- restoreQuerySnapshots(queryClient, prev);
2027
- throw err;
2028
- }
2029
- },
2030
- [queryClient, deleteAnalysisMut],
1915
+ [queryClient, renameAnalysis, renameDashboard],
2031
1916
  );
2032
1917
 
2033
1918
  const handleDashboardSetVisibility = useCallback(
2034
1919
  async (d: SidebarDashboard, visibility: Visibility) => {
2035
1920
  if (d.source === "static") return;
1921
+ if (d.source === "analysis") {
1922
+ await setResourceVisibility({
1923
+ resourceType: "analysis",
1924
+ resourceId: d.resourceId ?? d.id,
1925
+ visibility,
1926
+ } as any);
1927
+ queryClient.invalidateQueries({ queryKey: ["analyses-sidebar"] });
1928
+ queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
1929
+ toast.success(
1930
+ visibility === "org"
1931
+ ? t("sidebar.nameSharedWithOrg", { name: d.name })
1932
+ : t("sidebar.nameMadePrivate", { name: d.name }),
1933
+ );
1934
+ return;
1935
+ }
2036
1936
  const queryKey = [
2037
1937
  "sql-dashboards-sidebar",
2038
1938
  dashboardsSyncRef.current,
@@ -2066,143 +1966,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2066
1966
  [queryClient, setResourceVisibility, t],
2067
1967
  );
2068
1968
 
2069
- const handleAnalysisSetVisibility = useCallback(
2070
- async (a: { id: string; name: string }, visibility: Visibility) => {
2071
- const sidebarKey = ["analyses-sidebar", analysesSyncRef.current] as const;
2072
- const listKey = ["analyses-list"] as const;
2073
- const prevSidebar = getQuerySnapshots<
2074
- { id: string; name: string; visibility?: Visibility }[]
2075
- >(queryClient, sidebarKey);
2076
- const prevList = getQuerySnapshots<any[]>(queryClient, listKey);
2077
- queryClient.setQueriesData<
2078
- { id: string; name: string; visibility?: Visibility }[]
2079
- >({ queryKey: sidebarKey }, (old) =>
2080
- (old ?? []).map((item) =>
2081
- item.id === a.id ? { ...item, visibility } : item,
2082
- ),
2083
- );
2084
- queryClient.setQueriesData<any[]>({ queryKey: listKey }, (old) =>
2085
- (old ?? []).map((item) =>
2086
- item.id === a.id ? { ...item, visibility } : item,
2087
- ),
2088
- );
2089
- try {
2090
- await setResourceVisibility({
2091
- resourceType: "analysis",
2092
- resourceId: a.id,
2093
- visibility,
2094
- } as any);
2095
- queryClient.invalidateQueries({ queryKey: sidebarKey });
2096
- queryClient.invalidateQueries({ queryKey: listKey });
2097
- toast.success(
2098
- visibility === "org"
2099
- ? t("sidebar.nameSharedWithOrg", { name: a.name })
2100
- : t("sidebar.nameMadePrivate", { name: a.name }),
2101
- );
2102
- } catch (err) {
2103
- restoreQuerySnapshots(queryClient, prevSidebar);
2104
- restoreQuerySnapshots(queryClient, prevList);
2105
- throw err;
2106
- }
2107
- },
2108
- [queryClient, setResourceVisibility, t],
2109
- );
2110
-
2111
- const handleAnalysisRename = useCallback(
2112
- async (a: { id: string; name: string }, name: string) => {
2113
- const trimmed = name.trim();
2114
- if (!trimmed || trimmed === a.name) return;
2115
-
2116
- const sidebarKey = ["analyses-sidebar"] as const;
2117
- const listKey = ["analyses-list"] as const;
2118
- const detailKey = ["analysis-detail", a.id] as const;
2119
- const prevSidebar = getQuerySnapshots<{ id: string; name: string }[]>(
2120
- queryClient,
2121
- sidebarKey,
2122
- );
2123
- const prevList = getQuerySnapshots<any[]>(queryClient, listKey);
2124
- const prevDetail = getQuerySnapshots<any>(queryClient, detailKey);
2125
-
2126
- queryClient.setQueriesData<{ id: string; name: string }[]>(
2127
- { queryKey: sidebarKey },
2128
- (old) =>
2129
- (old ?? []).map((item) =>
2130
- item.id === a.id ? { ...item, name: trimmed } : item,
2131
- ),
2132
- );
2133
- queryClient.setQueriesData<any[]>({ queryKey: listKey }, (old) =>
2134
- (old ?? []).map((item) =>
2135
- item.id === a.id ? { ...item, name: trimmed } : item,
2136
- ),
2137
- );
2138
- queryClient.setQueriesData<any>({ queryKey: detailKey }, (old: any) =>
2139
- old ? { ...old, name: trimmed } : old,
2140
- );
2141
-
2142
- try {
2143
- await renameAnalysis({ id: a.id, name: trimmed });
2144
- queryClient.removeQueries({
2145
- queryKey: analysisDetailPrefetchKey(a.id),
2146
- });
2147
- queryClient.invalidateQueries({ queryKey: sidebarKey });
2148
- queryClient.invalidateQueries({ queryKey: listKey });
2149
- queryClient.invalidateQueries({ queryKey: detailKey });
2150
- } catch (err) {
2151
- restoreQuerySnapshots(queryClient, prevSidebar);
2152
- restoreQuerySnapshots(queryClient, prevList);
2153
- restoreQuerySnapshots(queryClient, prevDetail);
2154
- throw err;
2155
- }
2156
- },
2157
- [queryClient, renameAnalysis],
2158
- );
2159
-
2160
- const handleAnalysisHide = useCallback(
2161
- async (a: { id: string; name: string }) => {
2162
- const sidebarKey = ["analyses-sidebar"] as const;
2163
- const prev = getQuerySnapshots<{ id: string }[]>(queryClient, sidebarKey);
2164
- // Optimistically drop it from the visible list (the hidden-filter
2165
- // variant is invalidated below to pick it up).
2166
- queryClient.setQueriesData<{ id: string }[]>(
2167
- { queryKey: sidebarKey },
2168
- (old) => (old ?? []).filter((item) => item.id !== a.id),
2169
- );
2170
- try {
2171
- await hideAnalysisMut({ id: a.id, hidden: true });
2172
- queryClient.invalidateQueries({ queryKey: sidebarKey });
2173
- queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
2174
- toast.success(t("sidebar.nameHidden", { name: a.name }));
2175
- } catch (err) {
2176
- restoreQuerySnapshots(queryClient, prev);
2177
- throw err;
2178
- }
2179
- },
2180
- [queryClient, hideAnalysisMut, t],
2181
- );
2182
-
2183
- const handleAnalysisUnhide = useCallback(
2184
- async (a: { id: string; name: string }) => {
2185
- const sidebarKey = ["analyses-sidebar"] as const;
2186
- const prev = getQuerySnapshots<{ id: string }[]>(queryClient, sidebarKey);
2187
- // Optimistically drop it from the hidden list (the visible variant is
2188
- // invalidated below to pick it up).
2189
- queryClient.setQueriesData<{ id: string }[]>(
2190
- { queryKey: sidebarKey },
2191
- (old) => (old ?? []).filter((item) => item.id !== a.id),
2192
- );
2193
- try {
2194
- await hideAnalysisMut({ id: a.id, hidden: false });
2195
- queryClient.invalidateQueries({ queryKey: sidebarKey });
2196
- queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
2197
- toast.success(t("sidebar.nameUnhidden", { name: a.name }));
2198
- } catch (err) {
2199
- restoreQuerySnapshots(queryClient, prev);
2200
- throw err;
2201
- }
2202
- },
2203
- [queryClient, hideAnalysisMut, t],
2204
- );
2205
-
2206
1969
  const sensors = useSensors(
2207
1970
  useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
2208
1971
  useSensor(KeyboardSensor, {
@@ -2226,24 +1989,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2226
1989
  [setDashboardSortMode, dashboardRenderOrderIds],
2227
1990
  );
2228
1991
 
2229
- const handleAnalysisDragEnd = useCallback(
2230
- (event: DragEndEvent) => {
2231
- const { active, over } = event;
2232
- if (!over || active.id === over.id) return;
2233
- setAnalysisSortMode("manual");
2234
- setAnalysisOrderState((prev) => {
2235
- const ids = prev.length > 0 ? prev : sortedAnalyses.map((a) => a.id);
2236
- const oldIndex = ids.indexOf(active.id as string);
2237
- const newIndex = ids.indexOf(over.id as string);
2238
- if (oldIndex === -1 || newIndex === -1) return prev;
2239
- const newOrder = arrayMove(ids, oldIndex, newIndex);
2240
- setAnalysisOrder(newOrder);
2241
- return newOrder;
2242
- });
2243
- },
2244
- [setAnalysisSortMode, sortedAnalyses],
2245
- );
2246
-
2247
1992
  const handleResizeStart = useCallback(
2248
1993
  (e: React.MouseEvent) => {
2249
1994
  if (effectiveCollapsed) return;
@@ -2283,7 +2028,8 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2283
2028
 
2284
2029
  const isAdhocActive =
2285
2030
  location.pathname.startsWith("/adhoc") ||
2286
- location.pathname.startsWith("/dashboards");
2031
+ location.pathname.startsWith("/dashboards") ||
2032
+ location.pathname.startsWith("/analyses");
2287
2033
 
2288
2034
  useEffect(() => {
2289
2035
  if (typeof window === "undefined") return;
@@ -2322,12 +2068,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2322
2068
  href: "/dashboards",
2323
2069
  active: isAdhocActive,
2324
2070
  },
2325
- {
2326
- icon: IconReportAnalytics,
2327
- label: t("navigation.analyses"),
2328
- href: "/analyses",
2329
- active: location.pathname.startsWith("/analyses"),
2330
- },
2331
2071
  {
2332
2072
  icon: IconPlayerPlay,
2333
2073
  label: t("navigation.sessions"),
@@ -2372,6 +2112,63 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2372
2112
  },
2373
2113
  ];
2374
2114
 
2115
+ const footerSearch = (
2116
+ <Tooltip>
2117
+ <TooltipTrigger asChild>
2118
+ <button
2119
+ type="button"
2120
+ onClick={openCommandMenu}
2121
+ aria-label={t("sidebar.search")}
2122
+ className="flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-sidebar-accent/50 hover:text-foreground"
2123
+ >
2124
+ <IconSearch className="h-4 w-4" />
2125
+ </button>
2126
+ </TooltipTrigger>
2127
+ <TooltipContent side="top">
2128
+ {t("sidebar.searchShortcut", {
2129
+ shortcut: `${shortcutModifierLabel()}+K`,
2130
+ })}
2131
+ </TooltipContent>
2132
+ </Tooltip>
2133
+ );
2134
+ const footerTranslate = (
2135
+ <LanguagePicker variant="ghost-icon" label={t("settings.languageLabel")} />
2136
+ );
2137
+ const footerCollapse = !mobile ? (
2138
+ <Tooltip>
2139
+ <TooltipTrigger asChild>
2140
+ <button
2141
+ type="button"
2142
+ onClick={() => setSidebarCollapsed(!effectiveCollapsed)}
2143
+ aria-label={
2144
+ effectiveCollapsed
2145
+ ? t("sidebar.expandSidebar")
2146
+ : t("sidebar.collapseSidebar")
2147
+ }
2148
+ className="flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-sidebar-accent/50 hover:text-foreground"
2149
+ >
2150
+ {effectiveCollapsed ? (
2151
+ <IconLayoutSidebarLeftExpand className="h-4 w-4 rtl:-scale-x-100" />
2152
+ ) : (
2153
+ <IconLayoutSidebarLeftCollapse className="h-4 w-4 rtl:-scale-x-100" />
2154
+ )}
2155
+ </button>
2156
+ </TooltipTrigger>
2157
+ <TooltipContent side="top">
2158
+ {effectiveCollapsed
2159
+ ? t("sidebar.expandSidebar")
2160
+ : t("sidebar.collapseSidebar")}
2161
+ </TooltipContent>
2162
+ </Tooltip>
2163
+ ) : null;
2164
+ const footerFeedback = (
2165
+ <FeedbackButton
2166
+ variant={effectiveCollapsed ? "icon" : "sidebar"}
2167
+ side="right"
2168
+ className={effectiveCollapsed ? "h-8 w-8" : "min-w-0"}
2169
+ />
2170
+ );
2171
+
2375
2172
  return (
2376
2173
  <div
2377
2174
  className="relative flex h-full min-w-0 flex-col overflow-hidden border-r border-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out"
@@ -2387,24 +2184,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2387
2184
  )}
2388
2185
  {effectiveCollapsed ? (
2389
2186
  <>
2390
- <div className="flex h-12 shrink-0 items-center justify-center border-b border-border px-1">
2391
- <Tooltip>
2392
- <TooltipTrigger asChild>
2393
- <button
2394
- type="button"
2395
- onClick={() => setSidebarCollapsed(false)}
2396
- aria-label={t("sidebar.expandSidebar")}
2397
- className="flex h-10 w-10 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-sidebar-accent/50 hover:text-foreground"
2398
- >
2399
- <IconLayoutSidebarLeftExpand className="h-4 w-4 rtl:-scale-x-100" />
2400
- </button>
2401
- </TooltipTrigger>
2402
- <TooltipContent side="right">
2403
- {t("sidebar.expandSidebar")}
2404
- </TooltipContent>
2405
- </Tooltip>
2406
- </div>
2407
-
2408
2187
  <nav className="flex min-h-0 flex-1 flex-col items-center gap-1 overflow-y-auto px-1 py-2">
2409
2188
  {collapsedNavItems.map((item) => {
2410
2189
  const Icon = item.icon;
@@ -2430,6 +2209,13 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2430
2209
  );
2431
2210
  })}
2432
2211
  </nav>
2212
+ <SidebarFooterActions
2213
+ collapsed
2214
+ feedback={footerFeedback}
2215
+ translate={footerTranslate}
2216
+ search={footerSearch}
2217
+ collapse={footerCollapse}
2218
+ />
2433
2219
  </>
2434
2220
  ) : (
2435
2221
  <>
@@ -2454,23 +2240,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2454
2240
  {t("navigation.brand")}
2455
2241
  </span>
2456
2242
  </Link>
2457
- {!mobile && (
2458
- <Tooltip>
2459
- <TooltipTrigger asChild>
2460
- <button
2461
- type="button"
2462
- onClick={() => setSidebarCollapsed(true)}
2463
- aria-label={t("sidebar.collapseSidebar")}
2464
- className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-sidebar-accent/50 hover:text-foreground"
2465
- >
2466
- <IconLayoutSidebarLeftCollapse className="h-4 w-4 rtl:-scale-x-100" />
2467
- </button>
2468
- </TooltipTrigger>
2469
- <TooltipContent side="right">
2470
- {t("sidebar.collapseSidebar")}
2471
- </TooltipContent>
2472
- </Tooltip>
2473
- )}
2474
2243
  </div>
2475
2244
  <div className="flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden py-2">
2476
2245
  <nav className="grid min-w-0 items-start px-2 text-sm font-medium lg:px-4 space-y-1">
@@ -2524,7 +2293,9 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2524
2293
  />
2525
2294
  </button>
2526
2295
  </div>
2527
- {askOpen && <AnalyticsChatsSection isAskRoute={isAskRoute} />}
2296
+ {askOpen && isAskRoute ? (
2297
+ <AnalyticsChatsSection isAskRoute open />
2298
+ ) : null}
2528
2299
  </div>
2529
2300
 
2530
2301
  {/* Sessions link */}
@@ -2665,7 +2436,11 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2665
2436
  <Fragment key={d.id}>
2666
2437
  <SortableDashboardItem
2667
2438
  d={d}
2668
- isActive={activeDashboardId === d.id}
2439
+ isActive={
2440
+ d.source === "analysis"
2441
+ ? activeAnalysisId === d.resourceId
2442
+ : activeDashboardId === d.id
2443
+ }
2669
2444
  location={location}
2670
2445
  favoriteIds={favoriteIds}
2671
2446
  onToggleFavorite={toggleFavorite}
@@ -2683,7 +2458,10 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2683
2458
  key={child.id}
2684
2459
  d={child}
2685
2460
  isActive={
2686
- activeDashboardId === child.id
2461
+ child.source === "analysis"
2462
+ ? activeAnalysisId ===
2463
+ child.resourceId
2464
+ : activeDashboardId === child.id
2687
2465
  }
2688
2466
  location={location}
2689
2467
  favoriteIds={favoriteIds}
@@ -2746,156 +2524,30 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2746
2524
  onRetry={() => void refetchSqlDashboards()}
2747
2525
  />
2748
2526
  )}
2749
- <NewDashboardDialog />
2750
- </div>
2751
- </SortableContext>
2752
- </DndContext>
2753
- )}
2754
- </div>
2755
-
2756
- {/* Analyses section */}
2757
- <div className="order-3 group/section min-w-0 space-y-1">
2758
- <div
2759
- className={cn(
2760
- "flex w-full min-w-0 items-center rounded-lg transition-colors hover:text-primary",
2761
- location.pathname.startsWith("/analyses")
2762
- ? "text-sidebar-accent-foreground"
2763
- : "text-muted-foreground hover:bg-sidebar-accent/50",
2764
- )}
2765
- >
2766
- <button
2767
- type="button"
2768
- onClick={toggleAnalysesOpen}
2769
- className="flex min-w-0 flex-1 items-center gap-3 px-3 py-2 text-start"
2770
- aria-expanded={analysesOpen}
2771
- >
2772
- <IconReportAnalytics className="h-4 w-4 shrink-0" />
2773
- <span className="min-w-0 flex-1 truncate">
2774
- {t("navigation.analyses")}
2775
- </span>
2776
- </button>
2777
- <SidebarSectionSettingsPopover
2778
- label={t("navigation.analyses")}
2779
- sortMode={analysisSortMode}
2780
- onSortModeChange={setAnalysisSortMode}
2781
- visibilityFilter={analysisFilter}
2782
- onVisibilityFilterChange={setAnalysisFilter}
2783
- showHidden={analysisHiddenFilter === "hidden"}
2784
- onShowHiddenChange={(checked) =>
2785
- setAnalysisHiddenFilter(checked ? "hidden" : "visible")
2786
- }
2787
- />
2788
- <button
2789
- type="button"
2790
- onClick={toggleAnalysesOpen}
2791
- className="me-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 hover:bg-sidebar-accent hover:text-foreground"
2792
- aria-label={
2793
- analysesOpen
2794
- ? t("sidebar.collapseAnalyses")
2795
- : t("sidebar.expandAnalyses")
2796
- }
2797
- >
2798
- <IconChevronDown
2799
- className={cn(
2800
- "h-3.5 w-3.5 shrink-0 transition-transform",
2801
- !analysesOpen && "-rotate-90",
2802
- )}
2803
- />
2804
- </button>
2805
- </div>
2806
-
2807
- {analysesOpen && (
2808
- <DndContext
2809
- sensors={sensors}
2810
- collisionDetection={closestCenter}
2811
- onDragEnd={handleAnalysisDragEnd}
2812
- >
2813
- <SortableContext
2814
- items={displayedAnalyses.map((a) => a.id)}
2815
- strategy={verticalListSortingStrategy}
2816
- >
2817
- <div className="ms-4 min-w-0 space-y-0.5">
2818
- {displayedAnalyses.map((a) => (
2819
- <SortableRow
2820
- key={a.id}
2821
- id={a.id}
2822
- favoriteKey={`analysis:${a.id}`}
2823
- name={a.name}
2824
- href={`/analyses/${a.id}`}
2825
- isActive={location.pathname === `/analyses/${a.id}`}
2826
- favoriteIds={favoriteIds}
2827
- onToggleFavorite={toggleFavorite}
2828
- onDelete={() => handleAnalysisDelete(a)}
2829
- onRename={(name) => handleAnalysisRename(a, name)}
2830
- hidden={analysisHiddenFilter === "hidden"}
2831
- onHide={
2832
- analysisHiddenFilter === "hidden"
2833
- ? undefined
2834
- : () => handleAnalysisHide(a)
2835
- }
2836
- onUnhide={
2837
- analysisHiddenFilter === "hidden"
2838
- ? () => handleAnalysisUnhide(a)
2839
- : undefined
2840
- }
2841
- visibility={a.visibility}
2842
- onSetVisibility={(v) =>
2843
- handleAnalysisSetVisibility(a, v)
2844
- }
2845
- onPrefetch={() => prefetchAnalysis(a.id)}
2846
- />
2847
- ))}
2848
- {filteredAnalyses.length > SIDEBAR_PREVIEW_COUNT && (
2849
- <button
2850
- onClick={() => setAnalysesShowAll(!analysesShowAll)}
2851
- className="flex items-center gap-1 px-3 py-1 text-[11px] text-muted-foreground/70 hover:text-primary"
2852
- >
2853
- {analysesShowAll
2854
- ? t("sidebar.showLess")
2855
- : t("sidebar.showMore", {
2856
- count:
2857
- filteredAnalyses.length -
2858
- SIDEBAR_PREVIEW_COUNT,
2859
- })}
2860
- </button>
2527
+ {analysesLoading && analysesList.length === 0 && (
2528
+ <div className="flex items-center gap-2 px-3 py-1">
2529
+ <Skeleton
2530
+ className={cn(
2531
+ "h-3.5 w-3.5 shrink-0 rounded-sm",
2532
+ SIDEBAR_SKELETON_CLASS,
2533
+ )}
2534
+ />
2535
+ <Skeleton
2536
+ className={cn(
2537
+ "h-3 w-3/4 rounded",
2538
+ SIDEBAR_SKELETON_CLASS,
2539
+ )}
2540
+ />
2541
+ </div>
2861
2542
  )}
2862
- {analysesLoading &&
2863
- sortedAnalyses.length === 0 &&
2864
- Array.from({ length: 3 }).map((_, i) => (
2865
- <div
2866
- key={`analysis-skeleton-${i}`}
2867
- className="flex items-center gap-2 px-3 py-1"
2868
- >
2869
- <Skeleton
2870
- className={cn(
2871
- "h-3.5 w-3.5 shrink-0 rounded-sm",
2872
- SIDEBAR_SKELETON_CLASS,
2873
- )}
2874
- />
2875
- <Skeleton
2876
- className={cn(
2877
- "h-3 rounded",
2878
- SIDEBAR_SKELETON_CLASS,
2879
- )}
2880
- style={{ width: `${60 + ((i * 17) % 30)}%` }}
2881
- />
2882
- </div>
2883
- ))}
2884
- {analysesError && sortedAnalyses.length === 0 && (
2543
+ {analysesError && analysesList.length === 0 && (
2885
2544
  <SidebarLoadError
2886
2545
  message={t("sidebar.analysesLoadFailed")}
2887
2546
  retryLabel={t("sidebar.retry")}
2888
2547
  onRetry={() => void refetchAnalyses()}
2889
2548
  />
2890
2549
  )}
2891
- {!analysesLoading &&
2892
- !analysesError &&
2893
- sortedAnalyses.length === 0 && (
2894
- <p className="px-3 py-1 text-[11px] text-muted-foreground/60">
2895
- {t("sidebar.noAnalysesYet")}
2896
- </p>
2897
- )}
2898
- <NewAnalysisDialog />
2550
+ <NewDashboardDialog />
2899
2551
  </div>
2900
2552
  </SortableContext>
2901
2553
  </DndContext>
@@ -2920,54 +2572,31 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2920
2572
  )}
2921
2573
  >
2922
2574
  <Icon className="h-4 w-4" />
2923
- {t(item.labelKey)}
2575
+ <span className="flex min-w-0 items-center gap-1">
2576
+ <span className="truncate">{t(item.labelKey)}</span>
2577
+ {item.showExternalLinkIcon && (
2578
+ <IconArrowUpRight
2579
+ aria-hidden="true"
2580
+ className="h-3.5 w-3.5 shrink-0 text-muted-foreground/70"
2581
+ />
2582
+ )}
2583
+ </span>
2924
2584
  </Link>
2925
2585
  );
2926
2586
  })}
2927
2587
  </nav>
2928
2588
 
2929
- <div className="min-w-0">
2930
- <ExtensionsSidebarSection />
2931
- </div>
2932
-
2933
2589
  <div className="space-y-2 pt-2">
2934
2590
  <OrgSwitcher />
2591
+ <DevDatabaseLink />
2935
2592
  <TooltipProvider delayDuration={200}>
2936
- <div className="flex items-center justify-end gap-1">
2937
- <DevDatabaseLink />
2938
- <FeedbackButton className="min-w-0 flex-1" />
2939
- <div className="flex shrink-0 items-center gap-0.5">
2940
- <Tooltip>
2941
- <TooltipTrigger asChild>
2942
- <button
2943
- onClick={() =>
2944
- window.dispatchEvent(
2945
- new CustomEvent(
2946
- "analytics:open-command-palette",
2947
- ),
2948
- )
2949
- }
2950
- aria-label={t("sidebar.search")}
2951
- className="flex items-center justify-center rounded-lg p-2 text-muted-foreground transition-colors hover:text-primary cursor-pointer hover:bg-sidebar-accent/50"
2952
- >
2953
- <IconSearch className="h-4 w-4" />
2954
- </button>
2955
- </TooltipTrigger>
2956
- <TooltipContent side="top">
2957
- <p>
2958
- {t("sidebar.searchShortcut", {
2959
- shortcut: `${shortcutModifierLabel()}+K`,
2960
- })}
2961
- </p>
2962
- </TooltipContent>
2963
- </Tooltip>
2964
- <LanguagePicker
2965
- variant="icon"
2966
- label={t("settings.languageLabel")}
2967
- className="[&_[data-language-picker-trigger]]:rounded-lg [&_[data-language-picker-trigger]]:border-0 [&_[data-language-picker-trigger]]:bg-transparent [&_[data-language-picker-trigger]]:text-muted-foreground [&_[data-language-picker-trigger]]:hover:bg-sidebar-accent/50 [&_[data-language-picker-trigger]]:hover:text-primary"
2968
- />
2969
- </div>
2970
- </div>
2593
+ <SidebarFooterActions
2594
+ feedback={footerFeedback}
2595
+ translate={footerTranslate}
2596
+ search={footerSearch}
2597
+ collapse={footerCollapse}
2598
+ className="px-0 py-0"
2599
+ />
2971
2600
  </TooltipProvider>
2972
2601
  </div>
2973
2602
  </div>