@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
|
@@ -18,6 +18,8 @@ export interface WaveformProps {
|
|
|
18
18
|
durationMs: number;
|
|
19
19
|
/** Excluded ranges (original time) — drawn as striped overlays. */
|
|
20
20
|
excludedRanges?: Array<{ startMs: number; endMs: number }>;
|
|
21
|
+
/** Split markers (original time) — drawn over the active selection. */
|
|
22
|
+
splitPoints?: number[];
|
|
21
23
|
/** Optional selection range (original time) highlighted in brand color. */
|
|
22
24
|
selectionRange?: { startMs: number; endMs: number } | null;
|
|
23
25
|
/** Transcript-backed activity ranges used when browser audio decoding fails. */
|
|
@@ -87,6 +89,7 @@ export function Waveform({
|
|
|
87
89
|
playheadMs,
|
|
88
90
|
durationMs,
|
|
89
91
|
excludedRanges,
|
|
92
|
+
splitPoints = [],
|
|
90
93
|
selectionRange,
|
|
91
94
|
activityRanges = [],
|
|
92
95
|
onSeek,
|
|
@@ -220,19 +223,28 @@ export function Waveform({
|
|
|
220
223
|
|
|
221
224
|
// Selection overlay
|
|
222
225
|
if (selectionRange) {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const xEnd =
|
|
228
|
-
(Math.max(selectionRange.startMs, selectionRange.endMs) /
|
|
229
|
-
Math.max(durationMs, 1)) *
|
|
230
|
-
totalWidth;
|
|
226
|
+
const startMs = Math.min(selectionRange.startMs, selectionRange.endMs);
|
|
227
|
+
const endMs = Math.max(selectionRange.startMs, selectionRange.endMs);
|
|
228
|
+
const xStart = (startMs / Math.max(durationMs, 1)) * totalWidth;
|
|
229
|
+
const xEnd = (endMs / Math.max(durationMs, 1)) * totalWidth;
|
|
231
230
|
ctx.fillStyle = getBrandColorAlpha(0.28);
|
|
232
231
|
ctx.fillRect(xStart, 0, xEnd - xStart, height);
|
|
233
232
|
ctx.strokeStyle = getBrandColor();
|
|
234
233
|
ctx.lineWidth = 1;
|
|
235
234
|
ctx.strokeRect(xStart + 0.5, 0.5, xEnd - xStart - 1, height - 1);
|
|
235
|
+
|
|
236
|
+
// Keep split markers visible on the selected track as well as on the
|
|
237
|
+
// ruler so a split is visibly actionable within the selection.
|
|
238
|
+
for (const splitMs of splitPoints) {
|
|
239
|
+
if (splitMs <= startMs || splitMs >= endMs) continue;
|
|
240
|
+
const splitX = (splitMs / Math.max(durationMs, 1)) * totalWidth;
|
|
241
|
+
ctx.strokeStyle = "rgba(244, 63, 94, 0.95)";
|
|
242
|
+
ctx.lineWidth = 2;
|
|
243
|
+
ctx.beginPath();
|
|
244
|
+
ctx.moveTo(splitX, 0);
|
|
245
|
+
ctx.lineTo(splitX, height);
|
|
246
|
+
ctx.stroke();
|
|
247
|
+
}
|
|
236
248
|
}
|
|
237
249
|
}, [
|
|
238
250
|
peaks,
|
|
@@ -240,6 +252,7 @@ export function Waveform({
|
|
|
240
252
|
height,
|
|
241
253
|
excludedRanges,
|
|
242
254
|
selectionRange,
|
|
255
|
+
splitPoints,
|
|
243
256
|
durationMs,
|
|
244
257
|
activityRanges,
|
|
245
258
|
]);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
IconFolderPlus,
|
|
7
7
|
IconTrash,
|
|
8
8
|
IconEdit,
|
|
9
|
+
IconDots,
|
|
9
10
|
} from "@tabler/icons-react";
|
|
10
11
|
import { useMemo, useState } from "react";
|
|
11
12
|
import { NavLink } from "react-router";
|
|
@@ -33,6 +34,13 @@ import {
|
|
|
33
34
|
ContextMenuSeparator,
|
|
34
35
|
ContextMenuTrigger,
|
|
35
36
|
} from "@/components/ui/context-menu";
|
|
37
|
+
import {
|
|
38
|
+
DropdownMenu,
|
|
39
|
+
DropdownMenuContent,
|
|
40
|
+
DropdownMenuItem,
|
|
41
|
+
DropdownMenuSeparator,
|
|
42
|
+
DropdownMenuTrigger,
|
|
43
|
+
} from "@/components/ui/dropdown-menu";
|
|
36
44
|
import {
|
|
37
45
|
useCreateFolder,
|
|
38
46
|
useDeleteFolder,
|
|
@@ -186,6 +194,50 @@ function FolderItem({
|
|
|
186
194
|
{node.name}
|
|
187
195
|
</span>
|
|
188
196
|
</NavLink>
|
|
197
|
+
<DropdownMenu>
|
|
198
|
+
<DropdownMenuTrigger asChild>
|
|
199
|
+
<button
|
|
200
|
+
type="button"
|
|
201
|
+
aria-label={`${node.name}: ${t("root.commandActions")}`}
|
|
202
|
+
title={`${node.name}: ${t("root.commandActions")}`}
|
|
203
|
+
className="rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100"
|
|
204
|
+
>
|
|
205
|
+
<IconDots className="h-3.5 w-3.5" />
|
|
206
|
+
</button>
|
|
207
|
+
</DropdownMenuTrigger>
|
|
208
|
+
<DropdownMenuContent align="start" side="right">
|
|
209
|
+
<DropdownMenuItem
|
|
210
|
+
onSelect={() => {
|
|
211
|
+
setTimeout(() => {
|
|
212
|
+
setRenameValue(node.name);
|
|
213
|
+
setRenameOpen(true);
|
|
214
|
+
}, 0);
|
|
215
|
+
}}
|
|
216
|
+
>
|
|
217
|
+
<IconEdit className="h-3.5 w-3.5 me-2" />{" "}
|
|
218
|
+
{t("folderTree.rename")}
|
|
219
|
+
</DropdownMenuItem>
|
|
220
|
+
<DropdownMenuItem
|
|
221
|
+
onSelect={() => {
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
setNewValue("");
|
|
224
|
+
setNewOpen(true);
|
|
225
|
+
}, 0);
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
<IconFolderPlus className="h-3.5 w-3.5 me-2" />{" "}
|
|
229
|
+
{t("folderTree.newSubfolder")}
|
|
230
|
+
</DropdownMenuItem>
|
|
231
|
+
<DropdownMenuSeparator />
|
|
232
|
+
<DropdownMenuItem
|
|
233
|
+
onSelect={() => setTimeout(() => setConfirmDelete(true), 0)}
|
|
234
|
+
className="text-destructive"
|
|
235
|
+
>
|
|
236
|
+
<IconTrash className="h-3.5 w-3.5 me-2" />{" "}
|
|
237
|
+
{t("folderTree.delete")}
|
|
238
|
+
</DropdownMenuItem>
|
|
239
|
+
</DropdownMenuContent>
|
|
240
|
+
</DropdownMenu>
|
|
189
241
|
</div>
|
|
190
242
|
</ContextMenuTrigger>
|
|
191
243
|
<ContextMenuContent>
|
|
@@ -361,6 +361,7 @@ function CtaEditor({
|
|
|
361
361
|
// editing this card, so an agent edit to the CTA shows up live. `editing`
|
|
362
362
|
// flips true while focus is anywhere inside the card.
|
|
363
363
|
const editing = useRef(false);
|
|
364
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
364
365
|
const [label, setLabel] = useReconciledState(cta.label, {
|
|
365
366
|
active: editing.current,
|
|
366
367
|
});
|
|
@@ -387,52 +388,91 @@ function CtaEditor({
|
|
|
387
388
|
}
|
|
388
389
|
}}
|
|
389
390
|
>
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
391
|
+
{collapsed ? (
|
|
392
|
+
<div className="flex items-center gap-2">
|
|
393
|
+
<div className="min-w-0 flex-1">
|
|
394
|
+
<p className="truncate text-sm font-medium">{cta.label}</p>
|
|
395
|
+
<p className="text-xs text-muted-foreground">
|
|
396
|
+
{cta.placement === "throughout"
|
|
397
|
+
? t("playerSettings.placementThroughout")
|
|
398
|
+
: t("playerSettings.placementEnd")}
|
|
399
|
+
</p>
|
|
400
|
+
</div>
|
|
401
|
+
<Button
|
|
402
|
+
type="button"
|
|
403
|
+
size="sm"
|
|
404
|
+
variant="ghost"
|
|
405
|
+
title={t("recordingPage.edit")}
|
|
406
|
+
onClick={() => setCollapsed(false)}
|
|
407
|
+
>
|
|
408
|
+
{t("recordingPage.edit")}
|
|
409
|
+
</Button>
|
|
410
|
+
<Button type="button" size="sm" variant="ghost" onClick={onDelete}>
|
|
411
|
+
{t("playerSettings.delete")}
|
|
412
|
+
</Button>
|
|
413
|
+
</div>
|
|
414
|
+
) : (
|
|
415
|
+
<>
|
|
416
|
+
<Input
|
|
417
|
+
value={label}
|
|
418
|
+
onChange={(e) => setLabel(e.target.value)}
|
|
419
|
+
placeholder={t("playerSettings.buttonLabelPlaceholder")}
|
|
420
|
+
/>
|
|
421
|
+
<Input
|
|
422
|
+
value={url}
|
|
423
|
+
onChange={(e) => setUrl(e.target.value)}
|
|
424
|
+
placeholder="https://…"
|
|
425
|
+
/>
|
|
426
|
+
<div className="flex gap-2">
|
|
427
|
+
<input
|
|
428
|
+
type="color"
|
|
429
|
+
value={color}
|
|
430
|
+
onChange={(e) => setColor(e.target.value)}
|
|
431
|
+
className="h-9 w-12 rounded cursor-pointer border border-border"
|
|
432
|
+
/>
|
|
433
|
+
<Select
|
|
434
|
+
value={placement}
|
|
435
|
+
onValueChange={(v) => setPlacement(v as "end" | "throughout")}
|
|
436
|
+
>
|
|
437
|
+
<SelectTrigger className="flex-1">
|
|
438
|
+
<SelectValue />
|
|
439
|
+
</SelectTrigger>
|
|
440
|
+
<SelectContent>
|
|
441
|
+
<SelectItem value="throughout">
|
|
442
|
+
{t("playerSettings.placementThroughout")}
|
|
443
|
+
</SelectItem>
|
|
444
|
+
<SelectItem value="end">
|
|
445
|
+
{t("playerSettings.placementEnd")}
|
|
446
|
+
</SelectItem>
|
|
447
|
+
</SelectContent>
|
|
448
|
+
</Select>
|
|
449
|
+
</div>
|
|
450
|
+
<div className="flex gap-2">
|
|
451
|
+
<Button
|
|
452
|
+
type="button"
|
|
453
|
+
size="sm"
|
|
454
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
|
455
|
+
onClick={() => {
|
|
456
|
+
onSave({ label, url, color, placement });
|
|
457
|
+
setCollapsed(true);
|
|
458
|
+
}}
|
|
459
|
+
>
|
|
460
|
+
{t("common.save")}
|
|
461
|
+
</Button>
|
|
462
|
+
<Button
|
|
463
|
+
type="button"
|
|
464
|
+
size="sm"
|
|
465
|
+
variant="ghost"
|
|
466
|
+
onClick={() => setCollapsed(true)}
|
|
467
|
+
>
|
|
468
|
+
{t("recordingPage.done")}
|
|
469
|
+
</Button>
|
|
470
|
+
<Button type="button" size="sm" variant="ghost" onClick={onDelete}>
|
|
471
|
+
{t("playerSettings.delete")}
|
|
472
|
+
</Button>
|
|
473
|
+
</div>
|
|
474
|
+
</>
|
|
475
|
+
)}
|
|
436
476
|
</div>
|
|
437
477
|
);
|
|
438
478
|
}
|
|
@@ -380,49 +380,55 @@ function LinkTab({
|
|
|
380
380
|
)}
|
|
381
381
|
|
|
382
382
|
<CopyField
|
|
383
|
-
label={
|
|
383
|
+
label={
|
|
384
|
+
isPublic
|
|
385
|
+
? t("shareDialog.shareLink")
|
|
386
|
+
: t("shareDialog.shareWithHumans")
|
|
387
|
+
}
|
|
384
388
|
value={shareUrl}
|
|
385
389
|
disabled={
|
|
386
390
|
visibilityPending || !sharesLoaded || (!isPublic && canManage)
|
|
387
391
|
}
|
|
388
392
|
/>
|
|
389
393
|
|
|
390
|
-
|
|
391
|
-
<
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
<
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
394
|
+
{!isPublic ? (
|
|
395
|
+
<div className="space-y-2">
|
|
396
|
+
<CopyField
|
|
397
|
+
label={t("shareDialog.shareWithAgents")}
|
|
398
|
+
value={agentLink}
|
|
399
|
+
disabled={agentShareDisabled}
|
|
400
|
+
/>
|
|
401
|
+
{sharesLoaded ? (
|
|
402
|
+
<>
|
|
403
|
+
<p className="text-xs text-muted-foreground">
|
|
404
|
+
{t("shareDialog.agentTokenDescription")}
|
|
405
|
+
</p>
|
|
406
|
+
{agentLinkError ? (
|
|
407
|
+
<div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
|
|
408
|
+
<p className="text-xs text-muted-foreground">
|
|
409
|
+
{t("shareDialog.agentLinkUnavailable")}
|
|
410
|
+
</p>
|
|
411
|
+
<Button
|
|
412
|
+
type="button"
|
|
413
|
+
variant="ghost"
|
|
414
|
+
size="sm"
|
|
415
|
+
className="h-7"
|
|
416
|
+
onClick={() => void loadAgentContextUrl()}
|
|
417
|
+
disabled={createAgentLink.isPending}
|
|
418
|
+
>
|
|
419
|
+
{t("shareDialog.retryAgentLink")}
|
|
420
|
+
</Button>
|
|
421
|
+
</div>
|
|
422
|
+
) : null}
|
|
423
|
+
<CopyField
|
|
424
|
+
label={t("shareDialog.copyAgentPrompt")}
|
|
425
|
+
value={agentPrompt}
|
|
426
|
+
disabled={agentShareDisabled}
|
|
427
|
+
/>
|
|
428
|
+
</>
|
|
429
|
+
) : null}
|
|
430
|
+
</div>
|
|
431
|
+
) : null}
|
|
426
432
|
|
|
427
433
|
{sharesLoaded && !isPublic && canManage ? (
|
|
428
434
|
<MakePublicCard
|
|
@@ -592,6 +592,14 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
592
592
|
pauseVideo();
|
|
593
593
|
return;
|
|
594
594
|
}
|
|
595
|
+
if (v.ended) {
|
|
596
|
+
try {
|
|
597
|
+
v.currentTime = 0;
|
|
598
|
+
setCurrentMs(0);
|
|
599
|
+
} catch {
|
|
600
|
+
// Let the normal play attempt report a media error if the seek fails.
|
|
601
|
+
}
|
|
602
|
+
}
|
|
595
603
|
requestPlay();
|
|
596
604
|
}, [isPlaying, pauseVideo, requestPlay]);
|
|
597
605
|
|
|
@@ -1423,6 +1431,21 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1423
1431
|
</div>
|
|
1424
1432
|
)}
|
|
1425
1433
|
|
|
1434
|
+
{thumbnailUrl &&
|
|
1435
|
+
!autoPlay &&
|
|
1436
|
+
!hasPlaybackStarted &&
|
|
1437
|
+
(!startMs || startMs <= 0) ? (
|
|
1438
|
+
<img
|
|
1439
|
+
src={resolveLocalUrl(thumbnailUrl)}
|
|
1440
|
+
alt=""
|
|
1441
|
+
aria-hidden="true"
|
|
1442
|
+
className={cn(
|
|
1443
|
+
"pointer-events-none absolute inset-0 z-[1] h-full w-full",
|
|
1444
|
+
cover ? "object-cover" : "object-contain",
|
|
1445
|
+
)}
|
|
1446
|
+
/>
|
|
1447
|
+
) : null}
|
|
1448
|
+
|
|
1426
1449
|
{centerOverlayMode ? (
|
|
1427
1450
|
<CenterPlaybackOverlay
|
|
1428
1451
|
mode={centerOverlayMode}
|
|
@@ -1462,7 +1485,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1462
1485
|
|
|
1463
1486
|
{/* Floating CTA (throughout placement) */}
|
|
1464
1487
|
{showThroughoutCta ? (
|
|
1465
|
-
<div data-player-ui className="absolute bottom-16 right-4 z-
|
|
1488
|
+
<div data-player-ui className="absolute bottom-16 right-4 z-30">
|
|
1466
1489
|
<CtaButton
|
|
1467
1490
|
cta={cta!}
|
|
1468
1491
|
onClick={() => onCtaClick?.(cta!.id)}
|
|
@@ -1484,6 +1507,27 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1484
1507
|
onClick={() => onCtaClick?.(cta!.id)}
|
|
1485
1508
|
large
|
|
1486
1509
|
/>
|
|
1510
|
+
<button
|
|
1511
|
+
type="button"
|
|
1512
|
+
data-player-ui
|
|
1513
|
+
aria-label={t("videoPlayer.playClip")}
|
|
1514
|
+
onClick={(e) => {
|
|
1515
|
+
e.stopPropagation();
|
|
1516
|
+
const v = videoRef.current;
|
|
1517
|
+
if (!v) return;
|
|
1518
|
+
try {
|
|
1519
|
+
v.currentTime = 0;
|
|
1520
|
+
setCurrentMs(0);
|
|
1521
|
+
} catch {
|
|
1522
|
+
// The regular play path will surface any media error.
|
|
1523
|
+
}
|
|
1524
|
+
requestPlay();
|
|
1525
|
+
}}
|
|
1526
|
+
className="pointer-events-auto inline-flex items-center gap-2 rounded-md border border-white/30 bg-white/10 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-white/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
|
|
1527
|
+
>
|
|
1528
|
+
<IconPlayerPlay className="h-4 w-4 fill-current" />
|
|
1529
|
+
{t("videoPlayer.playClip")}
|
|
1530
|
+
</button>
|
|
1487
1531
|
</div>
|
|
1488
1532
|
</div>
|
|
1489
1533
|
) : null}
|
|
@@ -455,6 +455,8 @@ const messages = {
|
|
|
455
455
|
retryAgentLink: "إعادة المحاولة",
|
|
456
456
|
gifPreview: "معاينة GIF",
|
|
457
457
|
openPlayer: "مشغل مفتوح",
|
|
458
|
+
chooseFile: "اختيار ملف",
|
|
459
|
+
remove: "إزالة",
|
|
458
460
|
downloadMp4: "تحميل MP4",
|
|
459
461
|
embedsNeedPublic: "التضمينات تحتاج إلى مقطع عام",
|
|
460
462
|
embedPublicDescription:
|
|
@@ -468,6 +468,8 @@ const messages = {
|
|
|
468
468
|
retryAgentLink: "Erneut versuchen",
|
|
469
469
|
gifPreview: "GIF-Vorschau",
|
|
470
470
|
openPlayer: "Spieler öffnen",
|
|
471
|
+
chooseFile: "Datei auswählen",
|
|
472
|
+
remove: "Entfernen",
|
|
471
473
|
downloadMp4: "Laden Sie MP4 herunter",
|
|
472
474
|
embedsNeedPublic: "Für Einbettungen ist ein öffentlicher Clip erforderlich",
|
|
473
475
|
embedPublicDescription:
|
|
@@ -451,6 +451,8 @@ const messages = {
|
|
|
451
451
|
retryAgentLink: "Retry",
|
|
452
452
|
gifPreview: "GIF preview",
|
|
453
453
|
openPlayer: "Open player",
|
|
454
|
+
chooseFile: "Choose file",
|
|
455
|
+
remove: "Remove",
|
|
454
456
|
downloadMp4: "Download MP4",
|
|
455
457
|
embedsNeedPublic: "Embeds need a public clip",
|
|
456
458
|
embedPublicDescription:
|
|
@@ -465,6 +465,8 @@ const messages = {
|
|
|
465
465
|
retryAgentLink: "Reintentar",
|
|
466
466
|
gifPreview: "vista previa de GIF",
|
|
467
467
|
openPlayer: "jugador abierto",
|
|
468
|
+
chooseFile: "Elige el archivo",
|
|
469
|
+
remove: "Eliminar",
|
|
468
470
|
downloadMp4: "Descargar MP4",
|
|
469
471
|
embedsNeedPublic: "Los elementos insertados necesitan un clip público",
|
|
470
472
|
embedPublicDescription:
|
|
@@ -465,6 +465,8 @@ const messages = {
|
|
|
465
465
|
retryAgentLink: "Réessayer",
|
|
466
466
|
gifPreview: "aperçu de GIF",
|
|
467
467
|
openPlayer: "Joueur ouvert",
|
|
468
|
+
chooseFile: "Choisir un fichier",
|
|
469
|
+
remove: "Retirer",
|
|
468
470
|
downloadMp4: "Télécharger MP4",
|
|
469
471
|
embedsNeedPublic: "Les intégrations nécessitent un clip public",
|
|
470
472
|
embedPublicDescription:
|
|
@@ -448,6 +448,8 @@ const messages = {
|
|
|
448
448
|
retryAgentLink: "फिर से प्रयास करें",
|
|
449
449
|
gifPreview: "GIF पूर्वावलोकन",
|
|
450
450
|
openPlayer: "खुला खिलाड़ी",
|
|
451
|
+
chooseFile: "फ़ाइल चुनें",
|
|
452
|
+
remove: "हटाएं",
|
|
451
453
|
downloadMp4: "MP4 डाउनलोड करें",
|
|
452
454
|
embedsNeedPublic: "एंबेड को एक सार्वजनिक क्लिप की आवश्यकता है",
|
|
453
455
|
embedPublicDescription:
|
|
@@ -460,6 +460,8 @@ const messages = {
|
|
|
460
460
|
retryAgentLink: "再試行",
|
|
461
461
|
gifPreview: "GIF プレビュー",
|
|
462
462
|
openPlayer: "プレーヤーを開く",
|
|
463
|
+
chooseFile: "ファイルを選択してください",
|
|
464
|
+
remove: "取り除く",
|
|
463
465
|
downloadMp4: "ダウンロード",
|
|
464
466
|
embedsNeedPublic: "埋め込みにはパブリッククリップが必要です",
|
|
465
467
|
embedPublicDescription:
|
|
@@ -462,6 +462,8 @@ const messages = {
|
|
|
462
462
|
retryAgentLink: "Tentar novamente",
|
|
463
463
|
gifPreview: "visualização de GIF",
|
|
464
464
|
openPlayer: "Jogador aberto",
|
|
465
|
+
chooseFile: "Escolher arquivo",
|
|
466
|
+
remove: "Remover",
|
|
465
467
|
downloadMp4: "Baixar MP4",
|
|
466
468
|
embedsNeedPublic: "As incorporações precisam de um clipe público",
|
|
467
469
|
embedPublicDescription:
|
|
@@ -198,6 +198,20 @@ export function getKeptRanges(
|
|
|
198
198
|
return out;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
/** Move a source timestamp to the first visible timestamp after a cut. */
|
|
202
|
+
export function skipExcludedRange(
|
|
203
|
+
ms: number,
|
|
204
|
+
excludedRanges: Pick<TrimRange, "startMs" | "endMs">[],
|
|
205
|
+
durationMs: number,
|
|
206
|
+
): number {
|
|
207
|
+
const range = excludedRanges.find(
|
|
208
|
+
(candidate) => ms >= candidate.startMs && ms < candidate.endMs,
|
|
209
|
+
);
|
|
210
|
+
if (!range) return ms;
|
|
211
|
+
const next = Math.max(ms, range.endMs);
|
|
212
|
+
return durationMs > 0 ? Math.min(next, durationMs) : next;
|
|
213
|
+
}
|
|
214
|
+
|
|
201
215
|
/**
|
|
202
216
|
* Merge a new excluded range into the edits, collapsing adjacent/overlapping
|
|
203
217
|
* entries. Preserves existing non-excluded (split) markers as-is.
|
|
@@ -6,8 +6,7 @@ import { NavLink } from "react-router";
|
|
|
6
6
|
import { LibraryGrid } from "@/components/library/library-grid";
|
|
7
7
|
import { usePageHeaderLayout } from "@/components/library/page-header";
|
|
8
8
|
|
|
9
|
-
const SEO_TITLE =
|
|
10
|
-
"Agent-Native Clips - Open Source, agent-friendly Loom alternative";
|
|
9
|
+
const SEO_TITLE = "Agent-Native Clips - Open Source screen recorder";
|
|
11
10
|
const SEO_DESCRIPTION =
|
|
12
11
|
"Open Source screen recorder and meeting-notes app with AI transcripts, summaries, search, dictation, and agent-readable share links.";
|
|
13
12
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { DefaultSpinner } from "@agent-native/core/client";
|
|
2
2
|
import { redirect, type LoaderFunctionArgs } from "react-router";
|
|
3
3
|
|
|
4
|
-
const SEO_TITLE =
|
|
5
|
-
"Agent-Native Clips - Open Source, agent-friendly Loom alternative";
|
|
4
|
+
const SEO_TITLE = "Agent-Native Clips - Open Source screen recorder";
|
|
6
5
|
const SEO_DESCRIPTION =
|
|
7
6
|
"Open Source screen recorder and meeting-notes app with AI transcripts, summaries, search, dictation, and agent-readable share links.";
|
|
8
7
|
|