@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
@@ -269,14 +269,18 @@ export default defineAction({
269
269
  databaseId: database.id,
270
270
  properties:
271
271
  (
272
- await listPropertiesForDatabaseDocuments(database.id, [
273
- {
274
- ...document,
275
- content:
276
- target === "document_body" ? content : document.content,
277
- updatedAt: now,
278
- },
279
- ])
272
+ await listPropertiesForDatabaseDocuments(
273
+ database.id,
274
+ [
275
+ {
276
+ ...document,
277
+ content:
278
+ target === "document_body" ? content : document.content,
279
+ updatedAt: now,
280
+ },
281
+ ],
282
+ { includeBlocksFieldIdentity: true },
283
+ )
280
284
  ).get(documentId) ?? [],
281
285
  };
282
286
  }
@@ -493,9 +497,11 @@ export default defineAction({
493
497
  documentId,
494
498
  databaseId: database.id,
495
499
  properties:
496
- (await listPropertiesForDatabaseDocuments(database.id, [document])).get(
497
- documentId,
498
- ) ?? [],
500
+ (
501
+ await listPropertiesForDatabaseDocuments(database.id, [document], {
502
+ includeBlocksFieldIdentity: true,
503
+ })
504
+ ).get(documentId) ?? [],
499
505
  };
500
506
  },
501
507
  });
@@ -42,6 +42,9 @@ import {
42
42
  import { VisualEditor } from "./VisualEditor";
43
43
 
44
44
  const BLOCK_FIELD_DRAG_THRESHOLD = 6;
45
+ // The default `auto` track grows to its widest unbreakable line, which widens
46
+ // the whole page body past the window; the track must be allowed to shrink.
47
+ const BLOCK_FIELDS_GRID = "grid grid-cols-[minmax(0,1fr)]";
45
48
 
46
49
  interface DocumentBlockFieldsProps {
47
50
  documentId: string;
@@ -309,7 +312,10 @@ export function DocumentBlockFields({
309
312
  // that state could bind the body before we know which storage target owns it.
310
313
  if (query.isError) {
311
314
  return (
312
- <div className="grid gap-1" data-block-fields-state="error">
315
+ <div
316
+ className={cn(BLOCK_FIELDS_GRID, "gap-1")}
317
+ data-block-fields-state="error"
318
+ >
313
319
  <QueryErrorState
314
320
  compact
315
321
  onRetry={() => globalThis.location.reload()}
@@ -325,7 +331,7 @@ export function DocumentBlockFields({
325
331
  case "loading":
326
332
  return (
327
333
  <div
328
- className="grid gap-1"
334
+ className={cn(BLOCK_FIELDS_GRID, "gap-1")}
329
335
  data-block-fields-state="loading"
330
336
  aria-busy="true"
331
337
  >
@@ -334,7 +340,10 @@ export function DocumentBlockFields({
334
340
  );
335
341
  case "empty":
336
342
  return (
337
- <div className="grid gap-1" data-block-fields-state="empty">
343
+ <div
344
+ className={cn(BLOCK_FIELDS_GRID, "gap-1")}
345
+ data-block-fields-state="empty"
346
+ >
338
347
  {canEdit ? (
339
348
  <p className="px-1 py-2 text-sm text-muted-foreground">
340
349
  {t("editor.noBlocksFields")}
@@ -345,7 +354,10 @@ export function DocumentBlockFields({
345
354
  case "solo":
346
355
  if (state.target === "block_field_store") {
347
356
  return (
348
- <div className="grid gap-1" data-block-fields-state="solo">
357
+ <div
358
+ className={cn(BLOCK_FIELDS_GRID, "gap-1")}
359
+ data-block-fields-state="solo"
360
+ >
349
361
  <AdditionalBlockEditor
350
362
  key={`${documentId}:${state.field.definition.id}`}
351
363
  documentId={documentId}
@@ -359,7 +371,10 @@ export function DocumentBlockFields({
359
371
  );
360
372
  }
361
373
  return (
362
- <div className="grid gap-1" data-block-fields-state="solo">
374
+ <div
375
+ className={cn(BLOCK_FIELDS_GRID, "gap-1")}
376
+ data-block-fields-state="solo"
377
+ >
363
378
  {primaryEditor}
364
379
  </div>
365
380
  );
@@ -546,7 +561,7 @@ function MultiBlockFields({
546
561
  }
547
562
 
548
563
  return (
549
- <div className="grid">
564
+ <div className={BLOCK_FIELDS_GRID}>
550
565
  <BlockFieldDragPreview preview={dragPreview} />
551
566
  {blockFields.map((property, index) => {
552
567
  const primary = isPrimaryBlocksField(property.definition.options);
@@ -10,6 +10,7 @@ import {
10
10
  actionErrorMessage,
11
11
  callAction,
12
12
  setClientAppState,
13
+ signOut,
13
14
  tryCallActionKeepalive,
14
15
  useAvatarUrl,
15
16
  useDbSync,
@@ -65,7 +66,7 @@ import {
65
66
  useState,
66
67
  } from "react";
67
68
  import type { ClipboardEvent, MutableRefObject, ReactNode } from "react";
68
- import { Navigate, useLocation, useNavigate } from "react-router";
69
+ import { Link, Navigate, useLocation, useNavigate } from "react-router";
69
70
  import { toast } from "sonner";
70
71
  import type { Doc as YDoc } from "yjs";
71
72
 
@@ -107,6 +108,7 @@ import {
107
108
  patchDocumentCaches,
108
109
  documentQueryFilter,
109
110
  documentQueryKey,
111
+ startPageOpenReviewReads,
110
112
  startPreviewDocumentDraftRead,
111
113
  useContentNavigationContext,
112
114
  useDeleteDocument,
@@ -129,7 +131,6 @@ import {
129
131
  } from "@/hooks/use-optimistic-document-title";
130
132
  import {
131
133
  CONTENT_LANDING_PATH,
132
- contentLandingRecoveryTarget,
133
134
  rememberContentLandingDocument,
134
135
  } from "@/lib/content-landing";
135
136
  import type { DesktopContentFileRevision } from "@/lib/desktop-content-files";
@@ -148,9 +149,12 @@ import {
148
149
  isDocumentCreationPending,
149
150
  } from "@/lib/optimistic-document";
150
151
  import {
152
+ readDocumentShapeHint,
151
153
  readPageIconRowHint,
154
+ readPageShapeHint,
152
155
  rememberPageIconRow,
153
- } from "@/lib/page-icon-row-hint";
156
+ rememberPageShape,
157
+ } from "@/lib/page-startup-hints";
154
158
  import { startupAnchor } from "@/lib/startup-timing";
155
159
  import { cn } from "@/lib/utils";
156
160
 
@@ -180,9 +184,12 @@ import {
180
184
  } from "./CommentsSidebar";
181
185
  import type { DatabaseExportContext } from "./database/DatabaseExportDialog";
182
186
  import {
187
+ DOCUMENT_EDITOR_DATABASE_TITLE_SIZE_CLASS_NAME,
188
+ DOCUMENT_EDITOR_INLINE_REVIEW_MIN_WIDTH,
183
189
  DOCUMENT_EDITOR_PAGE_TITLE_SIZE_CLASS_NAME,
184
190
  DOCUMENT_EDITOR_TITLE_CLASS_NAME,
185
191
  documentEditorBodyClassName,
192
+ documentEditorDatabaseRegionClassName,
186
193
  documentEditorTitleRegionClassName,
187
194
  type DocumentEditorIconRow,
188
195
  } from "./document-editor-layout";
@@ -487,6 +494,26 @@ export function documentEditorReservesInlineReviewSpace(args: {
487
494
  );
488
495
  }
489
496
 
497
+ export function documentEditorReviewReadsSettled(args: {
498
+ isLocalFileDocument: boolean;
499
+ hasThreads: boolean;
500
+ hasSuggestions: boolean;
501
+ commentsFetching: boolean;
502
+ suggestionsFetching: boolean;
503
+ commentsError: boolean;
504
+ suggestionsError: boolean;
505
+ }) {
506
+ return (
507
+ args.isLocalFileDocument ||
508
+ (args.hasThreads &&
509
+ args.hasSuggestions &&
510
+ !args.commentsFetching &&
511
+ !args.suggestionsFetching &&
512
+ !args.commentsError &&
513
+ !args.suggestionsError)
514
+ );
515
+ }
516
+
490
517
  export function suggestionPresentation(
491
518
  suggestion: Pick<ResourceSuggestion, "id" | "status" | "operations">,
492
519
  currentMarkdown: string,
@@ -1145,9 +1172,18 @@ function adoptConfirmedSaveWatermarks({
1145
1172
  }
1146
1173
  }
1147
1174
 
1148
- function DocumentUnavailable() {
1175
+ // A Page link that this account can't read stays on its URL and says so.
1176
+ // Opening some other page instead hides the denial from the person who
1177
+ // followed the link.
1178
+ export function DocumentUnavailable({
1179
+ host,
1180
+ }: {
1181
+ host: PageEditorSurfaceProps["host"];
1182
+ }) {
1149
1183
  const t = useT();
1150
1184
  const sidebarTrigger = useSidebarTrigger();
1185
+ const { session } = useSession();
1186
+ const viewerEmail = host === "page" ? (session?.email ?? null) : null;
1151
1187
 
1152
1188
  return (
1153
1189
  <div className="flex min-h-0 flex-1 flex-col">
@@ -1167,6 +1203,21 @@ function DocumentUnavailable() {
1167
1203
  <p className="mt-3 text-sm leading-6 text-muted-foreground">
1168
1204
  {t("empty.documentUnavailableDescription")}
1169
1205
  </p>
1206
+ {viewerEmail ? (
1207
+ <p className="mt-4 break-all text-sm text-muted-foreground">
1208
+ {t("empty.signedInAs", { email: viewerEmail })}
1209
+ </p>
1210
+ ) : null}
1211
+ {host === "page" ? (
1212
+ <div className="mt-6 flex flex-wrap items-center justify-center gap-2">
1213
+ <Button asChild>
1214
+ <Link to={CONTENT_LANDING_PATH}>{t("empty.goToMyPages")}</Link>
1215
+ </Button>
1216
+ <Button variant="outline" onClick={() => void signOut()}>
1217
+ {t("empty.switchAccount")}
1218
+ </Button>
1219
+ </div>
1220
+ ) : null}
1170
1221
  </div>
1171
1222
  </div>
1172
1223
  </div>
@@ -1243,7 +1294,6 @@ export function PageEditorSurface({
1243
1294
  isError,
1244
1295
  isFetching,
1245
1296
  } = documentQuery;
1246
- const navigate = useNavigate();
1247
1297
  const queryClient = useQueryClient();
1248
1298
  const documentQueryKeyValue = documentQueryKey(documentId, {
1249
1299
  databaseId,
@@ -1251,13 +1301,13 @@ export function PageEditorSurface({
1251
1301
  });
1252
1302
  useEffect(() => {
1253
1303
  if (readsStartedEarly) return;
1254
- startPreviewDocumentDraftRead(
1255
- queryClient,
1256
- documentId,
1257
- queryClient.getQueryData<Document>(
1258
- documentQueryKey(documentId, { databaseId, databaseDocumentId }),
1259
- ),
1304
+ const cached = queryClient.getQueryData<Document>(
1305
+ documentQueryKey(documentId, { databaseId, databaseDocumentId }),
1260
1306
  );
1307
+ startPreviewDocumentDraftRead(queryClient, documentId, cached);
1308
+ if (cached?.source?.mode !== "local-files") {
1309
+ startPageOpenReviewReads(queryClient, documentId);
1310
+ }
1261
1311
  }, [
1262
1312
  databaseDocumentId,
1263
1313
  databaseId,
@@ -1344,26 +1394,8 @@ export function PageEditorSurface({
1344
1394
  }
1345
1395
  }
1346
1396
 
1347
- const landingRecovery =
1348
- loadState.view === "unavailable"
1349
- ? contentLandingRecoveryTarget({ host, documentId })
1350
- : null;
1351
- const landingRecoveryDocumentId =
1352
- landingRecovery?.state.unavailableDocumentId ?? null;
1353
- useEffect(() => {
1354
- if (!landingRecoveryDocumentId) return;
1355
- void navigate(CONTENT_LANDING_PATH, {
1356
- replace: true,
1357
- state: { unavailableDocumentId: landingRecoveryDocumentId },
1358
- });
1359
- }, [landingRecoveryDocumentId, navigate]);
1360
-
1361
1397
  if (loadState.view === "unavailable") {
1362
- return landingRecovery ? (
1363
- <DocumentEditorSkeleton />
1364
- ) : (
1365
- <DocumentUnavailable />
1366
- );
1398
+ return <DocumentUnavailable host={host} />;
1367
1399
  }
1368
1400
 
1369
1401
  if (loadState.view === "error") {
@@ -1383,7 +1415,7 @@ export function PageEditorSurface({
1383
1415
  return host === "page" ? (
1384
1416
  <Navigate to="/home" replace />
1385
1417
  ) : (
1386
- <DocumentUnavailable />
1418
+ <DocumentUnavailable host={host} />
1387
1419
  );
1388
1420
  }
1389
1421
 
@@ -1392,6 +1424,11 @@ export function PageEditorSurface({
1392
1424
  <DocumentEditorSkeleton
1393
1425
  title={optimisticTitle}
1394
1426
  iconRow={readPageIconRowHint(documentId)}
1427
+ shape={
1428
+ document
1429
+ ? readDocumentShapeHint(document)
1430
+ : readPageShapeHint(documentId)
1431
+ }
1395
1432
  />
1396
1433
  );
1397
1434
  }
@@ -1847,7 +1884,9 @@ function useElementMinWidth(
1847
1884
  ) {
1848
1885
  const [matches, setMatches] = useState(false);
1849
1886
 
1850
- useEffect(() => {
1887
+ // Measured before the first paint: a page that opens beside the review
1888
+ // margin must not first paint without it.
1889
+ useLayoutEffect(() => {
1851
1890
  const element = ref.current;
1852
1891
  if (!element) return;
1853
1892
  const update = () =>
@@ -2027,11 +2066,10 @@ export function documentEditorShowsUtilityPanelSheet(
2027
2066
  return args.utilityPanel === "info" && !args.hasUtilityRailSpace;
2028
2067
  }
2029
2068
 
2030
- export { documentEditorTitleRegionClassName };
2031
-
2032
- export function documentEditorDatabaseRegionClassName() {
2033
- return "shrink-0 min-w-0 w-full max-w-none px-4 pb-8 sm:px-8 lg:px-10";
2034
- }
2069
+ export {
2070
+ documentEditorDatabaseRegionClassName,
2071
+ documentEditorTitleRegionClassName,
2072
+ };
2035
2073
 
2036
2074
  export function resizeDocumentTitleTextarea(
2037
2075
  textarea: Pick<HTMLTextAreaElement, "scrollHeight" | "style">,
@@ -6279,9 +6317,12 @@ function PageEditorSessionBody({
6279
6317
  null,
6280
6318
  );
6281
6319
  const appliedSuggestionLinkRef = useRef<string | null>(null);
6282
- const { data: threads, isLoading: commentsLoading } = useComments(
6283
- !isLocalFileDocument ? documentId : null,
6284
- );
6320
+ const {
6321
+ data: threads,
6322
+ isLoading: commentsLoading,
6323
+ isFetching: commentsFetching,
6324
+ isError: commentsError,
6325
+ } = useComments(!isLocalFileDocument ? documentId : null);
6285
6326
  const commentAi = useCommentAiRequests(documentId, {
6286
6327
  enabled: !isLocalFileDocument && canComment,
6287
6328
  });
@@ -6310,7 +6351,10 @@ function PageEditorSessionBody({
6310
6351
  useState<AnchoredCommentPosition | null>(null);
6311
6352
  const [commentLaneOffset, setCommentLaneOffset] = useState(0);
6312
6353
  const hasUtilityRailSpace = useElementMinWidth(documentLayoutRef, 960);
6313
- const hasInlineCommentSpace = useElementMinWidth(documentLayoutRef, 1088);
6354
+ const hasInlineCommentSpace = useElementMinWidth(
6355
+ documentLayoutRef,
6356
+ DOCUMENT_EDITOR_INLINE_REVIEW_MIN_WIDTH,
6357
+ );
6314
6358
  const showCommentsHistoryDrawer =
6315
6359
  utilityPanel === "comments" && commentsBrowseOpen;
6316
6360
  const showDesktopCommentsHistory =
@@ -6343,12 +6387,52 @@ function PageEditorSessionBody({
6343
6387
  hasSelectedCommentThread,
6344
6388
  hasPendingComment: !!pendingComment,
6345
6389
  });
6390
+ // A failed read says nothing about whether the page still has open review,
6391
+ // so the remembered margin holds until both reads answer.
6392
+ const reviewReadsSettled = documentEditorReviewReadsSettled({
6393
+ isLocalFileDocument,
6394
+ hasThreads: threads !== undefined,
6395
+ hasSuggestions: suggestionsQuery.data !== undefined,
6396
+ commentsFetching,
6397
+ suggestionsFetching: suggestionsQuery.isFetching,
6398
+ commentsError,
6399
+ suggestionsError: suggestionsQuery.isError,
6400
+ });
6401
+ const pageHadOpenReview = useMemo(
6402
+ () => host === "page" && readPageShapeHint(documentId) === "review",
6403
+ [documentId, host],
6404
+ );
6346
6405
  const reserveInlineReviewSpace = documentEditorReservesInlineReviewSpace({
6347
6406
  showInlineComments,
6348
- preserveInlineReviewSpace,
6407
+ preserveInlineReviewSpace:
6408
+ preserveInlineReviewSpace || (pageHadOpenReview && !reviewReadsSettled),
6349
6409
  hasInlineCommentSpace,
6350
6410
  isDatabasePage: Boolean(document.database),
6351
6411
  });
6412
+ const hasOpenReview = hasOpenCommentThreads || hasOpenSuggestions;
6413
+ useEffect(() => {
6414
+ if (
6415
+ host !== "page" ||
6416
+ document.database ||
6417
+ isLocalFileDocument ||
6418
+ !reviewReadsSettled
6419
+ )
6420
+ return;
6421
+ rememberPageShape(documentId, hasOpenReview ? "review" : "page");
6422
+ }, [
6423
+ document.database,
6424
+ documentId,
6425
+ hasOpenReview,
6426
+ host,
6427
+ isLocalFileDocument,
6428
+ reviewReadsSettled,
6429
+ suggestionsQuery.data,
6430
+ suggestionsQuery.isError,
6431
+ suggestionsQuery.isFetching,
6432
+ threads,
6433
+ commentsFetching,
6434
+ commentsError,
6435
+ ]);
6352
6436
  const showDesktopInfoPanel = utilityPanel === "info" && hasUtilityRailSpace;
6353
6437
  const showDesktopRightRail = showInlineComments || showDesktopInfoPanel;
6354
6438
  const showAnchoredCommentPopover =
@@ -7844,7 +7928,7 @@ function PageEditorSessionBody({
7844
7928
  DOCUMENT_EDITOR_TITLE_CLASS_NAME,
7845
7929
  "resize-none overflow-hidden border-none bg-transparent outline-none placeholder:text-muted-foreground/40",
7846
7930
  host === "preview" || isDatabasePage
7847
- ? "text-3xl"
7931
+ ? DOCUMENT_EDITOR_DATABASE_TITLE_SIZE_CLASS_NAME
7848
7932
  : DOCUMENT_EDITOR_PAGE_TITLE_SIZE_CLASS_NAME,
7849
7933
  )}
7850
7934
  />
@@ -3,29 +3,156 @@ import { Skeleton } from "@/components/ui/skeleton";
3
3
  import { startupAnchor } from "@/lib/startup-timing";
4
4
  import { cn } from "@/lib/utils";
5
5
 
6
+ import { DatabaseViewSkeleton } from "./database/DatabaseViewSkeleton";
6
7
  import {
8
+ DOCUMENT_EDITOR_DATABASE_TITLE_SIZE_CLASS_NAME,
7
9
  DOCUMENT_EDITOR_PAGE_TITLE_SIZE_CLASS_NAME,
8
10
  DOCUMENT_EDITOR_TITLE_CLASS_NAME,
9
11
  documentEditorBodyClassName,
12
+ documentEditorDatabaseRegionClassName,
10
13
  documentEditorTitleRegionClassName,
11
14
  type DocumentEditorIconRow,
15
+ type DocumentEditorShape,
12
16
  } from "./document-editor-layout";
13
17
 
14
- // Before the app loads, the startup script marks <html> with the icon row the
15
- // page last drew; "startup" sizes the row from that mark, since storage is out
16
- // of reach while the server renders.
18
+ // Before the app loads, the startup script marks <html> with the icon row and
19
+ // shape the page last drew; "startup" draws from those marks, since storage is
20
+ // out of reach while the server renders.
17
21
  const STARTUP_ICON_ROW_CLASS_NAME =
18
22
  "h-7 w-0 [html[data-content-page-icon-row=icon]_&]:size-14 [html[data-content-page-icon-row=none]_&]:hidden";
23
+ const STARTUP_PAGE_COLUMN_CLASS_NAME =
24
+ "[html[data-content-page-shape=database]_&]:hidden [html[data-content-page-shape=database-constrained]_&]:hidden";
25
+ const STARTUP_DATABASE_COLUMN_CLASS_NAME =
26
+ "hidden [html[data-content-page-shape=database]_&]:block [html[data-content-page-shape=database-constrained]_&]:block";
19
27
 
20
- // Every box here is the page editor's own box, so the title and the body start
21
- // where the editor will draw them. While `title` is undefined the title is
22
- // still unknown, and the body waits for it: a title that wraps would move it.
28
+ // The editor opens the review margin from DOCUMENT_EDITOR_INLINE_REVIEW_MIN_WIDTH
29
+ // of page width, measured on the box this one stands in for.
30
+ const REVIEW_MARGIN_CLASS_NAME = "@min-[1088px]:pr-80";
31
+ const STARTUP_REVIEW_MARGIN_CLASS_NAME =
32
+ "[html[data-content-page-shape=review]_&]:@min-[1088px]:pr-80";
33
+
34
+ function PageColumn({
35
+ title,
36
+ iconRow,
37
+ review,
38
+ className,
39
+ }: {
40
+ title?: string | null;
41
+ iconRow: DocumentEditorIconRow | "startup";
42
+ review: boolean | "startup";
43
+ className?: string;
44
+ }) {
45
+ return (
46
+ <div
47
+ className={cn(
48
+ review === "startup"
49
+ ? STARTUP_REVIEW_MARGIN_CLASS_NAME
50
+ : review && REVIEW_MARGIN_CLASS_NAME,
51
+ className,
52
+ )}
53
+ >
54
+ <div className={documentEditorTitleRegionClassName(false)}>
55
+ <div className="mb-1">
56
+ {iconRow === "startup" ? (
57
+ <Skeleton className={STARTUP_ICON_ROW_CLASS_NAME} />
58
+ ) : iconRow === "icon" ? (
59
+ <Skeleton className="size-14 rounded-md" />
60
+ ) : iconRow === "add" ? (
61
+ <div className="h-7" />
62
+ ) : null}
63
+ </div>
64
+ <div
65
+ {...startupAnchor("title")}
66
+ className={cn(
67
+ DOCUMENT_EDITOR_TITLE_CLASS_NAME,
68
+ DOCUMENT_EDITOR_PAGE_TITLE_SIZE_CLASS_NAME,
69
+ "relative",
70
+ )}
71
+ >
72
+ <SkeletonTitle title={title} />
73
+ </div>
74
+ </div>
75
+ {title === undefined ? null : (
76
+ <div
77
+ {...startupAnchor("body")}
78
+ className={documentEditorBodyClassName("page")}
79
+ >
80
+ <div className="space-y-3 pt-1.5">
81
+ <Skeleton className="h-4 w-full" />
82
+ <Skeleton className="h-4 w-11/12" />
83
+ <Skeleton className="h-4 w-5/6" />
84
+ <Skeleton className="h-4 w-3/4" />
85
+ </div>
86
+ <div className="space-y-3 pt-8">
87
+ <Skeleton className="h-4 w-10/12" />
88
+ <Skeleton className="h-4 w-4/5" />
89
+ <Skeleton className="h-4 w-7/12" />
90
+ </div>
91
+ </div>
92
+ )}
93
+ </div>
94
+ );
95
+ }
96
+
97
+ // A collection always draws its 56px icon, and its view below the title.
98
+ function DatabaseColumn({
99
+ title,
100
+ constraints,
101
+ className,
102
+ }: {
103
+ title?: string | null;
104
+ constraints: boolean | "startup";
105
+ className?: string;
106
+ }) {
107
+ return (
108
+ <div className={className}>
109
+ <div className={documentEditorTitleRegionClassName(true)}>
110
+ <div className="mb-1">
111
+ <Skeleton className="size-14 rounded-md" />
112
+ </div>
113
+ <div
114
+ {...startupAnchor("title")}
115
+ className={cn(
116
+ DOCUMENT_EDITOR_TITLE_CLASS_NAME,
117
+ DOCUMENT_EDITOR_DATABASE_TITLE_SIZE_CLASS_NAME,
118
+ "relative",
119
+ )}
120
+ >
121
+ <SkeletonTitle title={title} />
122
+ </div>
123
+ </div>
124
+ {title === undefined ? null : (
125
+ <div className={documentEditorDatabaseRegionClassName()}>
126
+ <DatabaseViewSkeleton constraints={constraints} />
127
+ </div>
128
+ )}
129
+ </div>
130
+ );
131
+ }
132
+
133
+ function SkeletonTitle({ title }: { title?: string | null }) {
134
+ return (
135
+ title || (
136
+ <>
137
+ &nbsp;
138
+ <Skeleton className="absolute inset-y-[20%] start-0 w-2/3" />
139
+ </>
140
+ )
141
+ );
142
+ }
143
+
144
+ // Every box here is the page editor's own box, so the title and what follows
145
+ // it start where the editor will draw them. While `title` is undefined the
146
+ // title is still unknown, and what follows waits for it: a title that wraps
147
+ // would move it.
23
148
  export function DocumentEditorSkeleton({
24
149
  title,
25
150
  iconRow = "add",
151
+ shape = "page",
26
152
  }: {
27
153
  title?: string | null;
28
154
  iconRow?: DocumentEditorIconRow | "startup";
155
+ shape?: DocumentEditorShape | "startup";
29
156
  }) {
30
157
  const sidebarTrigger = useSidebarTrigger();
31
158
  return (
@@ -42,50 +169,32 @@ export function DocumentEditorSkeleton({
42
169
  <Skeleton className="h-7 w-7 rounded-md" />
43
170
  </div>
44
171
  </div>
45
- <div className="min-h-0 flex-1 overflow-hidden">
46
- <div className={documentEditorTitleRegionClassName(false)}>
47
- <div className="mb-1">
48
- {iconRow === "startup" ? (
49
- <Skeleton className={STARTUP_ICON_ROW_CLASS_NAME} />
50
- ) : iconRow === "icon" ? (
51
- <Skeleton className="size-14 rounded-md" />
52
- ) : iconRow === "add" ? (
53
- <div className="h-7" />
54
- ) : null}
55
- </div>
56
- <div
57
- {...startupAnchor("title")}
58
- className={cn(
59
- DOCUMENT_EDITOR_TITLE_CLASS_NAME,
60
- DOCUMENT_EDITOR_PAGE_TITLE_SIZE_CLASS_NAME,
61
- "relative",
62
- )}
63
- >
64
- {title || (
65
- <>
66
- &nbsp;
67
- <Skeleton className="absolute inset-y-[20%] start-0 w-2/3" />
68
- </>
69
- )}
70
- </div>
71
- </div>
72
- {title === undefined ? null : (
73
- <div
74
- {...startupAnchor("body")}
75
- className={documentEditorBodyClassName("page")}
76
- >
77
- <div className="space-y-3 pt-1.5">
78
- <Skeleton className="h-4 w-full" />
79
- <Skeleton className="h-4 w-11/12" />
80
- <Skeleton className="h-4 w-5/6" />
81
- <Skeleton className="h-4 w-3/4" />
82
- </div>
83
- <div className="space-y-3 pt-8">
84
- <Skeleton className="h-4 w-10/12" />
85
- <Skeleton className="h-4 w-4/5" />
86
- <Skeleton className="h-4 w-7/12" />
87
- </div>
88
- </div>
172
+ <div className="@container min-h-0 flex-1 overflow-hidden">
173
+ {shape === "startup" ? (
174
+ <>
175
+ <PageColumn
176
+ title={title}
177
+ iconRow={iconRow}
178
+ review="startup"
179
+ className={STARTUP_PAGE_COLUMN_CLASS_NAME}
180
+ />
181
+ <DatabaseColumn
182
+ title={title}
183
+ constraints="startup"
184
+ className={STARTUP_DATABASE_COLUMN_CLASS_NAME}
185
+ />
186
+ </>
187
+ ) : shape === "database" || shape === "database-constrained" ? (
188
+ <DatabaseColumn
189
+ title={title}
190
+ constraints={shape === "database-constrained"}
191
+ />
192
+ ) : (
193
+ <PageColumn
194
+ title={title}
195
+ iconRow={iconRow}
196
+ review={shape === "review"}
197
+ />
89
198
  )}
90
199
  </div>
91
200
  </div>