@agent-native/core 0.79.20 → 0.79.22
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 +16 -0
- package/corpus/core/docs/content/deployment.md +133 -0
- package/corpus/core/docs/content/faq.md +1 -1
- package/corpus/core/docs/content/multi-app-workspace.md +2 -0
- package/corpus/core/docs/content/tracking.md +3 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/openai-compatible-endpoint.ts +25 -0
- package/corpus/core/src/agent/engine/provider-env-vars.ts +2 -0
- package/corpus/core/src/agent/engine/registry.ts +59 -11
- package/corpus/core/src/client/FeedbackButton.tsx +11 -1
- package/corpus/core/src/client/agent-engine-key.ts +57 -10
- package/corpus/core/src/client/analytics.ts +78 -27
- package/corpus/core/src/client/blocks/library/HighlightedCode.tsx +1 -1
- package/corpus/core/src/client/index.ts +2 -0
- package/corpus/core/src/client/session-replay.ts +159 -17
- package/corpus/core/src/client/settings/SettingsPanel.tsx +119 -4
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +5 -0
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +8 -3
- package/corpus/core/src/scripts/agent-engines/test-agent-engine.ts +54 -6
- package/corpus/core/src/server/agent-engine-api-key-route.ts +83 -12
- package/corpus/core/src/server/core-routes-plugin.ts +26 -2
- package/corpus/core/src/styles/blocks.css +23 -7
- package/corpus/templates/analytics/actions/list-session-recordings.ts +1 -1
- package/corpus/templates/analytics/app/components/layout/MobileNav.tsx +5 -10
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +22 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +64 -50
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +38 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-sync.ts +89 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +138 -59
- package/corpus/templates/analytics/changelog/2026-06-27-dashboard-charts-drag-smoothly-when-rearranging-panels.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-dashboard-email-reports-still-send-when-the-live-screenshot-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-fixed-dashboard-edits-so-charts-no-longer-briefly-snap-back-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-session-replay-lists-now-show-only-signed-in-recordings-with.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-sidebar-loading-placeholders-are-visible-in-light-mode.md +6 -0
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/scripts/seed-session-replay.ts +1 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +1 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +120 -29
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +1 -1
- package/corpus/templates/analytics/server/lib/session-replay.ts +158 -46
- package/corpus/templates/assets/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/assets/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +6 -0
- package/corpus/templates/clips/app/components/capture-install-options.tsx +0 -9
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +20 -4
- package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +6 -2
- package/corpus/templates/clips/changelog/2026-06-27-clips-no-longer-shows-desktop-app-prompts-on-mobile-screens.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-27-desktop-storage-setup-flow.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-27-recorder-download-options-now-show-the-chrome-extension-and-.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-27-recorder-setup-mobile-capture-options.md +6 -0
- package/corpus/templates/clips/chrome-extension/package.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/sentry.ts +17 -1
- package/corpus/templates/clips/desktop/package.json +1 -0
- package/corpus/templates/clips/desktop/scripts/check-tauri-prereqs.mjs +45 -0
- package/corpus/templates/clips/desktop/src/app.tsx +32 -3
- package/corpus/templates/clips/desktop/src/lib/sentry.ts +71 -0
- package/corpus/templates/clips/desktop/src/main.tsx +2 -0
- package/corpus/templates/clips/desktop/src/styles.css +67 -0
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -1
- package/corpus/templates/clips/desktop/src-tauri/build.rs +29 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/logfile.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/sentry_report.rs +142 -0
- package/corpus/templates/clips/desktop/vite.config.ts +66 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/design/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +6 -0
- package/corpus/templates/forms/actions/export-responses.ts +3 -2
- package/corpus/templates/forms/actions/list-responses.ts +2 -1
- package/corpus/templates/forms/actions/response-insights.ts +13 -4
- package/corpus/templates/forms/changelog/2026-06-27-anonymous-feedback-no-longer-shows-internal-agent-native-ano.md +6 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +5 -8
- package/corpus/templates/forms/server/lib/integrations.ts +9 -6
- package/corpus/templates/forms/shared/submitter-email.ts +20 -0
- package/corpus/templates/forms/shared/types.ts +1 -1
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +49 -10
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +6 -0
- package/dist/agent/engine/openai-compatible-endpoint.d.ts +3 -0
- package/dist/agent/engine/openai-compatible-endpoint.d.ts.map +1 -0
- package/dist/agent/engine/openai-compatible-endpoint.js +23 -0
- package/dist/agent/engine/openai-compatible-endpoint.js.map +1 -0
- package/dist/agent/engine/provider-env-vars.d.ts +1 -0
- package/dist/agent/engine/provider-env-vars.d.ts.map +1 -1
- package/dist/agent/engine/provider-env-vars.js +1 -0
- package/dist/agent/engine/provider-env-vars.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +39 -12
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/client/FeedbackButton.d.ts.map +1 -1
- package/dist/client/FeedbackButton.js +6 -1
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/agent-engine-key.d.ts +14 -0
- package/dist/client/agent-engine-key.d.ts.map +1 -1
- package/dist/client/agent-engine-key.js +35 -10
- package/dist/client/agent-engine-key.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +71 -24
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +1 -1
- package/dist/client/blocks/library/HighlightedCode.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/session-replay.d.ts +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +115 -17
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +42 -6
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +4 -0
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +5 -4
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/test-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/test-agent-engine.js +37 -5
- package/dist/scripts/agent-engines/test-agent-engine.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +5 -5
- package/dist/server/agent-engine-api-key-route.d.ts.map +1 -1
- package/dist/server/agent-engine-api-key-route.js +71 -11
- package/dist/server/agent-engine-api-key-route.js.map +1 -1
- 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/styles/blocks.css +23 -7
- package/docs/content/deployment.md +133 -0
- package/docs/content/faq.md +1 -1
- package/docs/content/multi-app-workspace.md +2 -0
- package/docs/content/tracking.md +3 -3
- package/package.json +1 -1
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
SelectValue,
|
|
42
42
|
} from "@/components/ui/select";
|
|
43
43
|
import { Switch } from "@/components/ui/switch";
|
|
44
|
+
import { useIsMobile } from "@/hooks/use-mobile";
|
|
44
45
|
import {
|
|
45
46
|
loadRecorderPreferences,
|
|
46
47
|
saveRecorderPreferences,
|
|
@@ -192,6 +193,7 @@ export function PreRecordPanel({
|
|
|
192
193
|
error: null,
|
|
193
194
|
hasPreview: false,
|
|
194
195
|
});
|
|
196
|
+
const isMobile = useIsMobile();
|
|
195
197
|
|
|
196
198
|
const modeOptions = useMemo<ModeOption[]>(
|
|
197
199
|
() => [
|
|
@@ -213,6 +215,14 @@ export function PreRecordPanel({
|
|
|
213
215
|
],
|
|
214
216
|
[t],
|
|
215
217
|
);
|
|
218
|
+
const visibleModeOptions = useMemo(
|
|
219
|
+
() =>
|
|
220
|
+
isMobile
|
|
221
|
+
? modeOptions.filter((option) => option.value === "camera")
|
|
222
|
+
: modeOptions,
|
|
223
|
+
[isMobile, modeOptions],
|
|
224
|
+
);
|
|
225
|
+
|
|
216
226
|
const surfaceOptions = useMemo<SurfaceOption[]>(
|
|
217
227
|
() => [
|
|
218
228
|
{
|
|
@@ -238,8 +248,14 @@ export function PreRecordPanel({
|
|
|
238
248
|
);
|
|
239
249
|
|
|
240
250
|
useEffect(() => {
|
|
251
|
+
if (isMobile) {
|
|
252
|
+
// Mobile browsers do not support the screen-capture choices this panel
|
|
253
|
+
// offers on desktop, so keep the setup focused on recording face video.
|
|
254
|
+
setMode("camera");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
241
257
|
if (initialMode) setMode(initialMode);
|
|
242
|
-
}, [initialMode]);
|
|
258
|
+
}, [initialMode, isMobile]);
|
|
243
259
|
|
|
244
260
|
useEffect(() => {
|
|
245
261
|
if (initialDisplaySurface) setDisplaySurface(initialDisplaySurface);
|
|
@@ -586,7 +602,7 @@ export function PreRecordPanel({
|
|
|
586
602
|
<div className="mx-auto w-full max-w-lg overflow-hidden rounded-2xl border border-border bg-card shadow-lg">
|
|
587
603
|
<div className="p-6">
|
|
588
604
|
<div className="grid gap-2 sm:grid-cols-3">
|
|
589
|
-
{
|
|
605
|
+
{visibleModeOptions.map((opt) => {
|
|
590
606
|
const Icon = opt.icon;
|
|
591
607
|
const active = opt.value === mode;
|
|
592
608
|
return (
|
|
@@ -600,7 +616,7 @@ export function PreRecordPanel({
|
|
|
600
616
|
className={cn(
|
|
601
617
|
"flex min-h-20 min-w-0 flex-col justify-between rounded-xl border p-3 text-start transition-colors",
|
|
602
618
|
active
|
|
603
|
-
? "border-primary bg-primary text-
|
|
619
|
+
? "border-primary/55 bg-primary/[0.12] text-foreground shadow-sm ring-1 ring-primary/15"
|
|
604
620
|
: "border-border bg-background text-foreground hover:border-foreground/30 hover:bg-muted/45",
|
|
605
621
|
)}
|
|
606
622
|
aria-pressed={active}
|
|
@@ -609,7 +625,7 @@ export function PreRecordPanel({
|
|
|
609
625
|
className={cn(
|
|
610
626
|
"mb-3 flex h-9 w-9 items-center justify-center rounded-full",
|
|
611
627
|
active
|
|
612
|
-
? "bg-primary
|
|
628
|
+
? "bg-primary/[0.18] text-foreground"
|
|
613
629
|
: "bg-muted text-muted-foreground",
|
|
614
630
|
)}
|
|
615
631
|
>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
+
import { useIsMobile } from "@/hooks/use-mobile";
|
|
4
|
+
|
|
3
5
|
const DISMISSED_KEY = "clips.desktop-promo.dismissed";
|
|
4
6
|
|
|
5
7
|
function detectDesktopApp(): boolean {
|
|
@@ -17,6 +19,7 @@ function detectDesktopApp(): boolean {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function useDesktopPromo() {
|
|
22
|
+
const isMobile = useIsMobile();
|
|
20
23
|
const [isDesktopApp, setIsDesktopApp] = useState(false);
|
|
21
24
|
const [dismissed, setDismissed] = useState(false);
|
|
22
25
|
|
|
@@ -40,8 +43,9 @@ export function useDesktopPromo() {
|
|
|
40
43
|
|
|
41
44
|
return {
|
|
42
45
|
isDesktopApp,
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
isMobile,
|
|
47
|
+
shouldShowPromo: !isMobile && !isDesktopApp && !dismissed,
|
|
48
|
+
shouldShowSidebarLink: !isMobile && !isDesktopApp,
|
|
45
49
|
dismiss,
|
|
46
50
|
};
|
|
47
51
|
}
|
|
@@ -74,7 +74,9 @@ function shouldDropExpectedMediaPermissionError(
|
|
|
74
74
|
const tags = context.tags ?? {};
|
|
75
75
|
const isPermissionProbe =
|
|
76
76
|
tags.surface === "permission" &&
|
|
77
|
-
(tags.permission
|
|
77
|
+
(!tags.permission ||
|
|
78
|
+
tags.permission === "mic" ||
|
|
79
|
+
tags.permission === "cam");
|
|
78
80
|
const isOptionalBubbleCamera =
|
|
79
81
|
tags.surface === "overlay" && tags.overlayPart === "bubble";
|
|
80
82
|
if (!isPermissionProbe && !isOptionalBubbleCamera) return false;
|
|
@@ -87,11 +89,24 @@ function shouldDropExpectedMediaPermissionError(
|
|
|
87
89
|
return (
|
|
88
90
|
/\bNotAllowedError\b/i.test(text) ||
|
|
89
91
|
/\bPermission (?:denied|dismissed)\b/i.test(text) ||
|
|
92
|
+
/\bPermission denied by system\b/i.test(text) ||
|
|
93
|
+
/\bNotReadableError\b/i.test(text) ||
|
|
94
|
+
/\bCould not start video source\b/i.test(text) ||
|
|
90
95
|
/\b(?:NotFoundError|DevicesNotFoundError)\b/i.test(text) ||
|
|
91
96
|
/\bRequested device not found\b/i.test(text)
|
|
92
97
|
);
|
|
93
98
|
}
|
|
94
99
|
|
|
100
|
+
function shouldDropExpectedMediaPermissionEvent(event: Sentry.Event): boolean {
|
|
101
|
+
if (event.tags?.surface !== "permission") return false;
|
|
102
|
+
const exceptionText = (event.exception?.values ?? [])
|
|
103
|
+
.map((value) => `${value.type ?? ""}: ${value.value ?? ""}`)
|
|
104
|
+
.join(" ");
|
|
105
|
+
return shouldDropExpectedMediaPermissionError(exceptionText, {
|
|
106
|
+
tags: { surface: "permission" },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
95
110
|
function scrubScopeString(value: string): string {
|
|
96
111
|
const scrubbed = scrubUrl(value, { redactAllQueryValues: true });
|
|
97
112
|
if (scrubbed !== value) return scrubbed;
|
|
@@ -185,6 +200,7 @@ export function initExtensionSentry(surface: string): void {
|
|
|
185
200
|
runtime: "chrome-extension",
|
|
186
201
|
surface,
|
|
187
202
|
};
|
|
203
|
+
if (shouldDropExpectedMediaPermissionEvent(event)) return null;
|
|
188
204
|
return scrubEvent(event);
|
|
189
205
|
},
|
|
190
206
|
});
|
|
@@ -14,6 +14,7 @@ const RED = "\x1b[31m";
|
|
|
14
14
|
const YELLOW = "\x1b[33m";
|
|
15
15
|
const CYAN = "\x1b[36m";
|
|
16
16
|
const DIM = "\x1b[2m";
|
|
17
|
+
const MIN_RUST_VERSION = [1, 88, 0];
|
|
17
18
|
|
|
18
19
|
function has(cmd) {
|
|
19
20
|
try {
|
|
@@ -107,3 +108,47 @@ if (!has("cargo")) {
|
|
|
107
108
|
|
|
108
109
|
fail(installLines);
|
|
109
110
|
}
|
|
111
|
+
|
|
112
|
+
function parseRustVersion(output) {
|
|
113
|
+
const match = output.match(/rustc\s+(\d+)\.(\d+)\.(\d+)/);
|
|
114
|
+
if (!match) return null;
|
|
115
|
+
return match.slice(1, 4).map((part) => Number(part));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function compareVersions(a, b) {
|
|
119
|
+
for (let i = 0; i < 3; i += 1) {
|
|
120
|
+
if (a[i] > b[i]) return 1;
|
|
121
|
+
if (a[i] < b[i]) return -1;
|
|
122
|
+
}
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let rustcOutput = "";
|
|
127
|
+
try {
|
|
128
|
+
rustcOutput = execFileSync("rustc", ["--version"], {
|
|
129
|
+
encoding: "utf8",
|
|
130
|
+
});
|
|
131
|
+
} catch {
|
|
132
|
+
fail([
|
|
133
|
+
"",
|
|
134
|
+
`${YELLOW}Cargo is installed, but ${BOLD}rustc${RESET}${YELLOW} is not available.${RESET}`,
|
|
135
|
+
"",
|
|
136
|
+
`${BOLD}Fix:${RESET} update your Rust toolchain, then retry:`,
|
|
137
|
+
"",
|
|
138
|
+
` ${CYAN}rustup update stable${RESET}`,
|
|
139
|
+
]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const rustVersion = parseRustVersion(rustcOutput);
|
|
143
|
+
if (!rustVersion || compareVersions(rustVersion, MIN_RUST_VERSION) < 0) {
|
|
144
|
+
fail([
|
|
145
|
+
"",
|
|
146
|
+
`${YELLOW}Clips Desktop needs Rust ${BOLD}${MIN_RUST_VERSION.join(".")}+${RESET}${YELLOW} for the current Tauri/Sentry native SDKs.${RESET}`,
|
|
147
|
+
"",
|
|
148
|
+
`Found: ${DIM}${rustcOutput.trim() || "unknown"}${RESET}`,
|
|
149
|
+
"",
|
|
150
|
+
`${BOLD}Fix:${RESET} update Rust, then retry:`,
|
|
151
|
+
"",
|
|
152
|
+
` ${CYAN}rustup update stable${RESET}`,
|
|
153
|
+
]);
|
|
154
|
+
}
|
|
@@ -1687,9 +1687,7 @@ export function App() {
|
|
|
1687
1687
|
const openVideoStorageSetup = useCallback(
|
|
1688
1688
|
(targetServerUrl?: string) => {
|
|
1689
1689
|
const base = (targetServerUrl?.trim() || serverUrl).replace(/\/+$/, "");
|
|
1690
|
-
setRecError(
|
|
1691
|
-
"Connect storage before recording from desktop: Builder.io (free tier storage + AI) or S3-compatible storage. Opening storage setup...",
|
|
1692
|
-
);
|
|
1690
|
+
setRecError(STORAGE_SETUP_HELP_TEXT);
|
|
1693
1691
|
void openExternal(`${base}/record`).catch((err) => {
|
|
1694
1692
|
setRecError(
|
|
1695
1693
|
err instanceof Error
|
|
@@ -1924,6 +1922,11 @@ export function App() {
|
|
|
1924
1922
|
setRecError(MACOS_CAPTURE_PERMISSION_MESSAGE);
|
|
1925
1923
|
return;
|
|
1926
1924
|
}
|
|
1925
|
+
if (isStorageSetupFailureMessage(message)) {
|
|
1926
|
+
setRecError(STORAGE_SETUP_HELP_TEXT);
|
|
1927
|
+
openVideoStorageSetup();
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1927
1930
|
setRecError(message);
|
|
1928
1931
|
}
|
|
1929
1932
|
|
|
@@ -2306,6 +2309,8 @@ export function App() {
|
|
|
2306
2309
|
panes={["speech", "microphone"]}
|
|
2307
2310
|
onRetry={handleStartRecording}
|
|
2308
2311
|
/>
|
|
2312
|
+
) : isStorageSetupFailureMessage(recError) ? (
|
|
2313
|
+
<StorageConnectionBanner onConnect={() => openVideoStorageSetup()} />
|
|
2309
2314
|
) : (
|
|
2310
2315
|
<div className="error-banner">{recError}</div>
|
|
2311
2316
|
)
|
|
@@ -2450,6 +2455,30 @@ function PermissionRecoveryBanner({
|
|
|
2450
2455
|
);
|
|
2451
2456
|
}
|
|
2452
2457
|
|
|
2458
|
+
function StorageConnectionBanner({ onConnect }: { onConnect: () => void }) {
|
|
2459
|
+
return (
|
|
2460
|
+
<div className="storage-flow-banner">
|
|
2461
|
+
<div className="storage-flow-icon" aria-hidden>
|
|
2462
|
+
<IconUpload size={17} stroke={1.8} />
|
|
2463
|
+
</div>
|
|
2464
|
+
<div className="storage-flow-copy">
|
|
2465
|
+
<div className="storage-flow-title">
|
|
2466
|
+
Connect storage to keep recording
|
|
2467
|
+
</div>
|
|
2468
|
+
<div className="storage-flow-sub">{STORAGE_SETUP_HELP_TEXT}</div>
|
|
2469
|
+
</div>
|
|
2470
|
+
<button
|
|
2471
|
+
type="button"
|
|
2472
|
+
className="storage-flow-connect"
|
|
2473
|
+
onClick={onConnect}
|
|
2474
|
+
>
|
|
2475
|
+
<IconExternalLink size={14} stroke={2} />
|
|
2476
|
+
Connect
|
|
2477
|
+
</button>
|
|
2478
|
+
</div>
|
|
2479
|
+
);
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2453
2482
|
function PendingUploadBanner({
|
|
2454
2483
|
uploads,
|
|
2455
2484
|
retryingUploadId,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/browser";
|
|
2
|
+
|
|
3
|
+
declare const __CLIPS_DESKTOP_SENTRY_DSN__: string;
|
|
4
|
+
declare const __CLIPS_DESKTOP_SENTRY_ENVIRONMENT__: string;
|
|
5
|
+
declare const __CLIPS_DESKTOP_VERSION__: string;
|
|
6
|
+
|
|
7
|
+
let initialized = false;
|
|
8
|
+
|
|
9
|
+
function configuredDsn(): string {
|
|
10
|
+
return typeof __CLIPS_DESKTOP_SENTRY_DSN__ === "string"
|
|
11
|
+
? __CLIPS_DESKTOP_SENTRY_DSN__.trim()
|
|
12
|
+
: "";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function configuredEnvironment(): string {
|
|
16
|
+
return typeof __CLIPS_DESKTOP_SENTRY_ENVIRONMENT__ === "string" &&
|
|
17
|
+
__CLIPS_DESKTOP_SENTRY_ENVIRONMENT__
|
|
18
|
+
? __CLIPS_DESKTOP_SENTRY_ENVIRONMENT__
|
|
19
|
+
: "production";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function scrubUrl(value: string): string {
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(value);
|
|
25
|
+
for (const key of [...url.searchParams.keys()]) {
|
|
26
|
+
url.searchParams.set(key, "<redacted>");
|
|
27
|
+
}
|
|
28
|
+
return url.toString();
|
|
29
|
+
} catch {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function scrubEvent<T extends Sentry.Event>(event: T): T {
|
|
35
|
+
if (event.request?.url) {
|
|
36
|
+
event.request.url = scrubUrl(event.request.url);
|
|
37
|
+
}
|
|
38
|
+
if (event.user) {
|
|
39
|
+
delete (event.user as Record<string, unknown>).ip_address;
|
|
40
|
+
}
|
|
41
|
+
return event;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function initDesktopSentry(route: string): void {
|
|
45
|
+
if (initialized) return;
|
|
46
|
+
const dsn = configuredDsn();
|
|
47
|
+
if (!dsn) return;
|
|
48
|
+
initialized = true;
|
|
49
|
+
|
|
50
|
+
Sentry.init({
|
|
51
|
+
dsn,
|
|
52
|
+
environment: configuredEnvironment(),
|
|
53
|
+
release: `clips-desktop@${__CLIPS_DESKTOP_VERSION__ || "0.0.0"}`,
|
|
54
|
+
sendDefaultPii: false,
|
|
55
|
+
beforeSend(event) {
|
|
56
|
+
event.tags = {
|
|
57
|
+
...event.tags,
|
|
58
|
+
app: "agent-native-clips",
|
|
59
|
+
template: "clips",
|
|
60
|
+
runtime: "tauri-webview",
|
|
61
|
+
surface: route,
|
|
62
|
+
};
|
|
63
|
+
return scrubEvent(event);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
Sentry.setTag("app", "agent-native-clips");
|
|
68
|
+
Sentry.setTag("template", "clips");
|
|
69
|
+
Sentry.setTag("runtime", "tauri-webview");
|
|
70
|
+
Sentry.setTag("surface", route);
|
|
71
|
+
}
|
|
@@ -3,6 +3,7 @@ import React from "react";
|
|
|
3
3
|
import ReactDOM from "react-dom/client";
|
|
4
4
|
|
|
5
5
|
import { App } from "./app";
|
|
6
|
+
import { initDesktopSentry } from "./lib/sentry";
|
|
6
7
|
import { Bubble } from "./overlays/bubble";
|
|
7
8
|
import { Countdown } from "./overlays/countdown";
|
|
8
9
|
import { Finalizing } from "./overlays/finalizing";
|
|
@@ -211,6 +212,7 @@ const rootEl = document.getElementById("root");
|
|
|
211
212
|
if (rootEl) {
|
|
212
213
|
const route = currentRoute();
|
|
213
214
|
installRouteAttributes(route);
|
|
215
|
+
initDesktopSentry(route);
|
|
214
216
|
installConsoleCapture(route);
|
|
215
217
|
installBeforeUnloadCleanup();
|
|
216
218
|
installHeapDebugLog();
|
|
@@ -1680,6 +1680,73 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1680
1680
|
background: var(--surface-hover);
|
|
1681
1681
|
}
|
|
1682
1682
|
|
|
1683
|
+
.storage-flow-banner {
|
|
1684
|
+
display: grid;
|
|
1685
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
1686
|
+
align-items: start;
|
|
1687
|
+
gap: 10px;
|
|
1688
|
+
padding: 10px;
|
|
1689
|
+
border: 1px solid #bfdbfe;
|
|
1690
|
+
border-radius: var(--radius);
|
|
1691
|
+
background: #eff6ff;
|
|
1692
|
+
color: #1e3a8a;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
.storage-flow-icon {
|
|
1696
|
+
width: 28px;
|
|
1697
|
+
height: 28px;
|
|
1698
|
+
display: inline-flex;
|
|
1699
|
+
align-items: center;
|
|
1700
|
+
justify-content: center;
|
|
1701
|
+
border-radius: 8px;
|
|
1702
|
+
background: rgba(59, 130, 246, 0.12);
|
|
1703
|
+
color: #1d4ed8;
|
|
1704
|
+
flex-shrink: 0;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
.storage-flow-copy {
|
|
1708
|
+
min-width: 0;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
.storage-flow-title {
|
|
1712
|
+
font-size: 12px;
|
|
1713
|
+
font-weight: 700;
|
|
1714
|
+
line-height: 1.25;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
.storage-flow-sub {
|
|
1718
|
+
margin-top: 3px;
|
|
1719
|
+
color: #3b5f9a;
|
|
1720
|
+
font-size: 11px;
|
|
1721
|
+
line-height: 1.35;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
.storage-flow-connect {
|
|
1725
|
+
grid-column: 2;
|
|
1726
|
+
justify-self: start;
|
|
1727
|
+
height: 28px;
|
|
1728
|
+
border: 1px solid #bfdbfe;
|
|
1729
|
+
border-radius: var(--radius-sm);
|
|
1730
|
+
background: white;
|
|
1731
|
+
color: #1d4ed8;
|
|
1732
|
+
cursor: pointer;
|
|
1733
|
+
display: inline-flex;
|
|
1734
|
+
align-items: center;
|
|
1735
|
+
justify-content: center;
|
|
1736
|
+
gap: 5px;
|
|
1737
|
+
padding: 0 9px;
|
|
1738
|
+
font-size: 11px;
|
|
1739
|
+
font-weight: 700;
|
|
1740
|
+
transition:
|
|
1741
|
+
background 120ms,
|
|
1742
|
+
border-color 120ms;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
.storage-flow-connect:hover {
|
|
1746
|
+
background: #dbeafe;
|
|
1747
|
+
border-color: #93c5fd;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1683
1750
|
.pending-upload-banner {
|
|
1684
1751
|
display: grid;
|
|
1685
1752
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
@@ -5,7 +5,7 @@ description = "Clips — menu-bar tray app (Tauri 2)"
|
|
|
5
5
|
authors = ["Clips"]
|
|
6
6
|
license = "MIT"
|
|
7
7
|
edition = "2021"
|
|
8
|
-
rust-version = "1.
|
|
8
|
+
rust-version = "1.88"
|
|
9
9
|
|
|
10
10
|
# See https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
11
11
|
|
|
@@ -44,6 +44,7 @@ tauri-plugin-fs = "2.5.1"
|
|
|
44
44
|
tauri-plugin-notification = "2"
|
|
45
45
|
serde = { version = "1", features = ["derive"] }
|
|
46
46
|
serde_json = "1"
|
|
47
|
+
sentry = "0.48.3"
|
|
47
48
|
url = "2"
|
|
48
49
|
# Used by meetings_watcher.rs to poll the backend on a background tokio
|
|
49
50
|
# task. Tauri 2 already pulls reqwest in transitively, but we declare it
|
|
@@ -2,10 +2,39 @@ use std::path::Path;
|
|
|
2
2
|
use std::process::Command;
|
|
3
3
|
|
|
4
4
|
fn main() {
|
|
5
|
+
emit_sentry_env_reruns();
|
|
5
6
|
add_swift_runtime_rpaths();
|
|
6
7
|
tauri_build::build()
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
fn emit_sentry_env_reruns() {
|
|
11
|
+
for name in [
|
|
12
|
+
"CLIPS_DESKTOP_SENTRY_DSN",
|
|
13
|
+
"TAURI_SENTRY_DSN",
|
|
14
|
+
"SENTRY_DESKTOP_DSN",
|
|
15
|
+
"SENTRY_CLIENT_DSN",
|
|
16
|
+
"VITE_SENTRY_CLIENT_DSN",
|
|
17
|
+
"VITE_SENTRY_DSN",
|
|
18
|
+
"SENTRY_DSN",
|
|
19
|
+
"CLIPS_DESKTOP_SENTRY_CLIENT_KEY",
|
|
20
|
+
"SENTRY_CLIENT_KEY",
|
|
21
|
+
"VITE_SENTRY_CLIENT_KEY",
|
|
22
|
+
"CLIPS_DESKTOP_SENTRY_PROJECT_ID",
|
|
23
|
+
"SENTRY_PROJECT_ID",
|
|
24
|
+
"VITE_SENTRY_PROJECT_ID",
|
|
25
|
+
"CLIPS_DESKTOP_SENTRY_INGEST_HOST",
|
|
26
|
+
"SENTRY_INGEST_HOST",
|
|
27
|
+
"VITE_SENTRY_INGEST_HOST",
|
|
28
|
+
"CLIPS_DESKTOP_SENTRY_ENVIRONMENT",
|
|
29
|
+
"SENTRY_ENVIRONMENT",
|
|
30
|
+
"NETLIFY_CONTEXT",
|
|
31
|
+
"VERCEL_ENV",
|
|
32
|
+
"NODE_ENV",
|
|
33
|
+
] {
|
|
34
|
+
println!("cargo:rerun-if-env-changed={name}");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
9
38
|
fn add_swift_runtime_rpaths() {
|
|
10
39
|
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("macos") {
|
|
11
40
|
return;
|
|
@@ -16,6 +16,7 @@ mod native_speech;
|
|
|
16
16
|
mod notifications;
|
|
17
17
|
mod permission_status;
|
|
18
18
|
mod recording_indicator;
|
|
19
|
+
mod sentry_report;
|
|
19
20
|
mod shortcuts;
|
|
20
21
|
mod silence_detector;
|
|
21
22
|
mod state;
|
|
@@ -46,6 +47,8 @@ pub(crate) const TRAY_PNG: &[u8] = include_bytes!("../icons/tray.png");
|
|
|
46
47
|
|
|
47
48
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
48
49
|
pub fn run() {
|
|
50
|
+
sentry_report::init();
|
|
51
|
+
|
|
49
52
|
tauri::Builder::default()
|
|
50
53
|
.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
|
|
51
54
|
// Second launch just focuses the popover of the already-running
|