@agent-native/core 0.98.0 → 0.98.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 +1 -1
- package/corpus/core/CHANGELOG.md +24 -0
- package/corpus/core/package.json +2 -2
- package/corpus/core/src/action-change-marker.ts +8 -1
- package/corpus/core/src/changelog/parse.ts +8 -3
- package/corpus/core/src/client/AgentPanel.tsx +2 -3
- package/corpus/core/src/client/AssistantChat.tsx +83 -39
- package/corpus/core/src/client/analytics.ts +29 -0
- package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +16 -2
- package/corpus/core/src/client/chat-model-groups.ts +28 -0
- package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +442 -85
- package/corpus/core/src/client/composer/TiptapComposer.tsx +46 -29
- package/corpus/core/src/client/composer/VoiceButton.tsx +2 -1
- package/corpus/core/src/client/composer/index.ts +5 -0
- package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +1206 -67
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/progress/RunsTray.tsx +13 -1
- package/corpus/core/src/client/settings/SecretsSection.tsx +387 -266
- package/corpus/core/src/client/settings/SettingsPanel.tsx +117 -38
- package/corpus/core/src/client/use-action.ts +12 -0
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +8 -1
- package/corpus/core/src/client/use-agent-engine-configured.ts +17 -3
- package/corpus/core/src/client/use-db-sync.ts +84 -34
- package/corpus/core/src/client/use-pausing-interval.ts +14 -4
- package/corpus/core/src/integrations/scope-store.ts +1 -1
- package/corpus/core/src/integrations/usage-budget-store.ts +1 -1
- package/corpus/core/src/localization/default-messages.ts +44 -1
- package/corpus/core/src/mcp-client/workspace-servers.ts +4 -4
- package/corpus/core/src/secrets/crypto.ts +28 -8
- package/corpus/core/src/server/action-change.ts +5 -1
- package/corpus/core/src/server/action-routes.ts +8 -0
- package/corpus/core/src/server/poll.ts +1 -0
- package/corpus/core/src/server/realtime-voice.ts +299 -13
- package/corpus/core/src/styles/agent-native.css +2 -2
- package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
- package/corpus/core/src/vite/client.ts +9 -1
- package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +10 -5
- package/corpus/templates/analytics/AGENTS.md +9 -13
- package/corpus/templates/analytics/actions/navigate.ts +59 -5
- package/corpus/templates/analytics/actions/view-screen.ts +3 -6
- package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +20 -3
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +2 -1
- package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -6
- package/corpus/templates/analytics/app/global.css +19 -20
- package/corpus/templates/analytics/app/lib/provider-corpus-job-sync.ts +26 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +35 -341
- package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +38 -7
- package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +1 -1
- package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +3 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +333 -272
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +72 -17
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +297 -108
- package/corpus/templates/analytics/app/root.tsx +28 -8
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +2 -1
- package/corpus/templates/analytics/changelog/2026-06-25-referrer-dashboard-panels-show-external-domains.md +2 -1
- package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-11-analytics-no-longer-repeatedly-restarts-background-requests-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-analytics-only-asks-for-replay-storage-when-you-enable-sessi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-charts-now-show-their-loading-skeleton-while-refreshing.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-realtime-voice-now-shows-a-clear-connection-indicator-comple.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-event-counts-stay-hidden-until-recordings-finish-load.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-storage-key-mismatches-show-actionable-setup-guidance.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-replay-timelines-group-continuous-scrolling-and-dev-tools-co.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-filters-now-stay-compact-in-a-settings-menu-at-mediu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replay-console-errors-now-link-to-matching-issues-or.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-load-large-recordings-faster-and-surface-fai.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-preserve-recorded-styles-and-viewport-layout.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-11-session-replays-stay-centered-instead-of-clipping-wide-recor.md +6 -0
- package/corpus/templates/analytics/package.json +1 -1
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -303
- package/corpus/templates/analytics/server/handlers/session-replay.ts +57 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
- package/corpus/templates/analytics/server/lib/demo-dashboards.ts +16 -3
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +12 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +208 -2
- package/corpus/templates/analytics/server/plugins/onboarding.ts +7 -79
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks.get.ts +1 -0
- package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/assets/app/global.css +4 -4
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +0 -1
- package/corpus/templates/assets/app/routes/library.tsx +0 -1
- package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/brain/app/global.css +5 -5
- package/corpus/templates/brain/app/root.tsx +0 -3
- package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/calendar/app/root.tsx +1 -0
- package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +74 -8
- package/corpus/templates/clips/app/hooks/use-library.ts +12 -25
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +20 -22
- package/corpus/templates/clips/changelog/2026-07-11-generated-titles-no-longer-cause-continuous-background-refreshes.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-11-s3-storage-setup-stays-collapsed-until-you-choose-it-as-the-.md +6 -0
- package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/content/app/hooks/use-comments.ts +0 -1
- package/corpus/templates/content/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
- package/corpus/templates/design/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -2
- package/corpus/templates/macros/app/root.tsx +0 -3
- package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/mail/app/hooks/use-compose-state.ts +1 -0
- package/corpus/templates/mail/app/root.tsx +1 -12
- package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +5 -2
- package/corpus/templates/plan/app/hooks/use-plans.ts +6 -13
- package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -27
- package/corpus/templates/plan/changelog/2026-07-11-plan-refreshes-only-when-content-changes.md +6 -0
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +4 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
- package/corpus/templates/slides/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -0
- package/corpus/templates/slides/app/hooks/use-slide-comments.ts +7 -31
- package/corpus/templates/slides/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
- package/dist/action-change-marker.d.ts +1 -0
- package/dist/action-change-marker.d.ts.map +1 -1
- package/dist/action-change-marker.js +7 -1
- package/dist/action-change-marker.js.map +1 -1
- package/dist/changelog/parse.d.ts +4 -2
- package/dist/changelog/parse.d.ts.map +1 -1
- package/dist/changelog/parse.js +5 -3
- package/dist/changelog/parse.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +7 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +62 -40
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +26 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.js +9 -2
- package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +24 -0
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts +40 -4
- package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.js +172 -42
- package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +4 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +47 -29
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/VoiceButton.js +2 -2
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/composer/index.d.ts +2 -2
- package/dist/client/composer/index.d.ts.map +1 -1
- package/dist/client/composer/index.js +1 -1
- package/dist/client/composer/index.js.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.d.ts +100 -3
- package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.js +952 -51
- package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/progress/RunsTray.d.ts.map +1 -1
- package/dist/client/progress/RunsTray.js +10 -1
- package/dist/client/progress/RunsTray.js.map +1 -1
- package/dist/client/settings/SecretsSection.d.ts +2 -3
- package/dist/client/settings/SecretsSection.d.ts.map +1 -1
- package/dist/client/settings/SecretsSection.js +46 -22
- package/dist/client/settings/SecretsSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +9 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +46 -12
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/use-action.d.ts +2 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +10 -0
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +2 -0
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +7 -1
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +17 -3
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +8 -2
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +57 -32
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-pausing-interval.d.ts.map +1 -1
- package/dist/client/use-pausing-interval.js +16 -4
- package/dist/client/use-pausing-interval.js.map +1 -1
- package/dist/integrations/scope-store.js +1 -1
- package/dist/integrations/scope-store.js.map +1 -1
- package/dist/integrations/usage-budget-store.js +1 -1
- package/dist/integrations/usage-budget-store.js.map +1 -1
- package/dist/localization/default-messages.d.ts +41 -1
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +41 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp-client/workspace-servers.js +4 -4
- package/dist/mcp-client/workspace-servers.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/secrets/crypto.d.ts +7 -6
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +30 -11
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-change.d.ts +1 -0
- package/dist/server/action-change.d.ts.map +1 -1
- package/dist/server/action-change.js +4 -1
- package/dist/server/action-change.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +5 -0
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +1 -0
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-voice.d.ts +7 -0
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +222 -12
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/styles/agent-native.css +2 -2
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +6 -1
- package/dist/vite/client.js.map +1 -1
- package/package.json +2 -2
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
|
@@ -45,10 +45,20 @@ import {
|
|
|
45
45
|
export type SessionIssueMatch = { issueId: string; status: string };
|
|
46
46
|
|
|
47
47
|
/** Deep-link from a session error to the Monitoring → Errors issue detail. */
|
|
48
|
-
function issueDetailPath(issueId: string): string {
|
|
48
|
+
export function issueDetailPath(issueId: string): string {
|
|
49
49
|
return `/monitoring?view=errors&issue=${encodeURIComponent(issueId)}`;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/** Search Monitoring for all captured issues resembling an unmatched line. */
|
|
53
|
+
export function issueSearchPath(message: string): string {
|
|
54
|
+
const params = new URLSearchParams({
|
|
55
|
+
view: "errors",
|
|
56
|
+
status: "all",
|
|
57
|
+
q: message,
|
|
58
|
+
});
|
|
59
|
+
return `/monitoring?${params.toString()}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
52
62
|
/** Pause row auto-follow for a while after the user scrolls the list. */
|
|
53
63
|
const MANUAL_SCROLL_FOLLOW_PAUSE_MS = 4000;
|
|
54
64
|
const DEVTOOLS_ROW_HEIGHT = 34;
|
|
@@ -65,14 +75,13 @@ const DEVTOOLS_MAX_HEIGHT = 620;
|
|
|
65
75
|
export function buildDevToolsRowOffsets(
|
|
66
76
|
entryCount: number,
|
|
67
77
|
expandedIndex: number,
|
|
78
|
+
expandedHeight = DEVTOOLS_EXPANDED_ESTIMATE,
|
|
68
79
|
): number[] {
|
|
69
80
|
const offsets = new Array<number>(entryCount + 1);
|
|
70
81
|
offsets[0] = 0;
|
|
71
82
|
for (let index = 0; index < entryCount; index += 1) {
|
|
72
83
|
const height =
|
|
73
|
-
index === expandedIndex
|
|
74
|
-
? DEVTOOLS_EXPANDED_ESTIMATE
|
|
75
|
-
: DEVTOOLS_ROW_HEIGHT;
|
|
84
|
+
index === expandedIndex ? expandedHeight : DEVTOOLS_ROW_HEIGHT;
|
|
76
85
|
offsets[index + 1] = offsets[index] + height;
|
|
77
86
|
}
|
|
78
87
|
return offsets;
|
|
@@ -86,6 +95,7 @@ export function SessionDevToolsPanel({
|
|
|
86
95
|
onHeightChange,
|
|
87
96
|
onSeek,
|
|
88
97
|
issueMatches,
|
|
98
|
+
issueMatching = false,
|
|
89
99
|
}: {
|
|
90
100
|
diagnostics: ReplayDevToolsDiagnostics;
|
|
91
101
|
currentTime: number;
|
|
@@ -95,6 +105,8 @@ export function SessionDevToolsPanel({
|
|
|
95
105
|
onSeek: (ms: number) => void;
|
|
96
106
|
/** Resolved error issues by console entry id, for cross-linking to Errors. */
|
|
97
107
|
issueMatches?: ReadonlyMap<string, SessionIssueMatch>;
|
|
108
|
+
/** Prevent an unmatched fallback from flashing while issue lookup is active. */
|
|
109
|
+
issueMatching?: boolean;
|
|
98
110
|
}) {
|
|
99
111
|
const t = useT();
|
|
100
112
|
const [tab, setTab] = useState<"console" | "network">("console");
|
|
@@ -265,6 +277,7 @@ export function SessionDevToolsPanel({
|
|
|
265
277
|
active={entry.id === activeConsoleId}
|
|
266
278
|
selected={expanded}
|
|
267
279
|
issueMatch={issueMatches?.get(entry.id) ?? null}
|
|
280
|
+
issueMatching={issueMatching}
|
|
268
281
|
onSelect={() =>
|
|
269
282
|
setSelectedConsoleId((current) =>
|
|
270
283
|
current === entry.id ? null : entry.id,
|
|
@@ -414,6 +427,12 @@ function VirtualizedDevToolsList<T extends { id: string }>({
|
|
|
414
427
|
const lastManualScrollAtRef = useRef(0);
|
|
415
428
|
const [scrollTop, setScrollTop] = useState(0);
|
|
416
429
|
const [viewportHeight, setViewportHeight] = useState(0);
|
|
430
|
+
const [expandedRowHeight, setExpandedRowHeight] = useState(
|
|
431
|
+
DEVTOOLS_EXPANDED_ESTIMATE,
|
|
432
|
+
);
|
|
433
|
+
const [expandedElement, setExpandedElement] = useState<HTMLDivElement | null>(
|
|
434
|
+
null,
|
|
435
|
+
);
|
|
417
436
|
|
|
418
437
|
const activeIndex = activeEntryId
|
|
419
438
|
? entries.findIndex((entry) => entry.id === activeEntryId)
|
|
@@ -423,12 +442,31 @@ function VirtualizedDevToolsList<T extends { id: string }>({
|
|
|
423
442
|
: -1;
|
|
424
443
|
|
|
425
444
|
const rowOffsets = useMemo(
|
|
426
|
-
() =>
|
|
427
|
-
|
|
445
|
+
() =>
|
|
446
|
+
buildDevToolsRowOffsets(entries.length, expandedIndex, expandedRowHeight),
|
|
447
|
+
[entries.length, expandedIndex, expandedRowHeight],
|
|
428
448
|
);
|
|
429
449
|
|
|
430
450
|
const totalHeight = rowOffsets[entries.length] ?? 0;
|
|
431
451
|
|
|
452
|
+
useEffect(() => {
|
|
453
|
+
setExpandedRowHeight(DEVTOOLS_EXPANDED_ESTIMATE);
|
|
454
|
+
}, [expandedEntryId]);
|
|
455
|
+
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
if (!expandedElement) return;
|
|
458
|
+
const update = () => {
|
|
459
|
+
const measured = Math.ceil(
|
|
460
|
+
expandedElement.getBoundingClientRect().height,
|
|
461
|
+
);
|
|
462
|
+
if (measured > 0) setExpandedRowHeight(measured);
|
|
463
|
+
};
|
|
464
|
+
update();
|
|
465
|
+
const observer = new ResizeObserver(update);
|
|
466
|
+
observer.observe(expandedElement);
|
|
467
|
+
return () => observer.disconnect();
|
|
468
|
+
}, [expandedElement]);
|
|
469
|
+
|
|
432
470
|
useEffect(() => {
|
|
433
471
|
const el = containerRef.current;
|
|
434
472
|
if (!el) return;
|
|
@@ -510,18 +548,11 @@ function VirtualizedDevToolsList<T extends { id: string }>({
|
|
|
510
548
|
const index = startIndex + offset;
|
|
511
549
|
const expanded = entry.id === expandedEntryId;
|
|
512
550
|
const top = rowOffsets[index] ?? 0;
|
|
513
|
-
const height =
|
|
514
|
-
(rowOffsets[index + 1] ?? top + DEVTOOLS_ROW_HEIGHT) - top;
|
|
515
551
|
return (
|
|
516
|
-
<div
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
minHeight: height,
|
|
521
|
-
top,
|
|
522
|
-
}}
|
|
523
|
-
>
|
|
524
|
-
{renderRow(entry, expanded)}
|
|
552
|
+
<div key={entry.id} className="absolute inset-x-0" style={{ top }}>
|
|
553
|
+
<div ref={expanded ? setExpandedElement : undefined}>
|
|
554
|
+
{renderRow(entry, expanded)}
|
|
555
|
+
</div>
|
|
525
556
|
</div>
|
|
526
557
|
);
|
|
527
558
|
})}
|
|
@@ -647,11 +678,27 @@ function ViewIssueLink({
|
|
|
647
678
|
);
|
|
648
679
|
}
|
|
649
680
|
|
|
681
|
+
function SearchIssuesLink({ message }: { message: string }) {
|
|
682
|
+
const et = useErrorsT();
|
|
683
|
+
return (
|
|
684
|
+
<Link
|
|
685
|
+
to={issueSearchPath(message)}
|
|
686
|
+
title={et.searchIssuesTooltip}
|
|
687
|
+
onClick={(event) => event.stopPropagation()}
|
|
688
|
+
className="inline-flex shrink-0 items-center gap-1 rounded border px-1.5 py-0.5 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
689
|
+
>
|
|
690
|
+
<IconSearch className="h-3 w-3" />
|
|
691
|
+
{et.searchIssues}
|
|
692
|
+
</Link>
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
650
696
|
function ConsoleRow({
|
|
651
697
|
entry,
|
|
652
698
|
active,
|
|
653
699
|
selected,
|
|
654
700
|
issueMatch,
|
|
701
|
+
issueMatching,
|
|
655
702
|
onSelect,
|
|
656
703
|
onSeek,
|
|
657
704
|
}: {
|
|
@@ -659,6 +706,7 @@ function ConsoleRow({
|
|
|
659
706
|
active: boolean;
|
|
660
707
|
selected: boolean;
|
|
661
708
|
issueMatch: SessionIssueMatch | null;
|
|
709
|
+
issueMatching: boolean;
|
|
662
710
|
onSelect: () => void;
|
|
663
711
|
onSeek: (ms: number) => void;
|
|
664
712
|
}) {
|
|
@@ -723,6 +771,9 @@ function ConsoleRow({
|
|
|
723
771
|
/>
|
|
724
772
|
</button>
|
|
725
773
|
{issueMatch ? <ViewIssueLink issueId={issueMatch.issueId} /> : null}
|
|
774
|
+
{!issueMatch && !issueMatching && bucket === "error" ? (
|
|
775
|
+
<SearchIssuesLink message={entry.message} />
|
|
776
|
+
) : null}
|
|
726
777
|
<JumpToButton offsetMs={entry.offsetMs} onSeek={onSeek} />
|
|
727
778
|
</div>
|
|
728
779
|
{selected ? (
|
|
@@ -757,6 +808,10 @@ function ConsoleRow({
|
|
|
757
808
|
<div className="pt-1">
|
|
758
809
|
<ViewIssueLink issueId={issueMatch.issueId} />
|
|
759
810
|
</div>
|
|
811
|
+
) : !issueMatching && bucket === "error" ? (
|
|
812
|
+
<div className="pt-1">
|
|
813
|
+
<SearchIssuesLink message={entry.message} />
|
|
814
|
+
</div>
|
|
760
815
|
) : null}
|
|
761
816
|
</div>
|
|
762
817
|
) : null}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { CodeSurface } from "@agent-native/core/blocks";
|
|
2
2
|
import {
|
|
3
|
+
agentNativePath,
|
|
3
4
|
useActionQuery,
|
|
4
5
|
useBuilderConnectFlow,
|
|
5
6
|
useBuilderStatus,
|
|
6
7
|
useT,
|
|
7
8
|
} from "@agent-native/core/client";
|
|
8
9
|
import {
|
|
9
|
-
IconChevronDown,
|
|
10
10
|
IconCheck,
|
|
11
|
+
IconChevronDown,
|
|
11
12
|
IconCloud,
|
|
12
13
|
IconCode,
|
|
13
14
|
IconExternalLink,
|
|
@@ -17,14 +18,28 @@ import {
|
|
|
17
18
|
IconRefresh,
|
|
18
19
|
IconSearch,
|
|
19
20
|
IconServer,
|
|
21
|
+
IconSettings,
|
|
20
22
|
} from "@tabler/icons-react";
|
|
21
23
|
import { useQuery } from "@tanstack/react-query";
|
|
22
24
|
import { useMemo, useState } from "react";
|
|
23
25
|
import { Link, useNavigate, useSearchParams } from "react-router";
|
|
26
|
+
import { toast } from "sonner";
|
|
24
27
|
|
|
28
|
+
import { Badge } from "@/components/ui/badge";
|
|
25
29
|
import { Button } from "@/components/ui/button";
|
|
26
30
|
import { Card, CardContent } from "@/components/ui/card";
|
|
31
|
+
import {
|
|
32
|
+
Collapsible,
|
|
33
|
+
CollapsibleContent,
|
|
34
|
+
CollapsibleTrigger,
|
|
35
|
+
} from "@/components/ui/collapsible";
|
|
27
36
|
import { Input } from "@/components/ui/input";
|
|
37
|
+
import { Label } from "@/components/ui/label";
|
|
38
|
+
import {
|
|
39
|
+
Popover,
|
|
40
|
+
PopoverContent,
|
|
41
|
+
PopoverTrigger,
|
|
42
|
+
} from "@/components/ui/popover";
|
|
28
43
|
import {
|
|
29
44
|
Select,
|
|
30
45
|
SelectContent,
|
|
@@ -33,6 +48,11 @@ import {
|
|
|
33
48
|
SelectValue,
|
|
34
49
|
} from "@/components/ui/select";
|
|
35
50
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
51
|
+
import {
|
|
52
|
+
Tooltip,
|
|
53
|
+
TooltipContent,
|
|
54
|
+
TooltipTrigger,
|
|
55
|
+
} from "@/components/ui/tooltip";
|
|
36
56
|
import { useReplayStorageStatus } from "@/hooks/use-replay-storage-status";
|
|
37
57
|
import { cn } from "@/lib/utils";
|
|
38
58
|
|
|
@@ -41,6 +61,73 @@ type ReplayRange = "24h" | "7d" | "30d" | "90d" | "all";
|
|
|
41
61
|
const SESSION_REPLAY_DOCS_URL =
|
|
42
62
|
"https://www.agent-native.com/docs/tracking#session-replay";
|
|
43
63
|
|
|
64
|
+
const S3_STORAGE_FIELDS = [
|
|
65
|
+
{
|
|
66
|
+
key: "S3_ENDPOINT",
|
|
67
|
+
labelKey: "settings.s3EndpointLabel",
|
|
68
|
+
placeholder: "https://s3.us-east-1.amazonaws.com",
|
|
69
|
+
required: true,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "S3_BUCKET",
|
|
73
|
+
labelKey: "settings.s3BucketLabel",
|
|
74
|
+
placeholder: "my-replays-bucket",
|
|
75
|
+
required: true,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
key: "S3_ACCESS_KEY_ID",
|
|
79
|
+
labelKey: "settings.s3AccessKeyLabel",
|
|
80
|
+
placeholder: "AKIA...",
|
|
81
|
+
required: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
key: "S3_SECRET_ACCESS_KEY",
|
|
85
|
+
labelKey: "settings.s3SecretAccessKeyLabel",
|
|
86
|
+
placeholder: "••••••••",
|
|
87
|
+
required: true,
|
|
88
|
+
secret: true,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: "S3_REGION",
|
|
92
|
+
labelKey: "settings.s3RegionLabel",
|
|
93
|
+
placeholder: "us-east-1",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: "S3_PUBLIC_BASE_URL",
|
|
97
|
+
labelKey: "settings.s3PublicBaseUrlLabel",
|
|
98
|
+
placeholder: "https://cdn.example.com",
|
|
99
|
+
},
|
|
100
|
+
] as const;
|
|
101
|
+
|
|
102
|
+
async function saveS3StorageSettings(
|
|
103
|
+
values: Record<string, string>,
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
const vars = S3_STORAGE_FIELDS.map((field) => ({
|
|
106
|
+
key: field.key,
|
|
107
|
+
value: (values[field.key] ?? "").trim(),
|
|
108
|
+
})).filter((entry) => entry.value.length > 0);
|
|
109
|
+
|
|
110
|
+
for (const { key, value } of vars) {
|
|
111
|
+
const res = await fetch(agentNativePath("/_agent-native/secrets/adhoc"), {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: { "Content-Type": "application/json" },
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
name: key,
|
|
116
|
+
value,
|
|
117
|
+
scope: "workspace",
|
|
118
|
+
description: "Analytics S3-compatible replay storage", // i18n-ignore -- secret metadata description, not visible UI
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!res.ok) {
|
|
123
|
+
const body = (await res.json().catch(() => null)) as {
|
|
124
|
+
error?: string;
|
|
125
|
+
} | null;
|
|
126
|
+
throw new Error(body?.error ?? `Save failed (${res.status})`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
44
131
|
type SessionRecordingSummary = {
|
|
45
132
|
id: string;
|
|
46
133
|
clientRecordingId: string;
|
|
@@ -85,7 +172,6 @@ export default function SessionsPage() {
|
|
|
85
172
|
const t = useT();
|
|
86
173
|
const navigate = useNavigate();
|
|
87
174
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
88
|
-
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
89
175
|
const range = readRange(searchParams.get("range"));
|
|
90
176
|
const app = searchParams.get("app") ?? "";
|
|
91
177
|
const query = searchParams.get("q") ?? "";
|
|
@@ -119,12 +205,12 @@ export default function SessionsPage() {
|
|
|
119
205
|
<div className="analytics-sessions-page mx-auto flex w-full max-w-7xl flex-col gap-4 px-4 py-5">
|
|
120
206
|
<Card>
|
|
121
207
|
<CardContent className="p-3">
|
|
122
|
-
<div className="analytics-sessions-filter-
|
|
208
|
+
<div className="analytics-sessions-filter-bar flex flex-wrap items-center gap-2">
|
|
123
209
|
<div className="analytics-sessions-filter-label flex items-center gap-2 px-1 text-sm font-medium">
|
|
124
210
|
<IconFilter className="h-4 w-4 text-muted-foreground" />
|
|
125
|
-
{t("sessions.
|
|
211
|
+
{t("sessions.filters")}
|
|
126
212
|
</div>
|
|
127
|
-
<div className="relative">
|
|
213
|
+
<div className="analytics-sessions-filter-search relative min-w-0">
|
|
128
214
|
<IconSearch className="pointer-events-none absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
129
215
|
<Input
|
|
130
216
|
value={query}
|
|
@@ -133,37 +219,75 @@ export default function SessionsPage() {
|
|
|
133
219
|
className="h-9 ps-9"
|
|
134
220
|
/>
|
|
135
221
|
</div>
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
222
|
+
<Popover>
|
|
223
|
+
<Tooltip>
|
|
224
|
+
<TooltipTrigger asChild>
|
|
225
|
+
<PopoverTrigger asChild>
|
|
226
|
+
<Button
|
|
227
|
+
type="button"
|
|
228
|
+
variant="outline"
|
|
229
|
+
size="icon"
|
|
230
|
+
className="h-9 w-9 shrink-0"
|
|
231
|
+
aria-label={t("sessions.filters")}
|
|
232
|
+
>
|
|
233
|
+
<IconSettings className="h-4 w-4" />
|
|
234
|
+
</Button>
|
|
235
|
+
</PopoverTrigger>
|
|
236
|
+
</TooltipTrigger>
|
|
237
|
+
<TooltipContent>{t("sessions.filters")}</TooltipContent>
|
|
238
|
+
</Tooltip>
|
|
239
|
+
<PopoverContent align="end" className="w-72 p-3">
|
|
240
|
+
<div className="grid gap-3">
|
|
241
|
+
<div className="text-sm font-medium">
|
|
242
|
+
{t("sessions.filters")}
|
|
243
|
+
</div>
|
|
244
|
+
<div className="grid gap-1.5">
|
|
245
|
+
<div className="text-xs font-medium text-muted-foreground">
|
|
246
|
+
{t("sessions.range")}
|
|
247
|
+
</div>
|
|
248
|
+
<Select
|
|
249
|
+
value={range}
|
|
250
|
+
onValueChange={(value) => updateFilter("range", value)}
|
|
251
|
+
>
|
|
252
|
+
<SelectTrigger
|
|
253
|
+
className="h-9"
|
|
254
|
+
aria-label={t("sessions.range")}
|
|
255
|
+
>
|
|
256
|
+
<SelectValue />
|
|
257
|
+
</SelectTrigger>
|
|
258
|
+
<SelectContent>
|
|
259
|
+
{RANGE_OPTIONS.map((value) => (
|
|
260
|
+
<SelectItem key={value} value={value}>
|
|
261
|
+
{rangeLabel(value, t)}
|
|
262
|
+
</SelectItem>
|
|
263
|
+
))}
|
|
264
|
+
</SelectContent>
|
|
265
|
+
</Select>
|
|
266
|
+
</div>
|
|
267
|
+
<div className="grid gap-1.5 border-t pt-3">
|
|
268
|
+
<div className="text-xs font-medium text-muted-foreground">
|
|
269
|
+
{t("sessions.userFilters")}
|
|
270
|
+
</div>
|
|
271
|
+
<Input
|
|
272
|
+
value={app}
|
|
273
|
+
onChange={(event) =>
|
|
274
|
+
updateFilter("app", event.target.value)
|
|
275
|
+
}
|
|
276
|
+
placeholder={t("sessions.appPlaceholder")}
|
|
277
|
+
className="h-9"
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
<div className="grid gap-1.5">
|
|
281
|
+
<div className="text-xs font-medium text-muted-foreground">
|
|
282
|
+
{t("sessions.eventFilters")}
|
|
283
|
+
</div>
|
|
284
|
+
<div className="flex h-9 items-center rounded-md border bg-muted/20 px-3 text-sm text-muted-foreground">
|
|
285
|
+
{t("sessions.anyActivity")}
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
</PopoverContent>
|
|
290
|
+
</Popover>
|
|
167
291
|
<Button
|
|
168
292
|
type="button"
|
|
169
293
|
variant="ghost"
|
|
@@ -178,29 +302,6 @@ export default function SessionsPage() {
|
|
|
178
302
|
/>
|
|
179
303
|
</Button>
|
|
180
304
|
</div>
|
|
181
|
-
{filtersOpen ? (
|
|
182
|
-
<div className="analytics-sessions-filter-extra mt-3 grid gap-3 border-t pt-3">
|
|
183
|
-
<div className="rounded-md border bg-muted/20 p-3">
|
|
184
|
-
<div className="mb-2 text-[11px] font-semibold uppercase text-muted-foreground">
|
|
185
|
-
{t("sessions.userFilters")}
|
|
186
|
-
</div>
|
|
187
|
-
<Input
|
|
188
|
-
value={app}
|
|
189
|
-
onChange={(event) => updateFilter("app", event.target.value)}
|
|
190
|
-
placeholder={t("sessions.appPlaceholder")}
|
|
191
|
-
className="h-9"
|
|
192
|
-
/>
|
|
193
|
-
</div>
|
|
194
|
-
<div className="rounded-md border bg-muted/20 p-3">
|
|
195
|
-
<div className="mb-2 text-[11px] font-semibold uppercase text-muted-foreground">
|
|
196
|
-
{t("sessions.eventFilters")}
|
|
197
|
-
</div>
|
|
198
|
-
<div className="flex h-9 items-center rounded-md border bg-background px-3 text-sm text-muted-foreground">
|
|
199
|
-
{t("sessions.anyActivity")}
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
) : null}
|
|
204
305
|
</CardContent>
|
|
205
306
|
</Card>
|
|
206
307
|
|
|
@@ -363,7 +464,7 @@ function EmptySessionsState() {
|
|
|
363
464
|
);
|
|
364
465
|
}
|
|
365
466
|
|
|
366
|
-
function ReplayStorageHint() {
|
|
467
|
+
export function ReplayStorageHint() {
|
|
367
468
|
const t = useT();
|
|
368
469
|
const storageStatus = useReplayStorageStatus();
|
|
369
470
|
const builderStatus = useBuilderStatus();
|
|
@@ -386,59 +487,147 @@ function ReplayStorageHint() {
|
|
|
386
487
|
storageStatus.isLoading ||
|
|
387
488
|
builderStatus.loading ||
|
|
388
489
|
!builderConnect.hasFetchedStatus;
|
|
490
|
+
const [s3Expanded, setS3Expanded] = useState(false);
|
|
491
|
+
const [s3Values, setS3Values] = useState<Record<string, string>>({});
|
|
492
|
+
const [savingStorage, setSavingStorage] = useState(false);
|
|
493
|
+
|
|
494
|
+
async function handleSaveS3Storage() {
|
|
495
|
+
const missing = S3_STORAGE_FIELDS.filter(
|
|
496
|
+
(field) =>
|
|
497
|
+
"required" in field &&
|
|
498
|
+
field.required &&
|
|
499
|
+
!(s3Values[field.key] ?? "").trim(),
|
|
500
|
+
);
|
|
501
|
+
if (missing.length > 0) {
|
|
502
|
+
toast.error(t("settings.storageRequired"));
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
setSavingStorage(true);
|
|
507
|
+
try {
|
|
508
|
+
await saveS3StorageSettings(s3Values);
|
|
509
|
+
setS3Values((current) => ({
|
|
510
|
+
...current,
|
|
511
|
+
S3_SECRET_ACCESS_KEY: "",
|
|
512
|
+
}));
|
|
513
|
+
await storageStatus.refetch();
|
|
514
|
+
toast.success(t("settings.storageSaved"));
|
|
515
|
+
} catch (err) {
|
|
516
|
+
toast.error(
|
|
517
|
+
err instanceof Error ? err.message : t("settings.storageSaveFailed"),
|
|
518
|
+
);
|
|
519
|
+
} finally {
|
|
520
|
+
setSavingStorage(false);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
389
523
|
|
|
390
524
|
return (
|
|
391
|
-
<
|
|
392
|
-
<div className="
|
|
393
|
-
<div className="flex
|
|
394
|
-
<
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
525
|
+
<Collapsible open={s3Expanded} onOpenChange={setS3Expanded}>
|
|
526
|
+
<div className="mb-6 rounded-md border border-primary/30 bg-primary/5 p-4">
|
|
527
|
+
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
528
|
+
<div className="flex min-w-0 items-start gap-3">
|
|
529
|
+
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-background text-primary">
|
|
530
|
+
<IconCloud className="h-5 w-5" />
|
|
531
|
+
</div>
|
|
532
|
+
<div className="min-w-0">
|
|
533
|
+
<div className="text-sm font-medium">
|
|
534
|
+
{t("sessions.storageSetupTitle")}
|
|
535
|
+
</div>
|
|
536
|
+
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
537
|
+
{t("sessions.storageSetupDescription")}
|
|
538
|
+
</p>
|
|
539
|
+
</div>
|
|
540
|
+
</div>
|
|
541
|
+
<div className="flex shrink-0 flex-wrap items-center gap-3">
|
|
542
|
+
<Button
|
|
543
|
+
type="button"
|
|
544
|
+
size="sm"
|
|
545
|
+
className="shrink-0"
|
|
546
|
+
onClick={() =>
|
|
547
|
+
builderConnect.start({
|
|
548
|
+
trackingSource: "analytics_sessions_storage_hint",
|
|
549
|
+
trackingFlow: "replay_storage",
|
|
550
|
+
})
|
|
551
|
+
}
|
|
552
|
+
disabled={
|
|
553
|
+
builderConnect.connecting ||
|
|
554
|
+
builderStatusLoading ||
|
|
555
|
+
builderConnected
|
|
556
|
+
}
|
|
557
|
+
>
|
|
558
|
+
{builderConnect.connecting ? (
|
|
559
|
+
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
560
|
+
) : builderConnected ? (
|
|
561
|
+
<IconCheck className="h-4 w-4" />
|
|
562
|
+
) : (
|
|
563
|
+
<IconExternalLink className="h-4 w-4" />
|
|
564
|
+
)}
|
|
565
|
+
{builderConnected
|
|
566
|
+
? t("sessions.storageConnected")
|
|
567
|
+
: t("sessions.connectBuilder")}
|
|
568
|
+
</Button>
|
|
569
|
+
<CollapsibleTrigger asChild>
|
|
570
|
+
<Button type="button" variant="ghost" size="sm">
|
|
571
|
+
<IconServer className="h-3.5 w-3.5" />
|
|
572
|
+
{t("sessions.configureS3")}
|
|
573
|
+
<Badge variant="outline" className="text-[10px]">
|
|
574
|
+
{t("settings.secondary")}
|
|
575
|
+
</Badge>
|
|
576
|
+
<IconChevronDown
|
|
577
|
+
className={cn(
|
|
578
|
+
"h-4 w-4 transition-transform",
|
|
579
|
+
s3Expanded && "rotate-180",
|
|
580
|
+
)}
|
|
581
|
+
/>
|
|
582
|
+
</Button>
|
|
583
|
+
</CollapsibleTrigger>
|
|
399
584
|
</div>
|
|
400
|
-
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
401
|
-
{t("sessions.storageSetupDescription")}
|
|
402
|
-
</p>
|
|
403
585
|
</div>
|
|
586
|
+
<CollapsibleContent>
|
|
587
|
+
<div className="mt-4 border-t border-primary/20 pt-4">
|
|
588
|
+
<p className="mb-4 text-xs text-muted-foreground">
|
|
589
|
+
{t("settings.s3OwnBucketDescription")}
|
|
590
|
+
</p>
|
|
591
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
592
|
+
{S3_STORAGE_FIELDS.map((field) => (
|
|
593
|
+
<div key={field.key} className="space-y-1.5">
|
|
594
|
+
<Label htmlFor={`replay-${field.key}`}>
|
|
595
|
+
{t(field.labelKey)}
|
|
596
|
+
</Label>
|
|
597
|
+
<Input
|
|
598
|
+
id={`replay-${field.key}`}
|
|
599
|
+
type={
|
|
600
|
+
"secret" in field && field.secret ? "password" : "text"
|
|
601
|
+
}
|
|
602
|
+
value={s3Values[field.key] ?? ""}
|
|
603
|
+
onChange={(event) =>
|
|
604
|
+
setS3Values((current) => ({
|
|
605
|
+
...current,
|
|
606
|
+
[field.key]: event.target.value,
|
|
607
|
+
}))
|
|
608
|
+
}
|
|
609
|
+
placeholder={field.placeholder}
|
|
610
|
+
autoComplete="off"
|
|
611
|
+
disabled={savingStorage}
|
|
612
|
+
/>
|
|
613
|
+
</div>
|
|
614
|
+
))}
|
|
615
|
+
</div>
|
|
616
|
+
<div className="mt-4 flex justify-end">
|
|
617
|
+
<Button
|
|
618
|
+
onClick={handleSaveS3Storage}
|
|
619
|
+
disabled={savingStorage || storageStatus.isLoading}
|
|
620
|
+
>
|
|
621
|
+
{savingStorage ? (
|
|
622
|
+
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
623
|
+
) : null}
|
|
624
|
+
{t("settings.saveStorage")}
|
|
625
|
+
</Button>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
</CollapsibleContent>
|
|
404
629
|
</div>
|
|
405
|
-
|
|
406
|
-
<Button
|
|
407
|
-
type="button"
|
|
408
|
-
size="sm"
|
|
409
|
-
className="shrink-0"
|
|
410
|
-
onClick={() =>
|
|
411
|
-
builderConnect.start({
|
|
412
|
-
trackingSource: "analytics_sessions_storage_hint",
|
|
413
|
-
trackingFlow: "replay_storage",
|
|
414
|
-
})
|
|
415
|
-
}
|
|
416
|
-
disabled={
|
|
417
|
-
builderConnect.connecting ||
|
|
418
|
-
builderStatusLoading ||
|
|
419
|
-
builderConnected
|
|
420
|
-
}
|
|
421
|
-
>
|
|
422
|
-
{builderConnect.connecting ? (
|
|
423
|
-
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
424
|
-
) : builderConnected ? (
|
|
425
|
-
<IconCheck className="h-4 w-4" />
|
|
426
|
-
) : (
|
|
427
|
-
<IconExternalLink className="h-4 w-4" />
|
|
428
|
-
)}
|
|
429
|
-
{builderConnected
|
|
430
|
-
? t("sessions.storageConnected")
|
|
431
|
-
: t("sessions.connectBuilder")}
|
|
432
|
-
</Button>
|
|
433
|
-
<Link
|
|
434
|
-
to="/settings#replay-storage"
|
|
435
|
-
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground"
|
|
436
|
-
>
|
|
437
|
-
<IconServer className="h-3.5 w-3.5" />
|
|
438
|
-
{t("sessions.configureS3")}
|
|
439
|
-
</Link>
|
|
440
|
-
</div>
|
|
441
|
-
</div>
|
|
630
|
+
</Collapsible>
|
|
442
631
|
);
|
|
443
632
|
}
|
|
444
633
|
|