@agent-native/core 0.155.0 → 0.157.1
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/server/jobs/analytics-bigquery-backfill.ts +19 -4
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
- package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
- package/corpus/templates/assets/app/global.css +4 -0
- package/corpus/templates/assets/app/i18n-data.ts +21 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +18 -15
- package/corpus/templates/brain/app/routes/sources.tsx +1 -2
- package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
- package/corpus/templates/calendar/server/handlers/bookings.ts +219 -93
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
- package/corpus/templates/clips/actions/create-folder.ts +7 -1
- package/corpus/templates/clips/actions/create-meeting.ts +6 -2
- package/corpus/templates/clips/actions/create-recording.ts +5 -3
- package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
- package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
- package/corpus/templates/clips/actions/request-recording-access.ts +272 -0
- package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +177 -134
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
- package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +24 -17
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +151 -20
- package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
- package/corpus/templates/clips/desktop/src/app.tsx +24 -0
- package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
- package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
- package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +24 -9
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
- package/corpus/templates/clips/server/db/index.ts +1 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
- package/corpus/templates/clips/server/lib/emails.ts +23 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
- package/corpus/templates/clips/server/lib/recordings.ts +2 -1
- package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
- package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
- package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
- package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +19 -0
- package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
- package/corpus/templates/clips/shared/recording-link.ts +4 -0
- package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +6 -2
- package/corpus/templates/factory/actions/view-screen.ts +15 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
- package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
- package/corpus/templates/factory/app/routes/agents.tsx +9 -0
- package/corpus/templates/mail/server/plugins/auth.ts +0 -2
- package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
- package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
- package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +108 -19
- package/corpus/templates/slides/app/global.css +7 -40
- package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
- package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +143 -45
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/production-agent.d.ts +30 -0
- package/dist/agent/production-agent.js +100 -8
- package/dist/agent/types.d.ts +9 -0
- package/dist/client/AgentPanel.d.ts +13 -1
- package/dist/client/AgentPanel.js +22 -17
- package/dist/client/AssistantChat.d.ts +15 -1
- package/dist/client/AssistantChat.js +135 -48
- package/dist/client/HighlightedCodeBlock.js +12 -2
- package/dist/client/MultiTabAssistantChat.js +44 -27
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/chat/markdown-renderer.js +20 -5
- package/dist/client/chat/message-components.d.ts +2 -2
- package/dist/client/chat/message-components.js +69 -35
- package/dist/client/chat/run-recovery.d.ts +4 -1
- package/dist/client/chat/run-recovery.js +62 -30
- package/dist/client/chat/tool-call-display.d.ts +12 -3
- package/dist/client/chat/tool-call-display.js +79 -40
- package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
- package/dist/client/chat/widgets/DataChartWidget.js +8 -2
- package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
- package/dist/client/chat/widgets/DataTableWidget.js +23 -9
- package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
- package/dist/client/chat-first/apps-rail.js +1 -1
- package/dist/client/components/ui/message-scroller.d.ts +1 -1
- package/dist/client/components/ui/message-scroller.js +7 -3
- package/dist/client/composer/runtime-adapters.js +3 -2
- package/dist/client/context-xray/ContextMeter.js +5 -1
- package/dist/client/context-xray/ContextXRayPanel.js +5 -5
- package/dist/client/error-format.d.ts +4 -0
- package/dist/client/error-format.js +93 -5
- package/dist/client/i18n.js +83 -17
- package/dist/client/observability/ThumbsFeedback.js +10 -8
- package/dist/client/settings/AccountSettingsCard.js +48 -19
- package/dist/client/sharing/ShareButton.d.ts +1 -2
- package/dist/client/sharing/ShareButton.js +151 -81
- package/dist/client/sharing/ShareDialog.js +5 -4
- package/dist/client/use-chat-threads.js +10 -14
- package/dist/coding-tools/run-code.js +2 -1
- package/dist/coding-tools/sandbox/background.d.ts +1 -0
- package/dist/coding-tools/sandbox/background.js +8 -1
- package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
- package/dist/coding-tools/sandbox/executions-store.js +41 -3
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/localization/core-messages/ar-SA.d.ts +3 -0
- package/dist/localization/core-messages/ar-SA.js +541 -0
- package/dist/localization/core-messages/de-DE.d.ts +3 -0
- package/dist/localization/core-messages/de-DE.js +513 -0
- package/dist/localization/core-messages/en-US.d.ts +513 -0
- package/dist/localization/core-messages/en-US.js +513 -0
- package/dist/localization/core-messages/es-ES.d.ts +3 -0
- package/dist/localization/core-messages/es-ES.js +520 -0
- package/dist/localization/core-messages/fr-FR.d.ts +3 -0
- package/dist/localization/core-messages/fr-FR.js +520 -0
- package/dist/localization/core-messages/hi-IN.d.ts +3 -0
- package/dist/localization/core-messages/hi-IN.js +513 -0
- package/dist/localization/core-messages/ja-JP.d.ts +3 -0
- package/dist/localization/core-messages/ja-JP.js +506 -0
- package/dist/localization/core-messages/ko-KR.d.ts +3 -0
- package/dist/localization/core-messages/ko-KR.js +506 -0
- package/dist/localization/core-messages/pt-BR.d.ts +3 -0
- package/dist/localization/core-messages/pt-BR.js +520 -0
- package/dist/localization/core-messages/zh-CN.d.ts +3 -0
- package/dist/localization/core-messages/zh-CN.js +506 -0
- package/dist/localization/core-messages/zh-TW.d.ts +3 -0
- package/dist/localization/core-messages/zh-TW.js +506 -0
- package/dist/localization/core-messages.d.ts +524 -0
- package/dist/localization/core-messages.js +124 -0
- package/dist/localization/default-messages.d.ts +3 -0
- package/dist/localization/default-messages.js +3 -0
- package/dist/notifications/routes.d.ts +1 -1
- 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 +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat/plugin-options.d.ts +7 -2
- package/dist/server/agent-chat-plugin.d.ts +10 -0
- package/dist/server/agent-chat-plugin.js +144 -41
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-run-context.d.ts +7 -1
- package/dist/server/agent-run-context.js +9 -2
- package/dist/server/agent-teams-run-queue.d.ts +3 -0
- package/dist/server/agent-teams.js +38 -9
- package/dist/server/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +0 -2
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.js +18 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/onboarding-html.d.ts +1 -0
- package/dist/server/onboarding-html.js +2 -88
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +7 -0
- package/dist/server/request-context.js +15 -0
- package/dist/shared/markdown-block-split.d.ts +16 -0
- package/dist/shared/markdown-block-split.js +81 -5
- package/dist/styles/agent-native.css +13 -0
- package/dist/voice/voice-cleanup-prompt.js +1 -0
- package/docs/content/agent-surfaces.mdx +30 -0
- package/package.json +2 -2
- package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
|
@@ -217,13 +217,18 @@ interface DeckContextType {
|
|
|
217
217
|
) => void;
|
|
218
218
|
reloadDecks: () => Promise<void>;
|
|
219
219
|
reloadDecksWithStatus: () => Promise<DeckReloadStatus>;
|
|
220
|
-
refreshOpenDeck: (
|
|
220
|
+
refreshOpenDeck: (
|
|
221
|
+
deckId: string,
|
|
222
|
+
options?: { clearPendingWrites?: boolean },
|
|
223
|
+
) => Promise<Deck | null>;
|
|
221
224
|
getDeck: (id: string) => Deck | undefined;
|
|
222
225
|
addSlide: (
|
|
223
226
|
deckId: string,
|
|
224
227
|
layout?: SlideLayout,
|
|
225
228
|
afterIndex?: number,
|
|
229
|
+
options?: { persistence?: "debounced" | "immediate" },
|
|
226
230
|
) => string;
|
|
231
|
+
flushDeckSave: (deckId: string) => Promise<void>;
|
|
227
232
|
updateSlide: (
|
|
228
233
|
deckId: string,
|
|
229
234
|
slideId: string,
|
|
@@ -310,6 +315,8 @@ function normalizeActionDeck(value: unknown): Deck | null {
|
|
|
310
315
|
const pendingSaves = new Map<string, ReturnType<typeof setTimeout>>();
|
|
311
316
|
const inFlightSaves = new Set<string>();
|
|
312
317
|
const inFlightSaveChains = new Map<string, Promise<void>>();
|
|
318
|
+
const inFlightSaveControllers = new Map<string, AbortController>();
|
|
319
|
+
const deckSaveGenerations = new Map<string, number>();
|
|
313
320
|
const immediateFlushRequests = new Set<string>();
|
|
314
321
|
const deckSaveRetryAttempts = new Map<string, number>();
|
|
315
322
|
const failedSaveDecks = new Set<string>();
|
|
@@ -401,7 +408,11 @@ function deckPayload(deck: Deck): Record<string, unknown> {
|
|
|
401
408
|
* the `save-deck` action is called first, then any trailing granular ops are
|
|
402
409
|
* sent through `patch-deck`.
|
|
403
410
|
*/
|
|
404
|
-
async function persistDeckOps(
|
|
411
|
+
async function persistDeckOps(
|
|
412
|
+
deckId: string,
|
|
413
|
+
ops: GranularOp[],
|
|
414
|
+
signal?: AbortSignal,
|
|
415
|
+
) {
|
|
405
416
|
if (ops[0].op === "full-replace") {
|
|
406
417
|
// Legacy full-deck write — used by undo/redo and setDeckSlides.
|
|
407
418
|
// `callAction` bounds it so a stalled save can't wedge `inFlightSaves`
|
|
@@ -411,20 +422,28 @@ async function persistDeckOps(deckId: string, ops: GranularOp[]) {
|
|
|
411
422
|
await callAction(
|
|
412
423
|
"save-deck",
|
|
413
424
|
{ deckId, deck: deckPayload(deck) },
|
|
414
|
-
{ method: "PUT" },
|
|
425
|
+
{ method: "PUT", signal },
|
|
415
426
|
);
|
|
416
427
|
const trailingOps = ops.slice(1) as PatchDeckOp[];
|
|
417
428
|
if (trailingOps.length > 0) {
|
|
418
|
-
await callAction(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
429
|
+
await callAction(
|
|
430
|
+
"patch-deck",
|
|
431
|
+
{
|
|
432
|
+
deckId,
|
|
433
|
+
operations: trailingOps,
|
|
434
|
+
},
|
|
435
|
+
{ signal },
|
|
436
|
+
);
|
|
422
437
|
}
|
|
423
438
|
} else {
|
|
424
|
-
await callAction(
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
439
|
+
await callAction(
|
|
440
|
+
"patch-deck",
|
|
441
|
+
{
|
|
442
|
+
deckId,
|
|
443
|
+
operations: ops as PatchDeckOp[],
|
|
444
|
+
},
|
|
445
|
+
{ signal },
|
|
446
|
+
);
|
|
428
447
|
}
|
|
429
448
|
}
|
|
430
449
|
|
|
@@ -453,15 +472,23 @@ function drainPendingDeckOps(deckId: string): Promise<void> {
|
|
|
453
472
|
return Promise.resolve();
|
|
454
473
|
}
|
|
455
474
|
|
|
475
|
+
const generation = deckSaveGenerations.get(deckId) ?? 0;
|
|
476
|
+
const controller =
|
|
477
|
+
typeof AbortController === "undefined" ? null : new AbortController();
|
|
478
|
+
if (controller) inFlightSaveControllers.set(deckId, controller);
|
|
456
479
|
inFlightSaves.add(deckId);
|
|
457
|
-
|
|
458
|
-
|
|
480
|
+
const isCurrentGeneration = () =>
|
|
481
|
+
(deckSaveGenerations.get(deckId) ?? 0) === generation;
|
|
482
|
+
const next = persistDeckOps(deckId, ops, controller?.signal)
|
|
459
483
|
.then(() => {
|
|
460
|
-
|
|
484
|
+
if (!isCurrentGeneration()) return;
|
|
461
485
|
deckSaveRetryAttempts.delete(deckId);
|
|
462
486
|
failedSaveDecks.delete(deckId);
|
|
463
487
|
})
|
|
464
488
|
.catch((err) => {
|
|
489
|
+
// A restore or delete invalidated this request. Its result must not
|
|
490
|
+
// resurrect the old queue or schedule a retry after the boundary.
|
|
491
|
+
if (!isCurrentGeneration()) return;
|
|
465
492
|
console.error(`Failed to save deck ${deckId}:`, err);
|
|
466
493
|
const pending = pendingOpsQueue.get(deckId) ?? [];
|
|
467
494
|
pendingOpsQueue.set(deckId, [...ops, ...pending]);
|
|
@@ -486,10 +513,13 @@ function drainPendingDeckOps(deckId: string): Promise<void> {
|
|
|
486
513
|
.finally(() => {
|
|
487
514
|
if (inFlightSaveChains.get(deckId) === next) {
|
|
488
515
|
inFlightSaveChains.delete(deckId);
|
|
516
|
+
if (controller && inFlightSaveControllers.get(deckId) === controller) {
|
|
517
|
+
inFlightSaveControllers.delete(deckId);
|
|
518
|
+
}
|
|
489
519
|
inFlightSaves.delete(deckId);
|
|
490
520
|
const flushImmediately = immediateFlushRequests.delete(deckId);
|
|
491
521
|
notifySaveListeners();
|
|
492
|
-
if (
|
|
522
|
+
if (flushImmediately) {
|
|
493
523
|
void drainPendingDeckOps(deckId);
|
|
494
524
|
}
|
|
495
525
|
}
|
|
@@ -499,6 +529,39 @@ function drainPendingDeckOps(deckId: string): Promise<void> {
|
|
|
499
529
|
return next;
|
|
500
530
|
}
|
|
501
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Wait for a deck's in-flight save(s) to fully settle, including any
|
|
534
|
+
* follow-up drain chained by immediateFlushRequests for ops queued while a
|
|
535
|
+
* save was already running, and any requeued retry after a failed attempt.
|
|
536
|
+
* Used when a caller must not proceed (e.g. firing an agent request against
|
|
537
|
+
* a slide or restoring a saved version) until every write issued before that
|
|
538
|
+
* boundary has reached the server. Throws if the save ultimately fails after
|
|
539
|
+
* retries exhaust, since `drainPendingDeckOps` swallows save errors internally
|
|
540
|
+
* to drive its own retry loop and its promise always resolves regardless of
|
|
541
|
+
* outcome.
|
|
542
|
+
*/
|
|
543
|
+
async function flushDeckSave(deckId: string): Promise<void> {
|
|
544
|
+
while (true) {
|
|
545
|
+
const active = inFlightSaveChains.get(deckId);
|
|
546
|
+
if (active) {
|
|
547
|
+
await active;
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
if (failedSaveDecks.has(deckId)) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
`Failed to save deck ${deckId} after ${MAX_DECK_SAVE_RETRIES} attempts`,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
if (pendingOpsQueue.has(deckId) || pendingSaves.has(deckId)) {
|
|
556
|
+
// A failed op was requeued for retry, or a debounced save is armed;
|
|
557
|
+
// wait for it to actually run rather than declaring success early.
|
|
558
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
502
565
|
function enqueueDeckOp(
|
|
503
566
|
deckId: string,
|
|
504
567
|
op: GranularOp,
|
|
@@ -613,6 +676,8 @@ function flushPendingSaves() {
|
|
|
613
676
|
}
|
|
614
677
|
|
|
615
678
|
function discardPendingDeckOps(deckId: string) {
|
|
679
|
+
deckSaveGenerations.set(deckId, (deckSaveGenerations.get(deckId) ?? 0) + 1);
|
|
680
|
+
inFlightSaveControllers.get(deckId)?.abort();
|
|
616
681
|
const timer = pendingSaves.get(deckId);
|
|
617
682
|
if (timer) clearTimeout(timer);
|
|
618
683
|
pendingSaves.delete(deckId);
|
|
@@ -1320,7 +1385,15 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
1320
1385
|
* this, chat-driven edits land in the editor with Undo disabled.
|
|
1321
1386
|
*/
|
|
1322
1387
|
const applyRemoteDeckUpdate = useCallback(
|
|
1323
|
-
(
|
|
1388
|
+
(
|
|
1389
|
+
updated: Deck,
|
|
1390
|
+
label = "Agent edit",
|
|
1391
|
+
options?: { clearPendingWrites?: boolean },
|
|
1392
|
+
) => {
|
|
1393
|
+
if (options?.clearPendingWrites) {
|
|
1394
|
+
discardPendingDeckOps(updated.id);
|
|
1395
|
+
dirtyDeckIdsRef.current.delete(updated.id);
|
|
1396
|
+
}
|
|
1324
1397
|
const before = decksRef.current.find((d) => d.id === updated.id);
|
|
1325
1398
|
if (
|
|
1326
1399
|
before &&
|
|
@@ -1413,7 +1486,10 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
1413
1486
|
// - Dirty deck / unsaved local create → additive merge only: surface
|
|
1414
1487
|
// agent-added slides without ever overwriting or dropping local slides.
|
|
1415
1488
|
const refetchOpenDeckIfChanged = useCallback(
|
|
1416
|
-
async (
|
|
1489
|
+
async (
|
|
1490
|
+
currentOpenId: string,
|
|
1491
|
+
options?: { clearPendingWrites?: boolean },
|
|
1492
|
+
): Promise<Deck | null> => {
|
|
1417
1493
|
const requestId =
|
|
1418
1494
|
(openDeckRequestIdByDeckRef.current.get(currentOpenId) ?? 0) + 1;
|
|
1419
1495
|
openDeckRequestIdByDeckRef.current.set(currentOpenId, requestId);
|
|
@@ -1425,6 +1501,13 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
1425
1501
|
// still-pending create — nothing authoritative to reconcile.
|
|
1426
1502
|
if (!serverDeck) return null;
|
|
1427
1503
|
const clientDeck = decksRef.current.find((d) => d.id === currentOpenId);
|
|
1504
|
+
if (options?.clearPendingWrites) {
|
|
1505
|
+
lastExternalUpdateRef.current = Date.now();
|
|
1506
|
+
applyRemoteDeckUpdate(serverDeck, "Deck restored", {
|
|
1507
|
+
clearPendingWrites: true,
|
|
1508
|
+
});
|
|
1509
|
+
return serverDeck;
|
|
1510
|
+
}
|
|
1428
1511
|
|
|
1429
1512
|
const hasLocalEdits =
|
|
1430
1513
|
pendingCreateIdsRef.current.has(currentOpenId) ||
|
|
@@ -2095,7 +2178,12 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
2095
2178
|
);
|
|
2096
2179
|
|
|
2097
2180
|
const addSlide = useCallback(
|
|
2098
|
-
(
|
|
2181
|
+
(
|
|
2182
|
+
deckId: string,
|
|
2183
|
+
layout: SlideLayout = "content",
|
|
2184
|
+
afterIndex?: number,
|
|
2185
|
+
addOptions?: { persistence?: "debounced" | "immediate" },
|
|
2186
|
+
) => {
|
|
2099
2187
|
markDeckDirty(deckId);
|
|
2100
2188
|
const newSlide: Slide = {
|
|
2101
2189
|
id: nanoid(8),
|
|
@@ -2128,7 +2216,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
2128
2216
|
afterSlideId,
|
|
2129
2217
|
fields: addSlideFields(newSlide),
|
|
2130
2218
|
};
|
|
2131
|
-
enqueueDeckOp(deckId, op);
|
|
2219
|
+
enqueueDeckOp(deckId, op, addOptions);
|
|
2132
2220
|
if (before) recordUndo(before, op, { label: "Add slide" });
|
|
2133
2221
|
|
|
2134
2222
|
return newSlide.id;
|
|
@@ -2329,6 +2417,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
|
|
|
2329
2417
|
refreshOpenDeck: refetchOpenDeckIfChanged,
|
|
2330
2418
|
getDeck,
|
|
2331
2419
|
addSlide,
|
|
2420
|
+
flushDeckSave,
|
|
2332
2421
|
updateSlide,
|
|
2333
2422
|
deleteSlide,
|
|
2334
2423
|
duplicateSlide,
|
|
@@ -290,53 +290,20 @@ button[title="Open agent sidebar"] {
|
|
|
290
290
|
|
|
291
291
|
.deck-editor-context-toolbar-host--narrow {
|
|
292
292
|
display: flex;
|
|
293
|
-
justify-content: flex-
|
|
293
|
+
justify-content: flex-start;
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
.deck-editor-context-toolbar-host--narrow
|
|
297
297
|
> [data-slide-inline-edit-surface="true"] {
|
|
298
298
|
min-width: 0;
|
|
299
|
-
width:
|
|
300
|
-
|
|
301
|
-
flex: 0 1 auto;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
@container deck-editor (max-width: 1149px) {
|
|
305
|
-
.deck-editor-context-toolbar-host--wide {
|
|
306
|
-
display: none;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.deck-editor-context-toolbar-host--narrow {
|
|
310
|
-
display: flex;
|
|
311
|
-
}
|
|
299
|
+
width: 100%;
|
|
300
|
+
flex: 1 1 auto;
|
|
312
301
|
}
|
|
313
302
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
.deck-editor-context-toolbar-host--wide[data-context-toolbar-visible="true"] {
|
|
320
|
-
display: flex;
|
|
321
|
-
flex: 1 1 0%;
|
|
322
|
-
align-items: center;
|
|
323
|
-
justify-content: flex-end;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
.deck-editor-context-toolbar-host--wide
|
|
327
|
-
> [data-slide-inline-edit-surface="true"] {
|
|
328
|
-
min-width: 0;
|
|
329
|
-
max-width: 100%;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
.deck-editor-context-toolbar-host--wide .slide-context-toolbar--top-row {
|
|
333
|
-
height: 2.75rem;
|
|
334
|
-
justify-content: flex-end;
|
|
335
|
-
border: 0;
|
|
336
|
-
background: transparent;
|
|
337
|
-
padding-inline: 0;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
303
|
+
/* The element-controls toolbar (New Slide, text/style controls, zoom)
|
|
304
|
+
* always renders as its own row below the title/Share/Present bar, at every
|
|
305
|
+
* viewport size — the "wide" host above stays permanently hidden by the
|
|
306
|
+
* base rule so the row never merges into the title bar on large screens. */
|
|
340
307
|
|
|
341
308
|
@container (max-width: 36rem) {
|
|
342
309
|
.deck-grid {
|
|
@@ -514,11 +514,15 @@ const messages = {
|
|
|
514
514
|
generatingSlide: "Generating slide",
|
|
515
515
|
uploadFailed: "Upload failed",
|
|
516
516
|
uploadAttachedFileFailed: "Could not upload the attached file.",
|
|
517
|
+
newSlideSaveFailed: "Couldn't save the new slide. Please try again.",
|
|
517
518
|
addSlides: "Add slides",
|
|
518
519
|
addEmptySlide: "Add empty slide",
|
|
519
520
|
noAi: "no AI",
|
|
520
521
|
duplicateCurrentSlide: "Duplicate current slide",
|
|
521
522
|
promptPlaceholder: "Describe the slides you want...",
|
|
523
|
+
newSlide: "New slide",
|
|
524
|
+
closeAddSlides: "Close",
|
|
525
|
+
describeThisSlide: "Describe this slide",
|
|
522
526
|
},
|
|
523
527
|
presentation: {
|
|
524
528
|
loadFailed: "Could not load this presentation.",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
interface EditorShortcutEvent {
|
|
2
|
+
key: string;
|
|
3
|
+
altKey: boolean;
|
|
4
|
+
ctrlKey: boolean;
|
|
5
|
+
metaKey: boolean;
|
|
6
|
+
shiftKey: boolean;
|
|
7
|
+
repeat: boolean;
|
|
8
|
+
isComposing: boolean;
|
|
9
|
+
target: EventTarget | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isEditableSurface(target: EventTarget | null): boolean {
|
|
13
|
+
return (
|
|
14
|
+
target instanceof HTMLElement &&
|
|
15
|
+
(target.closest(
|
|
16
|
+
"input, textarea, select, [contenteditable='true'], [role='textbox']",
|
|
17
|
+
) !== null ||
|
|
18
|
+
target.isContentEditable)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function shouldStopSlidesItalicShortcut(event: EditorShortcutEvent) {
|
|
23
|
+
if (
|
|
24
|
+
event.repeat ||
|
|
25
|
+
event.isComposing ||
|
|
26
|
+
event.key.toLowerCase() !== "i" ||
|
|
27
|
+
event.altKey ||
|
|
28
|
+
event.shiftKey ||
|
|
29
|
+
!(event.ctrlKey || event.metaKey)
|
|
30
|
+
) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isSlidesItalicEditableTarget(event: EditorShortcutEvent) {
|
|
38
|
+
return isEditableSurface(event.target);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function shouldSuppressSlidesItalicShortcut(event: EditorShortcutEvent) {
|
|
42
|
+
return (
|
|
43
|
+
shouldStopSlidesItalicShortcut(event) &&
|
|
44
|
+
!isSlidesItalicEditableTarget(event)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
shouldShowDeckEditorSkeleton,
|
|
77
77
|
} from "@/lib/deck-editor-loading";
|
|
78
78
|
import { getPreset } from "@/lib/design-systems";
|
|
79
|
+
import { shouldSuppressSlidesItalicShortcut } from "@/lib/editor-shortcuts";
|
|
79
80
|
import { exportDeckToGoogleSlides } from "@/lib/export-google-slides-client";
|
|
80
81
|
import { exportDeckAsPdf } from "@/lib/export-pdf-client";
|
|
81
82
|
import { exportDeckAsPptx } from "@/lib/export-pptx-client";
|
|
@@ -182,6 +183,7 @@ export default function DeckEditor() {
|
|
|
182
183
|
duplicateSlide,
|
|
183
184
|
duplicateDeck,
|
|
184
185
|
addSlide,
|
|
186
|
+
flushDeckSave,
|
|
185
187
|
reorderSlides,
|
|
186
188
|
markDeckDirty,
|
|
187
189
|
undo,
|
|
@@ -218,6 +220,25 @@ export default function DeckEditor() {
|
|
|
218
220
|
pendingDeckNavigationBlocker.proceed();
|
|
219
221
|
}, [pendingDeckNavigationBlocker]);
|
|
220
222
|
const { generating } = useAgentGenerating();
|
|
223
|
+
// Dedicated instance (not the `generating` one above, which reflects ANY
|
|
224
|
+
// agent chat activity) so an unrelated concurrent run can't be mistaken
|
|
225
|
+
// for this one finishing and clear the flag early. Owning the submit call
|
|
226
|
+
// here — instead of in EditorSidebar, which unmounts when the rail closes
|
|
227
|
+
// on narrow viewports — keeps both the run-scoping and the completion
|
|
228
|
+
// tracking correct across a remount.
|
|
229
|
+
const { generating: addSlideAgentGenerating, submit: addSlideAgentSubmit } =
|
|
230
|
+
useAgentGenerating();
|
|
231
|
+
const sawAddSlideAgentGeneratingRef = useRef(false);
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (addSlideAgentGenerating) {
|
|
234
|
+
sawAddSlideAgentGeneratingRef.current = true;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (addSlideGenerating && sawAddSlideAgentGeneratingRef.current) {
|
|
238
|
+
sawAddSlideAgentGeneratingRef.current = false;
|
|
239
|
+
setAddSlideGenerating(false);
|
|
240
|
+
}
|
|
241
|
+
}, [addSlideGenerating, addSlideAgentGenerating]);
|
|
221
242
|
// Generation intent can arrive after this route mounts because the user
|
|
222
243
|
// answers pre-generation questions from the empty editor.
|
|
223
244
|
const wasNewDeckCreation = useRef(searchParams.get("generating") === "1");
|
|
@@ -231,6 +252,14 @@ export default function DeckEditor() {
|
|
|
231
252
|
const [sidebarOpen, setSidebarOpen] = useState(
|
|
232
253
|
() => typeof window !== "undefined" && window.innerWidth >= 768,
|
|
233
254
|
);
|
|
255
|
+
// The slide just inserted via the toolbar's New Slide button, so the rail
|
|
256
|
+
// can anchor the "describe this slide" popover to its thumbnail once it
|
|
257
|
+
// mounts — even though the button that sets this now lives in the
|
|
258
|
+
// toolbar, outside the rail.
|
|
259
|
+
const [describeSlideId, setDescribeSlideId] = useState<string | null>(null);
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
setDescribeSlideId(null);
|
|
262
|
+
}, [id]);
|
|
234
263
|
const [contextToolbarSlot, setContextToolbarSlot] =
|
|
235
264
|
useState<HTMLDivElement | null>(null);
|
|
236
265
|
const [wideContextToolbarSlot, setWideContextToolbarSlot] =
|
|
@@ -756,6 +785,18 @@ export default function DeckEditor() {
|
|
|
756
785
|
document.removeEventListener("keydown", handleTextToolShortcut);
|
|
757
786
|
}, [canEdit]);
|
|
758
787
|
|
|
788
|
+
useEffect(() => {
|
|
789
|
+
const handleItalicShortcut = (event: KeyboardEvent) => {
|
|
790
|
+
if (!shouldSuppressSlidesItalicShortcut(event)) return;
|
|
791
|
+
event.preventDefault();
|
|
792
|
+
event.stopImmediatePropagation();
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
document.addEventListener("keydown", handleItalicShortcut, true);
|
|
796
|
+
return () =>
|
|
797
|
+
document.removeEventListener("keydown", handleItalicShortcut, true);
|
|
798
|
+
}, []);
|
|
799
|
+
|
|
759
800
|
// Delete key deletes the current slide
|
|
760
801
|
useEffect(() => {
|
|
761
802
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
@@ -807,49 +848,92 @@ export default function DeckEditor() {
|
|
|
807
848
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
808
849
|
}, [deck, id, activeSlideId, deleteSlideWithUndo, pinMode, drawMode]);
|
|
809
850
|
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
//
|
|
851
|
+
// Command/Ctrl+C then Command/Ctrl+V on the slide rail duplicates the
|
|
852
|
+
// selected slide directly below itself. Only claims the shortcut when no
|
|
853
|
+
// slide element is selected — SlideEditor owns Cmd+C/V for object copy/paste
|
|
854
|
+
// in that case.
|
|
814
855
|
const copiedSlideIdRef = useRef<string | null>(null);
|
|
815
856
|
useEffect(() => {
|
|
816
857
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
817
|
-
if (!deck || !id) return;
|
|
858
|
+
if (!deck || !id || !canEdit) return;
|
|
818
859
|
if (!(e.metaKey || e.ctrlKey) || e.altKey) return;
|
|
819
860
|
const key = e.key.toLowerCase();
|
|
820
861
|
if (key !== "c" && key !== "v") return;
|
|
821
862
|
if (pinMode || drawMode) return;
|
|
863
|
+
|
|
864
|
+
// A live browser text selection (e.g. the user triple-clicked rendered,
|
|
865
|
+
// non-editable slide copy) means Cmd/Ctrl+C is a normal text copy —
|
|
866
|
+
// let it through instead of hijacking it into a slide duplicate.
|
|
867
|
+
if (key === "c" && (window.getSelection()?.toString().length ?? 0) > 0) {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Radix Popper positions Popover/DropdownMenu/Select/Tooltip content
|
|
872
|
+
// inside the same [data-radix-popper-content-wrapper]. A tooltip opens
|
|
873
|
+
// on plain hover, so treating every such wrapper as blocking would
|
|
874
|
+
// disable this shortcut just by mousing over a toolbar button; only
|
|
875
|
+
// wrappers that aren't tooltips (marked with data-agent-native-tooltip)
|
|
876
|
+
// should count as an open menu/popover/dialog owning the keystroke.
|
|
877
|
+
const isBlockingPopperWrapper = (el: Element) =>
|
|
878
|
+
el.matches("[data-radix-popper-content-wrapper]") &&
|
|
879
|
+
!el.querySelector("[data-agent-native-tooltip]");
|
|
880
|
+
const isInsideSafeZone = (el: Element | null) => {
|
|
881
|
+
if (!el) return false;
|
|
882
|
+
if (el instanceof HTMLInputElement) return true;
|
|
883
|
+
if (el instanceof HTMLTextAreaElement) return true;
|
|
884
|
+
if (el instanceof HTMLElement) {
|
|
885
|
+
if (el.isContentEditable) return true;
|
|
886
|
+
if (el.closest("[contenteditable='true']")) return true;
|
|
887
|
+
if (el.closest("input, textarea, [role='textbox']")) return true;
|
|
888
|
+
if (el.closest("[data-pin-popover]")) return true;
|
|
889
|
+
if (el.closest("[data-add-slide-popover]")) return true;
|
|
890
|
+
if (el.closest(".agent-panel-root")) return true;
|
|
891
|
+
if (el.closest("[role='dialog'], [role='alertdialog']")) return true;
|
|
892
|
+
const popperWrapper = el.closest(
|
|
893
|
+
"[data-radix-popper-content-wrapper]",
|
|
894
|
+
);
|
|
895
|
+
if (popperWrapper && isBlockingPopperWrapper(popperWrapper))
|
|
896
|
+
return true;
|
|
897
|
+
}
|
|
898
|
+
return false;
|
|
899
|
+
};
|
|
900
|
+
if (isInsideSafeZone(e.target as Element | null)) return;
|
|
901
|
+
if (isInsideSafeZone(document.activeElement)) return;
|
|
902
|
+
if (document.querySelector("[data-pin-popover]")) return;
|
|
903
|
+
if (document.querySelector("[data-add-slide-popover]")) return;
|
|
904
|
+
// A dialog/sheet/menu/popover owning focus elsewhere in the DOM (not
|
|
905
|
+
// just under the event target) still shouldn't let this document-level
|
|
906
|
+
// shortcut duplicate the slide underneath it.
|
|
822
907
|
if (
|
|
823
908
|
document.querySelector(
|
|
824
|
-
"[role='dialog'], [role='menu'], [role='listbox']",
|
|
909
|
+
"[role='dialog'], [role='alertdialog'], [role='menu'], [role='listbox']",
|
|
825
910
|
)
|
|
826
|
-
)
|
|
911
|
+
)
|
|
912
|
+
return;
|
|
913
|
+
if (
|
|
914
|
+
Array.from(
|
|
915
|
+
document.querySelectorAll("[data-radix-popper-content-wrapper]"),
|
|
916
|
+
).some(isBlockingPopperWrapper)
|
|
917
|
+
)
|
|
918
|
+
return;
|
|
919
|
+
if (document.querySelector("[data-slide-element-selected='true']"))
|
|
827
920
|
return;
|
|
828
|
-
}
|
|
829
|
-
if (!canEdit) return;
|
|
830
|
-
const focusedThumbnail = document.activeElement?.closest(
|
|
831
|
-
"[data-slide-thumbnail-id]",
|
|
832
|
-
);
|
|
833
|
-
if (!focusedThumbnail) return;
|
|
834
|
-
const focusedSlideId = focusedThumbnail.getAttribute(
|
|
835
|
-
"data-slide-thumbnail-id",
|
|
836
|
-
);
|
|
837
|
-
if (!focusedSlideId) return;
|
|
838
921
|
|
|
839
922
|
if (key === "c") {
|
|
840
|
-
|
|
923
|
+
if (!activeSlideId) return;
|
|
924
|
+
copiedSlideIdRef.current = activeSlideId;
|
|
841
925
|
return;
|
|
842
926
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
if (!deck.slides.some((s) => s.id ===
|
|
927
|
+
|
|
928
|
+
const copiedId = copiedSlideIdRef.current;
|
|
929
|
+
if (!copiedId || !deck.slides.some((s) => s.id === copiedId)) return;
|
|
846
930
|
e.preventDefault();
|
|
847
|
-
const
|
|
848
|
-
if (
|
|
931
|
+
const newId = duplicateSlide(id, copiedId);
|
|
932
|
+
if (newId) setActiveSlideId(newId);
|
|
849
933
|
};
|
|
850
934
|
document.addEventListener("keydown", handleKeyDown);
|
|
851
935
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
852
|
-
}, [deck, id,
|
|
936
|
+
}, [deck, id, canEdit, activeSlideId, duplicateSlide, pinMode, drawMode]);
|
|
853
937
|
|
|
854
938
|
// Resolve the active slide from URL/deck state. Imports replace slide IDs, so
|
|
855
939
|
// keep this valid after deck contents change instead of only on first load.
|
|
@@ -1083,9 +1167,30 @@ export default function DeckEditor() {
|
|
|
1083
1167
|
|
|
1084
1168
|
const handleAddEmptySlide = () => {
|
|
1085
1169
|
const activeIdx = deck.slides.findIndex((s) => s.id === activeSlideId);
|
|
1086
|
-
|
|
1087
|
-
|
|
1170
|
+
// Immediate persistence: this placeholder is immediately followed by an
|
|
1171
|
+
// agent request to `update-slide` it, which can reach the server before
|
|
1172
|
+
// the default 500ms debounce would have flushed the `add-slide` op.
|
|
1173
|
+
const newId = addSlide(
|
|
1174
|
+
id,
|
|
1175
|
+
"blank",
|
|
1176
|
+
activeIdx >= 0 ? activeIdx : undefined,
|
|
1177
|
+
{
|
|
1178
|
+
persistence: "immediate",
|
|
1179
|
+
},
|
|
1088
1180
|
);
|
|
1181
|
+
setActiveSlideId(newId);
|
|
1182
|
+
return newId;
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
const handleNewSlideClick = () => {
|
|
1186
|
+
const newId = handleAddEmptySlide();
|
|
1187
|
+
if (newId) {
|
|
1188
|
+
// The rail owns the anchor node the describe-slide popover attaches
|
|
1189
|
+
// to, so it must be mounted even if it started closed on a narrow
|
|
1190
|
+
// viewport where the toolbar button is still reachable.
|
|
1191
|
+
setSidebarOpen(true);
|
|
1192
|
+
setDescribeSlideId(newId);
|
|
1193
|
+
}
|
|
1089
1194
|
};
|
|
1090
1195
|
|
|
1091
1196
|
return (
|
|
@@ -1101,7 +1206,6 @@ export default function DeckEditor() {
|
|
|
1101
1206
|
canEdit={canEdit}
|
|
1102
1207
|
canComment={canComment}
|
|
1103
1208
|
onTitleChange={(title) => updateDeck(id, { title })}
|
|
1104
|
-
slideCount={deck.slides.length}
|
|
1105
1209
|
currentSlideIndex={currentIndex >= 0 ? currentIndex : 0}
|
|
1106
1210
|
sidebarOpen={sidebarOpen}
|
|
1107
1211
|
onToggleSidebar={() => setSidebarOpen(!sidebarOpen)}
|
|
@@ -1113,6 +1217,8 @@ export default function DeckEditor() {
|
|
|
1113
1217
|
onShowHistory={() => setHistoryOpen((open) => !open)}
|
|
1114
1218
|
historyButtonRef={historyButtonRef}
|
|
1115
1219
|
currentSlide={currentSlide}
|
|
1220
|
+
onAddEmptySlide={canEdit ? handleNewSlideClick : undefined}
|
|
1221
|
+
addSlideGenerating={addSlideGenerating}
|
|
1116
1222
|
onWideContextToolbarSlotChange={setWideContextToolbarSlot}
|
|
1117
1223
|
activeUsers={slideActiveUsers.filter((u) => u.email !== session?.email)}
|
|
1118
1224
|
agentPresent={agentPresent}
|
|
@@ -1187,13 +1293,6 @@ export default function DeckEditor() {
|
|
|
1187
1293
|
}
|
|
1188
1294
|
return exportDeckToGoogleSlides(deck.title, slides, deck.aspectRatio);
|
|
1189
1295
|
}}
|
|
1190
|
-
currentSlideId={currentSlide?.id}
|
|
1191
|
-
addSlideGenerating={addSlideGenerating}
|
|
1192
|
-
onAddSlideGeneratingChange={setAddSlideGenerating}
|
|
1193
|
-
onAddEmptySlide={handleAddEmptySlide}
|
|
1194
|
-
onDuplicateCurrentSlide={
|
|
1195
|
-
currentSlide ? () => duplicateSlide(id, currentSlide.id) : undefined
|
|
1196
|
-
}
|
|
1197
1296
|
onChangeSlideTransition={
|
|
1198
1297
|
canEdit && currentSlide
|
|
1199
1298
|
? (transition) => updateSlide(id, currentSlide.id, { transition })
|
|
@@ -1226,6 +1325,13 @@ export default function DeckEditor() {
|
|
|
1226
1325
|
slides={deck.slides}
|
|
1227
1326
|
activeSlideId={currentSlide?.id || ""}
|
|
1228
1327
|
deckId={id}
|
|
1328
|
+
deckTitle={deck.title}
|
|
1329
|
+
describeSlideId={describeSlideId}
|
|
1330
|
+
onCloseDescribe={() => setDescribeSlideId(null)}
|
|
1331
|
+
onAwaitAddSlidePersisted={() => flushDeckSave(id)}
|
|
1332
|
+
onRemoveFailedSlide={(slideId) => deleteSlide(id, slideId)}
|
|
1333
|
+
addSlideAgentSubmit={addSlideAgentSubmit}
|
|
1334
|
+
onAddSlideGeneratingChange={setAddSlideGenerating}
|
|
1229
1335
|
onSelectSlide={(slideId) => {
|
|
1230
1336
|
setGeneratingSlideSelected(false);
|
|
1231
1337
|
setActiveSlideId(slideId);
|
|
@@ -1304,19 +1410,11 @@ export default function DeckEditor() {
|
|
|
1304
1410
|
contextToolbarLeading={
|
|
1305
1411
|
canEdit ? (
|
|
1306
1412
|
<EditorActionCluster
|
|
1307
|
-
deckId={id}
|
|
1308
|
-
deckTitle={deck.title}
|
|
1309
|
-
currentSlideId={currentSlide.id}
|
|
1310
|
-
slideCount={deck.slides.length}
|
|
1311
|
-
currentSlideIndex={currentIndex >= 0 ? currentIndex : 0}
|
|
1312
|
-
addSlideGenerating={addSlideGenerating}
|
|
1313
|
-
onAddSlideGeneratingChange={setAddSlideGenerating}
|
|
1314
|
-
onAddEmptySlide={handleAddEmptySlide}
|
|
1315
|
-
onDuplicateCurrentSlide={() =>
|
|
1316
|
-
duplicateSlide(id, currentSlide.id)
|
|
1317
|
-
}
|
|
1318
1413
|
textBoxMode={textBoxMode}
|
|
1319
1414
|
onToggleTextBoxMode={toggleTextBoxMode}
|
|
1415
|
+
onAddEmptySlide={handleNewSlideClick}
|
|
1416
|
+
addSlideGenerating={addSlideGenerating}
|
|
1417
|
+
currentSlideId={currentSlide.id}
|
|
1320
1418
|
slideTransition={currentSlide.transition}
|
|
1321
1419
|
onChangeSlideTransition={(transition) =>
|
|
1322
1420
|
updateSlide(id, currentSlide.id, { transition })
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createProductionAgentHandler, type ActionEntry, type ScriptEntry, type ProductionAgentOptions, type AgentLoopFinalResponseGuard, type AgentLoopFinalResponseGuardContext, type AgentLoopFinalResponseGuardResult, type AgentLoopToolCallSummary, type AgentLoopToolResultSummary, } from "./production-agent.js";
|
|
1
|
+
export { createProductionAgentHandler, type ActionEntry, type ScriptEntry, type ProductionAgentOptions, type AgentActionSurface, type AgentActionSurfaceDetails, type AgentLoopFinalResponseGuard, type AgentLoopFinalResponseGuardContext, type AgentLoopFinalResponseGuardResult, type AgentLoopToolCallSummary, type AgentLoopToolResultSummary, } from "./production-agent.js";
|
|
2
2
|
export { type ActionTool, type ScriptTool, type AgentMessage, type AgentChatRequest, type AgentChatEvent, type AgentChatAttachment, type AgentChatReference, type MentionProvider, type MentionProviderItem, } from "./types.js";
|
|
3
3
|
export { TripWire, type Processor, type ProcessorState, type ProcessorAbort, type ProcessOutputStreamArgs, type ProcessOutputStepArgs, type ProcessOutputResultArgs, } from "./processors.js";
|
|
4
4
|
export { DEFAULT_MODEL } from "./default-model.js";
|