@agent-native/core 0.102.1 → 0.102.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +9 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +7 -0
- package/corpus/core/src/client/AssistantChat.tsx +7 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +7 -1
- package/corpus/core/src/client/chat/message-components.tsx +17 -3
- package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +96 -6
- package/corpus/core/src/client/composer/asset-picker-url.ts +80 -0
- package/corpus/core/src/client/error-format.ts +24 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +8 -1
- package/corpus/templates/assets/app/routes/library.tsx +69 -0
- package/corpus/templates/assets/changelog/2026-07-15-fixed-top-level-image-picker-selections-returning-to-the-com.md +6 -0
- package/corpus/templates/clips/actions/disconnect-calendar.ts +57 -9
- package/corpus/templates/clips/actions/generate-workflow.ts +102 -59
- package/corpus/templates/clips/actions/get-recording-player-data.ts +64 -1
- package/corpus/templates/clips/actions/regenerate-summary.ts +7 -0
- package/corpus/templates/clips/actions/regenerate-title.ts +29 -11
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +22 -11
- package/corpus/templates/clips/app/components/editor/thumbnail-picker.tsx +87 -9
- package/corpus/templates/clips/app/components/editor/trim-handles.tsx +15 -0
- package/corpus/templates/clips/app/components/editor/waveform.tsx +21 -8
- package/corpus/templates/clips/app/components/library/folder-tree.tsx +52 -0
- package/corpus/templates/clips/app/components/player/settings-panel.tsx +86 -46
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +43 -37
- package/corpus/templates/clips/app/components/player/video-player.tsx +45 -1
- package/corpus/templates/clips/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +2 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/clips/app/lib/timestamp-mapping.ts +14 -0
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -2
- package/corpus/templates/clips/app/routes/_index.tsx +1 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +71 -31
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +4 -1
- package/corpus/templates/clips/changelog/2026-07-15-ai-workflow-generation-now-stays-visible-avoids-duplicate-re.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-clip-playback-and-cta-controls-remain-interactive-through-ed.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-clip-titles-no-longer-describe-the-product-as-a-loom-alterna.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-description-regeneration-now-opens-the-agent-chat-so-progres.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-direct-recording-urls-now-respect-share-link-and-password-ac.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-disconnecting-a-calendar-now-clears-unrecorded-synced-meetin.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-folders-now-expose-rename-and-delete-actions-directly-in-the.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-recording-pages-now-explain-sign-in-access-and-avoid-showing.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-the-clips-extension-quick-actions-toolbar-can-be-moved-out-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-the-editor-now-skips-cut-sections-during-preview-and-keeps-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-thumbnail-capture-now-updates-previews-while-scrubbing-and-l.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-windows-permission-links-open-the-matching-settings-page.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +117 -2
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +27 -0
- package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -1
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +139 -101
- package/corpus/templates/clips/server/lib/recording-page-access.ts +35 -0
- package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +7 -3
- package/corpus/templates/design/changelog/2026-07-15-prototype-print-and-download-buttons-now-work-in-preview.md +6 -0
- package/corpus/templates/slides/actions/extract-pdf.ts +1 -1
- package/corpus/templates/slides/actions/import-file.ts +4 -1
- package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/ImageSearchPanel.tsx +9 -12
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +29 -21
- package/corpus/templates/slides/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/slides/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/slides/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/slides/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/slides/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/slides/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/slides/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/slides/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/slides/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/slides/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/slides/changelog/2026-07-15-image-search-and-generation-controls-now-use-the-registered-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-15-pdf-deck-references-can-be-imported-for-slide-inspiration-ag.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-15-stable-style-inspector-dock.md +6 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -0
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +7 -0
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +8 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +4 -0
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +10 -3
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.js +58 -6
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/composer/asset-picker-url.d.ts +16 -0
- package/dist/client/composer/asset-picker-url.d.ts.map +1 -0
- package/dist/client/composer/asset-picker-url.js +64 -0
- package/dist/client/composer/asset-picker-url.js.map +1 -0
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +17 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +6 -1
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -205,6 +205,10 @@ function parseTimeParam(raw: string | null): number {
|
|
|
205
205
|
return (hours * 3600 + minutes * 60 + seconds) * 1000;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
function buildSignInHref(returnTo: string): string {
|
|
209
|
+
return `${agentNativePath("/_agent-native/sign-in")}?return=${encodeURIComponent(returnTo)}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
208
212
|
export default function RecordingPage() {
|
|
209
213
|
const t = useT();
|
|
210
214
|
useAutoTitleBridge();
|
|
@@ -256,6 +260,7 @@ export default function RecordingPage() {
|
|
|
256
260
|
);
|
|
257
261
|
const lastPlayerStateWriteRef = useRef(0);
|
|
258
262
|
const readyMediaPollRef = useRef<{ key: string; until: number } | null>(null);
|
|
263
|
+
const [metadataRefreshUntil, setMetadataRefreshUntil] = useState(0);
|
|
259
264
|
|
|
260
265
|
useEffect(() => {
|
|
261
266
|
if (
|
|
@@ -325,6 +330,7 @@ export default function RecordingPage() {
|
|
|
325
330
|
// `update-recording` and we want the skeleton to swap in promptly.
|
|
326
331
|
if (shouldShowGeneratedTitleSkeleton(rec, data?.transcript?.status))
|
|
327
332
|
return 3000;
|
|
333
|
+
if (Date.now() < metadataRefreshUntil) return 2000;
|
|
328
334
|
return false;
|
|
329
335
|
},
|
|
330
336
|
},
|
|
@@ -542,6 +548,8 @@ export default function RecordingPage() {
|
|
|
542
548
|
});
|
|
543
549
|
const regenerateTitle = useActionMutation("regenerate-title" as any, {
|
|
544
550
|
onSuccess: (result: any) => {
|
|
551
|
+
setMetadataRefreshUntil(Date.now() + 60_000);
|
|
552
|
+
void playerDataQ.refetch();
|
|
545
553
|
if (result?.updated) {
|
|
546
554
|
toast.success(t("recordingPage.titleUpdated"));
|
|
547
555
|
} else if (result?.reason === "builder_credits_paused") {
|
|
@@ -559,7 +567,11 @@ export default function RecordingPage() {
|
|
|
559
567
|
onError: handleAiError,
|
|
560
568
|
});
|
|
561
569
|
const regenerateSummary = useActionMutation("regenerate-summary" as any, {
|
|
562
|
-
onSuccess: () =>
|
|
570
|
+
onSuccess: () => {
|
|
571
|
+
setMetadataRefreshUntil(Date.now() + 60_000);
|
|
572
|
+
void playerDataQ.refetch();
|
|
573
|
+
toast.success(t("recordingPage.descriptionQueued"));
|
|
574
|
+
},
|
|
563
575
|
onError: handleAiError,
|
|
564
576
|
});
|
|
565
577
|
const regenerateChapters = useActionMutation("regenerate-chapters" as any, {
|
|
@@ -614,17 +626,22 @@ export default function RecordingPage() {
|
|
|
614
626
|
}
|
|
615
627
|
function handleGenerateWorkflow(kind: WorkflowKind) {
|
|
616
628
|
if (!recording) return;
|
|
629
|
+
if (generatedWorkflow?.status === "generating") return;
|
|
617
630
|
setEditing(false);
|
|
618
631
|
setPanel("agent");
|
|
619
632
|
window.dispatchEvent(
|
|
620
633
|
new CustomEvent("agent-panel:set-mode", { detail: { mode: "chat" } }),
|
|
621
634
|
);
|
|
635
|
+
window.dispatchEvent(new Event("agent-panel:open"));
|
|
622
636
|
generateWorkflow.mutate({
|
|
623
637
|
recordingId: recording.id,
|
|
624
638
|
kind,
|
|
625
639
|
} as any);
|
|
626
640
|
}
|
|
627
641
|
|
|
642
|
+
const workflowBusy =
|
|
643
|
+
generateWorkflow.isPending || generatedWorkflow?.status === "generating";
|
|
644
|
+
|
|
628
645
|
useEffect(() => {
|
|
629
646
|
if (recording && panel === "settings" && !canEdit) setPanel("agent");
|
|
630
647
|
}, [canEdit, panel, recording]);
|
|
@@ -709,18 +726,36 @@ export default function RecordingPage() {
|
|
|
709
726
|
}
|
|
710
727
|
|
|
711
728
|
if (playerDataQ.isError || !recording) {
|
|
729
|
+
const needsSignIn = !session;
|
|
730
|
+
const returnTo =
|
|
731
|
+
typeof window === "undefined"
|
|
732
|
+
? `/r/${recordingId}`
|
|
733
|
+
: window.location.pathname + window.location.search;
|
|
712
734
|
return (
|
|
713
735
|
<div className="flex flex-col items-center justify-center h-screen w-full bg-background px-6">
|
|
714
736
|
<h1 className="text-xl font-semibold mb-2">
|
|
715
|
-
{
|
|
737
|
+
{needsSignIn
|
|
738
|
+
? t("sharePage.clipUnavailable")
|
|
739
|
+
: t("recordingPage.recordingNotFound")}
|
|
716
740
|
</h1>
|
|
717
741
|
<p className="text-sm text-muted-foreground mb-4">
|
|
718
|
-
{
|
|
719
|
-
t("
|
|
742
|
+
{needsSignIn
|
|
743
|
+
? t("sharePage.clipUnavailableMessage")
|
|
744
|
+
: ((playerDataQ.error as Error | undefined)?.message ??
|
|
745
|
+
t("recordingPage.noAccess"))}
|
|
720
746
|
</p>
|
|
721
|
-
<
|
|
722
|
-
|
|
723
|
-
|
|
747
|
+
<div className="flex flex-wrap items-center justify-center gap-2">
|
|
748
|
+
{needsSignIn ? (
|
|
749
|
+
<Button asChild>
|
|
750
|
+
<a href={buildSignInHref(returnTo)}>{t("sharePage.signIn")}</a>
|
|
751
|
+
</Button>
|
|
752
|
+
) : null}
|
|
753
|
+
<Button asChild variant="outline">
|
|
754
|
+
<Link to="/library" replace>
|
|
755
|
+
{t("recordingPage.backToLibrary")}
|
|
756
|
+
</Link>
|
|
757
|
+
</Button>
|
|
758
|
+
</div>
|
|
724
759
|
</div>
|
|
725
760
|
);
|
|
726
761
|
}
|
|
@@ -784,7 +819,7 @@ export default function RecordingPage() {
|
|
|
784
819
|
className="shrink-0"
|
|
785
820
|
aria-label={t("recordingPage.back")}
|
|
786
821
|
>
|
|
787
|
-
<Link to="/">
|
|
822
|
+
<Link to="/library" replace>
|
|
788
823
|
<IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
|
|
789
824
|
</Link>
|
|
790
825
|
</Button>
|
|
@@ -956,7 +991,9 @@ export default function RecordingPage() {
|
|
|
956
991
|
: t("recordingPage.checkAgain")}
|
|
957
992
|
</Button>
|
|
958
993
|
<Button asChild variant="ghost" size="sm">
|
|
959
|
-
<Link to="/">
|
|
994
|
+
<Link to="/library" replace>
|
|
995
|
+
{t("recordingPage.backToLibrary")}
|
|
996
|
+
</Link>
|
|
960
997
|
</Button>
|
|
961
998
|
</div>
|
|
962
999
|
</div>
|
|
@@ -1136,7 +1173,7 @@ export default function RecordingPage() {
|
|
|
1136
1173
|
className="shrink-0"
|
|
1137
1174
|
aria-label={t("recordingPage.back")}
|
|
1138
1175
|
>
|
|
1139
|
-
<Link to="/">
|
|
1176
|
+
<Link to="/library" replace>
|
|
1140
1177
|
<IconArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
|
|
1141
1178
|
</Link>
|
|
1142
1179
|
</Button>
|
|
@@ -1174,7 +1211,7 @@ export default function RecordingPage() {
|
|
|
1174
1211
|
</Button>
|
|
1175
1212
|
) : null}
|
|
1176
1213
|
|
|
1177
|
-
{canEdit ? (
|
|
1214
|
+
{canEdit && !editing ? (
|
|
1178
1215
|
<DropdownMenu>
|
|
1179
1216
|
<DropdownMenuTrigger asChild>
|
|
1180
1217
|
<Button
|
|
@@ -1221,6 +1258,7 @@ export default function RecordingPage() {
|
|
|
1221
1258
|
onSelect={() =>
|
|
1222
1259
|
regenerateSummary.mutate({
|
|
1223
1260
|
recordingId: recording.id,
|
|
1261
|
+
openInChat: true,
|
|
1224
1262
|
} as any)
|
|
1225
1263
|
}
|
|
1226
1264
|
>
|
|
@@ -1264,7 +1302,7 @@ export default function RecordingPage() {
|
|
|
1264
1302
|
const menuItem = (
|
|
1265
1303
|
<DropdownMenuItem
|
|
1266
1304
|
key={item.kind}
|
|
1267
|
-
disabled={
|
|
1305
|
+
disabled={workflowBusy}
|
|
1268
1306
|
onSelect={() => handleGenerateWorkflow(item.kind)}
|
|
1269
1307
|
className={
|
|
1270
1308
|
item.tooltipKey ? "justify-between gap-3" : undefined
|
|
@@ -1319,24 +1357,26 @@ export default function RecordingPage() {
|
|
|
1319
1357
|
</DropdownMenu>
|
|
1320
1358
|
) : null}
|
|
1321
1359
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
<Button
|
|
1333
|
-
className="shrink-0 gap-1.5 bg-primary text-primary-foreground hover:bg-primary/90"
|
|
1334
|
-
size="sm"
|
|
1360
|
+
{!editing ? (
|
|
1361
|
+
<ShareRecordingPopover
|
|
1362
|
+
recordingId={recording.id}
|
|
1363
|
+
recordingTitle={recording.title}
|
|
1364
|
+
initialVisibility={recording.visibility}
|
|
1365
|
+
initialRole={role}
|
|
1366
|
+
videoUrl={recording.videoUrl}
|
|
1367
|
+
animatedThumbnailUrl={recording.animatedThumbnailUrl}
|
|
1368
|
+
isLoomRecording={isLoomEmbedBacked}
|
|
1369
|
+
hasPassword={Boolean(recording.hasPassword)}
|
|
1335
1370
|
>
|
|
1336
|
-
<
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1371
|
+
<Button
|
|
1372
|
+
className="shrink-0 gap-1.5 bg-primary text-primary-foreground hover:bg-primary/90"
|
|
1373
|
+
size="sm"
|
|
1374
|
+
>
|
|
1375
|
+
<IconShare3 className="h-4 w-4" />
|
|
1376
|
+
{t("recordingPage.share")}
|
|
1377
|
+
</Button>
|
|
1378
|
+
</ShareRecordingPopover>
|
|
1379
|
+
) : null}
|
|
1340
1380
|
|
|
1341
1381
|
{canDelete || canDownloadRecording ? (
|
|
1342
1382
|
<RecordingOptionsMenu
|
|
@@ -1486,7 +1526,7 @@ export default function RecordingPage() {
|
|
|
1486
1526
|
) : null}
|
|
1487
1527
|
</div>
|
|
1488
1528
|
{recording.description ? (
|
|
1489
|
-
<p className="mt-3
|
|
1529
|
+
<p className="mt-3 whitespace-pre-wrap break-words text-sm text-muted-foreground">
|
|
1490
1530
|
{recording.description}
|
|
1491
1531
|
</p>
|
|
1492
1532
|
) : null}
|
|
@@ -1528,7 +1568,7 @@ function GeneratedWorkflowNotice({
|
|
|
1528
1568
|
const status = workflow.status ?? (workflow.content ? "ready" : "generating");
|
|
1529
1569
|
const content =
|
|
1530
1570
|
typeof workflow.content === "string" ? workflow.content.trim() : "";
|
|
1531
|
-
const isReady = status
|
|
1571
|
+
const isReady = status !== "failed" && content.length > 0;
|
|
1532
1572
|
const isFailed = status === "failed";
|
|
1533
1573
|
const title = workflowTitle(workflow.kind);
|
|
1534
1574
|
|
|
@@ -855,6 +855,8 @@ export default function ShareRoute() {
|
|
|
855
855
|
<Button variant="ghost" size="sm" asChild>
|
|
856
856
|
<a
|
|
857
857
|
href={appPath("/")}
|
|
858
|
+
target="_blank"
|
|
859
|
+
rel="noopener noreferrer"
|
|
858
860
|
className="gap-1.5"
|
|
859
861
|
onClick={() => fireShareCtaClick("try_clips")}
|
|
860
862
|
>
|
|
@@ -965,7 +967,7 @@ export default function ShareRoute() {
|
|
|
965
967
|
</h2>
|
|
966
968
|
)}
|
|
967
969
|
{recording.description ? (
|
|
968
|
-
<p className="text-sm text-muted-foreground
|
|
970
|
+
<p className="whitespace-pre-wrap break-words text-sm text-muted-foreground">
|
|
969
971
|
{recording.description}
|
|
970
972
|
</p>
|
|
971
973
|
) : null}
|
|
@@ -1063,6 +1065,7 @@ export default function ShareRoute() {
|
|
|
1063
1065
|
t("recordingPage.draftQuestions"),
|
|
1064
1066
|
]}
|
|
1065
1067
|
browserTabId={getBrowserTabId()}
|
|
1068
|
+
showHeader={false}
|
|
1066
1069
|
/>
|
|
1067
1070
|
) : (
|
|
1068
1071
|
<PublicAgentEmptyState
|
|
@@ -83,6 +83,17 @@
|
|
|
83
83
|
let bubbleDragLayer: HTMLDivElement | null = null;
|
|
84
84
|
let bubblePersistTimer: ReturnType<typeof setTimeout> | undefined;
|
|
85
85
|
|
|
86
|
+
// The quick-actions toolbar is also draggable. Keep its geometry in the
|
|
87
|
+
// content script because the iframe cannot position itself on the host page.
|
|
88
|
+
const TOOLBAR_WIDTH = 68;
|
|
89
|
+
const TOOLBAR_COLLAPSED_HEIGHT = 154;
|
|
90
|
+
const toolbarGeom: { left: number | null; top: number | null } = {
|
|
91
|
+
left: null,
|
|
92
|
+
top: null,
|
|
93
|
+
};
|
|
94
|
+
let toolbarDragLayer: HTMLDivElement | null = null;
|
|
95
|
+
let toolbarPersistTimer: ReturnType<typeof setTimeout> | undefined;
|
|
96
|
+
|
|
86
97
|
function bubbleSizePx(): number {
|
|
87
98
|
return BUBBLE_SIZES[bubbleGeom.size] ?? BUBBLE_SIZES.lg;
|
|
88
99
|
}
|
|
@@ -130,6 +141,92 @@
|
|
|
130
141
|
}, 200);
|
|
131
142
|
}
|
|
132
143
|
|
|
144
|
+
function clampToolbar(
|
|
145
|
+
left: number,
|
|
146
|
+
top: number,
|
|
147
|
+
height: number,
|
|
148
|
+
): { left: number; top: number } {
|
|
149
|
+
return {
|
|
150
|
+
left: Math.max(8, Math.min(left, window.innerWidth - TOOLBAR_WIDTH - 8)),
|
|
151
|
+
top: Math.max(8, Math.min(top, window.innerHeight - height - 8)),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function applyToolbarGeom(): void {
|
|
156
|
+
const frame = document.getElementById(
|
|
157
|
+
partFrameId("toolbar"),
|
|
158
|
+
) as HTMLIFrameElement | null;
|
|
159
|
+
if (!frame) return;
|
|
160
|
+
const height =
|
|
161
|
+
frame.getBoundingClientRect().height || TOOLBAR_COLLAPSED_HEIGHT;
|
|
162
|
+
const base = clampToolbar(
|
|
163
|
+
toolbarGeom.left ?? 16,
|
|
164
|
+
toolbarGeom.top ?? window.innerHeight / 2 - TOOLBAR_COLLAPSED_HEIGHT / 2,
|
|
165
|
+
height,
|
|
166
|
+
);
|
|
167
|
+
Object.assign(frame.style, {
|
|
168
|
+
left: `${base.left}px`,
|
|
169
|
+
top: `${base.top}px`,
|
|
170
|
+
right: "auto",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function persistToolbarGeom(): void {
|
|
175
|
+
clearTimeout(toolbarPersistTimer);
|
|
176
|
+
toolbarPersistTimer = setTimeout(() => {
|
|
177
|
+
try {
|
|
178
|
+
chrome.storage.local.set({ toolbarGeom });
|
|
179
|
+
} catch {
|
|
180
|
+
/* ignore */
|
|
181
|
+
}
|
|
182
|
+
}, 200);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function startToolbarDrag(): void {
|
|
186
|
+
if (toolbarDragLayer) return;
|
|
187
|
+
const frame = document.getElementById(
|
|
188
|
+
partFrameId("toolbar"),
|
|
189
|
+
) as HTMLIFrameElement | null;
|
|
190
|
+
if (!frame) return;
|
|
191
|
+
|
|
192
|
+
const layer = document.createElement("div");
|
|
193
|
+
Object.assign(layer.style, {
|
|
194
|
+
position: "fixed",
|
|
195
|
+
inset: "0",
|
|
196
|
+
zIndex: "2147483647",
|
|
197
|
+
cursor: "grabbing",
|
|
198
|
+
});
|
|
199
|
+
(document.documentElement || document.body).appendChild(layer);
|
|
200
|
+
toolbarDragLayer = layer;
|
|
201
|
+
|
|
202
|
+
const onMove = (e: PointerEvent): void => {
|
|
203
|
+
const rect = frame.getBoundingClientRect();
|
|
204
|
+
const next = clampToolbar(
|
|
205
|
+
rect.left + e.movementX,
|
|
206
|
+
rect.top + e.movementY,
|
|
207
|
+
rect.height || TOOLBAR_COLLAPSED_HEIGHT,
|
|
208
|
+
);
|
|
209
|
+
toolbarGeom.left = next.left;
|
|
210
|
+
toolbarGeom.top = next.top;
|
|
211
|
+
Object.assign(frame.style, {
|
|
212
|
+
left: `${next.left}px`,
|
|
213
|
+
top: `${next.top}px`,
|
|
214
|
+
right: "auto",
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
const onUp = (): void => {
|
|
218
|
+
layer.removeEventListener("pointermove", onMove);
|
|
219
|
+
layer.removeEventListener("pointerup", onUp);
|
|
220
|
+
layer.removeEventListener("pointercancel", onUp);
|
|
221
|
+
layer.remove();
|
|
222
|
+
toolbarDragLayer = null;
|
|
223
|
+
persistToolbarGeom();
|
|
224
|
+
};
|
|
225
|
+
layer.addEventListener("pointermove", onMove);
|
|
226
|
+
layer.addEventListener("pointerup", onUp);
|
|
227
|
+
layer.addEventListener("pointercancel", onUp);
|
|
228
|
+
}
|
|
229
|
+
|
|
133
230
|
function startBubbleDrag(): void {
|
|
134
231
|
if (bubbleDragLayer) return;
|
|
135
232
|
const frame = document.getElementById(
|
|
@@ -177,7 +274,7 @@
|
|
|
177
274
|
}
|
|
178
275
|
|
|
179
276
|
try {
|
|
180
|
-
chrome.storage.local.get("bubbleGeom", (value) => {
|
|
277
|
+
chrome.storage.local.get(["bubbleGeom", "toolbarGeom"], (value) => {
|
|
181
278
|
if (chrome.runtime.lastError) return;
|
|
182
279
|
const g = value.bubbleGeom as
|
|
183
280
|
| { size?: unknown; left?: unknown; top?: unknown }
|
|
@@ -188,12 +285,24 @@
|
|
|
188
285
|
bubbleGeom.top = typeof g.top === "number" ? g.top : null;
|
|
189
286
|
}
|
|
190
287
|
applyBubbleGeom();
|
|
288
|
+
const toolbar = value.toolbarGeom as
|
|
289
|
+
| { left?: unknown; top?: unknown }
|
|
290
|
+
| undefined;
|
|
291
|
+
if (toolbar && typeof toolbar === "object") {
|
|
292
|
+
toolbarGeom.left =
|
|
293
|
+
typeof toolbar.left === "number" ? toolbar.left : null;
|
|
294
|
+
toolbarGeom.top = typeof toolbar.top === "number" ? toolbar.top : null;
|
|
295
|
+
}
|
|
296
|
+
applyToolbarGeom();
|
|
191
297
|
});
|
|
192
298
|
} catch {
|
|
193
299
|
/* ignore */
|
|
194
300
|
}
|
|
195
301
|
|
|
196
|
-
window.addEventListener("resize", () =>
|
|
302
|
+
window.addEventListener("resize", () => {
|
|
303
|
+
applyBubbleGeom();
|
|
304
|
+
applyToolbarGeom();
|
|
305
|
+
});
|
|
197
306
|
|
|
198
307
|
function readCurrentParts(
|
|
199
308
|
callback: (parts: OverlayPart[] | null) => void,
|
|
@@ -338,6 +447,7 @@
|
|
|
338
447
|
styleFrame(frame, part);
|
|
339
448
|
container.appendChild(frame);
|
|
340
449
|
if (part === "bubble") applyBubbleGeom();
|
|
450
|
+
if (part === "toolbar") applyToolbarGeom();
|
|
341
451
|
}
|
|
342
452
|
|
|
343
453
|
// Camera-ready gating + connecting spinner: while the camera connects we show a
|
|
@@ -520,6 +630,7 @@
|
|
|
520
630
|
const frame = document.getElementById(partFrameId("toolbar"));
|
|
521
631
|
if (frame && typeof data.height === "number") {
|
|
522
632
|
frame.style.height = `${Math.round(data.height)}px`;
|
|
633
|
+
applyToolbarGeom();
|
|
523
634
|
}
|
|
524
635
|
return;
|
|
525
636
|
}
|
|
@@ -537,6 +648,10 @@
|
|
|
537
648
|
startBubbleDrag();
|
|
538
649
|
return;
|
|
539
650
|
}
|
|
651
|
+
if (data.kind === "toolbar-drag-start") {
|
|
652
|
+
startToolbarDrag();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
540
655
|
if (data.kind === "bubble-size") {
|
|
541
656
|
bubbleGeom.size = data.size === "sm" ? "sm" : "lg";
|
|
542
657
|
applyBubbleGeom();
|
|
@@ -84,6 +84,17 @@ function postToolbarSize(height: number): void {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function postToolbarDragStart(): void {
|
|
88
|
+
try {
|
|
89
|
+
window.parent.postMessage(
|
|
90
|
+
{ source: "clips-overlay", kind: "toolbar-drag-start", part: "toolbar" },
|
|
91
|
+
"*",
|
|
92
|
+
);
|
|
93
|
+
} catch {
|
|
94
|
+
/* parent gone */
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
87
98
|
function postCountdownFinished(): void {
|
|
88
99
|
try {
|
|
89
100
|
window.parent.postMessage(
|
|
@@ -406,6 +417,22 @@ function initCountdown(): void {
|
|
|
406
417
|
function initToolbar(): void {
|
|
407
418
|
const pill = document.createElement("div");
|
|
408
419
|
pill.className = "toolbar-v";
|
|
420
|
+
pill.style.cursor = "grab";
|
|
421
|
+
|
|
422
|
+
// The content script owns iframe geometry, so ask it to capture the pointer
|
|
423
|
+
// page-wide when the user drags the toolbar outside its current bounds.
|
|
424
|
+
pill.addEventListener("pointerdown", (event) => {
|
|
425
|
+
if (event.button !== 0) return;
|
|
426
|
+
if ((event.target as HTMLElement).closest("button")) return;
|
|
427
|
+
event.preventDefault();
|
|
428
|
+
pill.style.cursor = "grabbing";
|
|
429
|
+
postToolbarDragStart();
|
|
430
|
+
const restore = (): void => {
|
|
431
|
+
pill.style.cursor = "grab";
|
|
432
|
+
window.removeEventListener("pointerup", restore);
|
|
433
|
+
};
|
|
434
|
+
window.addEventListener("pointerup", restore);
|
|
435
|
+
});
|
|
409
436
|
|
|
410
437
|
const makeBtn = (
|
|
411
438
|
cls: string,
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"plugins": {
|
|
42
42
|
"shell": {
|
|
43
|
-
"open": "(((mailto:\\w+)|(tel:\\w+)|(https?://\\w+)).+|(zoommtg://([A-Za-z0-9-]+\\.)*zoom\\.us/join\\?.+))"
|
|
43
|
+
"open": "(((mailto:\\w+)|(tel:\\w+)|(https?://\\w+)).+|(zoommtg://([A-Za-z0-9-]+\\.)*zoom\\.us/join\\?.+)|(ms-settings:[A-Za-z0-9._?=&-]+))"
|
|
44
44
|
},
|
|
45
45
|
"updater": {
|
|
46
46
|
"active": true,
|