@agent-native/core 0.153.0 → 0.153.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/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +21 -19
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -2
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +3 -2
- package/corpus/templates/calendar/app/lib/event-layout.ts +16 -35
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +27 -55
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +104 -66
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +45 -43
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +0 -1
- package/corpus/templates/clips/desktop/src/app.tsx +818 -442
- package/corpus/templates/clips/desktop/src/styles.css +244 -2
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +2 -2
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +584 -1
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +25 -0
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +1 -1
- package/corpus/templates/factory/app/routes/factory.tsx +0 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/ShareDialog.tsx +28 -25
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +50 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +12 -60
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.js +25 -2
- package/dist/agent/run-manager.js +13 -3
- package/dist/client/AgentPanel.js +6 -4
- package/dist/client/org/WorkspaceNotice.js +7 -4
- package/dist/client/sharing/ShareButton.d.ts +2 -2
- package/dist/client/sharing/ShareButton.js +18 -13
- package/dist/client/sharing/ShareDialog.d.ts +1 -1
- package/dist/client/sharing/ShareDialog.js +16 -11
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/auth.js +18 -4
- package/dist/server/better-auth-instance.js +8 -1
- package/dist/server/magic-link-attribution.d.ts +21 -0
- package/dist/server/magic-link-attribution.js +115 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +1 -1
|
@@ -101,25 +101,27 @@ Don't just dump raw data. Synthesize findings:
|
|
|
101
101
|
|
|
102
102
|
### Step 4: Generate Charts (when useful)
|
|
103
103
|
|
|
104
|
-
When the analysis benefits from a visual — trends over time, distributions,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
generate-chart
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
104
|
+
When the analysis benefits from a visual — trends over time, distributions, or
|
|
105
|
+
comparisons between categories — query the data first, then use the live
|
|
106
|
+
`/chart` embed described in `data-querying` for an in-chat answer. Do not call
|
|
107
|
+
`generate-chart` for a one-off chat result: it produces a static image for
|
|
108
|
+
saved artifacts, requires pre-stringified JSON, and does not count as a real
|
|
109
|
+
data query for the final response guard.
|
|
110
|
+
|
|
111
|
+
For a stacked multi-series bar chart, keep the query in long form and emit a
|
|
112
|
+
panel with `chartType: "bar"`, `config.pivot` containing `xKey`, `seriesKey`,
|
|
113
|
+
and `valueKey`, plus `config.stacked: true`. The live chart route pivots the
|
|
114
|
+
rows and renders one stack per x-axis category. See `data-querying`'s
|
|
115
|
+
"Inline Charts In Chat" section for the exact `embed` fence and encoding.
|
|
116
|
+
|
|
117
|
+
Only use `generate-chart` when a saved analysis artifact explicitly needs a
|
|
118
|
+
static image. If it returns an error while answering in chat, switch to the
|
|
119
|
+
live embed instead of retrying reformatted `labels` or `data` parameters.
|
|
120
|
+
|
|
121
|
+
You can include multiple live charts in one analysis. Reach for a chart when it
|
|
122
|
+
communicates the finding faster than a table - don't force visuals on every
|
|
123
|
+
analysis. Include the query and embed configuration in saved `instructions` so
|
|
124
|
+
re-runs produce fresh charts.
|
|
123
125
|
|
|
124
126
|
### Step 5: Format Results as Markdown
|
|
125
127
|
|
|
@@ -232,6 +232,7 @@ const DayEventCard = memo(function DayEventCard({
|
|
|
232
232
|
const li = layout.get(event.id) ?? {
|
|
233
233
|
left: 0,
|
|
234
234
|
width: 100,
|
|
235
|
+
indent: 0,
|
|
235
236
|
col: 0,
|
|
236
237
|
totalCols: 1,
|
|
237
238
|
stackOrder: 0,
|
|
@@ -304,8 +305,8 @@ const DayEventCard = memo(function DayEventCard({
|
|
|
304
305
|
}
|
|
305
306
|
style={{
|
|
306
307
|
...posStyle,
|
|
307
|
-
left: `calc(${li.left}% + ${li.
|
|
308
|
-
width: `calc(${li.width}% - ${li.
|
|
308
|
+
left: `calc(${li.left}% + ${li.indent}px)`,
|
|
309
|
+
width: `calc(${li.width}% - ${li.indent * 2 + 2}px)`,
|
|
309
310
|
zIndex:
|
|
310
311
|
isBeingDragged && isDragging
|
|
311
312
|
? 100
|
|
@@ -274,6 +274,7 @@ const WeekEventCard = memo(function WeekEventCard({
|
|
|
274
274
|
const li = layout.get(event.id) ?? {
|
|
275
275
|
left: 0,
|
|
276
276
|
width: 100,
|
|
277
|
+
indent: 0,
|
|
277
278
|
col: 0,
|
|
278
279
|
totalCols: 1,
|
|
279
280
|
stackOrder: 0,
|
|
@@ -372,8 +373,8 @@ const WeekEventCard = memo(function WeekEventCard({
|
|
|
372
373
|
}
|
|
373
374
|
style={{
|
|
374
375
|
...style,
|
|
375
|
-
left: `calc(${li.left}% + ${li.
|
|
376
|
-
width: `calc(${li.width}% - ${li.
|
|
376
|
+
left: `calc(${li.left}% + ${li.indent}px)`,
|
|
377
|
+
width: `calc(${li.width}% - ${li.indent * 2 + 2}px)`,
|
|
377
378
|
zIndex:
|
|
378
379
|
isBeingDragged && isDragging
|
|
379
380
|
? 100
|
|
@@ -4,6 +4,7 @@ import { addDays, parseISO, startOfDay } from "date-fns";
|
|
|
4
4
|
export interface TimedEventLayout {
|
|
5
5
|
left: number;
|
|
6
6
|
width: number;
|
|
7
|
+
indent: number;
|
|
7
8
|
col: number;
|
|
8
9
|
totalCols: number;
|
|
9
10
|
stackOrder: number;
|
|
@@ -20,18 +21,16 @@ interface EventEntry {
|
|
|
20
21
|
inputOrder: number;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
const TEXT_REGION_MINUTES = 45;
|
|
24
24
|
const MIN_VISIBLE_EVENT_MINUTES = 15;
|
|
25
|
+
const OVERLAP_INDENT_PX = 16;
|
|
25
26
|
|
|
26
27
|
function overlaps(a: EventBounds, b: EventBounds): boolean {
|
|
27
28
|
return a.start < b.end && b.start < a.end;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
|
-
* Pack timed events into
|
|
32
|
-
*
|
|
33
|
-
* event is present there at a different time, which keeps long events readable
|
|
34
|
-
* without wasting the column width needed for genuinely simultaneous labels.
|
|
32
|
+
* Pack timed events into shallow overlap layers. Later events stay wide enough
|
|
33
|
+
* to read while their inset exposes the boundary of the event underneath.
|
|
35
34
|
*/
|
|
36
35
|
export function computeTimedEventLayout(
|
|
37
36
|
dayEvents: readonly CalendarEvent[],
|
|
@@ -66,52 +65,34 @@ export function computeTimedEventLayout(
|
|
|
66
65
|
return a.inputOrder - b.inputOrder;
|
|
67
66
|
});
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return a.start < bTextEnd && b.start < aTextEnd;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Place labels side by side only when their visible text regions overlap.
|
|
76
|
-
const columns: EventEntry[][] = [];
|
|
68
|
+
// Put overlapping events into the first layer that is free at their start.
|
|
69
|
+
// Reusing finished layers keeps chained overlaps from creating empty gaps.
|
|
70
|
+
const overlapLayers: EventEntry[][] = [];
|
|
77
71
|
const eventColumns = new Map<CalendarEvent, number>();
|
|
78
72
|
|
|
79
73
|
for (const entry of sorted) {
|
|
80
|
-
let column =
|
|
81
|
-
|
|
82
|
-
(placed) => !textOverlaps(placed.bounds, entry.bounds),
|
|
83
|
-
),
|
|
74
|
+
let column = overlapLayers.findIndex((layerEntries) =>
|
|
75
|
+
layerEntries.every((placed) => !overlaps(placed.bounds, entry.bounds)),
|
|
84
76
|
);
|
|
85
77
|
|
|
86
78
|
if (column === -1) {
|
|
87
|
-
column =
|
|
88
|
-
|
|
79
|
+
column = overlapLayers.length;
|
|
80
|
+
overlapLayers.push([]);
|
|
89
81
|
}
|
|
90
82
|
|
|
91
|
-
|
|
83
|
+
overlapLayers[column].push(entry);
|
|
92
84
|
eventColumns.set(entry.event, column);
|
|
93
85
|
}
|
|
94
86
|
|
|
95
|
-
const totalCols =
|
|
87
|
+
const totalCols = overlapLayers.length;
|
|
96
88
|
|
|
97
89
|
for (const [stackOrder, entry] of sorted.entries()) {
|
|
98
90
|
const col = eventColumns.get(entry.event)!;
|
|
99
|
-
let span = 1;
|
|
100
|
-
|
|
101
|
-
// A column to the right is available when it has no event that overlaps
|
|
102
|
-
// this card in time. Label-only collisions do not need to block expansion.
|
|
103
|
-
for (let candidate = col + 1; candidate < totalCols; candidate++) {
|
|
104
|
-
if (
|
|
105
|
-
columns[candidate].some((other) => overlaps(other.bounds, entry.bounds))
|
|
106
|
-
) {
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
span++;
|
|
110
|
-
}
|
|
111
91
|
|
|
112
92
|
result.set(entry.event.id, {
|
|
113
|
-
left:
|
|
114
|
-
width:
|
|
93
|
+
left: 0,
|
|
94
|
+
width: 100,
|
|
95
|
+
indent: col * OVERLAP_INDENT_PX,
|
|
115
96
|
col,
|
|
116
97
|
totalCols,
|
|
117
98
|
stackOrder,
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
useActionQuery,
|
|
5
5
|
} from "@agent-native/core/client/hooks";
|
|
6
6
|
import { useT } from "@agent-native/core/client/i18n";
|
|
7
|
-
import {
|
|
7
|
+
import { IconLink, IconMail } from "@tabler/icons-react";
|
|
8
8
|
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
|
9
9
|
import { toast } from "sonner";
|
|
10
10
|
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
CopyField,
|
|
13
13
|
GeneralAccessSelect,
|
|
14
14
|
MakePublicCard,
|
|
15
|
-
ShareCardHeader,
|
|
16
15
|
SharePeopleTab,
|
|
17
16
|
copyToClipboard,
|
|
18
17
|
useResourceVisibilityMutation,
|
|
@@ -30,7 +29,6 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
|
30
29
|
|
|
31
30
|
export interface ShareMeetingPopoverProps {
|
|
32
31
|
meetingId: string;
|
|
33
|
-
meetingTitle?: string;
|
|
34
32
|
shareTranscript: boolean;
|
|
35
33
|
transcriptReady: boolean;
|
|
36
34
|
children: ReactNode;
|
|
@@ -38,7 +36,6 @@ export interface ShareMeetingPopoverProps {
|
|
|
38
36
|
|
|
39
37
|
export function ShareMeetingPopover({
|
|
40
38
|
meetingId,
|
|
41
|
-
meetingTitle,
|
|
42
39
|
shareTranscript,
|
|
43
40
|
transcriptReady,
|
|
44
41
|
children,
|
|
@@ -52,7 +49,6 @@ export function ShareMeetingPopover({
|
|
|
52
49
|
>
|
|
53
50
|
<ShareMeetingContent
|
|
54
51
|
meetingId={meetingId}
|
|
55
|
-
meetingTitle={meetingTitle}
|
|
56
52
|
shareTranscript={shareTranscript}
|
|
57
53
|
transcriptReady={transcriptReady}
|
|
58
54
|
/>
|
|
@@ -63,12 +59,10 @@ export function ShareMeetingPopover({
|
|
|
63
59
|
|
|
64
60
|
function ShareMeetingContent({
|
|
65
61
|
meetingId,
|
|
66
|
-
meetingTitle,
|
|
67
62
|
shareTranscript,
|
|
68
63
|
transcriptReady,
|
|
69
64
|
}: {
|
|
70
65
|
meetingId: string;
|
|
71
|
-
meetingTitle?: string;
|
|
72
66
|
shareTranscript: boolean;
|
|
73
67
|
transcriptReady: boolean;
|
|
74
68
|
}) {
|
|
@@ -85,14 +79,9 @@ function ShareMeetingContent({
|
|
|
85
79
|
|
|
86
80
|
const data = sharesQuery.data;
|
|
87
81
|
const canManage = data?.role === "owner" || data?.role === "admin";
|
|
88
|
-
const titleText = meetingTitle
|
|
89
|
-
? t("clipsFinalRaw.shareNamedMeeting", { title: meetingTitle })
|
|
90
|
-
: t("clipsFinalRaw.shareMeeting");
|
|
91
82
|
|
|
92
83
|
return (
|
|
93
84
|
<>
|
|
94
|
-
<ShareCardHeader title={titleText} ownerEmail={data?.ownerEmail} />
|
|
95
|
-
|
|
96
85
|
<Tabs defaultValue="link" className="min-w-0 px-4 py-3">
|
|
97
86
|
<TabsList className="grid w-full grid-cols-2">
|
|
98
87
|
<TabsTrigger value="link" className="gap-1.5">
|
|
@@ -192,57 +181,40 @@ function LinkTab({
|
|
|
192
181
|
};
|
|
193
182
|
|
|
194
183
|
return (
|
|
195
|
-
<div className="space-y-
|
|
184
|
+
<div className="space-y-3">
|
|
196
185
|
<GeneralAccessSelect
|
|
197
186
|
visibility={visibility}
|
|
198
187
|
canManage={canManage}
|
|
199
188
|
isPending={isPending}
|
|
200
189
|
onChange={(next) => setResourceVisibility(next)}
|
|
190
|
+
showDescription={false}
|
|
201
191
|
/>
|
|
202
192
|
|
|
203
|
-
<div>
|
|
204
|
-
<div className="
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
<label
|
|
220
|
-
htmlFor={`meeting-share-transcript-${meetingId}`}
|
|
221
|
-
className="text-sm font-medium"
|
|
222
|
-
>
|
|
223
|
-
{t("shareMeeting.includeTranscript")}
|
|
224
|
-
</label>
|
|
225
|
-
<p
|
|
226
|
-
id={`meeting-share-transcript-description-${meetingId}`}
|
|
227
|
-
className="mt-0.5 text-xs text-muted-foreground"
|
|
228
|
-
>
|
|
229
|
-
{transcriptReady
|
|
230
|
-
? t("shareMeeting.includeTranscriptDescription")
|
|
231
|
-
: t("shareMeeting.transcriptUnavailable")}
|
|
232
|
-
</p>
|
|
233
|
-
</div>
|
|
234
|
-
<Switch
|
|
235
|
-
id={`meeting-share-transcript-${meetingId}`}
|
|
236
|
-
checked={includeTranscript}
|
|
237
|
-
onCheckedChange={handleTranscriptSharingChange}
|
|
238
|
-
disabled={
|
|
239
|
-
!canManage || !transcriptReady || updateMeeting.isPending
|
|
240
|
-
}
|
|
241
|
-
aria-describedby={`meeting-share-transcript-description-${meetingId}`}
|
|
242
|
-
className="mt-0.5 shrink-0"
|
|
243
|
-
/>
|
|
244
|
-
</div>
|
|
193
|
+
<div className="flex items-center justify-between gap-4 rounded-md border border-border px-3 py-2.5">
|
|
194
|
+
<div className="min-w-0">
|
|
195
|
+
<label
|
|
196
|
+
htmlFor={`meeting-share-transcript-${meetingId}`}
|
|
197
|
+
className="text-sm font-medium"
|
|
198
|
+
>
|
|
199
|
+
{t("shareMeeting.includeTranscript")}
|
|
200
|
+
</label>
|
|
201
|
+
<p
|
|
202
|
+
id={`meeting-share-transcript-description-${meetingId}`}
|
|
203
|
+
className="sr-only"
|
|
204
|
+
>
|
|
205
|
+
{transcriptReady
|
|
206
|
+
? t("shareMeeting.includeTranscriptDescription")
|
|
207
|
+
: t("shareMeeting.transcriptUnavailable")}
|
|
208
|
+
</p>
|
|
245
209
|
</div>
|
|
210
|
+
<Switch
|
|
211
|
+
id={`meeting-share-transcript-${meetingId}`}
|
|
212
|
+
checked={includeTranscript}
|
|
213
|
+
onCheckedChange={handleTranscriptSharingChange}
|
|
214
|
+
disabled={!canManage || !transcriptReady || updateMeeting.isPending}
|
|
215
|
+
aria-describedby={`meeting-share-transcript-description-${meetingId}`}
|
|
216
|
+
className="shrink-0"
|
|
217
|
+
/>
|
|
246
218
|
</div>
|
|
247
219
|
|
|
248
220
|
<CopyField label={t("clipsFinalRaw.shareLink")} value={shareUrl} />
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "@agent-native/core/client/hooks";
|
|
8
8
|
import { useT } from "@agent-native/core/client/i18n";
|
|
9
9
|
import {
|
|
10
|
+
IconChevronDown,
|
|
10
11
|
IconCode,
|
|
11
12
|
IconExternalLink,
|
|
12
13
|
IconLink,
|
|
@@ -26,7 +27,6 @@ import {
|
|
|
26
27
|
CopyField,
|
|
27
28
|
GeneralAccessSelect,
|
|
28
29
|
MakePublicCard,
|
|
29
|
-
ShareCardHeader,
|
|
30
30
|
SharePeopleTab,
|
|
31
31
|
copyToClipboard,
|
|
32
32
|
useResourceVisibilityMutation,
|
|
@@ -35,6 +35,11 @@ import {
|
|
|
35
35
|
type Visibility,
|
|
36
36
|
} from "@/components/sharing/share-ui";
|
|
37
37
|
import { Button } from "@/components/ui/button";
|
|
38
|
+
import {
|
|
39
|
+
Collapsible,
|
|
40
|
+
CollapsibleContent,
|
|
41
|
+
CollapsibleTrigger,
|
|
42
|
+
} from "@/components/ui/collapsible";
|
|
38
43
|
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
|
39
44
|
import { Input } from "@/components/ui/input";
|
|
40
45
|
import { Label } from "@/components/ui/label";
|
|
@@ -45,6 +50,7 @@ import {
|
|
|
45
50
|
} from "@/components/ui/popover";
|
|
46
51
|
import { Switch } from "@/components/ui/switch";
|
|
47
52
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
|
53
|
+
import { cn } from "@/lib/utils";
|
|
48
54
|
|
|
49
55
|
import { buildEmailPreviewMarkup } from "../../../shared/email-preview";
|
|
50
56
|
import { isLoomEmbedUrl } from "../../../shared/loom";
|
|
@@ -222,19 +228,12 @@ function ShareRecordingContent({
|
|
|
222
228
|
absoluteAppUrl(`/share/${recordingId}`),
|
|
223
229
|
ownerViaId,
|
|
224
230
|
);
|
|
225
|
-
const titleText = recordingTitle
|
|
226
|
-
? t("shareDialog.shareTitle", { title: recordingTitle })
|
|
227
|
-
: t("shareDialog.shareRecording");
|
|
228
|
-
|
|
229
231
|
return (
|
|
230
232
|
<>
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
/>
|
|
236
|
-
|
|
237
|
-
<Tabs defaultValue="link" className="min-w-0 px-4 py-3">
|
|
233
|
+
<Tabs
|
|
234
|
+
defaultValue="link"
|
|
235
|
+
className={cn("min-w-0 px-4 py-3", reserveCloseButton && "pe-12")}
|
|
236
|
+
>
|
|
238
237
|
<TabsList className="grid w-full grid-cols-3">
|
|
239
238
|
<TabsTrigger value="link" className="gap-1.5">
|
|
240
239
|
<IconLink size={14} />
|
|
@@ -434,6 +433,30 @@ function LinkTab({
|
|
|
434
433
|
const agentPrompt = agentLink
|
|
435
434
|
? t("shareDialog.agentPrompt", { agentContextUrl: agentLink })
|
|
436
435
|
: "";
|
|
436
|
+
const [agentDetailsOpen, setAgentDetailsOpen] = useState(false);
|
|
437
|
+
const showMakePublic = sharesLoaded && !isPublic && canManage;
|
|
438
|
+
const downloadButton = videoUrl ? (
|
|
439
|
+
<Button
|
|
440
|
+
variant="outline"
|
|
441
|
+
size="sm"
|
|
442
|
+
className={isLoomRecording ? "gap-1.5" : undefined}
|
|
443
|
+
onClick={() => window.open(videoUrl, "_blank", "noopener,noreferrer")}
|
|
444
|
+
>
|
|
445
|
+
{isLoomRecording ? (
|
|
446
|
+
<>
|
|
447
|
+
<IconExternalLink className="h-4 w-4" />
|
|
448
|
+
{t("shareDialog.openPlayer")}
|
|
449
|
+
</>
|
|
450
|
+
) : (
|
|
451
|
+
t("recordRoute.downloadRecording")
|
|
452
|
+
)}
|
|
453
|
+
</Button>
|
|
454
|
+
) : null;
|
|
455
|
+
const moveDownloadIntoMakePublicRow = showMakePublic && Boolean(videoUrl);
|
|
456
|
+
|
|
457
|
+
useEffect(() => {
|
|
458
|
+
if (isPublic) setAgentDetailsOpen(false);
|
|
459
|
+
}, [isPublic]);
|
|
437
460
|
|
|
438
461
|
return (
|
|
439
462
|
<div className="space-y-4">
|
|
@@ -444,6 +467,7 @@ function LinkTab({
|
|
|
444
467
|
isPending={visibilityPending}
|
|
445
468
|
onChange={(next) => setResourceVisibility(next)}
|
|
446
469
|
publicDescription={t("shareDialog.publicDescription")}
|
|
470
|
+
showDescription={false}
|
|
447
471
|
/>
|
|
448
472
|
) : (
|
|
449
473
|
<div className="space-y-2" aria-hidden>
|
|
@@ -463,47 +487,77 @@ function LinkTab({
|
|
|
463
487
|
/>
|
|
464
488
|
|
|
465
489
|
{!isPublic ? (
|
|
466
|
-
<
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
</div>
|
|
493
|
-
) : null}
|
|
490
|
+
<Collapsible
|
|
491
|
+
open={agentDetailsOpen}
|
|
492
|
+
onOpenChange={setAgentDetailsOpen}
|
|
493
|
+
className="overflow-hidden rounded-md border border-border"
|
|
494
|
+
>
|
|
495
|
+
<CollapsibleTrigger asChild>
|
|
496
|
+
<button
|
|
497
|
+
type="button"
|
|
498
|
+
className="flex min-h-10 w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm font-medium transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
|
|
499
|
+
>
|
|
500
|
+
<span className="flex min-w-0 items-center gap-2 truncate">
|
|
501
|
+
<IconLink
|
|
502
|
+
aria-hidden="true"
|
|
503
|
+
className="size-3.5 shrink-0 text-muted-foreground"
|
|
504
|
+
strokeWidth={1.8}
|
|
505
|
+
/>
|
|
506
|
+
{t("shareDialog.shareWithAgents")}
|
|
507
|
+
</span>
|
|
508
|
+
<IconChevronDown
|
|
509
|
+
aria-hidden="true"
|
|
510
|
+
className={`size-4 shrink-0 text-muted-foreground transition-transform ${agentDetailsOpen ? "rotate-180" : ""}`}
|
|
511
|
+
/>
|
|
512
|
+
</button>
|
|
513
|
+
</CollapsibleTrigger>
|
|
514
|
+
<CollapsibleContent className="clips-collapsible-content border-t border-border px-3 py-3">
|
|
515
|
+
<div className="space-y-2">
|
|
494
516
|
<CopyField
|
|
495
|
-
label={t("shareDialog.
|
|
496
|
-
value={
|
|
517
|
+
label={t("shareDialog.shareWithAgents")}
|
|
518
|
+
value={agentLink}
|
|
497
519
|
disabled={agentShareDisabled}
|
|
498
520
|
/>
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
521
|
+
{sharesLoaded ? (
|
|
522
|
+
<>
|
|
523
|
+
<p className="text-xs text-muted-foreground">
|
|
524
|
+
{t("shareDialog.agentTokenDescription")}
|
|
525
|
+
</p>
|
|
526
|
+
{agentLinkError ? (
|
|
527
|
+
<div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
|
|
528
|
+
<p className="text-xs text-muted-foreground">
|
|
529
|
+
{t("shareDialog.agentLinkUnavailable")}
|
|
530
|
+
</p>
|
|
531
|
+
<Button
|
|
532
|
+
type="button"
|
|
533
|
+
variant="ghost"
|
|
534
|
+
size="sm"
|
|
535
|
+
className="h-7"
|
|
536
|
+
onClick={() => void loadAgentContextUrl()}
|
|
537
|
+
disabled={createAgentLink.isPending}
|
|
538
|
+
>
|
|
539
|
+
{t("shareDialog.retryAgentLink")}
|
|
540
|
+
</Button>
|
|
541
|
+
</div>
|
|
542
|
+
) : null}
|
|
543
|
+
<CopyField
|
|
544
|
+
label={t("shareDialog.copyAgentPrompt")}
|
|
545
|
+
value={agentPrompt}
|
|
546
|
+
disabled={agentShareDisabled}
|
|
547
|
+
/>
|
|
548
|
+
</>
|
|
549
|
+
) : null}
|
|
550
|
+
</div>
|
|
551
|
+
</CollapsibleContent>
|
|
552
|
+
</Collapsible>
|
|
502
553
|
) : null}
|
|
503
554
|
|
|
504
|
-
{
|
|
555
|
+
{showMakePublic ? (
|
|
505
556
|
<MakePublicCard
|
|
506
557
|
isPending={isPending}
|
|
558
|
+
secondaryAction={
|
|
559
|
+
moveDownloadIntoMakePublicRow ? downloadButton : undefined
|
|
560
|
+
}
|
|
507
561
|
onMakePublic={() =>
|
|
508
562
|
setResourceVisibility("public", {
|
|
509
563
|
onSuccess: () => copyToClipboard(shareUrl),
|
|
@@ -512,7 +566,9 @@ function LinkTab({
|
|
|
512
566
|
/>
|
|
513
567
|
) : null}
|
|
514
568
|
|
|
515
|
-
{emailPreviewThumbnailUrl ||
|
|
569
|
+
{emailPreviewThumbnailUrl ||
|
|
570
|
+
animatedThumbnailUrl ||
|
|
571
|
+
(videoUrl && !moveDownloadIntoMakePublicRow) ? (
|
|
516
572
|
<div className="flex flex-wrap gap-2">
|
|
517
573
|
{emailPreviewThumbnailUrl ? (
|
|
518
574
|
<Button
|
|
@@ -532,25 +588,7 @@ function LinkTab({
|
|
|
532
588
|
{t("shareDialog.gifPreview")}
|
|
533
589
|
</Button>
|
|
534
590
|
) : null}
|
|
535
|
-
{
|
|
536
|
-
<Button
|
|
537
|
-
variant="outline"
|
|
538
|
-
size="sm"
|
|
539
|
-
className={isLoomRecording ? "gap-1.5" : undefined}
|
|
540
|
-
onClick={() =>
|
|
541
|
-
window.open(videoUrl, "_blank", "noopener,noreferrer")
|
|
542
|
-
}
|
|
543
|
-
>
|
|
544
|
-
{isLoomRecording ? (
|
|
545
|
-
<>
|
|
546
|
-
<IconExternalLink className="h-4 w-4" />
|
|
547
|
-
{t("shareDialog.openPlayer")}
|
|
548
|
-
</>
|
|
549
|
-
) : (
|
|
550
|
-
t("recordRoute.downloadRecording")
|
|
551
|
-
)}
|
|
552
|
-
</Button>
|
|
553
|
-
) : null}
|
|
591
|
+
{!moveDownloadIntoMakePublicRow ? downloadButton : null}
|
|
554
592
|
</div>
|
|
555
593
|
) : null}
|
|
556
594
|
</div>
|