@agent-native/core 0.68.1 → 0.68.2
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/README.md +4 -4
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +10 -0
- package/corpus/core/docs/content/authentication.md +16 -2
- package/corpus/core/docs/content/template-analytics.md +14 -9
- package/corpus/core/docs/content/template-clips.md +30 -2
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +1 -1
- package/corpus/core/src/client/CommandMenu.tsx +98 -3
- package/corpus/core/src/client/ConnectBuilderCard.tsx +11 -2
- package/corpus/core/src/client/index.ts +2 -0
- package/corpus/core/src/oauth-tokens/google-refresh.ts +32 -17
- package/corpus/core/src/provider-api/index.ts +37 -17
- package/corpus/core/src/server/auth.ts +6 -8
- package/corpus/core/src/server/better-auth-instance.ts +19 -4
- package/corpus/core/src/server/core-routes-plugin.ts +157 -5
- package/corpus/core/src/server/google-oauth-credentials.ts +51 -0
- package/corpus/core/src/server/index.ts +8 -0
- package/corpus/core/src/server/onboarding-html.ts +2 -1
- package/corpus/templates/analytics/app/routes/$.tsx +9 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +33 -1
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +11 -3
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +25 -2
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +12 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +7 -2
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +37 -78
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +33 -1
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +46 -3
- package/corpus/templates/calendar/app/hooks/use-google-auth.ts +203 -2
- package/corpus/templates/calendar/app/lib/event-colors.ts +1 -4
- package/corpus/templates/calendar/app/lib/rsvp-status.tsx +6 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +9 -4
- package/corpus/templates/calendar/app/pages/NotFound.tsx +1 -1
- package/corpus/templates/calendar/app/pages/Settings.tsx +25 -1
- package/corpus/templates/calendar/app/routes/_app.$.tsx +9 -0
- package/corpus/templates/calendar/server/handlers/google-auth.ts +152 -12
- package/corpus/templates/calendar/server/lib/google-calendar.ts +63 -14
- package/corpus/templates/calendar/server/plugins/auth.ts +4 -20
- package/corpus/templates/calendar/shared/api.ts +4 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +17 -0
- package/corpus/templates/clips/actions/sync-calendars.ts +6 -7
- package/corpus/templates/clips/app/components/capture-install-options.tsx +157 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +9 -8
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +4 -6
- package/corpus/templates/clips/app/lib/capture-install-options.ts +13 -0
- package/corpus/templates/clips/app/root.tsx +54 -2
- package/corpus/templates/clips/app/routes/_app.dictate.tsx +9 -13
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -7
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +5 -7
- package/corpus/templates/clips/app/routes/download.tsx +49 -0
- package/corpus/templates/clips/app/routes/record.tsx +4 -4
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +5 -6
- package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +26 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -7
- package/corpus/templates/clips/server/lib/google-calendar-client.ts +61 -0
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +19 -12
- package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +6 -6
- package/corpus/templates/design/app/pages/NotFound.tsx +1 -1
- package/corpus/templates/design/app/routes/$.tsx +9 -0
- package/corpus/templates/mail/app/pages/NotFound.tsx +1 -1
- package/corpus/templates/mail/app/routes/$.tsx +9 -0
- package/corpus/templates/plan/app/global.css +2 -2
- package/corpus/templates/slides/app/pages/NotFound.tsx +1 -1
- package/corpus/templates/slides/app/routes/$.tsx +9 -0
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +81 -25
- package/corpus/templates/videos/app/pages/NotFound.tsx +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/client/CommandMenu.d.ts +15 -2
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +46 -3
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +11 -2
- package/dist/client/ConnectBuilderCard.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.map +1 -1
- package/dist/oauth-tokens/google-refresh.d.ts.map +1 -1
- package/dist/oauth-tokens/google-refresh.js +29 -17
- package/dist/oauth-tokens/google-refresh.js.map +1 -1
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +28 -16
- package/dist/provider-api/index.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +6 -6
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +18 -4
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +5 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +107 -5
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/google-oauth-credentials.d.ts +15 -0
- package/dist/server/google-oauth-credentials.d.ts.map +1 -0
- package/dist/server/google-oauth-credentials.js +34 -0
- package/dist/server/google-oauth-credentials.js.map +1 -0
- 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/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +2 -1
- package/dist/server/onboarding-html.js.map +1 -1
- package/docs/content/authentication.md +16 -2
- package/docs/content/template-analytics.md +14 -9
- package/docs/content/template-clips.md +30 -2
- package/package.json +1 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { Link } from "react-router";
|
|
3
|
+
import {
|
|
4
|
+
IconBrowser,
|
|
5
|
+
IconChevronDown,
|
|
6
|
+
IconDeviceDesktop,
|
|
7
|
+
IconExternalLink,
|
|
8
|
+
} from "@tabler/icons-react";
|
|
9
|
+
import { Button, type ButtonProps } from "@/components/ui/button";
|
|
10
|
+
import {
|
|
11
|
+
Popover,
|
|
12
|
+
PopoverContent,
|
|
13
|
+
PopoverTrigger,
|
|
14
|
+
} from "@/components/ui/popover";
|
|
15
|
+
import { cn } from "@/lib/utils";
|
|
16
|
+
import {
|
|
17
|
+
clipsChromeExtensionEnabled,
|
|
18
|
+
clipsChromeExtensionUrl,
|
|
19
|
+
} from "@/lib/capture-install-options";
|
|
20
|
+
|
|
21
|
+
type PopoverPlacement = {
|
|
22
|
+
align?: "start" | "center" | "end";
|
|
23
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type CaptureInstallButtonProps = Omit<ButtonProps, "asChild"> &
|
|
27
|
+
PopoverPlacement & {
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
desktopHref?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type CaptureInstallInlineLinkProps = PopoverPlacement & {
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
className?: string;
|
|
35
|
+
desktopHref?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function InstallOptionsContent({ desktopHref = "/download" }) {
|
|
39
|
+
const chromeAvailable = Boolean(clipsChromeExtensionUrl);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="grid gap-2">
|
|
43
|
+
<div className="px-1 pb-1">
|
|
44
|
+
<div className="text-sm font-medium text-popover-foreground">
|
|
45
|
+
Choose your recorder
|
|
46
|
+
</div>
|
|
47
|
+
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">
|
|
48
|
+
Use Chrome when you need browser logs, or desktop for the smoothest
|
|
49
|
+
everyday capture.
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
{chromeAvailable ? (
|
|
54
|
+
<a
|
|
55
|
+
href={clipsChromeExtensionUrl ?? undefined}
|
|
56
|
+
target="_blank"
|
|
57
|
+
rel="noreferrer"
|
|
58
|
+
className="flex items-start gap-3 rounded-md border border-border p-3 text-left transition hover:bg-accent"
|
|
59
|
+
>
|
|
60
|
+
<IconBrowser className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
|
|
61
|
+
<span className="min-w-0 flex-1">
|
|
62
|
+
<span className="block text-sm font-medium">Chrome extension</span>
|
|
63
|
+
<span className="mt-0.5 block text-xs leading-snug text-muted-foreground">
|
|
64
|
+
Best when you want redacted console and network diagnostics from
|
|
65
|
+
the browser tab.
|
|
66
|
+
</span>
|
|
67
|
+
</span>
|
|
68
|
+
<IconExternalLink className="mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
|
69
|
+
</a>
|
|
70
|
+
) : (
|
|
71
|
+
<div className="flex items-start gap-3 rounded-md border border-dashed border-border p-3 text-left opacity-70">
|
|
72
|
+
<IconBrowser className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
73
|
+
<span className="min-w-0 flex-1">
|
|
74
|
+
<span className="block text-sm font-medium">Chrome extension</span>
|
|
75
|
+
<span className="mt-0.5 block text-xs leading-snug text-muted-foreground">
|
|
76
|
+
Browser logs option is ready, pending the Chrome Web Store URL.
|
|
77
|
+
</span>
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
|
|
82
|
+
<Link
|
|
83
|
+
to={desktopHref}
|
|
84
|
+
className="flex items-start gap-3 rounded-md border border-border p-3 text-left transition hover:bg-accent"
|
|
85
|
+
>
|
|
86
|
+
<IconDeviceDesktop className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
|
|
87
|
+
<span className="min-w-0 flex-1">
|
|
88
|
+
<span className="block text-sm font-medium">Desktop app</span>
|
|
89
|
+
<span className="mt-0.5 block text-xs leading-snug text-muted-foreground">
|
|
90
|
+
Most seamless for global shortcuts, menu-bar recording, meetings,
|
|
91
|
+
and repeat captures.
|
|
92
|
+
</span>
|
|
93
|
+
</span>
|
|
94
|
+
</Link>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function CaptureInstallButton({
|
|
100
|
+
children,
|
|
101
|
+
className,
|
|
102
|
+
desktopHref = "/download",
|
|
103
|
+
align = "end",
|
|
104
|
+
side = "bottom",
|
|
105
|
+
...buttonProps
|
|
106
|
+
}: CaptureInstallButtonProps) {
|
|
107
|
+
if (!clipsChromeExtensionEnabled) {
|
|
108
|
+
return (
|
|
109
|
+
<Button asChild className={className} {...buttonProps}>
|
|
110
|
+
<Link to={desktopHref}>{children}</Link>
|
|
111
|
+
</Button>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Popover>
|
|
117
|
+
<PopoverTrigger asChild>
|
|
118
|
+
<Button className={className} {...buttonProps}>
|
|
119
|
+
{children}
|
|
120
|
+
<IconChevronDown className="h-3.5 w-3.5" />
|
|
121
|
+
</Button>
|
|
122
|
+
</PopoverTrigger>
|
|
123
|
+
<PopoverContent align={align} side={side} className="w-80 p-3">
|
|
124
|
+
<InstallOptionsContent desktopHref={desktopHref} />
|
|
125
|
+
</PopoverContent>
|
|
126
|
+
</Popover>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function CaptureInstallInlineLink({
|
|
131
|
+
children,
|
|
132
|
+
className,
|
|
133
|
+
desktopHref = "/download",
|
|
134
|
+
align = "start",
|
|
135
|
+
side = "bottom",
|
|
136
|
+
}: CaptureInstallInlineLinkProps) {
|
|
137
|
+
if (!clipsChromeExtensionEnabled) {
|
|
138
|
+
return (
|
|
139
|
+
<Link to={desktopHref} className={className}>
|
|
140
|
+
{children}
|
|
141
|
+
</Link>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Popover>
|
|
147
|
+
<PopoverTrigger asChild>
|
|
148
|
+
<button type="button" className={cn("cursor-pointer", className)}>
|
|
149
|
+
{children}
|
|
150
|
+
</button>
|
|
151
|
+
</PopoverTrigger>
|
|
152
|
+
<PopoverContent align={align} side={side} className="w-80 p-3">
|
|
153
|
+
<InstallOptionsContent desktopHref={desktopHref} />
|
|
154
|
+
</PopoverContent>
|
|
155
|
+
</Popover>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
@@ -53,6 +53,10 @@ import { FolderTree, type FolderNode } from "./folder-tree";
|
|
|
53
53
|
import { SearchBar } from "./search-bar";
|
|
54
54
|
import { PageHeaderSlotProvider } from "./page-header";
|
|
55
55
|
import { CreateSpaceDialog } from "./create-space-dialog";
|
|
56
|
+
import {
|
|
57
|
+
CaptureInstallButton,
|
|
58
|
+
CaptureInstallInlineLink,
|
|
59
|
+
} from "@/components/capture-install-options";
|
|
56
60
|
import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
|
|
57
61
|
import { toast } from "sonner";
|
|
58
62
|
import {
|
|
@@ -477,13 +481,10 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
477
481
|
<>
|
|
478
482
|
<div className="shrink-0 space-y-1.5 border-t border-border px-2 py-1.5">
|
|
479
483
|
{shouldShowSidebarLink && (
|
|
480
|
-
<
|
|
481
|
-
to="/download"
|
|
482
|
-
className="flex items-center gap-2 rounded px-2 py-1.5 text-xs text-foreground hover:bg-accent/60"
|
|
483
|
-
>
|
|
484
|
+
<CaptureInstallInlineLink className="flex items-center gap-2 rounded px-2 py-1.5 text-xs text-foreground hover:bg-accent/60">
|
|
484
485
|
<IconAppWindow className="h-4 w-4" />
|
|
485
486
|
Get desktop app
|
|
486
|
-
</
|
|
487
|
+
</CaptureInstallInlineLink>
|
|
487
488
|
)}
|
|
488
489
|
<SearchBar />
|
|
489
490
|
</div>
|
|
@@ -532,9 +533,9 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
|
|
|
532
533
|
Record from the menu bar, global shortcut, auto-updates.
|
|
533
534
|
</span>
|
|
534
535
|
</div>
|
|
535
|
-
<
|
|
536
|
-
|
|
537
|
-
</
|
|
536
|
+
<CaptureInstallButton size="sm" className="shrink-0">
|
|
537
|
+
Download
|
|
538
|
+
</CaptureInstallButton>
|
|
538
539
|
<Tooltip>
|
|
539
540
|
<TooltipTrigger asChild>
|
|
540
541
|
<Button
|
|
@@ -18,9 +18,10 @@ import {
|
|
|
18
18
|
IconUpload,
|
|
19
19
|
IconVideo,
|
|
20
20
|
} from "@tabler/icons-react";
|
|
21
|
-
import { agentNativePath
|
|
21
|
+
import { agentNativePath } from "@agent-native/core/client";
|
|
22
22
|
import { Button } from "@/components/ui/button";
|
|
23
23
|
import { Input } from "@/components/ui/input";
|
|
24
|
+
import { CaptureInstallInlineLink } from "@/components/capture-install-options";
|
|
24
25
|
import {
|
|
25
26
|
Popover,
|
|
26
27
|
PopoverContent,
|
|
@@ -724,12 +725,9 @@ export function PreRecordPanel({
|
|
|
724
725
|
{micAccessError ? (
|
|
725
726
|
<p className="text-[11px] leading-snug text-muted-foreground">
|
|
726
727
|
{micAccessError}{" "}
|
|
727
|
-
<
|
|
728
|
-
href={appPath("/download")}
|
|
729
|
-
className="text-foreground underline-offset-4 hover:underline"
|
|
730
|
-
>
|
|
728
|
+
<CaptureInstallInlineLink className="text-foreground underline-offset-4 hover:underline">
|
|
731
729
|
Try Clips Desktop.
|
|
732
|
-
</
|
|
730
|
+
</CaptureInstallInlineLink>
|
|
733
731
|
</p>
|
|
734
732
|
) : null}
|
|
735
733
|
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function isTruthy(value: unknown): boolean {
|
|
2
|
+
if (typeof value !== "string") return false;
|
|
3
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const chromeExtensionUrl =
|
|
7
|
+
import.meta.env.VITE_CLIPS_CHROME_EXTENSION_URL?.trim() ?? "";
|
|
8
|
+
|
|
9
|
+
export const clipsChromeExtensionEnabled = isTruthy(
|
|
10
|
+
import.meta.env.VITE_CLIPS_CHROME_EXTENSION_ENABLED,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export const clipsChromeExtensionUrl = chromeExtensionUrl || null;
|
|
@@ -19,9 +19,18 @@ import {
|
|
|
19
19
|
getThemeInitScript,
|
|
20
20
|
useCommandMenuShortcut,
|
|
21
21
|
} from "@agent-native/core/client";
|
|
22
|
-
import { IconSun, IconMoon } from "@tabler/icons-react";
|
|
22
|
+
import { IconCheck, IconSun, IconMoon } from "@tabler/icons-react";
|
|
23
23
|
import { useTheme } from "next-themes";
|
|
24
24
|
import { Toaster } from "@/components/ui/sonner";
|
|
25
|
+
import { Button } from "@/components/ui/button";
|
|
26
|
+
import {
|
|
27
|
+
Dialog,
|
|
28
|
+
DialogContent,
|
|
29
|
+
DialogDescription,
|
|
30
|
+
DialogFooter,
|
|
31
|
+
DialogHeader,
|
|
32
|
+
DialogTitle,
|
|
33
|
+
} from "@/components/ui/dialog";
|
|
25
34
|
import type { LinksFunction } from "react-router";
|
|
26
35
|
import stylesheet from "./global.css?url";
|
|
27
36
|
import { configureTracking } from "@agent-native/core/client";
|
|
@@ -117,8 +126,30 @@ type ExternalChromeRuntime = {
|
|
|
117
126
|
) => void;
|
|
118
127
|
};
|
|
119
128
|
|
|
129
|
+
const CLIPS_COMMAND_DOCS = [
|
|
130
|
+
{
|
|
131
|
+
title: "Use the Chrome extension for browser logs",
|
|
132
|
+
description:
|
|
133
|
+
"Record a browser tab with redacted console logs, JavaScript exceptions, and fetch/XHR diagnostics.",
|
|
134
|
+
href: "https://www.agent-native.com/docs/template-clips#browser-logs-and-developer-diagnostics",
|
|
135
|
+
keywords: [
|
|
136
|
+
"logs",
|
|
137
|
+
"browser logs",
|
|
138
|
+
"developer logs",
|
|
139
|
+
"console logs",
|
|
140
|
+
"network logs",
|
|
141
|
+
"fetch",
|
|
142
|
+
"xhr",
|
|
143
|
+
"diagnostics",
|
|
144
|
+
"chrome extension",
|
|
145
|
+
"recording",
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
] satisfies React.ComponentProps<typeof CommandMenu.DocsGroup>["docs"];
|
|
149
|
+
|
|
120
150
|
function ClipsExtensionAuthBridge() {
|
|
121
151
|
const location = useLocation();
|
|
152
|
+
const [showAuthSuccess, setShowAuthSuccess] = useState(false);
|
|
122
153
|
|
|
123
154
|
useEffect(() => {
|
|
124
155
|
const params = new URLSearchParams(location.search);
|
|
@@ -163,6 +194,7 @@ function ClipsExtensionAuthBridge() {
|
|
|
163
194
|
cleaned.searchParams.delete("clipsExtensionAuth");
|
|
164
195
|
cleaned.searchParams.delete("clipsExtensionId");
|
|
165
196
|
window.history.replaceState(window.history.state, "", cleaned);
|
|
197
|
+
setShowAuthSuccess(true);
|
|
166
198
|
},
|
|
167
199
|
);
|
|
168
200
|
}
|
|
@@ -173,7 +205,26 @@ function ClipsExtensionAuthBridge() {
|
|
|
173
205
|
};
|
|
174
206
|
}, [location.search]);
|
|
175
207
|
|
|
176
|
-
return
|
|
208
|
+
return (
|
|
209
|
+
<Dialog open={showAuthSuccess} onOpenChange={setShowAuthSuccess}>
|
|
210
|
+
<DialogContent className="max-w-sm text-center sm:text-center">
|
|
211
|
+
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500">
|
|
212
|
+
<IconCheck className="h-9 w-9" strokeWidth={2.5} />
|
|
213
|
+
</div>
|
|
214
|
+
<DialogHeader className="items-center text-center sm:text-center">
|
|
215
|
+
<DialogTitle>Signed in</DialogTitle>
|
|
216
|
+
<DialogDescription className="max-w-xs">
|
|
217
|
+
Open the Clips extension again to start recording.
|
|
218
|
+
</DialogDescription>
|
|
219
|
+
</DialogHeader>
|
|
220
|
+
<DialogFooter className="sm:justify-center">
|
|
221
|
+
<Button type="button" onClick={() => setShowAuthSuccess(false)}>
|
|
222
|
+
Got it
|
|
223
|
+
</Button>
|
|
224
|
+
</DialogFooter>
|
|
225
|
+
</DialogContent>
|
|
226
|
+
</Dialog>
|
|
227
|
+
);
|
|
177
228
|
}
|
|
178
229
|
|
|
179
230
|
/**
|
|
@@ -211,6 +262,7 @@ function AppContent() {
|
|
|
211
262
|
<CommandMenu.Group heading="Actions">
|
|
212
263
|
<CommandMenu.Item onSelect={() => {}}>Search</CommandMenu.Item>
|
|
213
264
|
</CommandMenu.Group>
|
|
265
|
+
<CommandMenu.DocsGroup docs={CLIPS_COMMAND_DOCS} />
|
|
214
266
|
<CommandMenu.Group heading="Appearance">
|
|
215
267
|
<ThemeToggleItem />
|
|
216
268
|
</CommandMenu.Group>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { Link } from "react-router";
|
|
3
2
|
import { toast } from "sonner";
|
|
4
3
|
import {
|
|
5
4
|
IconArrowsExchange,
|
|
@@ -34,6 +33,7 @@ import {
|
|
|
34
33
|
TooltipTrigger,
|
|
35
34
|
} from "@/components/ui/tooltip";
|
|
36
35
|
import { useDesktopPromo } from "@/hooks/use-desktop-promo";
|
|
36
|
+
import { CaptureInstallButton } from "@/components/capture-install-options";
|
|
37
37
|
|
|
38
38
|
export function meta() {
|
|
39
39
|
return [{ title: "Dictate · Clips" }];
|
|
@@ -609,12 +609,10 @@ function EmptyState({ isDesktopApp }: { isDesktopApp: boolean }) {
|
|
|
609
609
|
work in any app — Slack, your editor, anywhere.
|
|
610
610
|
</p>
|
|
611
611
|
<div className="mt-5 flex items-center justify-center">
|
|
612
|
-
<
|
|
613
|
-
<
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
</Link>
|
|
617
|
-
</Button>
|
|
612
|
+
<CaptureInstallButton size="sm" className="gap-1.5">
|
|
613
|
+
<IconDownload className="h-3.5 w-3.5" />
|
|
614
|
+
Download Clips desktop app
|
|
615
|
+
</CaptureInstallButton>
|
|
618
616
|
</div>
|
|
619
617
|
<div className="mt-3 flex items-center justify-center gap-2 text-xs text-muted-foreground">
|
|
620
618
|
<Kbd>Fn</Kbd>
|
|
@@ -646,12 +644,10 @@ function DownloadDesktopAppCard() {
|
|
|
646
644
|
desktop app is the way to use this.
|
|
647
645
|
</p>
|
|
648
646
|
</div>
|
|
649
|
-
<
|
|
650
|
-
<
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
</Link>
|
|
654
|
-
</Button>
|
|
647
|
+
<CaptureInstallButton size="sm" className="gap-1.5">
|
|
648
|
+
<IconDownload className="h-3.5 w-3.5" />
|
|
649
|
+
Download
|
|
650
|
+
</CaptureInstallButton>
|
|
655
651
|
</div>
|
|
656
652
|
);
|
|
657
653
|
}
|
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
TooltipContent,
|
|
62
62
|
TooltipTrigger,
|
|
63
63
|
} from "@/components/ui/tooltip";
|
|
64
|
+
import { CaptureInstallButton } from "@/components/capture-install-options";
|
|
64
65
|
|
|
65
66
|
export function meta() {
|
|
66
67
|
return [{ title: "Meeting · Clips" }];
|
|
@@ -699,17 +700,14 @@ export default function MeetingDetailRoute() {
|
|
|
699
700
|
transcript and AI notes will appear here automatically.
|
|
700
701
|
</span>
|
|
701
702
|
{!isDesktopApp && (
|
|
702
|
-
<
|
|
703
|
-
asChild
|
|
703
|
+
<CaptureInstallButton
|
|
704
704
|
size="sm"
|
|
705
705
|
variant="secondary"
|
|
706
706
|
className="ml-auto h-8 gap-1.5 cursor-pointer"
|
|
707
707
|
>
|
|
708
|
-
<
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
</NavLink>
|
|
712
|
-
</Button>
|
|
708
|
+
<IconExternalLink className="h-3.5 w-3.5" />
|
|
709
|
+
Get desktop app
|
|
710
|
+
</CaptureInstallButton>
|
|
713
711
|
)}
|
|
714
712
|
</div>
|
|
715
713
|
)}
|
|
@@ -45,6 +45,7 @@ import { Input } from "@/components/ui/input";
|
|
|
45
45
|
import { DayHeader, formatDayLabel } from "@/components/meetings/day-header";
|
|
46
46
|
import type { AttendeeStackParticipant } from "@/components/meetings/attendee-stack";
|
|
47
47
|
import { PageHeader } from "@/components/library/page-header";
|
|
48
|
+
import { CaptureInstallButton } from "@/components/capture-install-options";
|
|
48
49
|
|
|
49
50
|
export function meta() {
|
|
50
51
|
return [{ title: "Meetings · Clips" }];
|
|
@@ -668,17 +669,14 @@ function MeetingsHeader({
|
|
|
668
669
|
</div>
|
|
669
670
|
{showDesktopCta && (
|
|
670
671
|
<div className="flex w-fit shrink-0 flex-col items-start gap-1 sm:items-end">
|
|
671
|
-
<
|
|
672
|
-
asChild
|
|
672
|
+
<CaptureInstallButton
|
|
673
673
|
size="sm"
|
|
674
674
|
variant="secondary"
|
|
675
675
|
className="h-8 w-fit gap-1.5 cursor-pointer"
|
|
676
676
|
>
|
|
677
|
-
<
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
</NavLink>
|
|
681
|
-
</Button>
|
|
677
|
+
<IconAppWindow className="h-4 w-4" />
|
|
678
|
+
Get desktop app
|
|
679
|
+
</CaptureInstallButton>
|
|
682
680
|
<p className="max-w-56 text-[11px] leading-snug text-muted-foreground">
|
|
683
681
|
Required for meeting reminders and transcription.
|
|
684
682
|
</p>
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
import {
|
|
3
|
+
IconBrowser,
|
|
3
4
|
IconBrandApple,
|
|
4
5
|
IconBrandWindows,
|
|
6
|
+
IconExternalLink,
|
|
5
7
|
IconPlayerRecord,
|
|
6
8
|
} from "@tabler/icons-react";
|
|
7
9
|
import { appBasePath, appPath } from "@agent-native/core/client";
|
|
8
10
|
import { Button } from "@/components/ui/button";
|
|
9
11
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
12
|
+
import {
|
|
13
|
+
clipsChromeExtensionEnabled,
|
|
14
|
+
clipsChromeExtensionUrl,
|
|
15
|
+
} from "@/lib/capture-install-options";
|
|
10
16
|
|
|
11
17
|
export function meta() {
|
|
12
18
|
return [
|
|
@@ -237,6 +243,49 @@ export default function DownloadPage() {
|
|
|
237
243
|
)}
|
|
238
244
|
</div>
|
|
239
245
|
</div>
|
|
246
|
+
|
|
247
|
+
{clipsChromeExtensionEnabled && (
|
|
248
|
+
<section className="mt-10 w-full max-w-xl rounded-2xl border border-border bg-card p-4 text-left shadow-sm">
|
|
249
|
+
<div className="flex items-start gap-3">
|
|
250
|
+
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary">
|
|
251
|
+
<IconBrowser className="h-4 w-4" />
|
|
252
|
+
</div>
|
|
253
|
+
<div className="min-w-0 flex-1">
|
|
254
|
+
<h2 className="text-sm font-semibold text-foreground">
|
|
255
|
+
Chrome extension for browser logs
|
|
256
|
+
</h2>
|
|
257
|
+
<p className="mt-1 text-sm leading-relaxed text-muted-foreground">
|
|
258
|
+
Use the extension when you want a recording plus redacted
|
|
259
|
+
console and fetch/XHR diagnostics from the tab you launch
|
|
260
|
+
from. The desktop app is still the smoothest everyday
|
|
261
|
+
recorder.
|
|
262
|
+
</p>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
<Button
|
|
266
|
+
asChild={Boolean(clipsChromeExtensionUrl)}
|
|
267
|
+
disabled={!clipsChromeExtensionUrl}
|
|
268
|
+
variant="outline"
|
|
269
|
+
className="mt-4 w-full gap-2"
|
|
270
|
+
>
|
|
271
|
+
{clipsChromeExtensionUrl ? (
|
|
272
|
+
<a
|
|
273
|
+
href={clipsChromeExtensionUrl}
|
|
274
|
+
target="_blank"
|
|
275
|
+
rel="noreferrer"
|
|
276
|
+
>
|
|
277
|
+
<IconExternalLink className="h-4 w-4" />
|
|
278
|
+
Install Chrome extension
|
|
279
|
+
</a>
|
|
280
|
+
) : (
|
|
281
|
+
<>
|
|
282
|
+
<IconExternalLink className="h-4 w-4" />
|
|
283
|
+
Chrome Web Store URL pending
|
|
284
|
+
</>
|
|
285
|
+
)}
|
|
286
|
+
</Button>
|
|
287
|
+
</section>
|
|
288
|
+
)}
|
|
240
289
|
</div>
|
|
241
290
|
</main>
|
|
242
291
|
</div>
|
|
@@ -71,6 +71,7 @@ async function writeAppState(key: string, value: unknown): Promise<void> {
|
|
|
71
71
|
}
|
|
72
72
|
import { Button } from "@/components/ui/button";
|
|
73
73
|
import { Spinner } from "@/components/ui/spinner";
|
|
74
|
+
import { CaptureInstallButton } from "@/components/capture-install-options";
|
|
74
75
|
import { toast } from "sonner";
|
|
75
76
|
|
|
76
77
|
import { PreRecordPanel } from "@/components/recorder/pre-record-panel";
|
|
@@ -542,13 +543,12 @@ function DesktopRecorderCallout() {
|
|
|
542
543
|
</p>
|
|
543
544
|
</div>
|
|
544
545
|
</div>
|
|
545
|
-
<
|
|
546
|
-
asChild
|
|
546
|
+
<CaptureInstallButton
|
|
547
547
|
size="sm"
|
|
548
548
|
className="mt-4 w-full bg-primary text-primary-foreground hover:bg-primary/90"
|
|
549
549
|
>
|
|
550
|
-
|
|
551
|
-
</
|
|
550
|
+
Download desktop app
|
|
551
|
+
</CaptureInstallButton>
|
|
552
552
|
</aside>
|
|
553
553
|
);
|
|
554
554
|
}
|
|
@@ -41,6 +41,7 @@ import { DeleteRecordingMenu } from "@/components/player/delete-recording-menu";
|
|
|
41
41
|
import { usePlayerShortcuts } from "@/hooks/use-player-shortcuts";
|
|
42
42
|
import { useViewTracking } from "@/hooks/use-view-tracking";
|
|
43
43
|
import { Button } from "@/components/ui/button";
|
|
44
|
+
import { CaptureInstallButton } from "@/components/capture-install-options";
|
|
44
45
|
import {
|
|
45
46
|
DropdownMenu,
|
|
46
47
|
DropdownMenuContent,
|
|
@@ -1004,12 +1005,10 @@ function PublicAgentEmptyState() {
|
|
|
1004
1005
|
Loom alternative
|
|
1005
1006
|
</p>
|
|
1006
1007
|
<div className="mt-7 flex w-full max-w-[220px] flex-col gap-2">
|
|
1007
|
-
<
|
|
1008
|
-
<
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
</a>
|
|
1012
|
-
</Button>
|
|
1008
|
+
<CaptureInstallButton className="w-full gap-2" align="center">
|
|
1009
|
+
<IconDownload className="h-4 w-4" />
|
|
1010
|
+
{downloadLabel}
|
|
1011
|
+
</CaptureInstallButton>
|
|
1013
1012
|
<Button asChild variant="outline" className="w-full">
|
|
1014
1013
|
<a href={appPath("/signup")}>Sign up</a>
|
|
1015
1014
|
</Button>
|
|
@@ -39,3 +39,29 @@ Diagnostics are bounded and redacted before they are saved. Clips does not colle
|
|
|
39
39
|
## Review Notes
|
|
40
40
|
|
|
41
41
|
The extension opens `https://clips.agent-native.com/record` by default. Local development can point the settings page to a localhost Clips instance.
|
|
42
|
+
|
|
43
|
+
## Submission Artifact
|
|
44
|
+
|
|
45
|
+
Build the latest Chrome Web Store ZIP from the repo root:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pnpm --filter clips-chrome-extension package
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Upload the generated artifact:
|
|
52
|
+
|
|
53
|
+
```txt
|
|
54
|
+
templates/clips/chrome-extension/releases/clips-chrome-extension-0.1.0.zip
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Web App Rollout Gate
|
|
58
|
+
|
|
59
|
+
Keep the web app's Chrome extension UI hidden until the Web Store listing is
|
|
60
|
+
approved and there is a stable public URL.
|
|
61
|
+
|
|
62
|
+
- `VITE_CLIPS_CHROME_EXTENSION_ENABLED=1` reveals the Chrome option beside
|
|
63
|
+
Clips desktop prompts.
|
|
64
|
+
- `VITE_CLIPS_CHROME_EXTENSION_URL=<chrome-web-store-url>` powers the install
|
|
65
|
+
links.
|
|
66
|
+
- Leave both unset while submitting the draft so production continues to send
|
|
67
|
+
users directly to the desktop app.
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
detectPlatform,
|
|
13
13
|
getEvent,
|
|
14
14
|
pickJoinUrl,
|
|
15
|
-
|
|
15
|
+
resolveGoogleOAuthCredentialCandidates,
|
|
16
|
+
refreshAccessTokenWithFallback,
|
|
16
17
|
type CalendarEvent,
|
|
17
18
|
} from "./google-calendar-client.js";
|
|
18
19
|
|
|
@@ -77,9 +78,8 @@ export function shouldMarkNeedsReauth(message: string): boolean {
|
|
|
77
78
|
export async function resolveCalendarAccessToken(
|
|
78
79
|
account: CalendarAccountForEvents,
|
|
79
80
|
): Promise<string | null> {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
if (!clientId || !clientSecret || !account.ownerEmail) return null;
|
|
81
|
+
const credentialCandidates = resolveGoogleOAuthCredentialCandidates();
|
|
82
|
+
if (!credentialCandidates.length || !account.ownerEmail) return null;
|
|
83
83
|
|
|
84
84
|
let bundle: AccessTokenBundle | null = null;
|
|
85
85
|
if (account.accessTokenSecretRef) {
|
|
@@ -112,10 +112,9 @@ export async function resolveCalendarAccessToken(
|
|
|
112
112
|
|
|
113
113
|
let refreshed;
|
|
114
114
|
try {
|
|
115
|
-
refreshed = await
|
|
115
|
+
refreshed = await refreshAccessTokenWithFallback({
|
|
116
116
|
refreshToken: refreshSecret.value,
|
|
117
|
-
|
|
118
|
-
clientSecret,
|
|
117
|
+
credentials: credentialCandidates,
|
|
119
118
|
});
|
|
120
119
|
} catch {
|
|
121
120
|
// TODO(needs-reauth classification): a transient refresh failure (network
|