@agent-native/core 0.90.2 → 0.90.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 +2 -2
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +87 -80
- package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +89 -80
- package/corpus/core/docs/content/toolkit-agent-ux.mdx +43 -0
- package/corpus/core/docs/content/toolkit-app-adapters.mdx +9 -0
- package/corpus/core/docs/content/toolkit-collaboration-ui.mdx +12 -0
- package/corpus/core/docs/content/toolkit-collaboration.mdx +74 -0
- package/corpus/core/docs/content/toolkit-command-navigation.mdx +106 -0
- package/corpus/core/docs/content/toolkit-comments-review.mdx +98 -0
- package/corpus/core/docs/content/toolkit-history.mdx +95 -0
- package/corpus/core/docs/content/toolkit-observability.mdx +41 -0
- package/corpus/core/docs/content/toolkit-org-team.mdx +80 -0
- package/corpus/core/docs/content/toolkit-resources.mdx +57 -0
- package/corpus/core/docs/content/toolkit-settings.mdx +65 -0
- package/corpus/core/docs/content/toolkit-setup-connections.mdx +109 -0
- package/corpus/core/docs/content/toolkit-sharing-ui.mdx +18 -6
- package/corpus/core/docs/content/toolkit-sharing.mdx +81 -0
- package/corpus/core/docs/content/toolkit-shell-hooks.mdx +9 -0
- package/corpus/core/docs/content/toolkit-ui.mdx +9 -0
- package/corpus/core/package.json +35 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +19 -1
- package/corpus/core/src/agent/engine/anthropic-engine.ts +25 -1
- package/corpus/core/src/agent/engine/registry.ts +23 -16
- package/corpus/core/src/agent/production-agent.ts +37 -6
- package/corpus/core/src/client/command-navigation/index.ts +15 -0
- package/corpus/core/src/client/comments-review/index.ts +1 -0
- package/corpus/core/src/client/history/VersionHistoryPanel.tsx +145 -0
- package/corpus/core/src/client/history/index.ts +18 -0
- package/corpus/core/src/client/history/use-history.ts +86 -0
- package/corpus/core/src/client/index.ts +76 -9
- package/corpus/core/src/client/org/OrgSwitcher.tsx +6 -6
- package/corpus/core/src/client/org/RequireActiveOrg.tsx +2 -2
- package/corpus/core/src/client/org/TeamPage.tsx +3 -3
- package/corpus/core/src/client/org/hooks.ts +32 -0
- package/corpus/core/src/client/org/index.ts +30 -1
- package/corpus/core/src/client/org-team/index.ts +1 -0
- package/corpus/core/src/client/review/ReviewStatusBadge.tsx +39 -0
- package/corpus/core/src/client/review/ReviewThreadPanel.tsx +278 -0
- package/corpus/core/src/client/review/index.ts +25 -0
- package/corpus/core/src/client/review/use-review.ts +114 -0
- package/corpus/core/src/client/settings/index.ts +11 -1
- package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +94 -0
- package/corpus/core/src/client/setup-connections/ProviderReadinessBadge.tsx +73 -0
- package/corpus/core/src/client/setup-connections/SetupConnectionsPage.tsx +94 -0
- package/corpus/core/src/client/setup-connections/index.ts +46 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +20 -14
- package/corpus/core/src/client/sharing/ShareDialog.tsx +3 -3
- package/corpus/core/src/command-navigation/actions.ts +100 -0
- package/corpus/core/src/command-navigation/index.ts +12 -0
- package/corpus/core/src/comments-review/index.ts +1 -0
- package/corpus/core/src/file-upload/builder.ts +24 -3
- package/corpus/core/src/file-upload/types.ts +3 -1
- package/corpus/core/src/history/actions/create-resource-version.ts +89 -0
- package/corpus/core/src/history/actions/get-resource-version.ts +60 -0
- package/corpus/core/src/history/actions/list-resource-history.ts +53 -0
- package/corpus/core/src/history/actions/list-resource-versions.ts +43 -0
- package/corpus/core/src/history/actions/restore-resource-version.ts +96 -0
- package/corpus/core/src/history/index.ts +27 -0
- package/corpus/core/src/history/registry.ts +99 -0
- package/corpus/core/src/history/store.ts +348 -0
- package/corpus/core/src/history/types.ts +69 -0
- package/corpus/core/src/index.ts +10 -0
- package/corpus/core/src/navigation/index.ts +170 -0
- package/corpus/core/src/org/index.ts +8 -0
- package/corpus/core/src/org/permissions.ts +30 -0
- package/corpus/core/src/org-team/index.ts +1 -0
- package/corpus/core/src/private-blob/registry.ts +1 -0
- package/corpus/core/src/review/actions/consume-review-feedback.ts +38 -0
- package/corpus/core/src/review/actions/create-review-comment.ts +107 -0
- package/corpus/core/src/review/actions/delete-review-comment.ts +55 -0
- package/corpus/core/src/review/actions/get-review-feedback.ts +54 -0
- package/corpus/core/src/review/actions/list-review-comments.ts +53 -0
- package/corpus/core/src/review/actions/reply-review-comment.ts +107 -0
- package/corpus/core/src/review/actions/resolve-review-thread.ts +60 -0
- package/corpus/core/src/review/actions/set-review-status.ts +58 -0
- package/corpus/core/src/review/index.ts +36 -0
- package/corpus/core/src/review/mentions.ts +44 -0
- package/corpus/core/src/review/registry.ts +99 -0
- package/corpus/core/src/review/store.ts +687 -0
- package/corpus/core/src/review/types.ts +97 -0
- package/corpus/core/src/server/action-discovery.ts +54 -0
- package/corpus/core/src/server/agent-engine-api-key-route.ts +5 -0
- package/corpus/core/src/server/credential-provider.ts +108 -0
- package/corpus/core/src/setup-connections/index.ts +89 -0
- package/corpus/core/src/setup-connections/onboarding.ts +47 -0
- package/corpus/core/src/transcription/builder-transcription.ts +9 -2
- package/corpus/core/src/vite/action-types-plugin.ts +52 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/analytics/app/global.css +22 -10
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +6 -1
- package/corpus/templates/analytics/app/i18n-data.ts +81 -13
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +528 -108
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +466 -183
- package/corpus/templates/analytics/changelog/2026-07-07-session-replays-load-sooner-show-clearer-activity-timelines-.md +6 -0
- package/corpus/templates/brain/app/routes/settings.tsx +2 -2
- package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +32 -6
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +120 -40
- package/corpus/templates/calendar/app/hooks/use-people.ts +55 -0
- package/corpus/templates/calendar/changelog/2026-07-07-guest-suggestions-now-find-coworkers-from-your-google-worksp.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-07-zoom-stays-selected-after-connecting-it-from-a-draft-event.md +6 -0
- package/corpus/templates/clips/actions/create-recording.ts +5 -1
- package/corpus/templates/clips/actions/finalize-recording.ts +140 -1
- package/corpus/templates/clips/actions/import-loom-recording.ts +1 -0
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +1 -0
- package/corpus/templates/clips/actions/request-transcript.ts +31 -0
- package/corpus/templates/clips/actions/set-thumbnail.ts +2 -0
- package/corpus/templates/clips/app/components/library/organization-switcher.tsx +2 -2
- package/corpus/templates/clips/app/components/player/scrubber-position.ts +9 -0
- package/corpus/templates/clips/app/components/player/scrubber.tsx +5 -2
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +69 -41
- package/corpus/templates/clips/app/components/player/video-player.tsx +38 -1
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +3 -3
- package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +9 -19
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -3
- package/corpus/templates/clips/app/routes/record.tsx +21 -12
- package/corpus/templates/clips/changelog/2026-07-02-recordings-now-wait-for-storage-media-to-serve-before-finishing.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-a-pending-app-update-no-longer-blocks-screen-recording-updat.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-chrome-extension-recordings-keep-audio-after-refreshing-tab.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-clip-playback-progress-now-starts-and-finishes-at-the-right-times.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-sharing-public-clips-now-stays-focused-on-the-normal-link-wi.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +18 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +23 -8
- package/corpus/templates/clips/desktop/src/app.tsx +40 -3
- package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +19 -7
- package/corpus/templates/clips/desktop/src/lib/permissions.ts +2 -0
- package/corpus/templates/clips/desktop/src/lib/updater.ts +25 -2
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +7 -0
- package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +1 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +22 -5
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +8 -0
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +13 -6
- package/corpus/templates/clips/shared/finalize-recovery.ts +12 -0
- package/corpus/templates/content/actions/delete-document.ts +337 -52
- package/corpus/templates/content/changelog/2026-07-07-permanently-deleting-trashed-databases-now-finishes-reliably.md +6 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +2 -2
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +15 -1
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +20 -1
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +22 -14
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +27 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/command-navigation/index.d.ts +4 -0
- package/dist/client/command-navigation/index.d.ts.map +1 -0
- package/dist/client/command-navigation/index.js +4 -0
- package/dist/client/command-navigation/index.js.map +1 -0
- package/dist/client/comments-review/index.d.ts +2 -0
- package/dist/client/comments-review/index.d.ts.map +1 -0
- package/dist/client/comments-review/index.js +2 -0
- package/dist/client/comments-review/index.js.map +1 -0
- package/dist/client/history/VersionHistoryPanel.d.ts +18 -0
- package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -0
- package/dist/client/history/VersionHistoryPanel.js +24 -0
- package/dist/client/history/VersionHistoryPanel.js.map +1 -0
- package/dist/client/history/index.d.ts +3 -0
- package/dist/client/history/index.d.ts.map +1 -0
- package/dist/client/history/index.js +3 -0
- package/dist/client/history/index.js.map +1 -0
- package/dist/client/history/use-history.d.ts +46 -0
- package/dist/client/history/use-history.d.ts.map +1 -0
- package/dist/client/history/use-history.js +18 -0
- package/dist/client/history/use-history.js.map +1 -0
- package/dist/client/index.d.ts +6 -4
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -4
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/OrgSwitcher.js +5 -5
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/RequireActiveOrg.js +2 -2
- package/dist/client/org/RequireActiveOrg.js.map +1 -1
- package/dist/client/org/TeamPage.js +3 -3
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/org/hooks.d.ts +12 -1
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +15 -0
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/index.d.ts +4 -2
- package/dist/client/org/index.d.ts.map +1 -1
- package/dist/client/org/index.js +3 -1
- package/dist/client/org/index.js.map +1 -1
- package/dist/client/org-team/index.d.ts +2 -0
- package/dist/client/org-team/index.d.ts.map +1 -0
- package/dist/client/org-team/index.js +2 -0
- package/dist/client/org-team/index.js.map +1 -0
- package/dist/client/review/ReviewStatusBadge.d.ts +7 -0
- package/dist/client/review/ReviewStatusBadge.d.ts.map +1 -0
- package/dist/client/review/ReviewStatusBadge.js +19 -0
- package/dist/client/review/ReviewStatusBadge.js.map +1 -0
- package/dist/client/review/ReviewThreadPanel.d.ts +20 -0
- package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -0
- package/dist/client/review/ReviewThreadPanel.js +84 -0
- package/dist/client/review/ReviewThreadPanel.js.map +1 -0
- package/dist/client/review/index.d.ts +4 -0
- package/dist/client/review/index.d.ts.map +1 -0
- package/dist/client/review/index.js +4 -0
- package/dist/client/review/index.js.map +1 -0
- package/dist/client/review/use-review.d.ts +68 -0
- package/dist/client/review/use-review.d.ts.map +1 -0
- package/dist/client/review/use-review.js +22 -0
- package/dist/client/review/use-review.js.map +1 -0
- package/dist/client/settings/index.d.ts +1 -1
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js +1 -1
- package/dist/client/settings/index.js.map +1 -1
- package/dist/client/setup-connections/BuilderConnectCard.d.ts +9 -0
- package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -0
- package/dist/client/setup-connections/BuilderConnectCard.js +25 -0
- package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -0
- package/dist/client/setup-connections/ProviderReadinessBadge.d.ts +8 -0
- package/dist/client/setup-connections/ProviderReadinessBadge.d.ts.map +1 -0
- package/dist/client/setup-connections/ProviderReadinessBadge.js +32 -0
- package/dist/client/setup-connections/ProviderReadinessBadge.js.map +1 -0
- package/dist/client/setup-connections/SetupConnectionsPage.d.ts +17 -0
- package/dist/client/setup-connections/SetupConnectionsPage.d.ts.map +1 -0
- package/dist/client/setup-connections/SetupConnectionsPage.js +10 -0
- package/dist/client/setup-connections/SetupConnectionsPage.js.map +1 -0
- package/dist/client/setup-connections/index.d.ts +7 -0
- package/dist/client/setup-connections/index.d.ts.map +1 -0
- package/dist/client/setup-connections/index.js +7 -0
- package/dist/client/setup-connections/index.js.map +1 -0
- package/dist/client/sharing/ShareButton.d.ts +4 -4
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +13 -7
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sharing/ShareDialog.js +3 -3
- package/dist/client/sharing/ShareDialog.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/command-navigation/actions.d.ts +25 -0
- package/dist/command-navigation/actions.d.ts.map +1 -0
- package/dist/command-navigation/actions.js +64 -0
- package/dist/command-navigation/actions.js.map +1 -0
- package/dist/command-navigation/index.d.ts +4 -0
- package/dist/command-navigation/index.d.ts.map +1 -0
- package/dist/command-navigation/index.js +4 -0
- package/dist/command-navigation/index.js.map +1 -0
- package/dist/comments-review/index.d.ts +2 -0
- package/dist/comments-review/index.d.ts.map +1 -0
- package/dist/comments-review/index.js +2 -0
- package/dist/comments-review/index.js.map +1 -0
- package/dist/file-upload/builder.d.ts.map +1 -1
- package/dist/file-upload/builder.js +18 -2
- package/dist/file-upload/builder.js.map +1 -1
- package/dist/file-upload/types.d.ts +3 -0
- package/dist/file-upload/types.d.ts.map +1 -1
- package/dist/file-upload/types.js.map +1 -1
- package/dist/history/actions/create-resource-version.d.ts +10 -0
- package/dist/history/actions/create-resource-version.d.ts.map +1 -0
- package/dist/history/actions/create-resource-version.js +65 -0
- package/dist/history/actions/create-resource-version.js.map +1 -0
- package/dist/history/actions/get-resource-version.d.ts +10 -0
- package/dist/history/actions/get-resource-version.d.ts.map +1 -0
- package/dist/history/actions/get-resource-version.js +38 -0
- package/dist/history/actions/get-resource-version.js.map +1 -0
- package/dist/history/actions/list-resource-history.d.ts +10 -0
- package/dist/history/actions/list-resource-history.d.ts.map +1 -0
- package/dist/history/actions/list-resource-history.js +45 -0
- package/dist/history/actions/list-resource-history.js.map +1 -0
- package/dist/history/actions/list-resource-versions.d.ts +10 -0
- package/dist/history/actions/list-resource-versions.d.ts.map +1 -0
- package/dist/history/actions/list-resource-versions.js +35 -0
- package/dist/history/actions/list-resource-versions.js.map +1 -0
- package/dist/history/actions/restore-resource-version.d.ts +11 -0
- package/dist/history/actions/restore-resource-version.d.ts.map +1 -0
- package/dist/history/actions/restore-resource-version.js +59 -0
- package/dist/history/actions/restore-resource-version.js.map +1 -0
- package/dist/history/index.d.ts +4 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +3 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/registry.d.ts +10 -0
- package/dist/history/registry.d.ts.map +1 -0
- package/dist/history/registry.js +61 -0
- package/dist/history/registry.js.map +1 -0
- package/dist/history/store.d.ts +34 -0
- package/dist/history/store.d.ts.map +1 -0
- package/dist/history/store.js +265 -0
- package/dist/history/store.js.map +1 -0
- package/dist/history/types.d.ts +55 -0
- package/dist/history/types.d.ts.map +1 -0
- package/dist/history/types.js +2 -0
- package/dist/history/types.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/navigation/index.d.ts +53 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +105 -0
- package/dist/navigation/index.js.map +1 -0
- 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/permissions.d.ts +7 -0
- package/dist/org/permissions.d.ts.map +1 -0
- package/dist/org/permissions.js +21 -0
- package/dist/org/permissions.js.map +1 -0
- package/dist/org-team/index.d.ts +2 -0
- package/dist/org-team/index.d.ts.map +1 -0
- package/dist/org-team/index.js +2 -0
- package/dist/org-team/index.js.map +1 -0
- package/dist/private-blob/registry.d.ts.map +1 -1
- package/dist/private-blob/registry.js +1 -0
- package/dist/private-blob/registry.js.map +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/review/actions/consume-review-feedback.d.ts +9 -0
- package/dist/review/actions/consume-review-feedback.d.ts.map +1 -0
- package/dist/review/actions/consume-review-feedback.js +29 -0
- package/dist/review/actions/consume-review-feedback.js.map +1 -0
- package/dist/review/actions/create-review-comment.d.ts +18 -0
- package/dist/review/actions/create-review-comment.d.ts.map +1 -0
- package/dist/review/actions/create-review-comment.js +81 -0
- package/dist/review/actions/create-review-comment.js.map +1 -0
- package/dist/review/actions/delete-review-comment.d.ts +10 -0
- package/dist/review/actions/delete-review-comment.d.ts.map +1 -0
- package/dist/review/actions/delete-review-comment.js +45 -0
- package/dist/review/actions/delete-review-comment.js.map +1 -0
- package/dist/review/actions/get-review-feedback.d.ts +10 -0
- package/dist/review/actions/get-review-feedback.d.ts.map +1 -0
- package/dist/review/actions/get-review-feedback.js +45 -0
- package/dist/review/actions/get-review-feedback.js.map +1 -0
- package/dist/review/actions/list-review-comments.d.ts +13 -0
- package/dist/review/actions/list-review-comments.d.ts.map +1 -0
- package/dist/review/actions/list-review-comments.js +44 -0
- package/dist/review/actions/list-review-comments.js.map +1 -0
- package/dist/review/actions/reply-review-comment.d.ts +16 -0
- package/dist/review/actions/reply-review-comment.d.ts.map +1 -0
- package/dist/review/actions/reply-review-comment.js +84 -0
- package/dist/review/actions/reply-review-comment.js.map +1 -0
- package/dist/review/actions/resolve-review-thread.d.ts +11 -0
- package/dist/review/actions/resolve-review-thread.d.ts.map +1 -0
- package/dist/review/actions/resolve-review-thread.js +46 -0
- package/dist/review/actions/resolve-review-thread.js.map +1 -0
- package/dist/review/actions/set-review-status.d.ts +9 -0
- package/dist/review/actions/set-review-status.d.ts.map +1 -0
- package/dist/review/actions/set-review-status.js +43 -0
- package/dist/review/actions/set-review-status.js.map +1 -0
- package/dist/review/index.d.ts +5 -0
- package/dist/review/index.d.ts.map +1 -0
- package/dist/review/index.js +4 -0
- package/dist/review/index.js.map +1 -0
- package/dist/review/mentions.d.ts +4 -0
- package/dist/review/mentions.d.ts.map +1 -0
- package/dist/review/mentions.js +39 -0
- package/dist/review/mentions.js.map +1 -0
- package/dist/review/registry.d.ts +10 -0
- package/dist/review/registry.d.ts.map +1 -0
- package/dist/review/registry.js +61 -0
- package/dist/review/registry.js.map +1 -0
- package/dist/review/store.d.ts +63 -0
- package/dist/review/store.d.ts.map +1 -0
- package/dist/review/store.js +558 -0
- package/dist/review/store.js.map +1 -0
- package/dist/review/types.d.ts +77 -0
- package/dist/review/types.d.ts.map +1 -0
- package/dist/review/types.js +2 -0
- package/dist/review/types.js.map +1 -0
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +54 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts.map +1 -1
- package/dist/server/agent-engine-api-key-route.js +5 -0
- package/dist/server/agent-engine-api-key-route.js.map +1 -1
- package/dist/server/credential-provider.d.ts +26 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +82 -0
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/setup-connections/index.d.ts +6 -0
- package/dist/setup-connections/index.d.ts.map +1 -0
- package/dist/setup-connections/index.js +6 -0
- package/dist/setup-connections/index.js.map +1 -0
- package/dist/setup-connections/onboarding.d.ts +14 -0
- package/dist/setup-connections/onboarding.d.ts.map +1 -0
- package/dist/setup-connections/onboarding.js +23 -0
- package/dist/setup-connections/onboarding.js.map +1 -0
- package/dist/transcription/builder-transcription.d.ts +1 -0
- package/dist/transcription/builder-transcription.d.ts.map +1 -1
- package/dist/transcription/builder-transcription.js +5 -2
- package/dist/transcription/builder-transcription.js.map +1 -1
- package/dist/vite/action-types-plugin.d.ts.map +1 -1
- package/dist/vite/action-types-plugin.js +52 -0
- package/dist/vite/action-types-plugin.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +87 -80
- package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/de-DE/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/es-ES/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +89 -80
- package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +89 -80
- package/docs/content/toolkit-agent-ux.mdx +43 -0
- package/docs/content/toolkit-app-adapters.mdx +9 -0
- package/docs/content/toolkit-collaboration-ui.mdx +12 -0
- package/docs/content/toolkit-collaboration.mdx +74 -0
- package/docs/content/toolkit-command-navigation.mdx +106 -0
- package/docs/content/toolkit-comments-review.mdx +98 -0
- package/docs/content/toolkit-history.mdx +95 -0
- package/docs/content/toolkit-observability.mdx +41 -0
- package/docs/content/toolkit-org-team.mdx +80 -0
- package/docs/content/toolkit-resources.mdx +57 -0
- package/docs/content/toolkit-settings.mdx +65 -0
- package/docs/content/toolkit-setup-connections.mdx +109 -0
- package/docs/content/toolkit-sharing-ui.mdx +18 -6
- package/docs/content/toolkit-sharing.mdx +81 -0
- package/docs/content/toolkit-shell-hooks.mdx +9 -0
- package/docs/content/toolkit-ui.mdx +9 -0
- package/package.json +36 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AgentToggleButton,
|
|
2
3
|
appApiPath,
|
|
3
4
|
PromptComposer,
|
|
4
5
|
useActionMutation,
|
|
5
|
-
useActionQuery,
|
|
6
6
|
useSendToAgentChat,
|
|
7
7
|
useT,
|
|
8
8
|
} from "@agent-native/core/client";
|
|
@@ -10,6 +10,7 @@ import { SESSION_REPLAY_AGENT_ACCESS_PARAM } from "@shared/session-replay-agent-
|
|
|
10
10
|
import {
|
|
11
11
|
IconArrowLeft,
|
|
12
12
|
IconCheck,
|
|
13
|
+
IconChevronRight,
|
|
13
14
|
IconCopy,
|
|
14
15
|
IconExclamationCircle,
|
|
15
16
|
IconKeyboard,
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
IconTerminal2,
|
|
25
26
|
IconTimelineEvent,
|
|
26
27
|
} from "@tabler/icons-react";
|
|
27
|
-
import { useQuery } from "@tanstack/react-query";
|
|
28
28
|
import {
|
|
29
29
|
type ReactNode,
|
|
30
30
|
useCallback,
|
|
@@ -113,6 +113,11 @@ type SessionReplayPlaybackResponse = {
|
|
|
113
113
|
eventCount: number;
|
|
114
114
|
truncated: boolean;
|
|
115
115
|
unavailableChunks: number;
|
|
116
|
+
loadedChunks: number;
|
|
117
|
+
totalChunks: number;
|
|
118
|
+
loadedBytes: number;
|
|
119
|
+
totalBytes: number;
|
|
120
|
+
isComplete: boolean;
|
|
116
121
|
};
|
|
117
122
|
|
|
118
123
|
type AnyReplayEvent = Record<string, any>;
|
|
@@ -124,9 +129,11 @@ type ReplayMarker = {
|
|
|
124
129
|
id: string;
|
|
125
130
|
offsetMs: number;
|
|
126
131
|
timestamp: number;
|
|
127
|
-
kind: "navigation" | "input" | "click" | "custom";
|
|
132
|
+
kind: "navigation" | "input" | "click" | "console" | "custom";
|
|
128
133
|
label: string;
|
|
129
134
|
detail?: string;
|
|
135
|
+
severity?: "info" | "warn" | "error";
|
|
136
|
+
fields?: Array<{ label: string; value: string }>;
|
|
130
137
|
};
|
|
131
138
|
|
|
132
139
|
type SkipRange = {
|
|
@@ -148,6 +155,17 @@ const MIN_IDLE_SKIP_MS = 8000;
|
|
|
148
155
|
const IDLE_EDGE_PAD_MS = 1200;
|
|
149
156
|
const REPLAY_CHUNK_FETCH_CONCURRENCY = 6;
|
|
150
157
|
const REPLAY_CHUNK_UNAVAILABLE_MESSAGE = "Session replay chunk is unavailable";
|
|
158
|
+
const DEFAULT_DEVTOOLS_HEIGHT = 280;
|
|
159
|
+
const SCRUBBER_MARKER_LIMIT = 500;
|
|
160
|
+
const SESSION_REPLAY_CONSOLE_EVENT_TAG = "agent-native.console";
|
|
161
|
+
const SESSION_REPLAY_NETWORK_EVENT_TAG = "agent-native.network";
|
|
162
|
+
|
|
163
|
+
const EMPTY_DIAGNOSTICS: ReturnType<typeof extractReplayDiagnostics> = {
|
|
164
|
+
console: [],
|
|
165
|
+
network: [],
|
|
166
|
+
consoleErrorCount: 0,
|
|
167
|
+
networkFailedCount: 0,
|
|
168
|
+
};
|
|
151
169
|
|
|
152
170
|
const RRWEB_EVENT_TYPE = {
|
|
153
171
|
FullSnapshot: 2,
|
|
@@ -211,10 +229,13 @@ export default function SessionDetailPage() {
|
|
|
211
229
|
) : null}
|
|
212
230
|
</div>
|
|
213
231
|
{recording ? (
|
|
214
|
-
<
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
|
|
232
|
+
<TooltipProvider>
|
|
233
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
234
|
+
<CopySessionForAgentButton recordingId={recording.id} />
|
|
235
|
+
<AskSessionPopover recording={recording} />
|
|
236
|
+
<AgentToggleButton />
|
|
237
|
+
</div>
|
|
238
|
+
</TooltipProvider>
|
|
218
239
|
) : null}
|
|
219
240
|
</div>
|
|
220
241
|
|
|
@@ -257,19 +278,24 @@ function CopySessionForAgentButton({ recordingId }: { recordingId: string }) {
|
|
|
257
278
|
}
|
|
258
279
|
|
|
259
280
|
return (
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
281
|
+
<Tooltip>
|
|
282
|
+
<TooltipTrigger asChild>
|
|
283
|
+
<Button
|
|
284
|
+
type="button"
|
|
285
|
+
variant="outline"
|
|
286
|
+
onClick={() => void handleCopy()}
|
|
287
|
+
disabled={createLink.isPending}
|
|
288
|
+
>
|
|
289
|
+
{copied ? (
|
|
290
|
+
<IconCheck className="h-4 w-4" />
|
|
291
|
+
) : (
|
|
292
|
+
<IconCopy className="h-4 w-4" />
|
|
293
|
+
)}
|
|
294
|
+
{copied ? t("sessions.copiedForAgent") : t("sessions.copyForAgent")}
|
|
295
|
+
</Button>
|
|
296
|
+
</TooltipTrigger>
|
|
297
|
+
<TooltipContent>{t("sessions.copyForAgentTooltip")}</TooltipContent>
|
|
298
|
+
</Tooltip>
|
|
273
299
|
);
|
|
274
300
|
}
|
|
275
301
|
|
|
@@ -298,12 +324,17 @@ function AskSessionPopover({
|
|
|
298
324
|
|
|
299
325
|
return (
|
|
300
326
|
<Popover open={open} onOpenChange={setOpen}>
|
|
301
|
-
<
|
|
302
|
-
<
|
|
303
|
-
<
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
327
|
+
<Tooltip>
|
|
328
|
+
<TooltipTrigger asChild>
|
|
329
|
+
<PopoverTrigger asChild>
|
|
330
|
+
<Button type="button" variant="outline" disabled={isGenerating}>
|
|
331
|
+
<IconMessageCircle className="h-4 w-4" />
|
|
332
|
+
{t("sessions.askAgent")}
|
|
333
|
+
</Button>
|
|
334
|
+
</PopoverTrigger>
|
|
335
|
+
</TooltipTrigger>
|
|
336
|
+
<TooltipContent>{t("sessions.askAgentTooltip")}</TooltipContent>
|
|
337
|
+
</Tooltip>
|
|
307
338
|
<PopoverContent
|
|
308
339
|
className="w-[calc(100vw-2rem)] p-3 sm:w-[460px]"
|
|
309
340
|
align="end"
|
|
@@ -398,6 +429,7 @@ function ReplayPlayer({
|
|
|
398
429
|
const [speed, setSpeed] = useState(DEFAULT_SPEED);
|
|
399
430
|
const [skipInactive, setSkipInactive] = useState(true);
|
|
400
431
|
const [devToolsOpen, setDevToolsOpen] = useState(false);
|
|
432
|
+
const [devToolsHeight, setDevToolsHeight] = useState(DEFAULT_DEVTOOLS_HEIGHT);
|
|
401
433
|
const [streamedDims, setStreamedDims] = useState<{
|
|
402
434
|
width: number;
|
|
403
435
|
height: number;
|
|
@@ -414,14 +446,25 @@ function ReplayPlayer({
|
|
|
414
446
|
const skipInactiveRef = useLiveRef(skipInactive);
|
|
415
447
|
const currentTimeRef = useLiveRef(currentTime);
|
|
416
448
|
const playingRef = useLiveRef(playing);
|
|
449
|
+
const speedRef = useLiveRef(speed);
|
|
417
450
|
|
|
418
451
|
const currentUrl = useMemo(
|
|
419
452
|
() => currentUrlAt(events, currentTime),
|
|
420
453
|
[events, currentTime],
|
|
421
454
|
);
|
|
422
|
-
const diagnostics = useMemo(
|
|
423
|
-
|
|
424
|
-
|
|
455
|
+
const diagnostics = useMemo(
|
|
456
|
+
() => (devToolsOpen ? extractReplayDiagnostics(events) : EMPTY_DIAGNOSTICS),
|
|
457
|
+
[devToolsOpen, events],
|
|
458
|
+
);
|
|
459
|
+
const devToolsIssueCount = devToolsOpen
|
|
460
|
+
? diagnostics.consoleErrorCount + diagnostics.networkFailedCount
|
|
461
|
+
: response.recording.errorCount;
|
|
462
|
+
const loadingPercent =
|
|
463
|
+
response.totalChunks > 0
|
|
464
|
+
? clamp(response.loadedChunks / response.totalChunks, 0, 1)
|
|
465
|
+
: response.isComplete
|
|
466
|
+
? 1
|
|
467
|
+
: 0;
|
|
425
468
|
|
|
426
469
|
useEffect(() => {
|
|
427
470
|
const el = stageAreaRef.current;
|
|
@@ -479,6 +522,13 @@ function ReplayPlayer({
|
|
|
479
522
|
let localReplayer: any = null;
|
|
480
523
|
|
|
481
524
|
async function loadReplay() {
|
|
525
|
+
const hasPlayableEvents = hasPlayableReplayEvents(events);
|
|
526
|
+
if (!hasPlayableEvents && !response.isComplete) {
|
|
527
|
+
setStatus("loading");
|
|
528
|
+
setError(null);
|
|
529
|
+
setPlaying(false);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
482
532
|
if (events.length < 2) {
|
|
483
533
|
throw new Error(t("sessions.noReplayEvents"));
|
|
484
534
|
}
|
|
@@ -497,7 +547,7 @@ function ReplayPlayer({
|
|
|
497
547
|
setStreamedDims(initialDims);
|
|
498
548
|
localReplayer = new Replayer(events as any[], {
|
|
499
549
|
root: stageRootRef.current,
|
|
500
|
-
speed:
|
|
550
|
+
speed: speedRef.current,
|
|
501
551
|
skipInactive: false,
|
|
502
552
|
showWarning: false,
|
|
503
553
|
showDebug: false,
|
|
@@ -518,6 +568,11 @@ function ReplayPlayer({
|
|
|
518
568
|
const meta = localReplayer.getMetaData?.();
|
|
519
569
|
const total = Number(meta?.totalTime ?? replayDuration(events));
|
|
520
570
|
setTotalTime(Number.isFinite(total) ? total : 0);
|
|
571
|
+
const startAt = clamp(
|
|
572
|
+
currentTimeRef.current,
|
|
573
|
+
0,
|
|
574
|
+
Number.isFinite(total) ? total : 0,
|
|
575
|
+
);
|
|
521
576
|
applyReplayFrameDimensions(localReplayer, initialDims);
|
|
522
577
|
localReplayer.on?.("finish", () => {
|
|
523
578
|
setPlaying(false);
|
|
@@ -540,15 +595,15 @@ function ReplayPlayer({
|
|
|
540
595
|
);
|
|
541
596
|
revealReplayFrame(localReplayer);
|
|
542
597
|
});
|
|
543
|
-
updateTime(
|
|
598
|
+
updateTime(startAt);
|
|
544
599
|
setStatus("ready");
|
|
545
600
|
try {
|
|
546
|
-
localReplayer.play?.(
|
|
601
|
+
localReplayer.play?.(startAt);
|
|
547
602
|
setPlaying(true);
|
|
548
603
|
} catch (autoplayError) {
|
|
549
604
|
console.warn("[session-replay] autoplay failed", autoplayError);
|
|
550
605
|
try {
|
|
551
|
-
localReplayer.pause?.(
|
|
606
|
+
localReplayer.pause?.(startAt);
|
|
552
607
|
} catch {
|
|
553
608
|
// Some rrweb versions only render after play; the first click still works.
|
|
554
609
|
}
|
|
@@ -584,7 +639,15 @@ function ReplayPlayer({
|
|
|
584
639
|
replayerRef.current = null;
|
|
585
640
|
if (stageRootRef.current) stageRootRef.current.innerHTML = "";
|
|
586
641
|
};
|
|
587
|
-
}, [
|
|
642
|
+
}, [
|
|
643
|
+
currentTimeRef,
|
|
644
|
+
events,
|
|
645
|
+
initialDims,
|
|
646
|
+
response.isComplete,
|
|
647
|
+
speedRef,
|
|
648
|
+
t,
|
|
649
|
+
updateTime,
|
|
650
|
+
]);
|
|
588
651
|
|
|
589
652
|
useEffect(() => {
|
|
590
653
|
if (!playing || status !== "ready") {
|
|
@@ -711,8 +774,26 @@ function ReplayPlayer({
|
|
|
711
774
|
onClick={togglePlay}
|
|
712
775
|
/>
|
|
713
776
|
{status === "loading" ? (
|
|
714
|
-
<div className="absolute inset-0 z-30 grid place-items-center bg-background/70 text-sm text-muted-foreground">
|
|
715
|
-
|
|
777
|
+
<div className="absolute inset-0 z-30 grid place-items-center bg-background/70 p-6 text-center text-sm text-muted-foreground">
|
|
778
|
+
<div className="w-full max-w-sm">
|
|
779
|
+
<p>{t("sessions.replayLoading")}</p>
|
|
780
|
+
<div className="mt-3 h-1.5 overflow-hidden rounded-full bg-muted">
|
|
781
|
+
<div
|
|
782
|
+
className="h-full rounded-full bg-primary transition-[width]"
|
|
783
|
+
style={{
|
|
784
|
+
width: `${Math.round(loadingPercent * 100)}%`,
|
|
785
|
+
}}
|
|
786
|
+
/>
|
|
787
|
+
</div>
|
|
788
|
+
{response.totalChunks > 0 ? (
|
|
789
|
+
<p className="mt-2 font-mono text-[11px]">
|
|
790
|
+
{t("sessions.replayLoadingProgress", {
|
|
791
|
+
loaded: String(response.loadedChunks),
|
|
792
|
+
total: String(response.totalChunks),
|
|
793
|
+
})}
|
|
794
|
+
</p>
|
|
795
|
+
) : null}
|
|
796
|
+
</div>
|
|
716
797
|
</div>
|
|
717
798
|
) : null}
|
|
718
799
|
{status === "error" && error ? (
|
|
@@ -845,6 +926,8 @@ function ReplayPlayer({
|
|
|
845
926
|
<SessionDevToolsPanel
|
|
846
927
|
diagnostics={diagnostics}
|
|
847
928
|
currentTime={currentTime}
|
|
929
|
+
height={devToolsHeight}
|
|
930
|
+
onHeightChange={setDevToolsHeight}
|
|
848
931
|
onSeek={(ms) => seek(ms, true)}
|
|
849
932
|
/>
|
|
850
933
|
) : null}
|
|
@@ -880,6 +963,10 @@ function ReplayScrubber({
|
|
|
880
963
|
onSeek: (ms: number) => void;
|
|
881
964
|
}) {
|
|
882
965
|
const t = useT();
|
|
966
|
+
const scrubberMarkers = useMemo(
|
|
967
|
+
() => visibleScrubberMarkers(markers, totalTime),
|
|
968
|
+
[markers, totalTime],
|
|
969
|
+
);
|
|
883
970
|
return (
|
|
884
971
|
<div className="relative min-h-8 min-w-[180px] flex-1">
|
|
885
972
|
<input
|
|
@@ -904,8 +991,8 @@ function ReplayScrubber({
|
|
|
904
991
|
<span
|
|
905
992
|
key={`${range.startMs}-${range.endMs}`}
|
|
906
993
|
className={cn(
|
|
907
|
-
"absolute top-0 h-full rounded-full bg-
|
|
908
|
-
skipInactive ? "opacity-
|
|
994
|
+
"absolute top-0 h-full rounded-full bg-amber-400 transition-opacity",
|
|
995
|
+
skipInactive ? "opacity-50" : "opacity-20",
|
|
909
996
|
)}
|
|
910
997
|
style={{ left: `${left}%`, width: `${width}%` }}
|
|
911
998
|
/>
|
|
@@ -913,7 +1000,7 @@ function ReplayScrubber({
|
|
|
913
1000
|
})}
|
|
914
1001
|
</div>
|
|
915
1002
|
<div className="pointer-events-none absolute inset-x-0 top-1/2 z-30 h-5 -translate-y-1/2">
|
|
916
|
-
{
|
|
1003
|
+
{scrubberMarkers.map((marker) => {
|
|
917
1004
|
const left = totalTime > 0 ? (marker.offsetMs / totalTime) * 100 : 0;
|
|
918
1005
|
return (
|
|
919
1006
|
<span
|
|
@@ -926,7 +1013,13 @@ function ReplayScrubber({
|
|
|
926
1013
|
? "bg-emerald-500"
|
|
927
1014
|
: marker.kind === "click"
|
|
928
1015
|
? "bg-sky-500"
|
|
929
|
-
: "
|
|
1016
|
+
: marker.kind === "console"
|
|
1017
|
+
? marker.severity === "error"
|
|
1018
|
+
? "bg-red-500"
|
|
1019
|
+
: marker.severity === "warn"
|
|
1020
|
+
? "bg-amber-500"
|
|
1021
|
+
: "bg-slate-400"
|
|
1022
|
+
: "bg-violet-500",
|
|
930
1023
|
)}
|
|
931
1024
|
style={{ left: `${left}%` }}
|
|
932
1025
|
/>
|
|
@@ -978,7 +1071,8 @@ function ReplayTimeline({
|
|
|
978
1071
|
onSeek: (ms: number) => void;
|
|
979
1072
|
}) {
|
|
980
1073
|
const t = useT();
|
|
981
|
-
const
|
|
1074
|
+
const [expandedMarkerId, setExpandedMarkerId] = useState<string | null>(null);
|
|
1075
|
+
const visibleMarkers = markers.slice(0, 300);
|
|
982
1076
|
return (
|
|
983
1077
|
<Card className="analytics-session-detail-timeline min-h-0 overflow-hidden">
|
|
984
1078
|
<CardContent className="flex min-h-0 flex-1 flex-col p-0">
|
|
@@ -991,6 +1085,7 @@ function ReplayTimeline({
|
|
|
991
1085
|
{visibleMarkers.length
|
|
992
1086
|
? t("sessions.timelineDescription", {
|
|
993
1087
|
count: String(visibleMarkers.length),
|
|
1088
|
+
total: String(markers.length),
|
|
994
1089
|
})
|
|
995
1090
|
: t("sessions.noTimelineEvents")}
|
|
996
1091
|
</p>
|
|
@@ -998,48 +1093,71 @@ function ReplayTimeline({
|
|
|
998
1093
|
<div className="min-h-0 flex-1 overflow-auto">
|
|
999
1094
|
{visibleMarkers.length ? (
|
|
1000
1095
|
<div className="divide-y">
|
|
1001
|
-
{visibleMarkers.map((marker) =>
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
marker.id === activeMarkerId && "bg-muted",
|
|
1008
|
-
)}
|
|
1009
|
-
onClick={() => onSeek(marker.offsetMs)}
|
|
1010
|
-
>
|
|
1011
|
-
<span
|
|
1012
|
-
className={cn(
|
|
1013
|
-
"mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md border",
|
|
1014
|
-
marker.kind === "navigation" &&
|
|
1015
|
-
"border-amber-500/35 bg-amber-500/10 text-amber-500",
|
|
1016
|
-
marker.kind === "input" &&
|
|
1017
|
-
"border-emerald-500/35 bg-emerald-500/10 text-emerald-500",
|
|
1018
|
-
marker.kind === "click" &&
|
|
1019
|
-
"border-sky-500/35 bg-sky-500/10 text-sky-500",
|
|
1020
|
-
marker.kind === "custom" &&
|
|
1021
|
-
"border-violet-500/35 bg-violet-500/10 text-violet-500",
|
|
1022
|
-
)}
|
|
1096
|
+
{visibleMarkers.map((marker) => {
|
|
1097
|
+
const expanded = marker.id === expandedMarkerId;
|
|
1098
|
+
return (
|
|
1099
|
+
<div
|
|
1100
|
+
key={marker.id}
|
|
1101
|
+
className={cn(marker.id === activeMarkerId && "bg-muted")}
|
|
1023
1102
|
>
|
|
1024
|
-
<
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1103
|
+
<button
|
|
1104
|
+
type="button"
|
|
1105
|
+
className="flex w-full gap-2.5 px-3 py-2.5 text-left transition-colors hover:bg-muted/50"
|
|
1106
|
+
aria-expanded={expanded}
|
|
1107
|
+
onClick={() => {
|
|
1108
|
+
onSeek(marker.offsetMs);
|
|
1109
|
+
setExpandedMarkerId((current) =>
|
|
1110
|
+
current === marker.id ? null : marker.id,
|
|
1111
|
+
);
|
|
1112
|
+
}}
|
|
1113
|
+
>
|
|
1114
|
+
<span
|
|
1115
|
+
className={cn(
|
|
1116
|
+
"mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md border",
|
|
1117
|
+
marker.kind === "navigation" &&
|
|
1118
|
+
"border-amber-500/35 bg-amber-500/10 text-amber-500",
|
|
1119
|
+
marker.kind === "input" &&
|
|
1120
|
+
"border-emerald-500/35 bg-emerald-500/10 text-emerald-500",
|
|
1121
|
+
marker.kind === "click" &&
|
|
1122
|
+
"border-sky-500/35 bg-sky-500/10 text-sky-500",
|
|
1123
|
+
marker.kind === "console" &&
|
|
1124
|
+
marker.severity !== "error" &&
|
|
1125
|
+
"border-slate-500/35 bg-slate-500/10 text-slate-400",
|
|
1126
|
+
marker.kind === "console" &&
|
|
1127
|
+
marker.severity === "error" &&
|
|
1128
|
+
"border-red-500/35 bg-red-500/10 text-red-500",
|
|
1129
|
+
marker.kind === "custom" &&
|
|
1130
|
+
"border-violet-500/35 bg-violet-500/10 text-violet-500",
|
|
1131
|
+
)}
|
|
1132
|
+
>
|
|
1133
|
+
<MarkerIcon kind={marker.kind} />
|
|
1030
1134
|
</span>
|
|
1031
|
-
<span className="
|
|
1032
|
-
|
|
1135
|
+
<span className="min-w-0 flex-1">
|
|
1136
|
+
<span className="flex items-center justify-between gap-3">
|
|
1137
|
+
<span className="truncate text-sm font-medium">
|
|
1138
|
+
{marker.label}
|
|
1139
|
+
</span>
|
|
1140
|
+
<span className="font-mono text-xs text-muted-foreground">
|
|
1141
|
+
{formatClock(marker.offsetMs)}
|
|
1142
|
+
</span>
|
|
1143
|
+
</span>
|
|
1144
|
+
{marker.detail ? (
|
|
1145
|
+
<span className="mt-1 block truncate text-xs text-muted-foreground">
|
|
1146
|
+
{marker.detail}
|
|
1147
|
+
</span>
|
|
1148
|
+
) : null}
|
|
1033
1149
|
</span>
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1150
|
+
<IconChevronRight
|
|
1151
|
+
className={cn(
|
|
1152
|
+
"mt-1 h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform rtl:-scale-x-100",
|
|
1153
|
+
expanded && "rotate-90 rtl:scale-x-100",
|
|
1154
|
+
)}
|
|
1155
|
+
/>
|
|
1156
|
+
</button>
|
|
1157
|
+
{expanded ? <MarkerDetail marker={marker} /> : null}
|
|
1158
|
+
</div>
|
|
1159
|
+
);
|
|
1160
|
+
})}
|
|
1043
1161
|
</div>
|
|
1044
1162
|
) : (
|
|
1045
1163
|
<div className="p-4 text-sm text-muted-foreground">
|
|
@@ -1056,9 +1174,36 @@ function MarkerIcon({ kind }: { kind: ReplayMarker["kind"] }) {
|
|
|
1056
1174
|
if (kind === "navigation") return <IconRoute className="h-4 w-4" />;
|
|
1057
1175
|
if (kind === "input") return <IconKeyboard className="h-4 w-4" />;
|
|
1058
1176
|
if (kind === "click") return <IconMouse className="h-4 w-4" />;
|
|
1177
|
+
if (kind === "console") return <IconTerminal2 className="h-4 w-4" />;
|
|
1059
1178
|
return <IconTimelineEvent className="h-4 w-4" />;
|
|
1060
1179
|
}
|
|
1061
1180
|
|
|
1181
|
+
function MarkerDetail({ marker }: { marker: ReplayMarker }) {
|
|
1182
|
+
return (
|
|
1183
|
+
<div className="space-y-2 border-t bg-muted/25 px-3 py-2 ps-12">
|
|
1184
|
+
{marker.detail ? (
|
|
1185
|
+
<p className="break-words text-xs text-muted-foreground">
|
|
1186
|
+
{marker.detail}
|
|
1187
|
+
</p>
|
|
1188
|
+
) : null}
|
|
1189
|
+
{marker.fields?.length ? (
|
|
1190
|
+
<dl className="grid gap-2 text-xs">
|
|
1191
|
+
{marker.fields.map((field) => (
|
|
1192
|
+
<div key={`${marker.id}-${field.label}`} className="min-w-0">
|
|
1193
|
+
<dt className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1194
|
+
{field.label}
|
|
1195
|
+
</dt>
|
|
1196
|
+
<dd className="mt-0.5 break-all font-mono text-[11px] text-foreground/80">
|
|
1197
|
+
{field.value}
|
|
1198
|
+
</dd>
|
|
1199
|
+
</div>
|
|
1200
|
+
))}
|
|
1201
|
+
</dl>
|
|
1202
|
+
) : null}
|
|
1203
|
+
</div>
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1062
1207
|
function DetailSkeleton() {
|
|
1063
1208
|
return (
|
|
1064
1209
|
<div className="analytics-session-detail-workbench grid min-h-0 flex-1 gap-3">
|
|
@@ -1070,14 +1215,136 @@ function DetailSkeleton() {
|
|
|
1070
1215
|
|
|
1071
1216
|
function useSessionReplayPlayback(recordingId: string) {
|
|
1072
1217
|
const agentAccessToken = currentSessionReplayAgentAccessToken();
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1218
|
+
const [state, setState] = useState<{
|
|
1219
|
+
data: SessionReplayPlaybackResponse | null;
|
|
1220
|
+
isLoading: boolean;
|
|
1221
|
+
error: Error | null;
|
|
1222
|
+
}>({
|
|
1223
|
+
data: null,
|
|
1224
|
+
isLoading: Boolean(recordingId),
|
|
1225
|
+
error: null,
|
|
1080
1226
|
});
|
|
1227
|
+
|
|
1228
|
+
useEffect(() => {
|
|
1229
|
+
if (!recordingId) {
|
|
1230
|
+
setState({ data: null, isLoading: false, error: null });
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
let cancelled = false;
|
|
1235
|
+
setState({ data: null, isLoading: true, error: null });
|
|
1236
|
+
|
|
1237
|
+
async function loadProgressively() {
|
|
1238
|
+
try {
|
|
1239
|
+
const manifest = await fetchReplayManifest(recordingId, {
|
|
1240
|
+
agentAccessToken,
|
|
1241
|
+
});
|
|
1242
|
+
if (cancelled) return;
|
|
1243
|
+
|
|
1244
|
+
const loadedChunks = new Array<ReplayChunkEvents | undefined>(
|
|
1245
|
+
manifest.chunks.length,
|
|
1246
|
+
);
|
|
1247
|
+
let loadedCount = 0;
|
|
1248
|
+
let loadedBytes = 0;
|
|
1249
|
+
let unavailableChunks = 0;
|
|
1250
|
+
let nextIndex = 0;
|
|
1251
|
+
let firstPlayablePublished = false;
|
|
1252
|
+
|
|
1253
|
+
const publish = (force = false) => {
|
|
1254
|
+
const complete = loadedCount >= manifest.chunks.length;
|
|
1255
|
+
const chunks = loadedChunks.filter(
|
|
1256
|
+
(chunk): chunk is ReplayChunkEvents => Boolean(chunk),
|
|
1257
|
+
);
|
|
1258
|
+
const shouldPublishEvents =
|
|
1259
|
+
force ||
|
|
1260
|
+
complete ||
|
|
1261
|
+
(!firstPlayablePublished &&
|
|
1262
|
+
hasPlayableReplayEvents(chunks.flatMap((chunk) => chunk.events)));
|
|
1263
|
+
|
|
1264
|
+
if (shouldPublishEvents) {
|
|
1265
|
+
firstPlayablePublished = true;
|
|
1266
|
+
setState({
|
|
1267
|
+
data: playbackResponseFromChunks(manifest, chunks, {
|
|
1268
|
+
isComplete: complete,
|
|
1269
|
+
loadedChunks: loadedCount,
|
|
1270
|
+
loadedBytes,
|
|
1271
|
+
unavailableChunks,
|
|
1272
|
+
}),
|
|
1273
|
+
isLoading: false,
|
|
1274
|
+
error: null,
|
|
1275
|
+
});
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
setState((current) => ({
|
|
1280
|
+
data: current.data
|
|
1281
|
+
? {
|
|
1282
|
+
...current.data,
|
|
1283
|
+
loadedChunks: loadedCount,
|
|
1284
|
+
loadedBytes,
|
|
1285
|
+
unavailableChunks,
|
|
1286
|
+
}
|
|
1287
|
+
: playbackResponseFromChunks(manifest, [], {
|
|
1288
|
+
isComplete: complete,
|
|
1289
|
+
loadedChunks: loadedCount,
|
|
1290
|
+
loadedBytes,
|
|
1291
|
+
unavailableChunks,
|
|
1292
|
+
}),
|
|
1293
|
+
isLoading: false,
|
|
1294
|
+
error: null,
|
|
1295
|
+
}));
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
publish();
|
|
1299
|
+
|
|
1300
|
+
async function worker() {
|
|
1301
|
+
while (!cancelled && nextIndex < manifest.chunks.length) {
|
|
1302
|
+
const index = nextIndex;
|
|
1303
|
+
nextIndex += 1;
|
|
1304
|
+
const chunk = await fetchReplayChunk(manifest.chunks[index], {
|
|
1305
|
+
agentAccessToken,
|
|
1306
|
+
});
|
|
1307
|
+
loadedChunks[index] = chunk;
|
|
1308
|
+
loadedCount += 1;
|
|
1309
|
+
loadedBytes += manifest.chunks[index].byteLength;
|
|
1310
|
+
if (chunk.unavailable) unavailableChunks += 1;
|
|
1311
|
+
if (!cancelled) publish();
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
await Promise.all(
|
|
1316
|
+
Array.from(
|
|
1317
|
+
{
|
|
1318
|
+
length: Math.min(
|
|
1319
|
+
REPLAY_CHUNK_FETCH_CONCURRENCY,
|
|
1320
|
+
manifest.chunks.length,
|
|
1321
|
+
),
|
|
1322
|
+
},
|
|
1323
|
+
worker,
|
|
1324
|
+
),
|
|
1325
|
+
);
|
|
1326
|
+
if (!cancelled) publish(true);
|
|
1327
|
+
} catch (loadError) {
|
|
1328
|
+
if (cancelled) return;
|
|
1329
|
+
cancelled = true;
|
|
1330
|
+
setState({
|
|
1331
|
+
data: null,
|
|
1332
|
+
isLoading: false,
|
|
1333
|
+
error:
|
|
1334
|
+
loadError instanceof Error
|
|
1335
|
+
? loadError
|
|
1336
|
+
: new Error(String(loadError)),
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
void loadProgressively();
|
|
1342
|
+
return () => {
|
|
1343
|
+
cancelled = true;
|
|
1344
|
+
};
|
|
1345
|
+
}, [agentAccessToken, recordingId]);
|
|
1346
|
+
|
|
1347
|
+
return state;
|
|
1081
1348
|
}
|
|
1082
1349
|
|
|
1083
1350
|
interface FetchSessionReplayPlaybackOptions {
|
|
@@ -1091,16 +1358,47 @@ export async function fetchSessionReplayPlayback(
|
|
|
1091
1358
|
const manifest = await fetchReplayManifest(recordingId, options);
|
|
1092
1359
|
const chunks = await fetchReplayChunks(manifest.chunks, options);
|
|
1093
1360
|
const unavailableChunks = chunks.filter((chunk) => chunk.unavailable).length;
|
|
1361
|
+
const loadedBytes = manifest.chunks.reduce(
|
|
1362
|
+
(sum, chunk) => sum + chunk.byteLength,
|
|
1363
|
+
0,
|
|
1364
|
+
);
|
|
1365
|
+
return playbackResponseFromChunks(manifest, chunks, {
|
|
1366
|
+
isComplete: true,
|
|
1367
|
+
loadedChunks: chunks.length,
|
|
1368
|
+
loadedBytes,
|
|
1369
|
+
unavailableChunks,
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
function playbackResponseFromChunks(
|
|
1374
|
+
manifest: SessionReplayManifestResponse,
|
|
1375
|
+
chunks: ReplayChunkEvents[],
|
|
1376
|
+
progress: {
|
|
1377
|
+
isComplete: boolean;
|
|
1378
|
+
loadedChunks: number;
|
|
1379
|
+
loadedBytes: number;
|
|
1380
|
+
unavailableChunks: number;
|
|
1381
|
+
},
|
|
1382
|
+
): SessionReplayPlaybackResponse {
|
|
1094
1383
|
const eventCount = chunks.reduce(
|
|
1095
1384
|
(sum, chunk) => sum + chunk.events.length,
|
|
1096
1385
|
0,
|
|
1097
1386
|
);
|
|
1387
|
+
const totalBytes = manifest.chunks.reduce(
|
|
1388
|
+
(sum, chunk) => sum + chunk.byteLength,
|
|
1389
|
+
0,
|
|
1390
|
+
);
|
|
1098
1391
|
return {
|
|
1099
1392
|
recording: manifest.recording,
|
|
1100
1393
|
chunks,
|
|
1101
1394
|
eventCount,
|
|
1102
1395
|
truncated: false,
|
|
1103
|
-
unavailableChunks,
|
|
1396
|
+
unavailableChunks: progress.unavailableChunks,
|
|
1397
|
+
loadedChunks: progress.loadedChunks,
|
|
1398
|
+
totalChunks: manifest.chunks.length,
|
|
1399
|
+
loadedBytes: progress.loadedBytes,
|
|
1400
|
+
totalBytes,
|
|
1401
|
+
isComplete: progress.isComplete,
|
|
1104
1402
|
};
|
|
1105
1403
|
}
|
|
1106
1404
|
|
|
@@ -1436,7 +1734,7 @@ function isScriptLikeLink(attributes: unknown): boolean {
|
|
|
1436
1734
|
);
|
|
1437
1735
|
}
|
|
1438
1736
|
|
|
1439
|
-
function buildReplayMarkers(events: AnyReplayEvent[]): ReplayMarker[] {
|
|
1737
|
+
export function buildReplayMarkers(events: AnyReplayEvent[]): ReplayMarker[] {
|
|
1440
1738
|
const startedAt = replayStartedAt(events);
|
|
1441
1739
|
const markers: ReplayMarker[] = [];
|
|
1442
1740
|
for (const event of events) {
|
|
@@ -1452,19 +1750,26 @@ function buildReplayMarkers(events: AnyReplayEvent[]): ReplayMarker[] {
|
|
|
1452
1750
|
timestamp,
|
|
1453
1751
|
offsetMs: Math.max(0, timestamp - startedAt),
|
|
1454
1752
|
kind: "navigation",
|
|
1455
|
-
label:
|
|
1753
|
+
label: "Navigate",
|
|
1456
1754
|
detail: href,
|
|
1755
|
+
fields: [{ label: "URL", value: href }],
|
|
1457
1756
|
});
|
|
1458
1757
|
} else if (
|
|
1459
1758
|
event.type === RRWEB_EVENT_TYPE.IncrementalSnapshot &&
|
|
1460
1759
|
event.data?.source === INCREMENTAL_SOURCE.Input
|
|
1461
1760
|
) {
|
|
1761
|
+
const inputValue = replayInputValue(event.data);
|
|
1462
1762
|
markers.push({
|
|
1463
1763
|
id: `input-${timestamp}-${markers.length}`,
|
|
1464
1764
|
timestamp,
|
|
1465
1765
|
offsetMs: Math.max(0, timestamp - startedAt),
|
|
1466
1766
|
kind: "input",
|
|
1467
|
-
label: "Input",
|
|
1767
|
+
label: inputValue ? "Input changed" : "Input",
|
|
1768
|
+
detail: inputValue,
|
|
1769
|
+
fields: markerFields([
|
|
1770
|
+
["Element id", event.data?.id],
|
|
1771
|
+
["Value", inputValue],
|
|
1772
|
+
]),
|
|
1468
1773
|
});
|
|
1469
1774
|
} else if (
|
|
1470
1775
|
event.type === RRWEB_EVENT_TYPE.IncrementalSnapshot &&
|
|
@@ -1478,25 +1783,75 @@ function buildReplayMarkers(events: AnyReplayEvent[]): ReplayMarker[] {
|
|
|
1478
1783
|
timestamp,
|
|
1479
1784
|
offsetMs: Math.max(0, timestamp - startedAt),
|
|
1480
1785
|
kind: "click",
|
|
1481
|
-
label: event.data?.type
|
|
1786
|
+
label: mouseInteractionLabel(event.data?.type),
|
|
1787
|
+
detail: pointerDetail(event.data),
|
|
1788
|
+
fields: markerFields([
|
|
1789
|
+
["Element id", event.data?.id],
|
|
1790
|
+
["X", event.data?.x],
|
|
1791
|
+
["Y", event.data?.y],
|
|
1792
|
+
]),
|
|
1482
1793
|
});
|
|
1483
1794
|
} else if (event.type === RRWEB_EVENT_TYPE.Custom) {
|
|
1484
|
-
|
|
1485
|
-
|
|
1795
|
+
const marker = customReplayMarker(
|
|
1796
|
+
event,
|
|
1486
1797
|
timestamp,
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
typeof event.data?.payload?.message === "string"
|
|
1492
|
-
? event.data.payload.message
|
|
1493
|
-
: undefined,
|
|
1494
|
-
});
|
|
1798
|
+
startedAt,
|
|
1799
|
+
markers.length,
|
|
1800
|
+
);
|
|
1801
|
+
if (marker) markers.push(marker);
|
|
1495
1802
|
}
|
|
1496
1803
|
}
|
|
1497
1804
|
return markers.sort((a, b) => a.offsetMs - b.offsetMs);
|
|
1498
1805
|
}
|
|
1499
1806
|
|
|
1807
|
+
function customReplayMarker(
|
|
1808
|
+
event: AnyReplayEvent,
|
|
1809
|
+
timestamp: number,
|
|
1810
|
+
startedAt: number,
|
|
1811
|
+
index: number,
|
|
1812
|
+
): ReplayMarker | null {
|
|
1813
|
+
const tag = String(event.data?.tag ?? "Custom event");
|
|
1814
|
+
const payload = isRecord(event.data?.payload) ? event.data.payload : {};
|
|
1815
|
+
const offsetMs = Math.max(0, timestamp - startedAt);
|
|
1816
|
+
|
|
1817
|
+
if (tag === SESSION_REPLAY_CONSOLE_EVENT_TAG) {
|
|
1818
|
+
const level = typeof payload.level === "string" ? payload.level : "log";
|
|
1819
|
+
if (level !== "error" && level !== "warn") return null;
|
|
1820
|
+
const message =
|
|
1821
|
+
typeof payload.message === "string" ? payload.message : undefined;
|
|
1822
|
+
return {
|
|
1823
|
+
id: `console-${timestamp}-${index}`,
|
|
1824
|
+
timestamp,
|
|
1825
|
+
offsetMs,
|
|
1826
|
+
kind: "console",
|
|
1827
|
+
label: level === "error" ? "Console error" : `Console ${level}`,
|
|
1828
|
+
detail: message,
|
|
1829
|
+
severity:
|
|
1830
|
+
level === "error" ? "error" : level === "warn" ? "warn" : "info",
|
|
1831
|
+
fields: markerFields([
|
|
1832
|
+
["Level", level],
|
|
1833
|
+
["Source", payload.source],
|
|
1834
|
+
["Message", message],
|
|
1835
|
+
["URL", payload.url],
|
|
1836
|
+
["Stack", payload.stack],
|
|
1837
|
+
]),
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
if (tag === SESSION_REPLAY_NETWORK_EVENT_TAG) {
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
return {
|
|
1846
|
+
id: `custom-${timestamp}-${index}`,
|
|
1847
|
+
timestamp,
|
|
1848
|
+
offsetMs,
|
|
1849
|
+
kind: "custom",
|
|
1850
|
+
label: tag,
|
|
1851
|
+
detail: typeof payload.message === "string" ? payload.message : undefined,
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1500
1855
|
function buildIdleSkipRanges(events: AnyReplayEvent[]): SkipRange[] {
|
|
1501
1856
|
const startedAt = replayStartedAt(events);
|
|
1502
1857
|
const interactions: number[] = [];
|
|
@@ -1563,6 +1918,18 @@ function currentUrlAt(events: AnyReplayEvent[], currentTime: number): string {
|
|
|
1563
1918
|
return current;
|
|
1564
1919
|
}
|
|
1565
1920
|
|
|
1921
|
+
function hasPlayableReplayEvents(events: unknown[]): boolean {
|
|
1922
|
+
let hasFullSnapshot = false;
|
|
1923
|
+
let hasMeta = false;
|
|
1924
|
+
for (const event of events) {
|
|
1925
|
+
if (!isRecord(event)) continue;
|
|
1926
|
+
if (event.type === RRWEB_EVENT_TYPE.FullSnapshot) hasFullSnapshot = true;
|
|
1927
|
+
if (event.type === RRWEB_EVENT_TYPE.Meta) hasMeta = true;
|
|
1928
|
+
if (hasFullSnapshot && hasMeta) return true;
|
|
1929
|
+
}
|
|
1930
|
+
return false;
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1566
1933
|
export function replayViewportDimensions(
|
|
1567
1934
|
events: AnyReplayEvent[],
|
|
1568
1935
|
): ReplayViewportDimensions | null {
|
|
@@ -1639,13 +2006,66 @@ function replayDuration(events: AnyReplayEvent[]): number {
|
|
|
1639
2006
|
return Math.max(0, endedAt - startedAt);
|
|
1640
2007
|
}
|
|
1641
2008
|
|
|
1642
|
-
function
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
return href;
|
|
2009
|
+
function replayInputValue(data: AnyRecord): string | undefined {
|
|
2010
|
+
if (typeof data.text === "string" && data.text) return data.text;
|
|
2011
|
+
if (typeof data.value === "string" && data.value) return data.value;
|
|
2012
|
+
if (typeof data.isChecked === "boolean") {
|
|
2013
|
+
return data.isChecked ? "Checked" : "Unchecked";
|
|
1648
2014
|
}
|
|
2015
|
+
return undefined;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
function mouseInteractionLabel(type: unknown): string {
|
|
2019
|
+
if (type === MOUSE_INTERACTION.DblClick) return "Double click";
|
|
2020
|
+
if (type === MOUSE_INTERACTION.Focus) return "Focus";
|
|
2021
|
+
return "Click";
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
function pointerDetail(data: AnyRecord): string | undefined {
|
|
2025
|
+
const x = Number(data.x);
|
|
2026
|
+
const y = Number(data.y);
|
|
2027
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
2028
|
+
return `x ${Math.round(x)}, y ${Math.round(y)}`;
|
|
2029
|
+
}
|
|
2030
|
+
return undefined;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
function markerFields(
|
|
2034
|
+
entries: Array<[string, unknown]>,
|
|
2035
|
+
): Array<{ label: string; value: string }> | undefined {
|
|
2036
|
+
const fields = entries.flatMap(([label, value]) => {
|
|
2037
|
+
if (value === undefined || value === null || value === "") return [];
|
|
2038
|
+
return [{ label, value: String(value) }];
|
|
2039
|
+
});
|
|
2040
|
+
return fields.length ? fields : undefined;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
function visibleScrubberMarkers(
|
|
2044
|
+
markers: ReplayMarker[],
|
|
2045
|
+
totalTime: number,
|
|
2046
|
+
): ReplayMarker[] {
|
|
2047
|
+
if (markers.length <= SCRUBBER_MARKER_LIMIT || totalTime <= 0) return markers;
|
|
2048
|
+
const bucketMs = Math.max(1, totalTime / SCRUBBER_MARKER_LIMIT);
|
|
2049
|
+
const buckets = new Map<number, ReplayMarker>();
|
|
2050
|
+
|
|
2051
|
+
for (const marker of markers) {
|
|
2052
|
+
const bucket = Math.floor(marker.offsetMs / bucketMs);
|
|
2053
|
+
const current = buckets.get(bucket);
|
|
2054
|
+
if (!current || markerPriority(marker) > markerPriority(current)) {
|
|
2055
|
+
buckets.set(bucket, marker);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
return Array.from(buckets.values()).sort((a, b) => a.offsetMs - b.offsetMs);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
function markerPriority(marker: ReplayMarker): number {
|
|
2063
|
+
if (marker.severity === "error") return 6;
|
|
2064
|
+
if (marker.kind === "navigation") return 5;
|
|
2065
|
+
if (marker.kind === "click") return 4;
|
|
2066
|
+
if (marker.severity === "warn") return 3;
|
|
2067
|
+
if (marker.kind === "input") return 2;
|
|
2068
|
+
return 1;
|
|
1649
2069
|
}
|
|
1650
2070
|
|
|
1651
2071
|
function visitorLabel(
|