@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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { listen } from "@tauri-apps/api/event";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
|
|
4
|
+
const BAR_SHAPES = [0.52, 1, 0.68];
|
|
5
|
+
const LEVEL_DECAY = 0.78;
|
|
6
|
+
const EVENT_ATTACK_DECAY = 0.52;
|
|
7
|
+
|
|
8
|
+
interface LiveAudioBarsProps {
|
|
9
|
+
className?: string;
|
|
10
|
+
compact?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Small Granola-style meter shared by the compact and expanded overlays. */
|
|
14
|
+
export function LiveAudioBars({
|
|
15
|
+
className,
|
|
16
|
+
compact = false,
|
|
17
|
+
}: LiveAudioBarsProps) {
|
|
18
|
+
const [level, setLevel] = useState(0);
|
|
19
|
+
const levelRef = useRef(0);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
let stopped = false;
|
|
23
|
+
let unlisten: (() => void) | null = null;
|
|
24
|
+
|
|
25
|
+
const decayTimer = window.setInterval(() => {
|
|
26
|
+
const next = levelRef.current * LEVEL_DECAY;
|
|
27
|
+
levelRef.current = next < 0.01 ? 0 : next;
|
|
28
|
+
setLevel(levelRef.current);
|
|
29
|
+
}, 60);
|
|
30
|
+
|
|
31
|
+
listen<{ level?: number }>("voice:audio-level", (event) => {
|
|
32
|
+
const incoming = Number(event.payload?.level);
|
|
33
|
+
if (!Number.isFinite(incoming)) return;
|
|
34
|
+
const next = Math.max(
|
|
35
|
+
levelRef.current * EVENT_ATTACK_DECAY,
|
|
36
|
+
Math.max(0, Math.min(1, incoming)),
|
|
37
|
+
);
|
|
38
|
+
levelRef.current = next;
|
|
39
|
+
setLevel(next);
|
|
40
|
+
})
|
|
41
|
+
.then((cleanup) => {
|
|
42
|
+
if (stopped) {
|
|
43
|
+
cleanup();
|
|
44
|
+
} else {
|
|
45
|
+
unlisten = cleanup;
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.catch(() => {});
|
|
49
|
+
|
|
50
|
+
return () => {
|
|
51
|
+
stopped = true;
|
|
52
|
+
window.clearInterval(decayTimer);
|
|
53
|
+
unlisten?.();
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
// Peak levels from speech taps are often quiet even when speech is clear.
|
|
58
|
+
// A gentle curve keeps the meter responsive without turning background noise
|
|
59
|
+
// into a full-height signal.
|
|
60
|
+
const visualLevel = level > 0 ? Math.min(1, Math.pow(level, 0.52) * 1.08) : 0;
|
|
61
|
+
const rootClassName = [
|
|
62
|
+
"live-audio-bars",
|
|
63
|
+
compact ? "live-audio-bars-compact" : null,
|
|
64
|
+
className,
|
|
65
|
+
]
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.join(" ");
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<span className={rootClassName} aria-hidden="true">
|
|
71
|
+
{BAR_SHAPES.map((shape, index) => {
|
|
72
|
+
const idleHeight = index === 1 ? 0.2 : 0.14;
|
|
73
|
+
const height = Math.round(
|
|
74
|
+
(idleHeight + visualLevel * shape * 0.8) * 100,
|
|
75
|
+
);
|
|
76
|
+
return (
|
|
77
|
+
<span
|
|
78
|
+
className="live-audio-bar"
|
|
79
|
+
key={index}
|
|
80
|
+
style={{ height: `${Math.max(12, height)}%` }}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
})}
|
|
84
|
+
</span>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -39,6 +39,7 @@ interface TranscriptionStatusPayload {
|
|
|
39
39
|
|
|
40
40
|
const SNOOZE_MS = 5 * 60_000;
|
|
41
41
|
const FALLBACK_AUTO_HIDE_MS = 6 * 60_000;
|
|
42
|
+
const DISMISSAL_TOMBSTONE_MS = 30 * 60_000;
|
|
42
43
|
// Card is up to 440px wide; the extra width leaves room for the drop shadow
|
|
43
44
|
// (~32px each side) so it isn't clipped by the transparent window edges.
|
|
44
45
|
const NOTIFICATION_WINDOW_WIDTH = 504;
|
|
@@ -128,6 +129,7 @@ export function MeetingNotification() {
|
|
|
128
129
|
const [pending, setPending] = useState(false);
|
|
129
130
|
const autoHideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
130
131
|
const dataRef = useRef<NotificationData | null>(null);
|
|
132
|
+
const dismissedKeysRef = useRef(new Map<string, number>());
|
|
131
133
|
// Real DOM hover only fires while this overlay window is key, which macOS
|
|
132
134
|
// won't grant it without a click (`show_without_activation` never
|
|
133
135
|
// activates). `polledHovered` mirrors the Rust-side global cursor poll
|
|
@@ -139,6 +141,21 @@ export function MeetingNotification() {
|
|
|
139
141
|
const hovered = domHovered || polledHovered;
|
|
140
142
|
const prevHoveredRef = useRef(false);
|
|
141
143
|
|
|
144
|
+
function notificationKey(payload: NotificationData): string {
|
|
145
|
+
return [payload.type, payload.meetingId, payload.scheduledStart ?? ""].join(
|
|
146
|
+
"|",
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function isDismissed(payload: NotificationData): boolean {
|
|
151
|
+
const now = Date.now();
|
|
152
|
+
const dismissed = dismissedKeysRef.current;
|
|
153
|
+
for (const [key, expiresAt] of dismissed) {
|
|
154
|
+
if (expiresAt <= now) dismissed.delete(key);
|
|
155
|
+
}
|
|
156
|
+
return dismissed.has(notificationKey(payload));
|
|
157
|
+
}
|
|
158
|
+
|
|
142
159
|
useEffect(() => {
|
|
143
160
|
dataRef.current = data;
|
|
144
161
|
}, [data]);
|
|
@@ -188,6 +205,7 @@ export function MeetingNotification() {
|
|
|
188
205
|
payload: NotificationData,
|
|
189
206
|
options?: { hydrated?: boolean },
|
|
190
207
|
) {
|
|
208
|
+
if (isDismissed(payload)) return;
|
|
191
209
|
setData(payload);
|
|
192
210
|
setError(null);
|
|
193
211
|
setMenuOpen(false);
|
|
@@ -311,6 +329,12 @@ export function MeetingNotification() {
|
|
|
311
329
|
|
|
312
330
|
function dismissNotification() {
|
|
313
331
|
const current = dataRef.current;
|
|
332
|
+
if (current) {
|
|
333
|
+
dismissedKeysRef.current.set(
|
|
334
|
+
notificationKey(current),
|
|
335
|
+
Date.now() + DISMISSAL_TOMBSTONE_MS,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
314
338
|
hideNotification();
|
|
315
339
|
if (current) {
|
|
316
340
|
void dismissMeetingNotification(current);
|
|
@@ -17,6 +17,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
17
17
|
|
|
18
18
|
import { isDirectPillClick, type ScreenPoint } from "../lib/pill-interaction";
|
|
19
19
|
import { speakerFor } from "../lib/transcription-engine";
|
|
20
|
+
import { LiveAudioBars } from "./live-audio-bars";
|
|
20
21
|
import { LiveTranscript, type FinalLine } from "./live-transcript";
|
|
21
22
|
import { PillLogo } from "./pill-logo";
|
|
22
23
|
|
|
@@ -65,9 +66,6 @@ export function RecordingPill() {
|
|
|
65
66
|
const tickRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
66
67
|
// Mic and system audio share one calm activity meter, matching Granola's
|
|
67
68
|
// single indicator for the combined meeting capture.
|
|
68
|
-
const levelRef = useRef(0);
|
|
69
|
-
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
70
|
-
const rafRef = useRef<number | null>(null);
|
|
71
69
|
const stopFallbackRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
72
70
|
const dragStartScreenPointRef = useRef<ScreenPoint | null>(null);
|
|
73
71
|
|
|
@@ -165,15 +163,6 @@ export function RecordingPill() {
|
|
|
165
163
|
if (ev.payload?.detached) setExpanded(false);
|
|
166
164
|
}),
|
|
167
165
|
);
|
|
168
|
-
trackListen(
|
|
169
|
-
listen<{ level: number; source?: "mic" | "system" }>(
|
|
170
|
-
"voice:audio-level",
|
|
171
|
-
(ev) => {
|
|
172
|
-
const lvl = Math.max(0, Math.min(1, ev.payload.level));
|
|
173
|
-
levelRef.current = lvl;
|
|
174
|
-
},
|
|
175
|
-
),
|
|
176
|
-
);
|
|
177
166
|
// Signal that all listeners are registered. app.tsx listens for this and
|
|
178
167
|
// re-emits the pill context and transcript preload for a fresh window.
|
|
179
168
|
emit("clips:pill-ready", {}).catch(() => {});
|
|
@@ -207,62 +196,6 @@ export function RecordingPill() {
|
|
|
207
196
|
};
|
|
208
197
|
}, [ctx.mode, paused]);
|
|
209
198
|
|
|
210
|
-
// One combined "dancing bars" meter — a few discrete vertical bars instead
|
|
211
|
-
// of separate mic and system waveforms.
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
const canvas = canvasRef.current;
|
|
214
|
-
if (!canvas) return;
|
|
215
|
-
const N_BARS = 3;
|
|
216
|
-
const dpr = window.devicePixelRatio || 1;
|
|
217
|
-
const W = canvas.clientWidth;
|
|
218
|
-
const H = canvas.clientHeight;
|
|
219
|
-
canvas.width = W * dpr;
|
|
220
|
-
canvas.height = H * dpr;
|
|
221
|
-
const ctx2d = canvas.getContext("2d");
|
|
222
|
-
if (!ctx2d) return;
|
|
223
|
-
ctx2d.scale(dpr, dpr);
|
|
224
|
-
const slot = W / N_BARS;
|
|
225
|
-
const gap = Math.max(2, slot * 0.3);
|
|
226
|
-
const barWidth = Math.max(3, slot - gap);
|
|
227
|
-
const centerY = H / 2;
|
|
228
|
-
const startMs = Date.now();
|
|
229
|
-
let lastDrawMs = 0;
|
|
230
|
-
const FRAME_INTERVAL_MS = 1000 / 20;
|
|
231
|
-
const tick = () => {
|
|
232
|
-
rafRef.current = requestAnimationFrame(tick);
|
|
233
|
-
const nowMs = Date.now();
|
|
234
|
-
if (nowMs - lastDrawMs < FRAME_INTERVAL_MS) return;
|
|
235
|
-
lastDrawMs = nowMs;
|
|
236
|
-
const t = (nowMs - startMs) % 1_000_000;
|
|
237
|
-
const target = Math.min(1, levelRef.current);
|
|
238
|
-
ctx2d.clearRect(0, 0, W, H);
|
|
239
|
-
ctx2d.fillStyle = "rgba(132, 204, 22, 0.98)";
|
|
240
|
-
ctx2d.shadowColor = "rgba(132, 204, 22, 0.48)";
|
|
241
|
-
ctx2d.shadowBlur = 4;
|
|
242
|
-
for (let i = 0; i < N_BARS; i += 1) {
|
|
243
|
-
const phase = t * 0.005 + i * (Math.PI * 0.65);
|
|
244
|
-
const barTarget = 0.2 + Math.sin(phase) * 0.42 * target + target * 0.38;
|
|
245
|
-
const h = Math.max(4, Math.min(1, barTarget) * H * 0.92);
|
|
246
|
-
const x = i * (barWidth + gap) + gap / 2;
|
|
247
|
-
const y = centerY - h / 2;
|
|
248
|
-
const radius = Math.min(barWidth / 2, 3);
|
|
249
|
-
ctx2d.beginPath();
|
|
250
|
-
if (typeof ctx2d.roundRect === "function") {
|
|
251
|
-
ctx2d.roundRect(x, y, barWidth, h, radius);
|
|
252
|
-
} else {
|
|
253
|
-
ctx2d.rect(x, y, barWidth, h);
|
|
254
|
-
}
|
|
255
|
-
ctx2d.fill();
|
|
256
|
-
}
|
|
257
|
-
ctx2d.shadowBlur = 0;
|
|
258
|
-
};
|
|
259
|
-
tick();
|
|
260
|
-
return () => {
|
|
261
|
-
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
262
|
-
rafRef.current = null;
|
|
263
|
-
};
|
|
264
|
-
}, [expanded]);
|
|
265
|
-
|
|
266
199
|
// Let the compact chip land first, then reveal the live transcript once per
|
|
267
200
|
// meeting. The delay keeps the indicator from feeling like a sudden panel.
|
|
268
201
|
useEffect(() => {
|
|
@@ -380,7 +313,10 @@ export function RecordingPill() {
|
|
|
380
313
|
onClick={!expanded && !detached ? handlePillMediaClick : undefined}
|
|
381
314
|
>
|
|
382
315
|
<PillLogo className="pill-logo" />
|
|
383
|
-
<
|
|
316
|
+
<LiveAudioBars
|
|
317
|
+
compact={!expanded && !detached}
|
|
318
|
+
className="pill-wave-meter"
|
|
319
|
+
/>
|
|
384
320
|
</div>
|
|
385
321
|
<div className="pill-controls">
|
|
386
322
|
<span className="pill-timer">
|
|
@@ -805,13 +805,45 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
805
805
|
fill: none;
|
|
806
806
|
/* Matches the recording pill's mic waveform accent. */
|
|
807
807
|
stroke: rgba(74, 222, 128, 0.95);
|
|
808
|
-
stroke-width: 1.
|
|
808
|
+
stroke-width: 1.25;
|
|
809
809
|
stroke-linecap: round;
|
|
810
810
|
stroke-linejoin: round;
|
|
811
811
|
/* Keep the line crisp regardless of the non-uniform x-stretch. */
|
|
812
812
|
vector-effect: non-scaling-stroke;
|
|
813
813
|
}
|
|
814
814
|
|
|
815
|
+
.live-audio-bars {
|
|
816
|
+
display: inline-flex;
|
|
817
|
+
width: 48px;
|
|
818
|
+
height: 26px;
|
|
819
|
+
align-items: center;
|
|
820
|
+
justify-content: center;
|
|
821
|
+
gap: 4px;
|
|
822
|
+
flex-shrink: 0;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.live-audio-bars-compact {
|
|
826
|
+
width: 22px;
|
|
827
|
+
height: 22px;
|
|
828
|
+
gap: 3px;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.live-audio-bar {
|
|
832
|
+
display: block;
|
|
833
|
+
width: 2px;
|
|
834
|
+
min-height: 3px;
|
|
835
|
+
border-radius: 999px;
|
|
836
|
+
background: rgba(132, 204, 22, 0.98);
|
|
837
|
+
box-shadow: 0 0 5px rgba(132, 204, 22, 0.35);
|
|
838
|
+
transition: height 70ms ease-out;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
@media (prefers-reduced-motion: reduce) {
|
|
842
|
+
.live-audio-bar {
|
|
843
|
+
transition: none;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
815
847
|
/* ------------------------------------------------------------------------- */
|
|
816
848
|
/* Primary CTA */
|
|
817
849
|
/* ------------------------------------------------------------------------- */
|
|
@@ -5534,7 +5566,7 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
5534
5566
|
display: none;
|
|
5535
5567
|
}
|
|
5536
5568
|
|
|
5537
|
-
.pill-vertical .pill-wave-
|
|
5569
|
+
.pill-vertical .pill-wave-meter {
|
|
5538
5570
|
height: 22px;
|
|
5539
5571
|
width: 22px;
|
|
5540
5572
|
}
|
|
@@ -5569,7 +5601,7 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
5569
5601
|
font-variant-numeric: tabular-nums;
|
|
5570
5602
|
}
|
|
5571
5603
|
|
|
5572
|
-
.pill-wave-
|
|
5604
|
+
.pill-wave-meter {
|
|
5573
5605
|
height: 26px;
|
|
5574
5606
|
width: 48px;
|
|
5575
5607
|
flex-shrink: 0;
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
//!
|
|
3
3
|
//! Polls the frontmost macOS app every few seconds. When Zoom or Teams stays
|
|
4
4
|
//! frontmost for a short dwell window, creates a meeting row via
|
|
5
|
-
//! `create-meeting
|
|
6
|
-
//!
|
|
5
|
+
//! `create-meeting`, and silently starts transcription when the user has
|
|
6
|
+
//! explicitly selected Auto mode. Ask/manual mode stays quiet because a
|
|
7
|
+
//! frontmost Zoom window is not reliable evidence of an active call.
|
|
7
8
|
//!
|
|
8
9
|
//! Reuses `MeetingsWatcherState` session (server URL + cookie + auth token)
|
|
9
10
|
//! so the popover only needs to push credentials once.
|
|
@@ -157,7 +158,7 @@ async fn tick_macos(
|
|
|
157
158
|
let front = crate::util::frontmost_bundle_id();
|
|
158
159
|
let matched = front.as_deref().and_then(match_vc_bundle);
|
|
159
160
|
|
|
160
|
-
let Some((platform,
|
|
161
|
+
let Some((platform, _title)) = matched else {
|
|
161
162
|
// VC app left front — clear session dedupe so a later re-focus can
|
|
162
163
|
// fire again (cooldown still applies).
|
|
163
164
|
clear_session_if_left(app, None);
|
|
@@ -173,9 +174,7 @@ async fn tick_macos(
|
|
|
173
174
|
return Ok(());
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
if config.meeting_transcription_mode
|
|
177
|
-
&& !config.show_meeting_widget_enabled
|
|
178
|
-
{
|
|
177
|
+
if config.meeting_transcription_mode != MeetingTranscriptionMode::Auto {
|
|
179
178
|
reset_dwell(app);
|
|
180
179
|
return Ok(());
|
|
181
180
|
}
|
|
@@ -250,44 +249,14 @@ async fn tick_macos(
|
|
|
250
249
|
}
|
|
251
250
|
};
|
|
252
251
|
|
|
253
|
-
let
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
let title_clone = title.to_string();
|
|
262
|
-
let platform_clone = platform.to_string();
|
|
263
|
-
let scheduled_start = chrono::Utc::now().to_rfc3339();
|
|
264
|
-
tauri::async_runtime::spawn(async move {
|
|
265
|
-
let _ = crate::notifications::notify_meeting_starting(
|
|
266
|
-
app_clone,
|
|
267
|
-
id_clone,
|
|
268
|
-
title_clone,
|
|
269
|
-
0,
|
|
270
|
-
None,
|
|
271
|
-
Some(scheduled_start),
|
|
272
|
-
None,
|
|
273
|
-
Some(platform_clone),
|
|
274
|
-
Some(auto_start),
|
|
275
|
-
Some("adhoc".to_string()),
|
|
276
|
-
)
|
|
277
|
-
.await;
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if auto_start {
|
|
282
|
-
let _ = app.emit(
|
|
283
|
-
"meetings:start-transcription",
|
|
284
|
-
serde_json::json!({
|
|
285
|
-
"meetingId": meeting_id,
|
|
286
|
-
"joinUrl": null,
|
|
287
|
-
"reason": "adhoc-auto",
|
|
288
|
-
}),
|
|
289
|
-
);
|
|
290
|
-
}
|
|
252
|
+
let _ = app.emit(
|
|
253
|
+
"meetings:start-transcription",
|
|
254
|
+
serde_json::json!({
|
|
255
|
+
"meetingId": meeting_id,
|
|
256
|
+
"joinUrl": null,
|
|
257
|
+
"reason": "adhoc-auto",
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
291
260
|
|
|
292
261
|
Ok(())
|
|
293
262
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
use serde::{Deserialize, Serialize};
|
|
8
8
|
use serde_json::Value;
|
|
9
|
+
use std::collections::HashMap;
|
|
9
10
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
10
11
|
use std::sync::Mutex;
|
|
11
12
|
use std::time::Duration;
|
|
@@ -28,9 +29,16 @@ const NOTIFICATION_W_LOGICAL: u32 = 504;
|
|
|
28
29
|
const NOTIFICATION_H_LOGICAL: u32 = 120;
|
|
29
30
|
const NOTIFICATION_TOP_MARGIN_LOGICAL: u32 = 44;
|
|
30
31
|
const NOTIFICATION_RIGHT_MARGIN_LOGICAL: u32 = 0;
|
|
32
|
+
const DISMISSAL_TOMBSTONE_SECS: i64 = 30 * 60;
|
|
31
33
|
|
|
32
34
|
#[derive(Default)]
|
|
33
|
-
pub struct MeetingNotificationState(
|
|
35
|
+
pub struct MeetingNotificationState(Mutex<MeetingNotificationStateInner>);
|
|
36
|
+
|
|
37
|
+
#[derive(Default)]
|
|
38
|
+
struct MeetingNotificationStateInner {
|
|
39
|
+
pending: Option<Value>,
|
|
40
|
+
dismissed_until: HashMap<String, i64>,
|
|
41
|
+
}
|
|
34
42
|
|
|
35
43
|
#[allow(dead_code)]
|
|
36
44
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
@@ -176,22 +184,54 @@ pub fn show_meeting_notification_window(app: &AppHandle) -> Result<(), String> {
|
|
|
176
184
|
Ok(())
|
|
177
185
|
}
|
|
178
186
|
|
|
179
|
-
fn store_pending_meeting_notification(app: &AppHandle, payload: &Value) {
|
|
180
|
-
if let Some(state) = app.try_state::<MeetingNotificationState>() {
|
|
181
|
-
if let Ok(mut pending) = state.0.lock() {
|
|
182
|
-
*pending = Some(payload.clone());
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
187
|
#[tauri::command]
|
|
188
188
|
pub fn take_pending_meeting_notification(app: AppHandle) -> Result<Option<Value>, String> {
|
|
189
189
|
let state = app.state::<MeetingNotificationState>();
|
|
190
|
-
let mut
|
|
190
|
+
let mut state = state
|
|
191
191
|
.0
|
|
192
192
|
.lock()
|
|
193
193
|
.map_err(|_| "meeting notification state lock poisoned".to_string())?;
|
|
194
|
-
|
|
194
|
+
let now = chrono::Utc::now().timestamp();
|
|
195
|
+
state.dismissed_until.retain(|_, until| *until > now);
|
|
196
|
+
if state.pending.as_ref().is_some_and(|payload| {
|
|
197
|
+
state
|
|
198
|
+
.dismissed_until
|
|
199
|
+
.contains_key(¬ification_key(payload))
|
|
200
|
+
}) {
|
|
201
|
+
state.pending = None;
|
|
202
|
+
}
|
|
203
|
+
Ok(state.pending.take())
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fn notification_key(payload: &Value) -> String {
|
|
207
|
+
format!(
|
|
208
|
+
"{}|{}|{}",
|
|
209
|
+
payload
|
|
210
|
+
.get("type")
|
|
211
|
+
.and_then(Value::as_str)
|
|
212
|
+
.unwrap_or("calendar"),
|
|
213
|
+
payload
|
|
214
|
+
.get("meetingId")
|
|
215
|
+
.and_then(Value::as_str)
|
|
216
|
+
.unwrap_or_default(),
|
|
217
|
+
payload
|
|
218
|
+
.get("scheduledStart")
|
|
219
|
+
.and_then(Value::as_str)
|
|
220
|
+
.unwrap_or_default(),
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fn notification_key_from_parts(
|
|
225
|
+
notification_type: &str,
|
|
226
|
+
meeting_id: &str,
|
|
227
|
+
scheduled_start: Option<&str>,
|
|
228
|
+
) -> String {
|
|
229
|
+
format!(
|
|
230
|
+
"{}|{}|{}",
|
|
231
|
+
notification_type,
|
|
232
|
+
meeting_id,
|
|
233
|
+
scheduled_start.unwrap_or_default()
|
|
234
|
+
)
|
|
195
235
|
}
|
|
196
236
|
|
|
197
237
|
fn clear_pending_notification(pending: &mut Option<Value>, meeting_id: &str) -> bool {
|
|
@@ -211,19 +251,38 @@ pub fn dismiss_meeting_notification(
|
|
|
211
251
|
app: AppHandle,
|
|
212
252
|
meeting_id: String,
|
|
213
253
|
notification_type: String,
|
|
254
|
+
join_url: Option<String>,
|
|
214
255
|
platform: Option<String>,
|
|
215
256
|
scheduled_start: Option<String>,
|
|
216
257
|
scheduled_end: Option<String>,
|
|
217
258
|
) -> Result<(), String> {
|
|
218
259
|
let state = app.state::<MeetingNotificationState>();
|
|
219
|
-
let mut
|
|
260
|
+
let mut state = state
|
|
220
261
|
.0
|
|
221
262
|
.lock()
|
|
222
263
|
.map_err(|_| "meeting notification state lock poisoned".to_string())?;
|
|
223
|
-
|
|
224
|
-
|
|
264
|
+
let now = chrono::Utc::now().timestamp();
|
|
265
|
+
state.dismissed_until.retain(|_, until| *until > now);
|
|
266
|
+
state.dismissed_until.insert(
|
|
267
|
+
notification_key_from_parts(¬ification_type, &meeting_id, scheduled_start.as_deref()),
|
|
268
|
+
now + DISMISSAL_TOMBSTONE_SECS,
|
|
269
|
+
);
|
|
270
|
+
clear_pending_notification(&mut state.pending, &meeting_id);
|
|
271
|
+
drop(state);
|
|
225
272
|
|
|
226
|
-
|
|
273
|
+
let suppression_platform = platform.as_deref().or_else(|| {
|
|
274
|
+
join_url.as_deref().and_then(|url| {
|
|
275
|
+
let url = url.to_ascii_lowercase();
|
|
276
|
+
if url.contains("zoom") {
|
|
277
|
+
Some("zoom")
|
|
278
|
+
} else if url.contains("teams.microsoft") || url.contains("teams") {
|
|
279
|
+
Some("teams")
|
|
280
|
+
} else {
|
|
281
|
+
None
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
});
|
|
285
|
+
if let Some(platform) = suppression_platform {
|
|
227
286
|
crate::adhoc_meetings_watcher::refresh_dismissal_suppression(&app, platform)?;
|
|
228
287
|
}
|
|
229
288
|
|
|
@@ -312,14 +371,41 @@ pub async fn notify_meeting_starting(
|
|
|
312
371
|
"type": kind,
|
|
313
372
|
"title": title,
|
|
314
373
|
"subtitle": body,
|
|
315
|
-
"meetingId": meeting_id,
|
|
374
|
+
"meetingId": meeting_id.clone(),
|
|
316
375
|
"joinUrl": join_url,
|
|
317
376
|
"platform": platform,
|
|
318
377
|
"scheduledStart": scheduled_start,
|
|
319
378
|
"scheduledEnd": scheduled_end,
|
|
320
379
|
"autoStart": auto_start.unwrap_or(false),
|
|
321
380
|
});
|
|
322
|
-
|
|
381
|
+
let state = app.state::<MeetingNotificationState>();
|
|
382
|
+
let should_show = {
|
|
383
|
+
let mut state = state
|
|
384
|
+
.0
|
|
385
|
+
.lock()
|
|
386
|
+
.map_err(|_| "meeting notification state lock poisoned".to_string())?;
|
|
387
|
+
let now = chrono::Utc::now().timestamp();
|
|
388
|
+
state.dismissed_until.retain(|_, until| *until > now);
|
|
389
|
+
if state
|
|
390
|
+
.dismissed_until
|
|
391
|
+
.get(¬ification_key(&payload))
|
|
392
|
+
.copied()
|
|
393
|
+
.unwrap_or_default()
|
|
394
|
+
> now
|
|
395
|
+
{
|
|
396
|
+
false
|
|
397
|
+
} else {
|
|
398
|
+
state.pending = Some(payload.clone());
|
|
399
|
+
true
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
if !should_show {
|
|
403
|
+
dlog!(
|
|
404
|
+
"[clips-tray] skipped dismissed meeting notification id={}",
|
|
405
|
+
meeting_id
|
|
406
|
+
);
|
|
407
|
+
return Ok(());
|
|
408
|
+
}
|
|
323
409
|
let _ = app.emit("meetings:show-notification", payload.clone());
|
|
324
410
|
|
|
325
411
|
// Ensure the overlay window exists / is visible for cold starts.
|
|
@@ -346,4 +432,20 @@ mod tests {
|
|
|
346
432
|
assert!(clear_pending_notification(&mut pending, "meeting-1"));
|
|
347
433
|
assert!(pending.is_none());
|
|
348
434
|
}
|
|
435
|
+
|
|
436
|
+
#[test]
|
|
437
|
+
fn dismissal_key_changes_when_a_meeting_is_rescheduled() {
|
|
438
|
+
let first = serde_json::json!({
|
|
439
|
+
"type": "calendar",
|
|
440
|
+
"meetingId": "meeting-1",
|
|
441
|
+
"scheduledStart": "2026-07-23T17:00:00Z"
|
|
442
|
+
});
|
|
443
|
+
let moved = serde_json::json!({
|
|
444
|
+
"type": "calendar",
|
|
445
|
+
"meetingId": "meeting-1",
|
|
446
|
+
"scheduledStart": "2026-07-23T18:00:00Z"
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
assert_ne!(notification_key(&first), notification_key(&moved));
|
|
450
|
+
}
|
|
349
451
|
}
|