@agent-native/core 0.84.66 → 0.85.0
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 +20 -0
- package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/de-DE/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/es-ES/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +55 -0
- package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +55 -0
- package/corpus/core/docs/content/tracking.mdx +2 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +199 -38
- package/corpus/core/src/agent/run-manager.ts +79 -0
- package/corpus/core/src/agent/run-store.ts +117 -5
- package/corpus/core/src/agent/types.ts +13 -0
- package/corpus/core/src/client/RunStuckBanner.tsx +14 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +475 -0
- package/corpus/core/src/client/analytics.ts +2 -0
- package/corpus/core/src/client/error-format.ts +4 -0
- package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +1 -1
- package/corpus/core/src/client/session-replay.ts +774 -2
- package/corpus/core/src/client/sse-event-processor.ts +65 -9
- package/corpus/core/src/client/use-run-stuck-detection.ts +31 -7
- package/corpus/core/src/db/runtime-diagnostics.ts +331 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +115 -2
- package/corpus/core/src/server/analytics.ts +1 -34
- package/corpus/core/src/server/core-routes-plugin.ts +91 -4
- package/corpus/core/src/server/http-response-telemetry.ts +189 -0
- package/corpus/core/src/server/self-dispatch.ts +29 -0
- package/corpus/core/src/server/ssr-handler.ts +18 -377
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +53 -1
- package/corpus/templates/analytics/AGENTS.md +13 -0
- package/corpus/templates/analytics/actions/view-screen.ts +25 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +20 -0
- package/corpus/templates/analytics/app/global.css +55 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +100 -1
- package/corpus/templates/analytics/app/i18n-data.ts +1226 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +3 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +182 -144
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +11 -18
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +11 -18
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +49 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +557 -0
- package/corpus/templates/analytics/app/pages/sessions/session-replay-devtools.ts +274 -0
- package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +974 -0
- package/corpus/templates/analytics/changelog/2026-07-02-alert-rules-can-now-be-viewed-and-managed-from-settings.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-02-recent-ask-chats-show-a-loading-placeholder-while-your-chat-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-02-session-replays-now-capture-console-logs-and-network-request.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-02-sharing-status-labels-use-neutral-icons-instead-of-colored-d.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-02-the-session-replay-viewer-has-a-dev-tools-panel-with-console.md +6 -0
- package/corpus/templates/analytics/server/db/schema.ts +3 -0
- package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +4 -0
- package/corpus/templates/analytics/server/lib/analytics-alerts.ts +142 -1
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +369 -8
- package/corpus/templates/analytics/server/lib/session-replay.ts +76 -2
- package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +101 -0
- package/corpus/templates/analytics/shared/session-replay-diagnostics.ts +78 -0
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +0 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +24 -12
- package/corpus/templates/design/changelog/2026-07-02-the-temporary-code-tab-is-hidden-from-the-design-editor-side.md +6 -0
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +0 -1
- package/corpus/templates/plan/actions/create-visual-recap.ts +1 -1
- package/corpus/templates/plan/actions/update-local-plan-folder.ts +3 -1
- package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
- package/corpus/templates/plan/changelog/2026-07-02-trying-to-edit-a-recap-or-plan-you-can-only-view-now-explain.md +6 -0
- package/corpus/templates/plan/server/plans.ts +26 -9
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +155 -34
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +29 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +73 -0
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +41 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +100 -4
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/types.d.ts +13 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +13 -0
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +410 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +2 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/session-replay.d.ts +36 -0
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +579 -1
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +20 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +42 -11
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts +14 -4
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
- package/dist/client/use-run-stuck-detection.js +18 -4
- package/dist/client/use-run-stuck-detection.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/runtime-diagnostics.d.ts +52 -0
- package/dist/db/runtime-diagnostics.d.ts.map +1 -0
- package/dist/db/runtime-diagnostics.js +250 -0
- package/dist/db/runtime-diagnostics.js.map +1 -0
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +101 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/analytics.d.ts +1 -19
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +1 -31
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +17 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +65 -5
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/http-response-telemetry.d.ts +4 -0
- package/dist/server/http-response-telemetry.d.ts.map +1 -0
- package/dist/server/http-response-telemetry.js +174 -0
- package/dist/server/http-response-telemetry.js.map +1 -0
- package/dist/server/self-dispatch.d.ts +17 -0
- package/dist/server/self-dispatch.d.ts.map +1 -1
- package/dist/server/self-dispatch.js +11 -0
- package/dist/server/self-dispatch.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +18 -271
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/docs/content/locales/ar-SA/tracking.mdx +55 -0
- package/docs/content/locales/de-DE/tracking.mdx +55 -0
- package/docs/content/locales/es-ES/tracking.mdx +55 -0
- package/docs/content/locales/fr-FR/tracking.mdx +55 -0
- package/docs/content/locales/hi-IN/tracking.mdx +55 -0
- package/docs/content/locales/ja-JP/tracking.mdx +55 -0
- package/docs/content/locales/ko-KR/tracking.mdx +55 -0
- package/docs/content/locales/pt-BR/tracking.mdx +55 -0
- package/docs/content/locales/zh-CN/tracking.mdx +55 -0
- package/docs/content/locales/zh-TW/tracking.mdx +55 -0
- package/docs/content/tracking.mdx +2 -0
- package/package.json +1 -1
|
@@ -43,6 +43,7 @@ import { useReplayStorageStatus } from "@/hooks/use-replay-storage-status";
|
|
|
43
43
|
import { cn } from "@/lib/utils";
|
|
44
44
|
|
|
45
45
|
import changelog from "../../CHANGELOG.md?raw";
|
|
46
|
+
import { AlertRulesSettingsCard } from "./settings/AlertRulesSettingsCard";
|
|
46
47
|
|
|
47
48
|
const S3_STORAGE_FIELDS = [
|
|
48
49
|
{
|
|
@@ -418,6 +419,8 @@ export default function Settings() {
|
|
|
418
419
|
</CardContent>
|
|
419
420
|
</Card>
|
|
420
421
|
|
|
422
|
+
<AlertRulesSettingsCard />
|
|
423
|
+
|
|
421
424
|
<Card className="bg-card border-border/50">
|
|
422
425
|
<CardHeader>
|
|
423
426
|
<CardTitle className="text-base">
|
|
@@ -5,9 +5,8 @@ import {
|
|
|
5
5
|
useT,
|
|
6
6
|
} from "@agent-native/core/client";
|
|
7
7
|
import {
|
|
8
|
+
IconChevronLeft,
|
|
8
9
|
IconFilter,
|
|
9
|
-
IconMail,
|
|
10
|
-
IconPencil,
|
|
11
10
|
IconPlus,
|
|
12
11
|
IconSend,
|
|
13
12
|
IconTrash,
|
|
@@ -50,6 +49,8 @@ type DashboardReportSubscription = {
|
|
|
50
49
|
lastError: string | null;
|
|
51
50
|
};
|
|
52
51
|
|
|
52
|
+
type DialogView = "list" | "form";
|
|
53
|
+
|
|
53
54
|
interface EmailReportDialogProps {
|
|
54
55
|
open: boolean;
|
|
55
56
|
onOpenChange: (open: boolean) => void;
|
|
@@ -98,6 +99,7 @@ export function EmailReportDialog({
|
|
|
98
99
|
const defaultEmail = session?.email ?? "";
|
|
99
100
|
const defaultName = `${dashboardName} daily email`;
|
|
100
101
|
|
|
102
|
+
const [view, setView] = useState<DialogView>("form");
|
|
101
103
|
const [selectedId, setSelectedId] = useState<string | undefined>();
|
|
102
104
|
const [name, setName] = useState(defaultName);
|
|
103
105
|
const [recipientsText, setRecipientsText] = useState(defaultEmail);
|
|
@@ -155,6 +157,7 @@ export function EmailReportDialog({
|
|
|
155
157
|
setTimeOfDay(sub.timeOfDay);
|
|
156
158
|
setTimezone(sub.timezone);
|
|
157
159
|
setEnabled(sub.enabled);
|
|
160
|
+
setView("form");
|
|
158
161
|
};
|
|
159
162
|
|
|
160
163
|
const resetForm = () => {
|
|
@@ -167,6 +170,16 @@ export function EmailReportDialog({
|
|
|
167
170
|
setEnabled(true);
|
|
168
171
|
};
|
|
169
172
|
|
|
173
|
+
const startNewSubscription = () => {
|
|
174
|
+
resetForm();
|
|
175
|
+
setView("form");
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const returnToList = () => {
|
|
179
|
+
resetForm();
|
|
180
|
+
setView("list");
|
|
181
|
+
};
|
|
182
|
+
|
|
170
183
|
useEffect(() => {
|
|
171
184
|
if (!open) {
|
|
172
185
|
initialized.current = false;
|
|
@@ -174,9 +187,11 @@ export function EmailReportDialog({
|
|
|
174
187
|
}
|
|
175
188
|
if (initialized.current || isLoading) return;
|
|
176
189
|
if (subscriptions[0]) {
|
|
177
|
-
|
|
190
|
+
resetForm();
|
|
191
|
+
setView("list");
|
|
178
192
|
} else {
|
|
179
193
|
resetForm();
|
|
194
|
+
setView("form");
|
|
180
195
|
}
|
|
181
196
|
initialized.current = true;
|
|
182
197
|
}, [open, isLoading, subscriptions, defaultEmail, defaultName]);
|
|
@@ -213,8 +228,12 @@ export function EmailReportDialog({
|
|
|
213
228
|
const handleDelete = async () => {
|
|
214
229
|
if (!selectedId) return;
|
|
215
230
|
try {
|
|
231
|
+
const hasRemainingSubscriptions = subscriptions.some(
|
|
232
|
+
(sub) => sub.id !== selectedId,
|
|
233
|
+
);
|
|
216
234
|
await deleteSubscription.mutateAsync({ id: selectedId });
|
|
217
235
|
resetForm();
|
|
236
|
+
setView(hasRemainingSubscriptions ? "list" : "form");
|
|
218
237
|
await refetch();
|
|
219
238
|
toast.success(t("sqlDashboard.reportSubscriptionDeleted"));
|
|
220
239
|
} catch (err: any) {
|
|
@@ -241,6 +260,9 @@ export function EmailReportDialog({
|
|
|
241
260
|
}
|
|
242
261
|
};
|
|
243
262
|
|
|
263
|
+
const isListView = subscriptions.length > 0 && view === "list";
|
|
264
|
+
const isFormView = !isListView;
|
|
265
|
+
|
|
244
266
|
return (
|
|
245
267
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
246
268
|
<DialogContent className="sm:max-w-[560px] max-h-[90vh] overflow-y-auto">
|
|
@@ -253,30 +275,26 @@ export function EmailReportDialog({
|
|
|
253
275
|
</DialogDescription>
|
|
254
276
|
</DialogHeader>
|
|
255
277
|
|
|
256
|
-
<div className="space-y-
|
|
257
|
-
|
|
258
|
-
<div className="
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
<
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
278
|
+
<div className="space-y-5">
|
|
279
|
+
{isListView ? (
|
|
280
|
+
<div className="space-y-3">
|
|
281
|
+
<div className="flex items-center justify-between gap-3">
|
|
282
|
+
<Label>{t("sqlDashboard.existingReportSubscriptions")}</Label>
|
|
283
|
+
<Button
|
|
284
|
+
variant="outline"
|
|
285
|
+
size="sm"
|
|
286
|
+
onClick={startNewSubscription}
|
|
287
|
+
>
|
|
288
|
+
<IconPlus className="mr-1.5 h-3.5 w-3.5" />
|
|
289
|
+
{t("sqlDashboard.newSubscription")}
|
|
290
|
+
</Button>
|
|
268
291
|
</div>
|
|
269
|
-
) : (
|
|
270
292
|
<div className="space-y-2">
|
|
271
293
|
{subscriptions.map((sub) => (
|
|
272
294
|
<button
|
|
273
295
|
key={sub.id}
|
|
274
296
|
type="button"
|
|
275
|
-
className=
|
|
276
|
-
sub.id === selectedId
|
|
277
|
-
? "border-primary bg-primary/5"
|
|
278
|
-
: "hover:bg-muted/60"
|
|
279
|
-
}`}
|
|
297
|
+
className="flex w-full items-center justify-between gap-3 rounded-md border px-3 py-2 text-left transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
280
298
|
onClick={() => loadSubscription(sub)}
|
|
281
299
|
>
|
|
282
300
|
<span className="min-w-0">
|
|
@@ -293,144 +311,164 @@ export function EmailReportDialog({
|
|
|
293
311
|
</button>
|
|
294
312
|
))}
|
|
295
313
|
</div>
|
|
296
|
-
)}
|
|
297
|
-
</div>
|
|
298
|
-
|
|
299
|
-
<div className="grid gap-4 sm:grid-cols-2">
|
|
300
|
-
<div className="space-y-2 sm:col-span-2">
|
|
301
|
-
<Label htmlFor="dashboard-report-name">
|
|
302
|
-
{t("sqlDashboard.reportName")}
|
|
303
|
-
</Label>
|
|
304
|
-
<Input
|
|
305
|
-
id="dashboard-report-name"
|
|
306
|
-
value={name}
|
|
307
|
-
onChange={(event) => setName(event.target.value)}
|
|
308
|
-
/>
|
|
309
314
|
</div>
|
|
315
|
+
) : null}
|
|
310
316
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
317
|
+
{isFormView ? (
|
|
318
|
+
<>
|
|
319
|
+
<div className="flex items-center justify-between gap-3">
|
|
320
|
+
{subscriptions.length > 0 ? (
|
|
321
|
+
<Button
|
|
322
|
+
variant="ghost"
|
|
323
|
+
size="sm"
|
|
324
|
+
className="-ml-2 px-2"
|
|
325
|
+
onClick={returnToList}
|
|
326
|
+
>
|
|
327
|
+
<IconChevronLeft className="mr-1.5 h-3.5 w-3.5" />
|
|
328
|
+
{t("sqlDashboard.existingReportSubscriptions")}
|
|
329
|
+
</Button>
|
|
330
|
+
) : null}
|
|
331
|
+
<p className="text-sm font-medium">
|
|
332
|
+
{selectedId
|
|
333
|
+
? t("sqlDashboard.updateSubscription")
|
|
334
|
+
: t("sqlDashboard.newSubscription")}
|
|
335
|
+
</p>
|
|
336
|
+
</div>
|
|
323
337
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
338
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
339
|
+
<div className="space-y-2 sm:col-span-2">
|
|
340
|
+
<Label htmlFor="dashboard-report-name">
|
|
341
|
+
{t("sqlDashboard.reportName")}
|
|
342
|
+
</Label>
|
|
343
|
+
<Input
|
|
344
|
+
id="dashboard-report-name"
|
|
345
|
+
value={name}
|
|
346
|
+
onChange={(event) => setName(event.target.value)}
|
|
347
|
+
/>
|
|
348
|
+
</div>
|
|
335
349
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
350
|
+
<div className="space-y-2 sm:col-span-2">
|
|
351
|
+
<Label htmlFor="dashboard-report-recipients">
|
|
352
|
+
{t("sqlDashboard.reportRecipients")}
|
|
353
|
+
</Label>
|
|
354
|
+
<Textarea
|
|
355
|
+
id="dashboard-report-recipients"
|
|
356
|
+
value={recipientsText}
|
|
357
|
+
onChange={(event) => setRecipientsText(event.target.value)}
|
|
358
|
+
placeholder={t("sqlDashboard.reportRecipientsPlaceholder")}
|
|
359
|
+
className="min-h-[76px]"
|
|
360
|
+
/>
|
|
361
|
+
</div>
|
|
346
362
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
type="button"
|
|
359
|
-
variant="outline"
|
|
360
|
-
size="sm"
|
|
361
|
-
onClick={() => setReportFilters(currentFilterSnapshot)}
|
|
362
|
-
disabled={reportFiltersMatchCurrent}
|
|
363
|
-
>
|
|
364
|
-
<IconFilter className="mr-1.5 h-3.5 w-3.5" />
|
|
365
|
-
{t("sqlDashboard.reportUseCurrentFilters")}
|
|
366
|
-
</Button>
|
|
367
|
-
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
|
368
|
-
</div>
|
|
369
|
-
</div>
|
|
363
|
+
<div className="space-y-2">
|
|
364
|
+
<Label htmlFor="dashboard-report-time">
|
|
365
|
+
{t("sqlDashboard.reportSendTime")}
|
|
366
|
+
</Label>
|
|
367
|
+
<Input
|
|
368
|
+
id="dashboard-report-time"
|
|
369
|
+
type="time"
|
|
370
|
+
value={timeOfDay}
|
|
371
|
+
onChange={(event) => setTimeOfDay(event.target.value)}
|
|
372
|
+
/>
|
|
373
|
+
</div>
|
|
370
374
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
375
|
+
<div className="space-y-2">
|
|
376
|
+
<Label htmlFor="dashboard-report-timezone">
|
|
377
|
+
{t("sqlDashboard.reportTimezone")}
|
|
378
|
+
</Label>
|
|
379
|
+
<Input
|
|
380
|
+
id="dashboard-report-timezone"
|
|
381
|
+
value={timezone}
|
|
382
|
+
onChange={(event) => setTimezone(event.target.value)}
|
|
383
|
+
/>
|
|
379
384
|
</div>
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
385
|
+
|
|
386
|
+
<div className="flex items-center justify-between gap-3 rounded-md border px-3 py-2 sm:col-span-2">
|
|
387
|
+
<div className="min-w-0">
|
|
388
|
+
<Label>{t("sqlDashboard.reportEnabled")}</Label>
|
|
389
|
+
<p className="text-xs text-muted-foreground">
|
|
390
|
+
{t("sqlDashboard.reportFilterSnapshot", {
|
|
391
|
+
count: reportFilterCount,
|
|
392
|
+
})}
|
|
393
|
+
</p>
|
|
394
|
+
</div>
|
|
395
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
396
|
+
<Button
|
|
397
|
+
type="button"
|
|
398
|
+
variant="outline"
|
|
399
|
+
size="sm"
|
|
400
|
+
onClick={() => setReportFilters(currentFilterSnapshot)}
|
|
401
|
+
disabled={reportFiltersMatchCurrent}
|
|
402
|
+
>
|
|
403
|
+
<IconFilter className="mr-1.5 h-3.5 w-3.5" />
|
|
404
|
+
{t("sqlDashboard.reportUseCurrentFilters")}
|
|
405
|
+
</Button>
|
|
406
|
+
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
|
407
|
+
</div>
|
|
386
408
|
</div>
|
|
409
|
+
|
|
410
|
+
{selectedSubscription ? (
|
|
411
|
+
<div className="grid gap-2 text-xs text-muted-foreground sm:col-span-2 sm:grid-cols-2">
|
|
412
|
+
<div>
|
|
413
|
+
{t("sqlDashboard.reportLastRun")}:{" "}
|
|
414
|
+
{formatDate(
|
|
415
|
+
selectedSubscription.lastRunAt,
|
|
416
|
+
t("sqlDashboard.never"),
|
|
417
|
+
)}
|
|
418
|
+
</div>
|
|
419
|
+
<div>
|
|
420
|
+
{t("sqlDashboard.reportNextRun")}:{" "}
|
|
421
|
+
{formatDate(
|
|
422
|
+
selectedSubscription.nextRunAt,
|
|
423
|
+
t("sqlDashboard.never"),
|
|
424
|
+
)}
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
) : null}
|
|
387
428
|
</div>
|
|
388
|
-
|
|
389
|
-
|
|
429
|
+
</>
|
|
430
|
+
) : null}
|
|
390
431
|
</div>
|
|
391
432
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
433
|
+
{isFormView ? (
|
|
434
|
+
<DialogFooter className="gap-2 sm:gap-0">
|
|
435
|
+
{selectedId ? (
|
|
436
|
+
<Button
|
|
437
|
+
variant="outline"
|
|
438
|
+
size="sm"
|
|
439
|
+
onClick={handleDelete}
|
|
440
|
+
disabled={deleteSubscription.isPending}
|
|
441
|
+
>
|
|
442
|
+
<IconTrash className="mr-1.5 h-3.5 w-3.5" />
|
|
443
|
+
{t("sidebar.delete")}
|
|
444
|
+
</Button>
|
|
445
|
+
) : null}
|
|
446
|
+
{selectedId ? (
|
|
447
|
+
<Button
|
|
448
|
+
variant="outline"
|
|
449
|
+
size="sm"
|
|
450
|
+
onClick={handleSendNow}
|
|
451
|
+
disabled={sendNow.isPending}
|
|
452
|
+
>
|
|
453
|
+
<IconSend className="mr-1.5 h-3.5 w-3.5" />
|
|
454
|
+
{sendNow.isPending
|
|
455
|
+
? t("sqlDashboard.reportSending")
|
|
456
|
+
: t("sqlDashboard.reportSendNow")}
|
|
457
|
+
</Button>
|
|
458
|
+
) : null}
|
|
405
459
|
<Button
|
|
406
|
-
variant="outline"
|
|
407
460
|
size="sm"
|
|
408
|
-
onClick={
|
|
409
|
-
disabled={
|
|
461
|
+
onClick={handleSave}
|
|
462
|
+
disabled={saveSubscription.isPending}
|
|
410
463
|
>
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
464
|
+
{saveSubscription.isPending
|
|
465
|
+
? t("sqlDashboard.saving")
|
|
466
|
+
: selectedId
|
|
467
|
+
? t("sqlDashboard.updateSubscription")
|
|
468
|
+
: t("sqlDashboard.saveSubscription")}
|
|
415
469
|
</Button>
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
size="sm"
|
|
419
|
-
onClick={handleSave}
|
|
420
|
-
disabled={saveSubscription.isPending}
|
|
421
|
-
>
|
|
422
|
-
{selectedId ? (
|
|
423
|
-
<IconPencil className="mr-1.5 h-3.5 w-3.5" />
|
|
424
|
-
) : (
|
|
425
|
-
<IconMail className="mr-1.5 h-3.5 w-3.5" />
|
|
426
|
-
)}
|
|
427
|
-
{saveSubscription.isPending
|
|
428
|
-
? t("sqlDashboard.saving")
|
|
429
|
-
: selectedId
|
|
430
|
-
? t("sqlDashboard.updateSubscription")
|
|
431
|
-
: t("sqlDashboard.saveSubscription")}
|
|
432
|
-
</Button>
|
|
433
|
-
</DialogFooter>
|
|
470
|
+
</DialogFooter>
|
|
471
|
+
) : null}
|
|
434
472
|
</DialogContent>
|
|
435
473
|
</Dialog>
|
|
436
474
|
);
|
|
@@ -27,17 +27,20 @@ import {
|
|
|
27
27
|
} from "@dnd-kit/core";
|
|
28
28
|
import {
|
|
29
29
|
IconArchive,
|
|
30
|
+
IconBuilding,
|
|
30
31
|
IconClock,
|
|
31
32
|
IconDotsVertical,
|
|
32
33
|
IconEye,
|
|
33
34
|
IconEyeOff,
|
|
34
35
|
IconGripVertical,
|
|
35
36
|
IconInfoCircle,
|
|
37
|
+
IconLock,
|
|
36
38
|
IconMail,
|
|
37
39
|
IconPencil,
|
|
38
40
|
IconPlus,
|
|
39
41
|
IconTrash,
|
|
40
42
|
IconUser,
|
|
43
|
+
IconWorld,
|
|
41
44
|
IconX,
|
|
42
45
|
} from "@tabler/icons-react";
|
|
43
46
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
@@ -1422,24 +1425,14 @@ export default function SqlDashboardPage() {
|
|
|
1422
1425
|
</span>
|
|
1423
1426
|
)}
|
|
1424
1427
|
{dashboardVisibility ? (
|
|
1425
|
-
<span
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
}
|
|
1433
|
-
>
|
|
1434
|
-
<span
|
|
1435
|
-
className={`h-1.5 w-1.5 rounded-full ${
|
|
1436
|
-
dashboardVisibility === "public"
|
|
1437
|
-
? "bg-green-500"
|
|
1438
|
-
: dashboardVisibility === "org"
|
|
1439
|
-
? "bg-blue-500"
|
|
1440
|
-
: "bg-yellow-500"
|
|
1441
|
-
}`}
|
|
1442
|
-
/>
|
|
1428
|
+
<span className="flex items-center gap-1.5">
|
|
1429
|
+
{dashboardVisibility === "public" ? (
|
|
1430
|
+
<IconWorld className="h-3 w-3" />
|
|
1431
|
+
) : dashboardVisibility === "org" ? (
|
|
1432
|
+
<IconBuilding className="h-3 w-3" />
|
|
1433
|
+
) : (
|
|
1434
|
+
<IconLock className="h-3 w-3" />
|
|
1435
|
+
)}
|
|
1443
1436
|
{dashboardVisibility === "public"
|
|
1444
1437
|
? t("sqlDashboard.public")
|
|
1445
1438
|
: dashboardVisibility === "org"
|
|
@@ -11,7 +11,10 @@ import {
|
|
|
11
11
|
IconTrash,
|
|
12
12
|
IconClock,
|
|
13
13
|
IconArrowLeft,
|
|
14
|
+
IconBuilding,
|
|
14
15
|
IconDatabase,
|
|
16
|
+
IconLock,
|
|
17
|
+
IconWorld,
|
|
15
18
|
} from "@tabler/icons-react";
|
|
16
19
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
17
20
|
import { useEffect } from "react";
|
|
@@ -288,24 +291,14 @@ export default function AnalysisDetail() {
|
|
|
288
291
|
{analysis.author && (
|
|
289
292
|
<span>{t("analyses.byAuthor", { author: analysis.author })}</span>
|
|
290
293
|
)}
|
|
291
|
-
<span
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
>
|
|
300
|
-
<span
|
|
301
|
-
className={`h-1.5 w-1.5 rounded-full ${
|
|
302
|
-
analysis.visibility === "public"
|
|
303
|
-
? "bg-green-500"
|
|
304
|
-
: analysis.visibility === "org"
|
|
305
|
-
? "bg-blue-500"
|
|
306
|
-
: "bg-yellow-500"
|
|
307
|
-
}`}
|
|
308
|
-
/>
|
|
294
|
+
<span className="flex items-center gap-1.5">
|
|
295
|
+
{analysis.visibility === "public" ? (
|
|
296
|
+
<IconWorld className="h-3 w-3" />
|
|
297
|
+
) : analysis.visibility === "org" ? (
|
|
298
|
+
<IconBuilding className="h-3 w-3" />
|
|
299
|
+
) : (
|
|
300
|
+
<IconLock className="h-3 w-3" />
|
|
301
|
+
)}
|
|
309
302
|
{analysis.visibility === "public"
|
|
310
303
|
? t("analyses.public")
|
|
311
304
|
: analysis.visibility === "org"
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
IconPlayerSkipForward,
|
|
22
22
|
IconPlayerTrackNext,
|
|
23
23
|
IconRoute,
|
|
24
|
+
IconTerminal2,
|
|
24
25
|
IconTimelineEvent,
|
|
25
26
|
} from "@tabler/icons-react";
|
|
26
27
|
import { useQuery } from "@tanstack/react-query";
|
|
@@ -51,6 +52,9 @@ import {
|
|
|
51
52
|
import { getIdToken } from "@/lib/auth";
|
|
52
53
|
import { cn } from "@/lib/utils";
|
|
53
54
|
|
|
55
|
+
import { extractReplayDiagnostics } from "./session-replay-devtools";
|
|
56
|
+
import { SessionDevToolsPanel } from "./SessionDevToolsPanel";
|
|
57
|
+
|
|
54
58
|
type SessionRecordingSummary = {
|
|
55
59
|
id: string;
|
|
56
60
|
clientRecordingId: string;
|
|
@@ -393,6 +397,7 @@ function ReplayPlayer({
|
|
|
393
397
|
const [totalTime, setTotalTime] = useState(0);
|
|
394
398
|
const [speed, setSpeed] = useState(DEFAULT_SPEED);
|
|
395
399
|
const [skipInactive, setSkipInactive] = useState(true);
|
|
400
|
+
const [devToolsOpen, setDevToolsOpen] = useState(false);
|
|
396
401
|
const [streamedDims, setStreamedDims] = useState<{
|
|
397
402
|
width: number;
|
|
398
403
|
height: number;
|
|
@@ -414,6 +419,9 @@ function ReplayPlayer({
|
|
|
414
419
|
() => currentUrlAt(events, currentTime),
|
|
415
420
|
[events, currentTime],
|
|
416
421
|
);
|
|
422
|
+
const diagnostics = useMemo(() => extractReplayDiagnostics(events), [events]);
|
|
423
|
+
const devToolsIssueCount =
|
|
424
|
+
diagnostics.consoleErrorCount + diagnostics.networkFailedCount;
|
|
417
425
|
|
|
418
426
|
useEffect(() => {
|
|
419
427
|
const el = stageAreaRef.current;
|
|
@@ -494,7 +502,14 @@ function ReplayPlayer({
|
|
|
494
502
|
showWarning: false,
|
|
495
503
|
showDebug: false,
|
|
496
504
|
triggerFocus: false,
|
|
497
|
-
|
|
505
|
+
// Subtle FullStory-style trail behind the visitor cursor. The canvas
|
|
506
|
+
// is resized alongside the iframe in applyReplayFrameDimensions.
|
|
507
|
+
mouseTail: {
|
|
508
|
+
strokeStyle: "rgba(59, 130, 246, 0.35)",
|
|
509
|
+
lineWidth: 2,
|
|
510
|
+
duration: 600,
|
|
511
|
+
lineCap: "round",
|
|
512
|
+
},
|
|
498
513
|
insertStyleRules: [
|
|
499
514
|
"[data-radix-popper-content-wrapper], .Toastify, [class*='toast'], [class*='Toast'] { display: none !important; }",
|
|
500
515
|
],
|
|
@@ -793,6 +808,31 @@ function ReplayPlayer({
|
|
|
793
808
|
: t("sessions.skipInactiveOff")}
|
|
794
809
|
</TooltipContent>
|
|
795
810
|
</Tooltip>
|
|
811
|
+
|
|
812
|
+
<button
|
|
813
|
+
type="button"
|
|
814
|
+
className={cn(
|
|
815
|
+
"inline-flex h-8 items-center gap-1.5 rounded-md border px-2 text-xs font-medium transition-colors hover:bg-muted",
|
|
816
|
+
devToolsOpen && "border-primary/40 bg-primary/10 text-primary",
|
|
817
|
+
)}
|
|
818
|
+
onClick={() => setDevToolsOpen((value) => !value)}
|
|
819
|
+
aria-pressed={devToolsOpen}
|
|
820
|
+
aria-expanded={devToolsOpen}
|
|
821
|
+
>
|
|
822
|
+
<IconTerminal2 className="h-4 w-4" />
|
|
823
|
+
{t("sessions.devtools")}
|
|
824
|
+
{devToolsIssueCount > 0 ? (
|
|
825
|
+
<span
|
|
826
|
+
className="inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 font-mono text-[10px] font-semibold leading-none text-white"
|
|
827
|
+
aria-label={t("sessions.devtoolsIssueCount", {
|
|
828
|
+
count: String(devToolsIssueCount),
|
|
829
|
+
})}
|
|
830
|
+
>
|
|
831
|
+
{devToolsIssueCount > 99 ? "99+" : devToolsIssueCount}
|
|
832
|
+
</span>
|
|
833
|
+
) : null}
|
|
834
|
+
</button>
|
|
835
|
+
|
|
796
836
|
<span className="ms-auto hidden text-xs text-muted-foreground lg:inline">
|
|
797
837
|
{t("sessions.replayEventCount", {
|
|
798
838
|
events: String(response.eventCount),
|
|
@@ -801,6 +841,14 @@ function ReplayPlayer({
|
|
|
801
841
|
</span>
|
|
802
842
|
</div>
|
|
803
843
|
|
|
844
|
+
{devToolsOpen ? (
|
|
845
|
+
<SessionDevToolsPanel
|
|
846
|
+
diagnostics={diagnostics}
|
|
847
|
+
currentTime={currentTime}
|
|
848
|
+
onSeek={(ms) => seek(ms, true)}
|
|
849
|
+
/>
|
|
850
|
+
) : null}
|
|
851
|
+
|
|
804
852
|
{response.unavailableChunks > 0 ? (
|
|
805
853
|
<div className="border-t px-4 py-2 text-xs text-muted-foreground">
|
|
806
854
|
{t("sessions.unavailableChunks", {
|