@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.
- package/corpus/README.md +3 -3
- package/corpus/core/CHANGELOG.md +29 -0
- package/corpus/core/docs/content/extensions.mdx +23 -4
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +4 -0
- package/corpus/core/src/agent/run-manager.ts +36 -10
- package/corpus/core/src/agent/types.ts +2 -0
- package/corpus/core/src/cli/context-xray-local.ts +1 -1
- package/corpus/core/src/cli/index.ts +63 -18
- package/corpus/core/src/cli/skills.ts +5 -0
- package/corpus/core/src/cli/telemetry.ts +118 -1
- package/corpus/core/src/client/AgentPanel.tsx +10 -0
- package/corpus/core/src/client/CommandMenu.tsx +13 -1
- package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +39 -4
- package/corpus/core/src/client/analytics.ts +13 -0
- package/corpus/core/src/client/error-capture.ts +6 -1
- package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +28 -8
- package/corpus/core/src/client/extensions/agent-native-extension-runtime.ts +1 -1
- package/corpus/core/src/client/navigation/index.ts +2 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +14 -32
- package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
- package/corpus/core/src/deploy/build.ts +3 -0
- package/corpus/core/src/extensions/theme.ts +1 -1
- package/corpus/core/src/localization/default-messages.ts +2 -0
- package/corpus/core/src/server/analytics.ts +154 -25
- package/corpus/core/src/server/core-routes-plugin.ts +10 -1
- package/corpus/core/src/server/framework-request-handler.ts +17 -26
- package/corpus/core/src/styles/agent-native.css +13 -0
- package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +3 -0
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +113 -51
- package/corpus/core/src/templates/chat/app/global.css +1 -1
- package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/core/src/templates/chat/netlify.toml +1 -0
- package/corpus/core/src/templates/default/app/global.css +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/corpus/core/src/tracking/error-capture.ts +151 -0
- package/corpus/core/src/tracking/index.ts +5 -0
- package/corpus/core/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
- package/corpus/templates/analytics/AGENTS.md +20 -8
- package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
- package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
- package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
- package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
- package/corpus/templates/analytics/actions/view-screen.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/CumulativeNetChart.tsx +1 -1
- package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +1 -4
- package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
- package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +1 -1
- package/corpus/templates/analytics/app/components/dashboard/DateRangePicker.tsx +2 -2
- package/corpus/templates/analytics/app/components/dashboard/MetricCard.tsx +1 -1
- package/corpus/templates/analytics/app/components/dashboard/RecentActivity.tsx +1 -1
- package/corpus/templates/analytics/app/components/dashboard/RevenueChart.tsx +1 -1
- package/corpus/templates/analytics/app/components/dashboard/RevenueComparisonChart.tsx +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +41 -9
- package/corpus/templates/analytics/app/components/dashboard/StatsCard.tsx +1 -1
- package/corpus/templates/analytics/app/components/dashboard/TimeSeriesChart.tsx +1 -1
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +2 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +28 -14
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +255 -626
- package/corpus/templates/analytics/app/global.css +31 -1
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
- package/corpus/templates/analytics/app/i18n-data.ts +80 -0
- package/corpus/templates/analytics/app/lib/dashboard-report-capture.ts +42 -0
- package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
- package/corpus/templates/analytics/app/pages/adhoc/_shared/KpiChart.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/ChartTypePicker.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/DateRangePicker.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventRow.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/SqlPreview.tsx +2 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +2 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +4 -54
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +178 -69
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
- package/corpus/templates/analytics/app/root.tsx +28 -0
- package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
- package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-24-analytics-charts-now-explain-slow-loads-and-offer-a-direct-r.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-24-daily-dashboard-emails-now-load-signed-reports-more-reliably.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-24-manage-agent-is-clearly-marked-as-a-link-to-its-dedicated-pa.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -1
- package/corpus/templates/analytics/server/db/schema.ts +9 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +21 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +11 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +54 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +235 -26
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +4 -1
- package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
- package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
- package/corpus/templates/analytics/server/plugins/db.ts +19 -0
- package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +1 -1
- package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
- package/corpus/templates/assets/actions/generate-image.ts +28 -1
- package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +376 -0
- package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +14 -4
- package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +164 -82
- package/corpus/templates/assets/app/global.css +1 -1
- package/corpus/templates/assets/app/i18n-data.ts +10 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +99 -10
- package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/assets/app/routes/library.tsx +21 -198
- package/corpus/templates/assets/changelog/2026-07-21-unified-asset-preview-click-any-asset-to-open-one-preview-wi.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/assets/netlify.toml +1 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +3 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +114 -51
- package/corpus/templates/brain/app/global.css +1 -1
- package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/brain/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/brain/netlify.toml +1 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +110 -57
- package/corpus/templates/calendar/app/global.css +1 -1
- package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/calendar/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/calendar/netlify.toml +1 -0
- package/corpus/templates/chat/app/components/layout/Layout.tsx +3 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +113 -51
- package/corpus/templates/chat/app/global.css +1 -1
- package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/chat/netlify.toml +1 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +117 -37
- package/corpus/templates/clips/app/components/library/organization-switcher.tsx +2 -2
- package/corpus/templates/clips/app/components/player/video-player.tsx +16 -1
- package/corpus/templates/clips/app/global.css +1 -1
- package/corpus/templates/clips/app/hooks/use-view-tracking.ts +55 -12
- package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +6 -5
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +7 -7
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -5
- package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
- package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
- package/corpus/templates/clips/desktop/src/styles.css +35 -3
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
- package/corpus/templates/clips/netlify.toml +1 -0
- package/corpus/templates/clips/server/plugins/db.ts +113 -68
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +69 -49
- package/corpus/templates/content/app/global.css +1 -1
- package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/content/netlify.toml +1 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +115 -42
- package/corpus/templates/design/app/entry.client.tsx +14 -1
- package/corpus/templates/design/app/global.css +1 -1
- package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/design/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/design/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/design/netlify.toml +1 -0
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
- package/corpus/templates/dispatch/app/global.css +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/dispatch/netlify.toml +1 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +3 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +113 -82
- package/corpus/templates/forms/app/global.css +1 -1
- package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-24-forms-sidebar-actions-now-keep-search-language-feedback-and-.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/forms/netlify.toml +1 -0
- package/corpus/templates/macros/app/components/DailyProgress.tsx +5 -5
- package/corpus/templates/macros/app/components/ExerciseCard.tsx +2 -2
- package/corpus/templates/macros/app/components/MealCard.tsx +2 -2
- package/corpus/templates/macros/app/components/WeightCard.tsx +2 -2
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +92 -34
- package/corpus/templates/macros/app/components/ui/button.tsx +1 -2
- package/corpus/templates/macros/app/components/ui/card.tsx +1 -1
- package/corpus/templates/macros/app/global.css +1 -1
- package/corpus/templates/macros/app/routes/_index.tsx +1 -1
- package/corpus/templates/macros/app/routes/analytics.tsx +4 -7
- package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/macros/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/macros/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/macros/netlify.toml +1 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +125 -46
- package/corpus/templates/mail/app/global.css +1 -1
- package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/mail/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/mail/netlify.toml +1 -0
- package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +128 -65
- package/corpus/templates/plan/app/global.css +1 -1
- package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/plan/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/plan/netlify.toml +1 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +116 -36
- package/corpus/templates/slides/app/global.css +1 -1
- package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/slides/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/corpus/templates/slides/netlify.toml +1 -0
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
- package/corpus/templates/tasks/app/global.css +1 -1
- package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
- package/corpus/templates/tasks/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/corpus/templates/tasks/netlify.toml +1 -0
- package/corpus/toolkit/CHANGELOG.md +8 -0
- package/corpus/toolkit/README.md +8 -1
- package/corpus/toolkit/agent-native.eject.json +1 -1
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/app-shell/index.ts +1 -0
- package/corpus/toolkit/src/app-shell/sidebar-footer-actions.tsx +48 -0
- package/corpus/toolkit/src/chat-history.css +52 -1
- package/corpus/toolkit/src/design-system/default-adapter.tsx +1 -1
- package/corpus/toolkit/src/ui/button.tsx +1 -2
- package/corpus/toolkit/src/ui/card.tsx +1 -1
- package/corpus/toolkit/src/ui/table.tsx +4 -11
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +25 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/types.d.ts +2 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/context-xray-local.js +1 -1
- package/dist/cli/context-xray-local.js.map +1 -1
- package/dist/cli/index.js +56 -17
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +5 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/telemetry.d.ts +7 -0
- package/dist/cli/telemetry.d.ts.map +1 -1
- package/dist/cli/telemetry.js +91 -1
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +7 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/CommandMenu.d.ts +2 -0
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +12 -1
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/agent-page/AgentTabsPage.d.ts +4 -0
- package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
- package/dist/client/agent-page/AgentTabsPage.js +8 -4
- package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +7 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/error-capture.d.ts.map +1 -1
- package/dist/client/error-capture.js +8 -1
- package/dist/client/error-capture.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +6 -4
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/extensions/agent-native-extension-runtime.js +1 -1
- package/dist/client/extensions/agent-native-extension-runtime.js.map +1 -1
- package/dist/client/navigation/index.d.ts +1 -1
- package/dist/client/navigation/index.d.ts.map +1 -1
- package/dist/client/navigation/index.js +1 -1
- package/dist/client/navigation/index.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +9 -13
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
- package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
- package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
- package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +10 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/deploy/build.js +1 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/theme.js +1 -1
- package/dist/extensions/theme.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +2 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +9 -9
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/analytics.d.ts +9 -4
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +111 -22
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +8 -1
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +17 -27
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/styles/agent-native.css +13 -0
- package/dist/templates/chat/app/components/layout/Layout.tsx +3 -0
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +113 -51
- package/dist/templates/chat/app/global.css +1 -1
- package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/dist/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/dist/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/dist/templates/chat/netlify.toml +1 -0
- package/dist/templates/default/app/global.css +1 -1
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/dist/tracking/error-capture.d.ts +14 -0
- package/dist/tracking/error-capture.d.ts.map +1 -0
- package/dist/tracking/error-capture.js +113 -0
- package/dist/tracking/error-capture.js.map +1 -0
- package/dist/tracking/index.d.ts +1 -0
- package/dist/tracking/index.d.ts.map +1 -1
- package/dist/tracking/index.js +1 -0
- package/dist/tracking/index.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/extensions.mdx +23 -4
- package/package.json +2 -2
- package/src/agent/production-agent.ts +4 -0
- package/src/agent/run-manager.ts +36 -10
- package/src/agent/types.ts +2 -0
- package/src/cli/context-xray-local.ts +1 -1
- package/src/cli/index.ts +63 -18
- package/src/cli/skills.ts +5 -0
- package/src/cli/telemetry.ts +118 -1
- package/src/client/AgentPanel.tsx +10 -0
- package/src/client/CommandMenu.tsx +13 -1
- package/src/client/agent-page/AgentTabsPage.tsx +39 -4
- package/src/client/analytics.ts +13 -0
- package/src/client/error-capture.ts +6 -1
- package/src/client/extensions/ExtensionsListPage.tsx +28 -8
- package/src/client/extensions/agent-native-extension-runtime.ts +1 -1
- package/src/client/navigation/index.ts +2 -0
- package/src/client/org/OrgSwitcher.tsx +14 -32
- package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/src/client/settings/SettingsPanel.tsx +10 -0
- package/src/deploy/build.ts +3 -0
- package/src/extensions/theme.ts +1 -1
- package/src/localization/default-messages.ts +2 -0
- package/src/server/analytics.ts +154 -25
- package/src/server/core-routes-plugin.ts +10 -1
- package/src/server/framework-request-handler.ts +17 -26
- package/src/styles/agent-native.css +13 -0
- package/src/templates/chat/app/components/layout/Layout.tsx +3 -0
- package/src/templates/chat/app/components/layout/Sidebar.tsx +113 -51
- package/src/templates/chat/app/global.css +1 -1
- package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/src/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
- package/src/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
- package/src/templates/chat/netlify.toml +1 -0
- package/src/templates/default/app/global.css +1 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/src/tracking/error-capture.ts +151 -0
- package/src/tracking/index.ts +5 -0
- package/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
- package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +0 -175
|
@@ -4,15 +4,16 @@ import {
|
|
|
4
4
|
} from "@agent-native/core/client/agent-chat";
|
|
5
5
|
import { appPath } from "@agent-native/core/client/api-path";
|
|
6
6
|
import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
|
|
7
|
-
import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
|
|
8
7
|
import { getBrowserTabId } from "@agent-native/core/client/hooks";
|
|
9
|
-
import { useT } from "@agent-native/core/client/i18n";
|
|
8
|
+
import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
|
|
9
|
+
import { openCommandMenu } from "@agent-native/core/client/navigation";
|
|
10
10
|
import {
|
|
11
11
|
InvitationBanner,
|
|
12
12
|
OrgSwitcher,
|
|
13
13
|
useOrgRole,
|
|
14
14
|
} from "@agent-native/core/client/org";
|
|
15
15
|
import { FeedbackButton } from "@agent-native/core/client/ui";
|
|
16
|
+
import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
|
|
16
17
|
import {
|
|
17
18
|
IconInbox,
|
|
18
19
|
IconArchive,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
IconShare,
|
|
32
33
|
IconHierarchy2,
|
|
33
34
|
IconSettings,
|
|
35
|
+
IconSearch,
|
|
34
36
|
} from "@tabler/icons-react";
|
|
35
37
|
import { ReactNode, useEffect, useMemo, useState } from "react";
|
|
36
38
|
import { NavLink, useLocation, useParams } from "react-router";
|
|
@@ -155,6 +157,59 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
155
157
|
);
|
|
156
158
|
const [headerSlot, setHeaderSlot] = useState<HTMLElement | null>(null);
|
|
157
159
|
const showCollapsedSidebar = sidebarCollapsed && !isMobile;
|
|
160
|
+
|
|
161
|
+
const collapseButton = !isMobile ? (
|
|
162
|
+
<Tooltip>
|
|
163
|
+
<TooltipTrigger asChild>
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
aria-label={
|
|
167
|
+
showCollapsedSidebar
|
|
168
|
+
? t("navigation.expandSidebar")
|
|
169
|
+
: t("navigation.collapseSidebar")
|
|
170
|
+
}
|
|
171
|
+
className="flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/60 hover:text-foreground"
|
|
172
|
+
onClick={() => setSidebarCollapsed((value) => !value)}
|
|
173
|
+
>
|
|
174
|
+
{showCollapsedSidebar ? (
|
|
175
|
+
<IconLayoutSidebarLeftExpand className="h-4 w-4" />
|
|
176
|
+
) : (
|
|
177
|
+
<IconLayoutSidebarLeftCollapse className="h-4 w-4" />
|
|
178
|
+
)}
|
|
179
|
+
</button>
|
|
180
|
+
</TooltipTrigger>
|
|
181
|
+
<TooltipContent side="right">
|
|
182
|
+
{showCollapsedSidebar
|
|
183
|
+
? t("navigation.expandSidebar")
|
|
184
|
+
: t("navigation.collapseSidebar")}
|
|
185
|
+
</TooltipContent>
|
|
186
|
+
</Tooltip>
|
|
187
|
+
) : null;
|
|
188
|
+
const searchButton = (
|
|
189
|
+
<Tooltip>
|
|
190
|
+
<TooltipTrigger asChild>
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
aria-label={t("root.commandSearch")}
|
|
194
|
+
className="flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/60 hover:text-foreground"
|
|
195
|
+
onClick={openCommandMenu}
|
|
196
|
+
>
|
|
197
|
+
<IconSearch className="h-4 w-4" />
|
|
198
|
+
</button>
|
|
199
|
+
</TooltipTrigger>
|
|
200
|
+
<TooltipContent side="right">{t("root.commandSearch")}</TooltipContent>
|
|
201
|
+
</Tooltip>
|
|
202
|
+
);
|
|
203
|
+
const translateButton = (
|
|
204
|
+
<LanguagePicker variant="ghost-icon" label={t("settings.languageLabel")} />
|
|
205
|
+
);
|
|
206
|
+
const feedbackButton = (
|
|
207
|
+
<FeedbackButton
|
|
208
|
+
variant={showCollapsedSidebar ? "icon" : "sidebar"}
|
|
209
|
+
side="right"
|
|
210
|
+
className={showCollapsedSidebar ? "size-8" : "min-w-0"}
|
|
211
|
+
/>
|
|
212
|
+
);
|
|
158
213
|
const sidebarHasNewRecordingAction = isMobile
|
|
159
214
|
? sidebarOpen
|
|
160
215
|
: !sidebarCollapsed;
|
|
@@ -241,9 +296,17 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
241
296
|
icon: IconTrash,
|
|
242
297
|
match: (p) => p.startsWith("/trash"),
|
|
243
298
|
},
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
const bottomNavItems: {
|
|
302
|
+
to: string;
|
|
303
|
+
label: string;
|
|
304
|
+
icon: React.ComponentType<{ className?: string }>;
|
|
305
|
+
match: (path: string) => boolean;
|
|
306
|
+
}[] = [
|
|
244
307
|
{
|
|
245
308
|
to: "/agent",
|
|
246
|
-
label: t("
|
|
309
|
+
label: t("settings.agentTitle"),
|
|
247
310
|
icon: IconHierarchy2,
|
|
248
311
|
match: (p) => p.startsWith("/agent"),
|
|
249
312
|
},
|
|
@@ -307,35 +370,6 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
307
370
|
</>
|
|
308
371
|
)}
|
|
309
372
|
</div>
|
|
310
|
-
|
|
311
|
-
<Tooltip>
|
|
312
|
-
<TooltipTrigger asChild>
|
|
313
|
-
<Button
|
|
314
|
-
type="button"
|
|
315
|
-
variant="ghost"
|
|
316
|
-
size="icon"
|
|
317
|
-
className="hidden h-7 w-7 shrink-0 text-muted-foreground hover:text-foreground md:inline-flex"
|
|
318
|
-
aria-label={
|
|
319
|
-
showCollapsedSidebar
|
|
320
|
-
? t("navigation.expandSidebar")
|
|
321
|
-
: t("navigation.collapseSidebar")
|
|
322
|
-
}
|
|
323
|
-
aria-expanded={!showCollapsedSidebar}
|
|
324
|
-
onClick={() => setSidebarCollapsed((value) => !value)}
|
|
325
|
-
>
|
|
326
|
-
{showCollapsedSidebar ? (
|
|
327
|
-
<IconLayoutSidebarLeftExpand className="h-4 w-4" />
|
|
328
|
-
) : (
|
|
329
|
-
<IconLayoutSidebarLeftCollapse className="h-4 w-4" />
|
|
330
|
-
)}
|
|
331
|
-
</Button>
|
|
332
|
-
</TooltipTrigger>
|
|
333
|
-
<TooltipContent side="right">
|
|
334
|
-
{showCollapsedSidebar
|
|
335
|
-
? t("navigation.expandSidebar")
|
|
336
|
-
: t("navigation.collapseSidebar")}
|
|
337
|
-
</TooltipContent>
|
|
338
|
-
</Tooltip>
|
|
339
373
|
</div>
|
|
340
374
|
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
341
375
|
{showCollapsedSidebar ? (
|
|
@@ -519,6 +553,49 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
519
553
|
)}
|
|
520
554
|
</div>
|
|
521
555
|
|
|
556
|
+
<div className="shrink-0">
|
|
557
|
+
{showCollapsedSidebar ? (
|
|
558
|
+
<nav className="flex flex-col items-center gap-1 px-2 py-1">
|
|
559
|
+
{bottomNavItems.map(({ to, label, icon: Icon, match }) => (
|
|
560
|
+
<Tooltip key={to}>
|
|
561
|
+
<TooltipTrigger asChild>
|
|
562
|
+
<NavLink
|
|
563
|
+
to={to}
|
|
564
|
+
aria-label={label}
|
|
565
|
+
className={cn(
|
|
566
|
+
"flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/60 hover:text-foreground",
|
|
567
|
+
match(location.pathname) &&
|
|
568
|
+
"bg-primary/10 text-primary hover:bg-primary/10 hover:text-primary",
|
|
569
|
+
)}
|
|
570
|
+
>
|
|
571
|
+
<Icon className="h-4 w-4" />
|
|
572
|
+
</NavLink>
|
|
573
|
+
</TooltipTrigger>
|
|
574
|
+
<TooltipContent side="right">{label}</TooltipContent>
|
|
575
|
+
</Tooltip>
|
|
576
|
+
))}
|
|
577
|
+
</nav>
|
|
578
|
+
) : (
|
|
579
|
+
<nav className="space-y-0.5 border-t border-border px-2 py-1">
|
|
580
|
+
{bottomNavItems.map(({ to, label, icon: Icon, match }) => (
|
|
581
|
+
<NavLink
|
|
582
|
+
key={to}
|
|
583
|
+
to={to}
|
|
584
|
+
className={cn(
|
|
585
|
+
"flex items-center gap-2 rounded px-2 py-1.5 text-xs",
|
|
586
|
+
match(location.pathname)
|
|
587
|
+
? "bg-primary/10 font-medium text-primary"
|
|
588
|
+
: "text-foreground hover:bg-accent/60",
|
|
589
|
+
)}
|
|
590
|
+
>
|
|
591
|
+
<Icon className="h-4 w-4" />
|
|
592
|
+
<span className="flex-1 truncate">{label}</span>
|
|
593
|
+
</NavLink>
|
|
594
|
+
))}
|
|
595
|
+
</nav>
|
|
596
|
+
)}
|
|
597
|
+
</div>
|
|
598
|
+
|
|
522
599
|
{!showCollapsedSidebar && (
|
|
523
600
|
<>
|
|
524
601
|
<div className="shrink-0 space-y-1.5 px-2 py-1.5">
|
|
@@ -539,17 +616,20 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
539
616
|
{(isMobile || !pageHasHeaderSearch) && <SearchBar />}
|
|
540
617
|
</div>
|
|
541
618
|
|
|
542
|
-
<div className="shrink-0 px-1 py-1">
|
|
543
|
-
<ExtensionsSidebarSection />
|
|
544
|
-
</div>
|
|
545
|
-
|
|
546
619
|
<div className="shrink-0 space-y-2 px-3 py-2">
|
|
547
620
|
<OrgSwitcher settingsPath="/settings/organization" />
|
|
548
621
|
<DevDatabaseLink />
|
|
549
|
-
<FeedbackButton />
|
|
550
622
|
</div>
|
|
551
623
|
</>
|
|
552
624
|
)}
|
|
625
|
+
<SidebarFooterActions
|
|
626
|
+
collapsed={showCollapsedSidebar}
|
|
627
|
+
feedback={feedbackButton}
|
|
628
|
+
translate={translateButton}
|
|
629
|
+
search={searchButton}
|
|
630
|
+
collapse={collapseButton}
|
|
631
|
+
className={showCollapsedSidebar ? undefined : "px-0 py-0"}
|
|
632
|
+
/>
|
|
553
633
|
</aside>
|
|
554
634
|
|
|
555
635
|
<AgentSidebar
|
|
@@ -59,8 +59,8 @@ export function OrganizationSwitcher({ className }: OrganizationSwitcherProps) {
|
|
|
59
59
|
<button
|
|
60
60
|
type="button"
|
|
61
61
|
className={cn(
|
|
62
|
-
"flex w-full items-center gap-2 rounded-md border
|
|
63
|
-
"hover:bg-accent",
|
|
62
|
+
"flex w-full items-center gap-2 rounded-md border-0 bg-accent/50 px-2 py-1.5 text-start",
|
|
63
|
+
"hover:bg-accent/70",
|
|
64
64
|
className,
|
|
65
65
|
)}
|
|
66
66
|
>
|
|
@@ -209,6 +209,13 @@ export interface VideoPlayerProps {
|
|
|
209
209
|
* a visible frame for missing or blank auto-generated library thumbnails.
|
|
210
210
|
*/
|
|
211
211
|
role?: "owner" | "admin" | "editor" | "viewer";
|
|
212
|
+
/**
|
|
213
|
+
* Called with the live `<video>` DOM node whenever it is created or
|
|
214
|
+
* destroyed (e.g. swapping to/from the Loom iframe or unsupported-format
|
|
215
|
+
* placeholder). Lets a caller key an effect off the actual element
|
|
216
|
+
* lifecycle instead of polling an imperative-handle getter.
|
|
217
|
+
*/
|
|
218
|
+
onVideoElementChange?: (video: HTMLVideoElement | null) => void;
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
@@ -245,6 +252,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
245
252
|
cover,
|
|
246
253
|
recordingId,
|
|
247
254
|
role,
|
|
255
|
+
onVideoElementChange,
|
|
248
256
|
} = props;
|
|
249
257
|
|
|
250
258
|
const resolvedVideoSrc = useMemo(
|
|
@@ -252,6 +260,13 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
252
260
|
[videoUrl],
|
|
253
261
|
);
|
|
254
262
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
|
263
|
+
const setVideoNode = useCallback(
|
|
264
|
+
(el: HTMLVideoElement | null) => {
|
|
265
|
+
videoRef.current = el;
|
|
266
|
+
onVideoElementChange?.(el);
|
|
267
|
+
},
|
|
268
|
+
[onVideoElementChange],
|
|
269
|
+
);
|
|
255
270
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
256
271
|
const idleTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
257
272
|
const touchTapCandidateRef = useRef<{
|
|
@@ -1262,7 +1277,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1262
1277
|
</div>
|
|
1263
1278
|
) : activeVideoSrc ? (
|
|
1264
1279
|
<video
|
|
1265
|
-
ref={
|
|
1280
|
+
ref={setVideoNode}
|
|
1266
1281
|
src={domVideoSrc}
|
|
1267
1282
|
poster={resolveLocalUrl(thumbnailUrl)}
|
|
1268
1283
|
// `crossOrigin` is only needed so the owner's canvas thumbnail
|
|
@@ -34,7 +34,14 @@ function createViewSessionId(recordingId: string): string {
|
|
|
34
34
|
|
|
35
35
|
export interface UseViewTrackingOpts {
|
|
36
36
|
recordingId: string;
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The live `<video>` DOM node, or `null` when there is none (e.g. a Loom
|
|
39
|
+
* iframe embed). Pass the actual element — not a ref wrapper — so this
|
|
40
|
+
* hook's effect can depend on it directly and React's own dependency
|
|
41
|
+
* comparison decides when to reattach, instead of hand-rolled identity
|
|
42
|
+
* bookkeeping.
|
|
43
|
+
*/
|
|
44
|
+
videoEl: HTMLVideoElement | null;
|
|
38
45
|
durationMs: number;
|
|
39
46
|
/** Disable tracking entirely (e.g. for the recording's owner viewing their own clip). */
|
|
40
47
|
disabled?: boolean;
|
|
@@ -46,10 +53,24 @@ export interface UseViewTrackingOpts {
|
|
|
46
53
|
* Wires up the view-event tracker for a player instance. Fires a "view-start"
|
|
47
54
|
* on mount, then throttled "watch-progress" every 5s while playing, plus
|
|
48
55
|
* seek/pause/resume events and a final flush on unmount.
|
|
56
|
+
*
|
|
57
|
+
* The effect depends on `[recordingId, videoEl, trackOpenWithoutVideo,
|
|
58
|
+
* disabled]`, so React naturally creates a fresh closure — and runs the
|
|
59
|
+
* previous one's cleanup — exactly when any of those actually change (a
|
|
60
|
+
* different video element, a different recording, or the no-video/embed
|
|
61
|
+
* mode flipping). Each closure captures its own `recordingId` and `videoEl`,
|
|
62
|
+
* so a cleanup's final flush always describes the session it belonged to,
|
|
63
|
+
* never a session that has since replaced it.
|
|
64
|
+
*
|
|
65
|
+
* `durationMs` is intentionally excluded from the dependency array — it can
|
|
66
|
+
* load asynchronously after the video/recording is already attached, and
|
|
67
|
+
* reattaching just for that would be wasted work. It's kept in a ref that's
|
|
68
|
+
* synced every render and read fresh inside `post()`.
|
|
49
69
|
*/
|
|
50
70
|
export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
51
|
-
const { recordingId,
|
|
71
|
+
const { recordingId, videoEl, durationMs, disabled, trackOpenWithoutVideo } =
|
|
52
72
|
opts;
|
|
73
|
+
|
|
53
74
|
const watchMsRef = useRef(0);
|
|
54
75
|
const lastTickRef = useRef<number | null>(null);
|
|
55
76
|
const startedRef = useRef(false);
|
|
@@ -57,11 +78,25 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
57
78
|
const lastSentProgressRef = useRef(0);
|
|
58
79
|
const maxPctRef = useRef(0);
|
|
59
80
|
const viewSessionRef = useRef<string | null>(null);
|
|
81
|
+
const durationMsRef = useRef(durationMs);
|
|
82
|
+
const recordingIdRef = useRef(recordingId);
|
|
83
|
+
|
|
84
|
+
durationMsRef.current = durationMs;
|
|
85
|
+
recordingIdRef.current = recordingId;
|
|
60
86
|
|
|
61
87
|
useEffect(() => {
|
|
62
88
|
if (disabled) return;
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
|
|
90
|
+
// Reset per-session counters — this effect only reruns when the video
|
|
91
|
+
// element, recording, or embed mode actually change.
|
|
92
|
+
watchMsRef.current = 0;
|
|
93
|
+
lastTickRef.current = null;
|
|
94
|
+
startedRef.current = false;
|
|
95
|
+
lastSentProgressRef.current = 0;
|
|
96
|
+
maxPctRef.current = 0;
|
|
97
|
+
viewSessionRef.current = null;
|
|
98
|
+
|
|
99
|
+
if (!videoEl) {
|
|
65
100
|
if (
|
|
66
101
|
!trackOpenWithoutVideo ||
|
|
67
102
|
!recordingId ||
|
|
@@ -69,6 +104,10 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
69
104
|
) {
|
|
70
105
|
return;
|
|
71
106
|
}
|
|
107
|
+
// Persists for the hook's lifetime (never reset on cleanup): this is
|
|
108
|
+
// what stops a React StrictMode dev mount->cleanup->remount cycle
|
|
109
|
+
// from double-posting the same iframe-open view-start, since — unlike
|
|
110
|
+
// the with-video path below — there's no native DOM event gating it.
|
|
72
111
|
openTrackedRecordingRef.current = recordingId;
|
|
73
112
|
viewSessionRef.current = createViewSessionId(recordingId);
|
|
74
113
|
fetch(`${appBasePath()}/api/view-event`, {
|
|
@@ -90,6 +129,7 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
90
129
|
return;
|
|
91
130
|
}
|
|
92
131
|
|
|
132
|
+
const video = videoEl;
|
|
93
133
|
const sessionId = getSessionId();
|
|
94
134
|
viewSessionRef.current = createViewSessionId(recordingId);
|
|
95
135
|
let progressTimer: ReturnType<typeof setInterval> | null = null;
|
|
@@ -105,8 +145,7 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
105
145
|
| "reaction",
|
|
106
146
|
extra?: Record<string, unknown>,
|
|
107
147
|
) {
|
|
108
|
-
const
|
|
109
|
-
if (!v) return;
|
|
148
|
+
const durationMs = durationMsRef.current;
|
|
110
149
|
const completedPct =
|
|
111
150
|
durationMs > 0 ? (watchMsRef.current / durationMs) * 100 : 0;
|
|
112
151
|
maxPctRef.current = Math.max(
|
|
@@ -120,12 +159,13 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
120
159
|
body: JSON.stringify({
|
|
121
160
|
recordingId,
|
|
122
161
|
kind,
|
|
123
|
-
timestampMs: Math.floor(
|
|
162
|
+
timestampMs: Math.floor(video.currentTime * 1000),
|
|
124
163
|
sessionId,
|
|
125
164
|
viewSessionId: viewSessionRef.current,
|
|
126
165
|
totalWatchMs: Math.floor(watchMsRef.current),
|
|
127
166
|
completedPct: Math.floor(maxPctRef.current),
|
|
128
|
-
scrubbedToEnd:
|
|
167
|
+
scrubbedToEnd:
|
|
168
|
+
video.duration > 0 && video.currentTime >= video.duration - 0.5,
|
|
129
169
|
payload: extra,
|
|
130
170
|
}),
|
|
131
171
|
}).catch(() => {});
|
|
@@ -186,10 +226,13 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
186
226
|
video.removeEventListener("seeked", onSeek);
|
|
187
227
|
video.removeEventListener("ended", onEnded);
|
|
188
228
|
if (progressTimer) clearInterval(progressTimer);
|
|
189
|
-
// Flush final progress
|
|
229
|
+
// Flush final progress, still scoped to this closure's own video and
|
|
230
|
+
// recordingId — never one a later render has since moved on to.
|
|
190
231
|
if (startedRef.current) post("watch-progress");
|
|
191
232
|
};
|
|
192
|
-
|
|
233
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- durationMs is
|
|
234
|
+
// deliberately excluded; it's read live from durationMsRef inside post().
|
|
235
|
+
}, [recordingId, videoEl, trackOpenWithoutVideo, disabled]);
|
|
193
236
|
|
|
194
237
|
return {
|
|
195
238
|
reportCtaClick: () => {
|
|
@@ -198,7 +241,7 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
198
241
|
keepalive: true,
|
|
199
242
|
headers: { "Content-Type": "application/json" },
|
|
200
243
|
body: JSON.stringify({
|
|
201
|
-
recordingId,
|
|
244
|
+
recordingId: recordingIdRef.current,
|
|
202
245
|
kind: "cta-click",
|
|
203
246
|
sessionId: getSessionId(),
|
|
204
247
|
}),
|
|
@@ -210,7 +253,7 @@ export function useViewTracking(opts: UseViewTrackingOpts) {
|
|
|
210
253
|
keepalive: true,
|
|
211
254
|
headers: { "Content-Type": "application/json" },
|
|
212
255
|
body: JSON.stringify({
|
|
213
|
-
recordingId,
|
|
256
|
+
recordingId: recordingIdRef.current,
|
|
214
257
|
kind: "reaction",
|
|
215
258
|
sessionId: getSessionId(),
|
|
216
259
|
payload: { emoji },
|
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useT } from "@agent-native/core/client/i18n";
|
|
3
|
-
|
|
4
|
-
import { PageHeader } from "@/components/library/page-header";
|
|
1
|
+
import { Navigate } from "react-router";
|
|
5
2
|
|
|
6
3
|
export default function ExtensionsRoute() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<>
|
|
11
|
-
<PageHeader>
|
|
12
|
-
<h1 className="text-base font-semibold tracking-tight truncate">
|
|
13
|
-
{t("navigation.extensions")}
|
|
14
|
-
</h1>
|
|
15
|
-
</PageHeader>
|
|
16
|
-
<ExtensionsListPage />
|
|
17
|
-
</>
|
|
18
|
-
);
|
|
4
|
+
return <Navigate to="/settings#extensions" replace />;
|
|
19
5
|
}
|
|
@@ -131,13 +131,13 @@ export default function EmbedRoute() {
|
|
|
131
131
|
const firstCta = ctas[0] ?? null;
|
|
132
132
|
const isLoomEmbedBacked = isLoomEmbedBackedRecording(recording);
|
|
133
133
|
|
|
134
|
+
const [trackedVideoEl, setTrackedVideoEl] = useState<HTMLVideoElement | null>(
|
|
135
|
+
null,
|
|
136
|
+
);
|
|
137
|
+
|
|
134
138
|
const tracking = useViewTracking({
|
|
135
139
|
recordingId: shareId ?? "",
|
|
136
|
-
|
|
137
|
-
get current() {
|
|
138
|
-
return playerRef.current?.video ?? null;
|
|
139
|
-
},
|
|
140
|
-
} as any,
|
|
140
|
+
videoEl: trackedVideoEl,
|
|
141
141
|
durationMs: recording?.durationMs ?? 0,
|
|
142
142
|
trackOpenWithoutVideo: isLoomEmbedBacked,
|
|
143
143
|
});
|
|
@@ -193,6 +193,7 @@ export default function EmbedRoute() {
|
|
|
193
193
|
<div className="fixed inset-0 h-dvh w-dvw overflow-hidden bg-black">
|
|
194
194
|
<VideoPlayer
|
|
195
195
|
ref={playerRef}
|
|
196
|
+
onVideoElementChange={setTrackedVideoEl}
|
|
196
197
|
recordingId={recording.id}
|
|
197
198
|
videoUrl={recording.videoUrl}
|
|
198
199
|
videoFormat={recording.videoFormat}
|
|
@@ -800,16 +800,15 @@ export default function RecordingPage() {
|
|
|
800
800
|
|
|
801
801
|
usePlayerShortcuts({ playerRef, chapters });
|
|
802
802
|
|
|
803
|
+
const [trackedVideoEl, setTrackedVideoEl] = useState<HTMLVideoElement | null>(
|
|
804
|
+
null,
|
|
805
|
+
);
|
|
806
|
+
|
|
803
807
|
const tracking = useViewTracking({
|
|
804
808
|
recordingId: recordingId ?? "",
|
|
805
|
-
|
|
806
|
-
get current() {
|
|
807
|
-
return playerRef.current?.video ?? null;
|
|
808
|
-
},
|
|
809
|
-
} as any,
|
|
809
|
+
videoEl: trackedVideoEl,
|
|
810
810
|
durationMs: recording?.durationMs ?? 0,
|
|
811
|
-
// Skip tracking for the owner
|
|
812
|
-
disabled: role === "owner",
|
|
811
|
+
disabled: role === "owner", // Skip tracking for the owner: they shouldn't inflate their own views.
|
|
813
812
|
});
|
|
814
813
|
|
|
815
814
|
if (!recordingId) return null;
|
|
@@ -1545,6 +1544,7 @@ export default function RecordingPage() {
|
|
|
1545
1544
|
<div className="relative aspect-video w-full xl:min-h-0 xl:flex-1 xl:aspect-auto">
|
|
1546
1545
|
<VideoPlayer
|
|
1547
1546
|
ref={playerRef}
|
|
1547
|
+
onVideoElementChange={setTrackedVideoEl}
|
|
1548
1548
|
recordingId={recording.id}
|
|
1549
1549
|
videoUrl={recording.videoUrl}
|
|
1550
1550
|
videoFormat={recording.videoFormat}
|
|
@@ -579,13 +579,13 @@ export default function ShareRoute() {
|
|
|
579
579
|
|
|
580
580
|
usePlayerShortcuts({ playerRef });
|
|
581
581
|
|
|
582
|
+
const [trackedVideoEl, setTrackedVideoEl] = useState<HTMLVideoElement | null>(
|
|
583
|
+
null,
|
|
584
|
+
);
|
|
585
|
+
|
|
582
586
|
const tracking = useViewTracking({
|
|
583
587
|
recordingId: shareId ?? "",
|
|
584
|
-
|
|
585
|
-
get current() {
|
|
586
|
-
return playerRef.current?.video ?? null;
|
|
587
|
-
},
|
|
588
|
-
} as any,
|
|
588
|
+
videoEl: trackedVideoEl,
|
|
589
589
|
durationMs: recording?.durationMs ?? 0,
|
|
590
590
|
trackOpenWithoutVideo: isLoomEmbedBacked,
|
|
591
591
|
});
|
|
@@ -961,6 +961,7 @@ export default function ShareRoute() {
|
|
|
961
961
|
<div className="aspect-video w-full lg:min-h-0 lg:flex-1 lg:aspect-auto">
|
|
962
962
|
<VideoPlayer
|
|
963
963
|
ref={playerRef}
|
|
964
|
+
onVideoElementChange={setTrackedVideoEl}
|
|
964
965
|
recordingId={recording.id}
|
|
965
966
|
videoUrl={recording.videoUrl}
|
|
966
967
|
videoFormat={recording.videoFormat}
|
|
@@ -88,7 +88,7 @@ export function sampleLevels(
|
|
|
88
88
|
const levels: number[] = [];
|
|
89
89
|
for (let i = 0; i < barCount; i++) {
|
|
90
90
|
const idx = Math.min(usable - 1, Math.floor((i / barCount) * usable));
|
|
91
|
-
levels.push(data[idx] / 255);
|
|
91
|
+
levels.push(Math.min(1, (data[idx] / 255) * 1.65));
|
|
92
92
|
}
|
|
93
93
|
return levels;
|
|
94
94
|
}
|
|
@@ -3,6 +3,7 @@ import { invoke } from "@tauri-apps/api/core";
|
|
|
3
3
|
export interface DismissibleMeetingNotification {
|
|
4
4
|
type: "calendar" | "adhoc";
|
|
5
5
|
meetingId: string;
|
|
6
|
+
joinUrl?: string | null;
|
|
6
7
|
platform?: string | null;
|
|
7
8
|
scheduledStart?: string | null;
|
|
8
9
|
scheduledEnd?: string | null;
|
|
@@ -21,6 +22,7 @@ export async function dismissMeetingNotification(
|
|
|
21
22
|
await nativeInvoke("dismiss_meeting_notification", {
|
|
22
23
|
meetingId: notification.meetingId,
|
|
23
24
|
notificationType: notification.type,
|
|
25
|
+
joinUrl: notification.joinUrl ?? null,
|
|
24
26
|
platform: notification.platform ?? null,
|
|
25
27
|
scheduledStart: notification.scheduledStart ?? null,
|
|
26
28
|
scheduledEnd: notification.scheduledEnd ?? null,
|
|
@@ -1139,7 +1139,7 @@ export function installDesktopVoiceDictation(
|
|
|
1139
1139
|
* `stop()` works the same way as for the browser path.
|
|
1140
1140
|
*
|
|
1141
1141
|
* No `getUserMedia()` here — the audio engine handles the mic on the Rust
|
|
1142
|
-
* side
|
|
1142
|
+
* side and emits the real level events consumed by the flow bar.
|
|
1143
1143
|
*/
|
|
1144
1144
|
const startNative = async (
|
|
1145
1145
|
cleanupProvider?: ServerVoiceProvider,
|
|
@@ -1191,7 +1191,6 @@ export function installDesktopVoiceDictation(
|
|
|
1191
1191
|
};
|
|
1192
1192
|
session = next;
|
|
1193
1193
|
startInFlight = false;
|
|
1194
|
-
startSyntheticMeter(next);
|
|
1195
1194
|
try {
|
|
1196
1195
|
// Bias the recognizer toward the user's learned vocabulary. Stage
|
|
1197
1196
|
// the list via a separate command so meeting capture can pass mic
|
|
@@ -1299,7 +1298,6 @@ export function installDesktopVoiceDictation(
|
|
|
1299
1298
|
};
|
|
1300
1299
|
session = next;
|
|
1301
1300
|
startInFlight = false;
|
|
1302
|
-
startSyntheticMeter(next);
|
|
1303
1301
|
if (stopRequestedBeforeReady) {
|
|
1304
1302
|
stop();
|
|
1305
1303
|
}
|