@agent-native/core 0.164.3 → 0.164.11
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/calendar/app/components/calendar/CreateEventDialog.tsx +36 -47
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +18 -1
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +256 -38
- package/corpus/templates/calendar/app/hooks/use-google-auth.ts +43 -3
- package/corpus/templates/calendar/app/i18n-data.ts +120 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +113 -0
- package/corpus/templates/calendar/server/handlers/google-auth.ts +119 -3
- package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +1 -1
- package/corpus/templates/clips/app/components/player/video-player.tsx +12 -0
- package/corpus/templates/clips/app/routes/_app.dictate.tsx +18 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +114 -20
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -78
- package/corpus/templates/clips/changelog/2026-08-19-clips-nightly-builds-are-available-for-trying-the-latest-upd.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +6 -0
- package/corpus/templates/clips/desktop/README.md +19 -4
- package/corpus/templates/clips/desktop/src/app.tsx +78 -16
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -2
- package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -0
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +7 -13
- package/corpus/templates/clips/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +41 -3
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +22 -1
- package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +41 -22
- package/corpus/templates/clips/shared/share-meta.ts +67 -3
- package/corpus/templates/content/actions/list-content-databases.ts +95 -1
- package/corpus/templates/content/changelog/2026-08-11-content-mutations-delegated-from-verified-slack-dms-now-use-.md +6 -0
- package/corpus/templates/content/shared/api.ts +11 -0
- package/corpus/templates/factory/AGENTS.md +1 -1
- package/corpus/templates/factory/actions/list-factory-automations.ts +4 -0
- package/corpus/templates/factory/actions/start-builder-for-item.ts +19 -17
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +100 -36
- package/corpus/templates/factory/app/i18n/en-US.ts +4 -0
- package/corpus/templates/factory/app/routes/factory.tsx +209 -46
- package/corpus/templates/factory/server/connectors/slack.ts +44 -2
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -7
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +13 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +5 -0
- package/corpus/templates/factory/server/triage/slack-poller.ts +72 -4
- package/corpus/templates/mail/actions/view-screen.ts +1 -1
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -18
- package/corpus/templates/mail/server/handlers/google-auth.ts +125 -24
- package/corpus/templates/mail/server/lib/gmail-query.ts +5 -2
- package/corpus/templates/mail/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +0 -12
- package/corpus/templates/plan/changelog/2026-08-19-plan-prompts-now-guide-you-to-connect-ai-before-sending-and-.md +6 -0
- package/corpus/templates/slides/actions/approve-deck-access-request.ts +234 -0
- package/corpus/templates/slides/actions/get-deck-access-status.ts +35 -2
- package/corpus/templates/slides/actions/request-deck-access.ts +263 -64
- package/corpus/templates/slides/app/components/editor/MissingDeckAccessPane.tsx +196 -0
- package/corpus/templates/slides/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
- package/corpus/templates/slides/app/hooks/use-deck-access.ts +7 -4
- package/corpus/templates/slides/app/i18n/en-US.ts +29 -6
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +147 -169
- package/corpus/templates/slides/app/routes/access-request.approve.tsx +250 -0
- package/corpus/templates/slides/server/db/schema.ts +8 -0
- package/corpus/templates/slides/server/lib/access-request-email.ts +36 -0
- package/corpus/templates/slides/server/lib/emails.ts +26 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +7 -0
- package/corpus/templates/slides/server/plugins/db.ts +50 -0
- package/corpus/templates/slides/shared/deck-access.ts +31 -0
- package/dist/a2a/artifact-response.d.ts +50 -0
- package/dist/a2a/artifact-response.js +266 -39
- package/dist/a2a/index.d.ts +2 -0
- package/dist/a2a/index.js +1 -0
- package/dist/a2a/task-store.d.ts +1 -0
- package/dist/a2a/task-store.js +1 -1
- package/dist/agent/observational-memory/store.d.ts +1 -0
- package/dist/agent/observational-memory/store.js +1 -1
- package/dist/agent/run-store.d.ts +1 -0
- package/dist/agent/run-store.js +1 -1
- package/dist/agent/tool-approval-store.d.ts +1 -0
- package/dist/agent/tool-approval-store.js +1 -1
- package/dist/application-state/store.d.ts +1 -0
- package/dist/application-state/store.js +1 -1
- package/dist/browser-sessions/store.d.ts +1 -0
- package/dist/browser-sessions/store.js +1 -1
- package/dist/checkpoints/store.d.ts +1 -0
- package/dist/checkpoints/store.js +1 -1
- package/dist/cli/code-agent-schedules.d.ts +11 -0
- package/dist/cli/code-agent-schedules.js +44 -5
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AssistantChat.d.ts +5 -1
- package/dist/client/AssistantChat.js +40 -6
- package/dist/client/EnvironmentBadge.d.ts +7 -13
- package/dist/client/EnvironmentBadge.js +43 -92
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/agent-chat-adapter.js +76 -50
- package/dist/client/app-providers.js +4 -4
- package/dist/client/chat/message-components.d.ts +4 -0
- package/dist/client/chat/message-components.js +28 -1
- package/dist/client/chat/run-recovery.js +26 -0
- package/dist/client/chat-first/apps-rail.d.ts +1 -0
- package/dist/client/chat-first/apps-rail.js +28 -2
- package/dist/client/sse-event-processor.js +13 -8
- package/dist/client/use-active-agent-chat-run.d.ts +2 -0
- package/dist/client/use-active-agent-chat-run.js +22 -0
- package/dist/coding-tools/sandbox/executions-store.d.ts +1 -0
- package/dist/coding-tools/sandbox/executions-store.js +1 -1
- package/dist/collab/awareness-store.d.ts +1 -0
- package/dist/collab/awareness-store.js +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/storage.d.ts +1 -0
- package/dist/collab/storage.js +1 -1
- package/dist/db/client.js +3 -3
- package/dist/db/ddl-guard.js +8 -0
- package/dist/db/migration-runtime.d.ts +23 -0
- package/dist/db/migration-runtime.js +41 -0
- package/dist/db/migrations.d.ts +1 -2
- package/dist/db/migrations.js +2 -24
- package/dist/email-catalog/log.d.ts +1 -0
- package/dist/email-catalog/log.js +1 -1
- package/dist/guards/index.d.ts +1 -0
- package/dist/guards/index.js +1 -0
- package/dist/guards/release-schema-complete.d.ts +22 -0
- package/dist/guards/release-schema-complete.js +144 -0
- package/dist/integrations/a2a-continuations-store.d.ts +1 -0
- package/dist/integrations/a2a-continuations-store.js +1 -1
- package/dist/integrations/adapters/slack.js +58 -7
- package/dist/integrations/awaiting-input-store.d.ts +1 -0
- package/dist/integrations/awaiting-input-store.js +1 -1
- package/dist/integrations/config-store.d.ts +1 -0
- package/dist/integrations/config-store.js +1 -1
- package/dist/integrations/controls-store.d.ts +1 -0
- package/dist/integrations/controls-store.js +1 -1
- package/dist/integrations/identity-links-store.d.ts +1 -0
- package/dist/integrations/identity-links-store.js +1 -1
- package/dist/integrations/installations-store.d.ts +1 -0
- package/dist/integrations/installations-store.js +1 -1
- package/dist/integrations/remote-commands-store.d.ts +1 -0
- package/dist/integrations/remote-commands-store.js +1 -1
- package/dist/integrations/remote-devices-store.d.ts +1 -0
- package/dist/integrations/remote-devices-store.js +1 -1
- package/dist/integrations/remote-push-store.d.ts +1 -0
- package/dist/integrations/remote-push-store.js +1 -1
- package/dist/integrations/remote-run-events-store.d.ts +1 -0
- package/dist/integrations/remote-run-events-store.js +1 -1
- package/dist/integrations/scope-store.d.ts +1 -0
- package/dist/integrations/scope-store.js +1 -1
- package/dist/integrations/thread-mapping-store.d.ts +1 -0
- package/dist/integrations/thread-mapping-store.js +1 -1
- package/dist/integrations/usage-budget-store.d.ts +1 -0
- package/dist/integrations/usage-budget-store.js +1 -1
- package/dist/jobs/background-automation-runner.js +111 -9
- package/dist/jobs/run-history.d.ts +1 -0
- package/dist/jobs/run-history.js +1 -1
- package/dist/jobs/scheduler-health.d.ts +1 -0
- package/dist/jobs/scheduler-health.js +1 -1
- package/dist/mcp/approval-store.d.ts +1 -0
- package/dist/mcp/approval-store.js +1 -1
- package/dist/mcp/connect-store.d.ts +1 -0
- package/dist/mcp/connect-store.js +1 -1
- package/dist/mcp/oauth-store.d.ts +1 -0
- package/dist/mcp/oauth-store.js +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/notifications/store.d.ts +1 -0
- package/dist/notifications/store.js +1 -1
- package/dist/oauth-tokens/store.d.ts +1 -0
- package/dist/oauth-tokens/store.js +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/progress/store.d.ts +1 -0
- package/dist/progress/store.js +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/provider-api/corpus-jobs-store.d.ts +1 -0
- package/dist/provider-api/corpus-jobs-store.js +1 -1
- package/dist/provider-api/custom-registry.d.ts +1 -0
- package/dist/provider-api/custom-registry.js +1 -1
- package/dist/provider-api/quota-governor.d.ts +1 -0
- package/dist/provider-api/quota-governor.js +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts +1 -0
- package/dist/provider-api/staged-datasets-store.js +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/resources/store.d.ts +1 -0
- package/dist/resources/store.js +1 -1
- package/dist/scripts/dev/index.js +15 -1
- package/dist/search/index.js +3 -0
- package/dist/secrets/storage.d.ts +1 -0
- package/dist/secrets/storage.js +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +2 -1
- package/dist/server/agent-chat/action-filters-a2a.js +10 -2
- package/dist/server/agent-chat-plugin.js +71 -20
- package/dist/server/agent-teams-run-queue.d.ts +1 -0
- package/dist/server/agent-teams-run-queue.js +1 -1
- package/dist/server/auth-marketing.js +3 -1
- package/dist/server/auth.d.ts +18 -1
- package/dist/server/auth.js +370 -154
- package/dist/server/embed-session.d.ts +1 -0
- package/dist/server/embed-session.js +1 -1
- package/dist/server/google-auth-plugin.js +3 -1
- package/dist/server/google-oauth.d.ts +10 -0
- package/dist/server/google-oauth.js +25 -1
- package/dist/server/identity-sso-store.d.ts +1 -0
- package/dist/server/identity-sso-store.js +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/onboarding-html.js +200 -31
- package/dist/server/poll.d.ts +2 -0
- package/dist/server/poll.js +19 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +5 -0
- package/dist/server/release-schema.d.ts +46 -0
- package/dist/server/release-schema.js +307 -0
- package/dist/settings/store.d.ts +1 -0
- package/dist/settings/store.js +1 -1
- package/dist/shared/environment-lanes.d.ts +28 -0
- package/dist/shared/environment-lanes.js +61 -0
- package/dist/sharing/actions/share-resource.js +22 -2
- package/dist/styles/agent-native.css +17 -15
- package/dist/triggers/routes.d.ts +11 -0
- package/dist/triggers/routes.js +10 -0
- package/dist/usage/alerts-store.d.ts +1 -0
- package/dist/usage/alerts-store.js +1 -1
- package/dist/usage/store.d.ts +1 -0
- package/dist/usage/store.js +1 -1
- package/docs/content/drop-in-agent.mdx +4 -4
- package/docs/content/faq.mdx +15 -15
- package/docs/content/getting-started.mdx +173 -41
- package/docs/content/key-concepts.mdx +10 -10
- package/docs/content/using-your-agent.mdx +2 -2
- package/docs/content/what-is-agent-native.mdx +79 -228
- package/package.json +2 -2
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
IconSparkles,
|
|
46
46
|
IconExternalLink,
|
|
47
47
|
} from "@tabler/icons-react";
|
|
48
|
-
import { useQuery } from "@tanstack/react-query";
|
|
48
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
49
49
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
50
50
|
import { createPortal } from "react-dom";
|
|
51
51
|
import {
|
|
@@ -110,6 +110,64 @@ const PROCESSING_STUCK_TIMEOUT_MS = 12 * 60 * 1000;
|
|
|
110
110
|
const READY_MEDIA_SETTLE_POLL_MS = 20 * 1000;
|
|
111
111
|
const READY_MEDIA_SETTLE_POLL_INTERVAL_MS = 1000;
|
|
112
112
|
|
|
113
|
+
type RecordingReaction = {
|
|
114
|
+
id: string;
|
|
115
|
+
emoji: string;
|
|
116
|
+
videoTimestampMs: number;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
type PendingRecordingReaction = RecordingReaction & {
|
|
120
|
+
recordingId: string;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export function mergeRecordingReactions(
|
|
124
|
+
serverReactions: RecordingReaction[] | undefined,
|
|
125
|
+
pendingReactions: PendingRecordingReaction[],
|
|
126
|
+
recordingId: string | undefined,
|
|
127
|
+
) {
|
|
128
|
+
const seen = new Set<string>();
|
|
129
|
+
const merged: RecordingReaction[] = [];
|
|
130
|
+
const visiblePendingReactions = recordingId
|
|
131
|
+
? pendingReactions.filter(
|
|
132
|
+
(reaction) => reaction.recordingId === recordingId,
|
|
133
|
+
)
|
|
134
|
+
: [];
|
|
135
|
+
|
|
136
|
+
for (const reaction of [
|
|
137
|
+
...(serverReactions ?? []),
|
|
138
|
+
...visiblePendingReactions,
|
|
139
|
+
]) {
|
|
140
|
+
const key = `${reaction.id}:${reaction.emoji}:${reaction.videoTimestampMs}`;
|
|
141
|
+
if (seen.has(key)) continue;
|
|
142
|
+
seen.add(key);
|
|
143
|
+
merged.push(reaction);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return merged;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function removePendingReaction(
|
|
150
|
+
pendingReactions: PendingRecordingReaction[],
|
|
151
|
+
pendingId: string,
|
|
152
|
+
) {
|
|
153
|
+
return pendingReactions.filter((reaction) => reaction.id !== pendingId);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function handleReactionWrite(
|
|
157
|
+
response: { ok: boolean; status: number },
|
|
158
|
+
refresh: () => Promise<unknown>,
|
|
159
|
+
) {
|
|
160
|
+
if (!response.ok) throw new Error(`react failed: ${response.status}`);
|
|
161
|
+
// The write is authoritative. A transient read failure must not tell the
|
|
162
|
+
// player that the reaction failed.
|
|
163
|
+
void Promise.resolve()
|
|
164
|
+
.then(refresh)
|
|
165
|
+
.catch((refreshError) => {
|
|
166
|
+
console.warn("[clips] reaction refresh failed", refreshError);
|
|
167
|
+
});
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
113
171
|
export function meta() {
|
|
114
172
|
return [{ title: enMessages.recordingRoute.pageTitle }];
|
|
115
173
|
}
|
|
@@ -263,16 +321,7 @@ export default function RecordingPage() {
|
|
|
263
321
|
// live time bridge, so we fall back to the last position the player reported
|
|
264
322
|
// via onTimeUpdate (seek/initial start).
|
|
265
323
|
const resolvePlaybackMs = useCallback(() => {
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
typeof liveCt === "number" &&
|
|
269
|
-
Number.isFinite(liveCt) &&
|
|
270
|
-
liveCt >= 0 &&
|
|
271
|
-
liveCt < 1e7
|
|
272
|
-
) {
|
|
273
|
-
return Math.floor(liveCt * 1000);
|
|
274
|
-
}
|
|
275
|
-
return currentMs;
|
|
324
|
+
return playerRef.current?.getCurrentOriginalMs() ?? currentMs;
|
|
276
325
|
}, [currentMs]);
|
|
277
326
|
// The compact layout stacks the panel below the video, so switching tabs
|
|
278
327
|
// alone leaves the user looking at the player. Desktop always renders the
|
|
@@ -310,9 +359,13 @@ export default function RecordingPage() {
|
|
|
310
359
|
recordingId ? { type: "recording" as const, id: recordingId } : null,
|
|
311
360
|
[recordingId],
|
|
312
361
|
);
|
|
362
|
+
const queryClient = useQueryClient();
|
|
313
363
|
const lastPlayerStateWriteRef = useRef(0);
|
|
314
364
|
const readyMediaPollRef = useRef<{ key: string; until: number } | null>(null);
|
|
315
365
|
const [metadataRefreshUntil, setMetadataRefreshUntil] = useState(0);
|
|
366
|
+
const [pendingReactions, setPendingReactions] = useState<
|
|
367
|
+
PendingRecordingReaction[]
|
|
368
|
+
>([]);
|
|
316
369
|
|
|
317
370
|
const playerDataQ = useActionQuery<any>(
|
|
318
371
|
"get-recording-player-data",
|
|
@@ -414,7 +467,15 @@ export default function RecordingPage() {
|
|
|
414
467
|
| "viewer"
|
|
415
468
|
| undefined;
|
|
416
469
|
const comments = playerDataQ.data?.comments ?? [];
|
|
417
|
-
const reactions =
|
|
470
|
+
const reactions = useMemo(
|
|
471
|
+
() =>
|
|
472
|
+
mergeRecordingReactions(
|
|
473
|
+
playerDataQ.data?.reactions,
|
|
474
|
+
pendingReactions,
|
|
475
|
+
recordingId,
|
|
476
|
+
),
|
|
477
|
+
[pendingReactions, playerDataQ.data?.reactions, recordingId],
|
|
478
|
+
);
|
|
418
479
|
const chapters = playerDataQ.data?.chapters ?? [];
|
|
419
480
|
const transcriptSegments = playerDataQ.data?.transcript?.segments ?? [];
|
|
420
481
|
const transcriptFullText = playerDataQ.data?.transcript?.fullText ?? null;
|
|
@@ -1643,11 +1704,10 @@ export default function RecordingPage() {
|
|
|
1643
1704
|
},
|
|
1644
1705
|
)
|
|
1645
1706
|
.then((res) => {
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1707
|
+
return handleReactionWrite(res, () =>
|
|
1708
|
+
playerDataQ.refetch(),
|
|
1709
|
+
);
|
|
1649
1710
|
})
|
|
1650
|
-
.then(() => true)
|
|
1651
1711
|
.catch((err) => {
|
|
1652
1712
|
console.warn("[clips] react failed", err);
|
|
1653
1713
|
return false;
|
|
@@ -1743,6 +1803,18 @@ export default function RecordingPage() {
|
|
|
1743
1803
|
onReact={(emoji) => {
|
|
1744
1804
|
tracking.reportReaction(emoji);
|
|
1745
1805
|
const liveMs = resolvePlaybackMs();
|
|
1806
|
+
const pendingReaction: PendingRecordingReaction = {
|
|
1807
|
+
id: `pending-${Date.now()}-${Math.random()
|
|
1808
|
+
.toString(36)
|
|
1809
|
+
.slice(2)}`,
|
|
1810
|
+
emoji,
|
|
1811
|
+
videoTimestampMs: liveMs,
|
|
1812
|
+
recordingId: recording.id,
|
|
1813
|
+
};
|
|
1814
|
+
setPendingReactions((current) => [
|
|
1815
|
+
...current,
|
|
1816
|
+
pendingReaction,
|
|
1817
|
+
]);
|
|
1746
1818
|
return fetch(
|
|
1747
1819
|
agentNativePath(
|
|
1748
1820
|
"/_agent-native/actions/react-to-recording",
|
|
@@ -1758,12 +1830,34 @@ export default function RecordingPage() {
|
|
|
1758
1830
|
},
|
|
1759
1831
|
)
|
|
1760
1832
|
.then((res) => {
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1833
|
+
const writeSucceeded = handleReactionWrite(
|
|
1834
|
+
res,
|
|
1835
|
+
() =>
|
|
1836
|
+
Promise.all([
|
|
1837
|
+
queryClient.invalidateQueries({
|
|
1838
|
+
queryKey: [
|
|
1839
|
+
"action",
|
|
1840
|
+
"get-recording-player-data",
|
|
1841
|
+
],
|
|
1842
|
+
}),
|
|
1843
|
+
playerDataQ.refetch(),
|
|
1844
|
+
]),
|
|
1845
|
+
);
|
|
1846
|
+
setPendingReactions((current) =>
|
|
1847
|
+
removePendingReaction(
|
|
1848
|
+
current,
|
|
1849
|
+
pendingReaction.id,
|
|
1850
|
+
),
|
|
1851
|
+
);
|
|
1852
|
+
return writeSucceeded;
|
|
1764
1853
|
})
|
|
1765
|
-
.then(() => true)
|
|
1766
1854
|
.catch((err) => {
|
|
1855
|
+
setPendingReactions((current) =>
|
|
1856
|
+
removePendingReaction(
|
|
1857
|
+
current,
|
|
1858
|
+
pendingReaction.id,
|
|
1859
|
+
),
|
|
1860
|
+
);
|
|
1767
1861
|
console.warn("[clips] react failed", err);
|
|
1768
1862
|
return false;
|
|
1769
1863
|
});
|
|
@@ -124,6 +124,7 @@ type SharePageMetaRecording = {
|
|
|
124
124
|
animatedThumbnailUrl: string | null;
|
|
125
125
|
visibility: "private" | "org" | "public";
|
|
126
126
|
status: "uploading" | "processing" | "ready" | "failed";
|
|
127
|
+
hasPassword: boolean;
|
|
127
128
|
archivedAt: string | null;
|
|
128
129
|
trashedAt: string | null;
|
|
129
130
|
};
|
|
@@ -270,6 +271,7 @@ export async function loader({ params, url }: LoaderFunctionArgs) {
|
|
|
270
271
|
animatedThumbnailUrl: null,
|
|
271
272
|
visibility: rec.visibility,
|
|
272
273
|
status: rec.status,
|
|
274
|
+
hasPassword: Boolean(rec.password),
|
|
273
275
|
archivedAt: rec.archivedAt,
|
|
274
276
|
trashedAt: rec.trashedAt,
|
|
275
277
|
};
|
|
@@ -315,6 +317,7 @@ export const meta: MetaFunction<typeof loader> = ({ loaderData }) => {
|
|
|
315
317
|
return buildClipsShareMeta({
|
|
316
318
|
recording: loaderData?.recording ?? null,
|
|
317
319
|
origin: loaderData?.origin ?? null,
|
|
320
|
+
basePath: appBasePath(),
|
|
318
321
|
shareUrl: loaderData?.shareUrl ?? null,
|
|
319
322
|
});
|
|
320
323
|
};
|
|
@@ -676,6 +679,7 @@ export default function ShareRoute() {
|
|
|
676
679
|
// an anonymous viewer sees the same controls but triggers the sign-in
|
|
677
680
|
// prompt instead (see `requireSignIn` below).
|
|
678
681
|
const viewerCanComment = Boolean(session) && viewerRole != null;
|
|
682
|
+
const viewerCanUseFullscreenInteractions = !session || viewerCanComment;
|
|
679
683
|
const viewerIsOwner = Boolean(dataQ.data?.data?.viewer?.isOwner);
|
|
680
684
|
const canReshareLink =
|
|
681
685
|
(viewerRole === "viewer" || viewerRole === "commenter") &&
|
|
@@ -1249,66 +1253,85 @@ export default function ShareRoute() {
|
|
|
1249
1253
|
cta={firstCta}
|
|
1250
1254
|
onCtaClick={() => tracking.reportCtaClick()}
|
|
1251
1255
|
onTimeUpdate={(ms) => setCurrentMs(ms)}
|
|
1252
|
-
onCommentClick={
|
|
1256
|
+
onCommentClick={
|
|
1257
|
+
viewerCanUseFullscreenInteractions
|
|
1258
|
+
? () => setPanel("comments")
|
|
1259
|
+
: undefined
|
|
1260
|
+
}
|
|
1253
1261
|
onFullscreenChange={setIsPlayerFullscreen}
|
|
1254
|
-
enableComments={
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1262
|
+
enableComments={
|
|
1263
|
+
recording.enableComments && viewerCanUseFullscreenInteractions
|
|
1264
|
+
}
|
|
1265
|
+
onAddComment={
|
|
1266
|
+
viewerCanUseFullscreenInteractions
|
|
1267
|
+
? () => {
|
|
1268
|
+
if (!session) {
|
|
1269
|
+
requireSignIn("comment");
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
const liveCt = isLoomEmbedBacked
|
|
1273
|
+
? null
|
|
1274
|
+
: playerRef.current?.video?.currentTime;
|
|
1275
|
+
const liveMs =
|
|
1276
|
+
typeof liveCt === "number" &&
|
|
1277
|
+
Number.isFinite(liveCt) &&
|
|
1278
|
+
liveCt >= 0 &&
|
|
1279
|
+
liveCt < 1e7
|
|
1280
|
+
? Math.floor(liveCt * 1000)
|
|
1281
|
+
: currentMs;
|
|
1282
|
+
setCommentAtMs(liveMs);
|
|
1283
|
+
setCommentOpen(true);
|
|
1284
|
+
}
|
|
1285
|
+
: undefined
|
|
1286
|
+
}
|
|
1287
|
+
enableReactions={
|
|
1288
|
+
recording.enableReactions && viewerCanUseFullscreenInteractions
|
|
1289
|
+
}
|
|
1290
|
+
onReact={
|
|
1291
|
+
viewerCanUseFullscreenInteractions
|
|
1292
|
+
? (emoji) => {
|
|
1293
|
+
if (!session) {
|
|
1294
|
+
requireSignIn("react");
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
tracking.reportReaction(emoji);
|
|
1298
|
+
const liveCt = isLoomEmbedBacked
|
|
1299
|
+
? null
|
|
1300
|
+
: playerRef.current?.video?.currentTime;
|
|
1301
|
+
const liveMs =
|
|
1302
|
+
typeof liveCt === "number" &&
|
|
1303
|
+
Number.isFinite(liveCt) &&
|
|
1304
|
+
liveCt >= 0 &&
|
|
1305
|
+
liveCt < 1e7
|
|
1306
|
+
? Math.floor(liveCt * 1000)
|
|
1307
|
+
: currentMs;
|
|
1308
|
+
return fetch(
|
|
1309
|
+
agentNativePath(
|
|
1310
|
+
"/_agent-native/actions/react-to-recording",
|
|
1311
|
+
),
|
|
1312
|
+
{
|
|
1313
|
+
method: "POST",
|
|
1314
|
+
headers: { "Content-Type": "application/json" },
|
|
1315
|
+
body: JSON.stringify({
|
|
1316
|
+
recordingId: recording.id,
|
|
1317
|
+
emoji,
|
|
1318
|
+
videoTimestampMs: liveMs,
|
|
1319
|
+
}),
|
|
1320
|
+
},
|
|
1321
|
+
)
|
|
1322
|
+
.then((res) => {
|
|
1323
|
+
if (!res.ok)
|
|
1324
|
+
throw new Error(`react failed: ${res.status}`);
|
|
1325
|
+
return dataQ.refetch();
|
|
1326
|
+
})
|
|
1327
|
+
.then(() => true)
|
|
1328
|
+
.catch((err) => {
|
|
1329
|
+
console.warn("[clips] react failed", err);
|
|
1330
|
+
return false;
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
: undefined
|
|
1334
|
+
}
|
|
1312
1335
|
className="h-full w-full rounded-none sm:rounded-xl"
|
|
1313
1336
|
/>
|
|
1314
1337
|
{commentOpen && viewerCanComment
|
|
@@ -1351,23 +1374,15 @@ export default function ShareRoute() {
|
|
|
1351
1374
|
{recording.enableComments ? (
|
|
1352
1375
|
<TimestampedCommentButton
|
|
1353
1376
|
enableComments={recording.enableComments}
|
|
1354
|
-
canComment
|
|
1377
|
+
canComment={!session || viewerCanComment}
|
|
1355
1378
|
className="shrink-0"
|
|
1356
1379
|
onOpen={() => {
|
|
1357
1380
|
if (!session) {
|
|
1358
1381
|
requireSignIn("comment");
|
|
1359
1382
|
return;
|
|
1360
1383
|
}
|
|
1361
|
-
const liveCt = isLoomEmbedBacked
|
|
1362
|
-
? null
|
|
1363
|
-
: playerRef.current?.video?.currentTime;
|
|
1364
1384
|
const liveMs =
|
|
1365
|
-
|
|
1366
|
-
Number.isFinite(liveCt) &&
|
|
1367
|
-
liveCt >= 0 &&
|
|
1368
|
-
liveCt < 1e7
|
|
1369
|
-
? Math.floor(liveCt * 1000)
|
|
1370
|
-
: currentMs;
|
|
1385
|
+
playerRef.current?.getCurrentOriginalMs() ?? currentMs;
|
|
1371
1386
|
setCommentAtMs(liveMs);
|
|
1372
1387
|
setCommentOpen(true);
|
|
1373
1388
|
}}
|
|
@@ -1376,22 +1391,15 @@ export default function ShareRoute() {
|
|
|
1376
1391
|
{recording.enableReactions ? (
|
|
1377
1392
|
<ReactionsTray
|
|
1378
1393
|
reactions={reactions}
|
|
1394
|
+
disabled={Boolean(session) && !viewerCanComment}
|
|
1379
1395
|
onReact={(emoji) => {
|
|
1380
1396
|
if (!session) {
|
|
1381
1397
|
requireSignIn("react");
|
|
1382
1398
|
return false;
|
|
1383
1399
|
}
|
|
1384
1400
|
tracking.reportReaction(emoji);
|
|
1385
|
-
const liveCt = isLoomEmbedBacked
|
|
1386
|
-
? null
|
|
1387
|
-
: playerRef.current?.video?.currentTime;
|
|
1388
1401
|
const liveMs =
|
|
1389
|
-
|
|
1390
|
-
Number.isFinite(liveCt) &&
|
|
1391
|
-
liveCt >= 0 &&
|
|
1392
|
-
liveCt < 1e7
|
|
1393
|
-
? Math.floor(liveCt * 1000)
|
|
1394
|
-
: currentMs;
|
|
1402
|
+
playerRef.current?.getCurrentOriginalMs() ?? currentMs;
|
|
1395
1403
|
return fetch(
|
|
1396
1404
|
agentNativePath(
|
|
1397
1405
|
"/_agent-native/actions/react-to-recording",
|
|
@@ -79,13 +79,28 @@ Clips registers itself to open at login by default, then runs quietly in the men
|
|
|
79
79
|
|
|
80
80
|
## Releases + auto-update
|
|
81
81
|
|
|
82
|
-
Clips Desktop
|
|
82
|
+
Clips Desktop has separate stable and Nightly lanes. The stable app keeps the
|
|
83
|
+
`Clips` name, `Clips` binary, `clips://` scheme, and `com.clips.tray` identifier;
|
|
84
|
+
its releases use `clips-v*` tags, the `clips-latest` updater pointer, and
|
|
85
|
+
`/api/clips-updater.json`. Nightly builds are named `Clips Nightly`, use the
|
|
86
|
+
`Clips-Nightly` binary, the `clips-nightly://` scheme, and
|
|
87
|
+
`com.clips.tray.nightly`. They use the `clips-nightly-v*` tags,
|
|
88
|
+
`clips-nightly-latest` pointer, and `/api/clips-updater.json?channel=nightly`.
|
|
89
|
+
The in-app updater only sees the pointer for the channel that produced the
|
|
90
|
+
installed app, and the separate native binary/desktop entry keeps both lanes
|
|
91
|
+
installable at once.
|
|
83
92
|
|
|
84
93
|
### Shipping a release
|
|
85
94
|
|
|
86
|
-
1.
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
1. For a stable release, dispatch **Clips Desktop Release** in GitHub Actions
|
|
96
|
+
with `channel: production` and an explicit version. Stable releases are
|
|
97
|
+
deliberate workflow runs; pushes to `main` never replace the stable lane.
|
|
98
|
+
2. Pushes to `main` automatically build the Nightly lane. A Nightly run can
|
|
99
|
+
also be dispatched explicitly with `channel: nightly`.
|
|
100
|
+
3. The workflow builds macOS (universal), Windows, and Linux x86_64 installers,
|
|
101
|
+
signs updater artifacts, and publishes the versioned release plus that
|
|
102
|
+
channel's pointer manifest. Installed copies auto-download only updates
|
|
103
|
+
from their own lane on the next hourly or app-focus check.
|
|
89
104
|
|
|
90
105
|
### Auto-update flow (inside the app)
|
|
91
106
|
|
|
@@ -2062,11 +2062,19 @@ export function App() {
|
|
|
2062
2062
|
const controller = new AbortController();
|
|
2063
2063
|
const abortTimer = setTimeout(() => controller.abort(), POLL_ABORT_MS);
|
|
2064
2064
|
try {
|
|
2065
|
-
const exchangeParams = new URLSearchParams({ flow_id: flowId });
|
|
2066
|
-
if (verifier) exchangeParams.set("verifier", verifier);
|
|
2067
2065
|
const xr = await fetch(
|
|
2068
|
-
`${base}/_agent-native/auth/desktop-exchange
|
|
2069
|
-
{
|
|
2066
|
+
`${base}/_agent-native/auth/desktop-exchange?flow_id=${encodeURIComponent(flowId)}`,
|
|
2067
|
+
{
|
|
2068
|
+
credentials: "include",
|
|
2069
|
+
...(verifier
|
|
2070
|
+
? {
|
|
2071
|
+
headers: {
|
|
2072
|
+
"X-Agent-Native-Desktop-Verifier": verifier,
|
|
2073
|
+
},
|
|
2074
|
+
}
|
|
2075
|
+
: {}),
|
|
2076
|
+
signal: controller.signal,
|
|
2077
|
+
},
|
|
2070
2078
|
);
|
|
2071
2079
|
if (!xr.ok) {
|
|
2072
2080
|
if (Date.now() - start > TIMEOUT_MS) {
|
|
@@ -2130,25 +2138,79 @@ export function App() {
|
|
|
2130
2138
|
void tick();
|
|
2131
2139
|
}
|
|
2132
2140
|
|
|
2133
|
-
// Google
|
|
2134
|
-
// the
|
|
2135
|
-
//
|
|
2141
|
+
// Google verification stays in the bound Tauri WebView so the callback sees
|
|
2142
|
+
// the same browser-binding cookie. Magic-link verification still completes
|
|
2143
|
+
// in the system browser through its own exchange flow.
|
|
2136
2144
|
async function signInExternal() {
|
|
2137
2145
|
if (signInInflightRef.current) return;
|
|
2138
2146
|
signInInflightRef.current = true;
|
|
2139
2147
|
|
|
2140
2148
|
try {
|
|
2141
2149
|
setSignInError(null);
|
|
2142
|
-
const flowId =
|
|
2143
|
-
|
|
2144
|
-
|
|
2150
|
+
const flowId = crypto.randomUUID?.() ?? null;
|
|
2151
|
+
const verifier = (() => {
|
|
2152
|
+
const randomUuid = crypto.randomUUID;
|
|
2153
|
+
if (typeof randomUuid === "function") {
|
|
2154
|
+
return `${randomUuid.call(crypto)}${randomUuid.call(crypto)}`;
|
|
2155
|
+
}
|
|
2156
|
+
if (typeof crypto.getRandomValues === "function") {
|
|
2157
|
+
const bytes = new Uint8Array(32);
|
|
2158
|
+
crypto.getRandomValues(bytes);
|
|
2159
|
+
let binary = "";
|
|
2160
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2161
|
+
return btoa(binary)
|
|
2162
|
+
.replace(/\+/g, "-")
|
|
2163
|
+
.replace(/\//g, "_")
|
|
2164
|
+
.replace(/=+$/, "");
|
|
2165
|
+
}
|
|
2166
|
+
return null;
|
|
2167
|
+
})();
|
|
2168
|
+
if (!flowId || !verifier) {
|
|
2169
|
+
// A Math.random flow id is guessable, which lets anyone else claim this
|
|
2170
|
+
// sign-in's exchange slot; fail closed rather than weaken the credential.
|
|
2171
|
+
throw new Error("Secure OAuth flow generation is unavailable.");
|
|
2172
|
+
}
|
|
2145
2173
|
const base = serverUrl.replace(/\/+$/, "");
|
|
2146
2174
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2175
|
+
const authParams = new URLSearchParams({
|
|
2176
|
+
desktop: "1",
|
|
2177
|
+
flow_id: flowId,
|
|
2178
|
+
webview: "1",
|
|
2179
|
+
});
|
|
2180
|
+
const authResponse = await fetch(
|
|
2181
|
+
`${base}/_agent-native/google/auth-url?${authParams.toString()}`,
|
|
2182
|
+
{
|
|
2183
|
+
method: "POST",
|
|
2184
|
+
headers: {
|
|
2185
|
+
Accept: "application/json",
|
|
2186
|
+
"X-Agent-Native-Desktop-Verifier": verifier,
|
|
2187
|
+
},
|
|
2188
|
+
},
|
|
2149
2189
|
);
|
|
2190
|
+
let authPayload: {
|
|
2191
|
+
url?: unknown;
|
|
2192
|
+
error?: unknown;
|
|
2193
|
+
message?: unknown;
|
|
2194
|
+
};
|
|
2195
|
+
try {
|
|
2196
|
+
authPayload = (await authResponse.json()) as typeof authPayload;
|
|
2197
|
+
} catch {
|
|
2198
|
+
throw new Error("Could not start Google sign-in.");
|
|
2199
|
+
}
|
|
2200
|
+
if (!authResponse.ok || typeof authPayload?.url !== "string") {
|
|
2201
|
+
const message =
|
|
2202
|
+
typeof authPayload.message === "string"
|
|
2203
|
+
? authPayload.message
|
|
2204
|
+
: typeof authPayload.error === "string"
|
|
2205
|
+
? authPayload.error
|
|
2206
|
+
: "Could not start Google sign-in.";
|
|
2207
|
+
throw new Error(message);
|
|
2208
|
+
}
|
|
2150
2209
|
setSignInPending("google");
|
|
2151
|
-
|
|
2210
|
+
// Stay in this exact WebView: the auth bootstrap set the HttpOnly
|
|
2211
|
+
// browser-binding cookie here, and the callback returns this page with
|
|
2212
|
+
// the staged session cookie after Google completes.
|
|
2213
|
+
window.location.href = authPayload.url;
|
|
2152
2214
|
} catch (err) {
|
|
2153
2215
|
console.error("[clips-tray] signInExternal failed:", err);
|
|
2154
2216
|
signInInflightRef.current = false;
|
|
@@ -3912,8 +3974,8 @@ export function App() {
|
|
|
3912
3974
|
// (not a separate Tauri window). This avoids Tauri 2's separate-WebKit-
|
|
3913
3975
|
// data-store-per-WebviewWindow cookie-jar issue — the cookie is set in
|
|
3914
3976
|
// the same webview that reads it on the next /auth/session poll.
|
|
3915
|
-
// Google
|
|
3916
|
-
// password
|
|
3977
|
+
// Google verification uses the bound WebView, while magic-link verification
|
|
3978
|
+
// uses the system browser and password stays inline here.
|
|
3917
3979
|
if (authStatus === "anon") {
|
|
3918
3980
|
return (
|
|
3919
3981
|
<div className="app" ref={appRef}>
|
|
@@ -4822,7 +4884,7 @@ function SignInForm({
|
|
|
4822
4884
|
type="button"
|
|
4823
4885
|
className="signin-google"
|
|
4824
4886
|
onClick={onUseBrowser}
|
|
4825
|
-
title="
|
|
4887
|
+
title="Returns to Clips to complete Google sign-in"
|
|
4826
4888
|
>
|
|
4827
4889
|
<GoogleIcon />
|
|
4828
4890
|
Sign in with Google
|
|
@@ -15,8 +15,9 @@ crate-type = ["lib", "cdylib", "staticlib"]
|
|
|
15
15
|
|
|
16
16
|
# Force the dev binary name so macOS shows "Clips" in the Dock / app switcher
|
|
17
17
|
# (macOS derives the display name from the executable filename when the app
|
|
18
|
-
# is unbundled — e.g. during `tauri dev`).
|
|
19
|
-
#
|
|
18
|
+
# is unbundled — e.g. during `tauri dev`). The Nightly release workflow
|
|
19
|
+
# rewrites this target and tauri.conf.json's mainBinaryName to
|
|
20
|
+
# `Clips-Nightly` before packaging so both lanes can coexist.
|
|
20
21
|
[[bin]]
|
|
21
22
|
name = "Clips"
|
|
22
23
|
path = "src/main.rs"
|