@agent-native/core 0.80.0 → 0.80.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 +2 -2
- package/corpus/core/CHANGELOG.md +8 -0
- package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +16 -1
- package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +14 -1
- package/corpus/core/docs/content/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/template-design.mdx +14 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/application-state/handlers.ts +1 -1
- package/corpus/core/src/brand-kit/index.ts +1 -1
- package/corpus/core/src/cli/skills.ts +18 -0
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
- package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
- package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
- package/corpus/core/src/extensions/path.ts +1 -4
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
- package/corpus/core/src/mcp/build-server.ts +1 -1
- package/corpus/core/src/org/handlers.ts +5 -5
- package/corpus/core/src/org/plugin.ts +2 -2
- package/corpus/core/src/scripts/call-agent.ts +1 -1
- package/corpus/core/src/scripts/db/safety.ts +1 -1
- package/corpus/core/src/server/auth.ts +3 -0
- package/corpus/core/src/server/builder-design-systems.ts +308 -0
- package/corpus/core/src/server/core-routes-plugin.ts +29 -3
- package/corpus/core/src/server/index.ts +8 -0
- package/corpus/core/src/styles/agent-native.css +14 -7
- package/corpus/core/src/usage/store.ts +2 -2
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
- package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +37 -0
- package/corpus/templates/analytics/app/i18n-data.ts +404 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +49 -26
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +137 -36
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
- package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +49 -26
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
- package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
- package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +29 -8
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
- package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
- package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
- package/corpus/templates/content/shared/document-export.ts +1 -1
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +32 -5
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
- package/corpus/templates/design/actions/update-file.ts +9 -2
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +33 -75
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +72 -9
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -39
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1029 -249
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +450 -101
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +978 -154
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -9
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +133 -66
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1234 -344
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +144 -94
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +301 -72
- package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +876 -286
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +30 -7
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +212 -155
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
- package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +487 -107
- package/corpus/templates/design/app/components/design/inspector/index.ts +27 -0
- package/corpus/templates/design/app/global.css +29 -3
- package/corpus/templates/design/app/i18n/zh-TW.ts +13 -0
- package/corpus/templates/design/app/i18n-data.ts +239 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +1023 -353
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
- package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
- package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
- package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
- package/corpus/templates/design/shared/code-layer.ts +209 -9
- package/corpus/templates/design/shared/html-content.ts +21 -0
- package/corpus/templates/design/shared/pen-path.ts +207 -0
- package/corpus/templates/mail/app/lib/utils.ts +2 -2
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
- package/corpus/templates/slides/AGENTS.md +3 -2
- package/corpus/templates/slides/actions/import-file.ts +26 -17
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
- package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
- package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
- package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
- package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
- package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
- package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
- package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
- package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
- package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
- package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
- package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
- package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
- package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
- package/dist/application-state/handlers.js +1 -1
- package/dist/application-state/handlers.js.map +1 -1
- package/dist/brand-kit/index.d.ts +1 -1
- package/dist/brand-kit/index.js +1 -1
- package/dist/brand-kit/index.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +18 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/extensions/portable-extension.d.ts +1 -1
- package/dist/client/extensions/portable-extension.d.ts.map +1 -1
- package/dist/client/extensions/portable-extension.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +1 -1
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/extensions/path.d.ts +1 -1
- package/dist/extensions/path.d.ts.map +1 -1
- package/dist/extensions/path.js.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +1 -1
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/org/handlers.js +5 -5
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/plugin.js +2 -2
- package/dist/org/plugin.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/scripts/call-agent.js +1 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/db/safety.js +1 -1
- package/dist/scripts/db/safety.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +3 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts +28 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -0
- package/dist/server/builder-design-systems.js +217 -0
- package/dist/server/builder-design-systems.js.map +1 -0
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +20 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +14 -7
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
- package/docs/content/locales/ar-SA/template-design.mdx +14 -1
- package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/docs/content/locales/de-DE/template-design.mdx +15 -1
- package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/docs/content/locales/es-ES/template-design.mdx +15 -1
- package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/docs/content/locales/fr-FR/template-design.mdx +15 -1
- package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/docs/content/locales/hi-IN/template-design.mdx +15 -1
- package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/docs/content/locales/ja-JP/template-design.mdx +16 -1
- package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/docs/content/locales/ko-KR/template-design.mdx +14 -1
- package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/docs/content/locales/pt-BR/template-design.mdx +14 -1
- package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-CN/template-design.mdx +14 -1
- package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-TW/template-design.mdx +14 -1
- package/docs/content/skills-guide.mdx +5 -0
- package/docs/content/template-design.mdx +14 -1
- package/package.json +1 -1
- package/corpus/templates/design/actions/import-figma-file.ts +0 -166
- package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import { CodeSurface } from "@agent-native/core/blocks";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useActionQuery,
|
|
4
|
+
useBuilderConnectFlow,
|
|
5
|
+
useBuilderStatus,
|
|
6
|
+
useT,
|
|
7
|
+
} from "@agent-native/core/client";
|
|
3
8
|
import {
|
|
4
9
|
IconChevronDown,
|
|
10
|
+
IconCheck,
|
|
11
|
+
IconCloud,
|
|
5
12
|
IconCode,
|
|
6
13
|
IconExternalLink,
|
|
7
14
|
IconFilter,
|
|
15
|
+
IconLoader2,
|
|
8
16
|
IconPlayerPlay,
|
|
9
17
|
IconRefresh,
|
|
10
18
|
IconSearch,
|
|
19
|
+
IconServer,
|
|
11
20
|
} from "@tabler/icons-react";
|
|
12
21
|
import { useMemo, useState } from "react";
|
|
13
|
-
import { useNavigate, useSearchParams } from "react-router";
|
|
22
|
+
import { Link, useNavigate, useSearchParams } from "react-router";
|
|
14
23
|
|
|
15
24
|
import { Badge } from "@/components/ui/badge";
|
|
16
25
|
import { Button } from "@/components/ui/button";
|
|
@@ -24,6 +33,7 @@ import {
|
|
|
24
33
|
SelectValue,
|
|
25
34
|
} from "@/components/ui/select";
|
|
26
35
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
36
|
+
import { useReplayStorageStatus } from "@/hooks/use-replay-storage-status";
|
|
27
37
|
import { cn } from "@/lib/utils";
|
|
28
38
|
|
|
29
39
|
type ReplayRange = "24h" | "7d" | "30d" | "90d" | "all";
|
|
@@ -294,46 +304,137 @@ export default function SessionsPage() {
|
|
|
294
304
|
|
|
295
305
|
function EmptySessionsState() {
|
|
296
306
|
const t = useT();
|
|
307
|
+
const storageStatus = useReplayStorageStatus();
|
|
308
|
+
const showStorageHint =
|
|
309
|
+
!storageStatus.isLoading && !storageStatus.data?.configured;
|
|
297
310
|
return (
|
|
298
|
-
<div className="
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
311
|
+
<div className="p-6 lg:p-8">
|
|
312
|
+
{showStorageHint ? <ReplayStorageHint /> : null}
|
|
313
|
+
<div className="grid min-h-[380px] gap-6 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
|
|
314
|
+
<div className="flex flex-col justify-center gap-3">
|
|
315
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-md border bg-muted/40">
|
|
316
|
+
<IconPlayerPlay className="h-5 w-5 text-primary" />
|
|
317
|
+
</div>
|
|
318
|
+
<div className="space-y-2">
|
|
319
|
+
<h2 className="text-lg font-semibold">
|
|
320
|
+
{t("sessions.noSessions")}
|
|
321
|
+
</h2>
|
|
322
|
+
<p className="max-w-xl text-sm text-muted-foreground">
|
|
323
|
+
{t("sessions.noSessionsDescription")}
|
|
324
|
+
</p>
|
|
325
|
+
</div>
|
|
302
326
|
</div>
|
|
303
|
-
<div className="
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
327
|
+
<div className="analytics-session-snippet overflow-hidden rounded-md border bg-muted/30">
|
|
328
|
+
<div className="flex items-center justify-between gap-3 border-b px-4 py-3">
|
|
329
|
+
<div className="flex min-w-0 items-center gap-2 text-sm font-medium">
|
|
330
|
+
<IconCode className="h-4 w-4 text-muted-foreground" />
|
|
331
|
+
<span className="truncate">
|
|
332
|
+
{t("sessions.installSnippetTitle")}
|
|
333
|
+
</span>
|
|
334
|
+
</div>
|
|
335
|
+
<Button
|
|
336
|
+
asChild
|
|
337
|
+
variant="ghost"
|
|
338
|
+
size="sm"
|
|
339
|
+
className="h-7 px-2 text-xs"
|
|
340
|
+
>
|
|
341
|
+
<a
|
|
342
|
+
href={SESSION_REPLAY_DOCS_URL}
|
|
343
|
+
target="_blank"
|
|
344
|
+
rel="noreferrer"
|
|
345
|
+
>
|
|
346
|
+
{t("common.docs")}
|
|
347
|
+
<IconExternalLink className="h-3.5 w-3.5" />
|
|
348
|
+
</a>
|
|
349
|
+
</Button>
|
|
350
|
+
</div>
|
|
351
|
+
<CodeSurface
|
|
352
|
+
code={SESSION_REPLAY_SNIPPET}
|
|
353
|
+
language="typescript"
|
|
354
|
+
maxLines={null}
|
|
355
|
+
showLanguageLabel={false}
|
|
356
|
+
className="mt-0"
|
|
357
|
+
/>
|
|
308
358
|
</div>
|
|
309
359
|
</div>
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function ReplayStorageHint() {
|
|
365
|
+
const t = useT();
|
|
366
|
+
const storageStatus = useReplayStorageStatus();
|
|
367
|
+
const builderStatus = useBuilderStatus();
|
|
368
|
+
const builderConnect = useBuilderConnectFlow({
|
|
369
|
+
popupUrl:
|
|
370
|
+
builderStatus.status?.cliAuthUrl ?? builderStatus.status?.connectUrl,
|
|
371
|
+
trackingSource: "analytics_sessions_storage_hint",
|
|
372
|
+
trackingFlow: "replay_storage",
|
|
373
|
+
onConnected: async () => {
|
|
374
|
+
await Promise.all([storageStatus.refetch(), builderStatus.refetch()]);
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const builderConnected = Boolean(
|
|
379
|
+
builderConnect.configured ||
|
|
380
|
+
builderStatus.status?.configured ||
|
|
381
|
+
storageStatus.data?.builderConfigured,
|
|
382
|
+
);
|
|
383
|
+
const builderStatusLoading =
|
|
384
|
+
storageStatus.isLoading ||
|
|
385
|
+
builderStatus.loading ||
|
|
386
|
+
!builderConnect.hasFetchedStatus;
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<div className="mb-6 flex flex-col gap-4 rounded-md border border-primary/30 bg-primary/5 p-4 sm:flex-row sm:items-center sm:justify-between">
|
|
390
|
+
<div className="flex min-w-0 items-start gap-3">
|
|
391
|
+
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-background text-primary">
|
|
392
|
+
<IconCloud className="h-5 w-5" />
|
|
393
|
+
</div>
|
|
394
|
+
<div className="min-w-0">
|
|
395
|
+
<div className="text-sm font-medium">
|
|
396
|
+
{t("sessions.storageSetupTitle")}
|
|
317
397
|
</div>
|
|
318
|
-
<
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
size="sm"
|
|
322
|
-
className="h-7 px-2 text-xs"
|
|
323
|
-
>
|
|
324
|
-
<a href={SESSION_REPLAY_DOCS_URL} target="_blank" rel="noreferrer">
|
|
325
|
-
{t("common.docs")}
|
|
326
|
-
<IconExternalLink className="h-3.5 w-3.5" />
|
|
327
|
-
</a>
|
|
328
|
-
</Button>
|
|
398
|
+
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
399
|
+
{t("sessions.storageSetupDescription")}
|
|
400
|
+
</p>
|
|
329
401
|
</div>
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
className="
|
|
336
|
-
|
|
402
|
+
</div>
|
|
403
|
+
<div className="flex shrink-0 flex-wrap items-center gap-3">
|
|
404
|
+
<Button
|
|
405
|
+
type="button"
|
|
406
|
+
size="sm"
|
|
407
|
+
className="shrink-0"
|
|
408
|
+
onClick={() =>
|
|
409
|
+
builderConnect.start({
|
|
410
|
+
trackingSource: "analytics_sessions_storage_hint",
|
|
411
|
+
trackingFlow: "replay_storage",
|
|
412
|
+
})
|
|
413
|
+
}
|
|
414
|
+
disabled={
|
|
415
|
+
builderConnect.connecting ||
|
|
416
|
+
builderStatusLoading ||
|
|
417
|
+
builderConnected
|
|
418
|
+
}
|
|
419
|
+
>
|
|
420
|
+
{builderConnect.connecting ? (
|
|
421
|
+
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
422
|
+
) : builderConnected ? (
|
|
423
|
+
<IconCheck className="h-4 w-4" />
|
|
424
|
+
) : (
|
|
425
|
+
<IconExternalLink className="h-4 w-4" />
|
|
426
|
+
)}
|
|
427
|
+
{builderConnected
|
|
428
|
+
? t("sessions.storageConnected")
|
|
429
|
+
: t("sessions.connectBuilder")}
|
|
430
|
+
</Button>
|
|
431
|
+
<Link
|
|
432
|
+
to="/settings#replay-storage"
|
|
433
|
+
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground"
|
|
434
|
+
>
|
|
435
|
+
<IconServer className="h-3.5 w-3.5" />
|
|
436
|
+
{t("sessions.configureS3")}
|
|
437
|
+
</Link>
|
|
337
438
|
</div>
|
|
338
439
|
</div>
|
|
339
440
|
);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One-time maintenance for session replay rows created before the capture fix.
|
|
5
|
+
*
|
|
6
|
+
* Two jobs:
|
|
7
|
+
* 1. Delete empty "shell" recordings — rows that were created on ingest but
|
|
8
|
+
* never stored any replay data (chunk_count = 0, event_count = 0, and no
|
|
9
|
+
* session_replay_chunks rows). These exist because, before the ingest
|
|
10
|
+
* credential-scope fix, blob storage failed in production (no resolvable
|
|
11
|
+
* upload provider) and left unplayable husks behind. They are always
|
|
12
|
+
* filtered out of /sessions, so this is pure DB hygiene.
|
|
13
|
+
* 2. Backfill visibility "private" -> "org" for org-scoped recordings, so
|
|
14
|
+
* existing recordings follow the same org-visible model new ingests now use
|
|
15
|
+
* (teammates in the org can see them, not only the key owner).
|
|
16
|
+
*
|
|
17
|
+
* SAFE BY DEFAULT: dry-run unless you pass --apply. This connects to whatever
|
|
18
|
+
* DATABASE_URL the app is configured with, so double-check the target before
|
|
19
|
+
* using --apply against production.
|
|
20
|
+
*
|
|
21
|
+
* pnpm --filter analytics exec tsx scripts/cleanup-empty-session-recordings.ts # dry run
|
|
22
|
+
* pnpm --filter analytics exec tsx scripts/cleanup-empty-session-recordings.ts --apply # mutate
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import { pathToFileURL } from "node:url";
|
|
27
|
+
|
|
28
|
+
import { and, eq, isNotNull, sql } from "drizzle-orm";
|
|
29
|
+
|
|
30
|
+
import { getDb, schema } from "../server/db/index";
|
|
31
|
+
import migrations from "../server/plugins/db";
|
|
32
|
+
|
|
33
|
+
const apply = process.argv.includes("--apply");
|
|
34
|
+
|
|
35
|
+
const emptyShellCondition = and(
|
|
36
|
+
eq(schema.sessionRecordings.chunkCount, 0),
|
|
37
|
+
eq(schema.sessionRecordings.eventCount, 0),
|
|
38
|
+
sql`not exists (
|
|
39
|
+
select 1 from ${schema.sessionReplayChunks}
|
|
40
|
+
where ${schema.sessionReplayChunks.recordingId} = ${schema.sessionRecordings.id}
|
|
41
|
+
)`,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const orgPrivateCondition = and(
|
|
45
|
+
isNotNull(schema.sessionRecordings.orgId),
|
|
46
|
+
eq(schema.sessionRecordings.visibility, "private"),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
async function countWhere(condition: unknown): Promise<number> {
|
|
50
|
+
const db = getDb() as any;
|
|
51
|
+
const rows = await db
|
|
52
|
+
.select({ n: sql<number>`count(*)::int` })
|
|
53
|
+
.from(schema.sessionRecordings)
|
|
54
|
+
.where(condition);
|
|
55
|
+
return Number(rows[0]?.n ?? 0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function main() {
|
|
59
|
+
await migrations({});
|
|
60
|
+
const db = getDb() as any;
|
|
61
|
+
|
|
62
|
+
const emptyShells = await countWhere(emptyShellCondition);
|
|
63
|
+
const orgPrivate = await countWhere(orgPrivateCondition);
|
|
64
|
+
|
|
65
|
+
console.log(`[cleanup] mode: ${apply ? "APPLY (mutating)" : "dry-run"}`);
|
|
66
|
+
console.log(`[cleanup] empty shell recordings to delete: ${emptyShells}`);
|
|
67
|
+
console.log(
|
|
68
|
+
`[cleanup] org-scoped recordings to make org-visible: ${orgPrivate}`,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (!apply) {
|
|
72
|
+
console.log("[cleanup] dry-run only — re-run with --apply to mutate.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Backfill visibility first so we don't widen rows we're about to delete.
|
|
77
|
+
await db
|
|
78
|
+
.update(schema.sessionRecordings)
|
|
79
|
+
.set({ visibility: "org" })
|
|
80
|
+
.where(orgPrivateCondition);
|
|
81
|
+
console.log(
|
|
82
|
+
`[cleanup] backfilled ${orgPrivate} recordings to visibility=org`,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
await db.delete(schema.sessionRecordings).where(emptyShellCondition);
|
|
86
|
+
console.log(`[cleanup] deleted ${emptyShells} empty shell recordings`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isDirectRun(): boolean {
|
|
90
|
+
const entrypoint = process.argv[1];
|
|
91
|
+
return Boolean(
|
|
92
|
+
entrypoint &&
|
|
93
|
+
import.meta.url === pathToFileURL(path.resolve(entrypoint)).href,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (isDirectRun()) {
|
|
98
|
+
main().catch((error) => {
|
|
99
|
+
console.error(error);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -39,17 +39,17 @@ const DATE_FILTER_TYPES: ReadonlySet<FilterType> = new Set([
|
|
|
39
39
|
const DEFAULT_SERVERLESS_CHROMIUM_PACK_URL =
|
|
40
40
|
"https://github.com/Sparticuz/chromium/releases/download/v149.0.0/chromium-v149.0.0-pack.x64.tar";
|
|
41
41
|
const DASHBOARD_REPORT_SCREENSHOT_PARAM = "reportScreenshot";
|
|
42
|
-
const DASHBOARD_REPORT_PANEL_LIMIT_PARAM = "reportPanelLimit";
|
|
43
42
|
const DASHBOARD_REPORT_SETTINGS_PARAM = "reportSettings";
|
|
44
43
|
const DASHBOARD_REPORT_CID = "dashboard-report-snapshot";
|
|
45
|
-
const COMPACT_REPORT_PANEL_LIMIT = 12;
|
|
46
44
|
const LOCAL_SCREENSHOT_TIMEOUT_MS = 90_000;
|
|
47
45
|
const SERVERLESS_SCREENSHOT_TIMEOUT_MS = 25_000;
|
|
48
46
|
const SERVERLESS_SECOND_READY_TIMEOUT_MS = 10_000;
|
|
47
|
+
const MAX_SCREENSHOT_VIEWPORT_HEIGHT = 30_000;
|
|
48
|
+
const SCREENSHOT_VIEWPORT_PADDING = 64;
|
|
49
49
|
|
|
50
50
|
type DashboardScreenshotAttempt = {
|
|
51
|
-
label: "full" | "
|
|
52
|
-
|
|
51
|
+
label: "full" | "full-lightweight";
|
|
52
|
+
viewport: { width: number; height: number };
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
function daysAgo(n: number): string {
|
|
@@ -119,7 +119,6 @@ function buildDashboardPath(
|
|
|
119
119
|
options?: {
|
|
120
120
|
reportScreenshot?: boolean;
|
|
121
121
|
reportSettings?: boolean;
|
|
122
|
-
panelLimit?: number;
|
|
123
122
|
},
|
|
124
123
|
): string {
|
|
125
124
|
const url = new URL(
|
|
@@ -135,12 +134,6 @@ function buildDashboardPath(
|
|
|
135
134
|
if (options?.reportSettings) {
|
|
136
135
|
url.searchParams.set(DASHBOARD_REPORT_SETTINGS_PARAM, "1");
|
|
137
136
|
}
|
|
138
|
-
if (options?.panelLimit && options.panelLimit > 0) {
|
|
139
|
-
url.searchParams.set(
|
|
140
|
-
DASHBOARD_REPORT_PANEL_LIMIT_PARAM,
|
|
141
|
-
String(Math.floor(options.panelLimit)),
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
137
|
return `${url.pathname}${url.search}`;
|
|
145
138
|
}
|
|
146
139
|
|
|
@@ -150,7 +143,6 @@ function buildDashboardUrl(
|
|
|
150
143
|
options?: {
|
|
151
144
|
reportScreenshot?: boolean;
|
|
152
145
|
reportSettings?: boolean;
|
|
153
|
-
panelLimit?: number;
|
|
154
146
|
},
|
|
155
147
|
): string {
|
|
156
148
|
const path = buildDashboardPath(dashboardId, filters, options);
|
|
@@ -319,6 +311,44 @@ async function scrollDashboardForLazyRendering(page: any): Promise<void> {
|
|
|
319
311
|
})()`);
|
|
320
312
|
}
|
|
321
313
|
|
|
314
|
+
async function fitViewportToDashboardCapture(
|
|
315
|
+
page: any,
|
|
316
|
+
capture: any,
|
|
317
|
+
minViewport: { width: number; height: number },
|
|
318
|
+
): Promise<void> {
|
|
319
|
+
const box = await capture.boundingBox();
|
|
320
|
+
if (!box) return;
|
|
321
|
+
|
|
322
|
+
const size = {
|
|
323
|
+
width: Math.max(
|
|
324
|
+
minViewport.width,
|
|
325
|
+
Math.min(1800, Math.ceil(box.width + SCREENSHOT_VIEWPORT_PADDING)),
|
|
326
|
+
),
|
|
327
|
+
height: Math.max(
|
|
328
|
+
minViewport.height,
|
|
329
|
+
Math.min(
|
|
330
|
+
MAX_SCREENSHOT_VIEWPORT_HEIGHT,
|
|
331
|
+
Math.ceil(box.height + SCREENSHOT_VIEWPORT_PADDING),
|
|
332
|
+
),
|
|
333
|
+
),
|
|
334
|
+
};
|
|
335
|
+
await page.setViewportSize(size);
|
|
336
|
+
await page.waitForTimeout(250);
|
|
337
|
+
|
|
338
|
+
const resizedBox = await capture.boundingBox();
|
|
339
|
+
if (!resizedBox) return;
|
|
340
|
+
const resizedHeight = Math.ceil(
|
|
341
|
+
resizedBox.height + SCREENSHOT_VIEWPORT_PADDING,
|
|
342
|
+
);
|
|
343
|
+
if (resizedHeight > size.height) {
|
|
344
|
+
await page.setViewportSize({
|
|
345
|
+
width: size.width,
|
|
346
|
+
height: Math.min(MAX_SCREENSHOT_VIEWPORT_HEIGHT, resizedHeight),
|
|
347
|
+
});
|
|
348
|
+
await page.waitForTimeout(250);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
322
352
|
async function captureDashboardPng(
|
|
323
353
|
sub: DashboardReportSubscription,
|
|
324
354
|
snapshot: ReportSnapshot,
|
|
@@ -329,7 +359,6 @@ async function captureDashboardPng(
|
|
|
329
359
|
snapshot.filters,
|
|
330
360
|
{
|
|
331
361
|
reportScreenshot: true,
|
|
332
|
-
panelLimit: attempt.panelLimit,
|
|
333
362
|
},
|
|
334
363
|
);
|
|
335
364
|
const token = signEmbedSessionToken({
|
|
@@ -347,7 +376,7 @@ async function captureDashboardPng(
|
|
|
347
376
|
try {
|
|
348
377
|
const timeout = screenshotTimeoutMs();
|
|
349
378
|
const page = await browser.newPage({
|
|
350
|
-
viewport:
|
|
379
|
+
viewport: attempt.viewport,
|
|
351
380
|
deviceScaleFactor: 1,
|
|
352
381
|
});
|
|
353
382
|
page.setDefaultTimeout(timeout);
|
|
@@ -371,13 +400,7 @@ async function captureDashboardPng(
|
|
|
371
400
|
: timeout,
|
|
372
401
|
);
|
|
373
402
|
|
|
374
|
-
|
|
375
|
-
if (box) {
|
|
376
|
-
await page.setViewportSize({
|
|
377
|
-
width: Math.max(1200, Math.min(1800, Math.ceil(box.width + 64))),
|
|
378
|
-
height: Math.max(1000, Math.min(7000, Math.ceil(box.height + 64))),
|
|
379
|
-
});
|
|
380
|
-
}
|
|
403
|
+
await fitViewportToDashboardCapture(page, capture, attempt.viewport);
|
|
381
404
|
await capture.scrollIntoViewIfNeeded();
|
|
382
405
|
const image = await capture.screenshot({
|
|
383
406
|
type: "png",
|
|
@@ -401,12 +424,12 @@ async function captureDashboardPngWithFallback(
|
|
|
401
424
|
snapshot: ReportSnapshot,
|
|
402
425
|
): Promise<{
|
|
403
426
|
png: Buffer | null;
|
|
404
|
-
mode: "full" | "
|
|
427
|
+
mode: "full" | "full-lightweight" | "none";
|
|
405
428
|
error?: string;
|
|
406
429
|
}> {
|
|
407
430
|
const attempts: DashboardScreenshotAttempt[] = [
|
|
408
|
-
{ label: "full" },
|
|
409
|
-
{ label: "
|
|
431
|
+
{ label: "full", viewport: { width: 1440, height: 1800 } },
|
|
432
|
+
{ label: "full-lightweight", viewport: { width: 1200, height: 1400 } },
|
|
410
433
|
];
|
|
411
434
|
let lastError: string | undefined;
|
|
412
435
|
|
|
@@ -446,7 +469,7 @@ function renderReportEmailHtml(
|
|
|
446
469
|
const date = escapeHtml(reportDate(snapshot));
|
|
447
470
|
const screenshotBlock = options.screenshotAttached
|
|
448
471
|
? `<a href="${dashboardUrl}" style="display:block;text-decoration:none;">
|
|
449
|
-
<img src="cid:${DASHBOARD_REPORT_CID}" alt="${title}" width="100%" style="display:block;width:100%;max-width:1280px;height:auto;border:
|
|
472
|
+
<img src="cid:${DASHBOARD_REPORT_CID}" alt="${title}" width="100%" style="display:block;width:100%;max-width:1280px;height:auto;border:0;outline:0;border-radius:0;" />
|
|
450
473
|
</a>`
|
|
451
474
|
: `<div style="margin:18px 0;padding:14px 16px;border:1px solid #e5e7eb;border-radius:8px;background:#f9fafb;color:#374151;font-size:14px;line-height:1.5;">
|
|
452
475
|
The dashboard image was unavailable for this run. Open the live dashboard to view the latest report.
|
|
@@ -502,7 +525,7 @@ export async function sendDashboardReportSubscription(
|
|
|
502
525
|
dashboardUrl: string;
|
|
503
526
|
recipientCount: number;
|
|
504
527
|
screenshotAttached: boolean;
|
|
505
|
-
screenshotMode: "full" | "
|
|
528
|
+
screenshotMode: "full" | "full-lightweight" | "none";
|
|
506
529
|
screenshotError?: string;
|
|
507
530
|
}> {
|
|
508
531
|
const snapshot = await collectReportSnapshot(sub);
|