@agent-native/core 0.157.0 → 0.157.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
- package/corpus/templates/assets/app/global.css +4 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1 -2
- package/corpus/templates/brain/app/routes/sources.tsx +1 -2
- package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
- package/corpus/templates/calendar/server/handlers/bookings.ts +219 -93
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
- package/corpus/templates/clips/actions/create-folder.ts +7 -1
- package/corpus/templates/clips/actions/create-meeting.ts +6 -2
- package/corpus/templates/clips/actions/create-recording.ts +5 -3
- package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
- package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
- package/corpus/templates/clips/actions/request-recording-access.ts +272 -0
- package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +75 -75
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
- package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -16
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +135 -18
- package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
- package/corpus/templates/clips/desktop/src/app.tsx +24 -0
- package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
- package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
- package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +24 -9
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
- package/corpus/templates/clips/server/db/index.ts +1 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
- package/corpus/templates/clips/server/lib/emails.ts +23 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
- package/corpus/templates/clips/server/lib/recordings.ts +2 -1
- package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
- package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
- package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
- package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +19 -0
- package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
- package/corpus/templates/clips/shared/recording-link.ts +4 -0
- package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +6 -2
- package/corpus/templates/factory/actions/view-screen.ts +15 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
- package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
- package/corpus/templates/factory/app/routes/agents.tsx +9 -0
- package/corpus/templates/mail/server/plugins/auth.ts +0 -2
- package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +71 -22
- package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +13 -0
- package/dist/client/AgentPanel.d.ts +13 -1
- package/dist/client/AgentPanel.js +12 -10
- package/dist/client/AssistantChat.js +25 -5
- package/dist/client/HighlightedCodeBlock.js +12 -2
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/chat/markdown-renderer.js +16 -3
- package/dist/client/chat-first/apps-rail.js +1 -1
- package/dist/client/components/ui/message-scroller.js +1 -1
- package/dist/client/settings/AccountSettingsCard.js +48 -19
- package/dist/client/sharing/ShareButton.d.ts +1 -2
- package/dist/client/sharing/ShareButton.js +5 -41
- package/dist/client/sharing/ShareDialog.js +5 -4
- package/dist/client/use-chat-threads.js +10 -14
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/server/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +0 -2
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.js +18 -0
- package/dist/server/onboarding-html.d.ts +1 -0
- package/dist/server/onboarding-html.js +2 -88
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/shared/markdown-block-split.d.ts +16 -0
- package/dist/shared/markdown-block-split.js +81 -5
- package/dist/styles/agent-native.css +13 -0
- package/dist/voice/voice-cleanup-prompt.js +1 -0
- package/package.json +2 -2
- package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
|
@@ -8,10 +8,6 @@ import {
|
|
|
8
8
|
isLikelyPhoneMicLabel,
|
|
9
9
|
type AudioInputFallback,
|
|
10
10
|
} from "@shared/media-device-selection";
|
|
11
|
-
import {
|
|
12
|
-
createMicAudioCleanup,
|
|
13
|
-
type MicAudioCleanupHandle,
|
|
14
|
-
} from "@shared/mic-audio-cleanup";
|
|
15
11
|
import {
|
|
16
12
|
SCREEN_CAPTURE_FRAME_RATE,
|
|
17
13
|
screenCaptureDisplayOptions,
|
|
@@ -509,7 +505,6 @@ export class RecorderEngine {
|
|
|
509
505
|
private cameraComposite: CameraCompositeHandle | null = null;
|
|
510
506
|
private audioMixCtx: AudioContext | null = null;
|
|
511
507
|
private audioMixSources: MediaStreamAudioSourceNode[] = [];
|
|
512
|
-
private micAudioCleanup: MicAudioCleanupHandle[] = [];
|
|
513
508
|
private recorder: MediaRecorder | null = null;
|
|
514
509
|
private mimeType: string = "video/webm";
|
|
515
510
|
|
|
@@ -1360,7 +1355,6 @@ export class RecorderEngine {
|
|
|
1360
1355
|
// Full stream teardown happens in the finally block below.
|
|
1361
1356
|
this.cameraLive = false;
|
|
1362
1357
|
this.audioMixSources = [];
|
|
1363
|
-
this.stopMicAudioCleanup();
|
|
1364
1358
|
this.audioMixCtx?.close().catch(() => {});
|
|
1365
1359
|
this.audioMixCtx = null;
|
|
1366
1360
|
for (const s of [this.cameraStream, this.rawCameraStream, this.micStream]) {
|
|
@@ -1828,26 +1822,15 @@ export class RecorderEngine {
|
|
|
1828
1822
|
this.audioMixSources = [];
|
|
1829
1823
|
const dest = ctx.createMediaStreamDestination();
|
|
1830
1824
|
for (const input of audioInputs) {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
audioContext: ctx,
|
|
1835
|
-
});
|
|
1836
|
-
this.micAudioCleanup.push(cleanup);
|
|
1837
|
-
track = cleanup.stream.getAudioTracks()[0] ?? track;
|
|
1838
|
-
}
|
|
1839
|
-
const source = ctx.createMediaStreamSource(new MediaStream([track]));
|
|
1825
|
+
const source = ctx.createMediaStreamSource(
|
|
1826
|
+
new MediaStream([input.track]),
|
|
1827
|
+
);
|
|
1840
1828
|
source.connect(dest);
|
|
1841
1829
|
this.audioMixSources.push(source);
|
|
1842
1830
|
}
|
|
1843
1831
|
return dest.stream.getAudioTracks()[0];
|
|
1844
1832
|
}
|
|
1845
1833
|
|
|
1846
|
-
private stopMicAudioCleanup(): void {
|
|
1847
|
-
for (const cleanup of this.micAudioCleanup) cleanup.stop();
|
|
1848
|
-
this.micAudioCleanup = [];
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
1834
|
private buildCombinedStream(): MediaStream {
|
|
1852
1835
|
if (this.opts.mode === "screen") {
|
|
1853
1836
|
return this.buildDisplayRecordingStream();
|
|
@@ -2382,7 +2365,6 @@ export class RecorderEngine {
|
|
|
2382
2365
|
// aren't mistaken for a disconnect.
|
|
2383
2366
|
this.cameraLive = false;
|
|
2384
2367
|
this.audioMixSources = [];
|
|
2385
|
-
this.stopMicAudioCleanup();
|
|
2386
2368
|
this.audioMixCtx?.close().catch(() => {});
|
|
2387
2369
|
this.audioMixCtx = null;
|
|
2388
2370
|
this.cameraComposite?.cleanup();
|
|
@@ -4,9 +4,8 @@ import {
|
|
|
4
4
|
useActionQuery,
|
|
5
5
|
} from "@agent-native/core/client/hooks";
|
|
6
6
|
import { useT } from "@agent-native/core/client/i18n";
|
|
7
|
+
import { ShareCopyRow } from "@agent-native/toolkit/sharing";
|
|
7
8
|
import {
|
|
8
|
-
IconCheck,
|
|
9
|
-
IconLink,
|
|
10
9
|
IconLock,
|
|
11
10
|
IconSend2,
|
|
12
11
|
IconTrash,
|
|
@@ -281,35 +280,19 @@ export function CopyField({
|
|
|
281
280
|
description?: string;
|
|
282
281
|
}) {
|
|
283
282
|
const t = useT();
|
|
284
|
-
const [copied, setCopied] = useState(false);
|
|
285
|
-
const copy = () => {
|
|
286
|
-
if (disabled) return;
|
|
287
|
-
copyToClipboard(value);
|
|
288
|
-
setCopied(true);
|
|
289
|
-
setTimeout(() => setCopied(false), 1400);
|
|
290
|
-
};
|
|
291
283
|
return (
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
onClick={copy}
|
|
305
|
-
disabled={disabled}
|
|
306
|
-
className="h-9"
|
|
307
|
-
>
|
|
308
|
-
{copied ? <IconCheck size={14} /> : <IconLink size={14} />}
|
|
309
|
-
{copied ? t("recordRoute.linkCopied") : t("shareUi.copy")}
|
|
310
|
-
</Button>
|
|
311
|
-
</div>
|
|
312
|
-
</div>
|
|
284
|
+
<ShareCopyRow
|
|
285
|
+
label={label}
|
|
286
|
+
value={value}
|
|
287
|
+
description={description}
|
|
288
|
+
disabled={disabled}
|
|
289
|
+
copyLabel={t("shareUi.copy")}
|
|
290
|
+
copiedLabel={t("recordRoute.linkCopied")}
|
|
291
|
+
onCopy={(nextValue) => {
|
|
292
|
+
copyToClipboard(nextValue);
|
|
293
|
+
return true;
|
|
294
|
+
}}
|
|
295
|
+
/>
|
|
313
296
|
);
|
|
314
297
|
}
|
|
315
298
|
|
|
@@ -256,6 +256,16 @@ const messages = {
|
|
|
256
256
|
passwordProtected: "This clip is password-protected",
|
|
257
257
|
linkExpired: "Link expired",
|
|
258
258
|
linkExpiredMessage: "The creator set an expiry on this share link.",
|
|
259
|
+
privateClip: "Private clip",
|
|
260
|
+
privateClipMessage:
|
|
261
|
+
"This clip is private. Request access and the owner will be notified.",
|
|
262
|
+
privateClipSignedOutMessage:
|
|
263
|
+
"This clip is private. Sign in to request access from the owner.",
|
|
264
|
+
requestAccess: "Request access",
|
|
265
|
+
requestingAccess: "Requesting access...",
|
|
266
|
+
accessRequested: "Access requested",
|
|
267
|
+
accessRequestSent: "The clip owner was notified.",
|
|
268
|
+
accessRequestFailed: "Couldn't request access. Try again.",
|
|
259
269
|
clipUnavailable: "Clip unavailable",
|
|
260
270
|
clipUnavailableMessage:
|
|
261
271
|
"This recording isn't public, or the link is invalid. If it's your clip, sign in to check access.",
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
useActionQuery,
|
|
6
6
|
} from "@agent-native/core/client/hooks";
|
|
7
7
|
import { useT } from "@agent-native/core/client/i18n";
|
|
8
|
+
import { ShareTrigger } from "@agent-native/toolkit/sharing";
|
|
8
9
|
import {
|
|
9
10
|
IconArrowLeft,
|
|
10
11
|
IconCheck,
|
|
@@ -18,7 +19,6 @@ import {
|
|
|
18
19
|
IconNotes,
|
|
19
20
|
IconPlayerStop,
|
|
20
21
|
IconRefresh,
|
|
21
|
-
IconShare3,
|
|
22
22
|
IconTrash,
|
|
23
23
|
IconUsers,
|
|
24
24
|
} from "@tabler/icons-react";
|
|
@@ -711,10 +711,10 @@ export default function MeetingDetailRoute() {
|
|
|
711
711
|
Boolean(data?.transcript?.fullText?.trim()))
|
|
712
712
|
}
|
|
713
713
|
>
|
|
714
|
-
<
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
714
|
+
<ShareTrigger
|
|
715
|
+
label={t("meetingDetail.share")}
|
|
716
|
+
className="shrink-0"
|
|
717
|
+
/>
|
|
718
718
|
</ShareMeetingPopover>
|
|
719
719
|
{canEdit && (
|
|
720
720
|
<AlertDialog open={deleteOpen} onOpenChange={setDeleteOpen}>
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
isHumanReadableDocumentTitle,
|
|
17
17
|
normalizeDocumentTitle,
|
|
18
18
|
} from "@agent-native/core/shared";
|
|
19
|
+
import { ShareTrigger } from "@agent-native/toolkit/sharing";
|
|
19
20
|
import {
|
|
20
21
|
BUILDER_CREDITS_UPGRADE_URL,
|
|
21
22
|
type BuilderCreditsStatus,
|
|
@@ -33,7 +34,6 @@ import {
|
|
|
33
34
|
} from "@shared/share-attribution";
|
|
34
35
|
import type { WorkflowKind } from "@shared/workflow";
|
|
35
36
|
import {
|
|
36
|
-
IconShare3,
|
|
37
37
|
IconArrowLeft,
|
|
38
38
|
IconChevronDown,
|
|
39
39
|
IconCalendar,
|
|
@@ -1002,12 +1002,10 @@ export default function RecordingPage() {
|
|
|
1002
1002
|
hasPassword={Boolean(recording.hasPassword)}
|
|
1003
1003
|
viewerReshareOnly={viewerReshareOnly}
|
|
1004
1004
|
>
|
|
1005
|
-
<
|
|
1006
|
-
{
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
{t("recordingPage.share")}
|
|
1010
|
-
</Button>
|
|
1005
|
+
<ShareTrigger
|
|
1006
|
+
label={t("recordingPage.share")}
|
|
1007
|
+
className="shrink-0"
|
|
1008
|
+
/>
|
|
1011
1009
|
</ShareRecordingPopover>
|
|
1012
1010
|
</header>
|
|
1013
1011
|
|
|
@@ -1570,15 +1568,10 @@ export default function RecordingPage() {
|
|
|
1570
1568
|
hasPassword={Boolean(recording.hasPassword)}
|
|
1571
1569
|
viewerReshareOnly={viewerReshareOnly}
|
|
1572
1570
|
>
|
|
1573
|
-
<
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
{recording.visibility !== "public" ? (
|
|
1578
|
-
<IconShare3 className="h-4 w-4" />
|
|
1579
|
-
) : null}
|
|
1580
|
-
{t("recordingPage.share")}
|
|
1581
|
-
</Button>
|
|
1571
|
+
<ShareTrigger
|
|
1572
|
+
label={t("recordingPage.share")}
|
|
1573
|
+
className="shrink-0"
|
|
1574
|
+
/>
|
|
1582
1575
|
</ShareRecordingPopover>
|
|
1583
1576
|
) : null}
|
|
1584
1577
|
|
|
@@ -5,10 +5,15 @@ import {
|
|
|
5
5
|
appBasePath,
|
|
6
6
|
appPath,
|
|
7
7
|
} from "@agent-native/core/client/api-path";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
useActionMutation,
|
|
10
|
+
useSession,
|
|
11
|
+
getBrowserTabId,
|
|
12
|
+
} from "@agent-native/core/client/hooks";
|
|
9
13
|
import { useT } from "@agent-native/core/client/i18n";
|
|
10
14
|
import { buildSignInReturnHref } from "@agent-native/core/client/ui";
|
|
11
15
|
import { docsUrl } from "@agent-native/core/shared";
|
|
16
|
+
import { ShareTrigger } from "@agent-native/toolkit/sharing";
|
|
12
17
|
import {
|
|
13
18
|
IconAlertTriangle,
|
|
14
19
|
IconArrowLeft,
|
|
@@ -16,8 +21,8 @@ import {
|
|
|
16
21
|
IconDownload,
|
|
17
22
|
IconDots,
|
|
18
23
|
IconExternalLink,
|
|
24
|
+
IconLock,
|
|
19
25
|
IconLogin2,
|
|
20
|
-
IconShare3,
|
|
21
26
|
} from "@tabler/icons-react";
|
|
22
27
|
import { useQuery } from "@tanstack/react-query";
|
|
23
28
|
import { eq } from "drizzle-orm";
|
|
@@ -87,6 +92,10 @@ import {
|
|
|
87
92
|
isLoomEmbedBackedRecording,
|
|
88
93
|
isLoomRecordingSource,
|
|
89
94
|
} from "../../shared/loom";
|
|
95
|
+
import {
|
|
96
|
+
CLIPS_ACCESS_REQUEST_TOKEN_PREFIX,
|
|
97
|
+
CLIPS_ACCESS_REQUEST_TOKEN_TTL_SECONDS,
|
|
98
|
+
} from "../../shared/recording-link";
|
|
90
99
|
import {
|
|
91
100
|
buildSignupAttributionQuery,
|
|
92
101
|
readShareAttribution,
|
|
@@ -116,16 +125,22 @@ type SharePageLoaderData = {
|
|
|
116
125
|
agentContextUrl: string | null;
|
|
117
126
|
origin: string | null;
|
|
118
127
|
shareUrl: string | null;
|
|
128
|
+
accessDeniedStatus?: 401 | 403;
|
|
129
|
+
accessRequestToken?: string;
|
|
119
130
|
};
|
|
120
131
|
|
|
121
132
|
const CLIPS_AGENT_ACCESS_TTL_SECONDS = 2 * 60 * 60;
|
|
122
133
|
|
|
123
|
-
function emptyLoaderData(
|
|
134
|
+
function emptyLoaderData(
|
|
135
|
+
url: URL,
|
|
136
|
+
accessDeniedStatus?: 401 | 403,
|
|
137
|
+
): SharePageLoaderData {
|
|
124
138
|
return {
|
|
125
139
|
recording: null,
|
|
126
140
|
agentContextUrl: null,
|
|
127
141
|
origin: url.origin,
|
|
128
142
|
shareUrl: null,
|
|
143
|
+
accessDeniedStatus,
|
|
129
144
|
};
|
|
130
145
|
}
|
|
131
146
|
|
|
@@ -224,7 +239,19 @@ export async function loader({ params, url }: LoaderFunctionArgs) {
|
|
|
224
239
|
if (rec.visibility !== "public" && !tokenGrantsAgentAccess) {
|
|
225
240
|
const userEmail = getRequestUserEmail();
|
|
226
241
|
const access = userEmail ? await resolveAccess("recording", id) : null;
|
|
227
|
-
if (!access)
|
|
242
|
+
if (!access) {
|
|
243
|
+
const status = userEmail ? 403 : 401;
|
|
244
|
+
const deniedData = emptyLoaderData(url, status);
|
|
245
|
+
if (userEmail) {
|
|
246
|
+
deniedData.accessRequestToken = signScopedAgentAccessToken({
|
|
247
|
+
resourceKind: CLIPS_ACCESS_REQUEST_TOKEN_PREFIX,
|
|
248
|
+
resourceId: id,
|
|
249
|
+
viewerEmail: userEmail,
|
|
250
|
+
ttlSeconds: CLIPS_ACCESS_REQUEST_TOKEN_TTL_SECONDS,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return privateShareLoaderData(deniedData, status);
|
|
254
|
+
}
|
|
228
255
|
}
|
|
229
256
|
|
|
230
257
|
const recording: SharePageMetaRecording = {
|
|
@@ -418,6 +445,16 @@ export default function ShareRoute() {
|
|
|
418
445
|
const [pwError, setPwError] = useState<string | null>(null);
|
|
419
446
|
const [currentMs, setCurrentMs] = useState(0);
|
|
420
447
|
const { session, isLoading: sessionLoading } = useSession();
|
|
448
|
+
const requestAccess = useActionMutation<
|
|
449
|
+
{
|
|
450
|
+
alreadyHasAccess: boolean;
|
|
451
|
+
alreadyRequested?: boolean;
|
|
452
|
+
message: string;
|
|
453
|
+
notifiedOwner: boolean;
|
|
454
|
+
ok: true;
|
|
455
|
+
},
|
|
456
|
+
{ accessRequestToken?: string; recordingId: string }
|
|
457
|
+
>("request-recording-access");
|
|
421
458
|
const [signInIntent, setSignInIntent] = useState<"comment" | "react" | null>(
|
|
422
459
|
null,
|
|
423
460
|
);
|
|
@@ -429,6 +466,7 @@ export default function ShareRoute() {
|
|
|
429
466
|
);
|
|
430
467
|
const [downloading, setDownloading] = useState(false);
|
|
431
468
|
const [downloadMenuOpen, setDownloadMenuOpen] = useState(false);
|
|
469
|
+
const [accessRequestSent, setAccessRequestSent] = useState(false);
|
|
432
470
|
const agentAccessToken = useMemo(() => {
|
|
433
471
|
if (typeof window === "undefined") return "";
|
|
434
472
|
return (
|
|
@@ -518,6 +556,16 @@ export default function ShareRoute() {
|
|
|
518
556
|
const transcriptFailureReason =
|
|
519
557
|
dataQ.data?.data?.transcript?.failureReason ?? null;
|
|
520
558
|
const ctas = dataQ.data?.data?.ctas ?? [];
|
|
559
|
+
const apiStatus = dataQ.data?.status;
|
|
560
|
+
const apiAccessDeniedStatus =
|
|
561
|
+
apiStatus === 401 || apiStatus === 403 ? apiStatus : null;
|
|
562
|
+
const accessDeniedStatus =
|
|
563
|
+
apiAccessDeniedStatus ??
|
|
564
|
+
(!recording && loaderData.accessDeniedStatus
|
|
565
|
+
? session
|
|
566
|
+
? 403
|
|
567
|
+
: 401
|
|
568
|
+
: null);
|
|
521
569
|
const firstCta = ctas[0] ?? null;
|
|
522
570
|
const viewerRole = dataQ.data?.data?.viewer?.role as
|
|
523
571
|
| "owner"
|
|
@@ -751,18 +799,58 @@ export default function ShareRoute() {
|
|
|
751
799
|
);
|
|
752
800
|
}
|
|
753
801
|
|
|
754
|
-
if (
|
|
802
|
+
if (accessDeniedStatus === 401 || accessDeniedStatus === 403) {
|
|
803
|
+
const canRequestAccess = accessDeniedStatus === 403 && Boolean(session);
|
|
804
|
+
const requestSent =
|
|
805
|
+
accessRequestSent || Boolean(dataQ.data?.data?.alreadyRequested);
|
|
806
|
+
|
|
755
807
|
return (
|
|
756
808
|
<>
|
|
757
809
|
{agentDiscovery}
|
|
758
810
|
<EndState
|
|
759
|
-
|
|
760
|
-
|
|
811
|
+
icon={<IconLock className="h-5 w-5" aria-hidden="true" />}
|
|
812
|
+
title={t("sharePage.privateClip")}
|
|
813
|
+
message={t(
|
|
814
|
+
canRequestAccess
|
|
815
|
+
? "sharePage.privateClipMessage"
|
|
816
|
+
: "sharePage.privateClipSignedOutMessage",
|
|
817
|
+
)}
|
|
761
818
|
action={
|
|
762
|
-
|
|
819
|
+
canRequestAccess ? (
|
|
820
|
+
<Button
|
|
821
|
+
size="sm"
|
|
822
|
+
disabled={requestAccess.isPending || requestSent}
|
|
823
|
+
onClick={() => {
|
|
824
|
+
if (!shareId || requestSent) return;
|
|
825
|
+
requestAccess.mutate(
|
|
826
|
+
{
|
|
827
|
+
accessRequestToken: loaderData.accessRequestToken,
|
|
828
|
+
recordingId: shareId,
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
onSuccess: () => {
|
|
832
|
+
setAccessRequestSent(true);
|
|
833
|
+
toast.success(t("sharePage.accessRequestSent"));
|
|
834
|
+
},
|
|
835
|
+
onError: () => {
|
|
836
|
+
toast.error(t("sharePage.accessRequestFailed"));
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
);
|
|
840
|
+
}}
|
|
841
|
+
>
|
|
842
|
+
{requestSent
|
|
843
|
+
? t("sharePage.accessRequested")
|
|
844
|
+
: requestAccess.isPending
|
|
845
|
+
? t("sharePage.requestingAccess")
|
|
846
|
+
: t("sharePage.requestAccess")}
|
|
847
|
+
</Button>
|
|
848
|
+
) : shareId ? (
|
|
763
849
|
<Button asChild size="sm">
|
|
764
850
|
<a
|
|
765
|
-
href={buildSignInReturnHref({
|
|
851
|
+
href={buildSignInReturnHref({
|
|
852
|
+
returnTo: `/share/${shareId}`,
|
|
853
|
+
})}
|
|
766
854
|
className="gap-1.5"
|
|
767
855
|
>
|
|
768
856
|
<IconLogin2 className="h-4 w-4 rtl:-scale-x-100" />
|
|
@@ -776,6 +864,18 @@ export default function ShareRoute() {
|
|
|
776
864
|
);
|
|
777
865
|
}
|
|
778
866
|
|
|
867
|
+
if (dataQ.data?.status === 404) {
|
|
868
|
+
return (
|
|
869
|
+
<>
|
|
870
|
+
{agentDiscovery}
|
|
871
|
+
<EndState
|
|
872
|
+
title={t("sharePage.clipUnavailable")}
|
|
873
|
+
message={t("sharePage.clipUnavailableMessage")}
|
|
874
|
+
/>
|
|
875
|
+
</>
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
|
|
779
879
|
if (!recording) {
|
|
780
880
|
return (
|
|
781
881
|
<>
|
|
@@ -930,14 +1030,26 @@ export default function ShareRoute() {
|
|
|
930
1030
|
</Link>
|
|
931
1031
|
</Button>
|
|
932
1032
|
) : null}
|
|
933
|
-
<div className="min-w-0 flex-1">
|
|
1033
|
+
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
1034
|
+
{recording.visibility === "private" ? (
|
|
1035
|
+
<span
|
|
1036
|
+
className="inline-flex shrink-0 text-muted-foreground"
|
|
1037
|
+
role="img"
|
|
1038
|
+
title={t("sharePage.privateClip")}
|
|
1039
|
+
>
|
|
1040
|
+
<IconLock className="h-3.5 w-3.5" aria-hidden="true" />
|
|
1041
|
+
<span className="sr-only">{t("sharePage.privateClip")}</span>
|
|
1042
|
+
</span>
|
|
1043
|
+
) : null}
|
|
934
1044
|
{showTitleSkeleton ? (
|
|
935
1045
|
<Skeleton
|
|
936
1046
|
aria-label={t("sharePage.generatingTitle")}
|
|
937
|
-
className="h-4 w-56 max-w-full"
|
|
1047
|
+
className="h-4 w-56 max-w-full flex-1"
|
|
938
1048
|
/>
|
|
939
1049
|
) : (
|
|
940
|
-
<h1 className="truncate text-sm font-medium">
|
|
1050
|
+
<h1 className="min-w-0 flex-1 truncate text-sm font-medium">
|
|
1051
|
+
{visibleTitle}
|
|
1052
|
+
</h1>
|
|
941
1053
|
)}
|
|
942
1054
|
</div>
|
|
943
1055
|
|
|
@@ -1021,12 +1133,10 @@ export default function ShareRoute() {
|
|
|
1021
1133
|
hasPassword={Boolean(recording.hasPassword)}
|
|
1022
1134
|
viewerReshareOnly={viewerReshareOnly}
|
|
1023
1135
|
>
|
|
1024
|
-
<
|
|
1025
|
-
{
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
{t("sharePage.share")}
|
|
1029
|
-
</Button>
|
|
1136
|
+
<ShareTrigger
|
|
1137
|
+
label={t("sharePage.share")}
|
|
1138
|
+
className="shrink-0"
|
|
1139
|
+
/>
|
|
1030
1140
|
</ShareRecordingPopover>
|
|
1031
1141
|
) : null}
|
|
1032
1142
|
</div>
|
|
@@ -1351,10 +1461,12 @@ function PublicAgentEmptyState({
|
|
|
1351
1461
|
}
|
|
1352
1462
|
|
|
1353
1463
|
function EndState({
|
|
1464
|
+
icon,
|
|
1354
1465
|
title,
|
|
1355
1466
|
message,
|
|
1356
1467
|
action,
|
|
1357
1468
|
}: {
|
|
1469
|
+
icon?: ReactNode;
|
|
1358
1470
|
title: string;
|
|
1359
1471
|
message: string;
|
|
1360
1472
|
action?: ReactNode;
|
|
@@ -1363,6 +1475,11 @@ function EndState({
|
|
|
1363
1475
|
|
|
1364
1476
|
return (
|
|
1365
1477
|
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-foreground px-6">
|
|
1478
|
+
{icon ? (
|
|
1479
|
+
<div className="mb-3 flex h-10 w-10 items-center justify-center rounded-full border border-border bg-muted text-muted-foreground">
|
|
1480
|
+
{icon}
|
|
1481
|
+
</div>
|
|
1482
|
+
) : null}
|
|
1366
1483
|
<h1 className="text-2xl font-semibold mb-2">{title}</h1>
|
|
1367
1484
|
<p className="mb-6 max-w-md text-center text-sm text-muted-foreground">
|
|
1368
1485
|
{message}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Agent-Native Clips",
|
|
4
4
|
"short_name": "Clips",
|
|
5
5
|
"version": "0.1.18",
|
|
6
|
-
"description": "Start Clips recordings from Chrome, capture optional diagnostics, and preview Clips links on GitHub.",
|
|
6
|
+
"description": "Start Clips recordings from Chrome, capture optional diagnostics, and preview Clips links on GitHub and Jira.",
|
|
7
7
|
"minimum_chrome_version": "116",
|
|
8
8
|
"action": {
|
|
9
9
|
"default_title": "Record with Clips",
|
|
@@ -35,7 +35,11 @@
|
|
|
35
35
|
],
|
|
36
36
|
"content_scripts": [
|
|
37
37
|
{
|
|
38
|
-
"matches": [
|
|
38
|
+
"matches": [
|
|
39
|
+
"https://github.com/*",
|
|
40
|
+
"https://*.atlassian.net/*",
|
|
41
|
+
"https://*.jira.com/*"
|
|
42
|
+
],
|
|
39
43
|
"js": ["assets/github-preview-content.js"],
|
|
40
44
|
"run_at": "document_idle",
|
|
41
45
|
"all_frames": false
|
|
@@ -48,7 +52,11 @@
|
|
|
48
52
|
},
|
|
49
53
|
{
|
|
50
54
|
"resources": ["src/github-preview.html"],
|
|
51
|
-
"matches": [
|
|
55
|
+
"matches": [
|
|
56
|
+
"https://github.com/*",
|
|
57
|
+
"https://*.atlassian.net/*",
|
|
58
|
+
"https://*.jira.com/*"
|
|
59
|
+
]
|
|
52
60
|
}
|
|
53
61
|
],
|
|
54
62
|
"externally_connectable": {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
(function
|
|
4
|
-
const ROOT_CLASS = "clips-
|
|
5
|
-
const PROCESSED_ATTR = "data-clips-
|
|
1
|
+
// Clips link previews for GitHub and Jira Cloud. The recording overlay remains
|
|
2
|
+
// activeTab-injected so this content script only handles rendered issue text.
|
|
3
|
+
(function clipsLinkPreviewContent() {
|
|
4
|
+
const ROOT_CLASS = "clips-link-preview";
|
|
5
|
+
const PROCESSED_ATTR = "data-clips-link-preview";
|
|
6
6
|
const SOURCE_ATTR = "data-clips-source-url";
|
|
7
7
|
const EXTENSION_ORIGIN = chrome.runtime.getURL("").replace(/\/$/, "");
|
|
8
8
|
const CLIPS_HOSTS = new Set(["clips.agent-native.com"]);
|
|
9
9
|
const LOCAL_HOSTS = new Set(["localhost", "127.0.0.1"]);
|
|
10
10
|
const ROUTE_SEGMENTS = new Set(["r", "share", "embed"]);
|
|
11
|
+
const JIRA_HOST_SUFFIXES = [".atlassian.net", ".jira.com"];
|
|
11
12
|
const MIN_HEIGHT = 120;
|
|
12
13
|
const MAX_HEIGHT = 720;
|
|
13
14
|
|
|
@@ -115,6 +116,34 @@
|
|
|
115
116
|
);
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
function isJiraPage(): boolean {
|
|
120
|
+
const hostname = location.hostname.toLowerCase();
|
|
121
|
+
return JIRA_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isJiraIssueContentAnchor(anchor: HTMLAnchorElement): boolean {
|
|
125
|
+
if (anchor.closest(`.${ROOT_CLASS}`)) return false;
|
|
126
|
+
return Boolean(
|
|
127
|
+
anchor.closest(
|
|
128
|
+
[
|
|
129
|
+
'[data-testid="issue.views.issue-base.foundation.description.text"]',
|
|
130
|
+
'[data-testid^="issue.views.issue-base.foundation.description"]',
|
|
131
|
+
'[data-testid^="issue.views.issue-base.common.comment"]',
|
|
132
|
+
'[data-testid^="issue.views.issue-base.common.comments"]',
|
|
133
|
+
'[data-testid*="comment"]',
|
|
134
|
+
".ak-renderer-document",
|
|
135
|
+
".ak-renderer-document-container",
|
|
136
|
+
].join(","),
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isSupportedContentAnchor(anchor: HTMLAnchorElement): boolean {
|
|
142
|
+
return isJiraPage()
|
|
143
|
+
? isJiraIssueContentAnchor(anchor)
|
|
144
|
+
: isGitHubMarkdownAnchor(anchor);
|
|
145
|
+
}
|
|
146
|
+
|
|
118
147
|
function previewInsertionTarget(anchor: HTMLAnchorElement): HTMLElement {
|
|
119
148
|
const block = anchor.closest(
|
|
120
149
|
[
|
|
@@ -145,7 +174,7 @@
|
|
|
145
174
|
return;
|
|
146
175
|
}
|
|
147
176
|
|
|
148
|
-
const frameId = `clips-
|
|
177
|
+
const frameId = `clips-link-preview-${++frameCounter}`;
|
|
149
178
|
const wrapper = document.createElement("div");
|
|
150
179
|
wrapper.className = ROOT_CLASS;
|
|
151
180
|
wrapper.id = `${frameId}-wrapper`;
|
|
@@ -195,7 +224,7 @@
|
|
|
195
224
|
for (const anchor of document.querySelectorAll<HTMLAnchorElement>(
|
|
196
225
|
`a[href]:not([${PROCESSED_ATTR}])`,
|
|
197
226
|
)) {
|
|
198
|
-
if (!
|
|
227
|
+
if (!isSupportedContentAnchor(anchor)) continue;
|
|
199
228
|
const link = parseClipsLink(anchor.href);
|
|
200
229
|
if (!link) continue;
|
|
201
230
|
mountPreview(anchor, link);
|
|
@@ -212,7 +241,7 @@
|
|
|
212
241
|
const data = event.data as
|
|
213
242
|
| { source?: string; frameId?: string; height?: number }
|
|
214
243
|
| undefined;
|
|
215
|
-
if (data?.source !== "clips-
|
|
244
|
+
if (data?.source !== "clips-link-preview") return;
|
|
216
245
|
if (!data.frameId || typeof data.height !== "number") return;
|
|
217
246
|
const frame = document.getElementById(data.frameId);
|
|
218
247
|
if (!(frame instanceof HTMLIFrameElement)) return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { captureExtensionError, initExtensionSentry } from "./sentry";
|
|
2
2
|
|
|
3
|
-
initExtensionSentry("
|
|
3
|
+
initExtensionSentry("link-preview");
|
|
4
4
|
|
|
5
5
|
const DEFAULT_HOST = "clips.agent-native.com";
|
|
6
6
|
const CLIPS_HOSTS = new Set(["clips.agent-native.com"]);
|
|
@@ -80,7 +80,7 @@ function postHeight(frameId: string): void {
|
|
|
80
80
|
const height = document.documentElement.scrollHeight;
|
|
81
81
|
window.parent.postMessage(
|
|
82
82
|
{
|
|
83
|
-
source: "clips-
|
|
83
|
+
source: "clips-link-preview",
|
|
84
84
|
frameId,
|
|
85
85
|
height,
|
|
86
86
|
},
|
|
@@ -415,7 +415,7 @@ async function main(): Promise<void> {
|
|
|
415
415
|
renderPreview(root, preview, await checkPublicPlayable(preview));
|
|
416
416
|
} catch (error) {
|
|
417
417
|
captureExtensionError(error, {
|
|
418
|
-
tags: { surface: "
|
|
418
|
+
tags: { surface: "link-preview" },
|
|
419
419
|
extra: { pageUrl: location.href },
|
|
420
420
|
});
|
|
421
421
|
}
|