@agent-native/core 0.134.1 → 0.135.1
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 +113 -0
- package/corpus/core/package.json +3 -2
- package/corpus/core/src/automations/service.ts +41 -4
- package/corpus/core/src/cli/mcp-config-writers.ts +27 -21
- package/corpus/core/src/client/agent-page/AgentJobsTab.tsx +187 -85
- package/corpus/core/src/client/agent-page/AutomationDetailsDialog.tsx +208 -0
- package/corpus/core/src/client/agent-page/AutomationScheduleDialog.tsx +180 -0
- package/corpus/core/src/client/agent-page/TimezoneSelect.tsx +241 -0
- package/corpus/core/src/client/agent-page/use-jobs.ts +50 -10
- package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -0
- package/corpus/core/src/client/settings/AccountSettingsCard.tsx +35 -31
- package/corpus/core/src/client/settings/SchedulingTimezoneField.tsx +71 -0
- package/corpus/core/src/client/settings/SettingsRow.tsx +113 -0
- package/corpus/core/src/client/settings/agent-settings-search.ts +2 -1
- package/corpus/core/src/client/settings/index.ts +6 -0
- package/corpus/core/src/integrations/identity.ts +2 -12
- package/corpus/core/src/jobs/actions/list-automation-runs.ts +33 -0
- package/corpus/core/src/jobs/actions/list-recurring-jobs.ts +27 -6
- package/corpus/core/src/jobs/actions/manage-recurring-job.ts +41 -9
- package/corpus/core/src/jobs/background-automation-runner.ts +88 -0
- package/corpus/core/src/jobs/cron.ts +52 -6
- package/corpus/core/src/jobs/frontmatter.ts +20 -0
- package/corpus/core/src/jobs/run-history.ts +242 -0
- package/corpus/core/src/jobs/scheduler.ts +73 -18
- package/corpus/core/src/jobs/tools.ts +54 -6
- package/corpus/core/src/localization/actions/get-localization-preference.ts +2 -2
- package/corpus/core/src/localization/actions/set-localization-preference.ts +49 -11
- package/corpus/core/src/localization/default-messages.ts +27 -0
- package/corpus/core/src/localization/shared.ts +39 -6
- package/corpus/core/src/localization/user-timezone.ts +36 -0
- package/corpus/core/src/org/app-roles-handlers.ts +1 -9
- package/corpus/core/src/org/index.ts +2 -0
- package/corpus/core/src/org/membership.ts +18 -0
- package/corpus/core/src/review/actions/create-review-comment.ts +4 -1
- package/corpus/core/src/review/actions/reply-review-comment.ts +4 -1
- package/corpus/core/src/review/index.ts +5 -0
- package/corpus/core/src/review/notifications.ts +163 -0
- package/corpus/core/src/review/types.ts +7 -0
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/activity-notifications.ts +174 -0
- package/corpus/core/src/server/app-name.ts +31 -0
- package/corpus/core/src/server/better-auth-instance.ts +4 -4
- package/corpus/core/src/server/email-templates.ts +70 -14
- package/corpus/core/src/server/email.ts +66 -3
- package/corpus/core/src/server/index.ts +10 -0
- package/corpus/core/src/sharing/access.ts +44 -2
- package/corpus/core/src/sharing/index.ts +5 -0
- package/corpus/core/src/sharing/recipients.ts +67 -0
- package/corpus/core/src/triggers/actions/list-automations.ts +27 -8
- package/corpus/core/src/triggers/actions/manage-automation.ts +17 -6
- package/corpus/core/src/triggers/actions.ts +16 -4
- package/corpus/core/src/triggers/dispatcher.ts +44 -30
- package/corpus/core/src/triggers/routes.ts +19 -6
- package/corpus/core/src/vitest-config.ts +68 -0
- package/corpus/core/vitest.config.ts +5 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +37 -53
- package/corpus/templates/analytics/changelog/2026-08-01-new-error-alerts-now-reach-you-by-email-and-a-scheduled-dash.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +43 -0
- package/corpus/templates/analytics/server/lib/error-capture.ts +5 -1
- package/corpus/templates/analytics/vitest.config.ts +16 -12
- package/corpus/templates/assets/actions/refresh-generation-run.ts +3 -0
- package/corpus/templates/assets/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/assets/app/hooks/use-assets-prefs.ts +55 -0
- package/corpus/templates/assets/app/i18n-data.ts +30 -0
- package/corpus/templates/assets/app/routes/settings.tsx +58 -55
- package/corpus/templates/assets/changelog/2026-08-01-get-an-email-when-a-generation-you-started-finishes-or-fails.md +6 -0
- package/corpus/templates/assets/server/lib/generation-run-notifications.ts +89 -0
- package/corpus/templates/assets/server/lib/video-runs.ts +8 -1
- package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.get.ts +17 -0
- package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.put.ts +36 -0
- package/corpus/templates/assets/shared/assets-user-prefs.ts +10 -0
- package/corpus/templates/assets/vitest.config.ts +16 -12
- package/corpus/templates/brain/app/routes/settings.tsx +15 -15
- package/corpus/templates/brain/vitest.config.ts +16 -12
- package/corpus/templates/calendar/app/pages/Settings.tsx +32 -40
- package/corpus/templates/calendar/changelog/2026-08-01-language-and-appearance-now-sit-together-in-one-preferences-.md +6 -0
- package/corpus/templates/calendar/vitest.config.ts +16 -12
- package/corpus/templates/chat/app/routes/settings.tsx +14 -22
- package/corpus/templates/chat/vitest.config.ts +9 -0
- package/corpus/templates/clips/.agents/skills/video-editing/SKILL.md +31 -0
- package/corpus/templates/clips/AGENTS.md +1 -0
- package/corpus/templates/clips/actions/add-comment.ts +12 -1
- package/corpus/templates/clips/actions/create-meeting.ts +2 -2
- package/corpus/templates/clips/actions/create-recording.ts +2 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +42 -6
- package/corpus/templates/clips/actions/generate-filmstrip.ts +154 -0
- package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -0
- package/corpus/templates/clips/actions/import-loom-recording.ts +2 -2
- package/corpus/templates/clips/actions/lib/ensure-recording-filmstrip.ts +258 -0
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +11 -3
- package/corpus/templates/clips/actions/react-to-comment.ts +13 -0
- package/corpus/templates/clips/actions/react-to-recording.ts +10 -1
- package/corpus/templates/clips/actions/reply-to-comment.ts +12 -1
- package/corpus/templates/clips/actions/stitch-recordings.ts +2 -2
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +152 -23
- package/corpus/templates/clips/app/components/editor/waveform.tsx +237 -75
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +23 -1
- package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +441 -0
- package/corpus/templates/clips/app/components/settings/slack-section.tsx +329 -0
- package/corpus/templates/clips/app/components/settings/types.ts +14 -0
- package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +443 -0
- package/corpus/templates/clips/app/hooks/use-secret-status.ts +69 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +9 -1
- package/corpus/templates/clips/app/lib/video-filmstrip.ts +262 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +165 -1351
- package/corpus/templates/clips/changelog/2026-07-27-saved-clips-now-resume-from-the-last-uploaded-byte-after-an-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-31-editor-timeline-now-shows-video-frames-behind-the-waveform-g.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-31-email-notifications-now-go-out-when-someone-comments-on-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-31-you-can-now-set-a-default-visibility-for-new-recordings-in-s.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-01-email-notifications-for-comments-and-reactions-now-actually-.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-03-the-desktop-app-now-opens-directly-beneath-its-tray-icon-on-.md +6 -0
- package/corpus/templates/clips/chrome-extension/package.json +1 -0
- package/corpus/templates/clips/chrome-extension/vitest.config.ts +9 -0
- package/corpus/templates/clips/desktop/package.json +1 -0
- package/corpus/templates/clips/desktop/src/app.tsx +17 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +331 -36
- package/corpus/templates/clips/desktop/src/lib/upload-recovery.ts +129 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +815 -15
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +61 -11
- package/corpus/templates/clips/desktop/vitest.config.ts +9 -0
- package/corpus/templates/clips/server/db/schema.ts +17 -0
- package/corpus/templates/clips/server/jobs/media-verification.ts +4 -0
- package/corpus/templates/clips/server/lib/activity-notifications.ts +185 -0
- package/corpus/templates/clips/server/lib/recording-media-cleanup.ts +2 -0
- package/corpus/templates/clips/server/lib/recording-upload-state.ts +46 -9
- package/corpus/templates/clips/server/lib/recordings.ts +24 -0
- package/corpus/templates/clips/server/lib/resumable-session.ts +10 -4
- package/corpus/templates/clips/server/lib/transactional-email-templates.ts +109 -0
- package/corpus/templates/clips/server/lib/upload-interruption.ts +8 -0
- package/corpus/templates/clips/server/lib/upload-lease.ts +50 -4
- package/corpus/templates/clips/server/lib/video-filmstrip-sprite.ts +207 -0
- package/corpus/templates/clips/server/lib/video-remux.ts +13 -4
- package/corpus/templates/clips/server/plugins/db.ts +16 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +4 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +123 -36
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +159 -29
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +197 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +193 -39
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/resume.get.ts +191 -12
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +6 -0
- package/corpus/templates/clips/shared/feature-flags.ts +12 -0
- package/corpus/templates/clips/shared/recording-core.ts +4 -0
- package/corpus/templates/clips/vitest.config.ts +23 -19
- package/corpus/templates/content/actions/_database-utils.ts +113 -4
- package/corpus/templates/content/actions/add-comment.ts +15 -1
- package/corpus/templates/content/actions/get-document.ts +84 -3
- package/corpus/templates/content/actions/list-documents.ts +1 -0
- package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +9 -2
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +34 -15
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +55 -13
- package/corpus/templates/content/app/components/editor/body-hydration.ts +12 -6
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2 -3
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -12
- package/corpus/templates/content/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +16 -27
- package/corpus/templates/content/app/hooks/use-content-prefs.ts +55 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +3 -6
- package/corpus/templates/content/app/hooks/use-document-properties.ts +9 -16
- package/corpus/templates/content/app/hooks/use-document-versions.ts +3 -3
- package/corpus/templates/content/app/hooks/use-documents.ts +85 -29
- package/corpus/templates/content/app/hooks/use-notion.ts +15 -13
- package/corpus/templates/content/app/i18n-data.ts +72 -1
- package/corpus/templates/content/app/lib/document-query.ts +36 -0
- package/corpus/templates/content/app/routes/_app.settings.tsx +47 -18
- package/corpus/templates/content/changelog/2026-07-30-breadcrumb-menus-now-hide-internal-system-pages.md +6 -0
- package/corpus/templates/content/changelog/2026-08-01-get-an-email-when-someone-comments-on-replies-in-or-mentions.md +6 -0
- package/corpus/templates/content/changelog/2026-08-02-pages-opened-from-a-database-now-keep-that-database-s-fields.md +6 -0
- package/corpus/templates/content/server/lib/comment-notifications.ts +146 -0
- package/corpus/templates/content/server/lib/document-context.ts +11 -6
- package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.get.ts +17 -0
- package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.put.ts +36 -0
- package/corpus/templates/content/shared/api.ts +8 -0
- package/corpus/templates/content/shared/content-user-prefs.ts +11 -0
- package/corpus/templates/content/vitest.config.ts +20 -16
- package/corpus/templates/crm/app/routes/settings.tsx +14 -22
- package/corpus/templates/crm/vitest.config.ts +8 -4
- package/corpus/templates/design/app/routes/settings.tsx +14 -22
- package/corpus/templates/design/changelog/2026-08-01-review-comments-replies-and-mentions-now-send-an-email-to-th.md +6 -0
- package/corpus/templates/design/vitest.config.ts +18 -12
- package/corpus/templates/dispatch/app/routes/settings.tsx +26 -40
- package/corpus/templates/dispatch/vitest.config.ts +11 -7
- package/corpus/templates/factory/app/routes/settings.tsx +14 -22
- package/corpus/templates/factory/vitest.config.ts +9 -0
- package/corpus/templates/forms/app/routes/_app.settings.tsx +14 -22
- package/corpus/templates/forms/vitest.config.ts +27 -23
- package/corpus/templates/macros/app/routes/settings.tsx +14 -22
- package/corpus/templates/macros/vitest.config.ts +16 -12
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +14 -16
- package/corpus/templates/mail/vitest.config.ts +16 -12
- package/corpus/templates/plan/app/routes/settings.tsx +30 -44
- package/corpus/templates/plan/vitest.config.ts +24 -20
- package/corpus/templates/slides/actions/add-slide-comment.ts +12 -1
- package/corpus/templates/slides/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/slides/app/hooks/use-slides-prefs.ts +55 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
- package/corpus/templates/slides/app/routes/settings.tsx +45 -22
- package/corpus/templates/slides/changelog/2026-08-01-get-an-email-when-someone-comments-on-or-replies-in-your-dec.md +6 -0
- package/corpus/templates/slides/server/lib/comment-notifications.ts +182 -0
- package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.get.ts +16 -0
- package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.put.ts +36 -0
- package/corpus/templates/slides/shared/slides-user-prefs.ts +11 -0
- package/corpus/templates/slides/vitest.config.ts +17 -13
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/tasks/app/routes/settings.tsx +14 -22
- package/corpus/templates/tasks/vitest.config.ts +17 -13
- package/corpus/toolkit/CHANGELOG.md +9 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/vitest.config.ts +3 -0
- package/dist/automations/service.d.ts +2 -0
- package/dist/automations/service.d.ts.map +1 -1
- package/dist/automations/service.js +29 -4
- package/dist/automations/service.js.map +1 -1
- package/dist/cli/mcp-config-writers.d.ts.map +1 -1
- package/dist/cli/mcp-config-writers.js +24 -23
- package/dist/cli/mcp-config-writers.js.map +1 -1
- package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -1
- package/dist/client/agent-page/AgentJobsTab.js +86 -29
- package/dist/client/agent-page/AgentJobsTab.js.map +1 -1
- package/dist/client/agent-page/AutomationDetailsDialog.d.ts +21 -0
- package/dist/client/agent-page/AutomationDetailsDialog.d.ts.map +1 -0
- package/dist/client/agent-page/AutomationDetailsDialog.js +33 -0
- package/dist/client/agent-page/AutomationDetailsDialog.js.map +1 -0
- package/dist/client/agent-page/AutomationScheduleDialog.d.ts +15 -0
- package/dist/client/agent-page/AutomationScheduleDialog.d.ts.map +1 -0
- package/dist/client/agent-page/AutomationScheduleDialog.js +46 -0
- package/dist/client/agent-page/AutomationScheduleDialog.js.map +1 -0
- package/dist/client/agent-page/TimezoneSelect.d.ts +17 -0
- package/dist/client/agent-page/TimezoneSelect.d.ts.map +1 -0
- package/dist/client/agent-page/TimezoneSelect.js +107 -0
- package/dist/client/agent-page/TimezoneSelect.js.map +1 -0
- package/dist/client/agent-page/use-jobs.d.ts +20 -7
- package/dist/client/agent-page/use-jobs.d.ts.map +1 -1
- package/dist/client/agent-page/use-jobs.js +24 -4
- package/dist/client/agent-page/use-jobs.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +8 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +8 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -1
- package/dist/client/settings/AccountSettingsCard.js +10 -9
- package/dist/client/settings/AccountSettingsCard.js.map +1 -1
- package/dist/client/settings/SchedulingTimezoneField.d.ts +10 -0
- package/dist/client/settings/SchedulingTimezoneField.d.ts.map +1 -0
- package/dist/client/settings/SchedulingTimezoneField.js +30 -0
- package/dist/client/settings/SchedulingTimezoneField.js.map +1 -0
- package/dist/client/settings/SettingsRow.d.ts +36 -0
- package/dist/client/settings/SettingsRow.d.ts.map +1 -0
- package/dist/client/settings/SettingsRow.js +15 -0
- package/dist/client/settings/SettingsRow.js.map +1 -0
- package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
- package/dist/client/settings/agent-settings-search.js +1 -1
- package/dist/client/settings/agent-settings-search.js.map +1 -1
- package/dist/client/settings/index.d.ts +1 -0
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js +1 -0
- package/dist/client/settings/index.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/identity.d.ts.map +1 -1
- package/dist/integrations/identity.js +2 -11
- package/dist/integrations/identity.js.map +1 -1
- package/dist/jobs/actions/list-automation-runs.d.ts +8 -0
- package/dist/jobs/actions/list-automation-runs.d.ts.map +1 -0
- package/dist/jobs/actions/list-automation-runs.js +29 -0
- package/dist/jobs/actions/list-automation-runs.js.map +1 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts +2 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts.map +1 -1
- package/dist/jobs/actions/list-recurring-jobs.js +20 -7
- package/dist/jobs/actions/list-recurring-jobs.js.map +1 -1
- package/dist/jobs/actions/manage-recurring-job.d.ts +2 -0
- package/dist/jobs/actions/manage-recurring-job.js +30 -9
- package/dist/jobs/actions/manage-recurring-job.js.map +1 -1
- package/dist/jobs/background-automation-runner.d.ts.map +1 -1
- package/dist/jobs/background-automation-runner.js +60 -0
- package/dist/jobs/background-automation-runner.js.map +1 -1
- package/dist/jobs/cron.d.ts +16 -2
- package/dist/jobs/cron.d.ts.map +1 -1
- package/dist/jobs/cron.js +43 -6
- package/dist/jobs/cron.js.map +1 -1
- package/dist/jobs/frontmatter.d.ts +12 -0
- package/dist/jobs/frontmatter.d.ts.map +1 -1
- package/dist/jobs/frontmatter.js +8 -0
- package/dist/jobs/frontmatter.js.map +1 -1
- package/dist/jobs/run-history.d.ts +56 -0
- package/dist/jobs/run-history.d.ts.map +1 -0
- package/dist/jobs/run-history.js +169 -0
- package/dist/jobs/run-history.js.map +1 -0
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +51 -18
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/jobs/tools.d.ts.map +1 -1
- package/dist/jobs/tools.js +37 -6
- package/dist/jobs/tools.js.map +1 -1
- package/dist/localization/actions/get-localization-preference.d.ts +1 -2
- package/dist/localization/actions/get-localization-preference.d.ts.map +1 -1
- package/dist/localization/actions/get-localization-preference.js.map +1 -1
- package/dist/localization/actions/set-localization-preference.d.ts +3 -3
- package/dist/localization/actions/set-localization-preference.d.ts.map +1 -1
- package/dist/localization/actions/set-localization-preference.js +33 -8
- package/dist/localization/actions/set-localization-preference.js.map +1 -1
- package/dist/localization/default-messages.d.ts +25 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +25 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/localization/shared.d.ts +16 -1
- package/dist/localization/shared.d.ts.map +1 -1
- package/dist/localization/shared.js +22 -4
- package/dist/localization/shared.js.map +1 -1
- package/dist/localization/user-timezone.d.ts +11 -0
- package/dist/localization/user-timezone.d.ts.map +1 -0
- package/dist/localization/user-timezone.js +31 -0
- package/dist/localization/user-timezone.js.map +1 -0
- package/dist/notifications/routes.d.ts +4 -4
- package/dist/observability/routes.d.ts +3 -3
- package/dist/org/app-roles-handlers.d.ts.map +1 -1
- package/dist/org/app-roles-handlers.js +1 -8
- package/dist/org/app-roles-handlers.js.map +1 -1
- package/dist/org/index.d.ts +1 -0
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +1 -0
- package/dist/org/index.js.map +1 -1
- package/dist/org/membership.d.ts +6 -0
- package/dist/org/membership.d.ts.map +1 -0
- package/dist/org/membership.js +16 -0
- package/dist/org/membership.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/review/actions/create-review-comment.d.ts +32 -1
- package/dist/review/actions/create-review-comment.d.ts.map +1 -1
- package/dist/review/actions/create-review-comment.js +3 -1
- package/dist/review/actions/create-review-comment.js.map +1 -1
- package/dist/review/actions/reply-review-comment.d.ts +32 -1
- package/dist/review/actions/reply-review-comment.d.ts.map +1 -1
- package/dist/review/actions/reply-review-comment.js +3 -1
- package/dist/review/actions/reply-review-comment.js.map +1 -1
- package/dist/review/index.d.ts +1 -0
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +1 -0
- package/dist/review/index.js.map +1 -1
- package/dist/review/notifications.d.ts +24 -0
- package/dist/review/notifications.d.ts.map +1 -0
- package/dist/review/notifications.js +123 -0
- package/dist/review/notifications.js.map +1 -0
- package/dist/review/types.d.ts +5 -0
- package/dist/review/types.d.ts.map +1 -1
- package/dist/review/types.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/activity-notifications.d.ts +61 -0
- package/dist/server/activity-notifications.d.ts.map +1 -0
- package/dist/server/activity-notifications.js +103 -0
- package/dist/server/activity-notifications.js.map +1 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/app-name.d.ts +18 -0
- package/dist/server/app-name.d.ts.map +1 -1
- package/dist/server/app-name.js +31 -0
- package/dist/server/app-name.js.map +1 -1
- package/dist/server/better-auth-instance.js +4 -4
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/email-templates.d.ts +12 -0
- package/dist/server/email-templates.d.ts.map +1 -1
- package/dist/server/email-templates.js +57 -14
- package/dist/server/email-templates.js.map +1 -1
- package/dist/server/email.d.ts +12 -0
- package/dist/server/email.d.ts.map +1 -1
- package/dist/server/email.js +52 -5
- package/dist/server/email.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +32 -2
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/index.d.ts +1 -0
- package/dist/sharing/index.d.ts.map +1 -1
- package/dist/sharing/index.js +1 -0
- package/dist/sharing/index.js.map +1 -1
- package/dist/sharing/recipients.d.ts +31 -0
- package/dist/sharing/recipients.d.ts.map +1 -0
- package/dist/sharing/recipients.js +40 -0
- package/dist/sharing/recipients.js.map +1 -0
- package/dist/templates/chat/app/routes/settings.tsx +14 -22
- package/dist/templates/chat/vitest.config.ts +9 -0
- package/dist/triggers/actions/list-automations.d.ts +2 -0
- package/dist/triggers/actions/list-automations.d.ts.map +1 -1
- package/dist/triggers/actions/list-automations.js +19 -8
- package/dist/triggers/actions/list-automations.js.map +1 -1
- package/dist/triggers/actions/manage-automation.d.ts +6 -0
- package/dist/triggers/actions/manage-automation.js +12 -6
- package/dist/triggers/actions/manage-automation.js.map +1 -1
- package/dist/triggers/actions.d.ts.map +1 -1
- package/dist/triggers/actions.js +15 -4
- package/dist/triggers/actions.js.map +1 -1
- package/dist/triggers/dispatcher.d.ts.map +1 -1
- package/dist/triggers/dispatcher.js +19 -17
- package/dist/triggers/dispatcher.js.map +1 -1
- package/dist/triggers/routes.d.ts.map +1 -1
- package/dist/triggers/routes.js +6 -6
- package/dist/triggers/routes.js.map +1 -1
- package/dist/vitest-config.d.ts +10 -0
- package/dist/vitest-config.d.ts.map +1 -0
- package/dist/vitest-config.js +55 -0
- package/dist/vitest-config.js.map +1 -0
- package/package.json +5 -4
- package/src/automations/service.ts +41 -4
- package/src/cli/mcp-config-writers.ts +27 -21
- package/src/client/agent-page/AgentJobsTab.tsx +187 -85
- package/src/client/agent-page/AutomationDetailsDialog.tsx +208 -0
- package/src/client/agent-page/AutomationScheduleDialog.tsx +180 -0
- package/src/client/agent-page/TimezoneSelect.tsx +241 -0
- package/src/client/agent-page/use-jobs.ts +50 -10
- package/src/client/org/OrgSwitcher.tsx +26 -0
- package/src/client/settings/AccountSettingsCard.tsx +35 -31
- package/src/client/settings/SchedulingTimezoneField.tsx +71 -0
- package/src/client/settings/SettingsRow.tsx +113 -0
- package/src/client/settings/agent-settings-search.ts +2 -1
- package/src/client/settings/index.ts +6 -0
- package/src/integrations/identity.ts +2 -12
- package/src/jobs/actions/list-automation-runs.ts +33 -0
- package/src/jobs/actions/list-recurring-jobs.ts +27 -6
- package/src/jobs/actions/manage-recurring-job.ts +41 -9
- package/src/jobs/background-automation-runner.ts +88 -0
- package/src/jobs/cron.ts +52 -6
- package/src/jobs/frontmatter.ts +20 -0
- package/src/jobs/run-history.ts +242 -0
- package/src/jobs/scheduler.ts +73 -18
- package/src/jobs/tools.ts +54 -6
- package/src/localization/actions/get-localization-preference.ts +2 -2
- package/src/localization/actions/set-localization-preference.ts +49 -11
- package/src/localization/default-messages.ts +27 -0
- package/src/localization/shared.ts +39 -6
- package/src/localization/user-timezone.ts +36 -0
- package/src/org/app-roles-handlers.ts +1 -9
- package/src/org/index.ts +2 -0
- package/src/org/membership.ts +18 -0
- package/src/review/actions/create-review-comment.ts +4 -1
- package/src/review/actions/reply-review-comment.ts +4 -1
- package/src/review/index.ts +5 -0
- package/src/review/notifications.ts +163 -0
- package/src/review/types.ts +7 -0
- package/src/server/action-discovery.ts +4 -0
- package/src/server/activity-notifications.ts +174 -0
- package/src/server/app-name.ts +31 -0
- package/src/server/better-auth-instance.ts +4 -4
- package/src/server/email-templates.ts +70 -14
- package/src/server/email.ts +66 -3
- package/src/server/index.ts +10 -0
- package/src/sharing/access.ts +44 -2
- package/src/sharing/index.ts +5 -0
- package/src/sharing/recipients.ts +67 -0
- package/src/templates/chat/app/routes/settings.tsx +14 -22
- package/src/templates/chat/vitest.config.ts +9 -0
- package/src/triggers/actions/list-automations.ts +27 -8
- package/src/triggers/actions/manage-automation.ts +17 -6
- package/src/triggers/actions.ts +16 -4
- package/src/triggers/dispatcher.ts +44 -30
- package/src/triggers/routes.ts +19 -6
- package/src/vitest-config.ts +68 -0
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef } from "react";
|
|
2
2
|
|
|
3
3
|
import { cn } from "@/lib/utils";
|
|
4
|
+
import type { FilmstripFrame, FilmstripSprite } from "@/lib/video-filmstrip";
|
|
4
5
|
import type { WaveformPeaks } from "@/lib/waveform-peaks";
|
|
5
6
|
|
|
6
7
|
export interface WaveformProps {
|
|
7
8
|
/** Peaks computed via `computePeaks()`. */
|
|
8
9
|
peaks: WaveformPeaks | null;
|
|
10
|
+
/** Server-generated filmstrip sprite. Preferred over `frames` when present. */
|
|
11
|
+
sprite?: FilmstripSprite | null;
|
|
12
|
+
/** Browser-extracted frame thumbnails — the fallback when there's no sprite. */
|
|
13
|
+
frames?: FilmstripFrame[];
|
|
9
14
|
/** Width in px of the viewport (the scroll container). */
|
|
10
15
|
width: number;
|
|
11
16
|
/** Height in px. */
|
|
@@ -49,15 +54,19 @@ const getBrandColorAlpha = (alpha: number) => {
|
|
|
49
54
|
return v ? `hsl(${v} / ${alpha})` : `rgba(15, 23, 42, ${alpha})`;
|
|
50
55
|
};
|
|
51
56
|
|
|
52
|
-
const getWaveColor = () => getBrandColorAlpha(0.
|
|
53
|
-
const getWaveBg = () => getBrandColorAlpha(0.
|
|
54
|
-
const EXCLUDED_FILL = "rgba(15, 23, 42, 0.
|
|
55
|
-
const EXCLUDED_STROKE = "rgba(148, 163, 184, 0.
|
|
57
|
+
const getWaveColor = () => getBrandColorAlpha(0.85);
|
|
58
|
+
const getWaveBg = () => getBrandColorAlpha(0.08);
|
|
59
|
+
const EXCLUDED_FILL = "rgba(15, 23, 42, 0.65)";
|
|
60
|
+
const EXCLUDED_STROKE = "rgba(148, 163, 184, 0.4)";
|
|
61
|
+
const EMPTY_FRAMES: FilmstripFrame[] = [];
|
|
56
62
|
const VISUAL_TARGET_PEAK = 0.78;
|
|
57
63
|
const VISUAL_MAX_GAIN = 24;
|
|
58
64
|
const VISUAL_GAIN_PERCENTILE = 0.95;
|
|
59
65
|
const VISUAL_SILENCE_FLOOR = 0.001;
|
|
60
66
|
|
|
67
|
+
const MAX_BASE_TRACK_WIDTH = 8192;
|
|
68
|
+
const MAX_CANVAS_PIXELS_WIDTH = 4096;
|
|
69
|
+
|
|
61
70
|
function clampSample(value: number): number {
|
|
62
71
|
return Math.max(-1, Math.min(1, value));
|
|
63
72
|
}
|
|
@@ -80,9 +89,28 @@ function computeVisualGain(samples: number[]): number {
|
|
|
80
89
|
return Math.min(VISUAL_MAX_GAIN, VISUAL_TARGET_PEAK / reference);
|
|
81
90
|
}
|
|
82
91
|
|
|
92
|
+
function drawPillBar(
|
|
93
|
+
ctx: CanvasRenderingContext2D,
|
|
94
|
+
x: number,
|
|
95
|
+
y: number,
|
|
96
|
+
width: number,
|
|
97
|
+
height: number,
|
|
98
|
+
) {
|
|
99
|
+
const radius = Math.min(width / 2, height / 2);
|
|
100
|
+
ctx.beginPath();
|
|
101
|
+
if (typeof ctx.roundRect === "function") {
|
|
102
|
+
ctx.roundRect(x, y, width, height, radius);
|
|
103
|
+
} else {
|
|
104
|
+
ctx.rect(x, y, width, height);
|
|
105
|
+
}
|
|
106
|
+
ctx.fill();
|
|
107
|
+
}
|
|
108
|
+
|
|
83
109
|
/** Canvas-rendered waveform. Supports up to 50x zoom with horizontal scroll. */
|
|
84
110
|
export function Waveform({
|
|
85
111
|
peaks,
|
|
112
|
+
sprite,
|
|
113
|
+
frames = EMPTY_FRAMES,
|
|
86
114
|
width,
|
|
87
115
|
height = 120,
|
|
88
116
|
zoom = 1,
|
|
@@ -100,8 +128,51 @@ export function Waveform({
|
|
|
100
128
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
101
129
|
const scrollRef = useRef<HTMLDivElement | null>(null);
|
|
102
130
|
|
|
103
|
-
|
|
104
|
-
|
|
131
|
+
const baseTrackWidth = useMemo(() => {
|
|
132
|
+
if (durationMs <= 0 || width <= 0) return width;
|
|
133
|
+
const durationSec = durationMs / 1000;
|
|
134
|
+
// Scale timeline track width proportionally with duration so long videos don't squish (bounded to safe max allocation)
|
|
135
|
+
const durationScaledPx = Math.min(
|
|
136
|
+
MAX_BASE_TRACK_WIDTH,
|
|
137
|
+
Math.round(durationSec * 14),
|
|
138
|
+
);
|
|
139
|
+
return Math.max(width, durationScaledPx);
|
|
140
|
+
}, [durationMs, width]);
|
|
141
|
+
|
|
142
|
+
// The total drawable width (scrolls horizontally). Scales with duration and zoom.
|
|
143
|
+
const totalWidth = Math.max(
|
|
144
|
+
baseTrackWidth,
|
|
145
|
+
Math.floor(baseTrackWidth * Math.max(1, zoom)),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// A sprite has a fixed frame count, but the track needs however many cells
|
|
149
|
+
// fit at the video's aspect — otherwise cells go portrait and each thumbnail
|
|
150
|
+
// shows a narrow centre slice. Pick the cell count from the geometry, then
|
|
151
|
+
// map each cell to the sprite frame nearest its midpoint.
|
|
152
|
+
const spriteCells = useMemo(() => {
|
|
153
|
+
if (!sprite?.url || sprite.frameCount <= 0 || sprite.columns <= 0)
|
|
154
|
+
return [];
|
|
155
|
+
const aspect =
|
|
156
|
+
sprite.frameHeight > 0 ? sprite.frameWidth / sprite.frameHeight : 16 / 9;
|
|
157
|
+
const cellWidth = Math.max(24, height * aspect);
|
|
158
|
+
const count = Math.max(
|
|
159
|
+
1,
|
|
160
|
+
Math.min(sprite.frameCount, Math.round(totalWidth / cellWidth)),
|
|
161
|
+
);
|
|
162
|
+
return Array.from({ length: count }, (_, i) => {
|
|
163
|
+
const frame = Math.min(
|
|
164
|
+
sprite.frameCount - 1,
|
|
165
|
+
Math.floor(((i + 0.5) * sprite.frameCount) / count),
|
|
166
|
+
);
|
|
167
|
+
return {
|
|
168
|
+
key: `${i}-${frame}`,
|
|
169
|
+
column: frame % sprite.columns,
|
|
170
|
+
row: Math.floor(frame / sprite.columns),
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
}, [sprite, totalWidth, height]);
|
|
174
|
+
|
|
175
|
+
const hasImagery = spriteCells.length > 0 || frames.length > 0;
|
|
105
176
|
|
|
106
177
|
useEffect(() => {
|
|
107
178
|
const el = scrollRef.current;
|
|
@@ -112,12 +183,15 @@ export function Waveform({
|
|
|
112
183
|
}
|
|
113
184
|
}, [scrollLeft, totalWidth, width]);
|
|
114
185
|
|
|
115
|
-
// Re-draw whenever peaks, size, or excluded ranges change.
|
|
186
|
+
// Re-draw whenever peaks, imagery, size, or excluded ranges change.
|
|
116
187
|
useEffect(() => {
|
|
117
188
|
const canvas = canvasRef.current;
|
|
118
189
|
if (!canvas) return;
|
|
119
190
|
const dpr = window.devicePixelRatio || 1;
|
|
120
|
-
canvas.width = Math.
|
|
191
|
+
canvas.width = Math.min(
|
|
192
|
+
MAX_CANVAS_PIXELS_WIDTH,
|
|
193
|
+
Math.floor(totalWidth * dpr),
|
|
194
|
+
);
|
|
121
195
|
canvas.height = Math.floor(height * dpr);
|
|
122
196
|
canvas.style.width = `${totalWidth}px`;
|
|
123
197
|
canvas.style.height = `${height}px`;
|
|
@@ -126,74 +200,75 @@ export function Waveform({
|
|
|
126
200
|
if (!ctx) return;
|
|
127
201
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
128
202
|
|
|
129
|
-
// Background
|
|
130
|
-
ctx.fillStyle = getWaveBg();
|
|
131
|
-
ctx.fillRect(0, 0, totalWidth, height);
|
|
132
|
-
|
|
133
203
|
const hasPeaks = Boolean(
|
|
134
204
|
peaks?.bucketCount &&
|
|
135
205
|
peaks.peaks.some((value) => Math.abs(value) > 0.0001),
|
|
136
206
|
);
|
|
137
207
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
208
|
+
// Canvas background & audio visualization
|
|
209
|
+
ctx.clearRect(0, 0, totalWidth, height);
|
|
210
|
+
if (!hasImagery) {
|
|
211
|
+
ctx.fillStyle = getWaveBg();
|
|
212
|
+
ctx.fillRect(0, 0, totalWidth, height);
|
|
213
|
+
|
|
214
|
+
const barWidth = 3;
|
|
215
|
+
const barGap = 1.5;
|
|
216
|
+
const step = barWidth + barGap;
|
|
217
|
+
const barCount = Math.floor(totalWidth / step);
|
|
218
|
+
const maxWaveHeight = Math.min(height * 0.5, 52);
|
|
219
|
+
const minBarHeight = 3;
|
|
220
|
+
const midY = height / 2;
|
|
221
|
+
|
|
222
|
+
if (peaks && hasPeaks) {
|
|
223
|
+
const visualGain = computeVisualGain(peaks.peaks);
|
|
224
|
+
const bucketsPerBar = peaks.bucketCount / barCount;
|
|
225
|
+
|
|
226
|
+
for (let i = 0; i < barCount; i++) {
|
|
227
|
+
const x = i * step;
|
|
228
|
+
const startBucket = Math.floor(i * bucketsPerBar);
|
|
229
|
+
const endBucket = Math.max(
|
|
230
|
+
startBucket + 1,
|
|
231
|
+
Math.floor((i + 1) * bucketsPerBar),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
let maxAmp = 0;
|
|
235
|
+
for (
|
|
236
|
+
let b = startBucket;
|
|
237
|
+
b < endBucket && b < peaks.bucketCount;
|
|
238
|
+
b++
|
|
239
|
+
) {
|
|
240
|
+
const lo = Math.abs(peaks.peaks[b * 2] ?? 0);
|
|
241
|
+
const hi = Math.abs(peaks.peaks[b * 2 + 1] ?? 0);
|
|
242
|
+
if (lo > maxAmp) maxAmp = lo;
|
|
243
|
+
if (hi > maxAmp) maxAmp = hi;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const scaledAmp = clampSample(maxAmp * visualGain);
|
|
247
|
+
const barHeight = Math.max(minBarHeight, scaledAmp * maxWaveHeight);
|
|
248
|
+
const topY = midY - barHeight / 2;
|
|
249
|
+
|
|
250
|
+
ctx.fillStyle =
|
|
251
|
+
maxAmp > VISUAL_SILENCE_FLOOR
|
|
252
|
+
? getWaveColor()
|
|
253
|
+
: getBrandColorAlpha(0.2);
|
|
254
|
+
|
|
255
|
+
drawPillBar(ctx, x, topY, barWidth, barHeight);
|
|
170
256
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (activityRanges.length) {
|
|
187
|
-
const blockHeight = Math.max(18, Math.min(32, height * 0.32));
|
|
188
|
-
const top = mid - blockHeight / 2;
|
|
189
|
-
ctx.fillStyle = getBrandColorAlpha(0.24);
|
|
190
|
-
for (const range of activityRanges) {
|
|
191
|
-
const xStart =
|
|
192
|
-
(Math.max(0, range.startMs) / Math.max(durationMs, 1)) * totalWidth;
|
|
193
|
-
const xEnd =
|
|
194
|
-
(Math.min(durationMs, range.endMs) / Math.max(durationMs, 1)) *
|
|
195
|
-
totalWidth;
|
|
196
|
-
ctx.fillRect(xStart, top, Math.max(2, xEnd - xStart), blockHeight);
|
|
257
|
+
} else {
|
|
258
|
+
// Idle state without imagery
|
|
259
|
+
for (let i = 0; i < barCount; i++) {
|
|
260
|
+
const x = i * step;
|
|
261
|
+
const barMs = (i / Math.max(1, barCount)) * durationMs;
|
|
262
|
+
const inActivity = activityRanges.some(
|
|
263
|
+
(r) => barMs >= r.startMs && barMs <= r.endMs,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
const barHeight = inActivity ? 12 : minBarHeight;
|
|
267
|
+
const topY = midY - barHeight / 2;
|
|
268
|
+
|
|
269
|
+
ctx.fillStyle = inActivity ? getWaveColor() : getBrandColorAlpha(0.2);
|
|
270
|
+
|
|
271
|
+
drawPillBar(ctx, x, topY, barWidth, barHeight);
|
|
197
272
|
}
|
|
198
273
|
}
|
|
199
274
|
}
|
|
@@ -248,6 +323,7 @@ export function Waveform({
|
|
|
248
323
|
}
|
|
249
324
|
}, [
|
|
250
325
|
peaks,
|
|
326
|
+
hasImagery,
|
|
251
327
|
totalWidth,
|
|
252
328
|
height,
|
|
253
329
|
excludedRanges,
|
|
@@ -257,15 +333,56 @@ export function Waveform({
|
|
|
257
333
|
activityRanges,
|
|
258
334
|
]);
|
|
259
335
|
|
|
260
|
-
const
|
|
336
|
+
const scrubRef = useRef<{ pointerId: number; startX: number } | null>(null);
|
|
337
|
+
|
|
338
|
+
const seekToEvent = (
|
|
339
|
+
e: React.PointerEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>, // i18n-ignore
|
|
340
|
+
) => {
|
|
261
341
|
if (!onSeek) return;
|
|
262
|
-
const
|
|
263
|
-
|
|
342
|
+
const el = scrollRef.current;
|
|
343
|
+
if (!el) return;
|
|
344
|
+
const rect = el.getBoundingClientRect();
|
|
345
|
+
const scroll = el.scrollLeft;
|
|
264
346
|
const x = e.clientX - rect.left + scroll;
|
|
265
347
|
const ms = Math.max(0, Math.min(durationMs, (x / totalWidth) * durationMs));
|
|
266
348
|
onSeek(ms);
|
|
267
349
|
};
|
|
268
350
|
|
|
351
|
+
const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
|
352
|
+
if (!onSeek || e.button !== 0) return;
|
|
353
|
+
const el = scrollRef.current;
|
|
354
|
+
if (!el) return;
|
|
355
|
+
// A pointerdown on this element's own horizontal scrollbar still targets
|
|
356
|
+
// the element. Capturing there would turn the drag that pans a zoomed
|
|
357
|
+
// track into a scrub, leaving no way to reach the rest of the timeline.
|
|
358
|
+
if (e.clientY - el.getBoundingClientRect().top >= el.clientHeight) return;
|
|
359
|
+
scrubRef.current = { pointerId: e.pointerId, startX: e.clientX };
|
|
360
|
+
// Touch keeps the browser's native pan; a tap still seeks on pointerup.
|
|
361
|
+
if (e.pointerType === "touch") return;
|
|
362
|
+
el.setPointerCapture(e.pointerId);
|
|
363
|
+
seekToEvent(e);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
|
|
367
|
+
const scrub = scrubRef.current;
|
|
368
|
+
if (!scrub || scrub.pointerId !== e.pointerId) return;
|
|
369
|
+
if (e.pointerType === "touch") return;
|
|
370
|
+
seekToEvent(e);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const handlePointerUp = (e: React.PointerEvent<HTMLDivElement>) => {
|
|
374
|
+
const scrub = scrubRef.current;
|
|
375
|
+
if (!scrub || scrub.pointerId !== e.pointerId) return;
|
|
376
|
+
scrubRef.current = null;
|
|
377
|
+
const el = scrollRef.current;
|
|
378
|
+
if (el?.hasPointerCapture(e.pointerId)) {
|
|
379
|
+
el.releasePointerCapture(e.pointerId);
|
|
380
|
+
}
|
|
381
|
+
if (e.pointerType === "touch" && Math.abs(e.clientX - scrub.startX) < 8) {
|
|
382
|
+
seekToEvent(e);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
269
386
|
const handleScroll = () => {
|
|
270
387
|
const el = scrollRef.current;
|
|
271
388
|
if (!el) return;
|
|
@@ -287,10 +404,55 @@ export function Waveform({
|
|
|
287
404
|
)}
|
|
288
405
|
style={{ width, height }}
|
|
289
406
|
onScroll={handleScroll}
|
|
290
|
-
|
|
407
|
+
onPointerDown={handlePointerDown}
|
|
408
|
+
onPointerMove={handlePointerMove}
|
|
409
|
+
onPointerUp={handlePointerUp}
|
|
410
|
+
onPointerCancel={handlePointerUp}
|
|
291
411
|
>
|
|
292
412
|
<div className="relative" style={{ width: totalWidth, height }}>
|
|
293
|
-
|
|
413
|
+
{spriteCells.length > 0 && sprite ? (
|
|
414
|
+
<div className="absolute inset-0 flex overflow-hidden pointer-events-none">
|
|
415
|
+
{spriteCells.map((cell) => (
|
|
416
|
+
<div
|
|
417
|
+
key={cell.key}
|
|
418
|
+
className="h-full animate-in fade-in duration-500"
|
|
419
|
+
style={{
|
|
420
|
+
width: `${100 / spriteCells.length}%`,
|
|
421
|
+
backgroundImage: `url(${sprite.url})`,
|
|
422
|
+
// Percentage sizing maps one grid cell onto one element box,
|
|
423
|
+
// so percentage positioning addresses cells exactly.
|
|
424
|
+
backgroundSize: `${sprite.columns * 100}% ${sprite.rows * 100}%`,
|
|
425
|
+
backgroundPosition: `${
|
|
426
|
+
sprite.columns > 1
|
|
427
|
+
? (cell.column * 100) / (sprite.columns - 1)
|
|
428
|
+
: 0
|
|
429
|
+
}% ${
|
|
430
|
+
sprite.rows > 1 ? (cell.row * 100) / (sprite.rows - 1) : 0
|
|
431
|
+
}%`,
|
|
432
|
+
backgroundRepeat: "no-repeat",
|
|
433
|
+
}}
|
|
434
|
+
/>
|
|
435
|
+
))}
|
|
436
|
+
</div>
|
|
437
|
+
) : (
|
|
438
|
+
frames.length > 0 && (
|
|
439
|
+
<div className="absolute inset-0 flex overflow-hidden pointer-events-none">
|
|
440
|
+
{frames.map((frame) => (
|
|
441
|
+
<img
|
|
442
|
+
key={frame.timeMs}
|
|
443
|
+
src={frame.dataUrl}
|
|
444
|
+
alt=""
|
|
445
|
+
className="h-full object-cover animate-in fade-in duration-500"
|
|
446
|
+
style={{ width: `${100 / frames.length}%` }}
|
|
447
|
+
/>
|
|
448
|
+
))}
|
|
449
|
+
</div>
|
|
450
|
+
)
|
|
451
|
+
)}
|
|
452
|
+
<canvas
|
|
453
|
+
ref={canvasRef}
|
|
454
|
+
className="absolute inset-0 pointer-events-none"
|
|
455
|
+
/>
|
|
294
456
|
<div
|
|
295
457
|
className="absolute top-0 h-full w-[2px] pointer-events-none"
|
|
296
458
|
style={{
|
|
@@ -540,6 +540,7 @@ export class RecorderEngine {
|
|
|
540
540
|
*/
|
|
541
541
|
private uploadAbort: AbortController | null = null;
|
|
542
542
|
private uploadMode: UploadMode = "buffered";
|
|
543
|
+
private uploadGenerationId: string | null = null;
|
|
543
544
|
/**
|
|
544
545
|
* Streaming-path buffer. MediaRecorder blobs accumulate here until at least
|
|
545
546
|
* STREAM_CHUNK_BYTES is available, then a 256 KiB-aligned slice is PUT to API
|
|
@@ -1102,6 +1103,7 @@ export class RecorderEngine {
|
|
|
1102
1103
|
this.opts.uploadUrl = target.uploadUrl;
|
|
1103
1104
|
this.opts.abortUrl = target.abortUrl;
|
|
1104
1105
|
this.opts.uploadMode = target.uploadMode ?? "buffered";
|
|
1106
|
+
this.uploadGenerationId = null;
|
|
1105
1107
|
}
|
|
1106
1108
|
|
|
1107
1109
|
// -------------------------------------------------------------------------
|
|
@@ -1178,6 +1180,7 @@ export class RecorderEngine {
|
|
|
1178
1180
|
this.lastFinalizeMeta = null;
|
|
1179
1181
|
this.uploadAbort = new AbortController();
|
|
1180
1182
|
this.uploadMode = this.opts.uploadMode ?? "buffered";
|
|
1183
|
+
this.uploadGenerationId = null;
|
|
1181
1184
|
this.pendingStreamBlobs = [];
|
|
1182
1185
|
this.pendingStreamBytes = 0;
|
|
1183
1186
|
this.cameraDisconnectNotified = false;
|
|
@@ -1540,6 +1543,10 @@ export class RecorderEngine {
|
|
|
1540
1543
|
compression,
|
|
1541
1544
|
requestStreaming: this.uploadMode === "streaming",
|
|
1542
1545
|
mimeType: uploadMimeType,
|
|
1546
|
+
useGenerationFence: true,
|
|
1547
|
+
...(this.uploadGenerationId
|
|
1548
|
+
? { uploadGenerationId: this.uploadGenerationId }
|
|
1549
|
+
: {}),
|
|
1543
1550
|
}),
|
|
1544
1551
|
signal,
|
|
1545
1552
|
});
|
|
@@ -1566,6 +1573,7 @@ export class RecorderEngine {
|
|
|
1566
1573
|
}
|
|
1567
1574
|
const reset = (await resetRes.json().catch(() => null)) as {
|
|
1568
1575
|
uploadMode?: unknown;
|
|
1576
|
+
uploadGenerationId?: unknown;
|
|
1569
1577
|
} | null;
|
|
1570
1578
|
if (reset?.uploadMode !== "streaming" && reset?.uploadMode !== "buffered") {
|
|
1571
1579
|
throw new Error(
|
|
@@ -1574,6 +1582,11 @@ export class RecorderEngine {
|
|
|
1574
1582
|
}
|
|
1575
1583
|
const uploadMode = reset.uploadMode;
|
|
1576
1584
|
this.uploadMode = uploadMode;
|
|
1585
|
+
this.uploadGenerationId =
|
|
1586
|
+
typeof reset.uploadGenerationId === "string" &&
|
|
1587
|
+
reset.uploadGenerationId.length > 0
|
|
1588
|
+
? reset.uploadGenerationId
|
|
1589
|
+
: null;
|
|
1577
1590
|
return uploadMode;
|
|
1578
1591
|
}
|
|
1579
1592
|
|
|
@@ -1752,8 +1765,10 @@ export class RecorderEngine {
|
|
|
1752
1765
|
this.uploadAbort = null;
|
|
1753
1766
|
}
|
|
1754
1767
|
this.cleanupTracks();
|
|
1768
|
+
const uploadGenerationId = this.uploadGenerationId;
|
|
1755
1769
|
this.chunkIndex = 0;
|
|
1756
1770
|
this.uploadFailure = null;
|
|
1771
|
+
this.uploadGenerationId = null;
|
|
1757
1772
|
this.startedAtMs = null;
|
|
1758
1773
|
this.pausedAccumMs = 0;
|
|
1759
1774
|
this.pausedStartedMs = null;
|
|
@@ -1764,7 +1779,13 @@ export class RecorderEngine {
|
|
|
1764
1779
|
|
|
1765
1780
|
if (this.opts.abortUrl) {
|
|
1766
1781
|
try {
|
|
1767
|
-
await fetch(this.opts.abortUrl, {
|
|
1782
|
+
await fetch(this.opts.abortUrl, {
|
|
1783
|
+
method: "POST",
|
|
1784
|
+
headers: { "Content-Type": "application/json" },
|
|
1785
|
+
body: JSON.stringify({
|
|
1786
|
+
...(uploadGenerationId ? { uploadGenerationId } : {}),
|
|
1787
|
+
}),
|
|
1788
|
+
});
|
|
1768
1789
|
} catch {
|
|
1769
1790
|
// ignore — best effort
|
|
1770
1791
|
}
|
|
@@ -2136,6 +2157,7 @@ export class RecorderEngine {
|
|
|
2136
2157
|
height: extra.height,
|
|
2137
2158
|
hasAudio: extra.hasAudio,
|
|
2138
2159
|
hasCamera: extra.hasCamera,
|
|
2160
|
+
uploadGenerationId: this.uploadGenerationId ?? undefined,
|
|
2139
2161
|
});
|
|
2140
2162
|
|
|
2141
2163
|
const body = await blob.arrayBuffer();
|