alinea 2.0.0-preview.12 → 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 (264) 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 +676 -280
  18. package/dist/chunks/{bun-sqlite-5SHXN3AT.js → bun-sqlite-RI6BYU5S.js} +3 -3
  19. package/dist/chunks/{chunk-OHGAVV4G.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/serve/mcp/DevMcp.js +1 -1
  35. package/dist/cli/serve/mcp/McpTools.js +29 -1
  36. package/dist/cloud/CloudRemote.js +1 -1
  37. package/dist/components/Alert.js +2 -2
  38. package/dist/components/AppShell.js +2 -2
  39. package/dist/components/Badge.d.ts +3 -1
  40. package/dist/components/Badge.js +5 -3
  41. package/dist/components/Breadcrumbs.js +7 -7
  42. package/dist/components/Button.d.ts +6 -1
  43. package/dist/components/Button.js +16 -7
  44. package/dist/components/Calendar.js +20 -20
  45. package/dist/components/Checkbox.js +7 -7
  46. package/dist/components/CheckboxGroup.js +4 -4
  47. package/dist/components/Code.js +2 -2
  48. package/dist/components/Collapsible.js +3 -3
  49. package/dist/components/ColorSwatch.js +2 -2
  50. package/dist/components/ColorSwatchPicker.js +2 -2
  51. package/dist/components/ComboBox.js +7 -7
  52. package/dist/components/Command.js +14 -13
  53. package/dist/components/ContentCard.d.ts +3 -1
  54. package/dist/components/ContentCard.js +46 -24
  55. package/dist/components/ContentGrid.js +17 -17
  56. package/dist/components/DataList.js +2 -2
  57. package/dist/components/DateField.js +2 -2
  58. package/dist/components/DatePicker.js +2 -2
  59. package/dist/components/DateRangePicker.js +2 -2
  60. package/dist/components/Dialog.js +7 -7
  61. package/dist/components/DropZone.js +6 -6
  62. package/dist/components/DropdownMenu.js +9 -9
  63. package/dist/components/Empty.js +5 -5
  64. package/dist/components/Field.js +8 -8
  65. package/dist/components/List.js +27 -27
  66. package/dist/components/MediaPreview.js +5 -5
  67. package/dist/components/MultipleSelect.js +22 -22
  68. package/dist/components/NavRail.js +4 -4
  69. package/dist/components/NumberField.js +4 -4
  70. package/dist/components/Page.js +3 -3
  71. package/dist/components/Popover.js +2 -2
  72. package/dist/components/PreviewFrame.js +9 -9
  73. package/dist/components/RadioGroup.js +9 -9
  74. package/dist/components/Resizable.js +4 -4
  75. package/dist/components/SearchField.d.ts +6 -1
  76. package/dist/components/SearchField.js +5 -3
  77. package/dist/components/Select.js +13 -13
  78. package/dist/components/Sheet.d.ts +50 -0
  79. package/dist/components/Sheet.js +202 -0
  80. package/dist/components/Sidebar.js +4 -4
  81. package/dist/components/SortableList.d.ts +34 -30
  82. package/dist/components/SortableList.js +161 -264
  83. package/dist/components/Spinner.js +2 -2
  84. package/dist/components/Surface.js +2 -2
  85. package/dist/components/Switch.js +8 -8
  86. package/dist/components/Table.d.ts +3 -1
  87. package/dist/components/Table.js +57 -46
  88. package/dist/components/Tabs.js +5 -5
  89. package/dist/components/TagGroup.js +8 -8
  90. package/dist/components/TextField.js +10 -10
  91. package/dist/components/TimeField.js +6 -6
  92. package/dist/components/Timestamp.js +1 -1
  93. package/dist/components/ToggleGroup.js +2 -2
  94. package/dist/components/Toolbar.js +2 -2
  95. package/dist/components/Tooltip.js +5 -5
  96. package/dist/components/Tree.js +24 -24
  97. package/dist/components/internal/DateControl.js +12 -12
  98. package/dist/components/internal/ListBoxOption.js +6 -6
  99. package/dist/components/internal/Locale.js +2 -2
  100. package/dist/components/internal/PopoverSurface.js +4 -4
  101. package/dist/components/internal/SelectionCheckbox.js +5 -5
  102. package/dist/components/internal/Slot.js +1 -1
  103. package/dist/components/internal/ToggleButton.js +3 -3
  104. package/dist/components/internal/Trigger.js +2 -2
  105. package/dist/components/internal/sortableDrag.d.ts +19 -0
  106. package/dist/components/internal/sortableDrag.js +175 -0
  107. package/dist/components/internal/useDragDrop.js +2 -2
  108. package/dist/components.d.ts +1 -0
  109. package/dist/components.js +1 -0
  110. package/dist/core/Config.d.ts +6 -1
  111. package/dist/core/Document.d.ts +9 -1
  112. package/dist/core/Document.js +31 -6
  113. package/dist/core/Overview.d.ts +2 -4
  114. package/dist/core/TextDoc.d.ts +4 -0
  115. package/dist/core/Validation.d.ts +0 -1
  116. package/dist/core/Validation.js +0 -6
  117. package/dist/core/field/RichTextField.js +23 -4
  118. package/dist/core/media/MediaTypes.js +1 -1
  119. package/dist/core/source/GithubSource.d.ts +4 -0
  120. package/dist/core/source/GithubSource.js +127 -25
  121. package/dist/core/source/GithubUsage.d.ts +21 -0
  122. package/dist/core/source/GithubUsage.js +47 -0
  123. package/dist/dashboard/App.d.ts +2 -2
  124. package/dist/dashboard/App.js +5 -3
  125. package/dist/dashboard/ViewKeys.d.ts +1 -0
  126. package/dist/dashboard/ViewKeys.js +1 -0
  127. package/dist/dashboard/app/ActivityStatus.js +7 -7
  128. package/dist/dashboard/app/AuthView.js +5 -5
  129. package/dist/dashboard/app/BlockSheet.d.ts +50 -0
  130. package/dist/dashboard/app/BlockSheet.js +207 -0
  131. package/dist/dashboard/app/BlockSheetStoryFrame.d.ts +6 -0
  132. package/dist/dashboard/app/BlockSheetStoryFrame.js +29 -0
  133. package/dist/dashboard/app/CompactField.js +16 -16
  134. package/dist/dashboard/app/DashboardLayout.js +3 -1
  135. package/dist/dashboard/app/EntryFields.js +2 -2
  136. package/dist/dashboard/app/EntryHeader.js +33 -34
  137. package/dist/dashboard/app/EntryReferences.js +7 -7
  138. package/dist/dashboard/app/EntrySidebar.js +15 -15
  139. package/dist/dashboard/app/EntrySidebarPreview.js +3 -3
  140. package/dist/dashboard/app/EntryStatusIcon.d.ts +23 -0
  141. package/dist/dashboard/app/EntryStatusIcon.js +58 -0
  142. package/dist/dashboard/app/EntryValidationModal.d.ts +6 -5
  143. package/dist/dashboard/app/EntryValidationModal.js +20 -20
  144. package/dist/dashboard/app/Explorer.d.ts +3 -1
  145. package/dist/dashboard/app/Explorer.js +48 -44
  146. package/dist/dashboard/app/ExplorerBatchActions.d.ts +3 -1
  147. package/dist/dashboard/app/ExplorerBatchActions.js +68 -43
  148. package/dist/dashboard/app/ExplorerCards.js +7 -1
  149. package/dist/dashboard/app/ExplorerControls.js +6 -6
  150. package/dist/dashboard/app/ExplorerList.js +5 -5
  151. package/dist/dashboard/app/ExplorerModal.js +2 -2
  152. package/dist/dashboard/app/ExplorerTable.js +11 -8
  153. package/dist/dashboard/app/ImageEditor.js +28 -28
  154. package/dist/dashboard/app/ImagePicker.js +3 -13
  155. package/dist/dashboard/app/LinkPicker.js +28 -26
  156. package/dist/dashboard/app/LocaleMenu.js +4 -4
  157. package/dist/dashboard/app/MediaPreviewCell.js +1 -1
  158. package/dist/dashboard/app/MoveConfirmDialog.d.ts +5 -0
  159. package/dist/dashboard/app/MoveConfirmDialog.js +79 -0
  160. package/dist/dashboard/app/OverviewCell.js +8 -13
  161. package/dist/dashboard/app/PendingUploadsView.js +3 -3
  162. package/dist/dashboard/app/ReadOnlyBadge.d.ts +1 -1
  163. package/dist/dashboard/app/RemoveDialog.d.ts +17 -0
  164. package/dist/dashboard/app/{DeleteDialog.js → RemoveDialog.js} +67 -27
  165. package/dist/dashboard/app/RemoveRedirect.d.ts +7 -0
  166. package/dist/dashboard/app/RemoveRedirect.js +123 -0
  167. package/dist/dashboard/app/SearchBar.d.ts +9 -0
  168. package/dist/dashboard/app/SearchBar.js +31 -0
  169. package/dist/dashboard/app/SidebarTree.js +27 -72
  170. package/dist/dashboard/app/WorkspaceMenu.js +7 -7
  171. package/dist/dashboard/app/WorkspaceRoots.js +12 -12
  172. package/dist/dashboard/app/editor/FileEditor.js +14 -14
  173. package/dist/dashboard/app/modals/CreateEntry.js +3 -3
  174. package/dist/dashboard/app/pages/EntryPage.d.ts +2 -1
  175. package/dist/dashboard/app/pages/EntryPage.js +34 -15
  176. package/dist/dashboard/app/pages/SplashPage.js +37 -37
  177. package/dist/dashboard/app/pages/UsersPage.js +12 -12
  178. package/dist/dashboard/app/ui/DashboardModal.js +15 -14
  179. package/dist/dashboard/atoms/dashboard.js +1 -1
  180. package/dist/dashboard/atoms/entry.js +1 -14
  181. package/dist/dashboard/atoms/entryTable.js +1 -0
  182. package/dist/dashboard/atoms/explorer.d.ts +4 -0
  183. package/dist/dashboard/atoms/explorer.js +19 -7
  184. package/dist/dashboard/atoms/graph.js +1 -1
  185. package/dist/dashboard/atoms/link.js +5 -2
  186. package/dist/dashboard/atoms/move.d.ts +5 -0
  187. package/dist/dashboard/atoms/move.js +29 -3
  188. package/dist/dashboard/atoms/moveConfirmation.d.ts +34 -0
  189. package/dist/dashboard/atoms/moveConfirmation.js +95 -0
  190. package/dist/dashboard/atoms/nav.d.ts +9 -5
  191. package/dist/dashboard/atoms/nav.js +120 -45
  192. package/dist/dashboard/atoms/overview.d.ts +6 -5
  193. package/dist/dashboard/atoms/overview.js +2 -16
  194. package/dist/dashboard/atoms/remove.d.ts +112 -0
  195. package/dist/dashboard/atoms/remove.js +268 -0
  196. package/dist/dashboard/atoms/root.d.ts +2 -2
  197. package/dist/dashboard/atoms/root.js +41 -26
  198. package/dist/dashboard/atoms/user.js +1 -1
  199. package/dist/dashboard/atoms/utils.js +1 -1
  200. package/dist/dashboard/boot/Boot.js +25 -17
  201. package/dist/dashboard/boot/DashboardWorker.d.ts +1 -0
  202. package/dist/dashboard/boot/DashboardWorker.js +10 -46
  203. package/dist/dashboard/boot/LoadWorker.js +17 -10
  204. package/dist/dashboard/cms/EntryTable.js +8 -5
  205. package/dist/database/BrowserEntryStore.d.ts +12 -62
  206. package/dist/database/BrowserEntryStore.js +97 -196
  207. package/dist/database/DatabaseTables.js +2 -2
  208. package/dist/database/EntryDatabase.js +21 -1
  209. package/dist/database/EntryTransaction.js +0 -35
  210. package/dist/database/EntryUrlAliases.d.ts +4 -0
  211. package/dist/database/EntryUrlAliases.js +16 -10
  212. package/dist/database/driver/RuntimeDatabase.js +3 -3
  213. package/dist/database/driver/WasmDatabase.d.ts +5 -18
  214. package/dist/database/driver/WasmDatabase.js +15 -16
  215. package/dist/database/entry/EntryTable.js +2 -2
  216. package/dist/database/query/EntryQuery.js +1 -1
  217. package/dist/database/query/Relation.js +1 -1
  218. package/dist/database/query/Search.js +2 -2
  219. package/dist/field/code/CodeField.view.js +16 -16
  220. package/dist/field/link/EntryLink.d.ts +4 -4
  221. package/dist/field/link/FileLink.d.ts +4 -4
  222. package/dist/field/link/ImageLink.d.ts +4 -4
  223. package/dist/field/link/Link.d.ts +1 -1
  224. package/dist/field/link/LinkField.d.ts +5 -0
  225. package/dist/field/link/LinkField.view.js +244 -370
  226. package/dist/field/link/UrlLink.d.ts +4 -4
  227. package/dist/field/list/ListField.d.ts +2 -0
  228. package/dist/field/list/ListField.view.d.ts +15 -0
  229. package/dist/field/list/ListField.view.js +343 -301
  230. package/dist/field/metadata/MetadataField.d.ts +17 -7
  231. package/dist/field/metadata/MetadataField.js +14 -3
  232. package/dist/field/metadata/MetadataField.view.d.ts +1 -0
  233. package/dist/field/metadata/MetadataField.view.js +25 -21
  234. package/dist/field/richtext/RichTextBlock.d.ts +3 -1
  235. package/dist/field/richtext/RichTextBlock.js +177 -74
  236. package/dist/field/richtext/RichTextBlockHost.js +1 -1
  237. package/dist/field/richtext/RichTextExtensions.js +31 -31
  238. package/dist/field/richtext/RichTextField.view.js +14 -8
  239. package/dist/field/richtext/RichTextInsertMenu.js +43 -27
  240. package/dist/field/views.js +2 -0
  241. package/dist/index.css +490 -309
  242. package/dist/picker/url/UrlPicker.js +1 -0
  243. package/docs/components/badge.md +1 -0
  244. package/docs/components/button.md +1 -0
  245. package/docs/components/content-card.md +1 -0
  246. package/docs/components/search-field.md +1 -0
  247. package/docs/components/sortable-list.md +5 -20
  248. package/docs/components/table.md +1 -0
  249. package/docs/configuration.md +1 -1
  250. package/docs/deploy/self-host.md +1 -1
  251. package/docs/deploy.md +5 -4
  252. package/docs/fields.md +1 -1
  253. package/docs/index.md +1 -1
  254. package/docs/instant-publishing.md +7 -22
  255. package/docs/overviews.md +3 -4
  256. package/docs/upgrading.md +3 -2
  257. package/llms-full.txt +31 -55
  258. package/package.json +2 -3
  259. package/dist/core/db/EntryValidationError.d.ts +0 -13
  260. package/dist/core/db/EntryValidationError.js +0 -23
  261. package/dist/dashboard/app/DeleteDialog.d.ts +0 -11
  262. package/dist/dashboard/atoms/delete.d.ts +0 -68
  263. package/dist/dashboard/atoms/delete.js +0 -143
  264. 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 {};
@@ -12037,6 +12455,7 @@ declare module 'alinea/dashboard/boot/DashboardWorker' {
12037
12455
  import { type Activity } from 'alinea/dashboard/boot/ActivityEvent';
12038
12456
  export class DashboardWorker extends EventTarget {
12039
12457
  #private;
12458
+ /** Keep the content in memory with `source`, instead of in the browser. */
12040
12459
  constructor(source?: Source);
12041
12460
  get db(): EntryStore | Promise<EntryStore>;
12042
12461
  sync(): Promise<string>;
@@ -13010,6 +13429,7 @@ declare module 'alinea/dashboard/ViewKeys' {
13010
13429
  MetadataUserInput: string;
13011
13430
  MetadataInput: string;
13012
13431
  MetadataDetailsView: string;
13432
+ MetadataPreviewsView: string;
13013
13433
  NumberInput: string;
13014
13434
  ObjectInput: string;
13015
13435
  PathInput: string;
@@ -13027,86 +13447,36 @@ declare module 'alinea/dashboard/ViewKeys' {
13027
13447
  declare module 'alinea/database/BrowserEntryStore' {
13028
13448
 
13029
13449
  import type { Config } from 'alinea/core/Config';
13030
- import type { Mutation } from 'alinea/core/db/Mutation';
13031
- import type { CommitRequest } from 'alinea/core/db/CommitRequest';
13032
13450
  import type { SyncOptions } from 'alinea/core/db/LocalStore';
13033
13451
  import type { RemoteSource } from 'alinea/core/source/Source';
13452
+ import { type SnapshotStorage } from '@alinea/sqlite-wasm/snapshots';
13034
13453
  import { EntryStore } from 'alinea/database/EntryStore';
13035
13454
  export interface BrowserEntryStoreOptions {
13036
13455
  name: string;
13037
- 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;
13038
13460
  /**
13039
- * Start over from an empty database instead of the stored one, such as
13461
+ * Start over from an empty database instead of a stored base, such as
13040
13462
  * when it turned out to be corrupt.
13041
13463
  */
13042
13464
  reset?: boolean;
13043
- /** The IndexedDB implementation, the global one by default. */
13044
- indexedDB?: IDBFactory;
13045
- IDBKeyRange?: typeof IDBKeyRange;
13046
- /** The Web Locks implementation, the global one by default. */
13047
- locks?: CacheLocks;
13048
- }
13049
- /** The part of the Web Locks API that keeps a cache to one worker. */
13050
- export interface CacheLocks {
13051
- request(name: string, options: {
13052
- ifAvailable?: boolean;
13053
- signal?: AbortSignal;
13054
- }, callback: (lock: Lock | null) => Promise<void> | void): Promise<unknown>;
13055
- }
13056
- export interface HoldCacheOptions {
13057
- /**
13058
- * How long to wait, in milliseconds, for another worker that holds the
13059
- * cache to end: none by default, Infinity until it does.
13060
- */
13061
- wait?: number;
13062
- /** The Web Locks implementation, the global one by default. */
13063
- locks?: CacheLocks;
13064
13465
  }
13065
13466
  /**
13066
- * Hold the cache called `name` for as long as this worker runs, and resolve
13067
- * with whether it does. Nothing coordinates two stores writing the same
13068
- * pages, which corrupts them, so only the worker that holds the cache opens
13069
- * it; another one, such as one of another dashboard build, reads a
13070
- * snapshot. Without Web Locks, or if they refuse, no worker holds it.
13071
- */
13072
- export function holdCache(name: string, options?: HoldCacheOptions): Promise<boolean>;
13073
- /**
13074
- * WASM entry store kept in IndexedDB: every commit stores the pages it
13075
- * 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.
13076
13472
  */
13077
13473
  export class BrowserEntryStore extends EntryStore {
13078
13474
  #private;
13079
- /**
13080
- * Open the store kept in IndexedDB. Only the worker that holds the cache
13081
- * opens it (see `holdCache`), and deletes the caches of other versions.
13082
- */
13083
13475
  static open(config: Config, options: BrowserEntryStoreOptions): Promise<BrowserEntryStore>;
13084
- /**
13085
- * A copy in memory of the stored content, for a worker that cannot store
13086
- * it while another one does: it loads the last committed state and never
13087
- * writes. Resolves with undefined if that state cannot be read.
13088
- */
13089
- static snapshot(config: Config, options: BrowserEntryStoreOptions): Promise<EntryStore | undefined>;
13090
13476
  private constructor();
13091
- /**
13092
- * Opening, and syncing, resolve once the content is in memory: storing it
13093
- * is a cache that finishes in the background, which takes seconds for a
13094
- * first sync or a rebuild of a large project.
13095
- */
13096
13477
  sync(): Promise<string>;
13097
13478
  syncWith(remote: RemoteSource, options?: SyncOptions): Promise<string>;
13098
- mutate(mutations: Array<Mutation>): Promise<{
13099
- sha: string;
13100
- }>;
13101
- write(request: CommitRequest): Promise<{
13102
- sha: string;
13103
- }>;
13104
- /**
13105
- * Stop storing, and resolve once the commits so far are stored, for a
13106
- * replacement to open the cache. Work in flight then changes only this
13107
- * store's memory, so the replacement need not wait for it.
13108
- */
13109
- release(): Promise<void>;
13479
+ /** Close once the last checkpoint is stored. */
13110
13480
  close(): Promise<void>;
13111
13481
  }
13112
13482
  /** Whether an error, or one it holds, reports a damaged SQLite database. */
@@ -13230,7 +13600,7 @@ declare module 'alinea/database/driver/RuntimeDatabase' {
13230
13600
 
13231
13601
  declare module 'alinea/database/driver/WasmDatabase' {
13232
13602
 
13233
- import type { Storage } from '@alinea/sqlite-wasm/Database.js';
13603
+ import type { Database as WasmSqlite } from '@alinea/sqlite-wasm/Database.js';
13234
13604
  import type { DatabaseOptions } from 'rado';
13235
13605
  import { connect } from 'rado/driver/sql.js';
13236
13606
  import type { DatabaseHandle } from 'alinea/database/driver/DatabaseHandle';
@@ -13238,24 +13608,11 @@ declare module 'alinea/database/driver/WasmDatabase' {
13238
13608
  database: ReturnType<typeof connect>;
13239
13609
  fork(): Promise<WasmDatabaseHandle>;
13240
13610
  export(): Uint8Array;
13241
- /** Resolves once every commit so far is stored; at once in memory. */
13242
- flush(): Promise<void>;
13243
- /**
13244
- * Store this database from now on, replacing what the storage held, once
13245
- * a database detached from it wrote its last commits.
13246
- */
13247
- attach(storage: Storage): Promise<void>;
13248
- /**
13249
- * Stop storing commits once the ones so far are written; later commits
13250
- * change only the memory copy.
13251
- */
13252
- detach(): void;
13253
13611
  }
13254
- /**
13255
- * Open a WASM connection from the database kept in storage, or an empty one,
13256
- * that stores every commit there.
13257
- */
13258
- 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;
13259
13616
  /** Open an in-memory WASM connection, optionally from a complete SQLite file. */
13260
13617
  export function openWasmDatabase(data?: Uint8Array, options?: DatabaseOptions): Promise<WasmDatabaseHandle>;
13261
13618
  export function wasmDatabase(data?: Uint8Array): Promise<import("rado").SyncDatabase<"sqlite">>;
@@ -13675,7 +14032,11 @@ declare module 'alinea/database/EntryTransaction' {
13675
14032
  declare module 'alinea/database/EntryUrlAliases' {
13676
14033
 
13677
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;
13678
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>;
13679
14040
 
13680
14041
  }
13681
14042
 
@@ -14470,10 +14831,10 @@ declare module 'alinea/field/link/EntryLink' {
14470
14831
  import type { Label } from 'alinea/core/Label';
14471
14832
  import type { Type } from 'alinea/core/Type';
14472
14833
  import type { ListRow } from 'alinea/core/ListRow';
14473
- import { type LinkFieldOptions } from 'alinea/field/link/LinkField';
14834
+ import { type LinkFieldOptions, type LinkFields } from 'alinea/field/link/LinkField';
14474
14835
  import { type EntryPickerOptions } from 'alinea/picker/entry';
14475
14836
  import type { EntryReference } from 'alinea/picker/entry/EntryReference';
14476
- export interface EntryLink<InferredFields = undefined> extends EntryReference {
14837
+ export interface EntryLink<InferredFields = {}> extends EntryReference {
14477
14838
  entryId: string;
14478
14839
  entryType: string;
14479
14840
  title: string;
@@ -14492,9 +14853,9 @@ declare module 'alinea/field/link/EntryLink' {
14492
14853
  }
14493
14854
  interface EntryOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14494
14855
  }
14495
- 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>>>;
14496
14857
  export namespace entry {
14497
- type EntryRow<Fields> = EntryLink<Type.Infer<Fields>> & ListRow;
14858
+ type EntryRow<Fields> = EntryLink<LinkFields<Type.Infer<Fields>>> & ListRow;
14498
14859
  interface EntryOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14499
14860
  }
14500
14861
  export function multiple<Fields = undefined>(label: Label, options?: WithoutLabel<EntryOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<EntryReference & ListRow & InferStoredValue<Fields>, EntryRow<Fields>>;
@@ -14510,10 +14871,10 @@ declare module 'alinea/field/link/FileLink' {
14510
14871
  import type { InferStoredValue } from 'alinea/core/Infer';
14511
14872
  import type { Label } from 'alinea/core/Label';
14512
14873
  import type { ListRow } from 'alinea/core/ListRow';
14513
- import { type LinkFieldOptions } from 'alinea/field/link/LinkField';
14874
+ import { type LinkFieldOptions, type LinkFields } from 'alinea/field/link/LinkField';
14514
14875
  import { type EntryPickerOptions } from 'alinea/picker/entry';
14515
14876
  import type { EntryReference } from 'alinea/picker/entry/EntryReference';
14516
- export interface FileLink<InferredFields = undefined> extends EntryReference {
14877
+ export interface FileLink<InferredFields = {}> extends EntryReference {
14517
14878
  title: string;
14518
14879
  /** Public url of the file */
14519
14880
  url: string;
@@ -14533,11 +14894,11 @@ declare module 'alinea/field/link/FileLink' {
14533
14894
  export function filePicker<Fields>(multiple: boolean, options: Omit<EntryPickerOptions<Fields>, 'selection'>): import("alinea/core/Picker").Picker<EntryReference, EntryPickerOptions<Fields>>;
14534
14895
  export interface FileOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14535
14896
  }
14536
- 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>>>;
14537
14898
  export namespace file {
14538
14899
  interface FilesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14539
14900
  }
14540
- 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>>>;
14541
14902
  }
14542
14903
 
14543
14904
  }
@@ -14549,10 +14910,10 @@ declare module 'alinea/field/link/ImageLink' {
14549
14910
  import type { Label } from 'alinea/core/Label';
14550
14911
  import type { Type } from 'alinea/core/Type';
14551
14912
  import type { ListRow } from 'alinea/core/ListRow';
14552
- 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';
14553
14914
  import { type EntryPickerOptions } from 'alinea/picker/entry';
14554
14915
  import type { EntryReference } from 'alinea/picker/entry/EntryReference';
14555
- export interface ImageLink<InferredFields = undefined> extends EntryReference {
14916
+ export interface ImageLink<InferredFields = {}> extends EntryReference {
14556
14917
  title: string;
14557
14918
  alt?: string;
14558
14919
  /** Public url of the image file, versioned by its hash */
@@ -14589,12 +14950,12 @@ declare module 'alinea/field/link/ImageLink' {
14589
14950
  y: number;
14590
14951
  }>;
14591
14952
  }
14592
- 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>>>> {
14593
14954
  }
14594
14955
  export interface ImageOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14595
14956
  }
14596
14957
  export function image<Fields = undefined>(label: Label, options?: WithoutLabel<ImageOptions<Fields>>): ImageField<Fields>;
14597
- 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>>>> {
14598
14959
  }
14599
14960
  export namespace image {
14600
14961
  interface ImagesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
@@ -14617,7 +14978,7 @@ declare module 'alinea/field/link/Link' {
14617
14978
  import { type UrlReference } from 'alinea/picker/url';
14618
14979
  import { EntryLink } from 'alinea/field/link/EntryLink';
14619
14980
  import type { UrlLink } from 'alinea/field/link/UrlLink';
14620
- export type Link<InferredFields> = EntryLink<InferredFields> | UrlLink<InferredFields> | FileLink<InferredFields>;
14981
+ export type Link<InferredFields = {}> = EntryLink<InferredFields> | UrlLink<InferredFields> | FileLink<InferredFields>;
14621
14982
  export interface LinkOptions<Definition, Row> extends LinkFieldOptions<Row>, EntryPickerConditions {
14622
14983
  fields?: Definition | Type<Definition>;
14623
14984
  }
@@ -14671,6 +15032,11 @@ declare module 'alinea/field/link/LinkField' {
14671
15032
  import { ListRow } from 'alinea/core/ListRow';
14672
15033
  import type { ReactNode } from 'react';
14673
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;
14674
15040
  export interface LinkFieldOptions<Value> extends FieldOptions<Value> {
14675
15041
  /**
14676
15042
  * Allow the same entry to be linked more than once.
@@ -14733,9 +15099,9 @@ declare module 'alinea/field/link/UrlLink' {
14733
15099
  import type { Label } from 'alinea/core/Label';
14734
15100
  import type { Type } from 'alinea/core/Type';
14735
15101
  import type { ListRow } from 'alinea/core/ListRow';
14736
- import { type LinkFieldOptions } from 'alinea/field/link/LinkField';
15102
+ import { type LinkFieldOptions, type LinkFields } from 'alinea/field/link/LinkField';
14737
15103
  import { type UrlPickerOptions, type UrlReference } from 'alinea/picker/url';
14738
- export interface UrlLink<InferredFields = undefined> extends UrlReference {
15104
+ export interface UrlLink<InferredFields = {}> extends UrlReference {
14739
15105
  url: string;
14740
15106
  href: string;
14741
15107
  title: string;
@@ -14746,9 +15112,9 @@ declare module 'alinea/field/link/UrlLink' {
14746
15112
  }
14747
15113
  export interface UrlOptions<Fields> extends LinkFieldOptions<UrlReference & InferStoredValue<Fields>>, UrlPickerOptions<Fields> {
14748
15114
  }
14749
- 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>>>;
14750
15116
  export namespace url {
14751
- type UrlRows<Fields> = UrlLink<Type.Infer<Fields>> & ListRow;
15117
+ type UrlRows<Fields> = UrlLink<LinkFields<Type.Infer<Fields>>> & ListRow;
14752
15118
  export interface UrlOptions<Fields> extends LinkFieldOptions<Array<UrlReference & ListRow & InferStoredValue<Fields>>>, UrlPickerOptions<Fields> {
14753
15119
  }
14754
15120
  export function multiple<Fields>(label: Label, options?: WithoutLabel<UrlOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<UrlReference & ListRow & InferStoredValue<Fields>, UrlRows<Fields>>;
@@ -14781,6 +15147,8 @@ declare module 'alinea/field/list/ListField' {
14781
15147
  help?: ReactNode;
14782
15148
  /** Display a minimal version */
14783
15149
  inline?: boolean;
15150
+ /** Show the rows folded when the entry opens, added rows start open */
15151
+ collapsed?: boolean;
14784
15152
  /** Hide this list field */
14785
15153
  hidden?: boolean;
14786
15154
  /** Mark the field as invalid while it has fewer items */
@@ -14810,12 +15178,27 @@ declare module 'alinea/field/list/ListField.view' {
14810
15178
  _type: string;
14811
15179
  [key: string]: unknown;
14812
15180
  }
15181
+ export interface ListFieldTypeItem {
15182
+ id: string;
15183
+ label: string;
15184
+ type: Schema[string];
15185
+ }
14813
15186
  export interface ListFieldViewProps {
14814
15187
  field: CoreListField<ListRow, ListValue, ListOptions<Schema>>;
14815
15188
  }
14816
15189
  export function ListFieldView({ field }: ListFieldViewProps): import("react").JSX.Element;
14817
15190
  export function insertIndex(rowIndex: number, position: 'before' | 'after'): number;
14818
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;
14819
15202
  export {};
14820
15203
 
14821
15204
  }
@@ -15018,19 +15401,20 @@ declare module 'alinea/field/metadata/MetadataField' {
15018
15401
  fields: Type<MetadataFields>;
15019
15402
  seo: Type<MetadataSeoFields>;
15020
15403
  details: Type<MetadataDetailsFields>;
15021
- /** The details are rendered by a separate `metadataDetails` section */
15022
- detailsSection?: boolean;
15404
+ /** The details and previews are rendered by the separate
15405
+ * `metadataDetails` and `metadataPreviews` sections */
15406
+ sections?: boolean;
15023
15407
  }
15024
15408
  export interface MetadataConfig {
15025
- /** Leave the details out of the field view, render them with
15026
- * `metadataDetails(field)` instead */
15027
- 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;
15028
15412
  }
15029
15413
  export class MetadataField extends RecordField<Metadata, MetadataOptions> {
15030
15414
  }
15031
15415
  export interface MetadataField extends MetadataFields {
15032
15416
  }
15033
- export function metadata(label?: string, { detailsSection }?: MetadataConfig): MetadataField;
15417
+ export function metadata(label?: string, { sections }?: MetadataConfig): MetadataField;
15034
15418
  export class MetadataDetailsSection implements SectionData {
15035
15419
  field: MetadataField;
15036
15420
  view: string;
@@ -15040,8 +15424,17 @@ declare module 'alinea/field/metadata/MetadataField' {
15040
15424
  constructor(field: MetadataField);
15041
15425
  }
15042
15426
  /** Renders the created/updated details and aliases of a metadata field
15043
- * created with `detailsSection: true` */
15427
+ * created with `sections: true` */
15044
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;
15045
15438
 
15046
15439
  }
15047
15440
 
@@ -15061,6 +15454,7 @@ declare module 'alinea/field/metadata/MetadataField.view' {
15061
15454
  }
15062
15455
  export function MetadataUserFieldView({ field }: MetadataUserFieldViewProps): import("react").JSX.Element;
15063
15456
  export function MetadataFieldView({ field }: MetadataFieldViewProps): import("react").JSX.Element;
15457
+ export function MetadataPreviewsView(): import("react").JSX.Element;
15064
15458
  export interface MetadataDetailsViewProps {
15065
15459
  section: Section;
15066
15460
  }
@@ -15390,8 +15784,10 @@ declare module 'alinea/field/richtext/RichTextBlock' {
15390
15784
  readOnly: boolean;
15391
15785
  onDelete: () => void;
15392
15786
  onDuplicate: () => void;
15787
+ /** Called as the header starts a drag, before the editor handles it */
15788
+ onDragStart: () => void;
15393
15789
  }
15394
- 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>;
15395
15791
 
15396
15792
  }
15397
15793