@agent-native/core 0.128.1 → 0.128.3

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 (84) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +12 -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/server/email-template.d.ts +7 -0
  71. package/dist/server/email-template.d.ts.map +1 -1
  72. package/dist/server/email-template.js +24 -0
  73. package/dist/server/email-template.js.map +1 -1
  74. package/dist/server/realtime-token.d.ts +1 -1
  75. package/dist/templates/chat/_gitignore +2 -0
  76. package/package.json +3 -2
  77. package/src/server/email-template.ts +33 -0
  78. package/src/templates/chat/_gitignore +2 -0
  79. package/corpus/core/src/cli/sync-builder-starter-manifest.ts +0 -631
  80. package/dist/cli/sync-builder-starter-manifest.d.ts +0 -79
  81. package/dist/cli/sync-builder-starter-manifest.d.ts.map +0 -1
  82. package/dist/cli/sync-builder-starter-manifest.js +0 -426
  83. package/dist/cli/sync-builder-starter-manifest.js.map +0 -1
  84. package/src/cli/sync-builder-starter-manifest.ts +0 -631
@@ -175,7 +175,7 @@ export function FavoriteDocumentItem({
175
175
  }}
176
176
  >
177
177
  <IconStar size={14} className="me-2 fill-current" />
178
- {t("sidebar.removeFromFavorites")}
178
+ {t("sidebar.unpinFromSidebar")}
179
179
  </DropdownMenuItem>
180
180
  )}
181
181
  {canEdit && canManage && <DropdownMenuSeparator />}
@@ -419,8 +419,8 @@ export function DocumentTreeItem({
419
419
  className={cn("me-2", node.isFavorite && "fill-current")}
420
420
  />
421
421
  {node.isFavorite
422
- ? "Remove from favorites"
423
- : "Add to favorites"}
422
+ ? t("sidebar.unpinFromSidebar")
423
+ : t("sidebar.pinToSidebar")}
424
424
  </DropdownMenuItem>
425
425
  )}
426
426
  {canEdit && canManage && <DropdownMenuSeparator />}
@@ -0,0 +1,409 @@
1
+ import {
2
+ closestCenter,
3
+ DndContext,
4
+ KeyboardSensor,
5
+ PointerSensor,
6
+ useSensor,
7
+ useSensors,
8
+ type Announcements,
9
+ type DragEndEvent,
10
+ type DragOverEvent,
11
+ type DragStartEvent,
12
+ } from "@dnd-kit/core";
13
+ import {
14
+ arrayMove,
15
+ sortableKeyboardCoordinates,
16
+ SortableContext,
17
+ useSortable,
18
+ verticalListSortingStrategy,
19
+ } from "@dnd-kit/sortable";
20
+ import { CSS } from "@dnd-kit/utilities";
21
+ import {
22
+ createContext,
23
+ useContext,
24
+ useEffect,
25
+ useRef,
26
+ useState,
27
+ type CSSProperties,
28
+ type ReactNode,
29
+ } from "react";
30
+
31
+ import { cn } from "@/lib/utils";
32
+
33
+ export interface SidebarReorderItem {
34
+ id: string;
35
+ label: string;
36
+ parentId: string | null;
37
+ }
38
+
39
+ export interface SidebarReorderLabels {
40
+ drag: (label: string) => string;
41
+ moveUp: string;
42
+ moveDown: string;
43
+ moveTo: string;
44
+ moveToPosition: (position: number) => string;
45
+ }
46
+
47
+ interface SidebarReorderContextValue {
48
+ items: SidebarReorderItem[];
49
+ activeId: string | null;
50
+ overId: string | null;
51
+ dragBounds: { minY: number; maxY: number } | null;
52
+ registerItemNode: (itemId: string, node: HTMLElement | null) => void;
53
+ onReorder: (
54
+ itemIds: string[],
55
+ moved: { itemId: string; position: number },
56
+ ) => void;
57
+ }
58
+
59
+ const SidebarReorderContext = createContext<SidebarReorderContextValue | null>(
60
+ null,
61
+ );
62
+ const DRAG_RELEASE_CLICK_WINDOW_MS = 60;
63
+
64
+ export function isSidebarDragReleaseClick(
65
+ event: Pick<MouseEvent, "button" | "detail" | "target">,
66
+ itemId: string,
67
+ ) {
68
+ if (event.button !== 0 || event.detail !== 1) return false;
69
+ const target = event.target;
70
+ if (!(target instanceof Element)) return false;
71
+ const row = target.closest<HTMLElement>("[data-sidebar-reorder-item-id]");
72
+ return row?.dataset.sidebarReorderItemId === itemId;
73
+ }
74
+
75
+ export function isPointerSidebarDrag(activatorEvent: Event) {
76
+ return (
77
+ typeof PointerEvent !== "undefined" &&
78
+ activatorEvent instanceof PointerEvent
79
+ );
80
+ }
81
+
82
+ export function reorderedSidebarItemIds(
83
+ items: SidebarReorderItem[],
84
+ activeId: string,
85
+ overId: string,
86
+ ) {
87
+ const activeIndex = items.findIndex((item) => item.id === activeId);
88
+ const overIndex = items.findIndex((item) => item.id === overId);
89
+ if (activeIndex < 0 || overIndex < 0 || activeIndex === overIndex) {
90
+ return items.map((item) => item.id);
91
+ }
92
+ if (items[activeIndex].parentId !== items[overIndex].parentId) {
93
+ return items.map((item) => item.id);
94
+ }
95
+ const siblingIndexes = items.flatMap((item, index) =>
96
+ item.parentId === items[activeIndex].parentId ? [index] : [],
97
+ );
98
+ const activeSiblingIndex = siblingIndexes.indexOf(activeIndex);
99
+ const overSiblingIndex = siblingIndexes.indexOf(overIndex);
100
+ const reorderedSiblings = arrayMove(
101
+ siblingIndexes.map((index) => items[index]),
102
+ activeSiblingIndex,
103
+ overSiblingIndex,
104
+ );
105
+ const nextItems = [...items];
106
+ siblingIndexes.forEach((index, siblingIndex) => {
107
+ nextItems[index] = reorderedSiblings[siblingIndex];
108
+ });
109
+ return nextItems.map((item) => item.id);
110
+ }
111
+
112
+ export function sidebarReorderAnnouncement(
113
+ items: SidebarReorderItem[],
114
+ itemId: string,
115
+ overId: string | null,
116
+ labels: SidebarReorderLabels,
117
+ ) {
118
+ const item = items.find((candidate) => candidate.id === itemId);
119
+ if (!item) return undefined;
120
+ const siblings = items.filter(
121
+ (candidate) => candidate.parentId === item.parentId,
122
+ );
123
+ const over = siblings.find((candidate) => candidate.id === overId);
124
+ const position = Math.max(0, siblings.indexOf(over ?? item));
125
+ return `${labels.drag(item.label)}. ${labels.moveToPosition(position + 1)}.`;
126
+ }
127
+
128
+ export function constrainedSidebarTransform<
129
+ Transform extends { x: number; y: number },
130
+ >(
131
+ transform: Transform,
132
+ isDragging: boolean,
133
+ bounds: { minY: number; maxY: number } | null,
134
+ ) {
135
+ return {
136
+ ...transform,
137
+ x: 0,
138
+ y:
139
+ isDragging && bounds
140
+ ? Math.min(Math.max(transform.y, bounds.minY), bounds.maxY)
141
+ : transform.y,
142
+ };
143
+ }
144
+
145
+ export function SidebarReorderProvider({
146
+ items,
147
+ labels,
148
+ onReorder,
149
+ children,
150
+ }: {
151
+ items: SidebarReorderItem[];
152
+ labels: SidebarReorderLabels;
153
+ onReorder: (
154
+ itemIds: string[],
155
+ moved: { itemId: string; position: number },
156
+ ) => void;
157
+ children: ReactNode;
158
+ }) {
159
+ const itemNodes = useRef(new Map<string, HTMLElement>());
160
+ const suppressedClickItemId = useRef<string | null>(null);
161
+ const suppressedClickTimer = useRef<ReturnType<typeof setTimeout> | null>(
162
+ null,
163
+ );
164
+ const [activeId, setActiveId] = useState<string | null>(null);
165
+ const [overId, setOverId] = useState<string | null>(null);
166
+ const [dragBounds, setDragBounds] = useState<{
167
+ minY: number;
168
+ maxY: number;
169
+ } | null>(null);
170
+ const sensors = useSensors(
171
+ useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
172
+ useSensor(KeyboardSensor, {
173
+ coordinateGetter: sortableKeyboardCoordinates,
174
+ }),
175
+ );
176
+ const announcements: Announcements = {
177
+ onDragStart: ({ active }) =>
178
+ sidebarReorderAnnouncement(items, String(active.id), null, labels),
179
+ onDragOver: ({ active, over }) =>
180
+ sidebarReorderAnnouncement(
181
+ items,
182
+ String(active.id),
183
+ over ? String(over.id) : null,
184
+ labels,
185
+ ),
186
+ onDragEnd: ({ active, over }) =>
187
+ sidebarReorderAnnouncement(
188
+ items,
189
+ String(active.id),
190
+ over ? String(over.id) : null,
191
+ labels,
192
+ ),
193
+ onDragCancel: ({ active }) =>
194
+ sidebarReorderAnnouncement(items, String(active.id), null, labels),
195
+ };
196
+
197
+ useEffect(() => {
198
+ function preventDraggedLinkNavigation(event: MouseEvent) {
199
+ const itemId = suppressedClickItemId.current;
200
+ if (!itemId || !isSidebarDragReleaseClick(event, itemId)) return;
201
+
202
+ event.preventDefault();
203
+ suppressedClickItemId.current = null;
204
+ }
205
+
206
+ document.addEventListener("click", preventDraggedLinkNavigation, true);
207
+ return () => {
208
+ document.removeEventListener("click", preventDraggedLinkNavigation, true);
209
+ if (suppressedClickTimer.current) {
210
+ clearTimeout(suppressedClickTimer.current);
211
+ }
212
+ };
213
+ }, []);
214
+
215
+ function registerItemNode(itemId: string, node: HTMLElement | null) {
216
+ if (node) itemNodes.current.set(itemId, node);
217
+ else itemNodes.current.delete(itemId);
218
+ }
219
+
220
+ function handleDragStart(event: DragStartEvent) {
221
+ const itemId = String(event.active.id);
222
+ const item = items.find((candidate) => candidate.id === itemId);
223
+ const activeNode = itemNodes.current.get(itemId);
224
+ if (!item || !activeNode) return;
225
+
226
+ const activeRect = activeNode.getBoundingClientRect();
227
+ const siblingRects = items
228
+ .filter((candidate) => candidate.parentId === item.parentId)
229
+ .flatMap((candidate) => {
230
+ const node = itemNodes.current.get(candidate.id);
231
+ return node ? [node.getBoundingClientRect()] : [];
232
+ });
233
+ setActiveId(itemId);
234
+ setOverId(itemId);
235
+ setDragBounds({
236
+ minY:
237
+ Math.min(...siblingRects.map((rect) => rect.top), activeRect.top) -
238
+ activeRect.top,
239
+ maxY:
240
+ Math.max(
241
+ ...siblingRects.map((rect) => rect.bottom),
242
+ activeRect.bottom,
243
+ ) - activeRect.bottom,
244
+ });
245
+ }
246
+
247
+ function handleDragOver(event: DragOverEvent) {
248
+ setOverId(event.over ? String(event.over.id) : null);
249
+ }
250
+
251
+ function clearDragState() {
252
+ setActiveId(null);
253
+ setOverId(null);
254
+ setDragBounds(null);
255
+ }
256
+
257
+ function suppressReleaseClick(itemId: string) {
258
+ suppressedClickItemId.current = itemId;
259
+ if (suppressedClickTimer.current) {
260
+ clearTimeout(suppressedClickTimer.current);
261
+ }
262
+ suppressedClickTimer.current = setTimeout(() => {
263
+ if (suppressedClickItemId.current === itemId) {
264
+ suppressedClickItemId.current = null;
265
+ }
266
+ suppressedClickTimer.current = null;
267
+ }, DRAG_RELEASE_CLICK_WINDOW_MS);
268
+ }
269
+
270
+ function handleDragEnd(event: DragEndEvent) {
271
+ const itemId = String(event.active.id);
272
+ if (isPointerSidebarDrag(event.activatorEvent)) {
273
+ suppressReleaseClick(itemId);
274
+ }
275
+ const overId = event.over?.id;
276
+ if (overId) {
277
+ const currentIds = items.map((item) => item.id);
278
+ const nextIds = reorderedSidebarItemIds(
279
+ items,
280
+ String(event.active.id),
281
+ String(overId),
282
+ );
283
+ if (nextIds.some((id, index) => id !== currentIds[index])) {
284
+ onReorder(nextIds, { itemId, position: nextIds.indexOf(itemId) });
285
+ }
286
+ }
287
+ clearDragState();
288
+ }
289
+
290
+ return (
291
+ <SidebarReorderContext.Provider
292
+ value={{
293
+ items,
294
+ activeId,
295
+ overId,
296
+ dragBounds,
297
+ registerItemNode,
298
+ onReorder,
299
+ }}
300
+ >
301
+ <DndContext
302
+ sensors={sensors}
303
+ collisionDetection={closestCenter}
304
+ autoScroll={false}
305
+ onDragStart={handleDragStart}
306
+ onDragOver={handleDragOver}
307
+ onDragEnd={handleDragEnd}
308
+ onDragCancel={clearDragState}
309
+ accessibility={{
310
+ announcements,
311
+ screenReaderInstructions: {
312
+ draggable: `${labels.moveTo}. ${labels.moveUp}. ${labels.moveDown}.`,
313
+ },
314
+ }}
315
+ >
316
+ <SortableContext
317
+ items={items.map((item) => item.id)}
318
+ strategy={verticalListSortingStrategy}
319
+ >
320
+ {children}
321
+ </SortableContext>
322
+ </DndContext>
323
+ </SidebarReorderContext.Provider>
324
+ );
325
+ }
326
+
327
+ export function useSidebarReorderItem(itemId: string) {
328
+ const context = useContext(SidebarReorderContext);
329
+ const sortable = useSortable({ id: itemId, disabled: !context });
330
+ const item = context?.items.find((candidate) => candidate.id === itemId);
331
+ const siblings = item
332
+ ? (context?.items.filter(
333
+ (candidate) => candidate.parentId === item.parentId,
334
+ ) ?? [])
335
+ : [];
336
+ const siblingIndex = siblings.findIndex(
337
+ (candidate) => candidate.id === itemId,
338
+ );
339
+ const activeSiblingIndex = siblings.findIndex(
340
+ (candidate) => candidate.id === context?.activeId,
341
+ );
342
+ const transform = sortable.transform
343
+ ? constrainedSidebarTransform(
344
+ sortable.transform,
345
+ sortable.isDragging,
346
+ context?.dragBounds ?? null,
347
+ )
348
+ : null;
349
+ const dropIndicator: "before" | "after" | null =
350
+ context?.activeId &&
351
+ context.activeId !== itemId &&
352
+ context.overId === itemId &&
353
+ activeSiblingIndex >= 0
354
+ ? activeSiblingIndex < siblingIndex
355
+ ? "after"
356
+ : "before"
357
+ : null;
358
+
359
+ function moveToSibling(target: SidebarReorderItem | undefined) {
360
+ if (!context || !target) return;
361
+ const nextIds = reorderedSidebarItemIds(context.items, itemId, target.id);
362
+ context.onReorder(nextIds, {
363
+ itemId,
364
+ position: nextIds.indexOf(itemId),
365
+ });
366
+ }
367
+
368
+ return {
369
+ setNodeRef: (node: HTMLElement | null) => {
370
+ sortable.setNodeRef(node);
371
+ context?.registerItemNode(itemId, node);
372
+ },
373
+ style: {
374
+ transform: CSS.Transform.toString(transform),
375
+ transition: sortable.transition,
376
+ opacity: sortable.isDragging ? 0.55 : undefined,
377
+ } satisfies CSSProperties,
378
+ attributes: sortable.attributes,
379
+ listeners: sortable.listeners,
380
+ itemId,
381
+ isDragging: sortable.isDragging,
382
+ dropIndicator,
383
+ siblings,
384
+ siblingIndex,
385
+ moveUp: () => moveToSibling(siblings[siblingIndex - 1]),
386
+ moveDown: () => moveToSibling(siblings[siblingIndex + 1]),
387
+ moveTo: (position: number) => moveToSibling(siblings[position]),
388
+ };
389
+ }
390
+
391
+ export function SidebarDropIndicator({
392
+ placement,
393
+ className,
394
+ }: {
395
+ placement: "before" | "after" | null;
396
+ className?: string;
397
+ }) {
398
+ if (!placement) return null;
399
+ return (
400
+ <span
401
+ aria-hidden="true"
402
+ className={cn(
403
+ "pointer-events-none absolute inset-x-1 z-20 h-0.5 rounded-full bg-foreground",
404
+ placement === "before" ? "-top-px" : "-bottom-px",
405
+ className,
406
+ )}
407
+ />
408
+ );
409
+ }
@@ -110,6 +110,25 @@ export function removeOptimisticItemFromContentDatabase(
110
110
  };
111
111
  }
112
112
 
113
+ export function moveOptimisticContentDatabaseItem(
114
+ current: ContentDatabaseResponse | undefined,
115
+ itemId: string,
116
+ position: number,
117
+ ) {
118
+ if (!current) return current;
119
+ const currentIndex = current.items.findIndex((item) => item.id === itemId);
120
+ if (currentIndex < 0 || current.items.length < 2) return current;
121
+ const nextIndex = Math.min(Math.max(position, 0), current.items.length - 1);
122
+ if (currentIndex === nextIndex) return current;
123
+ const items = [...current.items];
124
+ const [moved] = items.splice(currentIndex, 1);
125
+ items.splice(nextIndex, 0, moved);
126
+ return {
127
+ ...current,
128
+ items: items.map((item, index) => ({ ...item, position: index })),
129
+ };
130
+ }
131
+
113
132
  export function preserveScopedDatabasePlaceholder<T>(
114
133
  previous: T | undefined,
115
134
  previousQuery: Pick<Query, "queryKey"> | undefined,
@@ -823,10 +842,62 @@ export function useMoveDatabaseItem(documentId: string) {
823
842
  return useActionMutation<ContentDatabaseResponse, MoveDatabaseItemRequest>(
824
843
  "move-database-item",
825
844
  {
826
- onSuccess: () => {
845
+ skipActionQueryInvalidation: true,
846
+ onMutate: async (variables) => {
847
+ await queryClient.cancelQueries({
848
+ queryKey: ["action", "get-content-database"],
849
+ });
850
+ const previous = queryClient.getQueriesData<ContentDatabaseResponse>({
851
+ queryKey: ["action", "get-content-database"],
852
+ });
853
+ if (variables.itemId) {
854
+ queryClient.setQueriesData<ContentDatabaseResponse>(
855
+ { queryKey: ["action", "get-content-database"] },
856
+ (current) => {
857
+ if (
858
+ variables.databaseId &&
859
+ current?.database.id !== variables.databaseId
860
+ ) {
861
+ return current;
862
+ }
863
+ return moveOptimisticContentDatabaseItem(
864
+ current,
865
+ variables.itemId!,
866
+ variables.position,
867
+ );
868
+ },
869
+ );
870
+ }
871
+ return { previous };
872
+ },
873
+ onError: (_error, _variables, context) => {
874
+ const rollback = context as
875
+ | {
876
+ previous?: Array<
877
+ [readonly unknown[], ContentDatabaseResponse | undefined]
878
+ >;
879
+ }
880
+ | undefined;
881
+ for (const [queryKey, data] of rollback?.previous ?? []) {
882
+ queryClient.setQueryData(queryKey, data);
883
+ }
884
+ },
885
+ onSuccess: (data) => {
886
+ writeContentDatabaseResponseToCache(queryClient, documentId, data);
887
+ queryClient.setQueryData(
888
+ contentDatabaseByIdQueryKey(data.database.id),
889
+ data,
890
+ );
891
+ },
892
+ onSettled: (_data, _error, variables) => {
827
893
  queryClient.invalidateQueries({
828
894
  queryKey: contentDatabaseQueryKey(documentId),
829
895
  });
896
+ if (variables.databaseId) {
897
+ queryClient.invalidateQueries({
898
+ queryKey: contentDatabaseByIdQueryKey(variables.databaseId),
899
+ });
900
+ }
830
901
  queryClient.invalidateQueries({
831
902
  queryKey: ["action", "list-documents"],
832
903
  });
@@ -875,6 +946,31 @@ export function useUpdateContentDatabasePersonalView(
875
946
  ContentDatabasePersonalViewResponse,
876
947
  UpdateContentDatabasePersonalViewRequest
877
948
  >("update-content-database-personal-view", {
949
+ skipActionQueryInvalidation: true,
950
+ onMutate: async (variables) => {
951
+ if (!databaseId) return undefined;
952
+ const queryKey = [
953
+ "action",
954
+ "get-content-database-personal-view",
955
+ { databaseId },
956
+ ] as const;
957
+ await queryClient.cancelQueries({ queryKey });
958
+ const previous = queryClient.getQueryData(queryKey);
959
+ queryClient.setQueryData(queryKey, {
960
+ databaseId,
961
+ overrides: variables.overrides,
962
+ });
963
+ return { previous };
964
+ },
965
+ onError: (_error, _variables, context) => {
966
+ if (!databaseId) return;
967
+ const previous = (context as { previous?: unknown } | undefined)
968
+ ?.previous;
969
+ queryClient.setQueryData(
970
+ ["action", "get-content-database-personal-view", { databaseId }],
971
+ previous,
972
+ );
973
+ },
878
974
  onSuccess: (data) => {
879
975
  if (!databaseId) return;
880
976
  queryClient.setQueryData(
@@ -882,6 +978,16 @@ export function useUpdateContentDatabasePersonalView(
882
978
  data,
883
979
  );
884
980
  },
981
+ onSettled: () => {
982
+ if (!databaseId) return;
983
+ queryClient.invalidateQueries({
984
+ queryKey: [
985
+ "action",
986
+ "get-content-database-personal-view",
987
+ { databaseId },
988
+ ],
989
+ });
990
+ },
885
991
  });
886
992
  }
887
993
 
@@ -974,7 +1080,11 @@ export function useAddContentDatabaseSourceFieldProperty(documentId: string) {
974
1080
  queryKey: ["action", "get-content-database-source", { documentId }],
975
1081
  });
976
1082
  queryClient.invalidateQueries({
977
- queryKey: ["action", "list-document-properties", { documentId }],
1083
+ queryKey: [
1084
+ "action",
1085
+ "list-document-properties",
1086
+ { documentId, databaseId: data.databaseId },
1087
+ ],
978
1088
  });
979
1089
  },
980
1090
  });
@@ -995,7 +1105,11 @@ export function useMaterializeBuilderRequiredFields(documentId: string) {
995
1105
  queryKey: ["action", "get-content-database-source", { documentId }],
996
1106
  });
997
1107
  queryClient.invalidateQueries({
998
- queryKey: ["action", "list-document-properties", { documentId }],
1108
+ queryKey: [
1109
+ "action",
1110
+ "list-document-properties",
1111
+ { documentId, databaseId: data.database.id },
1112
+ ],
999
1113
  });
1000
1114
  },
1001
1115
  });
@@ -14,6 +14,7 @@ export type ContentSpaceSummary = {
14
14
  role: "owner" | "editor" | "viewer";
15
15
  catalogItemId: string;
16
16
  catalogDocumentId: string;
17
+ catalogPosition: number;
17
18
  };
18
19
 
19
20
  export type ListContentSpacesResponse = {