@agent-native/core 0.154.5 → 0.155.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
- package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
- package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
- package/corpus/templates/analytics/AGENTS.md +3 -0
- package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
- package/corpus/templates/analytics/actions/bigquery.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
- package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
- package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/calendar/AGENTS.md +2 -0
- package/corpus/templates/calendar/DEVELOPING.md +4 -4
- package/corpus/templates/calendar/actions/get-settings.ts +4 -11
- package/corpus/templates/calendar/actions/update-availability.ts +15 -4
- package/corpus/templates/calendar/actions/update-settings.ts +15 -4
- package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
- package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
- package/corpus/templates/calendar/app/i18n-data.ts +30 -0
- package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
- package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
- package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
- package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
- package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
- package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
- package/corpus/templates/calendar/shared/api.ts +1 -0
- package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
- package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
- package/corpus/templates/calendar/shared/settings.ts +44 -0
- package/corpus/templates/clips/AGENTS.md +2 -0
- package/corpus/templates/clips/actions/add-comment.ts +5 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
- package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
- package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
- package/corpus/templates/clips/actions/update-comment.ts +9 -2
- package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
- package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
- package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
- package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
- package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
- package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
- package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
- package/corpus/templates/clips/app/root.tsx +2 -1
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +14 -7
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +8 -4
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
- package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +116 -19
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
- package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
- package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
- package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
- package/corpus/templates/clips/desktop/src/styles.css +117 -0
- package/corpus/templates/clips/server/db/index.ts +10 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/recordings.ts +3 -1
- package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
- package/corpus/templates/clips/server/plugins/db.ts +5 -0
- package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
- package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
- package/corpus/templates/content/actions/add-comment.ts +2 -1
- package/corpus/templates/content/actions/update-comment.ts +1 -1
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
- package/corpus/templates/design/actions/generate-design.ts +10 -7
- package/corpus/templates/design/actions/present-design-variants.ts +35 -6
- package/corpus/templates/design/actions/run-design-audit.ts +33 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
- package/corpus/templates/design/shared/design-review.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +66 -0
- package/corpus/templates/dispatch/.env.example +5 -0
- package/corpus/templates/dispatch/AGENTS.md +24 -19
- package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
- package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +23 -0
- package/corpus/templates/factory/actions/view-screen.ts +17 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
- package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
- package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
- package/corpus/templates/factory/app/routes/factory.tsx +41 -0
- package/corpus/templates/factory/package.json +1 -0
- package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
- package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
- package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
- package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
- package/corpus/templates/mail/vite.config.ts +7 -0
- package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
- package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
- package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
- package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
- package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
- package/dist/agent/production-agent.js +5 -1
- package/dist/agent/thread-data-builder.js +17 -1
- package/dist/cli/skills-content/help.js +6 -4
- package/dist/cli/skills.js +17 -2
- package/dist/cli/workspacify.d.ts +5 -0
- package/dist/cli/workspacify.js +65 -8
- package/dist/client/AgentPanel.d.ts +5 -0
- package/dist/client/AgentPanel.js +20 -4
- package/dist/client/AssistantChat.d.ts +21 -0
- package/dist/client/AssistantChat.js +66 -68
- package/dist/client/active-run-state.d.ts +14 -1
- package/dist/client/active-run-state.js +0 -0
- package/dist/client/agent-chat-adapter.js +107 -5
- package/dist/client/analytics.js +9 -2
- package/dist/client/chat/tool-call-display.js +4 -4
- package/dist/client/chat-first/apps-rail.js +9 -4
- package/dist/client/chat-first/primary-nav.d.ts +2 -1
- package/dist/client/chat-first/primary-nav.js +3 -3
- package/dist/client/chat-first/types.d.ts +5 -1
- package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
- package/dist/client/markdown/InlineMarkdown.js +65 -0
- package/dist/client/markdown/index.d.ts +1 -0
- package/dist/client/markdown/index.js +1 -0
- package/dist/client/review/ReviewThreadPanel.js +2 -1
- package/dist/client/run-stream-ownership.d.ts +39 -0
- package/dist/client/run-stream-ownership.js +65 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/feature-flags/index.d.ts +1 -1
- package/dist/feature-flags/index.js +1 -1
- package/dist/feature-flags/store.d.ts +10 -0
- package/dist/feature-flags/store.js +112 -1
- package/dist/guards/no-env-credentials.js +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations/adapters/slack.js +5 -1
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +1 -0
- package/dist/observability/routes.d.ts +6 -6
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/resources/agents.js +40 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/metadata.d.ts +10 -0
- package/dist/resources/metadata.js +7 -3
- package/dist/review/actions/create-review-comment.js +1 -1
- package/dist/review/actions/reply-review-comment.js +1 -1
- package/dist/review/actions/resolve-review-thread.js +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/action-routes.js +32 -6
- package/dist/server/agent-discovery.js +14 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.js +1 -1
- package/dist/server/identity-sso-migrations.d.ts +7 -0
- package/dist/server/identity-sso-migrations.js +36 -0
- package/dist/server/identity-sso-store.d.ts +3 -2
- package/dist/server/identity-sso-store.js +8 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +4 -0
- package/dist/tracking/providers.js +13 -3
- package/dist/workspace-files/tool.js +1 -1
- package/package.json +3 -2
|
@@ -4,8 +4,15 @@ import {
|
|
|
4
4
|
useActionQuery,
|
|
5
5
|
} from "@agent-native/core/client/hooks";
|
|
6
6
|
import { useT } from "@agent-native/core/client/i18n";
|
|
7
|
-
import { IconLink, IconMail } from "@tabler/icons-react";
|
|
8
|
-
import {
|
|
7
|
+
import { IconChevronDown, IconLink, IconMail } from "@tabler/icons-react";
|
|
8
|
+
import {
|
|
9
|
+
useCallback,
|
|
10
|
+
useEffect,
|
|
11
|
+
useMemo,
|
|
12
|
+
useRef,
|
|
13
|
+
useState,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from "react";
|
|
9
16
|
import { toast } from "sonner";
|
|
10
17
|
|
|
11
18
|
import {
|
|
@@ -19,6 +26,12 @@ import {
|
|
|
19
26
|
type SharesResponse,
|
|
20
27
|
type Visibility,
|
|
21
28
|
} from "@/components/sharing/share-ui";
|
|
29
|
+
import { Button } from "@/components/ui/button";
|
|
30
|
+
import {
|
|
31
|
+
Collapsible,
|
|
32
|
+
CollapsibleContent,
|
|
33
|
+
CollapsibleTrigger,
|
|
34
|
+
} from "@/components/ui/collapsible";
|
|
22
35
|
import {
|
|
23
36
|
Popover,
|
|
24
37
|
PopoverContent,
|
|
@@ -157,6 +170,56 @@ function LinkTab({
|
|
|
157
170
|
const visibility: Visibility =
|
|
158
171
|
(data?.visibility as Visibility | null) ?? "private";
|
|
159
172
|
const isPublic = visibility === "public";
|
|
173
|
+
const sharesLoaded = data?.visibility != null;
|
|
174
|
+
const createAgentLink = useActionMutation("create-agent-resource-link");
|
|
175
|
+
const createAgentLinkAsyncRef = useRef(createAgentLink.mutateAsync);
|
|
176
|
+
const agentLinkRequestIdRef = useRef(0);
|
|
177
|
+
const [agentLink, setAgentLink] = useState("");
|
|
178
|
+
const [agentLinkError, setAgentLinkError] = useState(false);
|
|
179
|
+
const [agentDetailsOpen, setAgentDetailsOpen] = useState(false);
|
|
180
|
+
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
createAgentLinkAsyncRef.current = createAgentLink.mutateAsync;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const loadAgentLink = useCallback(async () => {
|
|
186
|
+
const requestId = agentLinkRequestIdRef.current + 1;
|
|
187
|
+
agentLinkRequestIdRef.current = requestId;
|
|
188
|
+
setAgentLink("");
|
|
189
|
+
setAgentLinkError(false);
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const result = (await createAgentLinkAsyncRef.current({
|
|
193
|
+
resourceType: "meeting",
|
|
194
|
+
resourceId: meetingId,
|
|
195
|
+
})) as { contextUrl?: string };
|
|
196
|
+
if (agentLinkRequestIdRef.current !== requestId) return;
|
|
197
|
+
if (result?.contextUrl) setAgentLink(result.contextUrl);
|
|
198
|
+
else setAgentLinkError(true);
|
|
199
|
+
} catch {
|
|
200
|
+
if (agentLinkRequestIdRef.current === requestId) setAgentLinkError(true);
|
|
201
|
+
}
|
|
202
|
+
}, [meetingId]);
|
|
203
|
+
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
setAgentLink("");
|
|
206
|
+
setAgentLinkError(false);
|
|
207
|
+
if (!sharesLoaded || isPublic) return;
|
|
208
|
+
void loadAgentLink();
|
|
209
|
+
|
|
210
|
+
return () => {
|
|
211
|
+
agentLinkRequestIdRef.current += 1;
|
|
212
|
+
};
|
|
213
|
+
}, [isPublic, loadAgentLink, meetingId, sharesLoaded, visibility]);
|
|
214
|
+
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
if (isPublic) setAgentDetailsOpen(false);
|
|
217
|
+
}, [isPublic]);
|
|
218
|
+
|
|
219
|
+
const agentShareDisabled =
|
|
220
|
+
sharesQuery.isLoading ||
|
|
221
|
+
!sharesLoaded ||
|
|
222
|
+
(!isPublic && (createAgentLink.isPending || !agentLink));
|
|
160
223
|
|
|
161
224
|
useEffect(() => {
|
|
162
225
|
setIncludeTranscript(shareTranscript);
|
|
@@ -217,7 +280,15 @@ function LinkTab({
|
|
|
217
280
|
/>
|
|
218
281
|
</div>
|
|
219
282
|
|
|
220
|
-
<CopyField
|
|
283
|
+
<CopyField
|
|
284
|
+
label={
|
|
285
|
+
isPublic
|
|
286
|
+
? t("clipsFinalRaw.shareLink")
|
|
287
|
+
: t("shareDialog.shareWithHumans")
|
|
288
|
+
}
|
|
289
|
+
value={shareUrl}
|
|
290
|
+
disabled={!sharesLoaded}
|
|
291
|
+
/>
|
|
221
292
|
|
|
222
293
|
{!isPublic && canManage ? (
|
|
223
294
|
<MakePublicCard
|
|
@@ -229,6 +300,67 @@ function LinkTab({
|
|
|
229
300
|
}
|
|
230
301
|
/>
|
|
231
302
|
) : null}
|
|
303
|
+
|
|
304
|
+
{!isPublic ? (
|
|
305
|
+
<Collapsible
|
|
306
|
+
open={agentDetailsOpen}
|
|
307
|
+
onOpenChange={setAgentDetailsOpen}
|
|
308
|
+
className="overflow-hidden rounded-md border border-border"
|
|
309
|
+
>
|
|
310
|
+
<CollapsibleTrigger asChild>
|
|
311
|
+
<button
|
|
312
|
+
type="button"
|
|
313
|
+
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"
|
|
314
|
+
>
|
|
315
|
+
<span className="flex min-w-0 items-center gap-2 truncate">
|
|
316
|
+
<IconLink
|
|
317
|
+
aria-hidden="true"
|
|
318
|
+
className="size-3.5 shrink-0 text-muted-foreground"
|
|
319
|
+
strokeWidth={1.8}
|
|
320
|
+
/>
|
|
321
|
+
{t("shareDialog.shareWithAgents")}
|
|
322
|
+
</span>
|
|
323
|
+
<IconChevronDown
|
|
324
|
+
aria-hidden="true"
|
|
325
|
+
className={`size-4 shrink-0 text-muted-foreground transition-transform ${agentDetailsOpen ? "rotate-180" : ""}`}
|
|
326
|
+
/>
|
|
327
|
+
</button>
|
|
328
|
+
</CollapsibleTrigger>
|
|
329
|
+
<CollapsibleContent className="clips-collapsible-content border-t border-border px-3 py-3">
|
|
330
|
+
<div className="space-y-2">
|
|
331
|
+
<CopyField
|
|
332
|
+
label={t("shareDialog.shareWithAgents")}
|
|
333
|
+
value={agentLink}
|
|
334
|
+
disabled={agentShareDisabled}
|
|
335
|
+
/>
|
|
336
|
+
{sharesLoaded ? (
|
|
337
|
+
<>
|
|
338
|
+
<p className="text-xs text-muted-foreground">
|
|
339
|
+
{t("shareMeeting.agentLinkDescription")}
|
|
340
|
+
</p>
|
|
341
|
+
{agentLinkError ? (
|
|
342
|
+
<div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
|
|
343
|
+
<p className="text-xs text-muted-foreground">
|
|
344
|
+
{t("shareDialog.agentLinkUnavailable")}
|
|
345
|
+
</p>
|
|
346
|
+
<Button
|
|
347
|
+
type="button"
|
|
348
|
+
variant="ghost"
|
|
349
|
+
size="sm"
|
|
350
|
+
className="h-7"
|
|
351
|
+
onClick={() => void loadAgentLink()}
|
|
352
|
+
disabled={createAgentLink.isPending}
|
|
353
|
+
>
|
|
354
|
+
{t("shareDialog.retryAgentLink")}
|
|
355
|
+
</Button>
|
|
356
|
+
</div>
|
|
357
|
+
) : null}
|
|
358
|
+
</>
|
|
359
|
+
) : null}
|
|
360
|
+
</div>
|
|
361
|
+
</CollapsibleContent>
|
|
362
|
+
</Collapsible>
|
|
363
|
+
) : null}
|
|
232
364
|
</div>
|
|
233
365
|
);
|
|
234
366
|
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
useAvatarUrl,
|
|
4
4
|
} from "@agent-native/core/client/hooks";
|
|
5
5
|
import { useT } from "@agent-native/core/client/i18n";
|
|
6
|
+
import { InlineMarkdown } from "@agent-native/core/client/markdown";
|
|
6
7
|
import {
|
|
7
8
|
IconSend,
|
|
8
9
|
IconCheck,
|
|
@@ -895,7 +896,6 @@ function CommentCard({
|
|
|
895
896
|
return updatedReactions;
|
|
896
897
|
}
|
|
897
898
|
|
|
898
|
-
const commentContent = linkifyCommentContent(comment.content);
|
|
899
899
|
const avatarUrl = useAvatarUrl(comment.authorEmail);
|
|
900
900
|
|
|
901
901
|
return (
|
|
@@ -940,9 +940,10 @@ function CommentCard({
|
|
|
940
940
|
/>
|
|
941
941
|
) : (
|
|
942
942
|
<>
|
|
943
|
-
<
|
|
944
|
-
{
|
|
945
|
-
|
|
943
|
+
<InlineMarkdown
|
|
944
|
+
content={comment.content}
|
|
945
|
+
className="mt-0.5 text-sm text-foreground"
|
|
946
|
+
/>
|
|
946
947
|
|
|
947
948
|
<div className="flex items-center gap-2 mt-1.5 text-xs text-muted-foreground">
|
|
948
949
|
{canComment ? (
|
|
@@ -1070,101 +1071,6 @@ function CommentCard({
|
|
|
1070
1071
|
);
|
|
1071
1072
|
}
|
|
1072
1073
|
|
|
1073
|
-
const COMMENT_URL_PATTERN = /\b(?:https?:\/\/|www\.)[^\s<]+/gi;
|
|
1074
|
-
const ALWAYS_TRAILING_PUNCTUATION = new Set([
|
|
1075
|
-
".",
|
|
1076
|
-
",",
|
|
1077
|
-
"!",
|
|
1078
|
-
"?",
|
|
1079
|
-
";",
|
|
1080
|
-
":",
|
|
1081
|
-
"'",
|
|
1082
|
-
'"',
|
|
1083
|
-
]);
|
|
1084
|
-
const PAIRED_TRAILING_PUNCTUATION: Record<string, string> = {
|
|
1085
|
-
")": "(",
|
|
1086
|
-
"]": "[",
|
|
1087
|
-
"}": "{",
|
|
1088
|
-
};
|
|
1089
|
-
|
|
1090
|
-
function trimTrailingUrlPunctuation(value: string): {
|
|
1091
|
-
url: string;
|
|
1092
|
-
trailing: string;
|
|
1093
|
-
} {
|
|
1094
|
-
let end = value.length;
|
|
1095
|
-
|
|
1096
|
-
while (end > 0) {
|
|
1097
|
-
const lastCharacter = value[end - 1];
|
|
1098
|
-
if (ALWAYS_TRAILING_PUNCTUATION.has(lastCharacter)) {
|
|
1099
|
-
end -= 1;
|
|
1100
|
-
continue;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
const openingCharacter = PAIRED_TRAILING_PUNCTUATION[lastCharacter];
|
|
1104
|
-
if (openingCharacter) {
|
|
1105
|
-
const candidate = value.slice(0, end);
|
|
1106
|
-
const openingCount = candidate.split(openingCharacter).length - 1;
|
|
1107
|
-
const closingCount = candidate.split(lastCharacter).length - 1;
|
|
1108
|
-
if (closingCount > openingCount) {
|
|
1109
|
-
end -= 1;
|
|
1110
|
-
continue;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
break;
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
return {
|
|
1118
|
-
url: value.slice(0, end),
|
|
1119
|
-
trailing: value.slice(end),
|
|
1120
|
-
};
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
function linkifyCommentContent(content: string): React.ReactNode[] {
|
|
1124
|
-
const result: React.ReactNode[] = [];
|
|
1125
|
-
let lastIndex = 0;
|
|
1126
|
-
|
|
1127
|
-
for (const match of content.matchAll(COMMENT_URL_PATTERN)) {
|
|
1128
|
-
const matchIndex = match.index;
|
|
1129
|
-
const matchedText = match[0];
|
|
1130
|
-
const { url, trailing } = trimTrailingUrlPunctuation(matchedText);
|
|
1131
|
-
const href = url.toLowerCase().startsWith("www.") ? `https://${url}` : url;
|
|
1132
|
-
|
|
1133
|
-
let isValidUrl = false;
|
|
1134
|
-
try {
|
|
1135
|
-
isValidUrl = Boolean(new URL(href).hostname);
|
|
1136
|
-
} catch {
|
|
1137
|
-
// Leave malformed URL-like text unlinked.
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
if (!isValidUrl) continue;
|
|
1141
|
-
|
|
1142
|
-
if (matchIndex > lastIndex) {
|
|
1143
|
-
result.push(content.slice(lastIndex, matchIndex));
|
|
1144
|
-
}
|
|
1145
|
-
result.push(
|
|
1146
|
-
<a
|
|
1147
|
-
key={`${matchIndex}-${url}`}
|
|
1148
|
-
href={href}
|
|
1149
|
-
target="_blank"
|
|
1150
|
-
rel="noopener noreferrer"
|
|
1151
|
-
className="text-primary hover:underline"
|
|
1152
|
-
>
|
|
1153
|
-
{url}
|
|
1154
|
-
</a>,
|
|
1155
|
-
);
|
|
1156
|
-
if (trailing) result.push(trailing);
|
|
1157
|
-
|
|
1158
|
-
lastIndex = matchIndex + matchedText.length;
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
if (lastIndex < content.length) {
|
|
1162
|
-
result.push(content.slice(lastIndex));
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
return result;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
1074
|
function parseReactions(raw: string): Record<string, string[]> {
|
|
1169
1075
|
try {
|
|
1170
1076
|
const v = JSON.parse(raw ?? "{}");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useAvatarUrl } from "@agent-native/core/client/hooks";
|
|
2
|
+
import { InlineMarkdown } from "@agent-native/core/client/markdown";
|
|
2
3
|
|
|
3
4
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
4
5
|
import { cn } from "@/lib/utils";
|
|
@@ -100,9 +101,12 @@ export function PlaybackCommentOverlay({
|
|
|
100
101
|
<p className="truncate text-xs font-semibold text-white/80">
|
|
101
102
|
{author}
|
|
102
103
|
</p>
|
|
103
|
-
<
|
|
104
|
-
{comment.content}
|
|
105
|
-
|
|
104
|
+
<InlineMarkdown
|
|
105
|
+
content={comment.content}
|
|
106
|
+
className="line-clamp-3 text-sm leading-5 text-primary-foreground"
|
|
107
|
+
linkClassName="text-primary-foreground underline decoration-primary-foreground/60 hover:decoration-primary-foreground"
|
|
108
|
+
codeClassName="bg-primary-foreground/15 text-primary-foreground"
|
|
109
|
+
/>
|
|
106
110
|
</div>
|
|
107
111
|
</div>
|
|
108
112
|
{rest.length > 0 && (
|
|
@@ -353,6 +353,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
353
353
|
const [captionsOn, setCaptionsOn] = useState(false);
|
|
354
354
|
const [hasPlaybackStarted, setHasPlaybackStarted] = useState(false);
|
|
355
355
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
356
|
+
const nativeFullscreenRef = useRef(false);
|
|
356
357
|
const [isPip, setIsPip] = useState(false);
|
|
357
358
|
const [canPlay, setCanPlay] = useState(false);
|
|
358
359
|
const [isPlayPending, setIsPlayPending] = useState(false);
|
|
@@ -1362,33 +1363,83 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1362
1363
|
const el = containerRef.current;
|
|
1363
1364
|
const video = videoRef.current as WebkitFullscreenVideo | null;
|
|
1364
1365
|
if (!el) return;
|
|
1366
|
+
const hasNativeVideoFullscreen =
|
|
1367
|
+
typeof video?.webkitEnterFullscreen === "function";
|
|
1368
|
+
let nativeFullscreenAttempted = false;
|
|
1365
1369
|
try {
|
|
1366
|
-
if (
|
|
1367
|
-
if (
|
|
1368
|
-
video?.webkitDisplayingFullscreen ||
|
|
1369
|
-
(isFullscreen && video?.webkitExitFullscreen)
|
|
1370
|
-
) {
|
|
1371
|
-
video?.webkitExitFullscreen?.();
|
|
1372
|
-
setIsFullscreen(false);
|
|
1373
|
-
} else if (typeof el.requestFullscreen === "function") {
|
|
1374
|
-
await el.requestFullscreen();
|
|
1375
|
-
setIsFullscreen(true);
|
|
1376
|
-
} else if (video?.webkitEnterFullscreen) {
|
|
1377
|
-
// iPhone Safari exposes fullscreen on the video element, not the
|
|
1378
|
-
// containing div used by the custom player controls.
|
|
1379
|
-
video.webkitEnterFullscreen();
|
|
1380
|
-
setIsFullscreen(true);
|
|
1381
|
-
} else {
|
|
1382
|
-
console.warn("[clips] Fullscreen unavailable");
|
|
1383
|
-
}
|
|
1384
|
-
} else {
|
|
1370
|
+
if (document.fullscreenElement) {
|
|
1385
1371
|
await document.exitFullscreen();
|
|
1386
1372
|
setIsFullscreen(false);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
if (nativeFullscreenRef.current || video?.webkitDisplayingFullscreen) {
|
|
1377
|
+
video?.webkitExitFullscreen?.();
|
|
1378
|
+
nativeFullscreenRef.current = false;
|
|
1379
|
+
setIsFullscreen(false);
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// iPhone Safari exposes fullscreen on the video element, not the
|
|
1384
|
+
// containing div used by the custom player controls. Older versions
|
|
1385
|
+
// can expose requestFullscreen on the div without supporting it.
|
|
1386
|
+
const documentFullscreenUnavailable = !document.fullscreenEnabled;
|
|
1387
|
+
if (
|
|
1388
|
+
hasNativeVideoFullscreen &&
|
|
1389
|
+
(documentFullscreenUnavailable ||
|
|
1390
|
+
typeof el.requestFullscreen !== "function")
|
|
1391
|
+
) {
|
|
1392
|
+
nativeFullscreenAttempted = true;
|
|
1393
|
+
video.webkitEnterFullscreen?.();
|
|
1394
|
+
nativeFullscreenRef.current = true;
|
|
1395
|
+
setIsFullscreen(true);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
if (isFullscreen) {
|
|
1400
|
+
// Keep the control reversible when neither browser fullscreen API
|
|
1401
|
+
// is available and the player is using its fixed-viewport fallback.
|
|
1402
|
+
setIsFullscreen(false);
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
if (typeof el.requestFullscreen === "function") {
|
|
1407
|
+
await el.requestFullscreen();
|
|
1408
|
+
if (document.fullscreenElement || !hasNativeVideoFullscreen) {
|
|
1409
|
+
setIsFullscreen(true);
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// A few mobile WebKit versions resolve the container request but do
|
|
1414
|
+
// not enter fullscreen. Retry against the video before falling back
|
|
1415
|
+
// to the in-app fixed viewport.
|
|
1416
|
+
nativeFullscreenAttempted = true;
|
|
1417
|
+
video.webkitEnterFullscreen?.();
|
|
1418
|
+
nativeFullscreenRef.current = true;
|
|
1419
|
+
setIsFullscreen(true);
|
|
1420
|
+
return;
|
|
1387
1421
|
}
|
|
1422
|
+
|
|
1423
|
+
if (hasNativeVideoFullscreen) {
|
|
1424
|
+
nativeFullscreenAttempted = true;
|
|
1425
|
+
video.webkitEnterFullscreen?.();
|
|
1426
|
+
nativeFullscreenRef.current = true;
|
|
1427
|
+
setIsFullscreen(true);
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// Keep fullscreen usable in browsers that expose neither API.
|
|
1432
|
+
setIsFullscreen(true);
|
|
1388
1433
|
} catch (err) {
|
|
1389
|
-
if (
|
|
1434
|
+
if (
|
|
1435
|
+
!nativeFullscreenAttempted &&
|
|
1436
|
+
hasNativeVideoFullscreen &&
|
|
1437
|
+
!document.fullscreenElement
|
|
1438
|
+
) {
|
|
1390
1439
|
try {
|
|
1391
|
-
|
|
1440
|
+
nativeFullscreenAttempted = true;
|
|
1441
|
+
video.webkitEnterFullscreen?.();
|
|
1442
|
+
nativeFullscreenRef.current = true;
|
|
1392
1443
|
setIsFullscreen(true);
|
|
1393
1444
|
return;
|
|
1394
1445
|
} catch (fallbackErr) {
|
|
@@ -1396,14 +1447,21 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1396
1447
|
}
|
|
1397
1448
|
}
|
|
1398
1449
|
console.warn("[clips] Fullscreen failed", err);
|
|
1450
|
+
setIsFullscreen(true);
|
|
1399
1451
|
}
|
|
1400
1452
|
}
|
|
1401
1453
|
|
|
1402
1454
|
useEffect(() => {
|
|
1403
1455
|
const video = videoRef.current as WebkitFullscreenVideo | null;
|
|
1404
1456
|
const onFs = () => setIsFullscreen(!!document.fullscreenElement);
|
|
1405
|
-
const onNativeFsEnter = () =>
|
|
1406
|
-
|
|
1457
|
+
const onNativeFsEnter = () => {
|
|
1458
|
+
nativeFullscreenRef.current = true;
|
|
1459
|
+
setIsFullscreen(true);
|
|
1460
|
+
};
|
|
1461
|
+
const onNativeFsExit = () => {
|
|
1462
|
+
nativeFullscreenRef.current = false;
|
|
1463
|
+
setIsFullscreen(false);
|
|
1464
|
+
};
|
|
1407
1465
|
document.addEventListener("fullscreenchange", onFs);
|
|
1408
1466
|
video?.addEventListener("webkitbeginfullscreen", onNativeFsEnter);
|
|
1409
1467
|
video?.addEventListener("webkitendfullscreen", onNativeFsExit);
|
|
@@ -1456,7 +1514,9 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1456
1514
|
// width (see CenterPlaybackOverlay) so it isn't oversized inside
|
|
1457
1515
|
// small embeds like the Slack unfurl iframe.
|
|
1458
1516
|
"relative @container bg-black overflow-hidden select-none group",
|
|
1459
|
-
theaterMode
|
|
1517
|
+
theaterMode || isFullscreen
|
|
1518
|
+
? "fixed inset-0 z-40 h-dvh w-dvw"
|
|
1519
|
+
: "rounded-xl",
|
|
1460
1520
|
className,
|
|
1461
1521
|
)}
|
|
1462
1522
|
onMouseMove={bumpControls}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useFormatters, useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { InlineMarkdown } from "@agent-native/core/client/markdown";
|
|
2
3
|
import {
|
|
3
4
|
IconMessage,
|
|
4
5
|
IconMoodSmile,
|
|
@@ -87,9 +88,16 @@ export function NotificationsList({ items, onReply }: NotificationsListProps) {
|
|
|
87
88
|
</span>
|
|
88
89
|
</div>
|
|
89
90
|
{item.preview ? (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
item.kind === "comment" || item.kind === "mention" ? (
|
|
92
|
+
<InlineMarkdown
|
|
93
|
+
content={item.preview}
|
|
94
|
+
className="mt-1 line-clamp-2 text-sm text-muted-foreground"
|
|
95
|
+
/>
|
|
96
|
+
) : (
|
|
97
|
+
<div className="mt-1 line-clamp-2 text-sm text-muted-foreground">
|
|
98
|
+
{item.preview}
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
93
101
|
) : null}
|
|
94
102
|
<div className="mt-1.5 flex items-center gap-3 text-xs">
|
|
95
103
|
<Link
|
|
@@ -969,6 +969,8 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
969
969
|
includeTranscriptDescription:
|
|
970
970
|
"Anyone with access to this meeting can read the complete transcript.",
|
|
971
971
|
transcriptUnavailable: "The transcript isn't ready yet.",
|
|
972
|
+
agentLinkDescription:
|
|
973
|
+
"This temporary link lets agents read these meeting notes without making them public. It expires after two hours.",
|
|
972
974
|
transcript: "Transcript",
|
|
973
975
|
copyTranscript: "Copy transcript",
|
|
974
976
|
transcriptCopied: "Transcript copied",
|
|
@@ -1545,6 +1547,16 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
1545
1547
|
calendarConnected: "Calendar connected",
|
|
1546
1548
|
calendarDisconnected: "Calendar disconnected",
|
|
1547
1549
|
calendarSettings: "Calendar settings",
|
|
1550
|
+
calendarAccountsButton: "Calendars",
|
|
1551
|
+
connectedAccounts: "Connected accounts",
|
|
1552
|
+
calendarConnectedLabel: "Connected",
|
|
1553
|
+
calendarNeedsReconnectLabel: "Needs reconnect",
|
|
1554
|
+
calendarDisconnectedLabel: "Disconnected",
|
|
1555
|
+
calendarStatusUnavailable: "Status unavailable",
|
|
1556
|
+
reconnectCalendar: "Reconnect calendar",
|
|
1557
|
+
addAnotherCalendarAccount: "Add another account",
|
|
1558
|
+
connectCalendar: "Connect calendar",
|
|
1559
|
+
disconnectCalendarAccount: "Disconnect an account",
|
|
1548
1560
|
connectCalendarReminder: "Connect Google Calendar for meeting reminders.",
|
|
1549
1561
|
disconnectGoogleCalendarTitle: "Disconnect Google Calendar?",
|
|
1550
1562
|
title: "Meetings",
|
|
@@ -2,6 +2,8 @@ import { appBasePath } from "@agent-native/core/client/api-path";
|
|
|
2
2
|
|
|
3
3
|
import type { TranscriptSegment } from "@/components/meetings/transcript-bubbles";
|
|
4
4
|
|
|
5
|
+
import { CLIPS_MEETING_AGENT_ACCESS_PARAM } from "../../shared/meeting-agent-access";
|
|
6
|
+
|
|
5
7
|
export interface PublicMeetingBullet {
|
|
6
8
|
text: string;
|
|
7
9
|
}
|
|
@@ -59,9 +61,13 @@ export function publicMeetingUrl(
|
|
|
59
61
|
meetingId: string,
|
|
60
62
|
origin: string,
|
|
61
63
|
basePath: string,
|
|
64
|
+
agentAccessToken?: string,
|
|
62
65
|
): string {
|
|
63
66
|
const url = new URL(`${basePath}/api/public-meeting`, origin);
|
|
64
67
|
url.searchParams.set("id", meetingId);
|
|
68
|
+
if (agentAccessToken) {
|
|
69
|
+
url.searchParams.set(CLIPS_MEETING_AGENT_ACCESS_PARAM, agentAccessToken);
|
|
70
|
+
}
|
|
65
71
|
return url.toString();
|
|
66
72
|
}
|
|
67
73
|
|
|
@@ -71,13 +77,15 @@ export async function fetchPublicMeeting(
|
|
|
71
77
|
signal?: AbortSignal;
|
|
72
78
|
origin?: string;
|
|
73
79
|
basePath?: string;
|
|
80
|
+
agentAccessToken?: string;
|
|
74
81
|
} = {},
|
|
75
82
|
): Promise<PublicMeetingResult> {
|
|
76
83
|
const origin = options.origin ?? window.location.origin;
|
|
77
84
|
const basePath = options.basePath ?? appBasePath();
|
|
78
|
-
const response = await fetch(
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
const response = await fetch(
|
|
86
|
+
publicMeetingUrl(meetingId, origin, basePath, options.agentAccessToken),
|
|
87
|
+
{ signal: options.signal },
|
|
88
|
+
);
|
|
81
89
|
const data = (await response.json().catch(() => ({}))) as
|
|
82
90
|
| PublicMeetingPayload
|
|
83
91
|
| { error?: string };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const SEARCH_FOCUS_PARAM = "focus";
|
|
2
|
+
export const SEARCH_FOCUS_VALUE = "search";
|
|
3
|
+
export const SEARCH_FOCUS_PATH = `/library?${SEARCH_FOCUS_PARAM}=${SEARCH_FOCUS_VALUE}`;
|
|
4
|
+
|
|
5
|
+
export function hasSearchFocusRequest(params: URLSearchParams): boolean {
|
|
6
|
+
return params.get(SEARCH_FOCUS_PARAM) === SEARCH_FOCUS_VALUE;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function clearSearchFocusRequest(
|
|
10
|
+
params: URLSearchParams,
|
|
11
|
+
): URLSearchParams {
|
|
12
|
+
const next = new URLSearchParams(params);
|
|
13
|
+
next.delete(SEARCH_FOCUS_PARAM);
|
|
14
|
+
return next;
|
|
15
|
+
}
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
import { Toaster } from "@/components/ui/sonner";
|
|
55
55
|
import { AppToolkitProvider } from "@/components/ui/toolkit-provider";
|
|
56
56
|
import { useNavigationState } from "@/hooks/use-navigation-state";
|
|
57
|
+
import { SEARCH_FOCUS_PATH } from "@/lib/search-focus";
|
|
57
58
|
|
|
58
59
|
import { i18nCatalog, loadI18nMessages } from "./i18n";
|
|
59
60
|
|
|
@@ -374,7 +375,7 @@ function AppContent() {
|
|
|
374
375
|
<IconHierarchy2 size={16} />
|
|
375
376
|
{t("root.openAgent")}
|
|
376
377
|
</CommandMenu.Item>
|
|
377
|
-
<CommandMenu.Item onSelect={() =>
|
|
378
|
+
<CommandMenu.Item onSelect={() => navigate(SEARCH_FOCUS_PATH)}>
|
|
378
379
|
{t("root.commandSearch")}
|
|
379
380
|
</CommandMenu.Item>
|
|
380
381
|
</CommandMenu.Group>
|