@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
@@ -130,7 +130,10 @@ import {
130
130
  getDesktopContentFiles,
131
131
  type DesktopContentFilesFolder,
132
132
  } from "@/lib/desktop-content-files";
133
- import { filesNavigationOrder } from "@/lib/files-navigation";
133
+ import {
134
+ filesNavigationOrder,
135
+ openFilesFolderIds,
136
+ } from "@/lib/files-navigation";
134
137
  import {
135
138
  filesRootHintScope,
136
139
  prefetchPagedFilesRoot,
@@ -150,6 +153,7 @@ import {
150
153
  import {
151
154
  readSidebarLayoutHint,
152
155
  rememberSidebarLayout,
156
+ withShownFilesBranch,
153
157
  type SidebarLayoutHint,
154
158
  type SidebarRowsHint,
155
159
  } from "@/lib/sidebar-layout-hint";
@@ -445,6 +449,8 @@ function WorkspaceSidebarItem({
445
449
  compact = false,
446
450
  filesPlaceholder,
447
451
  onFilesRootShown,
452
+ filesBranchPlaceholders,
453
+ onFilesBranchShown,
448
454
  }: {
449
455
  space: ContentSpaceSummary;
450
456
  selected: boolean;
@@ -476,6 +482,8 @@ function WorkspaceSidebarItem({
476
482
  compact?: boolean;
477
483
  filesPlaceholder?: SidebarRowsHint;
478
484
  onFilesRootShown?: (shown: SidebarRowsHint) => void;
485
+ filesBranchPlaceholders?: Readonly<Record<string, SidebarRowsHint>>;
486
+ onFilesBranchShown?: (documentId: string, shown: SidebarRowsHint) => void;
479
487
  }) {
480
488
  const t = useT();
481
489
  const [localWorkingCopies, setLocalWorkingCopies] = useState<
@@ -647,22 +655,13 @@ function WorkspaceSidebarItem({
647
655
  const queryClient = useQueryClient();
648
656
  const { session } = useSession();
649
657
  const filesRootScope = filesRootHintScope(session?.email, session?.orgId);
650
- const filesRootConfirmed =
651
- !localFileMode && expanded && filesPersonalView.isSuccess;
658
+ const filesRootConfirmed = !localFileMode && expanded;
652
659
  useEffect(() => {
653
660
  if (!filesRootScope || !filesRootConfirmed) return;
654
661
  rememberPagedFilesRoot(filesRootScope, {
655
662
  databaseId: space.filesDatabaseId,
656
- sort: sidebarOrder.mode,
657
- viewId: activeViewId,
658
663
  });
659
- }, [
660
- activeViewId,
661
- filesRootConfirmed,
662
- filesRootScope,
663
- sidebarOrder.mode,
664
- space.filesDatabaseId,
665
- ]);
664
+ }, [filesRootConfirmed, filesRootScope, space.filesDatabaseId]);
666
665
  const reorderLabels: SidebarReorderLabels = {
667
666
  drag: (label) => t("sidebar.dragToReorder", { label }),
668
667
  moveUp: t("sidebar.moveUp"),
@@ -957,8 +956,6 @@ function WorkspaceSidebarItem({
957
956
  ) : (
958
957
  <PagedContentFilesSidebarView
959
958
  databaseId={space.filesDatabaseId}
960
- sort={sidebarOrder.mode}
961
- viewId={activeViewId}
962
959
  activeDocumentId={activeDocumentId}
963
960
  expandedDocumentIds={expandedDocumentIds}
964
961
  onDocumentExpandedChange={onDocumentExpandedChange}
@@ -986,6 +983,8 @@ function WorkspaceSidebarItem({
986
983
  untitledLabel={t("sidebar.untitled")}
987
984
  rootPlaceholder={filesPlaceholder}
988
985
  onRootPageShown={onFilesRootShown}
986
+ branchPlaceholders={filesBranchPlaceholders}
987
+ onBranchShown={onFilesBranchShown}
989
988
  />
990
989
  )}
991
990
  </div>
@@ -1033,7 +1032,9 @@ export function DocumentSidebar({
1033
1032
  useEffect(() => {
1034
1033
  if (!filesRootScope) return;
1035
1034
  const root = readPagedFilesRootHint(filesRootScope);
1036
- if (root) prefetchPagedFilesRoot(queryClient, root);
1035
+ if (!root) return;
1036
+ const { branches } = readSidebarLayoutHint(filesRootScope, null);
1037
+ prefetchPagedFilesRoot(queryClient, root, Object.keys(branches ?? {}));
1037
1038
  }, [filesRootScope, queryClient]);
1038
1039
  const localFileMode = contentSpacesQuery.data?.sourceMode === "local-files";
1039
1040
  const documentsQuery = useDocuments({ enabled: localFileMode });
@@ -1617,9 +1618,47 @@ export function DocumentSidebar({
1617
1618
  return ids;
1618
1619
  }, [activeDocumentId, navigationContextQuery.data?.path]);
1619
1620
  const visibleExpandedDocumentIds = useMemo(
1620
- () => new Set([...expandedDocumentIds, ...activeAncestorIds]),
1621
+ () => openFilesFolderIds(activeAncestorIds, expandedDocumentIds),
1621
1622
  [activeAncestorIds, expandedDocumentIds],
1622
1623
  );
1624
+ // Open folders are kept by ID with the rows they drew, so the next load
1625
+ // reads them with the tree and holds their rows. Until the stored open
1626
+ // folders arrive, only the active page's ancestors are known to be open, so
1627
+ // folders are dropped from the hint only after that.
1628
+ const rememberFilesBranches = useCallback(
1629
+ (shown?: { documentId: string; rows: SidebarRowsHint }) => {
1630
+ const current = readSidebarLayoutHint(
1631
+ filesRootScope,
1632
+ selectedSpaceId,
1633
+ ).branches;
1634
+ const branches = shown
1635
+ ? withShownFilesBranch(current, shown.documentId, shown.rows)
1636
+ : { ...current };
1637
+ rememberLayout({
1638
+ branches: Object.fromEntries(
1639
+ Object.entries(branches).filter(
1640
+ ([documentId]) =>
1641
+ !sidebarStateHydratedRef.current ||
1642
+ visibleExpandedDocumentIds.has(documentId),
1643
+ ),
1644
+ ),
1645
+ });
1646
+ },
1647
+ [
1648
+ filesRootScope,
1649
+ rememberLayout,
1650
+ selectedSpaceId,
1651
+ visibleExpandedDocumentIds,
1652
+ ],
1653
+ );
1654
+ const rememberFilesBranch = useCallback(
1655
+ (documentId: string, rows: SidebarRowsHint) =>
1656
+ rememberFilesBranches({ documentId, rows }),
1657
+ [rememberFilesBranches],
1658
+ );
1659
+ useEffect(() => {
1660
+ if (sidebarStateHydratedRef.current) rememberFilesBranches();
1661
+ }, [rememberFilesBranches]);
1623
1662
 
1624
1663
  const expandedIds = new Set(expandedIdsRef.current);
1625
1664
  for (const id of activeAncestorIds) expandedIds.add(id);
@@ -2556,6 +2595,8 @@ export function DocumentSidebar({
2556
2595
  }
2557
2596
  filesPlaceholder={compact ? sidebarLayoutHint.files : undefined}
2558
2597
  onFilesRootShown={compact ? rememberFilesRoot : undefined}
2598
+ filesBranchPlaceholders={compact ? sidebarLayoutHint.branches : undefined}
2599
+ onFilesBranchShown={compact ? rememberFilesBranch : undefined}
2559
2600
  />
2560
2601
  );
2561
2602
 
@@ -26,6 +26,7 @@ import {
26
26
  DropdownMenuRadioItem,
27
27
  DropdownMenuTrigger,
28
28
  } from "@/components/ui/dropdown-menu";
29
+ import { filesNavigationPageParams } from "@/lib/files-navigation";
29
30
 
30
31
  import { SidebarRowIcon } from "./SidebarNavigationRow";
31
32
 
@@ -93,11 +94,10 @@ export function MovePageDialog({
93
94
 
94
95
  const roots = useActionQuery(
95
96
  "query-content-database-items",
96
- {
97
+ filesNavigationPageParams({
97
98
  databaseId: space?.filesDatabaseId ?? "",
98
- limit: 20,
99
- navigation: { parentId: null, sort: "custom" },
100
- },
99
+ parentId: null,
100
+ }),
101
101
  { enabled: open && query === "" },
102
102
  );
103
103
  const results = useActionQuery(
@@ -19,19 +19,40 @@ export function sidebarRowClassName(active = false) {
19
19
  export const sidebarShowMoreClassName =
20
20
  "grid h-7 w-full items-center gap-0 rounded p-0 pe-1.5 text-start text-xs font-medium text-muted-foreground hover:bg-transparent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring";
21
21
 
22
- export function revealActiveSidebarRow(row: HTMLElement | null) {
22
+ const SIDEBAR_ROW_PLACEHOLDER = "[data-sidebar-row-placeholder]";
23
+
24
+ // The open page scrolls into view in Files unless another section already
25
+ // shows it. Rows still loading may be that section, or may push the row once
26
+ // they arrive, so the check waits until the sidebar has no placeholder rows.
27
+ // Returns a cleanup that stops a check still waiting.
28
+ export function revealActiveSidebarRow(
29
+ row: HTMLElement | null,
30
+ ): (() => void) | undefined {
23
31
  const viewport = row?.closest<HTMLElement>(
24
32
  "[data-radix-scroll-area-viewport]",
25
33
  );
26
- if (!row || !viewport) return;
27
- const bounds = viewport.getBoundingClientRect();
28
- const alreadyVisible = Array.from(
29
- viewport.querySelectorAll<HTMLElement>('[aria-current="page"]'),
30
- ).some((element) => {
31
- const rect = element.getBoundingClientRect();
32
- return rect.bottom > bounds.top && rect.top < bounds.bottom;
34
+ if (!row || !viewport) return undefined;
35
+ const reveal = () => {
36
+ const bounds = viewport.getBoundingClientRect();
37
+ const alreadyVisible = Array.from(
38
+ viewport.querySelectorAll<HTMLElement>('[aria-current="page"]'),
39
+ ).some((element) => {
40
+ const rect = element.getBoundingClientRect();
41
+ return rect.bottom > bounds.top && rect.top < bounds.bottom;
42
+ });
43
+ if (!alreadyVisible) row.scrollIntoView({ block: "nearest" });
44
+ };
45
+ if (!viewport.querySelector(SIDEBAR_ROW_PLACEHOLDER)) {
46
+ reveal();
47
+ return undefined;
48
+ }
49
+ const observer = new MutationObserver(() => {
50
+ if (viewport.querySelector(SIDEBAR_ROW_PLACEHOLDER)) return;
51
+ observer.disconnect();
52
+ if (row.isConnected) reveal();
33
53
  });
34
- if (!alreadyVisible) row.scrollIntoView({ block: "nearest" });
54
+ observer.observe(viewport, { childList: true, subtree: true });
55
+ return () => observer.disconnect();
35
56
  }
36
57
 
37
58
  export function SidebarRowIcon({ icon }: { icon: ReactNode }) {
@@ -109,6 +130,7 @@ export function SidebarRowsSkeleton({
109
130
  <div
110
131
  key={index}
111
132
  aria-hidden="true"
133
+ data-sidebar-row-placeholder=""
112
134
  {...(index === 0 ? firstRowProps : {})}
113
135
  className="flex h-7 items-center gap-1.5 px-1.5"
114
136
  >
@@ -121,7 +143,9 @@ export function SidebarRowsSkeleton({
121
143
  />
122
144
  </div>
123
145
  ));
124
- const moreRow = more ? <div aria-hidden="true" className="h-7" /> : null;
146
+ const moreRow = more ? (
147
+ <div aria-hidden="true" data-sidebar-row-placeholder="" className="h-7" />
148
+ ) : null;
125
149
  if (!framed) {
126
150
  return (
127
151
  <>
@@ -121,10 +121,14 @@
121
121
  }
122
122
 
123
123
  pre,
124
- code {
124
+ pre code {
125
125
  overflow-x: auto;
126
126
  white-space: pre;
127
127
  }
128
+
129
+ code {
130
+ white-space: pre-wrap;
131
+ }
128
132
  }
129
133
 
130
134
  .notion-logo-icon {
@@ -476,7 +480,7 @@
476
480
  .notion-editor ul:not(.notion-task-list) > li,
477
481
  .notion-editor ol > li {
478
482
  display: grid;
479
- grid-template-columns: minmax(1rem, auto) 1fr;
483
+ grid-template-columns: minmax(1rem, auto) minmax(0, 1fr);
480
484
  column-gap: 0.5em;
481
485
  }
482
486
 
@@ -567,7 +571,7 @@
567
571
  }
568
572
  .notion-editor .notion-task-list li {
569
573
  display: grid;
570
- grid-template-columns: 1rem 1fr;
574
+ grid-template-columns: 1rem minmax(0, 1fr);
571
575
  column-gap: 0.5em;
572
576
  }
573
577
  .notion-editor .notion-task-list li label {
@@ -719,6 +723,7 @@
719
723
  margin: 0.6em 0;
720
724
  }
721
725
  .notion-editor pre code {
726
+ white-space: pre;
722
727
  font-family: inherit;
723
728
  background: none;
724
729
  padding: 0;
@@ -836,6 +841,7 @@
836
841
  border-spacing: 0;
837
842
  table-layout: fixed;
838
843
  width: 100% !important;
844
+ min-width: var(--content-table-min-width, 0px) !important;
839
845
  margin: 0;
840
846
  overflow: hidden;
841
847
  border-radius: var(--radius);
@@ -1063,7 +1069,7 @@
1063
1069
 
1064
1070
  div[data-notion-callout] {
1065
1071
  display: grid;
1066
- grid-template-columns: auto 1fr;
1072
+ grid-template-columns: auto minmax(0, 1fr);
1067
1073
  gap: 0.8rem;
1068
1074
  align-items: start;
1069
1075
  margin: 0.9em 0;
@@ -1430,7 +1436,7 @@ div[data-notion-column] > :last-child {
1430
1436
  display: flex;
1431
1437
  align-items: center;
1432
1438
  width: min(100%, 42rem);
1433
- min-width: min(18rem, calc(100vw - 3rem));
1439
+ min-width: min(18rem, 100%);
1434
1440
  min-height: 10rem;
1435
1441
  gap: 0.9rem;
1436
1442
  border: 1px dashed hsl(var(--border));
@@ -1478,7 +1484,7 @@ video.media-block__content {
1478
1484
 
1479
1485
  audio.media-block__content {
1480
1486
  width: min(40rem, calc(100vw - 2rem));
1481
- min-width: min(16.25rem, calc(100vw - 2rem));
1487
+ min-width: min(16.25rem, 100%);
1482
1488
  min-height: 54px;
1483
1489
  background: hsl(var(--muted));
1484
1490
  }
@@ -1611,12 +1617,14 @@ audio.media-block__content {
1611
1617
  outline: none;
1612
1618
  }
1613
1619
 
1620
+ /* Even hidden, a handle box that reaches past the page's narrowest side
1621
+ padding (16px) lets a full-width image scroll the page sideways. */
1614
1622
  .media-block__resize-handle--left {
1615
- left: -22px;
1623
+ left: -16px;
1616
1624
  }
1617
1625
 
1618
1626
  .media-block__resize-handle--right {
1619
- right: -22px;
1627
+ right: -16px;
1620
1628
  }
1621
1629
 
1622
1630
  .media-block__alt-badge {
@@ -253,12 +253,15 @@ function queryTargetsDatabase(query: ActionQuery, documentId: string): boolean {
253
253
  );
254
254
  }
255
255
 
256
+ // The Files tree is ordered by the stored personal view but not keyed by it,
257
+ // so it reads again with that view.
256
258
  function queryTargetsActiveDatabasePresentation(query: ActionQuery): boolean {
257
- return (
258
- query.queryKey[0] === "action" &&
259
- query.queryKey[1] === "get-content-database-personal-view" &&
260
- query.isActive?.() === true
261
- );
259
+ if (query.queryKey[0] !== "action" || query.isActive?.() !== true)
260
+ return false;
261
+ if (query.queryKey[1] === "get-content-database-personal-view") return true;
262
+ if (query.queryKey[1] !== "query-content-database-items") return false;
263
+ const args = query.queryKey[2];
264
+ return !!args && typeof args === "object" && "navigation" in args;
262
265
  }
263
266
 
264
267
  function queryTargetsActiveNavigationOrRecent(query: ActionQuery): boolean {
@@ -411,6 +414,12 @@ export function contentActionInvalidatePredicate(
411
414
  eventRefreshesDocumentQuery(event.key, query.queryKey[1]),
412
415
  );
413
416
  }
417
+ if (
418
+ queryTargetsActiveDatabasePresentation(query) &&
419
+ eventsIncludeMutation(events, DATABASE_PRESENTATION_MUTATIONS)
420
+ ) {
421
+ return true;
422
+ }
414
423
  if (queryTargetsDatabase(query, documentId)) {
415
424
  return events.some(
416
425
  (event) =>
@@ -419,14 +428,6 @@ export function contentActionInvalidatePredicate(
419
428
  DATABASE_RESULT_MUTATIONS.has(event.key),
420
429
  );
421
430
  }
422
- if (queryTargetsActiveDatabasePresentation(query)) {
423
- return events.some(
424
- (event) =>
425
- event.source === "action" &&
426
- typeof event.key === "string" &&
427
- DATABASE_PRESENTATION_MUTATIONS.has(event.key),
428
- );
429
- }
430
431
  return false;
431
432
  };
432
433
  }
@@ -769,7 +769,16 @@ export function useContentDatabase(
769
769
  meta: { contentDatabaseSystemRole: options?.systemRole },
770
770
  },
771
771
  );
772
- const page = tableQuery ? pageQuery.data : undefined;
772
+ const pageRead = tableQuery
773
+ ? contentDatabaseItemsPageReadState(
774
+ pageQuery.data,
775
+ pageQuery.isPlaceholderData,
776
+ pageQuery.isError,
777
+ )
778
+ : undefined;
779
+ const page = pageRead?.page;
780
+ const baseFailed = baseQuery.isError && baseQuery.data === undefined;
781
+ const pageFailed = Boolean(tableQuery) && pageRead?.failed === true;
773
782
  const data =
774
783
  page &&
775
784
  baseQuery.data &&
@@ -787,6 +796,24 @@ export function useContentDatabase(
787
796
  return {
788
797
  ...baseQuery,
789
798
  data,
799
+ // Whether `data` holds the requested rows or a read failed. A sorted or
800
+ // filtered view reads its own rows; until that read first answers, `data`
801
+ // holds the base read's rows in stored order.
802
+ itemsSettled:
803
+ baseQuery.isError ||
804
+ (tableQuery
805
+ ? pageRead?.settled === true || (page !== undefined && !!baseQuery.data)
806
+ : baseQuery.data !== undefined),
807
+ // No rows for the requested view could be read. After a failed sorted or
808
+ // filtered read, `data` still holds the base read's rows in stored order;
809
+ // they are not this view's rows and must not draw as them.
810
+ itemsFailed: baseFailed || pageFailed,
811
+ retryItems: () =>
812
+ Promise.all([
813
+ baseFailed ? baseQuery.refetch() : null,
814
+ pageFailed ? pageQuery.refetch() : null,
815
+ ]),
816
+ itemsRetrying: baseQuery.isFetching || pageQuery.isFetching,
790
817
  isLoading: tableQuery ? pageQuery.isLoading && !data : baseQuery.isLoading,
791
818
  isFetching: tableQuery ? pageQuery.isFetching : baseQuery.isFetching,
792
819
  isError: tableQuery ? pageQuery.isError : baseQuery.isError,
@@ -795,6 +822,19 @@ export function useContentDatabase(
795
822
  };
796
823
  }
797
824
 
825
+ export function contentDatabaseItemsPageReadState<T>(
826
+ data: T | undefined,
827
+ isPlaceholderData: boolean,
828
+ isError: boolean,
829
+ ) {
830
+ const page = isPlaceholderData ? undefined : data;
831
+ return {
832
+ page,
833
+ failed: isError && page === undefined,
834
+ settled: isError || page !== undefined,
835
+ };
836
+ }
837
+
798
838
  export function isContentDatabaseByIdQueryEnabled(
799
839
  databaseId: string | null,
800
840
  options?: { enabled?: boolean },
@@ -1298,6 +1338,35 @@ export function useContentDatabasePersonalView(
1298
1338
  );
1299
1339
  }
1300
1340
 
1341
+ // Every save of one database's personal view is keyed under this prefix, so a
1342
+ // save that settles can tell whether another one is still in flight.
1343
+ export function contentPersonalViewSaveKey(databaseId: string | null) {
1344
+ return ["content-personal-view-save", databaseId] as const;
1345
+ }
1346
+
1347
+ /**
1348
+ * Once the last in-flight save of a database's personal view settles, reads
1349
+ * that view and the Files tree it orders again. The tree is not keyed by the
1350
+ * view, and a read during a save can still see the view from before it.
1351
+ */
1352
+ export function refreshAfterPersonalViewSave(
1353
+ queryClient: QueryClient,
1354
+ databaseId: string,
1355
+ ) {
1356
+ // The save calling this is still counted while it settles.
1357
+ if (
1358
+ queryClient.isMutating({
1359
+ mutationKey: contentPersonalViewSaveKey(databaseId),
1360
+ }) > 1
1361
+ ) {
1362
+ return;
1363
+ }
1364
+ void queryClient.invalidateQueries({
1365
+ queryKey: ["action", "get-content-database-personal-view", { databaseId }],
1366
+ });
1367
+ invalidateContentDatabaseNavigationQueries(queryClient, { databaseId });
1368
+ }
1369
+
1301
1370
  export function useUpdateContentDatabasePersonalView(
1302
1371
  databaseId: string | null,
1303
1372
  ) {
@@ -1306,6 +1375,7 @@ export function useUpdateContentDatabasePersonalView(
1306
1375
  ContentDatabasePersonalViewResponse,
1307
1376
  UpdateContentDatabasePersonalViewRequest
1308
1377
  >("update-content-database-personal-view", {
1378
+ mutationKey: [...contentPersonalViewSaveKey(databaseId), "overrides"],
1309
1379
  skipActionQueryInvalidation: true,
1310
1380
  onMutate: async (variables) => {
1311
1381
  if (!databaseId) return undefined;
@@ -1339,14 +1409,7 @@ export function useUpdateContentDatabasePersonalView(
1339
1409
  );
1340
1410
  },
1341
1411
  onSettled: () => {
1342
- if (!databaseId) return;
1343
- void queryClient.invalidateQueries({
1344
- queryKey: [
1345
- "action",
1346
- "get-content-database-personal-view",
1347
- { databaseId },
1348
- ],
1349
- });
1412
+ if (databaseId) refreshAfterPersonalViewSave(queryClient, databaseId);
1350
1413
  },
1351
1414
  });
1352
1415
  }
@@ -6,6 +6,11 @@ import type {
6
6
  import { applyContentPersonalNavigationPatch } from "@shared/content-personal-navigation-patch";
7
7
  import { useQueryClient } from "@tanstack/react-query";
8
8
 
9
+ import {
10
+ contentPersonalViewSaveKey,
11
+ refreshAfterPersonalViewSave,
12
+ } from "./use-content-database";
13
+
9
14
  export function useUpdateContentPersonalNavigation(
10
15
  databaseId: string | null,
11
16
  sharedViews?: ContentDatabaseView[],
@@ -16,7 +21,7 @@ export function useUpdateContentPersonalNavigation(
16
21
  "get-content-database-personal-view",
17
22
  { databaseId },
18
23
  ];
19
- const mutationKey = ["content-personal-navigation", databaseId];
24
+ const mutationKey = [...contentPersonalViewSaveKey(databaseId), "navigation"];
20
25
  return useActionMutation("update-content-database-personal-view", {
21
26
  mutationKey,
22
27
  scope: { id: JSON.stringify(mutationKey) },
@@ -52,8 +57,7 @@ export function useUpdateContentPersonalNavigation(
52
57
  queryClient.setQueryData(queryKey, data);
53
58
  },
54
59
  onSettled: () => {
55
- if (queryClient.isMutating({ mutationKey }) > 1) return;
56
- void queryClient.invalidateQueries({ queryKey });
60
+ if (databaseId) refreshAfterPersonalViewSave(queryClient, databaseId);
57
61
  },
58
62
  });
59
63
  }
@@ -861,6 +861,33 @@ export function startPageOpenDocumentReads(
861
861
  retry: false,
862
862
  });
863
863
  startPreviewDocumentDraftRead(queryClient, documentId, cached);
864
+ if (cached?.source?.mode !== "local-files") {
865
+ startPageOpenReviewReads(queryClient, documentId);
866
+ }
867
+ }
868
+
869
+ // Open comments and suggestions hold the review margin open beside the page,
870
+ // so their reads start with the page read rather than after it lands. A page
871
+ // known to come from a local file has neither, so its open skips them.
872
+ export function startPageOpenReviewReads(
873
+ queryClient: QueryClient,
874
+ documentId: string,
875
+ ) {
876
+ const reads = [
877
+ ["list-comments", { documentId }],
878
+ [
879
+ "list-resource-suggestions",
880
+ { resourceType: "document", resourceId: documentId },
881
+ ],
882
+ ] as const;
883
+ for (const [actionName, params] of reads) {
884
+ void queryClient.prefetchQuery({
885
+ queryKey: ["action", actionName, params],
886
+ queryFn: ({ signal }) =>
887
+ callAction(actionName, params, { method: "GET", signal }),
888
+ retry: false,
889
+ });
890
+ }
864
891
  }
865
892
 
866
893
  export interface PreviewDocumentDraftRecord {
@@ -1106,8 +1106,6 @@ const enUS = {
1106
1106
  landing: {
1107
1107
  previousPageUnavailable:
1108
1108
  "Your previous page is no longer available, so we opened Welcome.",
1109
- requestedPageUnavailable:
1110
- "That page is not available to your account, so we opened Welcome.",
1111
1109
  saveFailed: "Your place could not be saved",
1112
1110
  workspaceWelcomeUnavailableTitle: "Nothing is open here yet",
1113
1111
  workspaceWelcomeUnavailableDescription:
@@ -1176,6 +1174,9 @@ const enUS = {
1176
1174
  documentUnavailable: "Document unavailable",
1177
1175
  documentUnavailableDescription:
1178
1176
  "This page may have been deleted, or it has not been shared with your account.",
1177
+ signedInAs: "Signed in as {{email}}",
1178
+ goToMyPages: "Go to my pages",
1179
+ switchAccount: "Switch account",
1179
1180
  documentNotFound: "Document not found",
1180
1181
  newPage: "New page",
1181
1182
  createFailed: "Failed to create page",
@@ -7,43 +7,15 @@ import {
7
7
 
8
8
  export const CONTENT_LANDING_PATH = "/home";
9
9
 
10
- export type ContentLandingRecoveryState = {
11
- unavailableDocumentId: string;
12
- };
13
-
14
- export function contentLandingRecoveryTarget(input: {
15
- host: string;
16
- documentId: string;
17
- }): { pathname: string; state: ContentLandingRecoveryState } | null {
18
- if (input.host !== "page" || !input.documentId) return null;
19
- return {
20
- pathname: CONTENT_LANDING_PATH,
21
- state: { unavailableDocumentId: input.documentId },
22
- };
23
- }
24
-
25
- export function readContentLandingRecovery(
26
- state: unknown,
27
- ): ContentLandingRecoveryState | null {
28
- if (!state || typeof state !== "object") return null;
29
- const documentId = (state as { unavailableDocumentId?: unknown })
30
- .unavailableDocumentId;
31
- return typeof documentId === "string" && documentId
32
- ? { unavailableDocumentId: documentId }
33
- : null;
34
- }
35
-
36
- // /home with no space and no unavailable page to explain returns to the last
37
- // page opened anywhere, which is the page a last-location hint names.
10
+ // /home with no space returns to the last page opened anywhere, which is the
11
+ // page a last-location hint names.
38
12
  export function isPersonalLanding(location: {
39
13
  pathname: string;
40
14
  search: string;
41
- state: unknown;
42
15
  }) {
43
16
  return (
44
17
  location.pathname === CONTENT_LANDING_PATH &&
45
- !new URLSearchParams(location.search).get("spaceId") &&
46
- !readContentLandingRecovery(location.state)
18
+ !new URLSearchParams(location.search).get("spaceId")
47
19
  );
48
20
  }
49
21