@agent-native/core 0.114.0 → 0.114.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +9 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/build-compatibility.ts +77 -0
  5. package/corpus/core/src/client/route-chunk-recovery/index.ts +1 -0
  6. package/corpus/core/src/client/use-action.ts +32 -1
  7. package/corpus/core/src/deploy/build.ts +4 -1
  8. package/corpus/core/src/org/context.ts +56 -30
  9. package/corpus/core/src/server/action-routes.ts +52 -2
  10. package/corpus/core/src/server/auth-plugin.ts +9 -4
  11. package/corpus/core/src/server/better-auth-instance.ts +5 -1
  12. package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
  13. package/corpus/core/src/vite/client.ts +17 -0
  14. package/corpus/templates/content/AGENTS.md +2 -0
  15. package/corpus/templates/content/actions/_content-database-personal-view.ts +49 -5
  16. package/corpus/templates/content/actions/_content-document-access.ts +21 -0
  17. package/corpus/templates/content/actions/_content-favorites.ts +91 -0
  18. package/corpus/templates/content/actions/_content-files.ts +79 -23
  19. package/corpus/templates/content/actions/_content-sidebar-state.ts +24 -0
  20. package/corpus/templates/content/actions/_content-space-access.ts +15 -17
  21. package/corpus/templates/content/actions/_content-space-catalog-guards.ts +20 -0
  22. package/corpus/templates/content/actions/_content-spaces.ts +364 -167
  23. package/corpus/templates/content/actions/_database-source-utils.ts +8 -0
  24. package/corpus/templates/content/actions/_database-utils.ts +249 -15
  25. package/corpus/templates/content/actions/_delete-content-space.ts +84 -0
  26. package/corpus/templates/content/actions/_files-system-properties.ts +499 -0
  27. package/corpus/templates/content/actions/_local-file-documents.ts +0 -1
  28. package/corpus/templates/content/actions/_property-utils.ts +18 -9
  29. package/corpus/templates/content/actions/bind-content-database-source-field.ts +3 -0
  30. package/corpus/templates/content/actions/configure-document-property.ts +3 -0
  31. package/corpus/templates/content/actions/create-content-database.ts +2 -2
  32. package/corpus/templates/content/actions/create-content-space.ts +34 -0
  33. package/corpus/templates/content/actions/create-document.ts +4 -1
  34. package/corpus/templates/content/actions/delete-content-space.ts +19 -0
  35. package/corpus/templates/content/actions/delete-database-items.ts +28 -1
  36. package/corpus/templates/content/actions/delete-document-property.ts +3 -0
  37. package/corpus/templates/content/actions/delete-document.ts +39 -2
  38. package/corpus/templates/content/actions/duplicate-database-item.ts +6 -0
  39. package/corpus/templates/content/actions/duplicate-database-items.ts +6 -0
  40. package/corpus/templates/content/actions/duplicate-document-property.ts +3 -0
  41. package/corpus/templates/content/actions/get-content-database.ts +11 -2
  42. package/corpus/templates/content/actions/get-content-sidebar-state.ts +23 -0
  43. package/corpus/templates/content/actions/get-document.ts +32 -7
  44. package/corpus/templates/content/actions/import-content-source.ts +33 -1
  45. package/corpus/templates/content/actions/list-content-spaces.ts +34 -2
  46. package/corpus/templates/content/actions/list-documents.ts +39 -7
  47. package/corpus/templates/content/actions/reorder-document-property.ts +9 -0
  48. package/corpus/templates/content/actions/set-document-property.ts +41 -23
  49. package/corpus/templates/content/actions/share-local-file-document.ts +15 -0
  50. package/corpus/templates/content/actions/submit-content-database-form.ts +7 -0
  51. package/corpus/templates/content/actions/sync-local-folder-source.ts +14 -0
  52. package/corpus/templates/content/actions/update-content-database-view.ts +1 -0
  53. package/corpus/templates/content/actions/update-content-sidebar-state.ts +27 -0
  54. package/corpus/templates/content/actions/update-document.ts +158 -66
  55. package/corpus/templates/content/app/components/EmptyState.tsx +4 -57
  56. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +8 -1
  57. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +519 -178
  58. package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +39 -0
  59. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +1 -1
  60. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +266 -7
  61. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +430 -152
  62. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +467 -66
  63. package/corpus/templates/content/app/components/editor/database/view-config.ts +9 -3
  64. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +720 -196
  65. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +173 -1
  66. package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +1 -1
  67. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +91 -16
  68. package/corpus/templates/content/app/entry.client.tsx +5 -1
  69. package/corpus/templates/content/app/hooks/use-content-database.ts +84 -3
  70. package/corpus/templates/content/app/hooks/use-content-spaces.ts +60 -2
  71. package/corpus/templates/content/app/hooks/use-create-page.ts +30 -0
  72. package/corpus/templates/content/app/hooks/use-documents.ts +270 -19
  73. package/corpus/templates/content/app/i18n-data.ts +243 -20
  74. package/corpus/templates/content/app/lib/local-folder-picker-safety.ts +54 -0
  75. package/corpus/templates/content/app/lib/local-folder-picker-support.ts +18 -0
  76. package/corpus/templates/content/app/routes/_app.favorites.tsx +30 -0
  77. package/corpus/templates/content/app/routes/_app.local-files.tsx +18 -6
  78. package/corpus/templates/content/changelog/2026-07-17-workspace-files-now-stay-in-a-loading-state-until-automatic-.md +6 -0
  79. package/corpus/templates/content/changelog/2026-07-17-workspace-switching-now-opens-the-selected-files-database-pr.md +6 -0
  80. package/corpus/templates/content/changelog/2026-07-17-workspaces-now-expand-above-their-own-files-list-without-dup.md +6 -0
  81. package/corpus/templates/content/changelog/2026-07-18-create-named-workspaces-each-with-its-own-private-files-data.md +6 -0
  82. package/corpus/templates/content/changelog/2026-07-18-favorites-align-with-workspace-and-file-rows-in-the-sidebar.md +6 -0
  83. package/corpus/templates/content/changelog/2026-07-18-local-folder-selection-no-longer-invokes-the-unsafe-native-p.md +6 -0
  84. package/corpus/templates/content/changelog/2026-07-18-made-content-workspaces-independently-expandable-restored-or.md +6 -0
  85. package/corpus/templates/content/changelog/2026-07-18-page-details-now-live-in-a-shared-info-and-comments-rail-dat.md +6 -0
  86. package/corpus/templates/content/changelog/2026-07-18-workspace-database-views-now-control-sidebar-workspace-navig.md +6 -0
  87. package/corpus/templates/content/changelog/2026-07-18-workspace-navigation-now-aligns-favorites-with-workspace-pag.md +6 -0
  88. package/corpus/templates/content/changelog/2026-07-19-favorites-now-open-as-a-table-and-compact-breadcrumbs-make-w.md +6 -0
  89. package/corpus/templates/content/changelog/2026-07-19-files-databases-now-start-unfiltered-sidebar-rows-exactly-fo.md +6 -0
  90. package/corpus/templates/content/changelog/2026-07-19-sidebar-page-trees-now-open-only-when-expanded-remember-thei.md +6 -0
  91. package/corpus/templates/content/changelog/2026-07-19-workspace-file-databases-now-use-the-workspace-name-open-as-.md +6 -0
  92. package/corpus/templates/content/changelog/2026-07-19-workspace-navigation-now-stays-stable-on-hover-uses-folder-i.md +6 -0
  93. package/corpus/templates/content/changelog/2026-07-19-workspace-rows-now-use-folder-icons-and-deleting-a-user-crea.md +6 -0
  94. package/corpus/templates/content/changelog/2026-07-19-workspaces-can-be-created-without-leaving-the-workspaces-dat.md +6 -0
  95. package/corpus/templates/content/e2e/global-setup.ts +3 -2
  96. package/corpus/templates/content/parity/matrix.md +28 -28
  97. package/corpus/templates/content/parity/matrix.ts +8 -2
  98. package/corpus/templates/content/server/db/index.ts +1 -0
  99. package/corpus/templates/content/server/db/schema.ts +10 -0
  100. package/corpus/templates/content/server/lib/documents.ts +12 -7
  101. package/corpus/templates/content/server/plugins/db.ts +17 -0
  102. package/corpus/templates/content/shared/api.ts +9 -0
  103. package/corpus/templates/content/shared/database-form.ts +11 -2
  104. package/corpus/templates/content/vite.config.ts +1 -0
  105. package/dist/client/build-compatibility.d.ts +11 -0
  106. package/dist/client/build-compatibility.d.ts.map +1 -0
  107. package/dist/client/build-compatibility.js +56 -0
  108. package/dist/client/build-compatibility.js.map +1 -0
  109. package/dist/client/route-chunk-recovery/index.d.ts +1 -0
  110. package/dist/client/route-chunk-recovery/index.d.ts.map +1 -1
  111. package/dist/client/route-chunk-recovery/index.js +1 -0
  112. package/dist/client/route-chunk-recovery/index.js.map +1 -1
  113. package/dist/client/use-action.d.ts.map +1 -1
  114. package/dist/client/use-action.js +19 -1
  115. package/dist/client/use-action.js.map +1 -1
  116. package/dist/collab/routes.d.ts +1 -1
  117. package/dist/collab/struct-routes.d.ts +1 -1
  118. package/dist/deploy/build.js +2 -1
  119. package/dist/deploy/build.js.map +1 -1
  120. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  121. package/dist/notifications/routes.d.ts +1 -1
  122. package/dist/observability/routes.d.ts +5 -5
  123. package/dist/org/context.d.ts +4 -2
  124. package/dist/org/context.d.ts.map +1 -1
  125. package/dist/org/context.js +44 -22
  126. package/dist/org/context.js.map +1 -1
  127. package/dist/progress/routes.d.ts +1 -1
  128. package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
  129. package/dist/provider-api/actions/provider-api.d.ts +12 -12
  130. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  131. package/dist/resources/handlers.d.ts +1 -1
  132. package/dist/secrets/routes.d.ts +9 -9
  133. package/dist/server/action-routes.d.ts.map +1 -1
  134. package/dist/server/action-routes.js +32 -2
  135. package/dist/server/action-routes.js.map +1 -1
  136. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  137. package/dist/server/auth-plugin.d.ts.map +1 -1
  138. package/dist/server/auth-plugin.js +9 -5
  139. package/dist/server/auth-plugin.js.map +1 -1
  140. package/dist/server/better-auth-instance.d.ts.map +1 -1
  141. package/dist/server/better-auth-instance.js +4 -1
  142. package/dist/server/better-auth-instance.js.map +1 -1
  143. package/dist/server/transcribe-voice.d.ts +1 -1
  144. package/dist/shared/mcp-embed-headers.d.ts +1 -1
  145. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  146. package/dist/shared/mcp-embed-headers.js +1 -1
  147. package/dist/shared/mcp-embed-headers.js.map +1 -1
  148. package/dist/vite/client.d.ts +6 -0
  149. package/dist/vite/client.d.ts.map +1 -1
  150. package/dist/vite/client.js +8 -0
  151. package/dist/vite/client.js.map +1 -1
  152. package/package.json +1 -1
  153. package/src/client/build-compatibility.ts +77 -0
  154. package/src/client/route-chunk-recovery/index.ts +1 -0
  155. package/src/client/use-action.ts +32 -1
  156. package/src/deploy/build.ts +4 -1
  157. package/src/org/context.ts +56 -30
  158. package/src/server/action-routes.ts +52 -2
  159. package/src/server/auth-plugin.ts +9 -4
  160. package/src/server/better-auth-instance.ts +5 -1
  161. package/src/shared/mcp-embed-headers.ts +1 -1
  162. package/src/vite/client.ts +17 -0
@@ -6,7 +6,7 @@ import {
6
6
  verticalListSortingStrategy,
7
7
  } from "@dnd-kit/sortable";
8
8
  import { CSS } from "@dnd-kit/utilities";
9
- import type { DocumentTreeNode } from "@shared/api";
9
+ import type { Document, DocumentTreeNode } from "@shared/api";
10
10
  import {
11
11
  IconChevronRight,
12
12
  IconDatabase,
@@ -88,6 +88,178 @@ export function DocumentSidebarIcon({
88
88
  return <IconFileText size={14} className="text-muted-foreground" />;
89
89
  }
90
90
 
91
+ export function FavoriteDocumentItem({
92
+ document,
93
+ active,
94
+ sidebarWidth,
95
+ onSelect,
96
+ onCreateChildPage,
97
+ onCreateChildDatabase,
98
+ onRemoveFavorite,
99
+ onDelete,
100
+ }: {
101
+ document: Document;
102
+ active: boolean;
103
+ sidebarWidth?: number;
104
+ onSelect: () => void;
105
+ onCreateChildPage: () => void;
106
+ onCreateChildDatabase: () => void;
107
+ onRemoveFavorite: () => void;
108
+ onDelete: () => void;
109
+ }) {
110
+ const t = useT();
111
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
112
+ const canEdit = document.canEdit !== false;
113
+ const canManage =
114
+ document.canManage === true ||
115
+ document.accessRole === "owner" ||
116
+ document.accessRole === "admin";
117
+ const canCreateChild = canEdit && document.source?.mode !== "local-files";
118
+ const title = document.title || t("sidebar.untitled");
119
+
120
+ return (
121
+ <div
122
+ className={cn(
123
+ "group relative flex min-w-0 cursor-pointer items-center gap-1.5 rounded-md py-[5px] pe-2 text-sm",
124
+ active
125
+ ? "font-semibold text-foreground"
126
+ : "text-muted-foreground hover:bg-accent hover:text-foreground",
127
+ )}
128
+ style={{
129
+ paddingInlineStart: "26px",
130
+ width:
131
+ sidebarWidth === undefined
132
+ ? undefined
133
+ : `${Math.max(0, sidebarWidth)}px`,
134
+ }}
135
+ aria-label={title}
136
+ >
137
+ <button
138
+ type="button"
139
+ className="absolute inset-0 rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
140
+ aria-label={`Open ${title}`}
141
+ onClick={onSelect}
142
+ />
143
+ <span className="pointer-events-none relative flex h-5 w-5 shrink-0 items-center justify-center text-center">
144
+ <DocumentSidebarIcon document={document} />
145
+ </span>
146
+ <span className="pointer-events-none relative min-w-0 flex-1 truncate pe-12">
147
+ {title}
148
+ </span>
149
+ <div
150
+ className={cn(
151
+ "pointer-events-none absolute right-1 top-1/2 z-10 flex -translate-y-1/2 items-center gap-0.5 rounded-md bg-accent px-0.5 opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100",
152
+ active && "text-accent-foreground",
153
+ )}
154
+ onPointerDown={(event) => event.stopPropagation()}
155
+ onClick={(event) => event.stopPropagation()}
156
+ >
157
+ {(canEdit || canManage) && (
158
+ <DropdownMenu>
159
+ <DropdownMenuTrigger asChild>
160
+ <button
161
+ type="button"
162
+ className="flex h-6 w-6 items-center justify-center rounded hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
163
+ aria-label={`More actions for ${title}`}
164
+ onClick={(event) => event.stopPropagation()}
165
+ >
166
+ <IconDots size={14} />
167
+ </button>
168
+ </DropdownMenuTrigger>
169
+ <DropdownMenuContent align="start" className="w-48">
170
+ {canEdit && (
171
+ <DropdownMenuItem
172
+ onClick={(event) => {
173
+ event.stopPropagation();
174
+ onRemoveFavorite();
175
+ }}
176
+ >
177
+ <IconStar size={14} className="me-2 fill-current" />
178
+ {t("sidebar.removeFromFavorites")}
179
+ </DropdownMenuItem>
180
+ )}
181
+ {canEdit && canManage && <DropdownMenuSeparator />}
182
+ {canManage && (
183
+ <DropdownMenuItem
184
+ className="text-destructive"
185
+ onClick={(event) => {
186
+ event.stopPropagation();
187
+ setDeleteDialogOpen(true);
188
+ }}
189
+ >
190
+ <IconTrash size={14} className="me-2" />
191
+ {t("database.delete")}
192
+ </DropdownMenuItem>
193
+ )}
194
+ </DropdownMenuContent>
195
+ </DropdownMenu>
196
+ )}
197
+ {canCreateChild && (
198
+ <DropdownMenu>
199
+ <Tooltip>
200
+ <TooltipTrigger asChild>
201
+ <DropdownMenuTrigger asChild>
202
+ <button
203
+ type="button"
204
+ className="flex h-7 w-7 items-center justify-center rounded hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
205
+ aria-label={t("sidebar.addChildTo", { title })}
206
+ onClick={(event) => event.stopPropagation()}
207
+ >
208
+ <IconPlus size={14} />
209
+ </button>
210
+ </DropdownMenuTrigger>
211
+ </TooltipTrigger>
212
+ <TooltipContent>{t("sidebar.addChild")}</TooltipContent>
213
+ </Tooltip>
214
+ <DropdownMenuContent align="start" className="w-44">
215
+ <DropdownMenuItem
216
+ onClick={(event) => {
217
+ event.stopPropagation();
218
+ onCreateChildPage();
219
+ }}
220
+ >
221
+ <IconFileText className="me-2 size-4" />
222
+ {t("sidebar.page")}
223
+ </DropdownMenuItem>
224
+ <DropdownMenuItem
225
+ onClick={(event) => {
226
+ event.stopPropagation();
227
+ onCreateChildDatabase();
228
+ }}
229
+ >
230
+ <IconDatabase className="me-2 size-4" />
231
+ {t("sidebar.database")}
232
+ </DropdownMenuItem>
233
+ </DropdownMenuContent>
234
+ </DropdownMenu>
235
+ )}
236
+ </div>
237
+
238
+ <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
239
+ <AlertDialogContent>
240
+ <AlertDialogHeader>
241
+ <AlertDialogTitle>
242
+ {t("sidebar.deletePageQuestion")}
243
+ </AlertDialogTitle>
244
+ <AlertDialogDescription>
245
+ {t("sidebar.deletePageDescription", { title })}
246
+ </AlertDialogDescription>
247
+ </AlertDialogHeader>
248
+ <AlertDialogFooter>
249
+ <AlertDialogCancel>{t("comments.cancel")}</AlertDialogCancel>
250
+ <AlertDialogAction
251
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
252
+ onClick={onDelete}
253
+ >
254
+ {t("database.delete")}
255
+ </AlertDialogAction>
256
+ </AlertDialogFooter>
257
+ </AlertDialogContent>
258
+ </AlertDialog>
259
+ </div>
260
+ );
261
+ }
262
+
91
263
  export function DocumentTreeItem({
92
264
  node,
93
265
  depth,
@@ -150,6 +150,6 @@ export function getDocumentSidebarSections(
150
150
  localSourceDocuments,
151
151
  databaseDocuments,
152
152
  favorites,
153
- showFavorites: favorites.length > 0,
153
+ showFavorites: !localFileMode || favorites.length > 0,
154
154
  };
155
155
  }
@@ -1,31 +1,106 @@
1
1
  import type { ContentSpaceSummary } from "@/hooks/use-content-spaces";
2
2
 
3
- export function contentSpaceForActiveOrg(args: {
3
+ export const SELECTED_CONTENT_SPACE_STORAGE_KEY = "content-selected-space";
4
+
5
+ export type ContentSpaceAvailability = "loading" | "ready" | "error";
6
+
7
+ export function contentSpaceAvailability(args: {
8
+ hasSelectedSpace: boolean;
9
+ contentSpacesLoading: boolean;
10
+ contentSpacesFetching: boolean;
11
+ contentSpacesError: boolean;
12
+ provisioningAttempted: boolean;
13
+ provisioningPending: boolean;
14
+ provisioningError: boolean;
15
+ }): ContentSpaceAvailability {
16
+ if (args.hasSelectedSpace) return "ready";
17
+ if (args.contentSpacesError || args.provisioningError) {
18
+ return "error";
19
+ }
20
+ if (
21
+ args.contentSpacesLoading ||
22
+ args.contentSpacesFetching ||
23
+ !args.provisioningAttempted ||
24
+ args.provisioningPending
25
+ ) {
26
+ return "loading";
27
+ }
28
+ return "error";
29
+ }
30
+
31
+ export function contentSpaceForStoredSelection(args: {
4
32
  spaces: ContentSpaceSummary[];
5
33
  storedSpaceId: string | null;
6
- activeOrgId: string | null | undefined;
7
34
  }) {
8
- if (args.activeOrgId === undefined) return null;
9
35
  const stored = args.spaces.find((space) => space.id === args.storedSpaceId);
10
- if (stored?.orgId === args.activeOrgId) return stored;
11
- const matching = args.spaces.filter(
12
- (space) => space.orgId === args.activeOrgId,
36
+ if (stored) return stored;
37
+ return (
38
+ args.spaces.find((space) => space.kind === "personal") ??
39
+ args.spaces[0] ??
40
+ null
41
+ );
42
+ }
43
+
44
+ export function contentSpaceForCatalogItem(args: {
45
+ databaseId: string;
46
+ catalogDatabaseId: string | undefined;
47
+ documentId: string;
48
+ spaces: ContentSpaceSummary[];
49
+ }) {
50
+ if (args.databaseId !== args.catalogDatabaseId) {
51
+ return null;
52
+ }
53
+ return (
54
+ args.spaces.find((space) => space.catalogDocumentId === args.documentId) ??
55
+ null
13
56
  );
14
- return args.activeOrgId === null
15
- ? (matching.find((space) => space.kind === "personal") ??
16
- matching[0] ??
17
- null)
18
- : (matching[0] ?? null);
57
+ }
58
+
59
+ export function toggleExpandedWorkspaceIds(
60
+ expandedIds: string[],
61
+ workspaceId: string,
62
+ ) {
63
+ return expandedIds.includes(workspaceId)
64
+ ? expandedIds.filter((id) => id !== workspaceId)
65
+ : [...expandedIds, workspaceId];
66
+ }
67
+
68
+ export function ensureWorkspaceExpanded(
69
+ expandedIds: string[],
70
+ workspaceId: string,
71
+ ) {
72
+ return expandedIds.includes(workspaceId)
73
+ ? expandedIds
74
+ : [...expandedIds, workspaceId];
75
+ }
76
+
77
+ export function contentSpaceIdForCreate(args: {
78
+ parentId?: string;
79
+ selectedSpace: ContentSpaceSummary | null;
80
+ }) {
81
+ if (args.parentId) return undefined;
82
+ if (!args.selectedSpace) {
83
+ throw new Error("Files are still loading. Try creating the page again.");
84
+ }
85
+ return args.selectedSpace.id;
19
86
  }
20
87
 
21
88
  export async function selectContentSpace(args: {
22
89
  space: ContentSpaceSummary;
23
- activeOrgId: string | null | undefined;
24
- switchOrg: (orgId: string | null) => Promise<unknown>;
90
+ syncApplicationState: (space: ContentSpaceSummary) => Promise<unknown>;
25
91
  persistSelection: (spaceId: string) => void;
92
+ openFiles: (documentId: string) => void;
26
93
  }) {
27
- if (args.activeOrgId !== args.space.orgId) {
28
- await args.switchOrg(args.space.orgId);
29
- }
94
+ await args.syncApplicationState(args.space);
30
95
  args.persistSelection(args.space.id);
96
+ args.openFiles(args.space.filesDocumentId);
97
+ }
98
+
99
+ export function createContentSpaceSelectionQueue() {
100
+ let pending = Promise.resolve();
101
+ return (selection: () => Promise<void>) => {
102
+ const next = pending.catch(() => undefined).then(selection);
103
+ pending = next;
104
+ return next;
105
+ };
31
106
  }
@@ -1,5 +1,8 @@
1
1
  import { appBasePath } from "@agent-native/core/client/api-path";
2
- import { installRouteChunkRecovery } from "@agent-native/core/client/route-chunk-recovery";
2
+ import {
3
+ installRouteChunkRecovery,
4
+ stripBuildCompatibilityCacheBuster,
5
+ } from "@agent-native/core/client/route-chunk-recovery";
3
6
  import { hydrateRoot } from "react-dom/client";
4
7
  import { HydratedRouter } from "react-router/dom";
5
8
 
@@ -20,3 +23,4 @@ if (context) {
20
23
  }
21
24
 
22
25
  hydrateRoot(document, <HydratedRouter />);
26
+ stripBuildCompatibilityCacheBuster();
@@ -11,6 +11,7 @@ import type {
11
11
  CancelPreparedBuilderSourceUpdateResponse,
12
12
  ChangeContentDatabaseSourceRoleRequest,
13
13
  ContentDatabaseResponse,
14
+ ContentDatabaseItem,
14
15
  ContentDatabasePersonalViewResponse,
15
16
  ContentDatabaseSourceFieldMapping,
16
17
  CreateInlineDatabaseRequest,
@@ -59,6 +60,77 @@ export function contentDatabaseByIdQueryKey(databaseId: string) {
59
60
  return ["action", "get-content-database", { databaseId }] as const;
60
61
  }
61
62
 
63
+ export function applyOptimisticItemToContentDatabase(
64
+ current: ContentDatabaseResponse | undefined,
65
+ item: ContentDatabaseItem,
66
+ ): ContentDatabaseResponse | undefined {
67
+ if (!current) return current;
68
+ if (
69
+ current.items.some(
70
+ (candidate) =>
71
+ candidate.id === item.id || candidate.document.id === item.document.id,
72
+ )
73
+ ) {
74
+ return current;
75
+ }
76
+
77
+ return {
78
+ ...current,
79
+ items: [...current.items, item],
80
+ pagination: current.pagination
81
+ ? {
82
+ ...current.pagination,
83
+ totalItems: current.pagination.totalItems + 1,
84
+ returnedItems: current.pagination.returnedItems + 1,
85
+ }
86
+ : current.pagination,
87
+ };
88
+ }
89
+
90
+ export function removeOptimisticItemFromContentDatabase(
91
+ current: ContentDatabaseResponse | undefined,
92
+ documentId: string,
93
+ ): ContentDatabaseResponse | undefined {
94
+ if (!current) return current;
95
+ const items = current.items.filter(
96
+ (candidate) => candidate.document.id !== documentId,
97
+ );
98
+ if (items.length === current.items.length) return current;
99
+
100
+ return {
101
+ ...current,
102
+ items,
103
+ pagination: current.pagination
104
+ ? {
105
+ ...current.pagination,
106
+ totalItems: Math.max(0, current.pagination.totalItems - 1),
107
+ returnedItems: Math.max(0, current.pagination.returnedItems - 1),
108
+ }
109
+ : current.pagination,
110
+ };
111
+ }
112
+
113
+ export function preserveScopedDatabasePlaceholder<T>(
114
+ previous: T | undefined,
115
+ previousQuery: Pick<Query, "queryKey"> | undefined,
116
+ scope: { documentId?: string; databaseId?: string },
117
+ ): T | undefined {
118
+ const previousParams = previousQuery?.queryKey[2];
119
+ if (!previousParams || typeof previousParams !== "object") return undefined;
120
+
121
+ const params = previousParams as {
122
+ documentId?: unknown;
123
+ databaseId?: unknown;
124
+ };
125
+ if (scope.documentId !== undefined) {
126
+ return params.documentId === scope.documentId ? previous : undefined;
127
+ }
128
+ if (scope.databaseId !== undefined) {
129
+ return params.databaseId === scope.databaseId ? previous : undefined;
130
+ }
131
+ return undefined;
132
+ }
133
+
62
134
  function isContentDatabaseQueryForDocument(
63
135
  queryKey: readonly unknown[],
64
136
  documentId: string,
@@ -449,7 +521,10 @@ export function useContentDatabase(documentId: string | null, limit?: number) {
449
521
  {
450
522
  enabled: !!documentId,
451
523
  retry: false,
452
- placeholderData: (previous) => previous,
524
+ placeholderData: (previous, previousQuery) =>
525
+ preserveScopedDatabasePlaceholder(previous, previousQuery, {
526
+ documentId: documentId ?? undefined,
527
+ }),
453
528
  initialData: () =>
454
529
  documentId
455
530
  ? readCachedContentDatabaseResponse(queryClient, documentId)
@@ -468,7 +543,10 @@ export function useContentDatabaseById(databaseId: string | null) {
468
543
  {
469
544
  enabled: !!databaseId,
470
545
  retry: false,
471
- placeholderData: (previous) => previous,
546
+ placeholderData: (previous, previousQuery) =>
547
+ preserveScopedDatabasePlaceholder(previous, previousQuery, {
548
+ databaseId: databaseId ?? undefined,
549
+ }),
472
550
  },
473
551
  );
474
552
  }
@@ -719,7 +797,10 @@ export function useContentDatabasePersonalView(databaseId: string | null) {
719
797
  {
720
798
  enabled: !!databaseId,
721
799
  retry: false,
722
- placeholderData: (previous) => previous,
800
+ placeholderData: (previous, previousQuery) =>
801
+ preserveScopedDatabasePlaceholder(previous, previousQuery, {
802
+ databaseId: databaseId ?? undefined,
803
+ }),
723
804
  },
724
805
  );
725
806
  }
@@ -9,6 +9,7 @@ export type ContentSpaceSummary = {
9
9
  name: string;
10
10
  kind: string;
11
11
  filesDatabaseId: string;
12
+ filesDocumentId: string;
12
13
  orgId: string | null;
13
14
  role: "owner" | "editor" | "viewer";
14
15
  catalogItemId: string;
@@ -17,6 +18,9 @@ export type ContentSpaceSummary = {
17
18
 
18
19
  export type ListContentSpacesResponse = {
19
20
  catalogDatabaseId: string;
21
+ catalogDocumentId: string;
22
+ favoritesDatabaseId: string | null;
23
+ favoritesDocumentId: string | null;
20
24
  spaces: ContentSpaceSummary[];
21
25
  };
22
26
 
@@ -33,10 +37,64 @@ export function useContentSpaces() {
33
37
  export function useEnsureContentSpaces() {
34
38
  const queryClient = useQueryClient();
35
39
  return useActionMutation("ensure-content-spaces", {
36
- onSuccess: () => {
37
- queryClient.invalidateQueries({
40
+ onSuccess: async () => {
41
+ await queryClient.refetchQueries({
38
42
  queryKey: ["action", "list-content-spaces"],
39
43
  });
40
44
  },
41
45
  });
42
46
  }
47
+
48
+ export function useCreateContentSpace() {
49
+ const queryClient = useQueryClient();
50
+ return useActionMutation<
51
+ {
52
+ spaceId: string;
53
+ filesDatabaseId: string;
54
+ filesDocumentId: string;
55
+ catalogDatabaseId: string;
56
+ catalogItemId: string;
57
+ catalogDocumentId: string;
58
+ name: string;
59
+ kind: "user";
60
+ },
61
+ {
62
+ name: string;
63
+ requestId: string;
64
+ propertyValues?: Record<string, unknown>;
65
+ }
66
+ >("create-content-space", {
67
+ onSuccess: async () => {
68
+ await Promise.all([
69
+ queryClient.refetchQueries({
70
+ queryKey: ["action", "list-content-spaces"],
71
+ }),
72
+ queryClient.refetchQueries({
73
+ queryKey: ["action", "get-content-database"],
74
+ }),
75
+ ]);
76
+ },
77
+ });
78
+ }
79
+
80
+ export function useDeleteContentSpace() {
81
+ const queryClient = useQueryClient();
82
+ return useActionMutation<
83
+ { success: boolean; spaceId: string; deletedDocuments: number },
84
+ { spaceId: string }
85
+ >("delete-content-space", {
86
+ onSuccess: async () => {
87
+ await Promise.all([
88
+ queryClient.refetchQueries({
89
+ queryKey: ["action", "list-content-spaces"],
90
+ }),
91
+ queryClient.refetchQueries({
92
+ queryKey: ["action", "get-content-database"],
93
+ }),
94
+ queryClient.refetchQueries({
95
+ queryKey: ["action", "list-documents"],
96
+ }),
97
+ ]);
98
+ },
99
+ });
100
+ }
@@ -4,7 +4,14 @@ import { useCallback } from "react";
4
4
  import { useNavigate } from "react-router";
5
5
  import { toast } from "sonner";
6
6
 
7
+ import {
8
+ contentSpaceForStoredSelection,
9
+ contentSpaceIdForCreate,
10
+ SELECTED_CONTENT_SPACE_STORAGE_KEY,
11
+ } from "@/components/sidebar/select-content-space";
12
+ import { useContentSpaces } from "@/hooks/use-content-spaces";
7
13
  import { useCreateDocument } from "@/hooks/use-documents";
14
+ import { useLocalStorage } from "@/hooks/use-local-storage";
8
15
 
9
16
  const LIST_DOCUMENTS_QUERY_KEY = [
10
17
  "action",
@@ -27,12 +34,33 @@ export function useCreatePage(opts?: {
27
34
  const navigate = useNavigate();
28
35
  const queryClient = useQueryClient();
29
36
  const createDocument = useCreateDocument();
37
+ const contentSpacesQuery = useContentSpaces();
38
+ const [storedSpaceId] = useLocalStorage<string | null>(
39
+ SELECTED_CONTENT_SPACE_STORAGE_KEY,
40
+ null,
41
+ );
42
+ const selectedSpace = contentSpaceForStoredSelection({
43
+ spaces: contentSpacesQuery.data?.spaces ?? [],
44
+ storedSpaceId,
45
+ });
30
46
  const onAfterNavigate = opts?.onAfterNavigate;
31
47
  const shouldNavigate = opts?.navigate ?? true;
32
48
  const shouldAwaitPersist = opts?.awaitPersist ?? true;
33
49
 
34
50
  return useCallback(
35
51
  async (parentId?: string) => {
52
+ let spaceId: string | undefined;
53
+ try {
54
+ spaceId = contentSpaceIdForCreate({
55
+ parentId,
56
+ selectedSpace,
57
+ });
58
+ } catch (error) {
59
+ toast.error(
60
+ error instanceof Error ? error.message : "Files are still loading",
61
+ );
62
+ throw error;
63
+ }
36
64
  const id = nanoid();
37
65
  const now = new Date().toISOString();
38
66
  const tempDoc: Document = {
@@ -66,6 +94,7 @@ export function useCreatePage(opts?: {
66
94
  id,
67
95
  title: "",
68
96
  parentId: parentId ?? undefined,
97
+ spaceId,
69
98
  });
70
99
  // Replace optimistic doc with real server doc + clear any 404 error
71
100
  // state from the in-flight fetch that ran before create completed.
@@ -109,6 +138,7 @@ export function useCreatePage(opts?: {
109
138
  navigate,
110
139
  onAfterNavigate,
111
140
  queryClient,
141
+ selectedSpace,
112
142
  shouldAwaitPersist,
113
143
  shouldNavigate,
114
144
  ],