@agent-native/core-corpus 0.201.0-nightly-20261005153421 → 0.201.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +9 -2
  3. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +14 -4
  4. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +12 -7
  5. package/corpus/templates/analytics/CHANGELOG.md +6 -0
  6. package/corpus/templates/analytics/README.md +2 -2
  7. package/corpus/templates/analytics/changelog/2026-10-02-events-with-a-very-long-name-app-or-page-path-no-longer-caus.md +6 -0
  8. package/corpus/templates/analytics/package.json +1 -0
  9. package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +9 -19
  10. package/corpus/templates/analytics/server/handlers/session-replay.ts +34 -47
  11. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +9 -6
  12. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +57 -36
  13. package/corpus/templates/analytics/server/lib/indexed-text.ts +53 -0
  14. package/corpus/templates/analytics/server/lib/request-errors.ts +73 -0
  15. package/corpus/templates/analytics/server/lib/session-event-index.ts +6 -16
  16. package/corpus/templates/analytics/server/lib/session-replay.ts +12 -2
  17. package/corpus/templates/analytics/server/plugins/auth.ts +2 -1
  18. package/corpus/templates/analytics/server/plugins/otel.ts +6 -0
  19. package/corpus/templates/assets/.agents/skills/security/SKILL.md +9 -2
  20. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +12 -7
  21. package/corpus/templates/assets/CHANGELOG.md +7 -0
  22. package/corpus/templates/assets/README.md +2 -2
  23. package/corpus/templates/assets/package.json +1 -0
  24. package/corpus/templates/assets/server/plugins/auth.ts +2 -1
  25. package/corpus/templates/assets/server/plugins/otel.ts +6 -0
  26. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +2 -1
  27. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +5 -4
  28. package/corpus/templates/brain/.agents/skills/security/SKILL.md +9 -2
  29. package/corpus/templates/brain/.agents/skills/storing-data/SKILL.md +12 -7
  30. package/corpus/templates/brain/CHANGELOG.md +6 -0
  31. package/corpus/templates/brain/README.md +12 -8
  32. package/corpus/templates/brain/changelog/2026-10-05-private-slack-channels-are-searchable-org-wide.md +6 -0
  33. package/corpus/templates/brain/package.json +1 -0
  34. package/corpus/templates/brain/server/lib/audiences.ts +1 -98
  35. package/corpus/templates/brain/server/lib/connectors.ts +2 -166
  36. package/corpus/templates/brain/server/plugins/otel.ts +6 -0
  37. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +9 -2
  38. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +12 -7
  39. package/corpus/templates/calendar/CHANGELOG.md +7 -0
  40. package/corpus/templates/calendar/README.md +2 -2
  41. package/corpus/templates/calendar/package.json +1 -0
  42. package/corpus/templates/calendar/server/plugins/auth.ts +2 -1
  43. package/corpus/templates/calendar/server/plugins/otel.ts +6 -0
  44. package/corpus/templates/chat/.agents/skills/security/SKILL.md +9 -2
  45. package/corpus/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
  46. package/corpus/templates/chat/README.md +2 -2
  47. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +9 -0
  48. package/corpus/templates/clips/.agents/skills/security/SKILL.md +9 -2
  49. package/corpus/templates/clips/CHANGELOG.md +24 -0
  50. package/corpus/templates/clips/README.md +2 -2
  51. package/corpus/templates/clips/actions/lib/audio-chunk-list.ts +23 -0
  52. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +187 -292
  53. package/corpus/templates/clips/actions/lib/chunked-transcription.ts +151 -0
  54. package/corpus/templates/clips/actions/lib/recording-audio-source.ts +191 -0
  55. package/corpus/templates/clips/actions/request-transcript.ts +81 -135
  56. package/corpus/templates/clips/actions/search-recordings.ts +19 -5
  57. package/corpus/templates/clips/app/components/clips-command-menu.tsx +5 -0
  58. package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -1
  59. package/corpus/templates/clips/app/components/library/search-bar.tsx +5 -0
  60. package/corpus/templates/clips/app/hooks/use-library.ts +1 -0
  61. package/corpus/templates/clips/app/lib/local-recording-upload.ts +13 -6
  62. package/corpus/templates/clips/app/lib/recording-section.ts +1 -1
  63. package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +7 -5
  64. package/corpus/templates/clips/app/routes/record.tsx +16 -6
  65. package/corpus/templates/clips/changelog/2026-10-02-long-uploads-an-hour-or-more-now-transcribe-reliably-instead.md +6 -0
  66. package/corpus/templates/clips/changelog/2026-10-05-confirming-discard-recording-now-returns-you-to-your-library.md +6 -0
  67. package/corpus/templates/clips/changelog/2026-10-05-discarding-a-recording-now-removes-its-failed-upload-card-fr.md +6 -0
  68. package/corpus/templates/clips/changelog/2026-10-05-discarding-a-recovered-recording-no-longer-warns-that-it-s-o.md +6 -0
  69. package/corpus/templates/clips/changelog/2026-10-05-search-results-identify-recordings-in-trash-and-open-them-wi.md +6 -0
  70. package/corpus/templates/clips/package.json +1 -0
  71. package/corpus/templates/clips/server/lib/builder-cdn.ts +5 -0
  72. package/corpus/templates/clips/server/lib/builder-media-compression.ts +2 -2
  73. package/corpus/templates/clips/server/plugins/auth.ts +2 -1
  74. package/corpus/templates/clips/server/plugins/otel.ts +6 -0
  75. package/corpus/templates/clips/shared/transcript-failure.ts +5 -0
  76. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +8 -2
  77. package/corpus/templates/content/.agents/skills/security/SKILL.md +9 -2
  78. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +12 -7
  79. package/corpus/templates/content/CHANGELOG.md +41 -0
  80. package/corpus/templates/content/README.md +2 -2
  81. package/corpus/templates/content/actions/_database-navigation.ts +523 -344
  82. package/corpus/templates/content/actions/_database-row-mutation.ts +44 -10
  83. package/corpus/templates/content/actions/_database-setup-discovery.ts +9 -3
  84. package/corpus/templates/content/actions/_database-utils.ts +299 -271
  85. package/corpus/templates/content/actions/_property-utils.ts +293 -190
  86. package/corpus/templates/content/actions/_relation-values.ts +3 -1
  87. package/corpus/templates/content/actions/get-content-database.ts +1 -0
  88. package/corpus/templates/content/actions/query-content-database-items.ts +32 -5
  89. package/corpus/templates/content/actions/set-document-property.ts +17 -11
  90. package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +21 -6
  91. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +127 -43
  92. package/corpus/templates/content/app/components/editor/DocumentEditorSkeleton.tsx +159 -50
  93. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +12 -28
  94. package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +6 -1
  95. package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +139 -70
  96. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +16 -1
  97. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +180 -75
  98. package/corpus/templates/content/app/components/editor/database/DatabaseViewSkeleton.tsx +76 -0
  99. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +60 -2
  100. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +41 -42
  101. package/corpus/templates/content/app/components/editor/document-editor-layout.ts +18 -0
  102. package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +11 -3
  103. package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +15 -7
  104. package/corpus/templates/content/app/components/editor/suggestions/model.ts +13 -0
  105. package/corpus/templates/content/app/components/editor/suggestions/read-only-blocks.ts +136 -0
  106. package/corpus/templates/content/app/components/editor/table-view.ts +73 -0
  107. package/corpus/templates/content/app/components/layout/ContentStartupShell.tsx +1 -1
  108. package/corpus/templates/content/app/components/layout/Layout.tsx +5 -1
  109. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +57 -16
  110. package/corpus/templates/content/app/components/sidebar/MovePageDialog.tsx +4 -4
  111. package/corpus/templates/content/app/components/sidebar/SidebarNavigationRow.tsx +34 -10
  112. package/corpus/templates/content/app/global.css +16 -8
  113. package/corpus/templates/content/app/hooks/content-action-refresh.ts +14 -13
  114. package/corpus/templates/content/app/hooks/use-content-database.ts +72 -9
  115. package/corpus/templates/content/app/hooks/use-content-personal-navigation.ts +7 -3
  116. package/corpus/templates/content/app/hooks/use-documents.ts +27 -0
  117. package/corpus/templates/content/app/i18n-data.ts +3 -2
  118. package/corpus/templates/content/app/lib/content-landing.ts +3 -31
  119. package/corpus/templates/content/app/lib/files-navigation.ts +182 -10
  120. package/corpus/templates/content/app/lib/files-root-hint.ts +20 -32
  121. package/corpus/templates/content/app/lib/page-startup-hints.ts +108 -0
  122. package/corpus/templates/content/app/lib/private-link-redirect.ts +25 -0
  123. package/corpus/templates/content/app/lib/sidebar-layout-hint.ts +34 -1
  124. package/corpus/templates/content/app/lib/startup-timing.ts +2 -0
  125. package/corpus/templates/content/app/root.tsx +3 -1
  126. package/corpus/templates/content/app/routes/_app.home.tsx +10 -19
  127. package/corpus/templates/content/app/routes/p.$id.tsx +23 -5
  128. package/corpus/templates/content/changelog/2026-10-02-a-page-link-you-cant-open-now-says-so-instead-of-opening-an.md +6 -0
  129. package/corpus/templates/content/changelog/2026-10-02-after-a-refresh-the-files-sidebar-shows-the-open-page-s-fold.md +6 -0
  130. package/corpus/templates/content/changelog/2026-10-02-large-collections-open-faster.md +6 -0
  131. package/corpus/templates/content/changelog/2026-10-02-pages-stay-inside-the-window-at-every-width-long-inline-code.md +6 -0
  132. package/corpus/templates/content/changelog/2026-10-02-pages-with-open-comments-and-collections-with-sorted-or-filt.md +6 -0
  133. package/corpus/templates/content/changelog/2026-10-02-suggest-edits-works-on-pages-written-by-agents-including-pag.md +6 -0
  134. package/corpus/templates/content/changelog/2026-10-02-while-you-suggest-edits-tables-and-other-blocks-that-suggest.md +6 -0
  135. package/corpus/templates/content/changelog/2026-10-05-collection-view-context-now-survives-quick-page-changes-when.md +6 -0
  136. package/corpus/templates/content/changelog/2026-10-05-collection-views-keep-their-saved-rows-accurate-during-refre.md +6 -0
  137. package/corpus/templates/content/changelog/2026-10-05-exact-saved-view-links-keep-collection-retry-available.md +6 -0
  138. package/corpus/templates/content/changelog/2026-10-05-expanding-a-files-folder-now-shares-one-navigation-read-acro.md +6 -0
  139. package/corpus/templates/content/changelog/2026-10-05-files-navigation-retries-temporary-failures-consistently.md +6 -0
  140. package/corpus/templates/content/changelog/2026-10-05-shared-page-owners-keep-edit-and-manage-controls-in-files-an.md +6 -0
  141. package/corpus/templates/content/changelog/2026-10-05-tagging-an-ai-in-a-comment-works-again-instead-of-failing-ri.md +6 -0
  142. package/corpus/templates/content/changelog/2026-10-05-the-home-loading-shell-keeps-the-layout-of-your-last-opened-.md +6 -0
  143. package/corpus/templates/content/changelog/2026-10-05-typing-and-a-space-or-inserting-a-toggle-or-callout-no-longe.md +6 -0
  144. package/corpus/templates/content/docs/product/capabilities/content.workspace.session-restore.md +4 -1
  145. package/corpus/templates/content/e2e/playwright.config.ts +1 -1
  146. package/corpus/templates/content/package.json +2 -1
  147. package/corpus/templates/content/scripts/seed-perf-database.mjs +600 -0
  148. package/corpus/templates/content/scripts/trace-startup.mjs +25 -3
  149. package/corpus/templates/content/server/db/index.ts +1 -1
  150. package/corpus/templates/content/server/lib/suggested-edits.ts +23 -26
  151. package/corpus/templates/content/server/plugins/auth.ts +2 -1
  152. package/corpus/templates/content/server/plugins/otel.ts +6 -0
  153. package/corpus/templates/content/shared/api.ts +17 -0
  154. package/corpus/templates/content/shared/suggestion-diff.ts +124 -38
  155. package/corpus/templates/content/shared/suggestion-formatting.ts +134 -3
  156. package/corpus/templates/crm/.agents/skills/security/SKILL.md +9 -2
  157. package/corpus/templates/crm/.agents/skills/storing-data/SKILL.md +12 -7
  158. package/corpus/templates/crm/package.json +1 -0
  159. package/corpus/templates/crm/server/plugins/otel.ts +6 -0
  160. package/corpus/templates/design/.agents/skills/security/SKILL.md +9 -2
  161. package/corpus/templates/design/.agents/skills/storing-data/SKILL.md +12 -7
  162. package/corpus/templates/design/CHANGELOG.md +41 -0
  163. package/corpus/templates/design/README.md +2 -2
  164. package/corpus/templates/design/actions/list-designs.ts +11 -4
  165. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +110 -32
  166. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +28 -0
  167. package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -0
  168. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +79 -0
  169. package/corpus/templates/design/app/components/layout/Header.tsx +2 -4
  170. package/corpus/templates/design/app/components/ui/tabs.tsx +59 -1
  171. package/corpus/templates/design/app/global.css +1 -34
  172. package/corpus/templates/design/app/i18n/en-US.ts +4 -0
  173. package/corpus/templates/design/app/lib/design-filter.ts +4 -2
  174. package/corpus/templates/design/app/pages/Index.tsx +91 -59
  175. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +58 -17
  176. package/corpus/templates/design/changelog/2026-10-01-filter-designs-by-owner.md +6 -0
  177. package/corpus/templates/design/changelog/2026-10-03-layers-dropped-into-visible-canvas-space-beyond-a-screen-now.md +6 -0
  178. package/corpus/templates/design/changelog/2026-10-03-visible-screen-overflow-no-longer-routes-drops-to-the-canvas.md +6 -0
  179. package/corpus/templates/design/changelog/2026-10-05-the-recent-tab-shows-loading-placeholders-until-your-designs.md +6 -0
  180. package/corpus/templates/design/package.json +1 -0
  181. package/corpus/templates/design/server/plugins/auth.ts +2 -1
  182. package/corpus/templates/design/server/plugins/otel.ts +6 -0
  183. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +9 -2
  184. package/corpus/templates/dispatch/.agents/skills/storing-data/SKILL.md +12 -7
  185. package/corpus/templates/dispatch/README.md +2 -2
  186. package/corpus/templates/dispatch/package.json +1 -0
  187. package/corpus/templates/dispatch/server/plugins/otel.ts +6 -0
  188. package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +2 -1
  189. package/corpus/templates/factory/.agents/skills/security/SKILL.md +9 -2
  190. package/corpus/templates/factory/.agents/skills/storing-data/SKILL.md +12 -7
  191. package/corpus/templates/factory/package.json +1 -0
  192. package/corpus/templates/factory/server/plugins/otel.ts +6 -0
  193. package/corpus/templates/forms/.agents/skills/security/SKILL.md +9 -2
  194. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +12 -7
  195. package/corpus/templates/forms/README.md +2 -2
  196. package/corpus/templates/forms/package.json +1 -0
  197. package/corpus/templates/forms/server/plugins/auth.ts +2 -1
  198. package/corpus/templates/forms/server/plugins/otel.ts +6 -0
  199. package/corpus/templates/mail/.agents/skills/security/SKILL.md +9 -2
  200. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +12 -7
  201. package/corpus/templates/mail/CHANGELOG.md +6 -0
  202. package/corpus/templates/mail/README.md +2 -2
  203. package/corpus/templates/mail/package.json +1 -0
  204. package/corpus/templates/mail/server/plugins/auth.ts +2 -1
  205. package/corpus/templates/mail/server/plugins/otel.ts +6 -0
  206. package/corpus/templates/plan/.agents/skills/security/SKILL.md +9 -2
  207. package/corpus/templates/plan/.agents/skills/storing-data/SKILL.md +12 -7
  208. package/corpus/templates/plan/CHANGELOG.md +1 -0
  209. package/corpus/templates/plan/README.md +2 -2
  210. package/corpus/templates/plan/package.json +1 -0
  211. package/corpus/templates/plan/server/plugins/auth.ts +2 -1
  212. package/corpus/templates/plan/server/plugins/otel.ts +6 -0
  213. package/corpus/templates/slides/.agents/skills/security/SKILL.md +9 -2
  214. package/corpus/templates/slides/.agents/skills/storing-data/SKILL.md +12 -7
  215. package/corpus/templates/slides/CHANGELOG.md +23 -0
  216. package/corpus/templates/slides/README.md +2 -2
  217. package/corpus/templates/slides/actions/list-decks.ts +11 -4
  218. package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +44 -36
  219. package/corpus/templates/slides/app/components/home/SlidesHomeLibrary.tsx +79 -0
  220. package/corpus/templates/slides/app/components/layout/Header.tsx +2 -15
  221. package/corpus/templates/slides/app/components/ui/tabs.tsx +59 -0
  222. package/corpus/templates/slides/app/global.css +3 -54
  223. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  224. package/corpus/templates/slides/app/lib/deck-filter.ts +5 -2
  225. package/corpus/templates/slides/app/pages/Index.tsx +102 -88
  226. package/corpus/templates/slides/changelog/2026-10-01-filter-decks-by-owner.md +6 -0
  227. package/corpus/templates/slides/changelog/2026-10-02-hide-recent-until-decks-available.md +6 -0
  228. package/corpus/templates/slides/changelog/2026-10-05-the-recent-tab-shows-loading-placeholders-until-your-decks-a.md +6 -0
  229. package/corpus/templates/slides/package.json +1 -0
  230. package/corpus/templates/slides/server/handlers/request-auth-context.ts +8 -0
  231. package/corpus/templates/slides/server/plugins/auth.ts +2 -1
  232. package/corpus/templates/slides/server/plugins/otel.ts +6 -0
  233. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +9 -2
  234. package/corpus/templates/tasks/.agents/skills/storing-data/SKILL.md +12 -7
  235. package/package.json +1 -1
@@ -1,5 +1,4 @@
1
1
  import { useCodeMode } from "@agent-native/core/client/agent-chat";
2
- import { agentNativePath } from "@agent-native/core/client/api-path";
3
2
  import {
4
3
  getBrowserTabId,
5
4
  setClientAppState,
@@ -229,6 +228,8 @@ import {
229
228
  } from "@/hooks/use-documents";
230
229
  import { useLocalStorage } from "@/hooks/use-local-storage";
231
230
  import { messagesByLocale } from "@/i18n-data";
231
+ import { readPageShapeHint, rememberPageShape } from "@/lib/page-startup-hints";
232
+ import { startupAnchor } from "@/lib/startup-timing";
232
233
  import { cn } from "@/lib/utils";
233
234
 
234
235
  import { resolveBuilderCmsWriteEffect } from "../../../../actions/_builder-cms-write-adapter.js";
@@ -299,9 +300,21 @@ import {
299
300
  DatabaseTableGrid,
300
301
  DatabaseTableLayout,
301
302
  } from "./DatabaseTableGrid";
303
+ import {
304
+ DATABASE_VIEW_CLASS_NAME,
305
+ DATABASE_VIEW_CONSTRAINTS_ROW_CLASS_NAME,
306
+ DATABASE_VIEW_TABS_ROW_CLASS_NAME,
307
+ DatabaseViewSkeleton,
308
+ } from "./DatabaseViewSkeleton";
302
309
  import { DatabaseFormView } from "./FormView";
303
310
  import { DatabaseGalleryView } from "./GalleryView";
304
311
  import { DatabaseListView } from "./ListView";
312
+ import {
313
+ DATABASE_NAVIGATION_VISIBLE_ITEM_LIMIT,
314
+ databaseNavigationStateFitsKeepaliveBudget,
315
+ databaseVisibleItemSummaries,
316
+ fitDatabaseNavigationState,
317
+ } from "./navigation-state";
305
318
  import {
306
319
  databaseItemCanDuplicate,
307
320
  databaseItemCanRemoveFromDatabase,
@@ -895,6 +908,7 @@ function DatabaseTable({
895
908
  const [hydratedDatabaseId, setHydratedDatabaseId] = useState<string | null>(
896
909
  null,
897
910
  );
911
+ const [drawnDatabaseId, setDrawnDatabaseId] = useState<string | null>(null);
898
912
  const [viewConfig, setViewConfig] = useState<ContentDatabaseViewConfig>(
899
913
  defaultDatabaseViewConfig(),
900
914
  );
@@ -985,7 +999,7 @@ function DatabaseTable({
985
999
  exactRequestedViewId,
986
1000
  searchParams.get(viewSelectionSearchParam),
987
1001
  );
988
- const personalViewDatabaseId = data?.database.id ?? null;
1002
+ const personalViewDatabaseId = databaseId;
989
1003
  const newDatabaseRowLabel = isWorkspaceCatalog
990
1004
  ? t("sidebar.addWorkspace")
991
1005
  : dbText("newPage");
@@ -1348,12 +1362,46 @@ function DatabaseTable({
1348
1362
  clientQueryExpandedItemLimit,
1349
1363
  data?.pagination?.limit ?? items.length,
1350
1364
  );
1365
+ // A failed read has no rows to expand, so calendar and timeline views would
1366
+ // otherwise count it as an expansion still pending and never draw.
1351
1367
  const isDatabaseViewLoading =
1352
- isDatabaseInitialLoading ||
1353
- isClientQueryExpansionPending ||
1354
- (database.isFetching && Boolean(tableQuery));
1368
+ !database.itemsFailed &&
1369
+ (isDatabaseInitialLoading ||
1370
+ isClientQueryExpansionPending ||
1371
+ (database.isFetching && Boolean(tableQuery)));
1372
+ // Until the saved view resolves and its first rows land, the view draws its
1373
+ // placeholder; after that, later reads keep the view on screen.
1374
+ const firstViewPending =
1375
+ drawnDatabaseId !== databaseId &&
1376
+ !(
1377
+ hydratedDatabaseId === databaseId &&
1378
+ database.itemsSettled &&
1379
+ !isDatabaseViewLoading
1380
+ );
1381
+ const constraintsBarHeld = databaseConstraintsBarIsVisible({
1382
+ forceShow: false,
1383
+ constraintCount: visibleConstraintCount,
1384
+ filterCount: filters.length,
1385
+ hasPersonalQueryChanges: personalQueryDirty,
1386
+ });
1355
1387
  useEffect(() => {
1356
- if (isDatabaseViewLoading) return;
1388
+ if (firstViewPending) return;
1389
+ setDrawnDatabaseId(databaseId);
1390
+ if (renderMode === "page") {
1391
+ rememberPageShape(
1392
+ document.id,
1393
+ constraintsBarHeld ? "database-constrained" : "database",
1394
+ );
1395
+ }
1396
+ }, [
1397
+ constraintsBarHeld,
1398
+ databaseId,
1399
+ document.id,
1400
+ firstViewPending,
1401
+ renderMode,
1402
+ ]);
1403
+ useEffect(() => {
1404
+ if (isDatabaseViewLoading || firstViewPending) return;
1357
1405
  window.document.documentElement.dataset.contentDatabaseRowsVisibleDocumentId =
1358
1406
  document.id;
1359
1407
  window.dispatchEvent(
@@ -1361,7 +1409,7 @@ function DatabaseTable({
1361
1409
  detail: { documentId: document.id },
1362
1410
  }),
1363
1411
  );
1364
- }, [document.id, isDatabaseViewLoading]);
1412
+ }, [document.id, firstViewPending, isDatabaseViewLoading]);
1365
1413
  useEffect(() => {
1366
1414
  if (clientQueryExpandedItemLimit === databaseRequestItemLimit) return;
1367
1415
  setDatabaseRequestItemLimit(clientQueryExpandedItemLimit);
@@ -1786,17 +1834,23 @@ function DatabaseTable({
1786
1834
  selectedItems,
1787
1835
  previewItem,
1788
1836
  });
1789
- fetch(
1790
- agentNativePath(
1791
- `/_agent-native/application-state/navigation:${getBrowserTabId()}`,
1792
- ),
1793
- {
1794
- method: "PUT",
1795
- keepalive: true,
1796
- headers: { "Content-Type": "application/json" },
1797
- body: JSON.stringify(state),
1798
- },
1799
- ).catch(() => {});
1837
+ // The route writes a bare navigation state to the same key from Root,
1838
+ // whose effects run after this one in the same commit. Writing after the
1839
+ // commit keeps this fuller state from being overwritten by it.
1840
+ const timer = setTimeout(() => {
1841
+ const navigationState = fitDatabaseNavigationState(state);
1842
+ void setClientAppState(
1843
+ `navigation:${getBrowserTabId()}`,
1844
+ navigationState,
1845
+ {
1846
+ keepalive:
1847
+ databaseNavigationStateFitsKeepaliveBudget(navigationState),
1848
+ },
1849
+ ).catch(() => {
1850
+ // Navigation sync is best-effort; the next view change rewrites it.
1851
+ });
1852
+ });
1853
+ return () => clearTimeout(timer);
1800
1854
  }, [
1801
1855
  activeView,
1802
1856
  effectiveFrozenColumnIds,
@@ -2796,21 +2850,28 @@ function DatabaseTable({
2796
2850
  orderedProperties,
2797
2851
  visibleItems,
2798
2852
  ]);
2853
+ // The page read already carries the saved views, so the view resolves
2854
+ // without waiting for the rows, and the first rows read is the saved view's.
2855
+ const viewSourceDatabase =
2856
+ data?.database ??
2857
+ (document.database?.id === expectedDatabaseId
2858
+ ? document.database
2859
+ : undefined);
2799
2860
  useEffect(() => {
2800
- if (!data?.database.id) return;
2861
+ if (!viewSourceDatabase) return;
2801
2862
  if (personalView.isLoading) return;
2802
2863
  const localSharedConfig = personalQueryDirty
2803
2864
  ? databaseViewConfigWithSavedQueryState(viewConfig, savedViewConfig)
2804
2865
  : viewConfig;
2805
2866
  if (
2806
- hydratedDatabaseIdRef.current === data.database.id &&
2807
- databaseViewStateKey(data.database.id, localSharedConfig) !==
2808
- databaseViewStateKey(data.database.id, savedViewConfig)
2867
+ hydratedDatabaseIdRef.current === viewSourceDatabase.id &&
2868
+ databaseViewStateKey(viewSourceDatabase.id, localSharedConfig) !==
2869
+ databaseViewStateKey(viewSourceDatabase.id, savedViewConfig)
2809
2870
  )
2810
2871
  return;
2811
- hydratedDatabaseIdRef.current = data.database.id;
2872
+ hydratedDatabaseIdRef.current = viewSourceDatabase.id;
2812
2873
  const nextSavedViewConfig = normalizeClientDatabaseViewConfig(
2813
- data.database.viewConfig,
2874
+ viewSourceDatabase.viewConfig,
2814
2875
  );
2815
2876
  const personalViewConfig = applyPersonalDatabaseViewOverrides(
2816
2877
  nextSavedViewConfig,
@@ -2838,12 +2899,12 @@ function DatabaseTable({
2838
2899
  );
2839
2900
  }
2840
2901
  const nextKey = databaseViewStateKey(
2841
- data.database.id,
2902
+ viewSourceDatabase.id,
2842
2903
  reconciled.viewConfig,
2843
2904
  );
2844
- setHydratedDatabaseId(data.database.id);
2845
- const mutationContract = data.mutationContract;
2846
- if (mutationContract && data.configurationRevision) {
2905
+ setHydratedDatabaseId(viewSourceDatabase.id);
2906
+ const mutationContract = data?.mutationContract;
2907
+ if (data && mutationContract && data.configurationRevision) {
2847
2908
  const { authorityScope: _authorityScope, ...target } =
2848
2909
  mutationContract.target;
2849
2910
  viewSetupRevisionRef.current = {
@@ -2865,13 +2926,14 @@ function DatabaseTable({
2865
2926
  ),
2866
2927
  );
2867
2928
  setViewConfig((current) =>
2868
- databaseViewStateKey(data.database.id, current) === nextKey
2929
+ databaseViewStateKey(viewSourceDatabase.id, current) === nextKey
2869
2930
  ? current
2870
2931
  : reconciled.viewConfig,
2871
2932
  );
2872
2933
  }, [
2934
+ viewSourceDatabase?.id,
2935
+ viewSourceDatabase?.viewConfig,
2873
2936
  data?.database.id,
2874
- data?.database.viewConfig,
2875
2937
  data?.configurationRevision,
2876
2938
  data?.mutationContract,
2877
2939
  personalView.data?.overrides,
@@ -2934,15 +2996,10 @@ function DatabaseTable({
2934
2996
  viewConfig,
2935
2997
  ]);
2936
2998
 
2937
- const exactViewUnavailable =
2938
- !!exactRequestedViewId &&
2939
- (database.isError ||
2940
- isContentDatabaseUnavailable(database.data) ||
2941
- (!!data &&
2942
- !resolveRequestedDatabaseView(
2943
- normalizeClientDatabaseViewConfig(data.database.viewConfig),
2944
- exactRequestedViewId,
2945
- )));
2999
+ const exactViewUnavailable = exactRequestedDatabaseViewUnavailable(
3000
+ exactRequestedViewId,
3001
+ database.data,
3002
+ );
2946
3003
  useRecordContentVisit(
2947
3004
  { documentId: document.id, databaseId, viewId: activeView.id },
2948
3005
  foreground &&
@@ -3032,9 +3089,38 @@ function DatabaseTable({
3032
3089
  );
3033
3090
  }
3034
3091
 
3092
+ if (firstViewPending) {
3093
+ // An exact view the page read does not list resolves only from the
3094
+ // collection read, so a failed first read would otherwise hold the
3095
+ // placeholder forever.
3096
+ if (database.itemsFailed && !personalView.isLoading) {
3097
+ return (
3098
+ <QueryErrorState
3099
+ compact
3100
+ onRetry={() => void database.retryItems()}
3101
+ retrying={database.itemsRetrying}
3102
+ />
3103
+ );
3104
+ }
3105
+ return (
3106
+ <DatabaseViewSkeleton
3107
+ anchored={renderMode === "page"}
3108
+ constraints={
3109
+ hydratedDatabaseId === databaseId
3110
+ ? constraintsBarHeld
3111
+ : renderMode === "page" &&
3112
+ readPageShapeHint(document.id) === "database-constrained"
3113
+ }
3114
+ />
3115
+ );
3116
+ }
3117
+
3035
3118
  return (
3036
- <div className="mt-4 min-w-0 w-full max-w-[calc(100vw-var(--content-sidebar-width,0px)-1.5rem)]">
3037
- <div className="mb-1 flex min-h-8 flex-wrap items-center justify-between gap-x-3 gap-y-1 pb-1">
3119
+ <div className={DATABASE_VIEW_CLASS_NAME}>
3120
+ <div
3121
+ {...(renderMode === "page" ? startupAnchor("database-tabs") : {})}
3122
+ className={DATABASE_VIEW_TABS_ROW_CLASS_NAME}
3123
+ >
3038
3124
  <DatabaseViewTabs
3039
3125
  assetScopeDocumentId={databaseDocumentId}
3040
3126
  viewConfig={viewConfig}
@@ -3293,7 +3379,13 @@ function DatabaseTable({
3293
3379
  );
3294
3380
  })}
3295
3381
 
3296
- {activeView.type === "form" ? (
3382
+ {database.itemsFailed ? (
3383
+ <QueryErrorState
3384
+ compact
3385
+ onRetry={() => void database.retryItems()}
3386
+ retrying={database.itemsRetrying}
3387
+ />
3388
+ ) : activeView.type === "form" ? (
3297
3389
  <DatabaseFormView
3298
3390
  databaseId={databaseId}
3299
3391
  databaseDocumentId={document.id}
@@ -3472,6 +3564,7 @@ function DatabaseTable({
3472
3564
  />
3473
3565
  ) : (
3474
3566
  <DatabaseTableView
3567
+ startupAnchored={renderMode === "page"}
3475
3568
  columnOrderIds={activeView.tableColumnOrderIds ?? []}
3476
3569
  databaseId={databaseId}
3477
3570
  databaseSystemRole={data?.database.systemRole}
@@ -3565,7 +3658,9 @@ function DatabaseTable({
3565
3658
  />
3566
3659
  )}
3567
3660
 
3568
- {hasMoreItems && !isClientQueryExpansionPending ? (
3661
+ {hasMoreItems &&
3662
+ !isClientQueryExpansionPending &&
3663
+ !database.itemsFailed ? (
3569
3664
  <div className="flex items-center justify-center border-t border-border/45 py-3">
3570
3665
  <Button
3571
3666
  type="button"
@@ -4056,33 +4151,7 @@ export function databaseViewSummaries(
4056
4151
  }));
4057
4152
  }
4058
4153
 
4059
- export const DATABASE_NAVIGATION_VISIBLE_ITEM_LIMIT = 50;
4060
-
4061
- export function databaseVisibleItemSummaries(
4062
- items: ContentDatabaseItem[],
4063
- visibleProperties: DocumentProperty[] = [],
4064
- limit = DATABASE_NAVIGATION_VISIBLE_ITEM_LIMIT,
4065
- ) {
4066
- return items.slice(0, limit).map((item) => ({
4067
- itemId: item.id,
4068
- documentId: item.document.id,
4069
- title: databaseItemPreviewTitle(item),
4070
- position: item.position,
4071
- properties: visibleProperties.map((property) => {
4072
- const itemProperty =
4073
- item.properties.find(
4074
- (candidate) => candidate.definition.id === property.definition.id,
4075
- ) ?? property;
4076
- return {
4077
- propertyId: property.definition.id,
4078
- name: property.definition.name,
4079
- type: property.definition.type,
4080
- value: itemProperty.value,
4081
- text: propertyValueText(itemProperty),
4082
- };
4083
- }),
4084
- }));
4085
- }
4154
+ export { DATABASE_NAVIGATION_VISIBLE_ITEM_LIMIT, databaseVisibleItemSummaries };
4086
4155
 
4087
4156
  export function databaseCalculationSummaries(
4088
4157
  calculations: Record<string, DatabaseColumnCalculation> | undefined,
@@ -5159,6 +5228,7 @@ function DatabaseTableView({
5159
5228
  hasSearch,
5160
5229
  totalCount,
5161
5230
  constrained,
5231
+ startupAnchored,
5162
5232
  rowsAreManuallyOrdered,
5163
5233
  wrapCells,
5164
5234
  columnWrapOverrides,
@@ -5219,6 +5289,7 @@ function DatabaseTableView({
5219
5289
  hasSearch: boolean;
5220
5290
  totalCount: number;
5221
5291
  constrained: boolean;
5292
+ startupAnchored: boolean;
5222
5293
  rowsAreManuallyOrdered: boolean;
5223
5294
  wrapCells: boolean;
5224
5295
  columnWrapOverrides: Record<string, boolean>;
@@ -5843,6 +5914,7 @@ function DatabaseTableView({
5843
5914
  >
5844
5915
  <DatabaseTableColumnOrder.Provider value={columnOrderIds}>
5845
5916
  <div
5917
+ {...(startupAnchored ? startupAnchor("database-table") : {})}
5846
5918
  ref={tableViewportRef}
5847
5919
  className="relative w-full min-w-0 max-w-full"
5848
5920
  >
@@ -6229,6 +6301,20 @@ export function databaseViewRequiresCompleteClientDataset(args: {
6229
6301
  );
6230
6302
  }
6231
6303
 
6304
+ export function databaseConstraintsBarIsVisible(args: {
6305
+ forceShow: boolean;
6306
+ constraintCount: number;
6307
+ filterCount: number;
6308
+ hasPersonalQueryChanges: boolean;
6309
+ }) {
6310
+ return (
6311
+ args.forceShow ||
6312
+ args.constraintCount > 0 ||
6313
+ args.filterCount > 0 ||
6314
+ args.hasPersonalQueryChanges
6315
+ );
6316
+ }
6317
+
6232
6318
  function DatabaseActiveConstraintsBar({
6233
6319
  documentId,
6234
6320
  items,
@@ -6290,10 +6376,12 @@ function DatabaseActiveConstraintsBar({
6290
6376
  }) {
6291
6377
  const filterEntries = filters.map((filter, index) => ({ filter, index }));
6292
6378
  if (
6293
- !forceShow &&
6294
- constraintCount === 0 &&
6295
- filterEntries.length === 0 &&
6296
- !hasPersonalQueryChanges
6379
+ !databaseConstraintsBarIsVisible({
6380
+ forceShow,
6381
+ constraintCount,
6382
+ filterCount: filters.length,
6383
+ hasPersonalQueryChanges,
6384
+ })
6297
6385
  )
6298
6386
  return null;
6299
6387
  const hasSearchOrSortConstraints =
@@ -6308,7 +6396,9 @@ function DatabaseActiveConstraintsBar({
6308
6396
  const hasSortFilterDivider = sorts.length > 0 && filterEntries.length > 0;
6309
6397
 
6310
6398
  return (
6311
- <ContentTableConstraintBar className="min-h-8 gap-1 py-0.5 text-muted-foreground">
6399
+ <ContentTableConstraintBar
6400
+ className={DATABASE_VIEW_CONSTRAINTS_ROW_CLASS_NAME}
6401
+ >
6312
6402
  {sorts.map((sort, index) => (
6313
6403
  <DatabaseInlineSortControl
6314
6404
  key={`${sort.key}-${index}`}
@@ -13616,6 +13706,21 @@ export function resolveRequestedDatabaseView(
13616
13706
  return selectDatabaseView(config, requestedViewId);
13617
13707
  }
13618
13708
 
13709
+ export function exactRequestedDatabaseViewUnavailable(
13710
+ requestedViewId: string | null | undefined,
13711
+ data: ContentDatabaseResponse | undefined,
13712
+ ): boolean {
13713
+ return (
13714
+ !!requestedViewId &&
13715
+ (isContentDatabaseUnavailable(data) ||
13716
+ (!!data &&
13717
+ !resolveRequestedDatabaseView(
13718
+ normalizeClientDatabaseViewConfig(data.database.viewConfig),
13719
+ requestedViewId,
13720
+ )))
13721
+ );
13722
+ }
13723
+
13619
13724
  export function addDatabaseView(
13620
13725
  config: ContentDatabaseViewConfig,
13621
13726
  name: string,
@@ -0,0 +1,76 @@
1
+ import { Skeleton } from "@/components/ui/skeleton";
2
+ import { startupAnchor } from "@/lib/startup-timing";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ export const DATABASE_VIEW_CLASS_NAME =
6
+ "mt-4 min-w-0 w-full max-w-[calc(100vw-var(--content-sidebar-width,0px)-1.5rem)]";
7
+
8
+ export const DATABASE_VIEW_TABS_ROW_CLASS_NAME =
9
+ "mb-1 flex min-h-8 flex-wrap items-center justify-between gap-x-3 gap-y-1 pb-1";
10
+
11
+ export const DATABASE_VIEW_CONSTRAINTS_ROW_CLASS_NAME =
12
+ "min-h-8 gap-1 py-0.5 text-muted-foreground";
13
+
14
+ const STARTUP_CONSTRAINTS_ROW_CLASS_NAME =
15
+ "hidden [html[data-content-page-shape=database-constrained]_&]:flex";
16
+
17
+ const PLACEHOLDER_ROW_WIDTHS = ["w-2/5", "w-1/3", "w-1/2", "w-1/4", "w-2/5"];
18
+
19
+ // A collection draws these boxes until its saved view and that view's first
20
+ // rows are in, so the tabs, the sort and filter row, and the table sit where
21
+ // the view will draw them. Rows from before the saved order arrives would
22
+ // draw in stored order and then jump.
23
+ export function DatabaseViewSkeleton({
24
+ constraints,
25
+ anchored = true,
26
+ }: {
27
+ constraints: boolean | "startup";
28
+ anchored?: boolean;
29
+ }) {
30
+ return (
31
+ <div className={DATABASE_VIEW_CLASS_NAME}>
32
+ <div
33
+ {...(anchored ? startupAnchor("database-tabs") : {})}
34
+ className={DATABASE_VIEW_TABS_ROW_CLASS_NAME}
35
+ >
36
+ <Skeleton className="h-5 w-24" />
37
+ <div className="flex min-h-8 items-center justify-end gap-1">
38
+ <Skeleton className="size-7 rounded-md" />
39
+ <Skeleton className="size-7 rounded-md" />
40
+ <Skeleton className="size-7 rounded-md" />
41
+ <Skeleton className="h-7 w-14 rounded-md" />
42
+ </div>
43
+ </div>
44
+ {constraints ? (
45
+ <div
46
+ className={cn(
47
+ "flex min-w-0 items-center",
48
+ DATABASE_VIEW_CONSTRAINTS_ROW_CLASS_NAME,
49
+ constraints === "startup" && STARTUP_CONSTRAINTS_ROW_CLASS_NAME,
50
+ )}
51
+ >
52
+ <Skeleton className="h-7 w-28 rounded" />
53
+ <Skeleton className="h-7 w-32 rounded" />
54
+ </div>
55
+ ) : null}
56
+ <div
57
+ {...(anchored ? startupAnchor("database-table") : {})}
58
+ className="w-full min-w-0 border-t border-border/35"
59
+ >
60
+ <div className="flex h-8 items-center gap-6 border-b border-border/35 px-2">
61
+ <Skeleton className="h-3 w-16" />
62
+ <Skeleton className="h-3 w-20" />
63
+ <Skeleton className="h-3 w-14" />
64
+ </div>
65
+ {PLACEHOLDER_ROW_WIDTHS.map((width, index) => (
66
+ <div
67
+ key={index}
68
+ className="flex h-9 items-center border-b border-border/35 px-2"
69
+ >
70
+ <Skeleton className={cn("h-3.5", width)} />
71
+ </div>
72
+ ))}
73
+ </div>
74
+ </div>
75
+ );
76
+ }
@@ -39,6 +39,12 @@ export function databaseViewSummaries(
39
39
  }
40
40
 
41
41
  export const DATABASE_NAVIGATION_VISIBLE_ITEM_LIMIT = 50;
42
+ export const DATABASE_NAVIGATION_CELL_TEXT_LIMIT = 200;
43
+ /**
44
+ * Navigation state is rewritten on every table change and read into the
45
+ * agent's context on every turn.
46
+ */
47
+ export const DATABASE_NAVIGATION_STATE_MAX_BYTES = 48 * 1024;
42
48
 
43
49
  export function databaseVisibleItemSummaries(
44
50
  items: ContentDatabaseItem[],
@@ -55,17 +61,69 @@ export function databaseVisibleItemSummaries(
55
61
  item.properties.find(
56
62
  (candidate) => candidate.definition.id === property.definition.id,
57
63
  ) ?? property;
64
+ const text = propertyValueText(itemProperty);
65
+ const truncated = text.length > DATABASE_NAVIGATION_CELL_TEXT_LIMIT;
66
+ const value = itemProperty.value;
67
+ const valueRepeatsText =
68
+ (value === null || typeof value !== "object") &&
69
+ String(value ?? "") === text;
58
70
  return {
59
71
  propertyId: property.definition.id,
60
72
  name: property.definition.name,
61
73
  type: property.definition.type,
62
- value: itemProperty.value,
63
- text: propertyValueText(itemProperty),
74
+ ...(valueRepeatsText ? {} : { value }),
75
+ text: truncated
76
+ ? `${text.slice(0, DATABASE_NAVIGATION_CELL_TEXT_LIMIT)}…`
77
+ : text,
78
+ ...(truncated ? { textTruncated: true } : {}),
64
79
  };
65
80
  }),
66
81
  }));
67
82
  }
68
83
 
84
+ function jsonByteLength(value: unknown) {
85
+ return new TextEncoder().encode(JSON.stringify(value)).length;
86
+ }
87
+
88
+ export function databaseNavigationStateFitsKeepaliveBudget(value: unknown) {
89
+ return jsonByteLength(value) <= DATABASE_NAVIGATION_STATE_MAX_BYTES;
90
+ }
91
+
92
+ /**
93
+ * Keep the largest row prefix whose state fits the byte budget, and report
94
+ * the applied cap in `databaseVisibleItemLimit` so a trimmed summary is never
95
+ * mistaken for the whole visible slice. Rows are the only part that grows
96
+ * with the collection. The view's own settings stay whole even when they
97
+ * alone pass the budget: dropping a sort or filter would describe a
98
+ * different view to the agent.
99
+ */
100
+ export function fitDatabaseNavigationState<
101
+ State extends {
102
+ databaseVisibleItems?: unknown[];
103
+ databaseSelectedItems?: unknown[];
104
+ databaseVisibleItemLimit?: number;
105
+ },
106
+ >(state: State, maxBytes = DATABASE_NAVIGATION_STATE_MAX_BYTES): State {
107
+ if (jsonByteLength(state) <= maxBytes) return state;
108
+ const withRowCap = (cap: number): State => ({
109
+ ...state,
110
+ databaseVisibleItems: state.databaseVisibleItems?.slice(0, cap),
111
+ databaseSelectedItems: state.databaseSelectedItems?.slice(0, cap),
112
+ databaseVisibleItemLimit: cap,
113
+ });
114
+ let fits = 0;
115
+ let tooBig = Math.max(
116
+ state.databaseVisibleItems?.length ?? 0,
117
+ state.databaseSelectedItems?.length ?? 0,
118
+ );
119
+ while (tooBig - fits > 1) {
120
+ const cap = Math.floor((fits + tooBig) / 2);
121
+ if (jsonByteLength(withRowCap(cap)) <= maxBytes) fits = cap;
122
+ else tooBig = cap;
123
+ }
124
+ return withRowCap(fits);
125
+ }
126
+
69
127
  export function databaseNavigationState({
70
128
  document,
71
129
  databaseId,