@agent-native/core 0.128.1 → 0.128.2

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.
Files changed (80) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +6 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/server/email-template.ts +33 -0
  5. package/corpus/templates/chat/_gitignore +2 -0
  6. package/corpus/templates/clips/AGENTS.md +6 -1
  7. package/corpus/templates/clips/actions/complete-transactional-email-summary.ts +56 -0
  8. package/corpus/templates/clips/actions/import-loom-recording.ts +258 -76
  9. package/corpus/templates/clips/actions/lib/direct-video.ts +96 -0
  10. package/corpus/templates/clips/actions/lib/loom-import-job.ts +53 -2
  11. package/corpus/templates/clips/actions/lib/loom-video.ts +2 -62
  12. package/corpus/templates/clips/actions/lib/video-download-limits.ts +62 -0
  13. package/corpus/templates/clips/actions/list-transactional-email-ai-requests.ts +249 -0
  14. package/corpus/templates/clips/app/components/library/library-grid.tsx +65 -0
  15. package/corpus/templates/clips/app/components/library/library-layout.tsx +55 -0
  16. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +28 -126
  17. package/corpus/templates/clips/app/global.css +23 -0
  18. package/corpus/templates/clips/app/hooks/use-transactional-email-bridge.ts +98 -0
  19. package/corpus/templates/clips/app/i18n/en-US.ts +20 -0
  20. package/corpus/templates/clips/app/routes/_app.tsx +2 -0
  21. package/corpus/templates/clips/app/routes/import.tsx +396 -0
  22. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +8 -6
  23. package/corpus/templates/clips/app/routes/record.tsx +13 -71
  24. package/corpus/templates/clips/changelog/2026-07-25-two-clip-emails-now-include-a-concise-summary-generated-secu.md +6 -0
  25. package/corpus/templates/clips/data/clips-transactional-emails/config.json +3 -0
  26. package/corpus/templates/clips/server/jobs/transactional-emails.ts +1034 -0
  27. package/corpus/templates/clips/server/lib/transactional-email-store.ts +865 -0
  28. package/corpus/templates/clips/server/lib/transactional-email-templates.ts +270 -0
  29. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  30. package/corpus/templates/clips/server/routes/api/view-event.post.ts +59 -2
  31. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +8 -2
  32. package/corpus/templates/content/AGENTS.md +8 -0
  33. package/corpus/templates/content/actions/_content-database-personal-view.ts +28 -3
  34. package/corpus/templates/content/actions/_property-utils.ts +32 -2
  35. package/corpus/templates/content/actions/configure-document-property.ts +12 -2
  36. package/corpus/templates/content/actions/delete-document-property.ts +13 -3
  37. package/corpus/templates/content/actions/duplicate-document-property.ts +13 -3
  38. package/corpus/templates/content/actions/list-content-spaces.ts +5 -2
  39. package/corpus/templates/content/actions/list-document-properties.ts +12 -3
  40. package/corpus/templates/content/actions/move-database-item.ts +74 -55
  41. package/corpus/templates/content/actions/reorder-document-property.ts +20 -4
  42. package/corpus/templates/content/actions/set-document-property.ts +10 -1
  43. package/corpus/templates/content/actions/update-content-database-personal-view.ts +7 -0
  44. package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +31 -21
  45. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +30 -3
  46. package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +8 -1
  47. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +72 -20
  48. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +79 -10
  49. package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +8 -1
  50. package/corpus/templates/content/app/components/editor/database/settings.tsx +6 -0
  51. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +246 -26
  52. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +491 -197
  53. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +3 -3
  54. package/corpus/templates/content/app/components/sidebar/sidebar-reorder.tsx +409 -0
  55. package/corpus/templates/content/app/hooks/use-content-database.ts +117 -3
  56. package/corpus/templates/content/app/hooks/use-content-spaces.ts +1 -0
  57. package/corpus/templates/content/app/hooks/use-document-properties.ts +62 -18
  58. package/corpus/templates/content/app/hooks/use-documents.ts +13 -5
  59. package/corpus/templates/content/app/i18n-data.ts +261 -3
  60. package/corpus/templates/content/app/routes/_app.page.$id.tsx +9 -2
  61. package/corpus/templates/content/changelog/2026-07-24-database-page-properties-keep-their-context.md +6 -0
  62. package/corpus/templates/content/changelog/2026-07-26-sidebar-references-can-now-be-personally-reordered.md +6 -0
  63. package/corpus/templates/content/docs/solutions/2026-07-24-database-property-context-switch-diagnosis.md +249 -0
  64. package/corpus/templates/content/docs/solutions/content-sidebar-order-shape.md +389 -0
  65. package/corpus/templates/content/shared/api.ts +28 -0
  66. package/corpus/toolkit/CHANGELOG.md +6 -0
  67. package/corpus/toolkit/package.json +1 -1
  68. package/corpus/toolkit/src/editor/DragHandle.ts +63 -9
  69. package/dist/collab/struct-routes.d.ts +1 -1
  70. package/dist/observability/routes.d.ts +3 -3
  71. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  72. package/dist/server/email-template.d.ts +7 -0
  73. package/dist/server/email-template.d.ts.map +1 -1
  74. package/dist/server/email-template.js +24 -0
  75. package/dist/server/email-template.js.map +1 -1
  76. package/dist/server/transcribe-voice.d.ts +1 -1
  77. package/dist/templates/chat/_gitignore +2 -0
  78. package/package.json +4 -3
  79. package/src/server/email-template.ts +33 -0
  80. package/src/templates/chat/_gitignore +2 -0
@@ -6,6 +6,10 @@ import { z } from "zod";
6
6
 
7
7
  import { getDb, schema } from "../server/db/index.js";
8
8
  import { getContentDatabaseResponse } from "./_database-utils.js";
9
+ import {
10
+ databaseItemsPositionScope,
11
+ withPositionLock,
12
+ } from "./_position-utils.js";
9
13
 
10
14
  function clamp(value: number, min: number, max: number) {
11
15
  return Math.min(Math.max(value, min), max);
@@ -21,13 +25,15 @@ function positionCaseSql(
21
25
  }
22
26
 
23
27
  export default defineAction({
24
- description: "Move a page row to a new position in a content database.",
28
+ description:
29
+ "Move an exact page membership to a new position in a content database without changing the page's parent or access.",
25
30
  schema: z.object({
31
+ databaseId: z.string().optional().describe("Database ID"),
26
32
  itemId: z.string().optional().describe("Database item ID"),
27
33
  documentId: z.string().optional().describe("Database row document ID"),
28
34
  position: z.coerce.number().int().describe("New zero-based row position"),
29
35
  }),
30
- run: async ({ itemId, documentId, position }) => {
36
+ run: async ({ databaseId, itemId, documentId, position }) => {
31
37
  if (!itemId && !documentId) {
32
38
  throw new Error("Either itemId or documentId is required.");
33
39
  }
@@ -48,6 +54,9 @@ export default defineAction({
48
54
  itemId
49
55
  ? eq(schema.contentDatabaseItems.id, itemId)
50
56
  : eq(schema.contentDatabaseItems.documentId, documentId!),
57
+ databaseId
58
+ ? eq(schema.contentDatabaseItems.databaseId, databaseId)
59
+ : undefined,
51
60
  isNull(schema.contentDatabases.deletedAt),
52
61
  ),
53
62
  );
@@ -56,63 +65,73 @@ export default defineAction({
56
65
 
57
66
  await assertAccess("document", row.database.documentId, "editor");
58
67
 
59
- const items = await db
60
- .select()
61
- .from(schema.contentDatabaseItems)
62
- .where(eq(schema.contentDatabaseItems.databaseId, row.item.databaseId))
63
- .orderBy(asc(schema.contentDatabaseItems.position));
64
-
65
- const currentIndex = items.findIndex((item) => item.id === row.item.id);
66
- if (currentIndex < 0) throw new Error("Database row not found.");
68
+ await withPositionLock(
69
+ databaseItemsPositionScope(row.item.databaseId),
70
+ () =>
71
+ db.transaction(async (tx) => {
72
+ const items = await tx
73
+ .select()
74
+ .from(schema.contentDatabaseItems)
75
+ .where(
76
+ eq(schema.contentDatabaseItems.databaseId, row.item.databaseId),
77
+ )
78
+ .orderBy(asc(schema.contentDatabaseItems.position));
79
+ const currentIndex = items.findIndex(
80
+ (item) => item.id === row.item.id,
81
+ );
82
+ if (currentIndex < 0) throw new Error("Database row not found.");
67
83
 
68
- const nextIndex = clamp(position, 0, items.length - 1);
69
- if (nextIndex === currentIndex) {
70
- return getContentDatabaseResponse(row.item.databaseId);
71
- }
84
+ const nextIndex = clamp(position, 0, items.length - 1);
85
+ if (nextIndex === currentIndex) return;
72
86
 
73
- const nextItems = [...items];
74
- const [moved] = nextItems.splice(currentIndex, 1);
75
- nextItems.splice(nextIndex, 0, moved);
76
- const now = new Date().toISOString();
77
- const itemIds = nextItems.map((item) => item.id);
78
- const documentIds = nextItems.map((item) => item.documentId);
87
+ const nextItems = [...items];
88
+ const [moved] = nextItems.splice(currentIndex, 1);
89
+ nextItems.splice(nextIndex, 0, moved);
90
+ const now = new Date().toISOString();
91
+ const itemIds = nextItems.map((item) => item.id);
92
+ const documentIds = nextItems.map((item) => item.documentId);
79
93
 
80
- await db.transaction(async (tx) => {
81
- await tx
82
- .update(schema.contentDatabaseItems)
83
- .set({
84
- position: positionCaseSql(
85
- schema.contentDatabaseItems.id,
86
- schema.contentDatabaseItems.position,
87
- itemIds,
88
- ),
89
- updatedAt: now,
90
- })
91
- .where(
92
- and(
93
- eq(schema.contentDatabaseItems.databaseId, row.item.databaseId),
94
- inArray(schema.contentDatabaseItems.id, itemIds),
95
- ),
96
- );
94
+ await tx
95
+ .update(schema.contentDatabaseItems)
96
+ .set({
97
+ position: positionCaseSql(
98
+ schema.contentDatabaseItems.id,
99
+ schema.contentDatabaseItems.position,
100
+ itemIds,
101
+ ),
102
+ updatedAt: now,
103
+ })
104
+ .where(
105
+ and(
106
+ eq(schema.contentDatabaseItems.databaseId, row.item.databaseId),
107
+ inArray(schema.contentDatabaseItems.id, itemIds),
108
+ ),
109
+ );
97
110
 
98
- await tx
99
- .update(schema.documents)
100
- .set({
101
- position: positionCaseSql(
102
- schema.documents.id,
103
- schema.documents.position,
104
- documentIds,
105
- ),
106
- updatedAt: now,
107
- })
108
- .where(
109
- and(
110
- eq(schema.documents.ownerEmail, row.database.ownerEmail),
111
- eq(schema.documents.parentId, row.database.documentId),
112
- inArray(schema.documents.id, documentIds),
113
- ),
114
- );
115
- });
111
+ if (
112
+ row.database.systemRole !== "favorites" &&
113
+ row.database.systemRole !== "workspaces"
114
+ ) {
115
+ await tx
116
+ .update(schema.documents)
117
+ .set({
118
+ position: positionCaseSql(
119
+ schema.documents.id,
120
+ schema.documents.position,
121
+ documentIds,
122
+ ),
123
+ updatedAt: now,
124
+ })
125
+ .where(
126
+ and(
127
+ eq(schema.documents.ownerEmail, row.database.ownerEmail),
128
+ eq(schema.documents.parentId, row.database.documentId),
129
+ inArray(schema.documents.id, documentIds),
130
+ ),
131
+ );
132
+ }
133
+ }),
134
+ );
116
135
 
117
136
  await writeAppState("refresh-signal", { ts: Date.now() });
118
137
 
@@ -15,6 +15,12 @@ export default defineAction({
15
15
  "Reorder a property definition within its database by moving it before or after another property. Used for reordering Blocks fields on the page.",
16
16
  schema: z.object({
17
17
  documentId: z.string().describe("Document ID used to scope access"),
18
+ databaseId: z
19
+ .string()
20
+ .optional()
21
+ .describe(
22
+ "Database ID that owns the properties; omit only for context-free entry points",
23
+ ),
18
24
  propertyId: z.string().describe("Property definition ID to move"),
19
25
  targetPropertyId: z.string().describe("Property to position relative to"),
20
26
  position: z
@@ -22,11 +28,21 @@ export default defineAction({
22
28
  .default("before")
23
29
  .describe("Place the moved property before or after the target"),
24
30
  }),
25
- run: async ({ documentId, propertyId, targetPropertyId, position }) => {
31
+ run: async ({
32
+ documentId,
33
+ databaseId,
34
+ propertyId,
35
+ targetPropertyId,
36
+ position,
37
+ }) => {
26
38
  const access = await assertAccess("document", documentId, "editor");
27
39
  const document = access.resource;
28
40
  const db = getDb();
29
- const database = await resolvePropertyDatabaseForDocument(document);
41
+ const database = await resolvePropertyDatabaseForDocument(
42
+ document,
43
+ databaseId,
44
+ "editor",
45
+ );
30
46
  if (!database) throw new Error("Document is not part of a database.");
31
47
 
32
48
  const definitions = await db
@@ -55,7 +71,7 @@ export default defineAction({
55
71
  return {
56
72
  documentId,
57
73
  databaseId: database.id,
58
- properties: await listPropertiesForDocument(document),
74
+ properties: await listPropertiesForDocument(document, database.id),
59
75
  };
60
76
  }
61
77
 
@@ -83,7 +99,7 @@ export default defineAction({
83
99
  return {
84
100
  documentId,
85
101
  databaseId: database.id,
86
- properties: await listPropertiesForDocument(document),
102
+ properties: await listPropertiesForDocument(document, database.id),
87
103
  };
88
104
  },
89
105
  });
@@ -26,10 +26,16 @@ export default defineAction({
26
26
  description: "Set a Notion-style property value on a document.",
27
27
  schema: z.object({
28
28
  documentId: z.string().describe("Document ID (required)"),
29
+ databaseId: z
30
+ .string()
31
+ .optional()
32
+ .describe(
33
+ "Database ID that owns the property; omit only for context-free entry points",
34
+ ),
29
35
  propertyId: z.string().describe("Property definition ID"),
30
36
  value: z.unknown().describe("Value for the property type"),
31
37
  }),
32
- run: async ({ documentId, propertyId, value }) => {
38
+ run: async ({ documentId, databaseId, propertyId, value }) => {
33
39
  const db = getDb();
34
40
  const [definition] = await db
35
41
  .select()
@@ -39,6 +45,9 @@ export default defineAction({
39
45
  if (!definition.databaseId) {
40
46
  throw new Error(`Property "${propertyId}" is not attached to a database`);
41
47
  }
48
+ if (databaseId && definition.databaseId !== databaseId) {
49
+ throw new Error(`Property "${propertyId}" not found`);
50
+ }
42
51
  const database = await getDatabaseById(definition.databaseId);
43
52
  if (!database) throw new Error("Document database not found.");
44
53
  await assertAccess("document", database.documentId, "editor");
@@ -4,9 +4,11 @@ import { z } from "zod";
4
4
 
5
5
  import {
6
6
  assertContentDatabaseViewerAccess,
7
+ normalizePersonalDatabaseViewOverrides,
7
8
  personalDatabaseViewSettingKey,
8
9
  personalViewOverridesSchema,
9
10
  } from "./_content-database-personal-view.js";
11
+ import { getContentDatabaseResponse } from "./_database-utils.js";
10
12
 
11
13
  export default defineAction({
12
14
  description:
@@ -21,6 +23,11 @@ export default defineAction({
21
23
 
22
24
  const key = personalDatabaseViewSettingKey(databaseId);
23
25
  if (overrides) {
26
+ const visibleDatabase = await getContentDatabaseResponse(databaseId);
27
+ overrides = normalizePersonalDatabaseViewOverrides(
28
+ overrides,
29
+ new Set(visibleDatabase.items.map((item) => item.id)),
30
+ );
24
31
  await putUserSetting(ctx.userEmail, key, overrides);
25
32
  } else {
26
33
  await deleteUserSetting(ctx.userEmail, key);
@@ -1,5 +1,5 @@
1
1
  import { useT } from "@agent-native/core/client/i18n";
2
- import type { DocumentProperty } from "@shared/api";
2
+ import type { DocumentPropertiesResponse, DocumentProperty } from "@shared/api";
3
3
  import {
4
4
  blocksRenderMode,
5
5
  blocksStorageTarget,
@@ -17,9 +17,12 @@ import {
17
17
  type PointerEvent as ReactPointerEvent,
18
18
  } from "react";
19
19
 
20
- import { useDocumentProperties } from "@/hooks/use-document-properties";
21
- import { useReorderDocumentProperty } from "@/hooks/use-document-properties";
22
- import { useSetDocumentProperty } from "@/hooks/use-document-properties";
20
+ import {
21
+ documentPropertiesResponseMatchesScope,
22
+ useDocumentProperties,
23
+ useReorderDocumentProperty,
24
+ useSetDocumentProperty,
25
+ } from "@/hooks/use-document-properties";
23
26
  import { cn } from "@/lib/utils";
24
27
 
25
28
  import {
@@ -38,6 +41,7 @@ const BLOCK_FIELD_DRAG_THRESHOLD = 6;
38
41
 
39
42
  interface DocumentBlockFieldsProps {
40
43
  documentId: string;
44
+ databaseId: string;
41
45
  databaseDocumentId: string;
42
46
  canEdit: boolean;
43
47
  /**
@@ -191,17 +195,18 @@ export type BlockFieldsRenderState =
191
195
  | { kind: "solo"; field: DocumentProperty; target: BlocksStorageTarget }
192
196
  | { kind: "multi"; fields: DocumentProperty[] };
193
197
 
194
- // Whether the query data we are holding actually belongs to the CURRENT row.
198
+ // Whether the query data we are holding belongs to the current row and database.
195
199
  // `useDocumentProperties` keeps the previous document's data as placeholder
196
- // across a documentId change, so identity must be confirmed before the field
200
+ // across a scope change, so both identities must be confirmed before the field
197
201
  // layout is trusted — otherwise the old doc's solo-primary layout could route
198
202
  // the new doc's edits to the body. The response carries its own `documentId`
199
203
  // (shared/api.ts → DocumentPropertiesResponse).
200
204
  export function isLoadedForDocument(
201
205
  documentId: string,
202
- data: { documentId: string } | undefined,
206
+ databaseId: string,
207
+ data: DocumentPropertiesResponse | undefined,
203
208
  ): boolean {
204
- return data?.documentId === documentId;
209
+ return documentPropertiesResponseMatchesScope(documentId, databaseId, data);
205
210
  }
206
211
 
207
212
  export function blockFieldsRenderState(args: {
@@ -239,28 +244,22 @@ export function blockFieldsRenderState(args: {
239
244
  */
240
245
  export function DocumentBlockFields({
241
246
  documentId,
247
+ databaseId,
242
248
  databaseDocumentId,
243
249
  canEdit,
244
250
  primaryEditor,
245
251
  }: DocumentBlockFieldsProps) {
246
252
  const t = useT();
247
- const query = useDocumentProperties(documentId);
253
+ const query = useDocumentProperties(documentId, databaseId);
248
254
  const properties = query.data?.properties ?? [];
249
255
  const blockFields = useMemo(
250
256
  () => blockFieldsFromProperties(properties),
251
257
  [properties],
252
258
  );
253
259
 
254
- // Loaded ONLY when the query data we hold actually belongs to the CURRENT
255
- // documentId. `useDocumentProperties` uses `placeholderData: (prev) => prev`,
256
- // so right after the viewed row changes, `query.data` still holds the PREVIOUS
257
- // document's field layout for a tick. Trusting it would let the old doc's
258
- // solo-primary layout route the NEW doc's edits to the body (clobbering a
259
- // non-primary field). The response carries its own `documentId`
260
- // (shared/api.ts → DocumentPropertiesResponse), so we gate on an identity
261
- // match: until the data is for THIS document, treat the row as still loading
262
- // (a non-editable placeholder), never a writable body editor.
263
- const loaded = isLoadedForDocument(documentId, query.data);
260
+ // Placeholder data may belong to the previous row or database. Trust it only
261
+ // after both response identities match the active scope.
262
+ const loaded = isLoadedForDocument(documentId, databaseId, query.data);
264
263
  const state = blockFieldsRenderState({ loaded, blockFields });
265
264
 
266
265
  switch (state.kind) {
@@ -321,6 +320,7 @@ export function DocumentBlockFields({
321
320
  return (
322
321
  <MultiBlockFields
323
322
  documentId={documentId}
323
+ databaseId={databaseId}
324
324
  databaseDocumentId={databaseDocumentId}
325
325
  canEdit={canEdit}
326
326
  blockFields={state.fields}
@@ -333,6 +333,7 @@ export function DocumentBlockFields({
333
333
 
334
334
  function MultiBlockFields({
335
335
  documentId,
336
+ databaseId,
336
337
  databaseDocumentId,
337
338
  canEdit,
338
339
  blockFields,
@@ -340,13 +341,18 @@ function MultiBlockFields({
340
341
  t,
341
342
  }: {
342
343
  documentId: string;
344
+ databaseId: string;
343
345
  databaseDocumentId: string;
344
346
  canEdit: boolean;
345
347
  blockFields: DocumentProperty[];
346
348
  primaryEditor: ReactNode;
347
349
  t: ReturnType<typeof useT>;
348
350
  }) {
349
- const reorder = useReorderDocumentProperty(documentId);
351
+ const reorder = useReorderDocumentProperty(
352
+ documentId,
353
+ databaseId,
354
+ databaseDocumentId,
355
+ );
350
356
  const [dragId, setDragId] = useState<string | null>(null);
351
357
  const [dragOverGapIndex, setDragOverGapIndex] = useState<number | null>(null);
352
358
  const [dragPreview, setDragPreview] =
@@ -805,7 +811,11 @@ function AdditionalBlockEditor({
805
811
  property: DocumentProperty;
806
812
  canEdit: boolean;
807
813
  }) {
808
- const setProperty = useSetDocumentProperty(documentId, databaseDocumentId);
814
+ const setProperty = useSetDocumentProperty(
815
+ documentId,
816
+ property.definition.databaseId!,
817
+ databaseDocumentId,
818
+ );
809
819
  const propertyId = property.definition.id;
810
820
  const initialContent =
811
821
  typeof property.value === "string" ? property.value : "";
@@ -109,6 +109,8 @@ const TAB_ID = generateTabId();
109
109
 
110
110
  interface DocumentEditorProps {
111
111
  documentId: string;
112
+ databaseId?: string | null;
113
+ databaseDocumentId?: string | null;
112
114
  }
113
115
 
114
116
  type FieldSaveWatermark = { title: string; updatedAt: string | null };
@@ -240,7 +242,11 @@ function DocumentUnavailable({ onOpenHome }: { onOpenHome: () => void }) {
240
242
  * only mount once we know the doc exists. Otherwise an invalid id triggers
241
243
  * an infinite spinner plus repeating 404/403 polls in the console.
242
244
  */
243
- export function DocumentEditor({ documentId }: DocumentEditorProps) {
245
+ export function DocumentEditor({
246
+ documentId,
247
+ databaseId,
248
+ databaseDocumentId,
249
+ }: DocumentEditorProps) {
244
250
  const documentQuery = useDocument(documentId);
245
251
  const {
246
252
  data: queriedDocument,
@@ -271,7 +277,14 @@ export function DocumentEditor({ documentId }: DocumentEditorProps) {
271
277
  return <DocumentEditorSkeleton />;
272
278
  }
273
279
 
274
- return <DocumentEditorBody documentId={documentId} document={document} />;
280
+ return (
281
+ <DocumentEditorBody
282
+ documentId={documentId}
283
+ document={document}
284
+ databaseId={databaseId}
285
+ databaseDocumentId={databaseDocumentId}
286
+ />
287
+ );
275
288
  }
276
289
 
277
290
  export function shouldAwaitAuthoritativeDocument({
@@ -287,6 +300,8 @@ export function shouldAwaitAuthoritativeDocument({
287
300
  interface DocumentEditorBodyProps {
288
301
  documentId: string;
289
302
  document: Document;
303
+ databaseId?: string | null;
304
+ databaseDocumentId?: string | null;
290
305
  }
291
306
 
292
307
  type PendingDocumentSave = {
@@ -508,7 +523,12 @@ export function documentEditorBreadcrumbNavigationItems(
508
523
  return navigationItems;
509
524
  }
510
525
 
511
- function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
526
+ function DocumentEditorBody({
527
+ documentId,
528
+ document,
529
+ databaseId,
530
+ databaseDocumentId,
531
+ }: DocumentEditorBodyProps) {
512
532
  const t = useT();
513
533
  const updateDocument = useUpdateDocument();
514
534
  const createDatabase = useCreateContentDatabase(documentId);
@@ -1674,6 +1694,8 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
1674
1694
  {utilityPanel === "info" ? (
1675
1695
  <DocumentInfoPanel
1676
1696
  document={document}
1697
+ databaseId={databaseId}
1698
+ databaseDocumentId={databaseDocumentId}
1677
1699
  canEdit={editorCanEdit}
1678
1700
  onSaveDescription={(description) =>
1679
1701
  persistDocumentUpdates({ description })
@@ -1970,7 +1992,12 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
1970
1992
  return (
1971
1993
  <DocumentBlockFields
1972
1994
  documentId={documentId}
1995
+ databaseId={
1996
+ databaseId ??
1997
+ document.databaseMembership.databaseId
1998
+ }
1973
1999
  databaseDocumentId={
2000
+ databaseDocumentId ??
1974
2001
  document.databaseMembership.databaseDocumentId
1975
2002
  }
1976
2003
  canEdit={editorCanEdit}
@@ -6,12 +6,16 @@ import { DocumentProperties } from "./DocumentProperties";
6
6
 
7
7
  interface DocumentInfoPanelProps {
8
8
  document: Document;
9
+ databaseId?: string | null;
10
+ databaseDocumentId?: string | null;
9
11
  canEdit: boolean;
10
12
  onSaveDescription: (description: string) => Promise<unknown> | unknown;
11
13
  }
12
14
 
13
15
  export function DocumentInfoPanel({
14
16
  document,
17
+ databaseId,
18
+ databaseDocumentId,
15
19
  canEdit,
16
20
  onSaveDescription,
17
21
  }: DocumentInfoPanelProps) {
@@ -34,7 +38,10 @@ export function DocumentInfoPanel({
34
38
  {document.databaseMembership && !isLocalFileDocument ? (
35
39
  <DocumentProperties
36
40
  documentId={document.id}
37
- databaseDocumentId={document.databaseMembership.databaseDocumentId}
41
+ databaseId={databaseId ?? document.databaseMembership.databaseId}
42
+ databaseDocumentId={
43
+ databaseDocumentId ?? document.databaseMembership.databaseDocumentId
44
+ }
38
45
  canEdit={canEdit}
39
46
  />
40
47
  ) : null}