@agent-native/core 0.157.2 → 0.157.4
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/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +40 -1
- package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +3 -1
- package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/clips/.agents/skills/meetings/SKILL.md +7 -7
- package/corpus/templates/clips/actions/approve-recording-access-request.ts +147 -0
- package/corpus/templates/clips/actions/request-recording-access.ts +121 -23
- package/corpus/templates/clips/actions/view-screen.ts +1 -1
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +34 -53
- package/corpus/templates/clips/app/components/player/request-access-dialog.tsx +104 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +36 -45
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +3 -6
- package/corpus/templates/clips/app/i18n/en-US.ts +26 -1
- package/corpus/templates/clips/app/lib/public-meeting.ts +1 -1
- package/corpus/templates/clips/app/routes/access-request.approve.tsx +250 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -32
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +123 -37
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +41 -1
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +1 -0
- package/corpus/templates/clips/server/lib/emails.ts +40 -11
- package/corpus/templates/clips/shared/recording-link.ts +32 -1
- package/corpus/templates/clips/shared/share-attribution.ts +13 -0
- package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/content/actions/add-database-item.ts +9 -0
- package/corpus/templates/content/actions/describe-content-database.ts +92 -0
- package/corpus/templates/content/actions/list-content-databases.ts +213 -72
- package/corpus/templates/content/actions/set-document-property.ts +9 -0
- package/corpus/templates/content/actions/submit-content-database-form.ts +9 -0
- package/corpus/templates/content/actions/update-document.ts +9 -0
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +22 -10
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +2 -1
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +245 -9
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +192 -134
- package/corpus/templates/content/app/components/editor/database/settings.tsx +7 -0
- package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +61 -45
- package/corpus/templates/content/app/components/editor/extensions/ImageNode.ts +6 -0
- package/corpus/templates/content/app/components/editor/image-upload.ts +137 -5
- package/corpus/templates/content/app/global.css +8 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +91 -11
- package/corpus/templates/content/changelog/2026-08-12-image-uploads-now-confirm-the-image-can-be-displayed-before-.md +6 -0
- package/corpus/templates/content/changelog/2026-08-14-images-fill-the-page-width-by-default-and-remain-visible-whe.md +6 -0
- package/corpus/templates/content/changelog/2026-08-14-images-inserted-from-the-slash-command-stay-saved-after-uplo.md +6 -0
- package/corpus/templates/content/changelog/2026-08-14-svg-files-can-be-selected-reliably-from-the-image-picker.md +6 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +7 -1
- package/corpus/templates/content/server/plugins/agent-chat.ts +3 -0
- package/corpus/templates/content/shared/api.ts +9 -0
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +6 -1
- package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +25 -30
- package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +21 -0
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +2 -1
- package/corpus/templates/slides/app/components/editor/ShareDialog.tsx +124 -180
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +1 -0
- package/corpus/templates/slides/app/global.css +99 -3
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +21 -0
- package/dist/chat-threads/schema.d.ts +1 -1
- package/dist/cli/code-agent-executor.js +318 -0
- package/dist/client/AgentPanel.d.ts +13 -1
- package/dist/client/AgentPanel.js +41 -3
- package/dist/client/AssistantChat.d.ts +9 -1
- package/dist/client/AssistantChat.js +26 -13
- package/dist/client/MultiTabAssistantChat.js +18 -5
- package/dist/client/agent-chat.d.ts +8 -0
- package/dist/client/agent-chat.js +15 -0
- package/dist/client/app-chat-sidebar.d.ts +23 -0
- package/dist/client/app-chat-sidebar.js +53 -0
- package/dist/client/app-providers.js +31 -2
- package/dist/client/frame-protocol.d.ts +20 -2
- package/dist/client/hooks/index.d.ts +1 -0
- package/dist/client/hooks/index.js +1 -0
- package/dist/client/sharing/AgentShareSection.d.ts +8 -0
- package/dist/client/sharing/AgentShareSection.js +50 -0
- package/dist/client/sharing/ShareButton.js +2 -1
- package/dist/client/sharing/ShareDialog.d.ts +2 -2
- package/dist/client/sharing/ShareDialog.js +21 -29
- package/dist/client/sharing/index.d.ts +1 -0
- package/dist/client/sharing/index.js +1 -0
- package/dist/client/sharing/useShareButtonController.d.ts +1 -0
- package/dist/client/sharing/useShareDialogController.d.ts +3 -0
- package/dist/client/sharing/useShareDialogController.js +2 -1
- package/dist/client/theme.d.ts +21 -0
- package/dist/client/theme.js +60 -0
- package/dist/client/ui/index.d.ts +1 -1
- package/dist/client/ui/index.js +1 -1
- package/dist/client/use-session.js +23 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/dashboard-storage/schema.d.ts +1 -1
- package/dist/data-programs/schema.d.ts +1 -1
- package/dist/extensions/schema.d.ts +1 -1
- package/dist/localization/core-messages/ar-SA.js +6 -0
- package/dist/localization/core-messages/de-DE.js +6 -0
- package/dist/localization/core-messages/en-US.d.ts +6 -0
- package/dist/localization/core-messages/en-US.js +6 -0
- package/dist/localization/core-messages/es-ES.js +6 -0
- package/dist/localization/core-messages/fr-FR.js +6 -0
- package/dist/localization/core-messages/hi-IN.js +6 -0
- package/dist/localization/core-messages/ja-JP.js +6 -0
- package/dist/localization/core-messages/ko-KR.js +6 -0
- package/dist/localization/core-messages/pt-BR.js +6 -0
- package/dist/localization/core-messages/zh-CN.js +6 -0
- package/dist/localization/core-messages/zh-TW.js +6 -0
- package/dist/localization/core-messages.d.ts +6 -0
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +1 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +14 -14
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +8 -5
- package/dist/server/identity-sso-store.d.ts +6 -1
- package/dist/server/identity-sso-store.js +57 -14
- package/dist/server/identity-sso.d.ts +7 -8
- package/dist/server/identity-sso.js +15 -12
- package/dist/sharing/actions/list-resource-shares.d.ts +2 -0
- package/dist/sharing/actions/list-resource-shares.js +1 -0
- package/dist/sharing/schema.d.ts +1 -1
- package/dist/sharing/schema.js +4 -1
- package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +21 -0
- package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/default/.agents/skills/sharing/SKILL.md +21 -0
- package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +21 -0
- package/docs/content/cross-app-sso.mdx +24 -17
- package/package.json +3 -3
- package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +21 -0
- package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/default/.agents/skills/sharing/SKILL.md +21 -0
- package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +21 -0
|
@@ -28,6 +28,15 @@ import {
|
|
|
28
28
|
|
|
29
29
|
export default defineAction({
|
|
30
30
|
description: "Set a Notion-style property value on a document.",
|
|
31
|
+
publicAgent: {
|
|
32
|
+
expose: true,
|
|
33
|
+
readOnly: false,
|
|
34
|
+
requiresAuth: true,
|
|
35
|
+
isConsequential: true,
|
|
36
|
+
title: "Set Content Document Property",
|
|
37
|
+
description:
|
|
38
|
+
"Delegate one property update on an existing Content database document.",
|
|
39
|
+
},
|
|
31
40
|
schema: z.object({
|
|
32
41
|
documentId: z.string().describe("Document ID (required)"),
|
|
33
42
|
databaseId: z
|
|
@@ -195,6 +195,15 @@ function resolveSubmittedProperties(
|
|
|
195
195
|
export default defineAction({
|
|
196
196
|
description:
|
|
197
197
|
"Submit one row through a Content database form. Validates that form's required questions, resolves option labels safely, writes the title, Blocks, and property values atomically, verifies the saved row, and returns its exact page link.",
|
|
198
|
+
publicAgent: {
|
|
199
|
+
expose: true,
|
|
200
|
+
readOnly: false,
|
|
201
|
+
requiresAuth: true,
|
|
202
|
+
isConsequential: true,
|
|
203
|
+
title: "Submit Content Database Form",
|
|
204
|
+
description:
|
|
205
|
+
"Delegate a validated, atomic submission to an existing Content database form.",
|
|
206
|
+
},
|
|
198
207
|
schema: submitContentDatabaseFormSchema,
|
|
199
208
|
mcpApp: {
|
|
200
209
|
compactCatalog: true,
|
|
@@ -290,6 +290,15 @@ export function isStaleBuilderImageSourceComponentSave(args: {
|
|
|
290
290
|
export default defineAction({
|
|
291
291
|
description:
|
|
292
292
|
"Update an existing document's title, content, icon, or favorite status.",
|
|
293
|
+
publicAgent: {
|
|
294
|
+
expose: true,
|
|
295
|
+
readOnly: false,
|
|
296
|
+
requiresAuth: true,
|
|
297
|
+
isConsequential: true,
|
|
298
|
+
title: "Update Content Document",
|
|
299
|
+
description:
|
|
300
|
+
"Delegate a sparse update to an existing Content document while preserving omitted fields.",
|
|
301
|
+
},
|
|
293
302
|
schema: z.object({
|
|
294
303
|
id: z.string().optional().describe("Document ID (required)"),
|
|
295
304
|
title: z.string().optional().describe("New title"),
|
|
@@ -696,6 +696,7 @@ export function useBlockFieldEditor({
|
|
|
696
696
|
content: string;
|
|
697
697
|
editorResetVersion: number;
|
|
698
698
|
onChange: (markdown: string) => void;
|
|
699
|
+
onSaveContent: (markdown: string) => Promise<boolean>;
|
|
699
700
|
} {
|
|
700
701
|
const key = `${documentId}:${propertyId}`;
|
|
701
702
|
|
|
@@ -876,7 +877,16 @@ export function useBlockFieldEditor({
|
|
|
876
877
|
controllerRef.current?.change(markdown);
|
|
877
878
|
}
|
|
878
879
|
|
|
879
|
-
|
|
880
|
+
async function onSaveContent(markdown: string) {
|
|
881
|
+
setContent(markdown);
|
|
882
|
+
const controller = controllerRef.current;
|
|
883
|
+
if (!controller) return false;
|
|
884
|
+
controller.change(markdown);
|
|
885
|
+
await controller.flush();
|
|
886
|
+
return controller.lastSaved === markdown;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
return { content, editorResetVersion, onChange, onSaveContent };
|
|
880
890
|
}
|
|
881
891
|
|
|
882
892
|
/**
|
|
@@ -904,15 +914,16 @@ function AdditionalBlockEditor({
|
|
|
904
914
|
const propertyId = property.definition.id;
|
|
905
915
|
const initialContent =
|
|
906
916
|
typeof property.value === "string" ? property.value : "";
|
|
907
|
-
const { content, editorResetVersion, onChange } =
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
917
|
+
const { content, editorResetVersion, onChange, onSaveContent } =
|
|
918
|
+
useBlockFieldEditor({
|
|
919
|
+
documentId,
|
|
920
|
+
propertyId,
|
|
921
|
+
initialContent,
|
|
922
|
+
initialRevision: property.blocksField?.revision ?? 0,
|
|
923
|
+
save: setProperty.mutateAsync,
|
|
924
|
+
onRevisionConflict: () =>
|
|
925
|
+
toast.error(t("editor.blocksFieldRevisionConflict")),
|
|
926
|
+
});
|
|
916
927
|
|
|
917
928
|
return (
|
|
918
929
|
<VisualEditor
|
|
@@ -920,6 +931,7 @@ function AdditionalBlockEditor({
|
|
|
920
931
|
documentId={documentId}
|
|
921
932
|
content={content}
|
|
922
933
|
onChange={onChange}
|
|
934
|
+
onSaveContent={onSaveContent}
|
|
923
935
|
editable={canEdit}
|
|
924
936
|
localFileMode
|
|
925
937
|
/>
|
|
@@ -49,6 +49,7 @@ import { cn } from "@/lib/utils";
|
|
|
49
49
|
import { localContentComponents } from "@/local-components";
|
|
50
50
|
|
|
51
51
|
import { focusMostRecentEmptyToggleSummary } from "./extensions/NotionExtensions";
|
|
52
|
+
import { createImagePickerId } from "./image-upload";
|
|
52
53
|
import { buildLocalComponentSlashItems } from "./localComponentSlashItems";
|
|
53
54
|
import { MathRenderer } from "./MathRenderer";
|
|
54
55
|
import { buildRegistrySlashItems } from "./registrySlashItems";
|
|
@@ -179,7 +180,7 @@ export function insertMediaPlaceholder(
|
|
|
179
180
|
) {
|
|
180
181
|
const attrs =
|
|
181
182
|
type === "image"
|
|
182
|
-
? { src: null, alt: "" }
|
|
183
|
+
? { src: null, alt: "", uploadId: createImagePickerId() }
|
|
183
184
|
: type === "video"
|
|
184
185
|
? { src: null, sourcePanelOpen: true }
|
|
185
186
|
: { src: null };
|
|
@@ -95,9 +95,13 @@ import {
|
|
|
95
95
|
hasImageFiles,
|
|
96
96
|
hasVideoFiles,
|
|
97
97
|
audioUploadErrorMessage,
|
|
98
|
+
completeImageFileUpload,
|
|
99
|
+
createMediaUploadId,
|
|
100
|
+
ImageRenderError,
|
|
98
101
|
imageUploadErrorMessage,
|
|
99
102
|
uploadAudioFile,
|
|
100
103
|
uploadImageFile,
|
|
104
|
+
waitForRenderedImage,
|
|
101
105
|
uploadVideoFile,
|
|
102
106
|
videoUploadErrorMessage,
|
|
103
107
|
} from "./image-upload";
|
|
@@ -1020,6 +1024,7 @@ interface VisualEditorExtensionOptions {
|
|
|
1020
1024
|
avatarUrl?: string;
|
|
1021
1025
|
} | null;
|
|
1022
1026
|
onImageComment?: (quotedText: string, offsetTop: number) => void;
|
|
1027
|
+
onImageFilePickerRequest?: (request: PendingImagePicker) => void;
|
|
1023
1028
|
onJoinTitle?: (text: string) => void;
|
|
1024
1029
|
resolveNotionPageLink?: (notionPageId: string) => NotionPageLink | null;
|
|
1025
1030
|
onOpenNotionPageLink?: (documentId: string) => void;
|
|
@@ -1143,19 +1148,17 @@ function mediaNodeLabel(typeName: MediaNodeType) {
|
|
|
1143
1148
|
return "Audio";
|
|
1144
1149
|
}
|
|
1145
1150
|
|
|
1146
|
-
function createMediaUploadId(kind: MediaNodeType) {
|
|
1147
|
-
const random =
|
|
1148
|
-
typeof crypto !== "undefined" && "randomUUID" in crypto
|
|
1149
|
-
? crypto.randomUUID()
|
|
1150
|
-
: Math.random().toString(36).slice(2);
|
|
1151
|
-
return `${kind}-upload-${random}`;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
1151
|
interface PendingMediaUpload {
|
|
1155
1152
|
file: File;
|
|
1156
1153
|
uploadId: string;
|
|
1157
1154
|
}
|
|
1158
1155
|
|
|
1156
|
+
export interface PendingImagePicker {
|
|
1157
|
+
pickerId: string;
|
|
1158
|
+
position: number;
|
|
1159
|
+
attrs: Record<string, unknown>;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1159
1162
|
function insertPendingMediaNodes(
|
|
1160
1163
|
view: EditorView,
|
|
1161
1164
|
typeName: MediaNodeType,
|
|
@@ -1204,7 +1207,7 @@ function updatePendingMediaNode(
|
|
|
1204
1207
|
tr = tr.setNodeMarkup(pos, undefined, {
|
|
1205
1208
|
...node.attrs,
|
|
1206
1209
|
...attrs,
|
|
1207
|
-
uploadId: null,
|
|
1210
|
+
uploadId: attrs.uploadId ?? null,
|
|
1208
1211
|
});
|
|
1209
1212
|
found = true;
|
|
1210
1213
|
return false;
|
|
@@ -1218,6 +1221,130 @@ function updatePendingMediaNode(
|
|
|
1218
1221
|
return found;
|
|
1219
1222
|
}
|
|
1220
1223
|
|
|
1224
|
+
function replacePendingMediaUploadId(
|
|
1225
|
+
view: EditorView,
|
|
1226
|
+
typeName: MediaNodeType,
|
|
1227
|
+
currentUploadId: string,
|
|
1228
|
+
nextUploadId: string,
|
|
1229
|
+
) {
|
|
1230
|
+
let found = false;
|
|
1231
|
+
let tr = view.state.tr;
|
|
1232
|
+
|
|
1233
|
+
view.state.doc.descendants((node, pos) => {
|
|
1234
|
+
if (found) return false;
|
|
1235
|
+
if (
|
|
1236
|
+
node.type.name === typeName &&
|
|
1237
|
+
node.attrs.uploadId === currentUploadId
|
|
1238
|
+
) {
|
|
1239
|
+
tr = tr.setNodeMarkup(pos, undefined, {
|
|
1240
|
+
...node.attrs,
|
|
1241
|
+
uploadId: nextUploadId,
|
|
1242
|
+
});
|
|
1243
|
+
found = true;
|
|
1244
|
+
return false;
|
|
1245
|
+
}
|
|
1246
|
+
return true;
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
if (found) view.dispatch(tr);
|
|
1250
|
+
return found;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
function insertImageNodeAtPendingPosition(
|
|
1254
|
+
view: EditorView,
|
|
1255
|
+
request: PendingImagePicker,
|
|
1256
|
+
attrs: Record<string, unknown>,
|
|
1257
|
+
restoreSelection: boolean,
|
|
1258
|
+
) {
|
|
1259
|
+
const imageType = view.state.schema.nodes.image;
|
|
1260
|
+
if (!imageType) return false;
|
|
1261
|
+
const position = Math.min(
|
|
1262
|
+
Math.max(request.position, 0),
|
|
1263
|
+
view.state.doc.content.size,
|
|
1264
|
+
);
|
|
1265
|
+
try {
|
|
1266
|
+
let tr = view.state.tr.insert(
|
|
1267
|
+
position,
|
|
1268
|
+
imageType.create({ ...request.attrs, ...attrs }),
|
|
1269
|
+
);
|
|
1270
|
+
if (restoreSelection) {
|
|
1271
|
+
tr = tr.setSelection(NodeSelection.create(tr.doc, position));
|
|
1272
|
+
}
|
|
1273
|
+
view.dispatch(tr.scrollIntoView());
|
|
1274
|
+
if (restoreSelection) view.focus();
|
|
1275
|
+
return true;
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
console.error("Could not restore the pending image node:", error);
|
|
1278
|
+
return false;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
export function ensurePendingImageUpload(
|
|
1283
|
+
view: EditorView,
|
|
1284
|
+
request: PendingImagePicker,
|
|
1285
|
+
uploadId: string,
|
|
1286
|
+
) {
|
|
1287
|
+
if (replacePendingMediaUploadId(view, "image", request.pickerId, uploadId)) {
|
|
1288
|
+
return true;
|
|
1289
|
+
}
|
|
1290
|
+
return insertImageNodeAtPendingPosition(view, request, { uploadId }, false);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
export function commitPendingImageUpload(
|
|
1294
|
+
view: EditorView,
|
|
1295
|
+
request: PendingImagePicker,
|
|
1296
|
+
uploadId: string,
|
|
1297
|
+
attrs: Record<string, unknown>,
|
|
1298
|
+
) {
|
|
1299
|
+
if (updatePendingMediaNode(view, "image", uploadId, attrs)) return true;
|
|
1300
|
+
return insertImageNodeAtPendingPosition(view, request, attrs, false);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
function findPendingImageElement(view: EditorView, uploadId: string) {
|
|
1304
|
+
return (
|
|
1305
|
+
Array.from(view.dom.querySelectorAll<HTMLElement>("[data-image-upload-id]"))
|
|
1306
|
+
.find((element) => element.dataset.imageUploadId === uploadId)
|
|
1307
|
+
?.querySelector<HTMLImageElement>("img") ?? null
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
export function restorePendingImagePicker(
|
|
1312
|
+
view: EditorView,
|
|
1313
|
+
request: PendingImagePicker,
|
|
1314
|
+
currentUploadId = request.pickerId,
|
|
1315
|
+
) {
|
|
1316
|
+
let found = false;
|
|
1317
|
+
let nodePosition: number | null = null;
|
|
1318
|
+
let tr = view.state.tr;
|
|
1319
|
+
|
|
1320
|
+
view.state.doc.descendants((node, pos) => {
|
|
1321
|
+
if (found) return false;
|
|
1322
|
+
if (node.type.name === "image" && node.attrs.uploadId === currentUploadId) {
|
|
1323
|
+
tr = tr.setNodeMarkup(pos, undefined, {
|
|
1324
|
+
...request.attrs,
|
|
1325
|
+
uploadId: null,
|
|
1326
|
+
});
|
|
1327
|
+
nodePosition = pos;
|
|
1328
|
+
found = true;
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
return true;
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
if (found && nodePosition !== null) {
|
|
1335
|
+
tr = tr.setSelection(NodeSelection.create(tr.doc, nodePosition));
|
|
1336
|
+
view.dispatch(tr);
|
|
1337
|
+
view.focus();
|
|
1338
|
+
return true;
|
|
1339
|
+
}
|
|
1340
|
+
return insertImageNodeAtPendingPosition(
|
|
1341
|
+
view,
|
|
1342
|
+
request,
|
|
1343
|
+
{ uploadId: null },
|
|
1344
|
+
true,
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1221
1348
|
function getVisualEditorPlaceholder({
|
|
1222
1349
|
editor,
|
|
1223
1350
|
node,
|
|
@@ -1499,6 +1626,7 @@ export function createVisualEditorExtensions({
|
|
|
1499
1626
|
localAwareness,
|
|
1500
1627
|
user,
|
|
1501
1628
|
onImageComment,
|
|
1629
|
+
onImageFilePickerRequest,
|
|
1502
1630
|
onJoinTitle,
|
|
1503
1631
|
resolveNotionPageLink,
|
|
1504
1632
|
onOpenNotionPageLink,
|
|
@@ -1558,6 +1686,7 @@ export function createVisualEditorExtensions({
|
|
|
1558
1686
|
HTMLAttributes: { class: "notion-image" },
|
|
1559
1687
|
documentId,
|
|
1560
1688
|
onImageComment,
|
|
1689
|
+
onImageFilePickerRequest,
|
|
1561
1690
|
}),
|
|
1562
1691
|
VideoNode.configure({
|
|
1563
1692
|
HTMLAttributes: { class: "notion-video" },
|
|
@@ -1880,6 +2009,8 @@ export function VisualEditor({
|
|
|
1880
2009
|
const t = useT();
|
|
1881
2010
|
const [isDraggingMedia, setIsDraggingMedia] = useState(false);
|
|
1882
2011
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
2012
|
+
const imageFileInputRef = useRef<HTMLInputElement>(null);
|
|
2013
|
+
const pendingImagePickerRef = useRef<PendingImagePicker | null>(null);
|
|
1883
2014
|
const onChangeRef = useRef(onChange);
|
|
1884
2015
|
onChangeRef.current = onChange;
|
|
1885
2016
|
const onSaveContentRef = useRef(onSaveContent);
|
|
@@ -1895,6 +2026,14 @@ export function VisualEditor({
|
|
|
1895
2026
|
},
|
|
1896
2027
|
[],
|
|
1897
2028
|
);
|
|
2029
|
+
const onImageFilePickerRequest = useCallback(
|
|
2030
|
+
(request: PendingImagePicker) => {
|
|
2031
|
+
if (pendingImagePickerRef.current) return;
|
|
2032
|
+
pendingImagePickerRef.current = request;
|
|
2033
|
+
imageFileInputRef.current?.click();
|
|
2034
|
+
},
|
|
2035
|
+
[],
|
|
2036
|
+
);
|
|
1898
2037
|
const resolveNotionPageLink = useCallback((notionPageId: string) => {
|
|
1899
2038
|
const normalized = notionPageId.replace(/-/g, "").toLowerCase();
|
|
1900
2039
|
return (
|
|
@@ -1946,6 +2085,7 @@ export function VisualEditor({
|
|
|
1946
2085
|
localAwareness,
|
|
1947
2086
|
user,
|
|
1948
2087
|
onImageComment: onComment,
|
|
2088
|
+
onImageFilePickerRequest,
|
|
1949
2089
|
onJoinTitle,
|
|
1950
2090
|
resolveNotionPageLink,
|
|
1951
2091
|
onOpenNotionPageLink,
|
|
@@ -1962,6 +2102,7 @@ export function VisualEditor({
|
|
|
1962
2102
|
user?.email,
|
|
1963
2103
|
user?.color,
|
|
1964
2104
|
onComment,
|
|
2105
|
+
onImageFilePickerRequest,
|
|
1965
2106
|
onJoinTitle,
|
|
1966
2107
|
resolveNotionPageLink,
|
|
1967
2108
|
onOpenNotionPageLink,
|
|
@@ -2014,6 +2155,7 @@ export function VisualEditor({
|
|
|
2014
2155
|
if (options?.immediate && onSaveContentRef.current) {
|
|
2015
2156
|
return onSaveContentRef.current(normalized);
|
|
2016
2157
|
}
|
|
2158
|
+
if (options?.immediate) return false;
|
|
2017
2159
|
// Don't persist an empty doc before Collaboration has seeded (would
|
|
2018
2160
|
// clobber DB content with an empty string). `registerEmitted` records
|
|
2019
2161
|
// this as the last-emitted value and returns false to skip the save.
|
|
@@ -2225,6 +2367,93 @@ export function VisualEditor({
|
|
|
2225
2367
|
},
|
|
2226
2368
|
});
|
|
2227
2369
|
|
|
2370
|
+
const handleImageFileInputChange = useCallback(
|
|
2371
|
+
async (event: Event) => {
|
|
2372
|
+
const input = event.currentTarget as HTMLInputElement;
|
|
2373
|
+
const file = input.files?.[0];
|
|
2374
|
+
input.value = "";
|
|
2375
|
+
const request = pendingImagePickerRef.current;
|
|
2376
|
+
pendingImagePickerRef.current = null;
|
|
2377
|
+
if (!editor || !file || !request) return;
|
|
2378
|
+
|
|
2379
|
+
const uploadId = createMediaUploadId("image");
|
|
2380
|
+
if (!ensurePendingImageUpload(editor.view, request, uploadId)) return;
|
|
2381
|
+
|
|
2382
|
+
const toastId = toast.loading(t("editor.media.uploadingImage"));
|
|
2383
|
+
let staged = false;
|
|
2384
|
+
let committed = false;
|
|
2385
|
+
try {
|
|
2386
|
+
await completeImageFileUpload({
|
|
2387
|
+
file,
|
|
2388
|
+
stageAttributes: (src) => {
|
|
2389
|
+
if (editor.isDestroyed) return;
|
|
2390
|
+
staged = commitPendingImageUpload(editor.view, request, uploadId, {
|
|
2391
|
+
src,
|
|
2392
|
+
uploadId,
|
|
2393
|
+
});
|
|
2394
|
+
},
|
|
2395
|
+
waitForRender: async () => {
|
|
2396
|
+
if (!staged) throw new Error(t("empty.genericError"));
|
|
2397
|
+
await waitForRenderedImage(() =>
|
|
2398
|
+
findPendingImageElement(editor.view, uploadId),
|
|
2399
|
+
);
|
|
2400
|
+
},
|
|
2401
|
+
commitAttributes: (src) => {
|
|
2402
|
+
if (editor.isDestroyed) return;
|
|
2403
|
+
committed = commitPendingImageUpload(
|
|
2404
|
+
editor.view,
|
|
2405
|
+
request,
|
|
2406
|
+
uploadId,
|
|
2407
|
+
{ src, uploadId: null },
|
|
2408
|
+
);
|
|
2409
|
+
},
|
|
2410
|
+
persistCommittedImage: async () => {
|
|
2411
|
+
if (!committed || editor.isDestroyed) return false;
|
|
2412
|
+
return await persistEditorContent(editor, {
|
|
2413
|
+
immediate: true,
|
|
2414
|
+
userInitiated: true,
|
|
2415
|
+
});
|
|
2416
|
+
},
|
|
2417
|
+
});
|
|
2418
|
+
if (!committed) throw new Error(t("empty.genericError"));
|
|
2419
|
+
toast.success(t("editor.media.imageAdded"), { id: toastId });
|
|
2420
|
+
} catch (error) {
|
|
2421
|
+
if (editor.isDestroyed) {
|
|
2422
|
+
toast.dismiss(toastId);
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
if (!committed) {
|
|
2426
|
+
restorePendingImagePicker(editor.view, request, uploadId);
|
|
2427
|
+
}
|
|
2428
|
+
toast.error(
|
|
2429
|
+
error instanceof ImageRenderError
|
|
2430
|
+
? t("editor.media.imageBroken")
|
|
2431
|
+
: imageUploadErrorMessage(error),
|
|
2432
|
+
{ id: toastId },
|
|
2433
|
+
);
|
|
2434
|
+
}
|
|
2435
|
+
},
|
|
2436
|
+
[editor, t],
|
|
2437
|
+
);
|
|
2438
|
+
|
|
2439
|
+
const handleImageFileInputCancel = useCallback(() => {
|
|
2440
|
+
const request = pendingImagePickerRef.current;
|
|
2441
|
+
pendingImagePickerRef.current = null;
|
|
2442
|
+
if (!editor || !request) return;
|
|
2443
|
+
restorePendingImagePicker(editor.view, request);
|
|
2444
|
+
}, [editor]);
|
|
2445
|
+
|
|
2446
|
+
useEffect(() => {
|
|
2447
|
+
const input = imageFileInputRef.current;
|
|
2448
|
+
if (!input) return;
|
|
2449
|
+
input.addEventListener("change", handleImageFileInputChange);
|
|
2450
|
+
input.addEventListener("cancel", handleImageFileInputCancel);
|
|
2451
|
+
return () => {
|
|
2452
|
+
input.removeEventListener("change", handleImageFileInputChange);
|
|
2453
|
+
input.removeEventListener("cancel", handleImageFileInputCancel);
|
|
2454
|
+
};
|
|
2455
|
+
}, [editor, handleImageFileInputCancel, handleImageFileInputChange]);
|
|
2456
|
+
|
|
2228
2457
|
// The shared seed / reconcile / lead-client / onUpdate-guard logic, with
|
|
2229
2458
|
// Content's NFM serializer injected so the editor reads/writes the exact same
|
|
2230
2459
|
// bytes as before (docToNfm / nfmToDoc / canonicalizeNfm, and the
|
|
@@ -2563,6 +2792,13 @@ export function VisualEditor({
|
|
|
2563
2792
|
<RegistryBlockDataProvider value={registryBlockDataValue}>
|
|
2564
2793
|
<EditorContent editor={editor} />
|
|
2565
2794
|
</RegistryBlockDataProvider>
|
|
2795
|
+
<input
|
|
2796
|
+
ref={imageFileInputRef}
|
|
2797
|
+
type="file"
|
|
2798
|
+
className="hidden"
|
|
2799
|
+
tabIndex={-1}
|
|
2800
|
+
aria-hidden="true"
|
|
2801
|
+
/>
|
|
2566
2802
|
</div>
|
|
2567
2803
|
);
|
|
2568
2804
|
}
|