@agent-native/core 0.149.6 → 0.151.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/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +83 -45
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-dashboards-keep-the-selected-tab-when-reopened.md +6 -0
- package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/clips/changelog/2026-08-11-clips-recovers-from-transient-updates-and-avoids-macos-priva.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +32 -176
- package/corpus/templates/clips/desktop/src/lib/updater.ts +52 -37
- package/corpus/templates/clips/desktop/src/styles.css +1 -166
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -0
- package/corpus/templates/clips/desktop/src-tauri/Info.plist +4 -0
- package/corpus/templates/clips/desktop/src-tauri/src/eventkit.rs +28 -1
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +48 -1
- package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +5 -0
- package/corpus/templates/clips/server/plugins/auth.ts +3 -0
- package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +2 -1
- package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -1
- package/corpus/templates/design/changelog/2026-08-11-design-agent-prompts-copy-successfully-in-the-desktop-app.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/dispatch/app/global.css +2 -2
- package/corpus/templates/dispatch/changelog/2026-08-10-desktop-workspace-sign-in.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-shows-the-generic-chat-starter-among-defa.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-integrations-and-scheduled-work-open-as-first-class-electron-pages.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-sidebar-text-is-slightly-easier-to-read-in-light-and-dark-mo.md +6 -0
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +57 -31
- package/corpus/templates/dispatch/server/plugins/feature-flags.ts +5 -0
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +82 -33
- package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +7 -15
- package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +9 -1
- package/corpus/templates/dispatch/shared/feature-flags.ts +15 -0
- package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +8 -7
- package/corpus/templates/forms/app/hooks/use-forms.ts +8 -1
- package/corpus/templates/forms/changelog/2026-08-11-fixed-public-forms-so-conditional-questions-stay-hidden-unti.md +6 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +2 -1
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +37 -2
- package/corpus/templates/forms/shared/page-url.ts +54 -0
- package/corpus/templates/forms/shared/types.ts +4 -4
- package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/slides/actions/add-slide.ts +0 -27
- package/corpus/templates/slides/actions/import-file.ts +28 -9
- package/corpus/templates/slides/actions/view-screen.ts +4 -4
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +8 -1
- package/corpus/templates/slides/app/components/editor/AiEditingMarker.tsx +25 -0
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +43 -48
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +9 -6
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -9
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +2 -0
- package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +0 -26
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +904 -217
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +11 -0
- package/corpus/templates/slides/app/components/editor/inline-edit-session.ts +16 -0
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +22 -6
- package/corpus/templates/slides/app/components/editor/style-clipboard.ts +61 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +26 -2
- package/corpus/templates/slides/app/components/ui/context-menu.tsx +1 -0
- package/corpus/templates/slides/app/context/DeckContext.tsx +75 -25
- package/corpus/templates/slides/app/global.css +1 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -1
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
- package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +12 -12
- package/corpus/templates/slides/app/lib/slide-agent-context.ts +52 -0
- package/corpus/templates/slides/app/lib/slide-image-replacement.ts +61 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +55 -116
- package/corpus/templates/slides/app/pages/Index.tsx +28 -3
- package/corpus/templates/slides/changelog/2026-08-11-agent-chat-now-follows-the-current-slide-and-selection.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-deck-titles-now-use-slide-content-instead-of-placeholder-filenames.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-history-now-opens-from-the-deck-overflow-menu.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-images-can-be-dropped-directly-onto-slides-and-resized-or-re.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-new-decks-now-transition-directly-to-a-full-page-editor-load.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-selected-slide-layers-can-be-nudged-precisely-with-arrow-key.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-can-copy-and-paste-element-styles-with-cmd-option-c-v.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-shows-one-compact-ai-marker-and-keeps-your-current-sl.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-text-edits-preserve-slide-layout-when-no-changes-are-made.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-the-style-toolbar-now-uses-the-full-available-width.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-undo-and-redo-now-move-one-visible-slide-state-at-a-time.md +6 -0
- package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -2
- package/corpus/templates/slides/shared/deck-title.ts +56 -0
- package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/agent/run-store.js +6 -1
- package/dist/agent/thread-data-builder.js +19 -44
- package/dist/agent/types.d.ts +13 -0
- package/dist/catalog.json +1 -0
- package/dist/cli/create.js +1 -1
- package/dist/client/AssistantChat.js +58 -10
- package/dist/client/FileStorageSetupCard.d.ts +6 -0
- package/dist/client/FileStorageSetupCard.js +18 -0
- package/dist/client/NewWorkspaceAppFlow.js +10 -0
- package/dist/client/agent-chat-adapter.js +87 -11
- package/dist/client/agent-chat.d.ts +1 -1
- package/dist/client/agent-chat.js +8 -3
- package/dist/client/chat/attachment-adapters.js +10 -0
- package/dist/client/chat/message-components.d.ts +4 -1
- package/dist/client/chat/message-components.js +37 -13
- package/dist/client/chat/tool-call-display.js +14 -0
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +6 -6
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/client/chat-first/types.d.ts +1 -0
- package/dist/client/extensions/EmbeddedExtension.js +1 -1
- package/dist/client/extensions/ExtensionViewer.js +1 -1
- package/dist/client/extensions/InlineExtensionFrame.js +1 -1
- package/dist/client/onboarding/FirstRunOnboarding.js +17 -2
- package/dist/client/settings/FileStorageSettingsForm.d.ts +2 -0
- package/dist/client/settings/FileStorageSettingsForm.js +113 -0
- package/dist/client/settings/SettingsPanel.js +2 -1
- package/dist/client/sse-event-processor.js +21 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/extensions/actions.js +2 -2
- package/dist/extensions/html-shell.js +3 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +15 -10
- package/dist/file-upload/pre-upload-attachments.js +68 -24
- package/dist/file-upload/registry.d.ts +4 -5
- package/dist/file-upload/registry.js +7 -14
- package/dist/file-upload/s3.d.ts +9 -0
- package/dist/file-upload/s3.js +168 -0
- package/dist/file-upload/types.d.ts +3 -3
- package/dist/file-upload/types.js +2 -2
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/app-profile.js +23 -1
- package/dist/onboarding/default-steps.js +69 -0
- package/dist/onboarding/types.d.ts +2 -0
- 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 +8 -8
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-chat/browser-team-tools.js +20 -0
- package/dist/server/agent-chat-plugin.js +12 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +45 -21
- package/dist/server/better-auth-instance.d.ts +2 -0
- package/dist/server/better-auth-instance.js +2 -2
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.js +26 -16
- package/dist/server/identity-sso-store.d.ts +3 -0
- package/dist/server/identity-sso-store.js +45 -0
- package/dist/server/identity-sso.d.ts +20 -10
- package/dist/server/identity-sso.js +79 -31
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
- package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
- package/docs/content/authentication.mdx +27 -0
- package/docs/content/cross-app-sso.mdx +38 -8
- package/docs/content/extensions.mdx +15 -15
- package/docs/content/file-uploads.mdx +10 -12
- package/docs/content/generative-ui.mdx +6 -1
- package/package.json +2 -2
- package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
- package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
- package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const SENSITIVE_QUERY_PARAMS = [
|
|
2
|
+
"password",
|
|
3
|
+
"p",
|
|
4
|
+
"token",
|
|
5
|
+
"state",
|
|
6
|
+
"code",
|
|
7
|
+
"share",
|
|
8
|
+
"share_token",
|
|
9
|
+
"bridge",
|
|
10
|
+
"access_token",
|
|
11
|
+
"id_token",
|
|
12
|
+
"refresh_token",
|
|
13
|
+
"authorization",
|
|
14
|
+
"api_key",
|
|
15
|
+
"key",
|
|
16
|
+
"secret",
|
|
17
|
+
"session",
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
const SENSITIVE_QUERY_PARAM_SET = new Set<string>(SENSITIVE_QUERY_PARAMS);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Keep response page context useful while never retaining URL-bar secrets.
|
|
24
|
+
* This mirrors the framework feedback URL scrubber for Forms-owned public
|
|
25
|
+
* submissions and re-checks metadata supplied by embeds on the server.
|
|
26
|
+
*/
|
|
27
|
+
export function scrubPageUrl(value: unknown): string | null {
|
|
28
|
+
if (typeof value !== "string") return null;
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
if (!trimmed) return null;
|
|
31
|
+
if (!URL.canParse(trimmed)) return null;
|
|
32
|
+
|
|
33
|
+
const url = new URL(trimmed);
|
|
34
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
35
|
+
url.username = "";
|
|
36
|
+
url.password = "";
|
|
37
|
+
scrubParams(url.searchParams);
|
|
38
|
+
|
|
39
|
+
if (url.hash.includes("=")) {
|
|
40
|
+
const hashParams = new URLSearchParams(url.hash.slice(1));
|
|
41
|
+
scrubParams(hashParams);
|
|
42
|
+
url.hash = hashParams.toString();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return url.toString();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function scrubParams(params: URLSearchParams) {
|
|
49
|
+
for (const key of params.keys()) {
|
|
50
|
+
if (SENSITIVE_QUERY_PARAM_SET.has(key.toLowerCase())) {
|
|
51
|
+
params.set(key, "<redacted>");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -155,15 +155,15 @@ export interface FormResponse {
|
|
|
155
155
|
/** Real submitter email when known; synthetic anonymous-owner ids are hidden. */
|
|
156
156
|
submitterEmail?: string | null;
|
|
157
157
|
/**
|
|
158
|
-
* URL of the
|
|
159
|
-
*
|
|
160
|
-
*
|
|
158
|
+
* URL of the direct public form or trusted embed source page. Sensitive URL
|
|
159
|
+
* query keys are scrubbed before it is stored; null when no page context is
|
|
160
|
+
* available or anonymous mode suppresses response metadata.
|
|
161
161
|
*/
|
|
162
162
|
pageUrl?: string | null;
|
|
163
163
|
/**
|
|
164
164
|
* Runtime shell the feedback was sent from — "web", "electron", or "tauri" —
|
|
165
165
|
* forwarded by trusted embeds as a hidden pass-through field. Null when
|
|
166
|
-
* unknown
|
|
166
|
+
* unknown or anonymous mode suppresses response metadata.
|
|
167
167
|
*/
|
|
168
168
|
clientSurface?: string | null;
|
|
169
169
|
}
|
|
@@ -49,6 +49,34 @@ sendToAgentChat({
|
|
|
49
49
|
});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
### Keep user text separate from injected context
|
|
53
|
+
|
|
54
|
+
Treat the visible `message` as the user's request or the shortest clear
|
|
55
|
+
description of an app-initiated operation. Put context the user did not type —
|
|
56
|
+
IDs, URLs, filenames, current selection or screen state, serialized records,
|
|
57
|
+
upload instructions, and bounded source excerpts — in the `context` field.
|
|
58
|
+
`context` is model input carried through the agent turn; it is stripped from
|
|
59
|
+
the rendered user message, so do not concatenate it into `message` with labels,
|
|
60
|
+
blank-line sections, or `<context>` tags yourself.
|
|
61
|
+
|
|
62
|
+
Use the related surface for each kind of supporting input:
|
|
63
|
+
|
|
64
|
+
| Surface | Use for |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `message` | User-authored intent or a concise app action description |
|
|
67
|
+
| `context` | Derived metadata and bounded text needed to carry out that intent |
|
|
68
|
+
| `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
|
|
69
|
+
| `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
|
|
70
|
+
|
|
71
|
+
If an app builds a prompt from a form, selection, upload, or editor state, keep
|
|
72
|
+
the visible message short and pass the assembled details as `context`. Preserve
|
|
73
|
+
the user's actual freeform text in `message` when it is the request; do not
|
|
74
|
+
restate it as metadata in `context` unless the agent needs a structured copy.
|
|
75
|
+
|
|
76
|
+
This boundary applies to auto-submitted turns and prefills. A hidden context
|
|
77
|
+
field is not a license to send unbounded records or secrets: cap excerpts,
|
|
78
|
+
prefer stable IDs and URLs, and use an action or resource lookup for full data.
|
|
79
|
+
|
|
52
80
|
**From the UI, in the background:**
|
|
53
81
|
|
|
54
82
|
```ts
|
|
@@ -49,6 +49,34 @@ sendToAgentChat({
|
|
|
49
49
|
});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
### Keep user text separate from injected context
|
|
53
|
+
|
|
54
|
+
Treat the visible `message` as the user's request or the shortest clear
|
|
55
|
+
description of an app-initiated operation. Put context the user did not type —
|
|
56
|
+
IDs, URLs, filenames, current selection or screen state, serialized records,
|
|
57
|
+
upload instructions, and bounded source excerpts — in the `context` field.
|
|
58
|
+
`context` is model input carried through the agent turn; it is stripped from
|
|
59
|
+
the rendered user message, so do not concatenate it into `message` with labels,
|
|
60
|
+
blank-line sections, or `<context>` tags yourself.
|
|
61
|
+
|
|
62
|
+
Use the related surface for each kind of supporting input:
|
|
63
|
+
|
|
64
|
+
| Surface | Use for |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `message` | User-authored intent or a concise app action description |
|
|
67
|
+
| `context` | Derived metadata and bounded text needed to carry out that intent |
|
|
68
|
+
| `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
|
|
69
|
+
| `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
|
|
70
|
+
|
|
71
|
+
If an app builds a prompt from a form, selection, upload, or editor state, keep
|
|
72
|
+
the visible message short and pass the assembled details as `context`. Preserve
|
|
73
|
+
the user's actual freeform text in `message` when it is the request; do not
|
|
74
|
+
restate it as metadata in `context` unless the agent needs a structured copy.
|
|
75
|
+
|
|
76
|
+
This boundary applies to auto-submitted turns and prefills. A hidden context
|
|
77
|
+
field is not a license to send unbounded records or secrets: cap excerpts,
|
|
78
|
+
prefer stable IDs and URLs, and use an action or resource lookup for full data.
|
|
79
|
+
|
|
52
80
|
**From the UI, in the background:**
|
|
53
81
|
|
|
54
82
|
```ts
|
|
@@ -49,6 +49,34 @@ sendToAgentChat({
|
|
|
49
49
|
});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
### Keep user text separate from injected context
|
|
53
|
+
|
|
54
|
+
Treat the visible `message` as the user's request or the shortest clear
|
|
55
|
+
description of an app-initiated operation. Put context the user did not type —
|
|
56
|
+
IDs, URLs, filenames, current selection or screen state, serialized records,
|
|
57
|
+
upload instructions, and bounded source excerpts — in the `context` field.
|
|
58
|
+
`context` is model input carried through the agent turn; it is stripped from
|
|
59
|
+
the rendered user message, so do not concatenate it into `message` with labels,
|
|
60
|
+
blank-line sections, or `<context>` tags yourself.
|
|
61
|
+
|
|
62
|
+
Use the related surface for each kind of supporting input:
|
|
63
|
+
|
|
64
|
+
| Surface | Use for |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `message` | User-authored intent or a concise app action description |
|
|
67
|
+
| `context` | Derived metadata and bounded text needed to carry out that intent |
|
|
68
|
+
| `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
|
|
69
|
+
| `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
|
|
70
|
+
|
|
71
|
+
If an app builds a prompt from a form, selection, upload, or editor state, keep
|
|
72
|
+
the visible message short and pass the assembled details as `context`. Preserve
|
|
73
|
+
the user's actual freeform text in `message` when it is the request; do not
|
|
74
|
+
restate it as metadata in `context` unless the agent needs a structured copy.
|
|
75
|
+
|
|
76
|
+
This boundary applies to auto-submitted turns and prefills. A hidden context
|
|
77
|
+
field is not a license to send unbounded records or secrets: cap excerpts,
|
|
78
|
+
prefer stable IDs and URLs, and use an action or resource lookup for full data.
|
|
79
|
+
|
|
52
80
|
**From the UI, in the background:**
|
|
53
81
|
|
|
54
82
|
```ts
|
|
@@ -24,10 +24,6 @@ import {
|
|
|
24
24
|
awaitLayoutFitCheck,
|
|
25
25
|
formatOverflowForTool,
|
|
26
26
|
} from "./_await-fit-check.js";
|
|
27
|
-
import {
|
|
28
|
-
readAppStateForCurrentTab,
|
|
29
|
-
writeAppStateForCurrentTab,
|
|
30
|
-
} from "./_tab-state.js";
|
|
31
27
|
// Use the shared, globalThis-pinned per-deck lock so add-slide, update-slide,
|
|
32
28
|
// and the browser's patch-deck all serialise against the SAME lock — writes to
|
|
33
29
|
// different slides of the same deck can never clobber each other.
|
|
@@ -383,29 +379,6 @@ export default defineAction({
|
|
|
383
379
|
// Include the new slideId + agent actor (backwards-compatible payload).
|
|
384
380
|
notifyClients(deckId, { slideId: newSlideId, actor: "agent" });
|
|
385
381
|
|
|
386
|
-
// Nudge any open editor onto the new slide so the renderer measures
|
|
387
|
-
// IT (not whichever slide was previously selected). Only fires when an
|
|
388
|
-
// editor is open on this deck; navigation state is a no-op if nobody
|
|
389
|
-
// is watching.
|
|
390
|
-
const nav = (await readAppStateForCurrentTab("navigation", {
|
|
391
|
-
fallbackToGlobal: false,
|
|
392
|
-
}).catch(() => null)) as {
|
|
393
|
-
view?: string;
|
|
394
|
-
deckId?: string;
|
|
395
|
-
} | null;
|
|
396
|
-
if (nav?.view === "editor" && nav.deckId === deckId) {
|
|
397
|
-
await writeAppStateForCurrentTab("navigate", {
|
|
398
|
-
deckId,
|
|
399
|
-
slideIndex: insertIndex,
|
|
400
|
-
// Unique-per-write token. The UI's `use-navigation-state` hook
|
|
401
|
-
// dedups by this so a race between the GET and the consume-DELETE
|
|
402
|
-
// doesn't cause the same command to be re-applied repeatedly
|
|
403
|
-
// (which previously bounced the editor between slides whenever the
|
|
404
|
-
// agent path errored partway through a turn).
|
|
405
|
-
_writeId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
406
|
-
}).catch(() => {});
|
|
407
|
-
}
|
|
408
|
-
|
|
409
382
|
// Wait briefly for the editor to render the new slide and report its
|
|
410
383
|
// measured fit. If we get an "overflows" signal, append the auto-fix
|
|
411
384
|
// hint so the agent can make one bounded structural repair. Timeout = no
|
|
@@ -28,7 +28,10 @@ import {
|
|
|
28
28
|
DEFAULT_ASPECT_RATIO,
|
|
29
29
|
type AspectRatio,
|
|
30
30
|
} from "../shared/aspect-ratios.js";
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
isOpaqueDeckTitle,
|
|
33
|
+
resolveImportedDeckTitle,
|
|
34
|
+
} from "../shared/deck-title.js";
|
|
32
35
|
import { readUserUploadedFile } from "./_uploaded-files.js";
|
|
33
36
|
import { withDeckLock } from "./patch-deck.js";
|
|
34
37
|
|
|
@@ -182,17 +185,18 @@ export default defineAction({
|
|
|
182
185
|
presentation.slides[0].heightEmu,
|
|
183
186
|
)
|
|
184
187
|
: undefined;
|
|
185
|
-
await appendDeckSlides(
|
|
188
|
+
const importedTitle = await appendDeckSlides(
|
|
186
189
|
deckId,
|
|
187
190
|
title,
|
|
188
191
|
slides,
|
|
189
192
|
"import-file:pptx",
|
|
190
193
|
aspectRatio,
|
|
191
194
|
sourceImport,
|
|
195
|
+
pptxResults[0]?.sourceText,
|
|
192
196
|
);
|
|
193
197
|
return {
|
|
194
198
|
format: "pptx",
|
|
195
|
-
title,
|
|
199
|
+
title: importedTitle,
|
|
196
200
|
slideCount: slides.length,
|
|
197
201
|
theme: presentation.theme,
|
|
198
202
|
deckId,
|
|
@@ -241,10 +245,18 @@ export default defineAction({
|
|
|
241
245
|
layout: "content",
|
|
242
246
|
notes: "",
|
|
243
247
|
}));
|
|
244
|
-
await appendDeckSlides(
|
|
248
|
+
const importedTitle = await appendDeckSlides(
|
|
249
|
+
deckId,
|
|
250
|
+
title,
|
|
251
|
+
slides,
|
|
252
|
+
"import-file:docx",
|
|
253
|
+
undefined,
|
|
254
|
+
undefined,
|
|
255
|
+
doc.text,
|
|
256
|
+
);
|
|
245
257
|
return {
|
|
246
258
|
format: "docx",
|
|
247
|
-
title,
|
|
259
|
+
title: importedTitle,
|
|
248
260
|
sectionCount: doc.sections.length,
|
|
249
261
|
slideCount: slides.length,
|
|
250
262
|
textLength: doc.text.length,
|
|
@@ -520,18 +532,19 @@ async function importPdfPagesWithFidelity(args: {
|
|
|
520
532
|
slides: imported.map((entry) => entry.snapshot),
|
|
521
533
|
});
|
|
522
534
|
|
|
523
|
-
await appendDeckSlides(
|
|
535
|
+
const importedTitle = await appendDeckSlides(
|
|
524
536
|
deckId,
|
|
525
537
|
title,
|
|
526
538
|
slides,
|
|
527
539
|
"import-file:pdf",
|
|
528
540
|
aspectRatio,
|
|
529
541
|
sourceImport,
|
|
542
|
+
imported[0]?.snapshot.text,
|
|
530
543
|
);
|
|
531
544
|
|
|
532
545
|
return {
|
|
533
546
|
format: "pdf",
|
|
534
|
-
title,
|
|
547
|
+
title: importedTitle,
|
|
535
548
|
pageCount: slides.length,
|
|
536
549
|
slideCount: slides.length,
|
|
537
550
|
aspectRatio,
|
|
@@ -674,7 +687,8 @@ async function appendDeckSlides(
|
|
|
674
687
|
source: string,
|
|
675
688
|
aspectRatio?: AspectRatio,
|
|
676
689
|
sourceImport?: ReturnType<typeof buildSourceImportMetadata>,
|
|
677
|
-
|
|
690
|
+
titleSource?: unknown,
|
|
691
|
+
): Promise<string> {
|
|
678
692
|
await assertAccess("deck", deckId, "editor");
|
|
679
693
|
|
|
680
694
|
// Read-modify-write under the shared per-deck lock used by patch-deck /
|
|
@@ -682,6 +696,7 @@ async function appendDeckSlides(
|
|
|
682
696
|
// with another import or an editor/agent slide mutation on the same deck
|
|
683
697
|
// could read stale data and clobber the other write when both save the
|
|
684
698
|
// whole decks.data blob back.
|
|
699
|
+
let resolvedTitle = title;
|
|
685
700
|
const now = await withDeckLock(deckId, async () => {
|
|
686
701
|
const db = getDb();
|
|
687
702
|
const existing = await db
|
|
@@ -706,7 +721,10 @@ async function appendDeckSlides(
|
|
|
706
721
|
// had no slides yet, otherwise resizing the canvas mid-deck would distort
|
|
707
722
|
// every slide already on it.
|
|
708
723
|
const hadExistingSlides = previousSlides.length > 0;
|
|
709
|
-
const nextTitle = hadExistingSlides
|
|
724
|
+
const nextTitle = hadExistingSlides
|
|
725
|
+
? (existing[0].title ?? title)
|
|
726
|
+
: resolveImportedDeckTitle(title, titleSource ?? slides[0]?.content);
|
|
727
|
+
resolvedTitle = nextTitle;
|
|
710
728
|
const nextSourceImport = sourceImport
|
|
711
729
|
? mergeSourceImportMetadata(
|
|
712
730
|
sourceImportForDeck(previousData.sourceImport),
|
|
@@ -736,6 +754,7 @@ async function appendDeckSlides(
|
|
|
736
754
|
|
|
737
755
|
notifyClients(deckId);
|
|
738
756
|
await writeAppState("refresh-signal", { ts: now, source });
|
|
757
|
+
return resolvedTitle;
|
|
739
758
|
}
|
|
740
759
|
|
|
741
760
|
function safeParseDeckData(raw: string): Record<string, unknown> {
|
|
@@ -162,12 +162,12 @@ export default defineAction({
|
|
|
162
162
|
style?: Record<string, unknown>;
|
|
163
163
|
}>;
|
|
164
164
|
} | null;
|
|
165
|
-
if (
|
|
166
|
-
selection &&
|
|
167
|
-
(!selection.slideId || selection.slideId === currentSlide?.id)
|
|
168
|
-
) {
|
|
165
|
+
if (selection && currentSlide && selection.slideId === currentSlide.id) {
|
|
169
166
|
lines.push(``);
|
|
170
167
|
lines.push(`### Current visual selection`);
|
|
168
|
+
lines.push(
|
|
169
|
+
`selectionSlideId: ${selection.slideId} (matches currentSlideId)`,
|
|
170
|
+
);
|
|
171
171
|
lines.push(`mode: ${selection.mode ?? "unknown"}`);
|
|
172
172
|
lines.push(`activeTool: ${selection.activeTool ?? "select"}`);
|
|
173
173
|
if (Array.isArray(selection.items) && selection.items.length > 0) {
|
|
@@ -581,8 +581,15 @@ export function DesignSystemSetup({
|
|
|
581
581
|
}. Present a summary for review.`,
|
|
582
582
|
);
|
|
583
583
|
|
|
584
|
+
const message =
|
|
585
|
+
parts[0] ?? "Set up a design system from the selected sources.";
|
|
586
|
+
const contextParts = parts.slice(1);
|
|
584
587
|
openAgentSidebar();
|
|
585
|
-
sendToAgentChat({
|
|
588
|
+
sendToAgentChat({
|
|
589
|
+
message,
|
|
590
|
+
context: contextParts.join("\n"),
|
|
591
|
+
submit: true,
|
|
592
|
+
});
|
|
586
593
|
toast(t("designSystemSetup.generationStarted"), {
|
|
587
594
|
description: t("designSystemSetup.generationStartedDescription"),
|
|
588
595
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { DEFAULT_AGENT_IDENTITY } from "@agent-native/toolkit/collab-ui";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
/** The single compact marker used for every AI-editing state in Slides. */
|
|
7
|
+
export function AiEditingMarker({ className }: { className?: string }) {
|
|
8
|
+
const t = useT();
|
|
9
|
+
const label = t("raw.aiEditing");
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<span
|
|
13
|
+
aria-label={label}
|
|
14
|
+
className={cn(
|
|
15
|
+
"inline-flex size-5 shrink-0 items-center justify-center rounded-full text-[9px] font-semibold leading-none text-primary-foreground",
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
role="img"
|
|
19
|
+
style={{ backgroundColor: DEFAULT_AGENT_IDENTITY.color }}
|
|
20
|
+
title={label}
|
|
21
|
+
>
|
|
22
|
+
AI
|
|
23
|
+
</span>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
agentNativePath,
|
|
3
3
|
appBasePath,
|
|
4
4
|
} from "@agent-native/core/client/api-path";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import type {
|
|
6
|
+
AttributedRecentEdit,
|
|
7
|
+
CollabUser,
|
|
8
8
|
} from "@agent-native/core/client/collab";
|
|
9
9
|
import { useAvatarUrl } from "@agent-native/core/client/hooks";
|
|
10
10
|
import { useT } from "@agent-native/core/client/i18n";
|
|
11
|
-
import {
|
|
11
|
+
import { DEFAULT_AGENT_IDENTITY } from "@agent-native/toolkit/collab-ui";
|
|
12
12
|
import {
|
|
13
13
|
useSortable,
|
|
14
14
|
SortableContext,
|
|
@@ -22,6 +22,7 @@ import { useCallback } from "react";
|
|
|
22
22
|
|
|
23
23
|
import SlideRenderer from "@/components/deck/SlideRenderer";
|
|
24
24
|
import type { SlideOverflowInfo } from "@/components/deck/SlideRenderer";
|
|
25
|
+
import { AiEditingMarker } from "@/components/editor/AiEditingMarker";
|
|
25
26
|
import GeneratingSlidePreview from "@/components/editor/GeneratingSlidePreview";
|
|
26
27
|
import {
|
|
27
28
|
Tooltip,
|
|
@@ -43,14 +44,14 @@ interface EditorSidebarProps {
|
|
|
43
44
|
readOnly?: boolean;
|
|
44
45
|
/** Presence map: slideId → list of users currently viewing that slide */
|
|
45
46
|
slidePresence?: Map<string, CollabUser[]>;
|
|
46
|
-
/** Lingering recent edits
|
|
47
|
+
/** Lingering recent edits used to show the compact AI marker. */
|
|
47
48
|
recentEdits?: AttributedRecentEdit[];
|
|
48
49
|
/** Deck aspect ratio (defaults to 16:9 when omitted) */
|
|
49
50
|
aspectRatio?: AspectRatio;
|
|
50
51
|
/** Active deck design system used by slide content tokens. */
|
|
51
52
|
designSystem?: DesignSystemData;
|
|
52
53
|
/** The next slide while the agent is preparing its HTML. */
|
|
53
|
-
generatingSlide?: { index: number
|
|
54
|
+
generatingSlide?: { index: number };
|
|
54
55
|
generatingSlideSelected?: boolean;
|
|
55
56
|
onSelectGeneratingSlide?: () => void;
|
|
56
57
|
}
|
|
@@ -72,6 +73,13 @@ function slideIdFromEdit(edit: AttributedRecentEdit): string | null {
|
|
|
72
73
|
return null;
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
function isAgentPresenceUser(user: CollabUser): boolean {
|
|
77
|
+
return (
|
|
78
|
+
user.email.trim().toLowerCase() ===
|
|
79
|
+
DEFAULT_AGENT_IDENTITY.email.trim().toLowerCase()
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
/** Small presence avatar circle with hover card showing name + email */
|
|
76
84
|
function PresenceAvatarTip({
|
|
77
85
|
user,
|
|
@@ -143,6 +151,7 @@ function SortableSlideThumb({
|
|
|
143
151
|
designSystem,
|
|
144
152
|
onOverflowChange,
|
|
145
153
|
readOnly = false,
|
|
154
|
+
aiEditing = false,
|
|
146
155
|
}: {
|
|
147
156
|
slide: Slide;
|
|
148
157
|
index: number;
|
|
@@ -154,6 +163,7 @@ function SortableSlideThumb({
|
|
|
154
163
|
aspectRatio?: AspectRatio;
|
|
155
164
|
designSystem?: DesignSystemData;
|
|
156
165
|
onOverflowChange: (info: SlideOverflowInfo) => void;
|
|
166
|
+
aiEditing?: boolean;
|
|
157
167
|
}) {
|
|
158
168
|
const t = useT();
|
|
159
169
|
const {
|
|
@@ -175,6 +185,11 @@ function SortableSlideThumb({
|
|
|
175
185
|
};
|
|
176
186
|
|
|
177
187
|
const thumbDims = getAspectRatioDims(aspectRatio);
|
|
188
|
+
const agentPresent = presenceUsers.some(isAgentPresenceUser);
|
|
189
|
+
const humanPresenceUsers = presenceUsers.filter(
|
|
190
|
+
(user) => !isAgentPresenceUser(user),
|
|
191
|
+
);
|
|
192
|
+
const showAiMarker = aiEditing || agentPresent;
|
|
178
193
|
|
|
179
194
|
return (
|
|
180
195
|
<div ref={setNodeRef} style={style}>
|
|
@@ -189,7 +204,7 @@ function SortableSlideThumb({
|
|
|
189
204
|
aria-current={isActive ? "true" : undefined}
|
|
190
205
|
data-slide-thumbnail-id={slide.id}
|
|
191
206
|
className={`w-full text-left flex items-start gap-1.5 p-1.5 rounded-lg transition-[background-color,box-shadow] duration-150 ${
|
|
192
|
-
isActive ? "bg-accent
|
|
207
|
+
isActive ? "bg-accent" : ""
|
|
193
208
|
} ${
|
|
194
209
|
readOnly ? "" : "cursor-grab active:cursor-grabbing"
|
|
195
210
|
} focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1`}
|
|
@@ -199,14 +214,17 @@ function SortableSlideThumb({
|
|
|
199
214
|
<span className="block text-center text-[10px] font-medium leading-5 text-muted-foreground/70">
|
|
200
215
|
{index + 1}
|
|
201
216
|
</span>
|
|
202
|
-
{
|
|
217
|
+
{(showAiMarker || humanPresenceUsers.length > 0) && (
|
|
203
218
|
<div className="absolute left-1/2 top-5 z-10 flex -translate-x-1/2 flex-col items-center gap-1">
|
|
204
|
-
{
|
|
219
|
+
{showAiMarker && (
|
|
220
|
+
<AiEditingMarker className="size-4 text-[8px]" />
|
|
221
|
+
)}
|
|
222
|
+
{humanPresenceUsers.slice(0, 4).map((u, i) => (
|
|
205
223
|
<PresenceAvatarTip key={i} user={u} size={14} />
|
|
206
224
|
))}
|
|
207
|
-
{
|
|
225
|
+
{humanPresenceUsers.length > 4 && (
|
|
208
226
|
<span className="flex h-4 min-w-4 items-center justify-center rounded-full bg-muted px-1 text-[8px] font-medium leading-none text-muted-foreground ring-1 ring-black/40">
|
|
209
|
-
+{
|
|
227
|
+
+{humanPresenceUsers.length - 4}
|
|
210
228
|
</span>
|
|
211
229
|
)}
|
|
212
230
|
</div>
|
|
@@ -224,8 +242,8 @@ function SortableSlideThumb({
|
|
|
224
242
|
className="w-full overflow-hidden rounded border"
|
|
225
243
|
style={{
|
|
226
244
|
borderColor:
|
|
227
|
-
|
|
228
|
-
?
|
|
245
|
+
humanPresenceUsers.length > 0
|
|
246
|
+
? humanPresenceUsers[0].color + "66"
|
|
229
247
|
: "rgba(255,255,255,0.06)",
|
|
230
248
|
aspectRatio: `${thumbDims.width} / ${thumbDims.height}`,
|
|
231
249
|
contentVisibility: "auto",
|
|
@@ -248,14 +266,12 @@ function GeneratingSlideSkeleton({
|
|
|
248
266
|
index,
|
|
249
267
|
aspectRatio,
|
|
250
268
|
designSystem,
|
|
251
|
-
content,
|
|
252
269
|
selected,
|
|
253
270
|
onSelect,
|
|
254
271
|
}: {
|
|
255
272
|
index: number;
|
|
256
273
|
aspectRatio?: AspectRatio;
|
|
257
274
|
designSystem?: DesignSystemData;
|
|
258
|
-
content?: string | null;
|
|
259
275
|
selected?: boolean;
|
|
260
276
|
onSelect?: () => void;
|
|
261
277
|
}) {
|
|
@@ -263,20 +279,20 @@ function GeneratingSlideSkeleton({
|
|
|
263
279
|
return (
|
|
264
280
|
<button
|
|
265
281
|
type="button"
|
|
266
|
-
className={`relative block w-full rounded-lg text-left transition-
|
|
267
|
-
selected ? "
|
|
282
|
+
className={`relative block w-full rounded-lg text-left transition-colors ${
|
|
283
|
+
selected ? "bg-accent" : ""
|
|
268
284
|
}`}
|
|
269
285
|
aria-label={t("editorSidebar.generatingSlide")}
|
|
270
286
|
aria-current={selected ? "true" : undefined}
|
|
271
287
|
onClick={onSelect}
|
|
272
288
|
>
|
|
273
289
|
<div className="flex w-full items-start gap-1.5 rounded-lg p-1.5">
|
|
274
|
-
<span className="flex-shrink-0
|
|
290
|
+
<span className="flex flex-shrink-0 flex-col items-center gap-1 text-center text-[10px] font-medium leading-5 text-muted-foreground/70">
|
|
275
291
|
{index + 1}
|
|
292
|
+
<AiEditingMarker className="size-4 text-[8px]" />
|
|
276
293
|
</span>
|
|
277
294
|
<div className="flex-1 min-w-0">
|
|
278
295
|
<GeneratingSlidePreview
|
|
279
|
-
content={content}
|
|
280
296
|
aspectRatio={aspectRatio}
|
|
281
297
|
designSystem={designSystem}
|
|
282
298
|
thumbnail
|
|
@@ -302,7 +318,6 @@ export default function EditorSidebar({
|
|
|
302
318
|
onSelectGeneratingSlide,
|
|
303
319
|
}: EditorSidebarProps) {
|
|
304
320
|
const slideButtonRefs = useRef(new Map<string, HTMLButtonElement>());
|
|
305
|
-
const thumbScrollRef = useRef<HTMLDivElement>(null);
|
|
306
321
|
const measurementsRef = useRef(
|
|
307
322
|
new Map<
|
|
308
323
|
string,
|
|
@@ -310,6 +325,12 @@ export default function EditorSidebar({
|
|
|
310
325
|
>(),
|
|
311
326
|
);
|
|
312
327
|
const writeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
328
|
+
const aiEditedSlideIds = new Set(
|
|
329
|
+
(recentEdits ?? [])
|
|
330
|
+
.filter((edit) => edit.isAgent)
|
|
331
|
+
.map(slideIdFromEdit)
|
|
332
|
+
.filter((slideId): slideId is string => Boolean(slideId)),
|
|
333
|
+
);
|
|
313
334
|
|
|
314
335
|
const writeDeckFitState = useCallback(() => {
|
|
315
336
|
const currentSlideIds = new Set(slides.map((slide) => slide.id));
|
|
@@ -383,19 +404,6 @@ export default function EditorSidebar({
|
|
|
383
404
|
};
|
|
384
405
|
}, []);
|
|
385
406
|
|
|
386
|
-
// Resolve a recent-edit descriptor (`slides.<id>`) to the on-screen rect of
|
|
387
|
-
// that slide's thumbnail button, relative to the scroll container, so the
|
|
388
|
-
// shared RecentEditHighlights overlay can draw a fading "AI edited" ring.
|
|
389
|
-
const resolveThumbRect = useCallback(
|
|
390
|
-
(edit: AttributedRecentEdit): DOMRect | null => {
|
|
391
|
-
const slideId = slideIdFromEdit(edit);
|
|
392
|
-
if (!slideId) return null;
|
|
393
|
-
const node = slideButtonRefs.current.get(slideId);
|
|
394
|
-
if (!node) return null;
|
|
395
|
-
return node.getBoundingClientRect();
|
|
396
|
-
},
|
|
397
|
-
[],
|
|
398
|
-
);
|
|
399
407
|
const registerSlideButton = useCallback(
|
|
400
408
|
(slideId: string, node: HTMLButtonElement | null) => {
|
|
401
409
|
if (node) {
|
|
@@ -446,10 +454,7 @@ export default function EditorSidebar({
|
|
|
446
454
|
|
|
447
455
|
return (
|
|
448
456
|
<div className="flex h-full min-h-0 w-48 flex-shrink-0 flex-col bg-background sm:w-52">
|
|
449
|
-
<div
|
|
450
|
-
ref={thumbScrollRef}
|
|
451
|
-
className="relative min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain p-2"
|
|
452
|
-
>
|
|
457
|
+
<div className="relative min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain p-2">
|
|
453
458
|
<SortableContext
|
|
454
459
|
items={slides.map((s) => s.id)}
|
|
455
460
|
strategy={verticalListSortingStrategy}
|
|
@@ -466,6 +471,7 @@ export default function EditorSidebar({
|
|
|
466
471
|
presenceUsers={slidePresence?.get(slide.id) ?? []}
|
|
467
472
|
aspectRatio={aspectRatio}
|
|
468
473
|
designSystem={designSystem}
|
|
474
|
+
aiEditing={aiEditedSlideIds.has(slide.id)}
|
|
469
475
|
onOverflowChange={(info) =>
|
|
470
476
|
handleSlideOverflowChange(slide, info)
|
|
471
477
|
}
|
|
@@ -477,21 +483,10 @@ export default function EditorSidebar({
|
|
|
477
483
|
index={generatingSlide.index}
|
|
478
484
|
aspectRatio={aspectRatio}
|
|
479
485
|
designSystem={designSystem}
|
|
480
|
-
content={generatingSlide.content}
|
|
481
486
|
selected={generatingSlideSelected}
|
|
482
487
|
onSelect={onSelectGeneratingSlide}
|
|
483
488
|
/>
|
|
484
489
|
)}
|
|
485
|
-
{/* Fading "AI edited" highlights over the thumbnails of just-edited
|
|
486
|
-
slides (the component handles the fade + name/color tag). */}
|
|
487
|
-
{recentEdits && recentEdits.length > 0 && (
|
|
488
|
-
<RecentEditHighlights
|
|
489
|
-
edits={recentEdits}
|
|
490
|
-
resolveRect={resolveThumbRect}
|
|
491
|
-
containerRef={thumbScrollRef}
|
|
492
|
-
outlineOnly
|
|
493
|
-
/>
|
|
494
|
-
)}
|
|
495
490
|
</div>
|
|
496
491
|
</div>
|
|
497
492
|
);
|