alinea 2.0.0-preview.11 → 2.0.0-preview.13

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 (268) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/.mcp/later/dev.json +1 -1
  3. package/dist/.mcp/online/dev.json +1 -1
  4. package/dist/adapter/next/cms.js +16 -8
  5. package/dist/adapter/next/dashboardUrl.d.ts +6 -0
  6. package/dist/adapter/next/dashboardUrl.js +13 -0
  7. package/dist/adapter/next/syncCheck.d.ts +15 -2
  8. package/dist/adapter/next/syncCheck.js +26 -9
  9. package/dist/backend/Handler.js +5 -1
  10. package/dist/backend/api/BlobStore.d.ts +21 -0
  11. package/dist/backend/api/BlobStore.js +63 -0
  12. package/dist/backend/api/CreateBackend.js +10 -8
  13. package/dist/backend/api/DatabaseApi.d.ts +4 -1
  14. package/dist/backend/api/DatabaseApi.js +88 -4
  15. package/dist/backend/api/GithubApi.js +47 -24
  16. package/dist/backend/api/database.js +6 -6
  17. package/dist/bundled.d.ts +687 -248
  18. package/dist/chunks/{bun-sqlite-5SHXN3AT.js → bun-sqlite-RI6BYU5S.js} +3 -3
  19. package/dist/chunks/{chunk-YCQR5GRT.js → chunk-3QFSGTGF.js} +2 -3
  20. package/dist/chunks/{chunk-QJGGYF6E.js → chunk-4ZA6KZHS.js} +5 -5
  21. package/dist/chunks/{chunk-ASYKC6IE.js → chunk-5H4D5JQL.js} +1 -1
  22. package/dist/chunks/{chunk-ETWOY4AL.js → chunk-A2I6MCCS.js} +1 -1
  23. package/dist/chunks/{chunk-W5AXN7GH.js → chunk-CYVOGRSD.js} +1 -2
  24. package/dist/chunks/{chunk-TIJTT5ZI.js → chunk-G43W7AME.js} +1 -0
  25. package/dist/chunks/{chunk-RE523BQ7.js → chunk-HOETF6HN.js} +5829 -5830
  26. package/dist/chunks/{chunk-AIU6CMVT.js → chunk-PTYZMV5G.js} +1 -1
  27. package/dist/chunks/{chunk-JOF663EG.js → chunk-QHHMKJUO.js} +1 -1
  28. package/dist/chunks/{chunk-66YZ47TY.js → chunk-RAR44T5O.js} +1 -1
  29. package/dist/chunks/{chunk-QSN6RH6X.js → chunk-VA6NC3MI.js} +1 -1
  30. package/dist/chunks/{node-sqlite-XVNTNMCY.js → node-sqlite-C7VJ2NSN.js} +3 -3
  31. package/dist/cli/Mcp.js +1 -1
  32. package/dist/cli/Serve.js +1 -1
  33. package/dist/cli/bin.js +2 -3
  34. package/dist/cli/generate/GenerateDashboard.js +17 -5
  35. package/dist/cli/serve/mcp/DevMcp.js +1 -1
  36. package/dist/cli/serve/mcp/McpTools.js +29 -1
  37. package/dist/cli/static/dashboard/entry.js +2 -1
  38. package/dist/cloud/CloudRemote.js +1 -1
  39. package/dist/components/Alert.js +3 -3
  40. package/dist/components/AppShell.js +2 -2
  41. package/dist/components/Badge.d.ts +3 -1
  42. package/dist/components/Badge.js +5 -3
  43. package/dist/components/Breadcrumbs.js +6 -6
  44. package/dist/components/Button.d.ts +6 -1
  45. package/dist/components/Button.js +14 -5
  46. package/dist/components/Calendar.js +20 -20
  47. package/dist/components/Checkbox.js +9 -9
  48. package/dist/components/CheckboxGroup.js +4 -4
  49. package/dist/components/Collapsible.js +3 -3
  50. package/dist/components/ColorSwatch.js +2 -2
  51. package/dist/components/ColorSwatchPicker.js +2 -2
  52. package/dist/components/ComboBox.js +7 -7
  53. package/dist/components/Command.js +10 -9
  54. package/dist/components/ContentCard.d.ts +3 -1
  55. package/dist/components/ContentCard.js +39 -17
  56. package/dist/components/ContentGrid.js +15 -15
  57. package/dist/components/DataList.js +2 -2
  58. package/dist/components/DateField.js +2 -2
  59. package/dist/components/DatePicker.js +2 -2
  60. package/dist/components/DateRangePicker.js +2 -2
  61. package/dist/components/Dialog.js +6 -6
  62. package/dist/components/DropZone.js +4 -4
  63. package/dist/components/DropdownMenu.js +10 -10
  64. package/dist/components/Empty.js +4 -4
  65. package/dist/components/Field.js +8 -8
  66. package/dist/components/List.js +23 -23
  67. package/dist/components/MediaPreview.js +5 -5
  68. package/dist/components/MultipleSelect.js +20 -20
  69. package/dist/components/NavRail.js +4 -4
  70. package/dist/components/NumberField.js +5 -5
  71. package/dist/components/Page.js +5 -5
  72. package/dist/components/Popover.js +2 -2
  73. package/dist/components/PreviewFrame.js +5 -5
  74. package/dist/components/RadioGroup.js +6 -6
  75. package/dist/components/Resizable.js +5 -5
  76. package/dist/components/SearchField.d.ts +6 -1
  77. package/dist/components/SearchField.js +7 -5
  78. package/dist/components/Select.js +14 -14
  79. package/dist/components/Sheet.d.ts +50 -0
  80. package/dist/components/Sheet.js +202 -0
  81. package/dist/components/Sidebar.js +4 -4
  82. package/dist/components/SortableList.d.ts +34 -30
  83. package/dist/components/SortableList.js +162 -265
  84. package/dist/components/Spinner.js +1 -1
  85. package/dist/components/Surface.js +2 -2
  86. package/dist/components/Switch.js +7 -7
  87. package/dist/components/Table.d.ts +3 -1
  88. package/dist/components/Table.js +53 -42
  89. package/dist/components/Tabs.js +5 -5
  90. package/dist/components/TagGroup.js +9 -9
  91. package/dist/components/TextField.js +10 -10
  92. package/dist/components/TimeField.js +8 -8
  93. package/dist/components/Timestamp.js +1 -1
  94. package/dist/components/ToggleGroup.js +2 -2
  95. package/dist/components/Toolbar.js +4 -4
  96. package/dist/components/Tooltip.js +5 -5
  97. package/dist/components/Tree.js +29 -29
  98. package/dist/components/internal/DateControl.js +10 -10
  99. package/dist/components/internal/ListBoxOption.js +13 -13
  100. package/dist/components/internal/Locale.js +2 -2
  101. package/dist/components/internal/PopoverSurface.js +2 -2
  102. package/dist/components/internal/SelectionCheckbox.js +6 -6
  103. package/dist/components/internal/Slot.js +1 -1
  104. package/dist/components/internal/ToggleButton.js +3 -3
  105. package/dist/components/internal/Trigger.js +2 -2
  106. package/dist/components/internal/sortableDrag.d.ts +19 -0
  107. package/dist/components/internal/sortableDrag.js +175 -0
  108. package/dist/components/internal/useDragDrop.js +2 -2
  109. package/dist/components.d.ts +1 -0
  110. package/dist/components.js +1 -0
  111. package/dist/core/Config.d.ts +6 -1
  112. package/dist/core/Document.d.ts +9 -1
  113. package/dist/core/Document.js +31 -6
  114. package/dist/core/Overview.d.ts +2 -4
  115. package/dist/core/TextDoc.d.ts +4 -0
  116. package/dist/core/Validation.d.ts +0 -1
  117. package/dist/core/Validation.js +0 -6
  118. package/dist/core/field/RichTextField.js +23 -4
  119. package/dist/core/media/MediaTypes.js +1 -1
  120. package/dist/core/source/GithubSource.d.ts +4 -0
  121. package/dist/core/source/GithubSource.js +127 -25
  122. package/dist/core/source/GithubUsage.d.ts +21 -0
  123. package/dist/core/source/GithubUsage.js +47 -0
  124. package/dist/dashboard/App.d.ts +2 -2
  125. package/dist/dashboard/App.js +5 -3
  126. package/dist/dashboard/ViewKeys.d.ts +1 -0
  127. package/dist/dashboard/ViewKeys.js +1 -0
  128. package/dist/dashboard/app/ActivityStatus.js +11 -11
  129. package/dist/dashboard/app/AuthView.js +5 -5
  130. package/dist/dashboard/app/BlockSheet.d.ts +50 -0
  131. package/dist/dashboard/app/BlockSheet.js +207 -0
  132. package/dist/dashboard/app/BlockSheetStoryFrame.d.ts +6 -0
  133. package/dist/dashboard/app/BlockSheetStoryFrame.js +29 -0
  134. package/dist/dashboard/app/CompactField.js +15 -15
  135. package/dist/dashboard/app/CroppedImage.js +2 -2
  136. package/dist/dashboard/app/DashboardErrorBoundary.js +3 -3
  137. package/dist/dashboard/app/DashboardLayout.js +3 -1
  138. package/dist/dashboard/app/EntryHeader.js +32 -33
  139. package/dist/dashboard/app/EntryReferences.js +5 -5
  140. package/dist/dashboard/app/EntrySidebar.js +8 -8
  141. package/dist/dashboard/app/EntrySidebarPreview.js +4 -4
  142. package/dist/dashboard/app/EntryStatusIcon.d.ts +23 -0
  143. package/dist/dashboard/app/EntryStatusIcon.js +58 -0
  144. package/dist/dashboard/app/EntryValidationModal.d.ts +6 -5
  145. package/dist/dashboard/app/EntryValidationModal.js +18 -18
  146. package/dist/dashboard/app/Explorer.d.ts +3 -1
  147. package/dist/dashboard/app/Explorer.js +51 -47
  148. package/dist/dashboard/app/ExplorerBatchActions.d.ts +3 -1
  149. package/dist/dashboard/app/ExplorerBatchActions.js +68 -43
  150. package/dist/dashboard/app/ExplorerCards.js +7 -1
  151. package/dist/dashboard/app/ExplorerControls.js +6 -6
  152. package/dist/dashboard/app/ExplorerList.js +5 -5
  153. package/dist/dashboard/app/ExplorerModal.js +1 -1
  154. package/dist/dashboard/app/ExplorerTable.js +14 -11
  155. package/dist/dashboard/app/ImageEditor.js +25 -25
  156. package/dist/dashboard/app/ImagePicker.js +3 -13
  157. package/dist/dashboard/app/LinkPicker.js +29 -27
  158. package/dist/dashboard/app/LocaleMenu.js +7 -7
  159. package/dist/dashboard/app/MediaPreviewCell.js +2 -2
  160. package/dist/dashboard/app/MoveConfirmDialog.d.ts +5 -0
  161. package/dist/dashboard/app/MoveConfirmDialog.js +79 -0
  162. package/dist/dashboard/app/OverviewCell.js +8 -13
  163. package/dist/dashboard/app/ReadOnlyBadge.d.ts +1 -1
  164. package/dist/dashboard/app/RemoveDialog.d.ts +17 -0
  165. package/dist/dashboard/app/{DeleteDialog.js → RemoveDialog.js} +67 -27
  166. package/dist/dashboard/app/RemoveRedirect.d.ts +7 -0
  167. package/dist/dashboard/app/RemoveRedirect.js +123 -0
  168. package/dist/dashboard/app/SearchBar.d.ts +9 -0
  169. package/dist/dashboard/app/SearchBar.js +31 -0
  170. package/dist/dashboard/app/SidebarTree.js +29 -74
  171. package/dist/dashboard/app/WorkspaceMenu.js +8 -8
  172. package/dist/dashboard/app/WorkspaceRoots.js +11 -11
  173. package/dist/dashboard/app/editor/FileEditor.js +16 -16
  174. package/dist/dashboard/app/modals/CreateEntry.js +5 -5
  175. package/dist/dashboard/app/pages/EntryPage.d.ts +2 -1
  176. package/dist/dashboard/app/pages/EntryPage.js +35 -16
  177. package/dist/dashboard/app/pages/SplashPage.js +35 -35
  178. package/dist/dashboard/app/pages/UsersPage.js +14 -14
  179. package/dist/dashboard/app/ui/DashboardModal.js +17 -16
  180. package/dist/dashboard/atoms/dashboard.js +1 -1
  181. package/dist/dashboard/atoms/entry.js +1 -14
  182. package/dist/dashboard/atoms/entryTable.js +1 -0
  183. package/dist/dashboard/atoms/explorer.d.ts +4 -0
  184. package/dist/dashboard/atoms/explorer.js +19 -7
  185. package/dist/dashboard/atoms/graph.js +1 -1
  186. package/dist/dashboard/atoms/link.js +5 -2
  187. package/dist/dashboard/atoms/move.d.ts +5 -0
  188. package/dist/dashboard/atoms/move.js +29 -3
  189. package/dist/dashboard/atoms/moveConfirmation.d.ts +34 -0
  190. package/dist/dashboard/atoms/moveConfirmation.js +95 -0
  191. package/dist/dashboard/atoms/nav.d.ts +9 -5
  192. package/dist/dashboard/atoms/nav.js +120 -45
  193. package/dist/dashboard/atoms/overview.d.ts +6 -5
  194. package/dist/dashboard/atoms/overview.js +2 -16
  195. package/dist/dashboard/atoms/remove.d.ts +112 -0
  196. package/dist/dashboard/atoms/remove.js +268 -0
  197. package/dist/dashboard/atoms/root.d.ts +2 -2
  198. package/dist/dashboard/atoms/root.js +41 -26
  199. package/dist/dashboard/atoms/user.js +1 -1
  200. package/dist/dashboard/atoms/utils.js +1 -1
  201. package/dist/dashboard/boot/Boot.js +25 -17
  202. package/dist/dashboard/boot/BootProd.d.ts +6 -1
  203. package/dist/dashboard/boot/BootProd.js +2 -2
  204. package/dist/dashboard/boot/DashboardWorker.d.ts +1 -0
  205. package/dist/dashboard/boot/DashboardWorker.js +18 -18
  206. package/dist/dashboard/boot/LoadWorker.js +17 -10
  207. package/dist/dashboard/cms/EntryTable.js +6 -3
  208. package/dist/database/BrowserEntryStore.d.ts +17 -29
  209. package/dist/database/BrowserEntryStore.js +116 -144
  210. package/dist/database/DatabaseTables.js +2 -2
  211. package/dist/database/EntryDatabase.js +21 -1
  212. package/dist/database/EntryTransaction.js +0 -35
  213. package/dist/database/EntryUrlAliases.d.ts +4 -0
  214. package/dist/database/EntryUrlAliases.js +16 -10
  215. package/dist/database/driver/RuntimeDatabase.js +3 -3
  216. package/dist/database/driver/WasmDatabase.d.ts +5 -18
  217. package/dist/database/driver/WasmDatabase.js +15 -16
  218. package/dist/database/entry/EntryTable.js +2 -2
  219. package/dist/database/query/EntryQuery.js +1 -1
  220. package/dist/database/query/Relation.js +1 -1
  221. package/dist/database/query/Search.js +2 -2
  222. package/dist/field/check/CheckField.view.js +2 -2
  223. package/dist/field/code/CodeField.view.js +14 -14
  224. package/dist/field/link/EntryLink.d.ts +4 -4
  225. package/dist/field/link/FileLink.d.ts +4 -4
  226. package/dist/field/link/ImageLink.d.ts +4 -4
  227. package/dist/field/link/Link.d.ts +1 -1
  228. package/dist/field/link/LinkField.d.ts +5 -0
  229. package/dist/field/link/LinkField.view.js +246 -372
  230. package/dist/field/link/UrlLink.d.ts +4 -4
  231. package/dist/field/list/ListField.d.ts +2 -0
  232. package/dist/field/list/ListField.view.d.ts +15 -0
  233. package/dist/field/list/ListField.view.js +343 -301
  234. package/dist/field/metadata/MetadataField.d.ts +17 -7
  235. package/dist/field/metadata/MetadataField.js +14 -3
  236. package/dist/field/metadata/MetadataField.view.d.ts +1 -0
  237. package/dist/field/metadata/MetadataField.view.js +27 -23
  238. package/dist/field/richtext/RichTextBlock.d.ts +3 -1
  239. package/dist/field/richtext/RichTextBlock.js +178 -75
  240. package/dist/field/richtext/RichTextBlockHost.js +1 -1
  241. package/dist/field/richtext/RichTextExtensions.js +35 -35
  242. package/dist/field/richtext/RichTextField.view.js +14 -8
  243. package/dist/field/richtext/RichTextInsertMenu.js +43 -27
  244. package/dist/field/views.js +2 -0
  245. package/dist/index.css +490 -309
  246. package/dist/picker/url/UrlPicker.js +1 -0
  247. package/docs/components/badge.md +1 -0
  248. package/docs/components/button.md +1 -0
  249. package/docs/components/content-card.md +1 -0
  250. package/docs/components/search-field.md +1 -0
  251. package/docs/components/sortable-list.md +5 -20
  252. package/docs/components/table.md +1 -0
  253. package/docs/configuration.md +1 -1
  254. package/docs/deploy/self-host.md +1 -1
  255. package/docs/deploy.md +5 -4
  256. package/docs/fields.md +1 -1
  257. package/docs/index.md +1 -1
  258. package/docs/instant-publishing.md +7 -22
  259. package/docs/overviews.md +3 -4
  260. package/docs/upgrading.md +3 -2
  261. package/llms-full.txt +31 -55
  262. package/package.json +2 -3
  263. package/dist/core/db/EntryValidationError.d.ts +0 -13
  264. package/dist/core/db/EntryValidationError.js +0 -23
  265. package/dist/dashboard/app/DeleteDialog.d.ts +0 -11
  266. package/dist/dashboard/atoms/delete.d.ts +0 -68
  267. package/dist/dashboard/atoms/delete.js +0 -143
  268. package/dist/chunks/{chunk-MOWR65LM.js → chunk-PYDOMKJZ.js} +71 -71
package/dist/bundled.d.ts CHANGED
@@ -93,6 +93,17 @@ declare module 'alinea/adapter/next/context' {
93
93
 
94
94
  }
95
95
 
96
+ declare module 'alinea/adapter/next/dashboardUrl' {
97
+
98
+ import { Config } from 'alinea/core/Config';
99
+ /**
100
+ * The dashboard as the site serves it: in development the site proxies the
101
+ * admin path to the dev server, in production it serves the generated file.
102
+ */
103
+ export function dashboardUrl(config: Config, isDev: boolean, handlerUrl: URL | string): string;
104
+
105
+ }
106
+
96
107
  declare module 'alinea/adapter/next/devCache' {
97
108
 
98
109
  /**
@@ -231,15 +242,28 @@ declare module 'alinea/adapter/next/syncCheck' {
231
242
  syncWith(source: RemoteSource, options?: SyncOptions): Promise<string>;
232
243
  }
233
244
  export const CONTENT_SHA_TAG = "alinea-content-sha";
245
+ /**
246
+ * How long renders share the latest content sha. Configured through the
247
+ * config's `syncInterval`, never a query's: all renders share one entry.
248
+ */
249
+ export function contentShaWindow(syncInterval?: number): number;
250
+ /**
251
+ * Tag a static render with the content sha without looking it up. Renders at
252
+ * build skip `syncIfStale`, so this is how their pages join the tag that
253
+ * commits revalidate, and get the same time-based safety net as pages
254
+ * rendered at runtime.
255
+ */
256
+ export function tagContentSha(revalidate?: number): Promise<void>;
234
257
  export function revalidateContentSha(): Promise<void>;
235
258
  /**
236
259
  * Sync the isolate-local db only when it is behind the shared content sha.
237
260
  * Returns true when the freshness question is settled here (synced, forced,
238
261
  * or explicitly disabled) and the caller can skip its throttled sync.
239
262
  * Returns false when the sha could not be determined, so the caller falls
240
- * back to its existing throttle behavior.
263
+ * back to its existing throttle behavior. `shaWindow` is how long renders
264
+ * share the latest sha, see `contentShaWindow`.
241
265
  */
242
- export function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, options?: SyncOptions): Promise<boolean>;
266
+ export function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, shaWindow?: number, options?: SyncOptions): Promise<boolean>;
243
267
  export {};
244
268
 
245
269
  }
@@ -296,6 +320,32 @@ declare module 'alinea/backend/api/BasicAuth' {
296
320
 
297
321
  }
298
322
 
323
+ declare module 'alinea/backend/api/BlobStore' {
324
+
325
+ import type { CommitApi, SyncApi } from 'alinea/core/Connection';
326
+ /**
327
+ * Content blobs kept where every handler instance finds them, such as the
328
+ * backend database: an instance that is behind reads what another one
329
+ * fetched or committed, instead of fetching it from the remote again.
330
+ */
331
+ export interface BlobStore {
332
+ /** The stored blobs among `shas`. */
333
+ readBlobs(shas: ReadonlyArray<string>): Promise<Map<string, Uint8Array>>;
334
+ /** Keep blobs, found again by their sha. */
335
+ storeBlobs(blobs: ReadonlyArray<[sha: string, blob: Uint8Array]>): Promise<void>;
336
+ }
337
+ export function isBlobStore<T extends object>(value: T): value is T & BlobStore;
338
+ /**
339
+ * Answer from the store what it has, and fetch the rest from the remote,
340
+ * storing it. The store only saves requests: when it fails, the remote
341
+ * answers instead.
342
+ */
343
+ export function storedBlobs(store: BlobStore, getBlobs: SyncApi['getBlobs']): SyncApi['getBlobs'];
344
+ /** Commit through the remote, and store the committed content. */
345
+ export function storedWrites(store: BlobStore, write: CommitApi['write']): CommitApi['write'];
346
+
347
+ }
348
+
299
349
  declare module 'alinea/backend/api/CreateBackend' {
300
350
 
301
351
  import type { Config } from 'alinea/core/Config';
@@ -344,10 +394,11 @@ declare module 'alinea/backend/api/DatabaseApi' {
344
394
  import { type Draft, type DraftKey } from 'alinea/core/Draft';
345
395
  import type { User, UserInput } from 'alinea/core/User';
346
396
  import { type Database } from 'rado';
397
+ import type { BlobStore } from 'alinea/backend/api/BlobStore';
347
398
  export interface DatabaseOptions {
348
399
  db: Database;
349
400
  }
350
- export class DatabaseApi implements DraftsApi, UploadsApi, UserApi {
401
+ export class DatabaseApi implements DraftsApi, UploadsApi, UserApi, BlobStore {
351
402
  #private;
352
403
  constructor(context: RequestContext, { db }: DatabaseOptions);
353
404
  getDraft(draftKey: DraftKey): Promise<Draft | undefined>;
@@ -355,6 +406,8 @@ declare module 'alinea/backend/api/DatabaseApi' {
355
406
  prepareUpload(file: string): Promise<UploadResponse>;
356
407
  handleUpload(entryId: string, file: Blob): Promise<void>;
357
408
  previewUpload(entryId: string): Promise<Response>;
409
+ readBlobs(shas: ReadonlyArray<string>): Promise<Map<string, Uint8Array>>;
410
+ storeBlobs(blobs: ReadonlyArray<[sha: string, blob: Uint8Array]>): Promise<void>;
358
411
  enrichUser(user: User): Promise<User>;
359
412
  listUsers(): Promise<Array<User>>;
360
413
  createUser(user: UserInput): Promise<User>;
@@ -1921,6 +1974,7 @@ declare module 'alinea/components' {
1921
1974
  export * from 'alinea/components/Resizable';
1922
1975
  export * from 'alinea/components/SearchField';
1923
1976
  export * from 'alinea/components/Select';
1977
+ export * from 'alinea/components/Sheet';
1924
1978
  export * from 'alinea/components/Sidebar';
1925
1979
  export * from 'alinea/components/SortableList';
1926
1980
  export * from 'alinea/components/Spinner';
@@ -2003,12 +2057,14 @@ declare module 'alinea/components/Badge' {
2003
2057
  export interface BadgeProps extends StyleProps, AriaProps, DataProps {
2004
2058
  icon?: IconType | ReactElement;
2005
2059
  size?: 'default' | 'sm';
2060
+ /** `primary` tints the badge with the accent color, eg. to name a type */
2061
+ color?: 'default' | 'primary';
2006
2062
  /** Colors the badge like the matching entry status */
2007
2063
  status?: ContentStatus;
2008
2064
  title?: string;
2009
2065
  children?: ReactNode;
2010
2066
  }
2011
- export function Badge({ icon, size, status, className, children, ...props }: BadgeProps): import("react").JSX.Element;
2067
+ export function Badge({ icon, size, color, status, className, children, ...props }: BadgeProps): import("react").JSX.Element;
2012
2068
 
2013
2069
  }
2014
2070
 
@@ -2095,11 +2151,16 @@ declare module 'alinea/components/Button' {
2095
2151
  'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time';
2096
2152
  /** Keyboard shortcuts that activate the button, eg. `Meta+K Control+K` */
2097
2153
  'aria-keyshortcuts'?: string;
2154
+ /**
2155
+ * Lets a press on the button start a native drag, eg. of the draggable
2156
+ * element it is in: browsers do not drag from a button otherwise
2157
+ */
2158
+ draggable?: boolean;
2098
2159
  onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
2099
2160
  ref?: Ref<HTMLButtonElement>;
2100
2161
  children?: ReactNode;
2101
2162
  }
2102
- export function Button({ variant, color, size, active, icon, asChild, disabled, loading, className, children, onClick, 'aria-keyshortcuts': ariaKeyShortcuts, ...props }: ButtonProps): import("react").JSX.Element;
2163
+ export function Button({ variant, color, size, active, icon, asChild, disabled, loading, className, children, onClick, 'aria-keyshortcuts': ariaKeyShortcuts, draggable, ...props }: ButtonProps): import("react").JSX.Element;
2103
2164
 
2104
2165
  }
2105
2166
 
@@ -2373,6 +2434,8 @@ declare module 'alinea/components/ContentCard' {
2373
2434
  import type { IconType, StyleProps } from 'alinea/components/types';
2374
2435
  export interface ContentCardProps extends StyleProps {
2375
2436
  title: ReactNode;
2437
+ /** Shown right after the title, eg. a status icon */
2438
+ suffix?: ReactNode;
2376
2439
  /**
2377
2440
  * `icon` (the default) shows the icon on a neutral background, `media`
2378
2441
  * previews a file: its image on the `color` placeholder, or a file icon
@@ -2394,7 +2457,7 @@ declare module 'alinea/components/ContentCard' {
2394
2457
  details?: ReactNode;
2395
2458
  }
2396
2459
  /** The contents of a ContentGridItem, mirroring the dashboard explorer cards */
2397
- export function ContentCard({ title, variant, icon, iconColor, image, color, breadcrumbs, description, details, className, style }: ContentCardProps): import("react").JSX.Element;
2460
+ export function ContentCard({ title, suffix, variant, icon, iconColor, image, color, breadcrumbs, description, details, className, style }: ContentCardProps): import("react").JSX.Element;
2398
2461
  export interface ContentCardSkeletonProps extends StyleProps {
2399
2462
  }
2400
2463
  /** A pulsing placeholder card for items that are still loading */
@@ -3045,6 +3108,30 @@ declare module 'alinea/components/internal/Slot' {
3045
3108
 
3046
3109
  }
3047
3110
 
3111
+ declare module 'alinea/components/internal/sortableDrag' {
3112
+
3113
+ export interface SortableDragOptions {
3114
+ /** The pointerdown on the item that may start the drag */
3115
+ event: PointerEvent;
3116
+ list: HTMLElement;
3117
+ item: HTMLElement;
3118
+ /** The item elements of the list, in order */
3119
+ items(): Array<HTMLElement>;
3120
+ /**
3121
+ * Moves `item` before or after `target`. Must render the new order before
3122
+ * it returns, so the items swap places without a frame in between.
3123
+ */
3124
+ onDrop(item: HTMLElement, target: HTMLElement, position: 'before' | 'after'): void;
3125
+ }
3126
+ /**
3127
+ * Lets the item follow the pointer once it moves past a small threshold,
3128
+ * while the other items move aside to show where it lands. Drops on release,
3129
+ * Escape cancels.
3130
+ */
3131
+ export function pressSortableItem({ event, list, item, items, onDrop }: SortableDragOptions): void;
3132
+
3133
+ }
3134
+
3048
3135
  declare module 'alinea/components/internal/ToggleButton' {
3049
3136
 
3050
3137
  import type { ReactElement, ReactNode, Ref } from 'react';
@@ -3617,6 +3704,11 @@ declare module 'alinea/components/SearchField' {
3617
3704
  placeholder?: string;
3618
3705
  /** Shows a spinner while results are being loaded */
3619
3706
  loading?: boolean;
3707
+ /**
3708
+ * `inline` drops the box so the field reads as the top row of the results
3709
+ * it searches, like a command palette. The row draws its own border.
3710
+ */
3711
+ variant?: 'default' | 'inline';
3620
3712
  name?: string;
3621
3713
  autoFocus?: boolean;
3622
3714
  onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
@@ -3642,7 +3734,7 @@ declare module 'alinea/components/SearchField' {
3642
3734
  * A text input for search queries with a clear button. The `icon` is shown
3643
3735
  * inside the input, in front of the query.
3644
3736
  */
3645
- export function SearchField({ label, description, error, required, disabled, readOnly, icon, shared, onValueChange, placeholder, loading, onBlur, onFocus, onKeyDown, role, 'aria-controls': ariaControls, 'aria-activedescendant': ariaActiveDescendant, 'aria-expanded': ariaExpanded, 'aria-haspopup': ariaHasPopup, 'aria-autocomplete': ariaAutocomplete, className, ...props }: SearchFieldProps): import("react").JSX.Element;
3737
+ export function SearchField({ label, description, error, required, disabled, readOnly, icon, shared, onValueChange, placeholder, loading, variant, onBlur, onFocus, onKeyDown, role, 'aria-controls': ariaControls, 'aria-activedescendant': ariaActiveDescendant, 'aria-expanded': ariaExpanded, 'aria-haspopup': ariaHasPopup, 'aria-autocomplete': ariaAutocomplete, className, ...props }: SearchFieldProps): import("react").JSX.Element;
3646
3738
 
3647
3739
  }
3648
3740
 
@@ -3689,6 +3781,61 @@ declare module 'alinea/components/Select' {
3689
3781
 
3690
3782
  }
3691
3783
 
3784
+ declare module 'alinea/components/Sheet' {
3785
+
3786
+ import { type MouseEvent, type ReactNode } from 'react';
3787
+ import type { AriaProps, DataProps, StyleProps } from 'alinea/components/types';
3788
+ export interface SheetContentProps extends StyleProps, AriaProps, DataProps {
3789
+ /** Called on Escape while focus is inside the sheet, and by `SheetClose` */
3790
+ onClose?: () => void;
3791
+ /** Accessible label of the back button in the header, defaults to "Back" */
3792
+ backLabel?: string;
3793
+ children: ReactNode;
3794
+ }
3795
+ /**
3796
+ * A non-modal panel that fills its positioned container. Mount it to open
3797
+ * the sheet: focus moves to its first field, and returns to the previously
3798
+ * focused element when it unmounts. Labelled by its `SheetTitle` unless
3799
+ * given an `aria-label`.
3800
+ */
3801
+ export function SheetContent({ onClose, backLabel, className, children, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...props }: SheetContentProps): import("react").JSX.Element;
3802
+ export interface SheetPartProps extends StyleProps, AriaProps, DataProps {
3803
+ children?: ReactNode;
3804
+ }
3805
+ export interface SheetHeaderProps extends SheetPartProps {
3806
+ }
3807
+ /**
3808
+ * Top bar of the sheet, eg. a badge, the title, a key hint and SheetClose.
3809
+ * Starts with a back button when the sheet has an `onClose`.
3810
+ */
3811
+ export function SheetHeader({ className, children, ...props }: SheetHeaderProps): import("react").JSX.Element;
3812
+ export interface SheetTitleProps extends SheetPartProps {
3813
+ }
3814
+ export function SheetTitle({ className, ...props }: SheetTitleProps): import("react").JSX.Element;
3815
+ export interface SheetBodyProps extends SheetPartProps {
3816
+ }
3817
+ /** Scrolling content of the sheet, holds SheetSection elements */
3818
+ export function SheetBody({ className, ...props }: SheetBodyProps): import("react").JSX.Element;
3819
+ export interface SheetSectionProps extends SheetPartProps {
3820
+ title?: ReactNode;
3821
+ }
3822
+ export function SheetSection({ title, className, children, ...props }: SheetSectionProps): import("react").JSX.Element;
3823
+ export interface SheetFooterProps extends SheetPartProps {
3824
+ }
3825
+ /**
3826
+ * Bottom bar of the sheet, holds small ghost buttons. Destructive buttons
3827
+ * are pushed to the end.
3828
+ */
3829
+ export function SheetFooter({ className, ...props }: SheetFooterProps): import("react").JSX.Element;
3830
+ export interface SheetCloseProps extends StyleProps, DataProps {
3831
+ 'aria-label': string;
3832
+ /** Defaults to the `onClose` of the surrounding SheetContent */
3833
+ onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
3834
+ }
3835
+ export function SheetClose({ onClick, ...props }: SheetCloseProps): import("react").JSX.Element;
3836
+
3837
+ }
3838
+
3692
3839
  declare module 'alinea/components/Sidebar' {
3693
3840
 
3694
3841
  import { type ReactNode, type Ref } from 'react';
@@ -3752,49 +3899,59 @@ declare module 'alinea/components/Sidebar' {
3752
3899
 
3753
3900
  declare module 'alinea/components/SortableList' {
3754
3901
 
3755
- import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
3902
+ import { type ComponentPropsWithRef, type ComponentPropsWithoutRef } from 'react';
3756
3903
  import { type ButtonProps } from 'alinea/components/Button';
3757
- import { type SurfaceProps } from 'alinea/components/Surface';
3758
- import type { DragMoveEvent, IconType, Key } from 'alinea/components/types';
3759
- export interface SortableListProps extends SurfaceProps {
3904
+ import type { DragMoveEvent, Key } from 'alinea/components/types';
3905
+ export interface SortableListProps extends ComponentPropsWithoutRef<'div'> {
3760
3906
  /**
3761
- * Enables reordering: items with an `id` can be dragged by their
3762
- * `SortableListHandle`, with a pointer or with the keyboard (Enter on the
3763
- * handle, Tab to an item, Enter to drop). Called with the key of the dragged
3764
- * item and the item it was dropped before or after.
3765
- * Items only drop within the list they were dragged from.
3907
+ * Enables reordering: items with an `id` follow the pointer when their
3908
+ * `SortableListItemHeader` is dragged, the other items move aside as it
3909
+ * passes them. With focus in the header, Alt+ArrowUp and Alt+ArrowDown
3910
+ * move an item. Called with the key of the moved item and the item it was
3911
+ * dropped before or after. Update the order synchronously: the dropped item
3912
+ * glides into the place it renders in right after this returns.
3766
3913
  */
3767
3914
  onReorder?: (event: DragMoveEvent) => void;
3768
- /** Mime type the dragged items carry, defaults to `alinea/sortable-list-item` */
3769
- dragType?: string;
3770
3915
  }
3771
- export function SortableList({ className, role, onReorder, dragType, ...props }: SortableListProps): import("react").JSX.Element;
3916
+ export function SortableList({ className, role, onReorder, ...props }: SortableListProps): import("react").JSX.Element;
3772
3917
  export interface SortableListItemProps extends Omit<ComponentPropsWithoutRef<'div'>, 'id'> {
3773
3918
  /**
3774
3919
  * Identifies the item within its list. Items with an id can be dragged when
3775
3920
  * the list has `onReorder`. The id is not rendered as a DOM id.
3776
3921
  */
3777
3922
  id?: Key;
3778
- /** Rendered under the pointer while dragging, eg. a `SortableListDragPreview` */
3779
- dragPreview?: ReactNode;
3923
+ /** Highlights the item, eg. while its settings are open next to the list */
3924
+ current?: boolean;
3780
3925
  }
3781
- export function SortableListItem({ id, dragPreview, ...props }: SortableListItemProps): import("react").JSX.Element;
3926
+ export function SortableListItem({ id, className, current, ...props }: SortableListItemProps): import("react").JSX.Element;
3782
3927
  export interface SortableListItemHeaderProps extends ComponentPropsWithoutRef<'div'> {
3783
3928
  }
3784
- /** Top bar of an item, holds its handle, title and actions */
3785
- export function SortableListItemHeader({ className, ...props }: SortableListItemHeaderProps): import("react").JSX.Element;
3786
- export interface SortableListHandleProps extends ComponentPropsWithoutRef<'span'> {
3787
- }
3788
3929
  /**
3789
- * Drags its `SortableListItem` when the list has `onReorder`. Focusable in
3790
- * that case: press Enter to start a keyboard drag. Place it in the
3791
- * `SortableListItemHeader`, it shows while the header is hovered or focused.
3930
+ * Top bar of an item, holds its title and actions. Drags the item when the
3931
+ * list has `onReorder`, a press that does not move still clicks.
3792
3932
  */
3793
- export function SortableListHandle({ className, ...props }: SortableListHandleProps): import("react").JSX.Element;
3933
+ export function SortableListItemHeader({ className, onPointerDownCapture, onKeyDown, onDragStart, ...props }: SortableListItemHeaderProps): import("react").JSX.Element;
3794
3934
  export interface SortableListItemTitleProps extends ComponentPropsWithoutRef<'div'> {
3795
3935
  }
3796
3936
  /** Row of the toggle, badges and description of an item, fills the header */
3797
3937
  export function SortableListItemTitle({ className, ...props }: SortableListItemTitleProps): import("react").JSX.Element;
3938
+ export interface SortableListItemTriggerProps extends Omit<ButtonProps, 'variant' | 'size' | 'icon' | 'asChild'> {
3939
+ /** Ends the button with a "…", defaults to true */
3940
+ more?: boolean;
3941
+ }
3942
+ /**
3943
+ * The badges and description of an item as one button that ends in a "…",
3944
+ * eg. to open the settings of the item. Fills the title up to the actions.
3945
+ */
3946
+ export function SortableListItemTrigger({ className, more, children, ...props }: SortableListItemTriggerProps): import("react").JSX.Element;
3947
+ export interface SortableListItemInsertProps extends Omit<ComponentPropsWithRef<'button'>, 'children'> {
3948
+ }
3949
+ /**
3950
+ * Inserts an item before the one it is placed in: a line with an add button
3951
+ * that shows when the pointer is on the border above the item. Place it first
3952
+ * in the `SortableListItem`.
3953
+ */
3954
+ export function SortableListItemInsert({ className, ...props }: SortableListItemInsertProps): import("react").JSX.Element;
3798
3955
  export interface SortableListItemDescriptionProps extends ComponentPropsWithoutRef<'span'> {
3799
3956
  }
3800
3957
  /** Muted, truncated text next to the badges of an item, eg. its label */
@@ -3803,7 +3960,7 @@ declare module 'alinea/components/SortableList' {
3803
3960
  }
3804
3961
  export function SortableListItemActions({ className, ...props }: SortableListItemActionsProps): import("react").JSX.Element;
3805
3962
  export interface SortableListItemToggleProps extends Omit<ButtonProps, 'variant' | 'children' | 'size'> {
3806
- /** Whether the item content is shown, rotates the fold icon */
3963
+ /** Whether the item content is shown, picks the unfold icon */
3807
3964
  expanded: boolean;
3808
3965
  }
3809
3966
  /** Folds the content of an item */
@@ -3822,12 +3979,6 @@ declare module 'alinea/components/SortableList' {
3822
3979
  }
3823
3980
  /** A group of settings or actions in the popover of an item */
3824
3981
  export function SortableListItemSettings({ variant, className, ...props }: SortableListItemSettingsProps): import("react").JSX.Element;
3825
- export interface SortableListDragPreviewProps extends ComponentPropsWithoutRef<'div'> {
3826
- icon?: IconType;
3827
- label: ReactNode;
3828
- }
3829
- /** Card shown under the pointer while dragging an item */
3830
- export function SortableListDragPreview({ className, icon, label, ...props }: SortableListDragPreviewProps): import("react").JSX.Element;
3831
3982
  export interface SortableListAddProps extends ComponentPropsWithoutRef<'div'> {
3832
3983
  }
3833
3984
  /** Last row of the list holding the buttons that add items */
@@ -3996,8 +4147,10 @@ declare module 'alinea/components/Table' {
3996
4147
  title: ReactNode;
3997
4148
  /** A small caption above the title, eg. the parent path */
3998
4149
  label?: ReactNode;
4150
+ /** Shown right after the title, eg. a status icon */
4151
+ suffix?: ReactNode;
3999
4152
  }
4000
- export function TableTitle({ icon, title, label, className, style }: TableTitleProps): import("react").JSX.Element;
4153
+ export function TableTitle({ icon, title, label, suffix, className, style }: TableTitleProps): import("react").JSX.Element;
4001
4154
 
4002
4155
  }
4003
4156
 
@@ -4615,7 +4768,12 @@ declare module 'alinea/core/Config' {
4615
4768
  preview?: Preview;
4616
4769
  /** Every edit will pass through a draft status before being published */
4617
4770
  enableDrafts?: boolean;
4618
- /** Fallback interval in seconds for polling content updates (freshness is primarily sha-driven) */
4771
+ /**
4772
+ * Fallback interval in seconds for polling content updates (freshness is
4773
+ * primarily sha-driven). In Next.js it also sets how long renders share the
4774
+ * latest content sha, which bounds how soon edits made outside the handler
4775
+ * show up: five minutes when unset.
4776
+ */
4619
4777
  syncInterval?: number;
4620
4778
  /** Maximum file upload size in bytes */
4621
4779
  maxUploadSize?: number;
@@ -4900,24 +5058,6 @@ declare module 'alinea/core/db/EntryUrlConflictError' {
4900
5058
 
4901
5059
  }
4902
5060
 
4903
- declare module 'alinea/core/db/EntryValidationError' {
4904
-
4905
- import { HttpError } from 'alinea/core/HttpError';
4906
- import { type FieldValidationError } from 'alinea/core/Validation';
4907
- export interface EntryValidationErrorInfo {
4908
- entryId: string;
4909
- title?: string;
4910
- errors: Array<FieldValidationError>;
4911
- }
4912
- /** A published version would contain fields which fail validation */
4913
- export class EntryValidationError extends HttpError {
4914
- info: EntryValidationErrorInfo;
4915
- name: string;
4916
- constructor(info: EntryValidationErrorInfo);
4917
- }
4918
-
4919
- }
4920
-
4921
5061
  declare module 'alinea/core/db/IndexEvent' {
4922
5062
 
4923
5063
  export class IndexEvent extends Event {
@@ -5282,7 +5422,15 @@ declare module 'alinea/core/Document' {
5282
5422
  path: PathField;
5283
5423
  metadata: MetadataField;
5284
5424
  };
5285
- export function document<Fields extends FieldsDefinition = {}>(label: string, { fields, ...config }: TypeConfig<Fields> | ContainerTypeConfig<Fields>): Type<Document & Fields>;
5425
+ export type DocumentConfig<Fields, Seo, Details> = (TypeConfig<Fields> | ContainerTypeConfig<Fields>) & {
5426
+ /** Fields shown in the SEO tab, above the previews */
5427
+ seo?: Seo;
5428
+ /** Fields shown in the Details tab, above the created and updated details */
5429
+ details?: Details;
5430
+ };
5431
+ export function document<Fields extends FieldsDefinition = {}, Seo extends FieldsDefinition = {}, Details extends FieldsDefinition = {}>(label: string, { fields, seo, details, ...config }: DocumentConfig<Fields, Seo, Details>): Type<Document & Fields & Seo & Details>;
5432
+ /** Whether `type` was created with `Config.document` */
5433
+ export function isDocument(type: Type): boolean;
5286
5434
 
5287
5435
  }
5288
5436
 
@@ -6754,7 +6902,7 @@ declare module 'alinea/core/Overview' {
6754
6902
  export interface OverviewSort {
6755
6903
  /**
6756
6904
  * A key of `overview.sorts`, of `overview.columns`, or a built-in column:
6757
- * `title`, `type`, `status`, `updated`, `author`
6905
+ * `title`, `type`, `updated`, `author`
6758
6906
  */
6759
6907
  column: string;
6760
6908
  direction: OverviewSortDirection;
@@ -6864,8 +7012,6 @@ declare module 'alinea/core/Overview' {
6864
7012
  export interface OverviewBuiltins {
6865
7013
  /** The entry type, shown by default when the children have several types */
6866
7014
  type?: boolean;
6867
- /** The publication status, shown by default when the statuses differ */
6868
- status?: boolean;
6869
7015
  /** When the entry was last edited, shown by default when recorded */
6870
7016
  updated?: boolean;
6871
7017
  /** Who last edited the entry, shown by default when recorded */
@@ -6876,7 +7022,7 @@ declare module 'alinea/core/Overview' {
6876
7022
  /**
6877
7023
  * Columns shown after the title and the built-in columns, or before the
6878
7024
  * built-in columns with `position: 'start'`. A column keyed `type`,
6879
- * `status`, `updated` or `author` replaces that built-in column.
7025
+ * `updated` or `author` replaces that built-in column.
6880
7026
  */
6881
7027
  columns?: Record<string, OverviewColumn>;
6882
7028
  /** Show or hide the built-in columns */
@@ -7538,6 +7684,10 @@ declare module 'alinea/core/source/GithubSource' {
7538
7684
  export class GithubSource implements Source {
7539
7685
  #private;
7540
7686
  constructor(options: GithubSourceOptions);
7687
+ /** Request GitHub, counting the request against its budget. */
7688
+ protected githubFetch(input: string, init?: RequestInit): Promise<Response>;
7689
+ /** Run an operation, and log what it spent of GitHub's budgets. */
7690
+ protected spending<T>(operation: string, run: () => Promise<T>): Promise<T>;
7541
7691
  protected get contentLocation(): string;
7542
7692
  getTree(): Promise<ReadonlyTree>;
7543
7693
  shaAt(ref: string): Promise<string>;
@@ -7548,6 +7698,32 @@ declare module 'alinea/core/source/GithubSource' {
7548
7698
 
7549
7699
  }
7550
7700
 
7701
+ declare module 'alinea/core/source/GithubUsage' {
7702
+
7703
+ /**
7704
+ * Counts the GitHub requests a source makes against its REST and GraphQL
7705
+ * budgets, and logs what each operation spent and what is left, so the logs
7706
+ * show where a rate limit goes. Answers of 304 are free and not counted.
7707
+ */
7708
+ export class GithubUsage {
7709
+ #private;
7710
+ rest: number;
7711
+ graphql: number;
7712
+ record(response: Response): void;
7713
+ /** The requests counted so far, to log what an operation adds. */
7714
+ count(): {
7715
+ rest: number;
7716
+ graphql: number;
7717
+ };
7718
+ /** Log what `operation` spent since `before`, if anything. */
7719
+ log(operation: string, before: {
7720
+ rest: number;
7721
+ graphql: number;
7722
+ }): void;
7723
+ }
7724
+
7725
+ }
7726
+
7551
7727
  declare module 'alinea/core/source/GitUtils' {
7552
7728
 
7553
7729
  import type { Entry } from 'alinea/core/source/Tree';
@@ -7953,6 +8129,10 @@ declare module 'alinea/core/TextDoc' {
7953
8129
  export interface BlockNode {
7954
8130
  _id: string;
7955
8131
  _type: string;
8132
+ /** A label editors give the block, its anchor follows it by default */
8133
+ _label?: string;
8134
+ /** Lets links point to the block, unique within the entry */
8135
+ _anchor?: string;
7956
8136
  }
7957
8137
  export namespace BlockNode {
7958
8138
  const id = "_id";
@@ -8886,7 +9066,6 @@ declare module 'alinea/core/Validation' {
8886
9066
  /** Validate entry data: required, min, max and validate options */
8887
9067
  export function validateEntry(type: Type, data: unknown, options?: ValidateEntryOptions): Array<FieldValidationError>;
8888
9068
  export function formatFieldPath(path: FieldPath): string;
8889
- export function formatValidationErrors(errors: ReadonlyArray<FieldValidationError>): string;
8890
9069
 
8891
9070
  }
8892
9071
 
@@ -8986,7 +9165,7 @@ declare module 'alinea/dashboard/App' {
8986
9165
  import type { LocalConnection } from 'alinea/core/Connection';
8987
9166
  import type { WriteableGraph } from 'alinea/core/db/WriteableGraph';
8988
9167
  import type { User } from 'alinea/core/User';
8989
- import { type ComponentType } from 'react';
9168
+ import { type ComponentType, type ReactNode } from 'react';
8990
9169
  import 'alinea/dashboard/global.css';
8991
9170
  export interface AppProps {
8992
9171
  graph: WriteableGraph;
@@ -8999,7 +9178,7 @@ declare module 'alinea/dashboard/App' {
8999
9178
  /** The user shown as signed in when the dashboard runs locally */
9000
9179
  user?: User;
9001
9180
  }
9002
- export const appAtom: import("jotai").Atom<readonly [false, import("react").JSX.Element] | readonly [true, import("react").JSX.Element | undefined]>;
9181
+ export const appAtom: import("jotai").Atom<readonly [false, string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | null | undefined] | readonly [true, string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | null | undefined]>;
9003
9182
  export function App(props: AppProps): import("react").JSX.Element;
9004
9183
 
9005
9184
  }
@@ -9073,6 +9252,72 @@ declare module 'alinea/dashboard/app/AuthView' {
9073
9252
 
9074
9253
  }
9075
9254
 
9255
+ declare module 'alinea/dashboard/app/BlockSheet' {
9256
+
9257
+ import type { StyleProps } from 'alinea/components';
9258
+ import { type ReactNode } from 'react';
9259
+ const triggerAttribute = "data-block-sheet-trigger";
9260
+ export interface BlockSheetProviderProps {
9261
+ children: ReactNode;
9262
+ }
9263
+ /** Holds the block settings sheet of an entry editor, one open at a time */
9264
+ export function BlockSheetProvider({ children }: BlockSheetProviderProps): import("react").JSX.Element;
9265
+ export interface BlockSheetBoundaryProps {
9266
+ children: ReactNode;
9267
+ }
9268
+ /**
9269
+ * Fields inside have no block settings sheet, eg. in a modal that covers the
9270
+ * sidebar the sheet would open in
9271
+ */
9272
+ export function BlockSheetBoundary({ children }: BlockSheetBoundaryProps): import("react").JSX.Element;
9273
+ export interface BlockSheetSlotProps extends StyleProps {
9274
+ }
9275
+ /** Where the open sheet renders, covers its positioned container */
9276
+ export function BlockSheetSlot({ className, style }: BlockSheetSlotProps): import("react").JSX.Element;
9277
+ export interface BlockSheetTriggerProps {
9278
+ [triggerAttribute]: string;
9279
+ }
9280
+ export interface BlockSheetState {
9281
+ /** False outside an entry editor, where there is no sidebar to cover */
9282
+ available: boolean;
9283
+ open: boolean;
9284
+ /**
9285
+ * Spread on the button that toggles the sheet, so moving focus to it
9286
+ * doesn't close the sheet before the toggle
9287
+ */
9288
+ triggerProps: BlockSheetTriggerProps;
9289
+ setOpen(open: boolean): void;
9290
+ toggle(): void;
9291
+ }
9292
+ /** Open state of the sheet of block `id`, closed outside an entry editor */
9293
+ export function useBlockSheet(id: string): BlockSheetState;
9294
+ /** Whether any block sheet is open or still animating out */
9295
+ export function useBlockSheetOpen(): boolean;
9296
+ export interface BlockSheetProps {
9297
+ id: string;
9298
+ children: ReactNode;
9299
+ }
9300
+ /**
9301
+ * Renders the sheet of block `id` in the slot while it is open. A portal
9302
+ * keeps the sheet owned by its block, so its fields keep the block's node,
9303
+ * editor context, local state and callbacks.
9304
+ */
9305
+ export function BlockSheet({ id, children }: BlockSheetProps): import("react").ReactPortal | null;
9306
+ export {};
9307
+
9308
+ }
9309
+
9310
+ declare module 'alinea/dashboard/app/BlockSheetStoryFrame' {
9311
+
9312
+ import type { ReactNode } from 'react';
9313
+ export interface BlockSheetStoryFrameProps {
9314
+ children: ReactNode;
9315
+ }
9316
+ /** Shows field stories next to a panel that hosts their block sheets */
9317
+ export function BlockSheetStoryFrame({ children }: BlockSheetStoryFrameProps): import("react").JSX.Element;
9318
+
9319
+ }
9320
+
9076
9321
  declare module 'alinea/dashboard/app/CompactField' {
9077
9322
 
9078
9323
  import { Field } from 'alinea/core/Field';
@@ -9172,22 +9417,6 @@ declare module 'alinea/dashboard/app/DashboardMeta' {
9172
9417
 
9173
9418
  }
9174
9419
 
9175
- declare module 'alinea/dashboard/app/DeleteDialog' {
9176
-
9177
- import type { DeletePlan } from 'alinea/dashboard/atoms/delete';
9178
- export interface DeleteDialogProps {
9179
- /** What to delete, loaded before it opens, closed when undefined */
9180
- plan: DeletePlan | undefined;
9181
- onClose(): void;
9182
- onConfirm(plan: DeletePlan): Promise<void>;
9183
- /** Archives the entries instead, offered when all of them can be */
9184
- onArchive?(plan: DeletePlan): Promise<void>;
9185
- }
9186
- /** Confirms deleting entries, warning about the links that will break */
9187
- export function DeleteDialog({ plan, onClose, onConfirm, onArchive }: DeleteDialogProps): import("react").JSX.Element;
9188
-
9189
- }
9190
-
9191
9420
  declare module 'alinea/dashboard/app/editor/FileEditor' {
9192
9421
 
9193
9422
  export interface FileEditorProps {
@@ -9430,6 +9659,34 @@ declare module 'alinea/dashboard/app/EntrySidebarToggle' {
9430
9659
 
9431
9660
  }
9432
9661
 
9662
+ declare module 'alinea/dashboard/app/EntryStatusIcon' {
9663
+
9664
+ import { type IconType } from 'alinea/components';
9665
+ import type { EntryStatus } from 'alinea/core/Entry';
9666
+ export interface EntryStatusDisplay {
9667
+ icon: IconType;
9668
+ label: string;
9669
+ status: 'draft' | 'unpublished' | 'archived' | 'untranslated';
9670
+ }
9671
+ /** A listed version of an entry, its draft when it has one */
9672
+ export interface ListedEntry {
9673
+ status?: EntryStatus;
9674
+ /** The version is the main one, a draft then was never published */
9675
+ main?: boolean;
9676
+ locale: string | null;
9677
+ }
9678
+ /**
9679
+ * How a listed entry differs from a published one, undefined when it does
9680
+ * not. With a locale, entries of another locale are untranslated.
9681
+ */
9682
+ export function entryStatus(entry: ListedEntry, locale?: string | null): EntryStatusDisplay | undefined;
9683
+ export interface EntryStatusIconProps {
9684
+ status: EntryStatusDisplay;
9685
+ }
9686
+ export function EntryStatusIcon({ status }: EntryStatusIconProps): import("react").JSX.Element;
9687
+
9688
+ }
9689
+
9433
9690
  declare module 'alinea/dashboard/app/EntryTranslationBanner' {
9434
9691
 
9435
9692
  export interface EntryTranslationBannerProps {
@@ -9448,16 +9705,17 @@ declare module 'alinea/dashboard/app/EntryValidationModal' {
9448
9705
 
9449
9706
  import type { FieldValidationError } from 'alinea/core/Validation';
9450
9707
  export interface EntryValidationFailure {
9451
- errors?: Array<FieldValidationError>;
9452
- /** Shown when the errors came back from the server without details */
9453
- message?: string;
9708
+ errors: Array<FieldValidationError>;
9709
+ /** Publishes the entry as it is */
9710
+ publish(): void | Promise<void>;
9454
9711
  }
9455
9712
  export interface EntryValidationModalProps {
9456
9713
  failure?: EntryValidationFailure;
9457
9714
  onClose(): void;
9715
+ onPublish(publish: () => void | Promise<void>): void;
9458
9716
  }
9459
- /** Explains why an entry cannot be published and lists the invalid fields */
9460
- export function EntryValidationModal({ failure, onClose }: EntryValidationModalProps): import("react").JSX.Element;
9717
+ /** Lists the invalid fields of an entry about to be published */
9718
+ export function EntryValidationModal({ failure, onClose, onPublish }: EntryValidationModalProps): import("react").JSX.Element;
9461
9719
 
9462
9720
  }
9463
9721
 
@@ -9500,6 +9758,8 @@ declare module 'alinea/dashboard/app/Explorer' {
9500
9758
  */
9501
9759
  onPick?: (entry: DashboardEntry) => void;
9502
9760
  page: ExplorerReadyPage;
9761
+ /** Floats over the list, such as the actions on the selected entries */
9762
+ children?: ReactNode;
9503
9763
  }
9504
9764
  interface ExplorerSearchProps {
9505
9765
  autoFocus?: boolean;
@@ -9510,7 +9770,7 @@ declare module 'alinea/dashboard/app/Explorer' {
9510
9770
  }
9511
9771
  export function ExplorerSearch({ autoFocus, explorer, onEntryAction, onEscape, page }: ExplorerSearchProps): import("react").JSX.Element;
9512
9772
  export function ExplorerHeader({ canBrowse, autoFocusSearch, onSearchEscape, controls, explorer, headerEntry, navigate, page, readOnly, titleControls }: ExplorerHeaderProps): import("react").JSX.Element;
9513
- export function ExplorerBody({ compactTable, explorer, onPick, page }: ExplorerBodyProps): import("react").JSX.Element;
9773
+ export function ExplorerBody({ compactTable, explorer, onPick, page, children }: ExplorerBodyProps): import("react").JSX.Element;
9514
9774
  export function Explorer({ controls, explorer, headerEntry, page: loadedPage, readOnly, titleControls }: ExplorerProps): import("react").JSX.Element;
9515
9775
  export {};
9516
9776
 
@@ -9526,14 +9786,16 @@ declare module 'alinea/dashboard/app/ExplorerBatchActions' {
9526
9786
  export function ExplorerBatchActions({ explorer }: ExplorerBatchActionsProps): import("react").JSX.Element | null;
9527
9787
  export interface ExplorerBatchActionBarProps {
9528
9788
  count: number;
9789
+ /** Every selected entry can be deleted */
9529
9790
  canDelete: boolean;
9791
+ /** Every selected entry can be moved */
9530
9792
  canMove: boolean;
9531
9793
  isPending?: boolean;
9532
9794
  onClear(): void;
9533
9795
  onDelete(): void;
9534
9796
  onMove(): void;
9535
9797
  }
9536
- /** Shows how many entries are selected and the actions on them */
9798
+ /** Floats over the list: how many entries are selected and their actions */
9537
9799
  export function ExplorerBatchActionBar({ count, canDelete, canMove, isPending, onClear, onDelete, onMove }: ExplorerBatchActionBarProps): import("react").JSX.Element;
9538
9800
 
9539
9801
  }
@@ -9828,6 +10090,16 @@ declare module 'alinea/dashboard/app/modals/CreateEntry' {
9828
10090
 
9829
10091
  }
9830
10092
 
10093
+ declare module 'alinea/dashboard/app/MoveConfirmDialog' {
10094
+
10095
+ /**
10096
+ * Asks before a drag moves pages under another parent, changing their urls,
10097
+ * or tells why the dragged entries can not move there
10098
+ */
10099
+ export function MoveConfirmDialog(): import("react").JSX.Element;
10100
+
10101
+ }
10102
+
9831
10103
  declare module 'alinea/dashboard/app/MoveDialog' {
9832
10104
 
9833
10105
  import { type MoveTargets } from 'alinea/dashboard/atoms/move';
@@ -9882,7 +10154,8 @@ declare module 'alinea/dashboard/app/OverviewCell' {
9882
10154
  declare module 'alinea/dashboard/app/pages/EntryPage' {
9883
10155
 
9884
10156
  import { Page } from 'alinea/dashboard/atoms/nav';
9885
- export const entryPage: (page: Page, get: import("jotai").Getter) => import("react").ReactNode | Promise<import("react").ReactNode>;
10157
+ import { type ReactNode } from 'react';
10158
+ export const entryPage: (page: Page, get: import("jotai").Getter) => ReactNode | Promise<ReactNode>;
9886
10159
  export interface NotFoundPanelProps {
9887
10160
  title: string;
9888
10161
  message: string;
@@ -10001,12 +10274,60 @@ declare module 'alinea/dashboard/app/PendingUploadsView' {
10001
10274
  declare module 'alinea/dashboard/app/ReadOnlyBadge' {
10002
10275
 
10003
10276
  import type { ComponentPropsWithoutRef } from 'react';
10004
- export interface ReadOnlyBadgeProps extends ComponentPropsWithoutRef<'span'> {
10277
+ export interface ReadOnlyBadgeProps extends Omit<ComponentPropsWithoutRef<'span'>, 'color'> {
10005
10278
  }
10006
10279
  export function ReadOnlyBadge(props: ReadOnlyBadgeProps): import("react").JSX.Element;
10007
10280
 
10008
10281
  }
10009
10282
 
10283
+ declare module 'alinea/dashboard/app/RemoveDialog' {
10284
+
10285
+ import { type RemoveAction, type RemovePlan } from 'alinea/dashboard/atoms/remove';
10286
+ export interface RemoveDialogProps {
10287
+ action: RemoveAction;
10288
+ /** What to remove, loaded before it opens, closed when undefined */
10289
+ plan: RemovePlan | undefined;
10290
+ onClose(): void;
10291
+ /** Deletes or archives the entries, as the action says */
10292
+ onConfirm(plan: RemovePlan): Promise<void>;
10293
+ /** Archives the entries instead of deleting them, offered when all of them
10294
+ * can be */
10295
+ onArchive?(plan: RemovePlan): Promise<void>;
10296
+ }
10297
+ /**
10298
+ * Confirms deleting or archiving entries, warning about the links that will
10299
+ * break, and redirects their URLs to another page
10300
+ */
10301
+ export function RemoveDialog({ action, plan, onClose, onConfirm, onArchive }: RemoveDialogProps): import("react").JSX.Element;
10302
+
10303
+ }
10304
+
10305
+ declare module 'alinea/dashboard/app/RemoveRedirect' {
10306
+
10307
+ import { type RemovePlan } from 'alinea/dashboard/atoms/remove';
10308
+ export interface RemoveRedirectProps {
10309
+ plan: RemovePlan;
10310
+ disabled?: boolean;
10311
+ }
10312
+ /** Picks a page the URLs of the removed pages redirect to */
10313
+ export function RemoveRedirect({ plan, disabled }: RemoveRedirectProps): import("react").JSX.Element | null;
10314
+
10315
+ }
10316
+
10317
+ declare module 'alinea/dashboard/app/SearchBar' {
10318
+
10319
+ import type { ReactNode } from 'react';
10320
+ export interface SearchBarProps {
10321
+ /** The search field, inline so it reads as the row itself */
10322
+ children: ReactNode;
10323
+ /** Compact controls at the end of the row, after a divider */
10324
+ controls?: ReactNode;
10325
+ }
10326
+ /** A search field as the top row of the results it searches */
10327
+ export function SearchBar({ children, controls }: SearchBarProps): import("react").JSX.Element;
10328
+
10329
+ }
10330
+
10010
10331
  declare module 'alinea/dashboard/app/SidebarLayout' {
10011
10332
 
10012
10333
  import type { ReactNode } from 'react';
@@ -10382,79 +10703,6 @@ declare module 'alinea/dashboard/atoms/dashboard' {
10382
10703
 
10383
10704
  }
10384
10705
 
10385
- declare module 'alinea/dashboard/atoms/delete' {
10386
-
10387
- import type { EntryReference } from 'alinea/core/db/EntryReference';
10388
- import { type Atom, type PrimitiveAtom } from 'jotai';
10389
- import { type EntryReferenceWithSource } from 'alinea/dashboard/atoms/entry';
10390
- /** An entry that is about to be deleted, in the locale it is shown in */
10391
- export interface DeleteSubject {
10392
- id: string;
10393
- title: string;
10394
- type: string;
10395
- workspace: string;
10396
- root: string;
10397
- /** Null for entries without languages */
10398
- locale: string | null;
10399
- parents: Array<string>;
10400
- hasChildren: boolean;
10401
- }
10402
- /** A translation that is removed, a null locale removes every version */
10403
- export interface DeleteRemoval {
10404
- id: string;
10405
- locale: string | null;
10406
- }
10407
- /** An entry that is deleted with the translation of its parent */
10408
- export interface DeleteDescendant extends DeleteRemoval {
10409
- subjectId: string;
10410
- }
10411
- /** Which languages of the entries are deleted and the links that will break */
10412
- export interface DeletePlan {
10413
- subjects: Array<DeleteSubject>;
10414
- /** The languages of a single entry the user can pick to delete, undefined
10415
- * for a batch */
10416
- locales?: Array<string>;
10417
- /** The picked languages, the shown language at first */
10418
- selectedLocales: PrimitiveAtom<Array<string>>;
10419
- removals: Atom<Array<DeleteRemoval>>;
10420
- /** The versions linking to the removed translations and their contents,
10421
- * that are not removed themselves */
10422
- references: Atom<Array<EntryReferenceWithSource>>;
10423
- /** How many entries the user can not read link to them, their titles stay
10424
- * hidden */
10425
- hiddenSources: Atom<number>;
10426
- /** Every removed translation is published and can be archived instead */
10427
- archivable: Atom<boolean>;
10428
- }
10429
- export interface DeletePlanOptions {
10430
- subjects: Array<DeleteSubject>;
10431
- locales?: Array<string>;
10432
- references?: Array<EntryReferenceWithSource>;
10433
- /** Links from versions the user can not read */
10434
- hidden?: Array<EntryReference>;
10435
- descendants?: Array<DeleteDescendant>;
10436
- /** The translations that can be archived */
10437
- archivable?: Array<DeleteRemoval>;
10438
- }
10439
- /**
10440
- * Entries without languages are deleted entirely, the others in the picked
10441
- * languages or, in a batch, the language they are shown in.
10442
- */
10443
- export function createDeletePlan({ subjects, locales, references, hidden, descendants, archivable }: DeletePlanOptions): DeletePlan;
10444
- /** Loads the references to the entries before the delete dialog opens */
10445
- export const loadDeletePlanAtom: import("jotai").WritableAtom<null, [subjects: DeleteSubject[], locales?: string[] | undefined], Promise<DeletePlan>> & {
10446
- init: null;
10447
- };
10448
- export const deleteEntriesAtom: import("jotai").WritableAtom<null, [plan: DeletePlan], Promise<void>> & {
10449
- init: null;
10450
- };
10451
- /** Archives the translations the plan would delete */
10452
- export const archiveEntriesAtom: import("jotai").WritableAtom<null, [plan: DeletePlan], Promise<void>> & {
10453
- init: null;
10454
- };
10455
-
10456
- }
10457
-
10458
10706
  declare module 'alinea/dashboard/atoms/editor' {
10459
10707
 
10460
10708
  import { Field, type EntryAnchorTarget, type FieldOptions } from 'alinea/core/Field';
@@ -10867,6 +11115,8 @@ declare module 'alinea/dashboard/atoms/explorer' {
10867
11115
  initialSearchScope?: 'workspace' | 'everything';
10868
11116
  location?: ExplorerLocation;
10869
11117
  limitLocations?: Array<ExplorerLimitLocation>;
11118
+ /** Lists only the media roots, for pickers of files and images */
11119
+ media?: boolean;
10870
11120
  mode?: 'browse' | 'search';
10871
11121
  nestedNavigation?: boolean;
10872
11122
  /**
@@ -10971,6 +11221,8 @@ declare module 'alinea/dashboard/atoms/explorer' {
10971
11221
  createdAt?: number | null;
10972
11222
  id: string;
10973
11223
  status?: EntryStatus;
11224
+ /** The version is the main one, a draft then was never published */
11225
+ main?: boolean;
10974
11226
  title: string;
10975
11227
  path: string;
10976
11228
  updatedAt?: number | null;
@@ -11299,6 +11551,11 @@ declare module 'alinea/dashboard/atoms/move' {
11299
11551
  * and entries missing a language of the moved entries are not a target
11300
11552
  */
11301
11553
  canSelect(item: ExplorerItemData): boolean;
11554
+ /**
11555
+ * Why the entries can not move into `parent`, or to the top level of the
11556
+ * root with null; undefined when they can
11557
+ */
11558
+ refusal(parent: MoveSubject | null): string | undefined;
11302
11559
  /** The entries can be moved to the top level of the root */
11303
11560
  rootAccepts: boolean;
11304
11561
  }
@@ -11334,9 +11591,48 @@ declare module 'alinea/dashboard/atoms/move' {
11334
11591
 
11335
11592
  }
11336
11593
 
11594
+ declare module 'alinea/dashboard/atoms/moveConfirmation' {
11595
+
11596
+ import type { DropTarget, Key } from 'alinea/components';
11597
+ /**
11598
+ * A dragged move to confirm: the pages it gives another parent, and so
11599
+ * another url, or why it can not move there
11600
+ */
11601
+ export interface MoveConfirmation {
11602
+ pages: Array<{
11603
+ id: string;
11604
+ title: string;
11605
+ }>;
11606
+ refusal?: string;
11607
+ resolve(confirmed: boolean): void;
11608
+ }
11609
+ /** Whether moving pages asks first, until the editor stops it here */
11610
+ export const confirmMovesAtom: import("jotai").WritableAtom<boolean, [boolean | typeof import("jotai/utils").RESET | ((prev: boolean) => boolean | typeof import("jotai/utils").RESET)], void>;
11611
+ /**
11612
+ * The entries being dragged, known from the start of a drag so that drop
11613
+ * targets can turn down the ones they can not hold
11614
+ */
11615
+ export const draggedEntriesAtom: import("jotai").PrimitiveAtom<ReadonlySet<Key>> & {
11616
+ init: ReadonlySet<Key>;
11617
+ };
11618
+ export const moveConfirmationAtom: import("jotai").PrimitiveAtom<MoveConfirmation | undefined> & {
11619
+ init: MoveConfirmation | undefined;
11620
+ };
11621
+ /**
11622
+ * Whether entries dropped on a target can move there, and the editor agrees:
11623
+ * entries that get another parent follow the rules of moving them with the
11624
+ * move dialog, and entries of a document type ask first, as their url and
11625
+ * those of the pages below them change.
11626
+ */
11627
+ export const confirmMoveAtom: import("jotai").WritableAtom<null, [ids: Iterable<string>, target: DropTarget], Promise<boolean>> & {
11628
+ init: null;
11629
+ };
11630
+
11631
+ }
11632
+
11337
11633
  declare module 'alinea/dashboard/atoms/nav' {
11338
11634
 
11339
- import { Atom, Getter } from 'jotai';
11635
+ import { Atom, type Getter } from 'jotai';
11340
11636
  import type { OverviewSort } from 'alinea/core/Overview';
11341
11637
  import type { EntryDefaultView } from 'alinea/core/Type';
11342
11638
  import { ReactNode } from 'react';
@@ -11350,7 +11646,9 @@ declare module 'alinea/dashboard/atoms/nav' {
11350
11646
  init: RouteBlock | null;
11351
11647
  };
11352
11648
  export interface DashboardRoute {
11353
- page?: 'splash' | 'entry' | 'users';
11649
+ /** An edit link names an entry by its url, until it is looked up */
11650
+ page?: 'splash' | 'entry' | 'users' | 'edit';
11651
+ url?: string;
11354
11652
  workspace?: string;
11355
11653
  root?: string;
11356
11654
  entry?: string;
@@ -11362,11 +11660,13 @@ declare module 'alinea/dashboard/atoms/nav' {
11362
11660
  replace?: boolean;
11363
11661
  }
11364
11662
  interface ResolvedDashboardRoute extends DashboardRoute {
11365
- page: 'splash' | 'entry' | 'users';
11663
+ page: 'splash' | 'entry' | 'users' | 'edit';
11366
11664
  }
11367
11665
  export const nav: {
11368
11666
  splash(): string;
11369
11667
  users(): string;
11668
+ /** The editor of the entry at `url`, as linked from a page of the site */
11669
+ edit(url: string, workspace?: string, root?: string): string;
11370
11670
  entry(workspace?: string, root?: string, entryId?: string, locale?: string | null, view?: EntryDefaultView, sort?: string): string;
11371
11671
  };
11372
11672
  /** The url parameter of an overview sort: the column, prefixed with - when descending */
@@ -11378,11 +11678,11 @@ declare module 'alinea/dashboard/atoms/nav' {
11378
11678
  route: ResolvedDashboardRoute;
11379
11679
  }
11380
11680
  /** @internal */
11381
- export const routeAtom: import("jotai").WritableAtom<ResolvedDashboardRoute, [update: DashboardRoute | NavigationRequest], void> & {
11681
+ export const routeAtom: import("jotai").WritableAtom<ResolvedDashboardRoute, [update: DashboardRoute | NavigationRequest], void | Promise<void>> & {
11382
11682
  onMount(navigate: (request: NavigationRequest) => void): (() => void) | undefined;
11383
11683
  };
11384
11684
  export interface Page {
11385
- type: 'splash' | 'users' | 'entry';
11685
+ type: 'splash' | 'users' | 'entry' | 'edit';
11386
11686
  workspace: string | undefined;
11387
11687
  root: string | undefined;
11388
11688
  requestedRoot?: string;
@@ -11416,7 +11716,7 @@ declare module 'alinea/dashboard/atoms/overview' {
11416
11716
  import { Type } from 'alinea/core/Type';
11417
11717
  import type { View } from 'alinea/core/View';
11418
11718
  /** The columns every overview can show next to its configured columns */
11419
- export type OverviewBuiltinColumn = 'type' | 'status' | 'updated' | 'author';
11719
+ export type OverviewBuiltinColumn = 'type' | 'updated' | 'author';
11420
11720
  /** The number of `overview: true` fields shown when a parent has no columns */
11421
11721
  export const overviewFieldColumnLimit = 5;
11422
11722
  /** Where a list of entries lives, its overview is configured there */
@@ -11514,7 +11814,7 @@ declare module 'alinea/dashboard/atoms/overview' {
11514
11814
  */
11515
11815
  mixed?: boolean;
11516
11816
  /**
11517
- * The listed children grouped by type and status, see `summarizeRows`.
11817
+ * The listed children grouped by type, see `summarizeRows`.
11518
11818
  * The columns then follow the children: the types present, and built-in
11519
11819
  * columns only when they tell the children apart.
11520
11820
  */
@@ -11563,6 +11863,8 @@ declare module 'alinea/dashboard/atoms/overview' {
11563
11863
  path: string;
11564
11864
  url?: string;
11565
11865
  status?: EntryStatus;
11866
+ /** The version is the main one, a draft then was never published */
11867
+ main?: boolean;
11566
11868
  locale: string | null;
11567
11869
  workspace: string;
11568
11870
  root: string;
@@ -11591,17 +11893,16 @@ declare module 'alinea/dashboard/atoms/overview' {
11591
11893
  export const openEntryAtom: import("jotai").WritableAtom<null, [id: string, locale: string | null], Promise<void>> & {
11592
11894
  init: null;
11593
11895
  };
11594
- /** The listed children that share a type and status */
11896
+ /** The listed children that share a type */
11595
11897
  export interface OverviewChildren {
11596
11898
  type: string;
11597
- status: EntryStatus;
11598
11899
  /** Some of these children store when they were last edited */
11599
11900
  updatedAt: boolean;
11600
11901
  /** Some of these children store who last edited them */
11601
11902
  updatedBy: boolean;
11602
11903
  }
11603
11904
  /**
11604
- * Groups rows by type and status, noting whether they store audit
11905
+ * Groups rows by type, noting whether they store audit
11605
11906
  * metadata. Lists load every child of their parent and searches their
11606
11907
  * shown results, so this covers every row and the columns stay put.
11607
11908
  */
@@ -11667,12 +11968,129 @@ declare module 'alinea/dashboard/atoms/ReactiveNode' {
11667
11968
 
11668
11969
  }
11669
11970
 
11971
+ declare module 'alinea/dashboard/atoms/remove' {
11972
+
11973
+ import type { Config } from 'alinea/core/Config';
11974
+ import type { EntryReference } from 'alinea/core/db/EntryReference';
11975
+ import type { EntryFields } from 'alinea/core/EntryFields';
11976
+ import type { Filter } from 'alinea/core/Filter';
11977
+ import type { Policy } from 'alinea/core/Role';
11978
+ import { type Atom, type PrimitiveAtom } from 'jotai';
11979
+ import { type EntryReferenceWithSource } from 'alinea/dashboard/atoms/entry';
11980
+ import type { ExplorerItemData, ExplorerLimitLocation } from 'alinea/dashboard/atoms/explorer';
11981
+ /** Deleting removes the entries, archiving unpublishes them until restored */
11982
+ export type RemoveAction = 'delete' | 'archive';
11983
+ /** An entry that is about to be removed, in the locale it is shown in */
11984
+ export interface RemoveSubject {
11985
+ id: string;
11986
+ title: string;
11987
+ type: string;
11988
+ workspace: string;
11989
+ root: string;
11990
+ /** Null for entries without languages */
11991
+ locale: string | null;
11992
+ parents: Array<string>;
11993
+ hasChildren: boolean;
11994
+ }
11995
+ /** A translation that is removed, a null locale removes every version */
11996
+ export interface Removal {
11997
+ id: string;
11998
+ locale: string | null;
11999
+ }
12000
+ /** An entry that is removed with the translation of its parent */
12001
+ export interface RemovedDescendant extends Removal {
12002
+ subjectId: string;
12003
+ }
12004
+ /** The public URL of a removed translation of a page */
12005
+ export interface RemovedUrl extends Removal {
12006
+ url: string;
12007
+ }
12008
+ /** The page the URLs of the removed pages redirect to */
12009
+ export interface RedirectTarget {
12010
+ id: string;
12011
+ title: string;
12012
+ url: string;
12013
+ /** The languages of its versions, null for an entry without languages */
12014
+ locales: Array<string | null>;
12015
+ }
12016
+ /** Which languages of the entries are removed and the links that will break */
12017
+ export interface RemovePlan {
12018
+ subjects: Array<RemoveSubject>;
12019
+ /** The languages of a single entry the user can pick to delete, undefined
12020
+ * for a batch */
12021
+ locales?: Array<string>;
12022
+ /** The picked languages, the shown language at first */
12023
+ selectedLocales: PrimitiveAtom<Array<string>>;
12024
+ removals: Atom<Array<Removal>>;
12025
+ /** The versions linking to the removed translations and their contents,
12026
+ * that are not removed themselves */
12027
+ references: Atom<Array<EntryReferenceWithSource>>;
12028
+ /** How many entries the user can not read link to them, their titles stay
12029
+ * hidden */
12030
+ hiddenSources: Atom<number>;
12031
+ /** Every removed translation is published and can be archived instead */
12032
+ archivable: Atom<boolean>;
12033
+ /** The public URLs of the removed translations of pages */
12034
+ urls: Atom<Array<RemovedUrl>>;
12035
+ /** The page the editor picked to redirect the URLs to */
12036
+ redirect: PrimitiveAtom<RedirectTarget | undefined>;
12037
+ /** The URLs the redirect target has no version for, they don't redirect */
12038
+ unredirected: Atom<Array<RemovedUrl>>;
12039
+ }
12040
+ export interface RemovePlanOptions {
12041
+ subjects: Array<RemoveSubject>;
12042
+ locales?: Array<string>;
12043
+ references?: Array<EntryReferenceWithSource>;
12044
+ /** Links from versions the user can not read */
12045
+ hidden?: Array<EntryReference>;
12046
+ descendants?: Array<RemovedDescendant>;
12047
+ /** The translations that can be archived */
12048
+ archivable?: Array<Removal>;
12049
+ /** The public URLs of the subjects, in each of their languages */
12050
+ urls?: Array<RemovedUrl>;
12051
+ }
12052
+ /**
12053
+ * Entries without languages are removed entirely, the others in the picked
12054
+ * languages or, in a batch, the language they are shown in.
12055
+ */
12056
+ export function createRemovePlan({ subjects, locales, references, hidden, descendants, archivable, urls }: RemovePlanOptions): RemovePlan;
12057
+ export interface RedirectPicker {
12058
+ /** The entries of the types that hold URL aliases */
12059
+ condition: Filter<EntryFields>;
12060
+ /** The roots of the workspace of the removed entries */
12061
+ locations: Array<ExplorerLimitLocation>;
12062
+ /** Entries that are not removed and the user may add an alias to */
12063
+ canSelect(item: ExplorerItemData): boolean;
12064
+ }
12065
+ /** Which pages the URLs of the removed entries can redirect to */
12066
+ export function redirectPicker(config: Config, policy: Policy, subjects: Array<RemoveSubject>): RedirectPicker;
12067
+ /** Loads the page the editor picked to redirect the URLs to */
12068
+ export const loadRedirectTargetAtom: import("jotai").WritableAtom<null, [id: string, locale: string | null], Promise<RedirectTarget | undefined>> & {
12069
+ init: null;
12070
+ };
12071
+ /** Loads the references to the entries before the remove dialog opens */
12072
+ export const loadRemovePlanAtom: import("jotai").WritableAtom<null, [subjects: RemoveSubject[], locales?: string[] | undefined], Promise<RemovePlan>> & {
12073
+ init: null;
12074
+ };
12075
+ /** Removes the translations and redirects their URLs in one commit */
12076
+ export const deleteEntriesAtom: import("jotai").WritableAtom<null, [plan: RemovePlan], Promise<void>> & {
12077
+ init: null;
12078
+ };
12079
+ /** Archives the translations the plan would delete */
12080
+ export const archiveEntriesAtom: import("jotai").WritableAtom<null, [plan: RemovePlan], Promise<void>> & {
12081
+ init: null;
12082
+ };
12083
+ /** Archive without asking first, until the browser session ends */
12084
+ export const archiveDirectlyAtom: import("jotai").WritableAtom<boolean, [boolean | typeof import("jotai/utils").RESET | ((prev: boolean) => boolean | typeof import("jotai/utils").RESET)], void>;
12085
+
12086
+ }
12087
+
11670
12088
  declare module 'alinea/dashboard/atoms/root' {
11671
12089
 
12090
+ import { Config } from 'alinea/core/Config';
11672
12091
  import type { EntryStatus } from 'alinea/core/Entry';
11673
12092
  import { Permission } from 'alinea/core/Role';
11674
12093
  import { type RootData, type RootI18n } from 'alinea/core/Root';
11675
- import { Schema } from 'alinea/core/Schema';
11676
12094
  import { ExplorerAtoms } from 'alinea/dashboard/atoms/explorer';
11677
12095
  import { type Atom, type PrimitiveAtom } from 'jotai';
11678
12096
  import type { ComponentType } from 'react';
@@ -11784,7 +12202,7 @@ declare module 'alinea/dashboard/atoms/root' {
11784
12202
  * Entries can be dropped on a container, but only placed before or after the
11785
12203
  * children of a parent that does not order them, and never inside themselves
11786
12204
  */
11787
- export function treeAcceptsDrop(schema: Schema, entries: Map<string, RootTreeItem>, target: DropTarget, keys?: ReadonlySet<Key>): boolean;
12205
+ export function treeAcceptsDrop(config: Config, rootData: RootData, entries: Map<string, RootTreeItem>, target: DropTarget, keys?: ReadonlySet<Key>): boolean;
11788
12206
  /** The selected entry, or the location of a selected entry that is not listed */
11789
12207
  export function locatedItem({ entries, snapshot }: TreeView): RootTreeItem | undefined;
11790
12208
  export {};
@@ -12017,7 +12435,12 @@ declare module 'alinea/dashboard/boot/BootProd' {
12017
12435
 
12018
12436
  import type { CMS } from 'alinea/core/CMS';
12019
12437
  import type { ComponentType } from 'react';
12020
- export function bootProd(handlerUrl: string, cms: CMS, views: Record<string, ComponentType>): Promise<void>;
12438
+ /**
12439
+ * Boot a built dashboard. Its build id is a hash of the built files, which
12440
+ * the page passes in the script URL, so that a deploy that leaves the
12441
+ * dashboard unchanged keeps its shared worker and the tabs it serves.
12442
+ */
12443
+ export function bootProd(handlerUrl: string, cms: CMS, views: Record<string, ComponentType>, buildId?: string): Promise<void>;
12021
12444
 
12022
12445
  }
12023
12446
 
@@ -12032,6 +12455,7 @@ declare module 'alinea/dashboard/boot/DashboardWorker' {
12032
12455
  import { type Activity } from 'alinea/dashboard/boot/ActivityEvent';
12033
12456
  export class DashboardWorker extends EventTarget {
12034
12457
  #private;
12458
+ /** Keep the content in memory with `source`, instead of in the browser. */
12035
12459
  constructor(source?: Source);
12036
12460
  get db(): EntryStore | Promise<EntryStore>;
12037
12461
  sync(): Promise<string>;
@@ -13005,6 +13429,7 @@ declare module 'alinea/dashboard/ViewKeys' {
13005
13429
  MetadataUserInput: string;
13006
13430
  MetadataInput: string;
13007
13431
  MetadataDetailsView: string;
13432
+ MetadataPreviewsView: string;
13008
13433
  NumberInput: string;
13009
13434
  ObjectInput: string;
13010
13435
  PathInput: string;
@@ -13022,52 +13447,40 @@ declare module 'alinea/dashboard/ViewKeys' {
13022
13447
  declare module 'alinea/database/BrowserEntryStore' {
13023
13448
 
13024
13449
  import type { Config } from 'alinea/core/Config';
13025
- import type { Mutation } from 'alinea/core/db/Mutation';
13026
- import type { CommitRequest } from 'alinea/core/db/CommitRequest';
13027
13450
  import type { SyncOptions } from 'alinea/core/db/LocalStore';
13028
13451
  import type { RemoteSource } from 'alinea/core/source/Source';
13452
+ import { type SnapshotStorage } from '@alinea/sqlite-wasm/snapshots';
13029
13453
  import { EntryStore } from 'alinea/database/EntryStore';
13030
13454
  export interface BrowserEntryStoreOptions {
13031
13455
  name: string;
13032
- revision: string;
13456
+ /** Stores of one config fingerprint open the bases it wrote. */
13457
+ fingerprint: string;
13458
+ /** Where bases are kept, Blobs in IndexedDB by default. */
13459
+ storage?: SnapshotStorage;
13033
13460
  /**
13034
- * The store this one replaces, such as one of a previous dashboard build:
13035
- * its content carries over from memory instead of loading it again, and it
13036
- * stops storing without waiting for its work in flight.
13461
+ * Start over from an empty database instead of a stored base, such as
13462
+ * when it turned out to be corrupt.
13037
13463
  */
13038
- replaces?: BrowserEntryStore;
13039
- /** The IndexedDB implementation, the global one by default. */
13040
- indexedDB?: IDBFactory;
13041
- IDBKeyRange?: typeof IDBKeyRange;
13464
+ reset?: boolean;
13042
13465
  }
13043
13466
  /**
13044
- * WASM entry store kept in IndexedDB: every commit stores the pages it
13045
- * changed.
13467
+ * WASM entry store on a copy-on-write overlay over the newest stored base:
13468
+ * it reads pages from the base as queries need them, and keeps its changes
13469
+ * in memory. Every sync stores them as a new base in the background, which
13470
+ * the next store opens; the stored bases never change, so stores of any
13471
+ * number of workers and dashboard builds can use them at once.
13046
13472
  */
13047
13473
  export class BrowserEntryStore extends EntryStore {
13048
13474
  #private;
13049
- static open(config: Config, options: BrowserEntryStoreOptions, retried?: boolean): Promise<BrowserEntryStore>;
13475
+ static open(config: Config, options: BrowserEntryStoreOptions): Promise<BrowserEntryStore>;
13050
13476
  private constructor();
13051
- /**
13052
- * Opening, and syncing, resolve once the content is in memory: storing it
13053
- * is a cache that finishes in the background, which takes seconds for a
13054
- * first sync or a rebuild of a large project.
13055
- */
13056
13477
  sync(): Promise<string>;
13057
13478
  syncWith(remote: RemoteSource, options?: SyncOptions): Promise<string>;
13058
- mutate(mutations: Array<Mutation>): Promise<{
13059
- sha: string;
13060
- }>;
13061
- write(request: CommitRequest): Promise<{
13062
- sha: string;
13063
- }>;
13479
+ /** Close once the last checkpoint is stored. */
13064
13480
  close(): Promise<void>;
13065
- /**
13066
- * Close a store that a replacement took over from: its work in flight
13067
- * changes only its memory copy, which is not stored.
13068
- */
13069
- abandon(): Promise<void>;
13070
13481
  }
13482
+ /** Whether an error, or one it holds, reports a damaged SQLite database. */
13483
+ export function isCorruptDatabaseError(error: unknown): boolean;
13071
13484
 
13072
13485
  }
13073
13486
 
@@ -13187,7 +13600,7 @@ declare module 'alinea/database/driver/RuntimeDatabase' {
13187
13600
 
13188
13601
  declare module 'alinea/database/driver/WasmDatabase' {
13189
13602
 
13190
- import type { Storage } from '@alinea/sqlite-wasm/Database.js';
13603
+ import type { Database as WasmSqlite } from '@alinea/sqlite-wasm/Database.js';
13191
13604
  import type { DatabaseOptions } from 'rado';
13192
13605
  import { connect } from 'rado/driver/sql.js';
13193
13606
  import type { DatabaseHandle } from 'alinea/database/driver/DatabaseHandle';
@@ -13195,24 +13608,11 @@ declare module 'alinea/database/driver/WasmDatabase' {
13195
13608
  database: ReturnType<typeof connect>;
13196
13609
  fork(): Promise<WasmDatabaseHandle>;
13197
13610
  export(): Uint8Array;
13198
- /** Resolves once every commit so far is stored; at once in memory. */
13199
- flush(): Promise<void>;
13200
- /**
13201
- * Store this database from now on, replacing what the storage held, once
13202
- * a database detached from it wrote its last commits.
13203
- */
13204
- attach(storage: Storage): Promise<void>;
13205
- /**
13206
- * Stop storing commits once the ones so far are written; later commits
13207
- * change only the memory copy.
13208
- */
13209
- detach(): void;
13210
13611
  }
13211
- /**
13212
- * Open a WASM connection from the database kept in storage, or an empty one,
13213
- * that stores every commit there.
13214
- */
13215
- export function syncWasmDatabase(storage: Storage, options?: DatabaseOptions): Promise<WasmDatabaseHandle>;
13612
+ /** The WASM SQLite database class, loaded on first use. */
13613
+ export function wasmSqlite(): Promise<new () => WasmSqlite>;
13614
+ /** Connect to `sqlite`; forks keep its page cache size. */
13615
+ export function wasmHandle(sqlite: WasmSqlite, options?: DatabaseOptions): WasmDatabaseHandle;
13216
13616
  /** Open an in-memory WASM connection, optionally from a complete SQLite file. */
13217
13617
  export function openWasmDatabase(data?: Uint8Array, options?: DatabaseOptions): Promise<WasmDatabaseHandle>;
13218
13618
  export function wasmDatabase(data?: Uint8Array): Promise<import("rado").SyncDatabase<"sqlite">>;
@@ -13632,7 +14032,11 @@ declare module 'alinea/database/EntryTransaction' {
13632
14032
  declare module 'alinea/database/EntryUrlAliases' {
13633
14033
 
13634
14034
  import { Type } from 'alinea/core/Type';
14035
+ /** Entries of the type keep URL aliases in their metadata */
14036
+ export function hasUrlAliases(type: Type): boolean;
13635
14037
  export function dataWithUrlAlias(type: Type, data: Record<string, unknown>, previousUrl: string, currentUrl: string): Record<string, unknown>;
14038
+ /** Adds the urls that are not an alias yet to the aliases in the metadata */
14039
+ export function dataWithUrlAliases(data: Record<string, unknown>, urls: Array<string>): Record<string, unknown>;
13636
14040
 
13637
14041
  }
13638
14042
 
@@ -14427,10 +14831,10 @@ declare module 'alinea/field/link/EntryLink' {
14427
14831
  import type { Label } from 'alinea/core/Label';
14428
14832
  import type { Type } from 'alinea/core/Type';
14429
14833
  import type { ListRow } from 'alinea/core/ListRow';
14430
- import { type LinkFieldOptions } from 'alinea/field/link/LinkField';
14834
+ import { type LinkFieldOptions, type LinkFields } from 'alinea/field/link/LinkField';
14431
14835
  import { type EntryPickerOptions } from 'alinea/picker/entry';
14432
14836
  import type { EntryReference } from 'alinea/picker/entry/EntryReference';
14433
- export interface EntryLink<InferredFields = undefined> extends EntryReference {
14837
+ export interface EntryLink<InferredFields = {}> extends EntryReference {
14434
14838
  entryId: string;
14435
14839
  entryType: string;
14436
14840
  title: string;
@@ -14449,9 +14853,9 @@ declare module 'alinea/field/link/EntryLink' {
14449
14853
  }
14450
14854
  interface EntryOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14451
14855
  }
14452
- export function entry<Fields = undefined>(label: Label, options?: WithoutLabel<EntryOptions<Fields>>): import("alinea/field/link/LinkField").LinkField<EntryReference & InferStoredValue<Fields>, EntryLink<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>;
14856
+ export function entry<Fields = undefined>(label: Label, options?: WithoutLabel<EntryOptions<Fields>>): import("alinea/field/link/LinkField").LinkField<EntryReference & InferStoredValue<Fields>, EntryLink<LinkFields<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>>;
14453
14857
  export namespace entry {
14454
- type EntryRow<Fields> = EntryLink<Type.Infer<Fields>> & ListRow;
14858
+ type EntryRow<Fields> = EntryLink<LinkFields<Type.Infer<Fields>>> & ListRow;
14455
14859
  interface EntryOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14456
14860
  }
14457
14861
  export function multiple<Fields = undefined>(label: Label, options?: WithoutLabel<EntryOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<EntryReference & ListRow & InferStoredValue<Fields>, EntryRow<Fields>>;
@@ -14467,10 +14871,10 @@ declare module 'alinea/field/link/FileLink' {
14467
14871
  import type { InferStoredValue } from 'alinea/core/Infer';
14468
14872
  import type { Label } from 'alinea/core/Label';
14469
14873
  import type { ListRow } from 'alinea/core/ListRow';
14470
- import { type LinkFieldOptions } from 'alinea/field/link/LinkField';
14874
+ import { type LinkFieldOptions, type LinkFields } from 'alinea/field/link/LinkField';
14471
14875
  import { type EntryPickerOptions } from 'alinea/picker/entry';
14472
14876
  import type { EntryReference } from 'alinea/picker/entry/EntryReference';
14473
- export interface FileLink<InferredFields = undefined> extends EntryReference {
14877
+ export interface FileLink<InferredFields = {}> extends EntryReference {
14474
14878
  title: string;
14475
14879
  /** Public url of the file */
14476
14880
  url: string;
@@ -14490,11 +14894,11 @@ declare module 'alinea/field/link/FileLink' {
14490
14894
  export function filePicker<Fields>(multiple: boolean, options: Omit<EntryPickerOptions<Fields>, 'selection'>): import("alinea/core/Picker").Picker<EntryReference, EntryPickerOptions<Fields>>;
14491
14895
  export interface FileOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14492
14896
  }
14493
- export function file<Fields = undefined>(label: Label, options?: WithoutLabel<FileOptions<Fields>>): import("alinea/field/link/LinkField").LinkField<EntryReference & InferStoredValue<Fields>, FileLink<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>;
14897
+ export function file<Fields = undefined>(label: Label, options?: WithoutLabel<FileOptions<Fields>>): import("alinea/field/link/LinkField").LinkField<EntryReference & InferStoredValue<Fields>, FileLink<LinkFields<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>>;
14494
14898
  export namespace file {
14495
14899
  interface FilesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14496
14900
  }
14497
- function multiple<Fields = undefined>(label: Label, options?: WithoutLabel<FilesOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<EntryReference & ListRow & InferStoredValue<Fields>, FileLink<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>;
14901
+ function multiple<Fields = undefined>(label: Label, options?: WithoutLabel<FilesOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<EntryReference & ListRow & InferStoredValue<Fields>, FileLink<LinkFields<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>>;
14498
14902
  }
14499
14903
 
14500
14904
  }
@@ -14506,10 +14910,10 @@ declare module 'alinea/field/link/ImageLink' {
14506
14910
  import type { Label } from 'alinea/core/Label';
14507
14911
  import type { Type } from 'alinea/core/Type';
14508
14912
  import type { ListRow } from 'alinea/core/ListRow';
14509
- import { type LinkField, type LinkFieldOptions, type LinksField } from 'alinea/field/link/LinkField';
14913
+ import { type LinkField, type LinkFieldOptions, type LinksField, type LinkFields } from 'alinea/field/link/LinkField';
14510
14914
  import { type EntryPickerOptions } from 'alinea/picker/entry';
14511
14915
  import type { EntryReference } from 'alinea/picker/entry/EntryReference';
14512
- export interface ImageLink<InferredFields = undefined> extends EntryReference {
14916
+ export interface ImageLink<InferredFields = {}> extends EntryReference {
14513
14917
  title: string;
14514
14918
  alt?: string;
14515
14919
  /** Public url of the image file, versioned by its hash */
@@ -14546,12 +14950,12 @@ declare module 'alinea/field/link/ImageLink' {
14546
14950
  y: number;
14547
14951
  }>;
14548
14952
  }
14549
- export interface ImageField<Fields = undefined> extends LinkField<EntryReference & InferStoredValue<Fields>, ImageLink<Type.Infer<Fields>>> {
14953
+ export interface ImageField<Fields = undefined> extends LinkField<EntryReference & InferStoredValue<Fields>, ImageLink<LinkFields<Type.Infer<Fields>>>> {
14550
14954
  }
14551
14955
  export interface ImageOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14552
14956
  }
14553
14957
  export function image<Fields = undefined>(label: Label, options?: WithoutLabel<ImageOptions<Fields>>): ImageField<Fields>;
14554
- export interface ImagesField<Fields = undefined> extends LinksField<EntryReference & ListRow & InferStoredValue<Fields>, ImageLink<Type.Infer<Fields>>> {
14958
+ export interface ImagesField<Fields = undefined> extends LinksField<EntryReference & ListRow & InferStoredValue<Fields>, ImageLink<LinkFields<Type.Infer<Fields>>>> {
14555
14959
  }
14556
14960
  export namespace image {
14557
14961
  interface ImagesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
@@ -14574,7 +14978,7 @@ declare module 'alinea/field/link/Link' {
14574
14978
  import { type UrlReference } from 'alinea/picker/url';
14575
14979
  import { EntryLink } from 'alinea/field/link/EntryLink';
14576
14980
  import type { UrlLink } from 'alinea/field/link/UrlLink';
14577
- export type Link<InferredFields> = EntryLink<InferredFields> | UrlLink<InferredFields> | FileLink<InferredFields>;
14981
+ export type Link<InferredFields = {}> = EntryLink<InferredFields> | UrlLink<InferredFields> | FileLink<InferredFields>;
14578
14982
  export interface LinkOptions<Definition, Row> extends LinkFieldOptions<Row>, EntryPickerConditions {
14579
14983
  fields?: Definition | Type<Definition>;
14580
14984
  }
@@ -14628,6 +15032,11 @@ declare module 'alinea/field/link/LinkField' {
14628
15032
  import { ListRow } from 'alinea/core/ListRow';
14629
15033
  import type { ReactNode } from 'react';
14630
15034
  /** Optional settings to configure a link field */
15035
+ /**
15036
+ * The extra fields of a queried link: a link without extra fields holds an
15037
+ * empty object, which is also what the link types default to
15038
+ */
15039
+ export type LinkFields<InferredFields> = InferredFields extends undefined ? {} : InferredFields;
14631
15040
  export interface LinkFieldOptions<Value> extends FieldOptions<Value> {
14632
15041
  /**
14633
15042
  * Allow the same entry to be linked more than once.
@@ -14690,9 +15099,9 @@ declare module 'alinea/field/link/UrlLink' {
14690
15099
  import type { Label } from 'alinea/core/Label';
14691
15100
  import type { Type } from 'alinea/core/Type';
14692
15101
  import type { ListRow } from 'alinea/core/ListRow';
14693
- import { type LinkFieldOptions } from 'alinea/field/link/LinkField';
15102
+ import { type LinkFieldOptions, type LinkFields } from 'alinea/field/link/LinkField';
14694
15103
  import { type UrlPickerOptions, type UrlReference } from 'alinea/picker/url';
14695
- export interface UrlLink<InferredFields = undefined> extends UrlReference {
15104
+ export interface UrlLink<InferredFields = {}> extends UrlReference {
14696
15105
  url: string;
14697
15106
  href: string;
14698
15107
  title: string;
@@ -14703,9 +15112,9 @@ declare module 'alinea/field/link/UrlLink' {
14703
15112
  }
14704
15113
  export interface UrlOptions<Fields> extends LinkFieldOptions<UrlReference & InferStoredValue<Fields>>, UrlPickerOptions<Fields> {
14705
15114
  }
14706
- export function url<Fields>(label: Label, options?: WithoutLabel<UrlOptions<Fields>>): import("alinea/field/link/LinkField").LinkField<UrlReference & InferStoredValue<Fields>, UrlLink<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>;
15115
+ export function url<Fields>(label: Label, options?: WithoutLabel<UrlOptions<Fields>>): import("alinea/field/link/LinkField").LinkField<UrlReference & InferStoredValue<Fields>, UrlLink<LinkFields<{ [K in keyof Fields as Fields[K] extends import("alinea/core/Expr").Expr<any> ? K : never]: Fields[K] extends import("alinea/core/Expr").Expr<infer T_1> ? T_1 : never; } extends infer T ? { [K in keyof T]: T[K]; } : never>>>;
14707
15116
  export namespace url {
14708
- type UrlRows<Fields> = UrlLink<Type.Infer<Fields>> & ListRow;
15117
+ type UrlRows<Fields> = UrlLink<LinkFields<Type.Infer<Fields>>> & ListRow;
14709
15118
  export interface UrlOptions<Fields> extends LinkFieldOptions<Array<UrlReference & ListRow & InferStoredValue<Fields>>>, UrlPickerOptions<Fields> {
14710
15119
  }
14711
15120
  export function multiple<Fields>(label: Label, options?: WithoutLabel<UrlOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<UrlReference & ListRow & InferStoredValue<Fields>, UrlRows<Fields>>;
@@ -14738,6 +15147,8 @@ declare module 'alinea/field/list/ListField' {
14738
15147
  help?: ReactNode;
14739
15148
  /** Display a minimal version */
14740
15149
  inline?: boolean;
15150
+ /** Show the rows folded when the entry opens, added rows start open */
15151
+ collapsed?: boolean;
14741
15152
  /** Hide this list field */
14742
15153
  hidden?: boolean;
14743
15154
  /** Mark the field as invalid while it has fewer items */
@@ -14767,12 +15178,27 @@ declare module 'alinea/field/list/ListField.view' {
14767
15178
  _type: string;
14768
15179
  [key: string]: unknown;
14769
15180
  }
15181
+ export interface ListFieldTypeItem {
15182
+ id: string;
15183
+ label: string;
15184
+ type: Schema[string];
15185
+ }
14770
15186
  export interface ListFieldViewProps {
14771
15187
  field: CoreListField<ListRow, ListValue, ListOptions<Schema>>;
14772
15188
  }
14773
15189
  export function ListFieldView({ field }: ListFieldViewProps): import("react").JSX.Element;
14774
15190
  export function insertIndex(rowIndex: number, position: 'before' | 'after'): number;
14775
15191
  export function reorderIndex(fromIndex: number, targetIndex: number): number;
15192
+ export interface ListFieldTypeCommandProps {
15193
+ items: Array<ListFieldTypeItem>;
15194
+ label: string;
15195
+ pasted?: ListValue;
15196
+ pasteLabel?: string;
15197
+ onPaste?: (row: ListValue) => void;
15198
+ onSelect: (item: ListFieldTypeItem) => void;
15199
+ }
15200
+ /** Searchable list of block types, also used to insert rich text blocks */
15201
+ export function ListFieldTypeCommand({ items, label, pasted, pasteLabel, onPaste, onSelect }: ListFieldTypeCommandProps): import("react").JSX.Element;
14776
15202
  export {};
14777
15203
 
14778
15204
  }
@@ -14975,19 +15401,20 @@ declare module 'alinea/field/metadata/MetadataField' {
14975
15401
  fields: Type<MetadataFields>;
14976
15402
  seo: Type<MetadataSeoFields>;
14977
15403
  details: Type<MetadataDetailsFields>;
14978
- /** The details are rendered by a separate `metadataDetails` section */
14979
- detailsSection?: boolean;
15404
+ /** The details and previews are rendered by the separate
15405
+ * `metadataDetails` and `metadataPreviews` sections */
15406
+ sections?: boolean;
14980
15407
  }
14981
15408
  export interface MetadataConfig {
14982
- /** Leave the details out of the field view, render them with
14983
- * `metadataDetails(field)` instead */
14984
- detailsSection?: boolean;
15409
+ /** Leave the details and previews out of the field view, render them
15410
+ * with `metadataDetails(field)` and `metadataPreviews()` instead */
15411
+ sections?: boolean;
14985
15412
  }
14986
15413
  export class MetadataField extends RecordField<Metadata, MetadataOptions> {
14987
15414
  }
14988
15415
  export interface MetadataField extends MetadataFields {
14989
15416
  }
14990
- export function metadata(label?: string, { detailsSection }?: MetadataConfig): MetadataField;
15417
+ export function metadata(label?: string, { sections }?: MetadataConfig): MetadataField;
14991
15418
  export class MetadataDetailsSection implements SectionData {
14992
15419
  field: MetadataField;
14993
15420
  view: string;
@@ -14997,8 +15424,17 @@ declare module 'alinea/field/metadata/MetadataField' {
14997
15424
  constructor(field: MetadataField);
14998
15425
  }
14999
15426
  /** Renders the created/updated details and aliases of a metadata field
15000
- * created with `detailsSection: true` */
15427
+ * created with `sections: true` */
15001
15428
  export function metadataDetails(field: MetadataField): Section;
15429
+ export class MetadataPreviewsSection implements SectionData {
15430
+ view: string;
15431
+ definition: {};
15432
+ fields: {};
15433
+ sections: never[];
15434
+ }
15435
+ /** Renders the search and Open Graph previews of the entry, for a metadata
15436
+ * field created with `sections: true` */
15437
+ export function metadataPreviews(): Section;
15002
15438
 
15003
15439
  }
15004
15440
 
@@ -15018,6 +15454,7 @@ declare module 'alinea/field/metadata/MetadataField.view' {
15018
15454
  }
15019
15455
  export function MetadataUserFieldView({ field }: MetadataUserFieldViewProps): import("react").JSX.Element;
15020
15456
  export function MetadataFieldView({ field }: MetadataFieldViewProps): import("react").JSX.Element;
15457
+ export function MetadataPreviewsView(): import("react").JSX.Element;
15021
15458
  export interface MetadataDetailsViewProps {
15022
15459
  section: Section;
15023
15460
  }
@@ -15347,8 +15784,10 @@ declare module 'alinea/field/richtext/RichTextBlock' {
15347
15784
  readOnly: boolean;
15348
15785
  onDelete: () => void;
15349
15786
  onDuplicate: () => void;
15787
+ /** Called as the header starts a drag, before the editor handles it */
15788
+ onDragStart: () => void;
15350
15789
  }
15351
- export const RichTextBlock: import("react").MemoExoticComponent<({ id, node, type, readOnly, onDelete, onDuplicate }: RichTextBlockProps) => import("react").JSX.Element>;
15790
+ export const RichTextBlock: import("react").MemoExoticComponent<({ id, node, type, readOnly, onDelete, onDuplicate, onDragStart }: RichTextBlockProps) => import("react").JSX.Element>;
15352
15791
 
15353
15792
  }
15354
15793