@agent-native/core 0.159.2 → 0.159.5
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/clips/.agents/skills/meetings/SKILL.md +11 -6
- package/corpus/templates/clips/AGENTS.md +1 -0
- package/corpus/templates/clips/actions/lib/meeting-content.ts +15 -4
- package/corpus/templates/clips/actions/list-meetings.ts +179 -42
- package/corpus/templates/clips/actions/search-meetings.ts +277 -0
- package/corpus/templates/clips/app/components/meetings/agenda-card.tsx +273 -0
- package/corpus/templates/clips/app/components/meetings/day-grouped-card.tsx +113 -0
- package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +117 -0
- package/corpus/templates/clips/app/hooks/use-navigation-state.ts +12 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -2
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +263 -357
- package/corpus/templates/clips/changelog/2026-08-14-meetings-history-is-searchable-again.md +6 -0
- package/corpus/templates/clips/desktop/design-refs/granola-ux.md +17 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +29 -2
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +11 -11
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +11 -50
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +57 -20
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +30 -0
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +204 -51
- package/corpus/templates/content/app/global.css +4 -1
- package/corpus/templates/content/app/i18n-data.ts +30 -0
- package/corpus/templates/content/changelog/2026-08-12-toggle-blocks-now-follow-notion-style-enter-and-shift-tab-be.md +6 -0
- package/corpus/templates/content/docs/solutions/2026-08-12-toggle-summary-focus-persistence-shape.md +733 -0
- package/corpus/templates/content/shared/builder-mdx.ts +44 -4
- package/corpus/templates/slides/actions/get-layout-overflows.ts +65 -2
- package/dist/client/chat-first/app-pane.js +1 -1
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +6 -4
- package/dist/deploy/build.js +27 -11
- package/dist/feature-flags/index.d.ts +0 -2
- package/dist/feature-flags/index.js +3 -2
- package/dist/feature-flags/server.d.ts +2 -0
- package/dist/feature-flags/server.js +2 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.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/auth.js +10 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/onboarding-html.js +16 -257
- package/dist/shared/auth-copy.d.ts +34 -0
- package/dist/shared/auth-copy.js +293 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/docs/content/actions-advanced.mdx +1 -1
- package/docs/content/portal.mdx +21 -8
- package/package.json +4 -2
- package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +0 -333
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
-
import {
|
|
3
|
-
IconCheck,
|
|
4
|
-
IconClock,
|
|
5
|
-
IconExternalLink,
|
|
6
|
-
IconNotes,
|
|
7
|
-
IconVideo,
|
|
8
|
-
} from "@tabler/icons-react";
|
|
9
|
-
/**
|
|
10
|
-
* <MeetingCard /> — Granola-style meeting tile.
|
|
11
|
-
*
|
|
12
|
-
* Renders title, time, attendee stack, status pills (Live / Transcript ready
|
|
13
|
-
* / Notes ready), and a 1-2 line summary preview. Hover lifts the card.
|
|
14
|
-
*/
|
|
15
|
-
import { NavLink } from "react-router";
|
|
16
|
-
|
|
17
|
-
import { Badge } from "@/components/ui/badge";
|
|
18
|
-
import { Button } from "@/components/ui/button";
|
|
19
|
-
import { Card, CardContent } from "@/components/ui/card";
|
|
20
|
-
import { cn } from "@/lib/utils";
|
|
21
|
-
|
|
22
|
-
import { AttendeeStack, type AttendeeStackParticipant } from "./attendee-stack";
|
|
23
|
-
|
|
24
|
-
export interface MeetingCardData {
|
|
25
|
-
id: string;
|
|
26
|
-
title: string;
|
|
27
|
-
scheduledStart: string;
|
|
28
|
-
scheduledEnd?: string | null;
|
|
29
|
-
actualStart?: string | null;
|
|
30
|
-
actualEnd?: string | null;
|
|
31
|
-
recordingId?: string | null;
|
|
32
|
-
transcriptStatus?:
|
|
33
|
-
| "pending"
|
|
34
|
-
| "ready"
|
|
35
|
-
| "failed"
|
|
36
|
-
| "in_progress"
|
|
37
|
-
| string
|
|
38
|
-
| null;
|
|
39
|
-
summaryPreview?: string | null;
|
|
40
|
-
summaryMd?: string | null;
|
|
41
|
-
participants?: AttendeeStackParticipant[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
type Translate = (key: string, params?: Record<string, unknown>) => string;
|
|
45
|
-
|
|
46
|
-
function formatTime(iso?: string | null): string {
|
|
47
|
-
if (!iso) return "";
|
|
48
|
-
try {
|
|
49
|
-
return new Date(iso).toLocaleTimeString([], {
|
|
50
|
-
hour: "numeric",
|
|
51
|
-
minute: "2-digit",
|
|
52
|
-
});
|
|
53
|
-
} catch {
|
|
54
|
-
return "";
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function buildPreview(m: MeetingCardData): string | null {
|
|
59
|
-
if (m.summaryPreview && m.summaryPreview.trim()) return m.summaryPreview;
|
|
60
|
-
if (m.summaryMd && m.summaryMd.trim()) {
|
|
61
|
-
// Strip simple markdown markers and collapse whitespace for the preview.
|
|
62
|
-
const plain = m.summaryMd
|
|
63
|
-
.replace(/[#*`>_~-]+/g, " ")
|
|
64
|
-
.replace(/\s+/g, " ")
|
|
65
|
-
.trim();
|
|
66
|
-
return plain.slice(0, 220);
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function MeetingCard({ meeting }: { meeting: MeetingCardData }) {
|
|
72
|
-
const t = useT();
|
|
73
|
-
const isLive = !!(
|
|
74
|
-
(meeting.actualStart && !meeting.actualEnd) ||
|
|
75
|
-
meeting.transcriptStatus === "in_progress"
|
|
76
|
-
);
|
|
77
|
-
const transcriptReady = meeting.transcriptStatus === "ready";
|
|
78
|
-
const hasNotes = !!meeting.summaryMd;
|
|
79
|
-
const preview = buildPreview(meeting);
|
|
80
|
-
const now = Date.now();
|
|
81
|
-
const scheduledEndMs = Date.parse(
|
|
82
|
-
meeting.scheduledEnd ?? meeting.scheduledStart,
|
|
83
|
-
);
|
|
84
|
-
const meetingHasEnded =
|
|
85
|
-
!!meeting.actualEnd ||
|
|
86
|
-
(!Number.isNaN(scheduledEndMs) && scheduledEndMs < now);
|
|
87
|
-
const shouldShowMissingSummary =
|
|
88
|
-
!preview &&
|
|
89
|
-
(meetingHasEnded || !!meeting.recordingId || transcriptReady || isLive);
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<NavLink
|
|
93
|
-
to={`/meetings/${meeting.id}`}
|
|
94
|
-
className="group block focus:outline-none"
|
|
95
|
-
>
|
|
96
|
-
<Card
|
|
97
|
-
className={cn(
|
|
98
|
-
"cursor-pointer transition-[transform,box-shadow,border-color] duration-150",
|
|
99
|
-
"hover:border-foreground/20 hover:shadow-sm hover:-translate-y-px",
|
|
100
|
-
"bg-background",
|
|
101
|
-
)}
|
|
102
|
-
>
|
|
103
|
-
<CardContent className="p-4 space-y-2.5">
|
|
104
|
-
<div className="flex items-start justify-between gap-2">
|
|
105
|
-
<h3 className="text-sm font-medium leading-snug line-clamp-2 flex-1 text-foreground">
|
|
106
|
-
{meeting.title || t("meetingDetail.untitledMeeting")}
|
|
107
|
-
</h3>
|
|
108
|
-
<div className="flex items-center gap-1 shrink-0">
|
|
109
|
-
{isLive ? (
|
|
110
|
-
<Badge
|
|
111
|
-
variant="secondary"
|
|
112
|
-
className="bg-red-500/10 text-red-600 border-red-500/20 text-[10px] gap-1 font-medium px-1.5"
|
|
113
|
-
>
|
|
114
|
-
<span className="relative flex h-1.5 w-1.5">
|
|
115
|
-
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-500 opacity-60" />
|
|
116
|
-
<span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-red-500" />
|
|
117
|
-
</span>
|
|
118
|
-
{t("meetingCard.live")}
|
|
119
|
-
</Badge>
|
|
120
|
-
) : transcriptReady ? (
|
|
121
|
-
<Badge
|
|
122
|
-
variant="secondary"
|
|
123
|
-
className="bg-emerald-500/10 text-emerald-600 border-emerald-500/20 text-[10px] gap-1 px-1.5"
|
|
124
|
-
>
|
|
125
|
-
<IconCheck className="h-3 w-3" />
|
|
126
|
-
{t("meetingCard.transcript")}
|
|
127
|
-
</Badge>
|
|
128
|
-
) : null}
|
|
129
|
-
{hasNotes && !isLive && (
|
|
130
|
-
<Badge
|
|
131
|
-
variant="secondary"
|
|
132
|
-
className="bg-amber-500/10 text-amber-700 border-amber-500/20 text-[10px] gap-1 px-1.5"
|
|
133
|
-
title={t("meetingCard.aiNotesReady")}
|
|
134
|
-
>
|
|
135
|
-
<IconNotes className="h-3 w-3" />
|
|
136
|
-
</Badge>
|
|
137
|
-
)}
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
|
|
141
|
-
<div className="flex items-center gap-1.5 text-xs text-muted-foreground tabular-nums">
|
|
142
|
-
<IconClock className="h-3.5 w-3.5" />
|
|
143
|
-
<span>{formatTime(meeting.scheduledStart)}</span>
|
|
144
|
-
{meeting.scheduledEnd && (
|
|
145
|
-
<span>– {formatTime(meeting.scheduledEnd)}</span>
|
|
146
|
-
)}
|
|
147
|
-
</div>
|
|
148
|
-
|
|
149
|
-
{preview ? (
|
|
150
|
-
<p className="text-xs text-muted-foreground leading-relaxed line-clamp-2">
|
|
151
|
-
{preview}
|
|
152
|
-
</p>
|
|
153
|
-
) : shouldShowMissingSummary ? (
|
|
154
|
-
<p className="text-xs text-muted-foreground/60 italic leading-relaxed">
|
|
155
|
-
{t("meetingCard.noSummary")}
|
|
156
|
-
</p>
|
|
157
|
-
) : null}
|
|
158
|
-
|
|
159
|
-
<div className="flex items-center justify-between gap-2 pt-1">
|
|
160
|
-
<AttendeeStack
|
|
161
|
-
participants={meeting.participants ?? []}
|
|
162
|
-
size="xs"
|
|
163
|
-
/>
|
|
164
|
-
{meeting.recordingId && (
|
|
165
|
-
<span className="flex items-center gap-1 text-[10px] text-muted-foreground">
|
|
166
|
-
<IconVideo className="h-3 w-3" />
|
|
167
|
-
{t("meetingCard.transcriptSource")}
|
|
168
|
-
</span>
|
|
169
|
-
)}
|
|
170
|
-
</div>
|
|
171
|
-
</CardContent>
|
|
172
|
-
</Card>
|
|
173
|
-
</NavLink>
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export interface UpcomingMeetingCardData {
|
|
178
|
-
id: string;
|
|
179
|
-
title: string;
|
|
180
|
-
scheduledStart: string;
|
|
181
|
-
scheduledEnd?: string | null;
|
|
182
|
-
actualStart?: string | null;
|
|
183
|
-
actualEnd?: string | null;
|
|
184
|
-
joinUrl?: string | null;
|
|
185
|
-
platform?: string | null;
|
|
186
|
-
participants?: AttendeeStackParticipant[];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** Human "starts in 5 min" / "now" / "in 2 hrs" label for an upcoming card. */
|
|
190
|
-
function relativeStartLabel(
|
|
191
|
-
iso: string,
|
|
192
|
-
t: Translate,
|
|
193
|
-
): { text: string; soon: boolean } {
|
|
194
|
-
const start = Date.parse(iso);
|
|
195
|
-
if (Number.isNaN(start)) return { text: "", soon: false };
|
|
196
|
-
const diffMin = Math.round((start - Date.now()) / 60000);
|
|
197
|
-
if (diffMin <= 0 && diffMin > -120)
|
|
198
|
-
return { text: t("meetingCard.now"), soon: true };
|
|
199
|
-
if (diffMin <= 0) return { text: t("meetingCard.started"), soon: false };
|
|
200
|
-
if (diffMin < 60)
|
|
201
|
-
return {
|
|
202
|
-
text: t("meetingCard.inMinutes", { count: diffMin }),
|
|
203
|
-
soon: diffMin <= 5,
|
|
204
|
-
};
|
|
205
|
-
const hrs = Math.round(diffMin / 60);
|
|
206
|
-
return { text: t("meetingCard.inHours", { count: hrs }), soon: false };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* <UpcomingMeetingCard /> — a not-yet-recorded calendar event (Granola-style).
|
|
211
|
-
* Recording is a native Clips desktop-app gesture, so the card just opens the
|
|
212
|
-
* meeting's notes ("Open notes") and links out to the call ("Join"). Buttons are
|
|
213
|
-
* NavLink/<a> siblings (never nested anchors) so the markup stays valid.
|
|
214
|
-
*/
|
|
215
|
-
export function UpcomingMeetingCard({
|
|
216
|
-
meeting,
|
|
217
|
-
}: {
|
|
218
|
-
meeting: UpcomingMeetingCardData;
|
|
219
|
-
}) {
|
|
220
|
-
const t = useT();
|
|
221
|
-
const isLive = !!(meeting.actualStart && !meeting.actualEnd);
|
|
222
|
-
const { text: whenText, soon } = relativeStartLabel(
|
|
223
|
-
meeting.scheduledStart,
|
|
224
|
-
t,
|
|
225
|
-
);
|
|
226
|
-
const joinable = soon || isLive;
|
|
227
|
-
|
|
228
|
-
return (
|
|
229
|
-
<Card
|
|
230
|
-
className={cn(
|
|
231
|
-
"transition-[transform,box-shadow,border-color] duration-150 hover:border-foreground/20 hover:shadow-sm",
|
|
232
|
-
joinable && "border-foreground/25",
|
|
233
|
-
)}
|
|
234
|
-
>
|
|
235
|
-
<CardContent className="p-4 space-y-2.5">
|
|
236
|
-
<NavLink
|
|
237
|
-
to={`/meetings/${meeting.id}`}
|
|
238
|
-
className="block group focus:outline-none"
|
|
239
|
-
>
|
|
240
|
-
<div className="flex items-start justify-between gap-2">
|
|
241
|
-
<h3 className="text-sm font-medium leading-snug line-clamp-2 flex-1 text-foreground group-hover:text-foreground">
|
|
242
|
-
{meeting.title || t("meetingDetail.untitledMeeting")}
|
|
243
|
-
</h3>
|
|
244
|
-
{isLive ? (
|
|
245
|
-
<Badge
|
|
246
|
-
variant="secondary"
|
|
247
|
-
className="bg-red-500/10 text-red-600 border-red-500/20 text-[10px] gap-1 font-medium px-1.5 shrink-0"
|
|
248
|
-
>
|
|
249
|
-
<span className="relative flex h-1.5 w-1.5">
|
|
250
|
-
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-500 opacity-60" />
|
|
251
|
-
<span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-red-500" />
|
|
252
|
-
</span>
|
|
253
|
-
{t("meetingCard.live")}
|
|
254
|
-
</Badge>
|
|
255
|
-
) : (
|
|
256
|
-
<span
|
|
257
|
-
className={cn(
|
|
258
|
-
"shrink-0 text-[11px] tabular-nums",
|
|
259
|
-
soon
|
|
260
|
-
? "text-foreground font-medium"
|
|
261
|
-
: "text-muted-foreground",
|
|
262
|
-
)}
|
|
263
|
-
>
|
|
264
|
-
{whenText}
|
|
265
|
-
</span>
|
|
266
|
-
)}
|
|
267
|
-
</div>
|
|
268
|
-
<div className="mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground tabular-nums">
|
|
269
|
-
<IconClock className="h-3.5 w-3.5" />
|
|
270
|
-
<span>{formatTime(meeting.scheduledStart)}</span>
|
|
271
|
-
{meeting.scheduledEnd && (
|
|
272
|
-
<span>– {formatTime(meeting.scheduledEnd)}</span>
|
|
273
|
-
)}
|
|
274
|
-
</div>
|
|
275
|
-
<p className="mt-1.5 text-[11px] leading-snug text-muted-foreground">
|
|
276
|
-
{soon || isLive
|
|
277
|
-
? t("meetingCard.startFromDesktopNow")
|
|
278
|
-
: t("meetingCard.startFromDesktopLater")}
|
|
279
|
-
</p>
|
|
280
|
-
</NavLink>
|
|
281
|
-
|
|
282
|
-
<div className="flex items-center justify-between gap-2 pt-1">
|
|
283
|
-
<AttendeeStack participants={meeting.participants ?? []} size="xs" />
|
|
284
|
-
<div className="flex items-center gap-1.5">
|
|
285
|
-
{meeting.joinUrl && (
|
|
286
|
-
<a
|
|
287
|
-
href={meeting.joinUrl}
|
|
288
|
-
target="_blank"
|
|
289
|
-
rel="noopener noreferrer"
|
|
290
|
-
onClick={(e) => e.stopPropagation()}
|
|
291
|
-
>
|
|
292
|
-
<Button
|
|
293
|
-
size="sm"
|
|
294
|
-
variant="outline"
|
|
295
|
-
className="h-7 gap-1 px-2 text-xs cursor-pointer"
|
|
296
|
-
tabIndex={-1}
|
|
297
|
-
>
|
|
298
|
-
<IconExternalLink className="h-3.5 w-3.5" />
|
|
299
|
-
{t("meetingCard.join")}
|
|
300
|
-
</Button>
|
|
301
|
-
</a>
|
|
302
|
-
)}
|
|
303
|
-
<Button
|
|
304
|
-
asChild
|
|
305
|
-
size="sm"
|
|
306
|
-
variant={isLive ? "default" : "secondary"}
|
|
307
|
-
className="h-7 gap-1 px-2.5 text-xs cursor-pointer"
|
|
308
|
-
>
|
|
309
|
-
<NavLink to={`/meetings/${meeting.id}`}>
|
|
310
|
-
{t("meetingCard.openNotes")}
|
|
311
|
-
</NavLink>
|
|
312
|
-
</Button>
|
|
313
|
-
</div>
|
|
314
|
-
</div>
|
|
315
|
-
</CardContent>
|
|
316
|
-
</Card>
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function MeetingCardSkeleton() {
|
|
321
|
-
return (
|
|
322
|
-
<div className="rounded-xl border border-border bg-background p-4 space-y-2.5">
|
|
323
|
-
<div className="h-4 w-3/4 rounded bg-muted animate-pulse" />
|
|
324
|
-
<div className="h-3 w-24 rounded bg-muted animate-pulse" />
|
|
325
|
-
<div className="h-3 w-full rounded bg-muted/70 animate-pulse" />
|
|
326
|
-
<div className="h-3 w-5/6 rounded bg-muted/70 animate-pulse" />
|
|
327
|
-
<div className="flex justify-between pt-1">
|
|
328
|
-
<div className="h-5 w-20 rounded-full bg-muted animate-pulse" />
|
|
329
|
-
<div className="h-3 w-16 rounded bg-muted animate-pulse" />
|
|
330
|
-
</div>
|
|
331
|
-
</div>
|
|
332
|
-
);
|
|
333
|
-
}
|