alinea 2.0.0-preview.13 → 2.0.0-preview.14

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 (323) 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 +6 -2
  5. package/dist/adapter/next/context.js +1 -4
  6. package/dist/adapter/next/handler.js +12 -1
  7. package/dist/backend/CommitEntries.d.ts +17 -0
  8. package/dist/backend/CommitEntries.js +39 -0
  9. package/dist/backend/Handler.d.ts +9 -0
  10. package/dist/backend/Handler.js +80 -20
  11. package/dist/backend/api/BasicAuth.js +5 -1
  12. package/dist/backend/api/CreateBackend.js +2 -2
  13. package/dist/backend/api/DatabaseApi.js +18 -5
  14. package/dist/backend/api/GithubApi.d.ts +13 -0
  15. package/dist/backend/api/GithubApi.js +170 -30
  16. package/dist/backend/api/database.js +2 -2
  17. package/dist/backend/util/Syncable.d.ts +6 -0
  18. package/dist/backend/util/Syncable.js +3 -1
  19. package/dist/bundled.d.ts +911 -151
  20. package/dist/chunks/{chunk-5H4D5JQL.js → chunk-2PP7WJUJ.js} +1 -11
  21. package/dist/chunks/{chunk-QHHMKJUO.js → chunk-7DDPSWRY.js} +465 -2
  22. package/dist/chunks/{chunk-HOETF6HN.js → chunk-PMGIIYDD.js} +1 -0
  23. package/dist/chunks/{chunk-3QFSGTGF.js → chunk-SCV3TEXM.js} +1 -1
  24. package/dist/chunks/{chunk-UPY2RUZF.js → chunk-TWBREZWC.js} +30 -0
  25. package/dist/cli/Generate.js +20 -7
  26. package/dist/cli/Mcp.js +1 -1
  27. package/dist/cli/Serve.js +1 -1
  28. package/dist/cli/bin.js +1 -1
  29. package/dist/cli/build/BuildEmitter.d.ts +5 -1
  30. package/dist/cli/build/BuildEmitter.js +8 -3
  31. package/dist/cli/generate/CompileConfig.js +10 -6
  32. package/dist/cli/generate/DevDB.d.ts +4 -1
  33. package/dist/cli/generate/DevDB.js +15 -7
  34. package/dist/cli/generate/GenerateDashboard.js +1 -0
  35. package/dist/cli/generate/MediaLocations.d.ts +18 -0
  36. package/dist/cli/generate/MediaLocations.js +69 -0
  37. package/dist/cli/serve/CreateLocalServer.js +1 -0
  38. package/dist/cli/serve/mcp/DevMcp.js +1 -1
  39. package/dist/cloud/CloudRemote.js +21 -7
  40. package/dist/cms.d.ts +4 -2
  41. package/dist/components/Alert.js +2 -2
  42. package/dist/components/AppShell.js +2 -2
  43. package/dist/components/AsyncList.d.ts +52 -0
  44. package/dist/components/AsyncList.js +69 -0
  45. package/dist/components/Breadcrumbs.js +6 -6
  46. package/dist/components/Button.js +4 -4
  47. package/dist/components/Calendar.js +18 -18
  48. package/dist/components/Checkbox.js +10 -10
  49. package/dist/components/CheckboxGroup.js +2 -2
  50. package/dist/components/Collapsible.js +5 -5
  51. package/dist/components/ColorSwatch.js +2 -2
  52. package/dist/components/ColorSwatchPicker.js +4 -4
  53. package/dist/components/ComboBox.d.ts +27 -9
  54. package/dist/components/ComboBox.js +40 -9
  55. package/dist/components/Command.js +13 -13
  56. package/dist/components/ContentCard.js +28 -28
  57. package/dist/components/ContentGrid.js +11 -11
  58. package/dist/components/DataList.js +1 -1
  59. package/dist/components/DateField.d.ts +4 -0
  60. package/dist/components/DateField.js +2 -2
  61. package/dist/components/DatePicker.js +2 -2
  62. package/dist/components/DateRangePicker.js +2 -2
  63. package/dist/components/Dialog.d.ts +14 -3
  64. package/dist/components/Dialog.js +38 -11
  65. package/dist/components/DropZone.js +6 -6
  66. package/dist/components/DropdownMenu.js +8 -8
  67. package/dist/components/Empty.js +4 -4
  68. package/dist/components/Field.d.ts +1 -1
  69. package/dist/components/Field.js +18 -12
  70. package/dist/components/Form.d.ts +23 -0
  71. package/dist/components/Form.js +64 -0
  72. package/dist/components/Link.d.ts +8 -1
  73. package/dist/components/List.js +27 -27
  74. package/dist/components/MediaPreview.js +5 -5
  75. package/dist/components/MultipleSelect.d.ts +13 -8
  76. package/dist/components/MultipleSelect.js +25 -25
  77. package/dist/components/NavRail.js +7 -7
  78. package/dist/components/NumberField.d.ts +5 -1
  79. package/dist/components/NumberField.js +6 -4
  80. package/dist/components/Page.js +6 -6
  81. package/dist/components/Popover.js +2 -2
  82. package/dist/components/PreviewFrame.js +8 -8
  83. package/dist/components/RadioGroup.js +10 -10
  84. package/dist/components/Resizable.js +1 -1
  85. package/dist/components/SearchField.js +8 -8
  86. package/dist/components/Select.d.ts +13 -10
  87. package/dist/components/Select.js +18 -32
  88. package/dist/components/Sheet.js +6 -6
  89. package/dist/components/Sidebar.js +5 -5
  90. package/dist/components/SortableList.js +12 -12
  91. package/dist/components/Spinner.js +4 -4
  92. package/dist/components/Stack.d.ts +20 -0
  93. package/dist/components/Stack.js +45 -0
  94. package/dist/components/Surface.js +1 -1
  95. package/dist/components/Switch.js +5 -5
  96. package/dist/components/Table.d.ts +11 -1
  97. package/dist/components/Table.js +81 -53
  98. package/dist/components/Tabs.js +6 -6
  99. package/dist/components/TagGroup.js +10 -10
  100. package/dist/components/TextField.d.ts +7 -1
  101. package/dist/components/TextField.js +20 -11
  102. package/dist/components/TimeField.d.ts +6 -1
  103. package/dist/components/TimeField.js +51 -26
  104. package/dist/components/Timestamp.js +1 -1
  105. package/dist/components/ToggleGroup.js +2 -2
  106. package/dist/components/Toolbar.js +3 -3
  107. package/dist/components/Tooltip.js +5 -4
  108. package/dist/components/Tree.js +27 -27
  109. package/dist/components/internal/DateControl.js +11 -11
  110. package/dist/components/internal/FormScope.d.ts +2 -0
  111. package/dist/components/internal/FormScope.js +8 -0
  112. package/dist/components/internal/ListBoxGroup.d.ts +12 -0
  113. package/dist/components/internal/ListBoxGroup.js +46 -0
  114. package/dist/components/internal/ListBoxOption.d.ts +2 -2
  115. package/dist/components/internal/ListBoxOption.js +5 -5
  116. package/dist/components/internal/Locale.js +2 -2
  117. package/dist/components/internal/PopoverSurface.js +5 -4
  118. package/dist/components/internal/SelectionCheckbox.js +5 -5
  119. package/dist/components/internal/Slot.js +1 -1
  120. package/dist/components/internal/ToggleButton.js +4 -4
  121. package/dist/components/internal/Trigger.js +2 -2
  122. package/dist/components/internal/useDragDrop.js +2 -2
  123. package/dist/components/types.d.ts +4 -0
  124. package/dist/components.d.ts +3 -0
  125. package/dist/components.js +3 -0
  126. package/dist/core/Client.js +12 -1
  127. package/dist/core/Config.d.ts +6 -0
  128. package/dist/core/Config.js +12 -0
  129. package/dist/core/DashboardEntry.d.ts +19 -0
  130. package/dist/core/DashboardEntry.js +0 -0
  131. package/dist/core/Document.d.ts +2 -2
  132. package/dist/core/Document.js +10 -3
  133. package/dist/core/EntryFields.d.ts +8 -2
  134. package/dist/core/Field.d.ts +5 -0
  135. package/dist/core/Filter.d.ts +19 -2
  136. package/dist/core/Filter.js +5 -0
  137. package/dist/core/Graph.d.ts +10 -2
  138. package/dist/core/Overview.d.ts +74 -5
  139. package/dist/core/Overview.js +1 -0
  140. package/dist/core/Root.js +1 -1
  141. package/dist/core/Scope.d.ts +5 -0
  142. package/dist/core/Scope.js +22 -5
  143. package/dist/core/Type.d.ts +25 -6
  144. package/dist/core/Type.js +25 -7
  145. package/dist/core/db/LocalStore.d.ts +7 -0
  146. package/dist/core/field/CreateField.d.ts +1 -2
  147. package/dist/core/source/Source.d.ts +2 -0
  148. package/dist/core/source/Source.js +11 -0
  149. package/dist/core/util/Async.d.ts +2 -0
  150. package/dist/core/util/Async.js +12 -1
  151. package/dist/core/util/Checks.js +1 -1
  152. package/dist/core/util/Slugs.js +4 -3
  153. package/dist/dashboard/App.css +13 -11
  154. package/dist/dashboard/App.d.ts +1 -0
  155. package/dist/dashboard/App.js +22 -6
  156. package/dist/dashboard/Href.d.ts +37 -0
  157. package/dist/dashboard/Href.js +46 -0
  158. package/dist/dashboard/app/ActivityStatus.js +34 -12
  159. package/dist/dashboard/app/AuthView.js +2 -2
  160. package/dist/dashboard/app/BlockSettings.d.ts +9 -0
  161. package/dist/dashboard/app/BlockSettings.js +16 -0
  162. package/dist/dashboard/app/BlockSheet.js +3 -3
  163. package/dist/dashboard/app/BlockSheetStoryFrame.js +2 -2
  164. package/dist/dashboard/app/CompactField.js +15 -15
  165. package/dist/dashboard/app/CroppedImage.js +2 -2
  166. package/dist/dashboard/app/DashboardLayout.d.ts +1 -1
  167. package/dist/dashboard/app/DashboardLayout.js +12 -0
  168. package/dist/dashboard/app/EntryActions.d.ts +11 -0
  169. package/dist/dashboard/app/EntryActions.js +30 -0
  170. package/dist/dashboard/app/EntryFields.d.ts +15 -2
  171. package/dist/dashboard/app/EntryFields.js +26 -5
  172. package/dist/dashboard/app/EntryHeader.js +17 -7
  173. package/dist/dashboard/app/EntryReferences.js +2 -2
  174. package/dist/dashboard/app/EntrySidebar.js +25 -23
  175. package/dist/dashboard/app/EntrySidebarPreview.js +2 -2
  176. package/dist/dashboard/app/EntryValidationModal.js +3 -3
  177. package/dist/dashboard/app/Explorer.js +84 -45
  178. package/dist/dashboard/app/ExplorerBatchActions.js +4 -4
  179. package/dist/dashboard/app/ExplorerControls.js +5 -5
  180. package/dist/dashboard/app/ExplorerList.js +2 -2
  181. package/dist/dashboard/app/ExplorerModal.js +3 -3
  182. package/dist/dashboard/app/ExplorerOverviewHeader.d.ts +6 -0
  183. package/dist/dashboard/app/ExplorerOverviewHeader.js +28 -0
  184. package/dist/dashboard/app/ExplorerTable.js +7 -7
  185. package/dist/dashboard/app/ExplorerTabs.d.ts +12 -0
  186. package/dist/dashboard/app/ExplorerTabs.js +42 -0
  187. package/dist/dashboard/app/ImageEditor.js +31 -31
  188. package/dist/dashboard/app/LinkPicker.js +3 -3
  189. package/dist/dashboard/app/LocaleMenu.js +9 -9
  190. package/dist/dashboard/app/MediaPreviewCell.js +5 -5
  191. package/dist/dashboard/app/OverviewCell.js +12 -10
  192. package/dist/dashboard/app/RemoveDialog.js +5 -0
  193. package/dist/dashboard/app/SearchBar.js +4 -4
  194. package/dist/dashboard/app/SettingsTab.d.ts +6 -0
  195. package/dist/dashboard/app/SettingsTab.js +27 -0
  196. package/dist/dashboard/app/SidebarTree.js +19 -19
  197. package/dist/dashboard/app/WorkspaceMenu.js +6 -6
  198. package/dist/dashboard/app/WorkspaceRoots.js +102 -98
  199. package/dist/dashboard/app/editor/FileEditor.js +18 -18
  200. package/dist/dashboard/app/pages/EntryPage.js +42 -39
  201. package/dist/dashboard/app/pages/RootPage.js +3 -3
  202. package/dist/dashboard/app/pages/SplashPage.js +94 -79
  203. package/dist/dashboard/app/pages/UsersPage.js +9 -9
  204. package/dist/dashboard/app/ui/DashboardModal.js +14 -14
  205. package/dist/dashboard/atoms/activity.d.ts +1 -0
  206. package/dist/dashboard/atoms/activity.js +8 -5
  207. package/dist/dashboard/atoms/config.d.ts +2 -0
  208. package/dist/dashboard/atoms/dashboard.d.ts +11 -0
  209. package/dist/dashboard/atoms/dashboard.js +8 -0
  210. package/dist/dashboard/atoms/editor.d.ts +7 -1
  211. package/dist/dashboard/atoms/editor.js +14 -2
  212. package/dist/dashboard/atoms/explorer.d.ts +15 -0
  213. package/dist/dashboard/atoms/explorer.js +43 -14
  214. package/dist/dashboard/atoms/graph.d.ts +5 -0
  215. package/dist/dashboard/atoms/graph.js +26 -1
  216. package/dist/dashboard/atoms/nav.d.ts +16 -10
  217. package/dist/dashboard/atoms/nav.js +96 -36
  218. package/dist/dashboard/atoms/overview.d.ts +26 -4
  219. package/dist/dashboard/atoms/overview.js +41 -12
  220. package/dist/dashboard/atoms/root.js +7 -1
  221. package/dist/dashboard/boot/ActivityEvent.d.ts +1 -1
  222. package/dist/dashboard/boot/Boot.js +6 -0
  223. package/dist/dashboard/boot/DashboardWorker.d.ts +6 -0
  224. package/dist/dashboard/boot/DashboardWorker.js +89 -8
  225. package/dist/dashboard/boot/LoadWorker.js +3 -0
  226. package/dist/dashboard/boot/SyncEvent.d.ts +7 -0
  227. package/dist/dashboard/boot/SyncEvent.js +14 -0
  228. package/dist/dashboard/boot/WorkerDB.d.ts +1 -0
  229. package/dist/dashboard/boot/WorkerDB.js +3 -0
  230. package/dist/dashboard/cms/EntryTable.d.ts +4 -3
  231. package/dist/dashboard/cms/EntryTable.js +2 -2
  232. package/dist/dashboard/cms/ViewProps.d.ts +3 -2
  233. package/dist/dashboard/cms/hooks.d.ts +34 -18
  234. package/dist/dashboard/cms/hooks.js +43 -18
  235. package/dist/dashboard/contexts.d.ts +3 -2
  236. package/dist/dashboard/contexts.js +3 -2
  237. package/dist/dashboard/hook/UseEntryEditor.d.ts +1 -1
  238. package/dist/dashboard/hook/UseEntryEditor.js +7 -3
  239. package/dist/dashboard/icons.d.ts +1 -0
  240. package/dist/dashboard/icons.js +15 -0
  241. package/dist/database/BrowserEntryStore.js +35 -13
  242. package/dist/database/DatabaseTables.js +1 -1
  243. package/dist/database/EntryDatabase.d.ts +10 -1
  244. package/dist/database/EntryDatabase.js +31 -10
  245. package/dist/database/EntryStore.js +1 -11
  246. package/dist/database/entry/EntryTable.d.ts +5 -3
  247. package/dist/database/entry/EntryTable.js +25 -8
  248. package/dist/database/query/Condition.d.ts +4 -1
  249. package/dist/database/query/Condition.js +16 -5
  250. package/dist/database/query/EntryQuery.js +59 -7
  251. package/dist/database/query/Relation.d.ts +13 -1
  252. package/dist/database/query/Relation.js +19 -1
  253. package/dist/database/query/Search.d.ts +7 -1
  254. package/dist/database/query/Search.js +54 -9
  255. package/dist/database/sync/EntrySyncer.d.ts +2 -0
  256. package/dist/database/sync/EntrySyncer.js +4 -2
  257. package/dist/database/sync/Ingest.d.ts +2 -1
  258. package/dist/database/sync/Ingest.js +20 -6
  259. package/dist/field/code/CodeField.view.js +12 -12
  260. package/dist/field/link/EntryLink.d.ts +4 -5
  261. package/dist/field/link/FileLink.d.ts +2 -2
  262. package/dist/field/link/ImageLink.d.ts +2 -5
  263. package/dist/field/link/ImageLink.js +0 -1
  264. package/dist/field/link/LinkField.view.js +18 -15
  265. package/dist/field/list/ListField.view.js +50 -35
  266. package/dist/field/metadata/MetadataField.view.js +14 -14
  267. package/dist/field/richtext/RichTextBlock.js +39 -35
  268. package/dist/field/richtext/RichTextExtensions.js +23 -23
  269. package/dist/field/richtext/RichTextField.view.js +6 -6
  270. package/dist/field/richtext/RichTextInsertMenu.js +5 -5
  271. package/dist/field/richtext/RichTextToolbar.js +26 -1
  272. package/dist/field/richtext/Toolbar.d.ts +9 -1
  273. package/dist/field/richtext/Toolbar.js +12 -2
  274. package/dist/field/select/SelectField.view.js +1 -0
  275. package/dist/index.css +174 -23
  276. package/dist/index.d.ts +1 -0
  277. package/dist/index.js +3 -1
  278. package/dist/next.d.ts +2 -0
  279. package/dist/next.edge.d.ts +2 -0
  280. package/dist/picker/entry/EntryPicker.d.ts +3 -1
  281. package/dist/picker/entry/EntryPicker.js +15 -1
  282. package/dist/preview/react.js +3 -0
  283. package/dist/query.d.ts +26 -14
  284. package/dist/query.js +4 -0
  285. package/dist/types.d.ts +8 -5
  286. package/dist/ui/Stack.d.ts +4 -2
  287. package/docs/ai-setup.md +3 -3
  288. package/docs/cli.md +4 -2
  289. package/docs/components/combo-box.md +105 -16
  290. package/docs/components/date-field.md +1 -1
  291. package/docs/components/dialog.md +51 -0
  292. package/docs/components/form.md +67 -0
  293. package/docs/components/link.md +10 -0
  294. package/docs/components/multiple-select.md +9 -17
  295. package/docs/components/number-field.md +5 -1
  296. package/docs/components/select.md +9 -20
  297. package/docs/components/stack.md +42 -0
  298. package/docs/components/table.md +83 -0
  299. package/docs/components/text-field.md +9 -1
  300. package/docs/components/time-field.md +5 -1
  301. package/docs/components.md +39 -1
  302. package/docs/configuration.md +3 -1
  303. package/docs/custom-fields.md +135 -7
  304. package/docs/deploy/self-host.md +2 -0
  305. package/docs/deploy.md +7 -4
  306. package/docs/fields/image.md +0 -1
  307. package/docs/fields/list.md +19 -0
  308. package/docs/fields/object.md +5 -3
  309. package/docs/fields/rich-text.md +31 -0
  310. package/docs/index.md +8 -6
  311. package/docs/instant-publishing.md +23 -2
  312. package/docs/overviews.md +128 -4
  313. package/docs/query/filtering.md +2 -0
  314. package/docs/query/related.md +58 -9
  315. package/docs/query.md +52 -1
  316. package/docs/quickstart.md +5 -5
  317. package/docs/schema/document.md +2 -0
  318. package/docs/schema/type.md +3 -1
  319. package/docs/upgrading.md +100 -19
  320. package/llms-full.txt +1022 -125
  321. package/package.json +1 -1
  322. package/readme.md +2 -2
  323. package/dist/chunks/{chunk-4ZA6KZHS.js → chunk-6FQ5IMNA.js} +3 -3
package/dist/bundled.d.ts CHANGED
@@ -432,6 +432,19 @@ declare module 'alinea/backend/api/GithubApi' {
432
432
  import type { CommitRequest } from 'alinea/core/db/CommitRequest';
433
433
  import { GithubSource, type GithubSourceOptions } from 'alinea/core/source/GithubSource';
434
434
  export interface GithubOptions extends GithubSourceOptions {
435
+ /**
436
+ * Commit as this author instead of the account that owns the token. Such
437
+ * commits are written through the REST API, which GraphQL commits do not
438
+ * allow, so GitHub does not mark them as verified. A save usually spends
439
+ * three REST requests, plus one per uploaded file. Removing a file that is
440
+ * already gone costs one more tree request, and a branch that moved in
441
+ * the meantime repeats the save.
442
+ */
443
+ author?: GithubCommitAuthor;
444
+ }
445
+ export interface GithubCommitAuthor {
446
+ name: string;
447
+ email: string;
435
448
  }
436
449
  export class GithubApi extends GithubSource implements HistoryApi, CommitApi, SyncApi {
437
450
  #private;
@@ -627,6 +640,28 @@ declare module 'alinea/backend/Auth' {
627
640
 
628
641
  }
629
642
 
643
+ declare module 'alinea/backend/CommitEntries' {
644
+
645
+ import type { DashboardEntry } from 'alinea/core/DashboardEntry';
646
+ import type { Mutation } from 'alinea/core/db/Mutation';
647
+ import type { Resolver } from 'alinea/core/Resolver';
648
+ /** An entry that no longer exists after a commit */
649
+ export interface RemovedEntry {
650
+ id: string;
651
+ type: string;
652
+ locale: string | null;
653
+ /** The url the entry had before the commit */
654
+ url: string;
655
+ }
656
+ /**
657
+ * The latest version, in every locale, of the entries that mutations change
658
+ */
659
+ export function committedEntries(resolver: Resolver, mutations: ReadonlyArray<Mutation>): Promise<Array<DashboardEntry>>;
660
+ /** The entries of `before` that are not in `after` */
661
+ export function removedEntries(before: Array<DashboardEntry>, after: Array<DashboardEntry>): Array<RemovedEntry>;
662
+
663
+ }
664
+
630
665
  declare module 'alinea/backend/HandleAction' {
631
666
 
632
667
  export enum HandleAction {
@@ -653,6 +688,8 @@ declare module 'alinea/backend/Handler' {
653
688
  import type { CMS } from 'alinea/core/CMS';
654
689
  import type { AuthedContext, RemoteConnection, RequestContext } from 'alinea/core/Connection';
655
690
  import type { LocalStore } from 'alinea/core/db/LocalStore';
691
+ import type { DashboardEntry } from 'alinea/core/DashboardEntry';
692
+ import { type RemovedEntry } from 'alinea/backend/CommitEntries';
656
693
  import type { Mutation } from 'alinea/core/db/Mutation';
657
694
  import type { User } from 'alinea/core/User';
658
695
  export interface Handler {
@@ -666,6 +703,13 @@ declare module 'alinea/backend/Handler' {
666
703
  }
667
704
  export interface AfterCommitContext extends BeforeCommitContext {
668
705
  sha: string;
706
+ /**
707
+ * The entries the mutations changed as they are after the commit: the
708
+ * latest version in each locale
709
+ */
710
+ entries: Array<DashboardEntry>;
711
+ /** Entries that no longer exist after the commit, with the url they had */
712
+ removed: Array<RemovedEntry>;
669
713
  }
670
714
  export interface HandlerHooks {
671
715
  beforeCommit?(context: BeforeCommitContext): HookResponse<ReadonlyArray<Mutation>>;
@@ -969,6 +1013,12 @@ declare module 'alinea/backend/util/ORM' {
969
1013
 
970
1014
  declare module 'alinea/backend/util/Syncable' {
971
1015
 
1016
+ /**
1017
+ * How long a read waits for a sync with the remote. A slower or hung remote
1018
+ * must not hold up every read: past this the read answers with the content
1019
+ * it has, while the sync carries on.
1020
+ */
1021
+ export const syncWait = 10000;
972
1022
  export function createThrottledSync(): (sync: () => Promise<unknown>, interval?: number) => Promise<void>;
973
1023
 
974
1024
  }
@@ -996,7 +1046,11 @@ declare module 'alinea/cli/build/BuildEmitter' {
996
1046
  type: 'done';
997
1047
  result: BuildResult;
998
1048
  };
999
- export function buildEmitter(config: BuildOptions): Emitter<BuildInfo>;
1049
+ export interface BuildEmitterOptions {
1050
+ /** Rebuild on changes, otherwise build once and stop esbuild */
1051
+ watch?: boolean;
1052
+ }
1053
+ export function buildEmitter(config: BuildOptions, { watch }?: BuildEmitterOptions): Emitter<BuildInfo>;
1000
1054
 
1001
1055
  }
1002
1056
 
@@ -1083,7 +1137,10 @@ declare module 'alinea/cli/generate/DevDB' {
1083
1137
  */
1084
1138
  reconfigure(options: Pick<DevDBOptions, 'config' | 'configFingerprint' | 'dashboardUrl'>): Promise<void>;
1085
1139
  sync(): Promise<string>;
1086
- finalize(): Promise<number>;
1140
+ /** Compact the database, rewriting it in full for a build. */
1141
+ finalize(options?: {
1142
+ rewrite?: boolean;
1143
+ }): Promise<number>;
1087
1144
  fix(): Promise<void>;
1088
1145
  watchFiles(): Promise<WatchFiles>;
1089
1146
  isInMediaLocation(file: string): boolean;
@@ -1175,6 +1232,29 @@ declare module 'alinea/cli/generate/LoadConfig' {
1175
1232
 
1176
1233
  }
1177
1234
 
1235
+ declare module 'alinea/cli/generate/MediaLocations' {
1236
+
1237
+ import { type EntryStatus } from 'alinea/core/Entry';
1238
+ import type { EntryStore } from 'alinea/database/EntryStore';
1239
+ export interface RemoteLocation {
1240
+ id: string;
1241
+ locale: string | null;
1242
+ status: EntryStatus;
1243
+ workspace: string;
1244
+ /** The content file of the media entry */
1245
+ file: string;
1246
+ location: string;
1247
+ /** The location of the one file with the same name in the mediaDir */
1248
+ local?: string;
1249
+ }
1250
+ /**
1251
+ * Media entries whose location is a url. Only the 1.x dashboard wrote those
1252
+ * (upload urls belong in previewUrl), they can't be served from the site.
1253
+ */
1254
+ export function remoteLocations(rootDir: string, db: EntryStore): Promise<Array<RemoteLocation>>;
1255
+
1256
+ }
1257
+
1178
1258
  declare module 'alinea/cli/Init' {
1179
1259
 
1180
1260
  export interface InitOptions {
@@ -1916,14 +1996,16 @@ declare module 'alinea/cms' {
1916
1996
  * @module
1917
1997
  */
1918
1998
  export { useField, useFieldError, useFieldKey, useFieldOptions, useFieldSetter, useFieldValue, useSiblingFieldValue } from 'alinea/dashboard/cms/hooks';
1919
- export { type DashboardLocation, useEntry, useGraph, useLocale, useNavigate, usePolicy, usePreviewMetadata, useUser } from 'alinea/dashboard/cms/hooks';
1999
+ export { useEntry, useGraph, useLocale, useNavigate, usePolicy, usePreviewMetadata, useUser } from 'alinea/dashboard/cms/hooks';
2000
+ export type { DashboardEntry, EntryActionProps } from 'alinea/core/DashboardEntry';
2001
+ export type { DashboardLocation } from 'alinea/dashboard/Href';
1920
2002
  export type { Policy } from 'alinea/core/Role';
1921
2003
  export type { PreviewMetadata } from 'alinea/core/Preview';
1922
2004
  export type { User } from 'alinea/core/User';
1923
2005
  export { EditField, type EditFieldProps, EditFields, type EditFieldsProps } from 'alinea/dashboard/app/EntryFields';
1924
2006
  export { FieldChrome, type FieldChromeProps } from 'alinea/dashboard/cms/FieldChrome';
1925
2007
  export { EntryTable, type EntryTableProps } from 'alinea/dashboard/cms/EntryTable';
1926
- export type { OverviewActionProps, OverviewCellProps, OverviewEntry } from 'alinea/core/Overview';
2008
+ export type { OverviewActionProps, OverviewCellProps, OverviewEntry, OverviewHeaderProps } from 'alinea/core/Overview';
1927
2009
  export type { FieldViewProps, RootViewProps, SectionViewProps, TypeViewProps } from 'alinea/dashboard/cms/ViewProps';
1928
2010
 
1929
2011
  }
@@ -1932,6 +2014,7 @@ declare module 'alinea/components' {
1932
2014
 
1933
2015
  export * from 'alinea/components/Alert';
1934
2016
  export * from 'alinea/components/AppShell';
2017
+ export * from 'alinea/components/AsyncList';
1935
2018
  export * from 'alinea/components/Badge';
1936
2019
  export * from 'alinea/components/Blockquote';
1937
2020
  export * from 'alinea/components/Breadcrumbs';
@@ -1958,6 +2041,7 @@ declare module 'alinea/components' {
1958
2041
  export * from 'alinea/components/Field';
1959
2042
  export * from 'alinea/components/FileTrigger';
1960
2043
  export * from 'alinea/components/FoldIcon';
2044
+ export * from 'alinea/components/Form';
1961
2045
  export * from 'alinea/components/Heading';
1962
2046
  export * from 'alinea/components/Icon';
1963
2047
  export * from 'alinea/components/Kbd';
@@ -1978,6 +2062,7 @@ declare module 'alinea/components' {
1978
2062
  export * from 'alinea/components/Sidebar';
1979
2063
  export * from 'alinea/components/SortableList';
1980
2064
  export * from 'alinea/components/Spinner';
2065
+ export * from 'alinea/components/Stack';
1981
2066
  export * from 'alinea/components/Surface';
1982
2067
  export * from 'alinea/components/Switch';
1983
2068
  export * from 'alinea/components/Table';
@@ -2050,6 +2135,63 @@ declare module 'alinea/components/AppShell' {
2050
2135
 
2051
2136
  }
2052
2137
 
2138
+ declare module 'alinea/components/AsyncList' {
2139
+
2140
+ import type { Key, Selection, SortDescriptor } from 'alinea/components/types';
2141
+ export interface AsyncListLoadOptions<C> {
2142
+ /** Aborted when a newer load replaces this one */
2143
+ signal: AbortSignal;
2144
+ /** The cursor returned by the previous page, set when loading more */
2145
+ cursor?: C;
2146
+ filterText: string;
2147
+ sort?: SortDescriptor;
2148
+ }
2149
+ export interface AsyncListPage<T, C> {
2150
+ items: Iterable<T>;
2151
+ /** Where the next page starts, leave out when there are no more items */
2152
+ cursor?: C | null;
2153
+ }
2154
+ export interface AsyncListOptions<T, C> {
2155
+ load(options: AsyncListLoadOptions<C>): Promise<AsyncListPage<T, C>>;
2156
+ initialSort?: SortDescriptor;
2157
+ initialFilterText?: string;
2158
+ initialSelectedKeys?: Iterable<Key>;
2159
+ /** The key of an item, defaults to its `id` or `key` property */
2160
+ getKey?: (item: T) => Key;
2161
+ }
2162
+ /**
2163
+ * Items loaded page by page. Its fields plug into Table (`items`,
2164
+ * `sortDescriptor`/`onSortChange`, `loading`, `onLoadMore`, `selectedKeys`/
2165
+ * `onSelectionChange`) and ComboBox (`inputValue`/`onInputValueChange`,
2166
+ * `loading`).
2167
+ */
2168
+ export interface AsyncList<T> {
2169
+ items: Array<T>;
2170
+ /** True while loading the first page, the next page, sorting or filtering */
2171
+ loading: boolean;
2172
+ /** The error the last load failed with */
2173
+ error?: Error;
2174
+ /** Whether the last page returned a cursor */
2175
+ hasMore: boolean;
2176
+ loadMore(): void;
2177
+ reload(): void;
2178
+ /** Replaces an item, eg. after saving it, without loading the list again */
2179
+ update(key: Key, item: T | ((previous: T) => T)): void;
2180
+ remove(...keys: Array<Key>): void;
2181
+ prepend(...items: Array<T>): void;
2182
+ append(...items: Array<T>): void;
2183
+ sortDescriptor?: SortDescriptor;
2184
+ sort(descriptor: SortDescriptor): void;
2185
+ filterText: string;
2186
+ setFilterText(text: string): void;
2187
+ selectedKeys: Selection;
2188
+ setSelectedKeys(keys: Selection): void;
2189
+ }
2190
+ /** Loads a list of items, with sorting, filtering and paging */
2191
+ export function useAsyncList<T, C = string>({ load, initialSort, initialFilterText, initialSelectedKeys, getKey }: AsyncListOptions<T, C>): AsyncList<T>;
2192
+
2193
+ }
2194
+
2053
2195
  declare module 'alinea/components/Badge' {
2054
2196
 
2055
2197
  import type { ReactElement, ReactNode } from 'react';
@@ -2323,13 +2465,14 @@ declare module 'alinea/components/ColorSwatchPicker' {
2323
2465
 
2324
2466
  declare module 'alinea/components/ComboBox' {
2325
2467
 
2326
- import { type ReactNode } from 'react';
2327
- import type { AriaProps, DataProps, FieldSharedProps, OpenStateProps, StyleProps } from 'alinea/components/types';
2328
- export interface ComboBoxProps extends FieldSharedProps, OpenStateProps, StyleProps, AriaProps, DataProps {
2329
- value?: string | null;
2330
- defaultValue?: string | null;
2468
+ import { type ReactElement, type ReactNode } from 'react';
2469
+ import type { AriaProps, DataProps, FieldSharedProps, IconType, Key, OpenStateProps, StyleProps } from 'alinea/components/types';
2470
+ export interface ComboBoxProps<V extends Key = Key> extends FieldSharedProps, OpenStateProps, StyleProps, AriaProps, DataProps {
2471
+ /** The value of the selected ComboBoxItem */
2472
+ value?: V | null;
2473
+ defaultValue?: V | null;
2331
2474
  /** Called with the selected value, or null when the selection is cleared */
2332
- onValueChange?: (value: string | null) => void;
2475
+ onValueChange?: (value: V | null) => void;
2333
2476
  inputValue?: string;
2334
2477
  defaultInputValue?: string;
2335
2478
  onInputValueChange?: (value: string) => void;
@@ -2338,22 +2481,39 @@ declare module 'alinea/components/ComboBox' {
2338
2481
  allowsCustomValue?: boolean;
2339
2482
  /** Shown in the list when no item matches, the list stays closed if unset */
2340
2483
  emptyMessage?: ReactNode;
2484
+ /**
2485
+ * Whether an item is shown for the typed text, defaults to a case and
2486
+ * accent insensitive match anywhere in its text. Pass false to show every
2487
+ * item, eg. when the server already filtered them.
2488
+ */
2489
+ filter?: false | ((textValue: string, inputValue: string) => boolean);
2490
+ /** Shows a spinner, eg. while the items for the typed text load */
2491
+ loading?: boolean;
2341
2492
  /** Name of the hidden form input carrying the value */
2342
2493
  name?: string;
2343
2494
  autoFocus?: boolean;
2344
- /** ComboBoxItem elements */
2495
+ /** ComboBoxItem and ComboBoxGroup elements */
2345
2496
  children: ReactNode;
2346
2497
  }
2347
2498
  /** A labelled text input that filters a list of options to pick from */
2348
- export function ComboBox({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, inputValue, defaultInputValue, onInputValueChange, open, defaultOpen, onOpenChange, placeholder, emptyMessage, autoFocus, className, style, children, ...props }: ComboBoxProps): import("react").JSX.Element;
2499
+ export function ComboBox<V extends Key = Key>({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, inputValue, defaultInputValue, onInputValueChange, open, defaultOpen, onOpenChange, placeholder, emptyMessage, filter, loading, autoFocus, className, style, children, ...props }: ComboBoxProps<V>): import("react").JSX.Element;
2349
2500
  export interface ComboBoxItemProps extends StyleProps {
2350
- value: string;
2501
+ value: Key;
2351
2502
  /** Text used for filtering and the input, defaults to string children */
2352
2503
  textValue?: string;
2353
2504
  disabled?: boolean;
2505
+ /** Icon shown before the label in the list */
2506
+ icon?: IconType | ReactElement;
2507
+ /** Secondary text shown below the label in the list */
2508
+ description?: ReactNode;
2354
2509
  children: ReactNode;
2355
2510
  }
2356
2511
  export function ComboBoxItem(props: ComboBoxItemProps): import("react").JSX.Element;
2512
+ export interface ComboBoxGroupProps extends AriaProps {
2513
+ label?: ReactNode;
2514
+ children: ReactNode;
2515
+ }
2516
+ export function ComboBoxGroup(props: ComboBoxGroupProps): import("react").JSX.Element;
2357
2517
 
2358
2518
  }
2359
2519
 
@@ -2574,6 +2734,10 @@ declare module 'alinea/components/DateField' {
2574
2734
  min?: string;
2575
2735
  /** The latest valid date, `YYYY-MM-DD` */
2576
2736
  max?: string;
2737
+ /**
2738
+ * Name of the hidden form input, which submits the date like a native
2739
+ * date input: `YYYY-MM-DD`, or an empty string when no date is set
2740
+ */
2577
2741
  name?: string;
2578
2742
  autoFocus?: boolean;
2579
2743
  }
@@ -2633,12 +2797,15 @@ declare module 'alinea/components/Dialog' {
2633
2797
 
2634
2798
  import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
2635
2799
  import { type ButtonProps } from 'alinea/components/Button';
2800
+ import { type FormProps } from 'alinea/components/Form';
2636
2801
  import { type TriggerProps } from 'alinea/components/internal/Trigger';
2637
- import type { AriaProps, DataProps, OpenStateProps, StyleProps } from 'alinea/components/types';
2802
+ import type { AriaProps, DataProps, OpenStateProps, StyleProps, ValidationErrors } from 'alinea/components/types';
2638
2803
  export interface DialogProps extends OpenStateProps {
2804
+ /** Called when the dialog closes, after `onOpenChange(false)` */
2805
+ onClose?: () => void;
2639
2806
  children: ReactNode;
2640
2807
  }
2641
- export function Dialog({ open, defaultOpen, onOpenChange, children }: DialogProps): import("react").JSX.Element;
2808
+ export function Dialog({ open, defaultOpen, onOpenChange, onClose, children }: DialogProps): import("react").JSX.Element;
2642
2809
  export interface DialogTriggerProps extends TriggerProps {
2643
2810
  }
2644
2811
  export function DialogTrigger(props: DialogTriggerProps): import("react").JSX.Element;
@@ -2654,9 +2821,17 @@ declare module 'alinea/components/Dialog' {
2654
2821
  dismissable?: boolean;
2655
2822
  /** Defaults to true */
2656
2823
  showCloseButton?: boolean;
2824
+ /**
2825
+ * Renders the content as a Form, submitted by a `type="submit"` Button.
2826
+ * The dialog closes once this resolves without returning errors, which
2827
+ * are shown on the fields otherwise.
2828
+ */
2829
+ onSubmit?: FormProps['onSubmit'];
2830
+ /** Errors keyed by field name, when the content is a form */
2831
+ validationErrors?: ValidationErrors;
2657
2832
  children: ReactNode;
2658
2833
  }
2659
- export function DialogContent({ role, size, dismissable, showCloseButton, className, style, children, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby, ...props }: DialogContentProps): import("react").JSX.Element;
2834
+ export function DialogContent({ role, size, dismissable, showCloseButton, onSubmit, validationErrors, className, style, children, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby, ...props }: DialogContentProps): import("react").JSX.Element;
2660
2835
  export interface DialogHeaderProps extends ComponentPropsWithoutRef<'div'> {
2661
2836
  }
2662
2837
  export function DialogHeader({ className, ...props }: DialogHeaderProps): import("react").JSX.Element;
@@ -2854,7 +3029,7 @@ declare module 'alinea/components/Empty' {
2854
3029
 
2855
3030
  declare module 'alinea/components/Field' {
2856
3031
 
2857
- import type { ReactNode } from 'react';
3032
+ import { type ReactNode } from 'react';
2858
3033
  import type { FieldSharedProps, StyleProps } from 'alinea/components/types';
2859
3034
  export interface FieldProps extends FieldSharedProps, StyleProps {
2860
3035
  /** Id of the control the label describes, not needed inside our inputs */
@@ -2928,6 +3103,34 @@ declare module 'alinea/components/FoldIcon' {
2928
3103
 
2929
3104
  }
2930
3105
 
3106
+ declare module 'alinea/components/Form' {
3107
+
3108
+ import { type ReactNode } from 'react';
3109
+ import type { AriaProps, DataProps, StyleProps, ValidationErrors } from 'alinea/components/types';
3110
+ export type FormSubmitResult = ValidationErrors | undefined | void;
3111
+ export interface FormProps extends StyleProps, AriaProps, DataProps {
3112
+ /**
3113
+ * Errors keyed by field name, eg. from the server. A field shows its
3114
+ * error until it is edited.
3115
+ */
3116
+ validationErrors?: ValidationErrors;
3117
+ /**
3118
+ * Called with the form data instead of submitting the form natively.
3119
+ * Errors it returns are shown on the fields, next to `validationErrors`.
3120
+ */
3121
+ onSubmit?: (data: FormData) => FormSubmitResult | Promise<FormSubmitResult>;
3122
+ children: ReactNode;
3123
+ }
3124
+ /**
3125
+ * A form whose fields show validation errors: native constraints such as
3126
+ * `required` once the form is submitted, and `validationErrors` or the
3127
+ * errors returned by `onSubmit` by field name. An `error` set on a field
3128
+ * itself takes precedence.
3129
+ */
3130
+ export function Form({ validationErrors, onSubmit, className, children, ...props }: FormProps): import("react").JSX.Element;
3131
+
3132
+ }
3133
+
2931
3134
  declare module 'alinea/components/Heading' {
2932
3135
 
2933
3136
  import { type ReactNode } from 'react';
@@ -3012,14 +3215,38 @@ declare module 'alinea/components/internal/DateValue' {
3012
3215
 
3013
3216
  }
3014
3217
 
3218
+ declare module 'alinea/components/internal/FormScope' {
3219
+
3220
+ /** Set inside a Form, where fields show the validation errors of react-aria */
3221
+ export const FormScope: import("react").Context<boolean>;
3222
+
3223
+ }
3224
+
3225
+ declare module 'alinea/components/internal/ListBoxGroup' {
3226
+
3227
+ import type { ReactNode } from 'react';
3228
+ import type { AriaProps } from 'alinea/components/types';
3229
+ export interface ListBoxGroupProps extends AriaProps {
3230
+ /** data-slot of the group, eg. "select-group" */
3231
+ slot: string;
3232
+ /** data-slot of its label */
3233
+ labelSlot: string;
3234
+ label?: ReactNode;
3235
+ children: ReactNode;
3236
+ }
3237
+ /** A labelled group of options in the listbox of a Select or ComboBox */
3238
+ export function ListBoxGroup({ slot, labelSlot, label, children, ...props }: ListBoxGroupProps): import("react").JSX.Element;
3239
+
3240
+ }
3241
+
3015
3242
  declare module 'alinea/components/internal/ListBoxOption' {
3016
3243
 
3017
3244
  import type { CSSProperties, ReactElement, ReactNode } from 'react';
3018
- import type { IconType } from 'alinea/components/types';
3245
+ import type { IconType, Key } from 'alinea/components/types';
3019
3246
  export interface ListBoxOptionProps {
3020
3247
  /** Prefix for the data-slot attributes, eg. "select-item" */
3021
3248
  slot: string;
3022
- value: string;
3249
+ value: Key;
3023
3250
  textValue?: string;
3024
3251
  disabled?: boolean;
3025
3252
  icon?: IconType | ReactElement;
@@ -3226,13 +3453,20 @@ declare module 'alinea/components/Kbd' {
3226
3453
 
3227
3454
  declare module 'alinea/components/Link' {
3228
3455
 
3229
- import type { MouseEvent, ReactNode, Ref } from 'react';
3456
+ import type { HTMLAttributeReferrerPolicy, MouseEvent, ReactNode, Ref } from 'react';
3230
3457
  import type { AriaProps, DataProps, StyleProps } from 'alinea/components/types';
3231
3458
  export interface LinkProps extends StyleProps, AriaProps, DataProps {
3232
3459
  href?: string;
3233
3460
  target?: string;
3234
3461
  rel?: string;
3235
3462
  download?: boolean | string;
3463
+ /** Tooltip text */
3464
+ title?: string;
3465
+ tabIndex?: number;
3466
+ lang?: string;
3467
+ hrefLang?: string;
3468
+ referrerPolicy?: HTMLAttributeReferrerPolicy;
3469
+ 'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time';
3236
3470
  /** `plain` underlines on hover, `underline` is always underlined */
3237
3471
  variant?: 'plain' | 'underline';
3238
3472
  disabled?: boolean;
@@ -3348,15 +3582,20 @@ declare module 'alinea/components/MediaPreview' {
3348
3582
  declare module 'alinea/components/MultipleSelect' {
3349
3583
 
3350
3584
  import { type ReactNode } from 'react';
3351
- import type { AriaProps, DataProps, FieldSharedProps, OpenStateProps, StyleProps } from 'alinea/components/types';
3352
- export interface MultipleSelectProps extends FieldSharedProps, OpenStateProps, StyleProps, AriaProps, DataProps {
3353
- value?: Array<string>;
3354
- defaultValue?: Array<string>;
3355
- onValueChange?: (value: Array<string>) => void;
3585
+ import type { AriaProps, DataProps, FieldSharedProps, Key, OpenStateProps, StyleProps } from 'alinea/components/types';
3586
+ export interface MultipleSelectProps<V extends Key = Key> extends FieldSharedProps, OpenStateProps, StyleProps, AriaProps, DataProps {
3587
+ /** The values of the selected MultipleSelectItems */
3588
+ value?: Array<V>;
3589
+ defaultValue?: Array<V>;
3590
+ onValueChange?: (value: Array<V>) => void;
3356
3591
  placeholder?: string;
3357
3592
  /** Shown in the list when no item matches the search, defaults to "No options" */
3358
3593
  emptyMessage?: ReactNode;
3359
- /** Name of the hidden form input carrying the values */
3594
+ /**
3595
+ * Name of the hidden form input carrying the values, it submits one entry
3596
+ * per selected value like a native multiple select: read them with
3597
+ * `formData.getAll(name)`
3598
+ */
3360
3599
  name?: string;
3361
3600
  /** MultipleSelectItem elements */
3362
3601
  children: ReactNode;
@@ -3365,9 +3604,9 @@ declare module 'alinea/components/MultipleSelect' {
3365
3604
  * A labelled field to pick several values from a searchable list, the
3366
3605
  * selected values are shown as removable tags.
3367
3606
  */
3368
- export function MultipleSelect({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, placeholder, emptyMessage, className, style, children, ...props }: MultipleSelectProps): import("react").JSX.Element;
3607
+ export function MultipleSelect<V extends Key = Key>({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, placeholder, emptyMessage, className, style, children, ...props }: MultipleSelectProps<V>): import("react").JSX.Element;
3369
3608
  export interface MultipleSelectItemProps extends StyleProps {
3370
- value: string;
3609
+ value: Key;
3371
3610
  /** Text used for searching and the tag, defaults to string children */
3372
3611
  textValue?: string;
3373
3612
  disabled?: boolean;
@@ -3435,6 +3674,10 @@ declare module 'alinea/components/NumberField' {
3435
3674
  min?: number;
3436
3675
  max?: number;
3437
3676
  step?: number;
3677
+ /**
3678
+ * How the number is shown and parsed. Digits are not grouped unless
3679
+ * `useGrouping` is set: number fields often hold years and ids
3680
+ */
3438
3681
  formatOptions?: Intl.NumberFormatOptions;
3439
3682
  placeholder?: string;
3440
3683
  /** Show increment and decrement buttons, defaults to true */
@@ -3445,7 +3688,7 @@ declare module 'alinea/components/NumberField' {
3445
3688
  onFocus?: (event: FocusEvent<HTMLInputElement>) => void;
3446
3689
  onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
3447
3690
  }
3448
- export function NumberField({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, min, max, placeholder, steppers, onBlur, onFocus, onKeyDown, className, ...props }: NumberFieldProps): import("react").JSX.Element;
3691
+ export function NumberField({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, min, max, formatOptions, placeholder, steppers, onBlur, onFocus, onKeyDown, className, ...props }: NumberFieldProps): import("react").JSX.Element;
3449
3692
 
3450
3693
  }
3451
3694
 
@@ -3741,13 +3984,16 @@ declare module 'alinea/components/SearchField' {
3741
3984
  declare module 'alinea/components/Select' {
3742
3985
 
3743
3986
  import { type ReactElement, type ReactNode } from 'react';
3744
- import type { AriaProps, DataProps, FieldSharedProps, IconType, OpenStateProps, StyleProps } from 'alinea/components/types';
3745
- export interface SelectProps extends FieldSharedProps, OpenStateProps, StyleProps, AriaProps, DataProps {
3746
- value?: string | null;
3747
- defaultValue?: string | null;
3987
+ import type { AriaProps, DataProps, FieldSharedProps, IconType, Key, OpenStateProps, StyleProps } from 'alinea/components/types';
3988
+ export interface SelectProps<V extends Key = Key> extends FieldSharedProps, OpenStateProps, StyleProps, AriaProps, DataProps {
3989
+ /** The value of the selected SelectItem */
3990
+ value?: V | null;
3991
+ defaultValue?: V | null;
3748
3992
  /** Called with the selected value, or null when the value is cleared */
3749
- onValueChange?: (value: string | null) => void;
3993
+ onValueChange?: (value: V | null) => void;
3750
3994
  placeholder?: string;
3995
+ /** Show a button that clears the value */
3996
+ clearable?: boolean;
3751
3997
  /** Name of the hidden form input carrying the value */
3752
3998
  name?: string;
3753
3999
  autoFocus?: boolean;
@@ -3757,12 +4003,12 @@ declare module 'alinea/components/Select' {
3757
4003
  children: ReactNode;
3758
4004
  }
3759
4005
  /**
3760
- * A labelled field to pick a single value from a list. The value can be
3761
- * cleared unless the field is required.
4006
+ * A labelled field to pick a single value from a list. Set `clearable` to
4007
+ * let the value be cleared again.
3762
4008
  */
3763
- export function Select({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, placeholder, size, className, style, children, ...props }: SelectProps): import("react").JSX.Element;
4009
+ export function Select<V extends Key = Key>({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, placeholder, clearable, size, className, style, children, ...props }: SelectProps<V>): import("react").JSX.Element;
3764
4010
  export interface SelectItemProps extends StyleProps {
3765
- value: string;
4011
+ value: Key;
3766
4012
  disabled?: boolean;
3767
4013
  icon?: IconType | ReactElement;
3768
4014
  /** Text used for typeahead and the value, defaults to string children */
@@ -3776,7 +4022,7 @@ declare module 'alinea/components/Select' {
3776
4022
  label?: ReactNode;
3777
4023
  children: ReactNode;
3778
4024
  }
3779
- export function SelectGroup({ label, children, ...props }: SelectGroupProps): import("react").JSX.Element;
4025
+ export function SelectGroup(props: SelectGroupProps): import("react").JSX.Element;
3780
4026
  export function SelectSeparator(): import("react").JSX.Element;
3781
4027
 
3782
4028
  }
@@ -3999,6 +4245,31 @@ declare module 'alinea/components/Spinner' {
3999
4245
 
4000
4246
  }
4001
4247
 
4248
+ declare module 'alinea/components/Stack' {
4249
+
4250
+ import type { ReactNode, Ref } from 'react';
4251
+ import type { AriaProps, DataProps, StyleProps } from 'alinea/components/types';
4252
+ export interface StackProps extends StyleProps, AriaProps, DataProps {
4253
+ /** Defaults to `column` */
4254
+ direction?: 'row' | 'column';
4255
+ /** Space between the items, a number is in pixels */
4256
+ gap?: number | string;
4257
+ /** Cross axis alignment of the items, they stretch by default */
4258
+ align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline';
4259
+ /** Main axis distribution of the items */
4260
+ justify?: 'start' | 'center' | 'end' | 'between';
4261
+ /** Wrap items onto multiple lines */
4262
+ wrap?: boolean;
4263
+ /** Lay out the single child element instead of rendering a div */
4264
+ asChild?: boolean;
4265
+ ref?: Ref<HTMLDivElement>;
4266
+ children?: ReactNode;
4267
+ }
4268
+ /** Lays out its children in a row or column with a gap between them */
4269
+ export function Stack({ direction, gap, align, justify, wrap, asChild, className, style, ...props }: StackProps): import("react").JSX.Element;
4270
+
4271
+ }
4272
+
4002
4273
  declare module 'alinea/components/Surface' {
4003
4274
 
4004
4275
  import type { ComponentPropsWithoutRef } from 'react';
@@ -4086,6 +4357,16 @@ declare module 'alinea/components/Table' {
4086
4357
  */
4087
4358
  onRowAction?: (key: Key) => void;
4088
4359
  renderEmptyState?: () => ReactNode;
4360
+ /**
4361
+ * Shows a spinner in place of the rows while there are none, below the
4362
+ * rows otherwise
4363
+ */
4364
+ loading?: boolean;
4365
+ /**
4366
+ * Called when the rows are scrolled close to the end, to load the next
4367
+ * page (see useAsyncList)
4368
+ */
4369
+ onLoadMore?: () => void;
4089
4370
  /**
4090
4371
  * Rows are cached per item, list the values `children` reads besides the
4091
4372
  * item to render them again when those change
@@ -4093,7 +4374,7 @@ declare module 'alinea/components/Table' {
4093
4374
  dependencies?: ReadonlyArray<unknown>;
4094
4375
  children: (item: T) => ReactElement;
4095
4376
  }
4096
- export function Table<T extends object>({ items, columns, showHeader, expandable, rowHeight, selectionMode, selectionBehavior, showSelectionControls, variant, selectedKeys, defaultSelectedKeys, onSelectionChange, disabledKeys, expandedKeys, defaultExpandedKeys, onExpandedChange, sortDescriptor, onSortChange, onRowAction, renderEmptyState, dependencies, getDragData, acceptedDragTypes, canDrop, onReorder, onMove, onDropItems, onDropFiles, renderDragPreview, className, style, children, ...props }: TableProps<T>): import("react").JSX.Element;
4377
+ export function Table<T extends object>({ items, columns, showHeader, expandable, rowHeight, selectionMode, selectionBehavior, showSelectionControls, variant, selectedKeys, defaultSelectedKeys, onSelectionChange, disabledKeys, expandedKeys, defaultExpandedKeys, onExpandedChange, sortDescriptor, onSortChange, onRowAction, renderEmptyState, loading, onLoadMore, dependencies, getDragData, acceptedDragTypes, canDrop, onReorder, onMove, onDropItems, onDropFiles, renderDragPreview, className, style, children, ...props }: TableProps<T>): import("react").JSX.Element;
4097
4378
  export interface TableRowProps extends DataProps {
4098
4379
  id: Key;
4099
4380
  /** Text used for typeahead and as the accessible row name */
@@ -4261,6 +4542,8 @@ declare module 'alinea/components/TextField' {
4261
4542
  multiline?: boolean;
4262
4543
  /** Minimum number of rows of a multiline field */
4263
4544
  rows?: number;
4545
+ /** Maximum number of rows a multiline field grows to before it scrolls */
4546
+ maxRows?: number;
4264
4547
  /** Icon displayed inside the input, before the text */
4265
4548
  startIcon?: IconType | ReactElement;
4266
4549
  /** Icon displayed inside the input, after the text */
@@ -4270,6 +4553,10 @@ declare module 'alinea/components/TextField' {
4270
4553
  autoComplete?: string;
4271
4554
  maxLength?: number;
4272
4555
  minLength?: number;
4556
+ /** Which virtual keyboard to show, eg. `numeric` for a code */
4557
+ inputMode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
4558
+ /** A regular expression the value must match to be valid */
4559
+ pattern?: string;
4273
4560
  onBlur?: (event: FocusEvent<TextFieldElement>) => void;
4274
4561
  onFocus?: (event: FocusEvent<TextFieldElement>) => void;
4275
4562
  onKeyDown?: (event: KeyboardEvent<TextFieldElement>) => void;
@@ -4277,7 +4564,7 @@ declare module 'alinea/components/TextField' {
4277
4564
  inputProps?: DataProps;
4278
4565
  ref?: Ref<HTMLDivElement>;
4279
4566
  }
4280
- export function TextField({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, placeholder, type, multiline, rows, startIcon, endIcon, autoComplete, maxLength, minLength, onBlur, onFocus, onKeyDown, inputProps, className, style, ...props }: TextFieldProps): import("react").JSX.Element;
4567
+ export function TextField({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, placeholder, type, multiline, rows, maxRows, startIcon, endIcon, autoComplete, maxLength, minLength, onBlur, onFocus, onKeyDown, inputProps, className, style, ...props }: TextFieldProps): import("react").JSX.Element;
4281
4568
  export {};
4282
4569
 
4283
4570
  }
@@ -4301,10 +4588,15 @@ declare module 'alinea/components/TimeField' {
4301
4588
  granularity?: 'minute' | 'second';
4302
4589
  /** Defaults to the locale's hour cycle */
4303
4590
  hourCycle?: 12 | 24;
4591
+ /**
4592
+ * Name of the hidden form input, which submits the time like a native
4593
+ * time input: `HH:mm`, or `HH:mm:ss` when granularity is `second`, and an
4594
+ * empty string when no time is set
4595
+ */
4304
4596
  name?: string;
4305
4597
  autoFocus?: boolean;
4306
4598
  }
4307
- export function TimeField({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, min, max, granularity, className, locale, ...props }: TimeFieldProps): import("react").JSX.Element;
4599
+ export function TimeField({ label, description, error, required, disabled, readOnly, icon, shared, value, defaultValue, onValueChange, min, max, granularity, name, className, locale, ...props }: TimeFieldProps): import("react").JSX.Element;
4308
4600
 
4309
4601
  }
4310
4602
 
@@ -4617,6 +4909,10 @@ declare module 'alinea/components/types' {
4617
4909
  renderDragPreview?: (items: Array<Record<string, string>>) => ReactNode;
4618
4910
  }
4619
4911
  export type ContentStatus = 'published' | 'draft' | 'unpublished' | 'archived' | 'untranslated';
4912
+ /** Error messages keyed by the `name` of a form field */
4913
+ export interface ValidationErrors {
4914
+ [name: string]: string | Array<string>;
4915
+ }
4620
4916
 
4621
4917
  }
4622
4918
 
@@ -4806,6 +5102,12 @@ declare module 'alinea/core/Config' {
4806
5102
  export namespace Config {
4807
5103
  function baseUrl(config: Config, env?: string): string | undefined;
4808
5104
  function handlerUrl(config: Config): string;
5105
+ /**
5106
+ * The handler as reached from server renders. There is no request to take
5107
+ * an origin from in static renders, and reading one would make every route
5108
+ * dynamic, so a relative handlerUrl needs a baseUrl.
5109
+ */
5110
+ function serverHandlerUrl(config: Config, env?: string): URL;
4809
5111
  function adminPath(config: Config): string;
4810
5112
  function filePathname(config: Config, file: string): string;
4811
5113
  function dashboardFile(config: Config): string;
@@ -4960,6 +5262,30 @@ declare module 'alinea/core/Connection' {
4960
5262
 
4961
5263
  }
4962
5264
 
5265
+ declare module 'alinea/core/DashboardEntry' {
5266
+
5267
+ import type { OverviewEntry } from 'alinea/core/Overview';
5268
+ /** The entry a dashboard view renders for, eg. the entry being edited */
5269
+ export interface DashboardEntry<Data = Record<string, unknown>> extends OverviewEntry {
5270
+ /**
5271
+ * The field values of the selected version as saved. Unsaved edits are not
5272
+ * included: read the live value of a field with `useFieldValue(field)`.
5273
+ */
5274
+ data: Data;
5275
+ }
5276
+ /** Props of an entry `actions` view, rendered in the entry header */
5277
+ export interface EntryActionProps {
5278
+ /** The entry being edited, in the selected version */
5279
+ entry: DashboardEntry;
5280
+ /**
5281
+ * The locale the entry is edited in, which differs from `entry.locale`
5282
+ * while a translation is not saved yet
5283
+ */
5284
+ locale: string | null;
5285
+ }
5286
+
5287
+ }
5288
+
4963
5289
  declare module 'alinea/core/db/CommitRequest' {
4964
5290
 
4965
5291
  import type { Change, ChangeFile, ChangesBatch } from 'alinea/core/source/Change';
@@ -5115,6 +5441,13 @@ declare module 'alinea/core/db/LocalStore' {
5115
5441
  export interface SyncOptions {
5116
5442
  /** Skip entry validation for a remote whose entries were already validated. */
5117
5443
  validate?: boolean;
5444
+ /** Called as files arrive from the remote, once there are files to fetch. */
5445
+ onProgress?(progress: SyncProgress): void;
5446
+ }
5447
+ /** Files fetched from the remote during a sync. */
5448
+ export interface SyncProgress {
5449
+ loaded: number;
5450
+ total: number;
5118
5451
  }
5119
5452
  /**
5120
5453
  * A locally available entry store: a writeable graph backed by a source that
@@ -5422,13 +5755,13 @@ declare module 'alinea/core/Document' {
5422
5755
  path: PathField;
5423
5756
  metadata: MetadataField;
5424
5757
  };
5425
- export type DocumentConfig<Fields, Seo, Details> = (TypeConfig<Fields> | ContainerTypeConfig<Fields>) & {
5758
+ export type DocumentConfig<Fields, Seo, Details, Settings = {}> = (TypeConfig<Fields, Settings> | ContainerTypeConfig<Fields, Settings>) & {
5426
5759
  /** Fields shown in the SEO tab, above the previews */
5427
5760
  seo?: Seo;
5428
5761
  /** Fields shown in the Details tab, above the created and updated details */
5429
5762
  details?: Details;
5430
5763
  };
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>;
5764
+ export function document<Fields extends FieldsDefinition = {}, Seo extends FieldsDefinition = {}, Details extends FieldsDefinition = {}, Settings extends FieldsDefinition = {}>(label: string, { fields, seo, details, settings, ...config }: DocumentConfig<Fields, Seo, Details, Settings>): Type<Document & Fields & Seo & Details & Settings>;
5432
5765
  /** Whether `type` was created with `Config.document` */
5433
5766
  export function isDocument(type: Type): boolean;
5434
5767
 
@@ -5533,6 +5866,7 @@ declare module 'alinea/core/Entry' {
5533
5866
  declare module 'alinea/core/EntryFields' {
5534
5867
 
5535
5868
  import type { EntryStatus } from 'alinea/core/Entry';
5869
+ import type { Related } from 'alinea/core/Filter';
5536
5870
  export interface EntryFields {
5537
5871
  _id: string;
5538
5872
  _type: string;
@@ -5550,8 +5884,13 @@ declare module 'alinea/core/EntryFields' {
5550
5884
  }
5551
5885
  /** The fields filters can match, next to those of the entry */
5552
5886
  export interface EntryFilterFields extends EntryFields {
5553
- /** Whether another entry links to the entry */
5554
- _referenced: boolean;
5887
+ /**
5888
+ * Whether another entry links to the entry, or an entry that matches a
5889
+ * query, eg. `{_referenced: Query.referencedBy({type: Product})}`
5890
+ */
5891
+ _referenced: Related<{
5892
+ edge: 'referencedBy';
5893
+ }>;
5555
5894
  }
5556
5895
  export const EntryFields: {
5557
5896
  _id: import("alinea/core/Expr").Expr<string>;
@@ -5722,6 +6061,11 @@ declare module 'alinea/core/Field' {
5722
6061
  required?: boolean;
5723
6062
  /** Validate the given value */
5724
6063
  validate?(value: StoredValue): boolean | string | undefined;
6064
+ /**
6065
+ * The width of the field in the entry form, a fraction between 0 and 1.
6066
+ * Fields that fit next to each other share a row.
6067
+ */
6068
+ width?: number;
5725
6069
  }
5726
6070
  export interface EntryAnchorTarget {
5727
6071
  id: string;
@@ -5825,8 +6169,7 @@ declare module 'alinea/core/field/CreateField' {
5825
6169
  import type { Field, FieldOptions, WithoutLabel } from 'alinea/core/Field';
5826
6170
  import type { Expand } from 'alinea/core/util/Types';
5827
6171
  import type { View } from 'alinea/core/View';
5828
- export interface Create<Value, Options = object> extends Field<Value, Value, (value: Value) => void, FieldOptions<Value> & Options> {
5829
- }
6172
+ export type Create<Value, Options = object> = Field<Value, Value, (value: Value) => void, FieldOptions<Value> & Options>;
5830
6173
  export type Options<F> = Expand<F extends Field<any, any, any, infer Options> ? WithoutLabel<Options & FieldOptions<any>> : WithoutLabel<FieldOptions<any>>>;
5831
6174
  export interface CreateConfig<Value, Options> {
5832
6175
  label: string;
@@ -5980,12 +6323,24 @@ declare module 'alinea/core/Filter' {
5980
6323
  interface ArrayOps<Fields> {
5981
6324
  includes?: Filter<Fields>;
5982
6325
  }
6326
+ const related: unique symbol;
6327
+ /**
6328
+ * A field matched by a query of related entries, eg. `_referenced`. Its
6329
+ * condition holds when the query finds an entry.
6330
+ */
6331
+ export interface Related<Query> {
6332
+ [related]: Query;
6333
+ }
6334
+ /** A query of related entries used as a condition */
6335
+ export interface RelationCondition {
6336
+ edge: string;
6337
+ }
5983
6338
  type FieldOps<Fields> = {
5984
6339
  [K in keyof Fields]?: Condition<Fields[K]>;
5985
6340
  };
5986
- export type Condition<Value> = [Value] extends [Primitive] ? Ops<Value> | Value : [Value] extends [Array<any>] ? ArrayOps<Value[0]> : ObjectOps<Value>;
6341
+ export type Condition<Value> = [Value] extends [Related<infer Query>] ? Condition<boolean> | Query : [Value] extends [Primitive] ? Ops<Value> | Value : [Value] extends [Array<any>] ? ArrayOps<Value[0]> : ObjectOps<Value>;
5987
6342
  /** A condition on a field whose type is not known */
5988
- export type AnyCondition = Primitive | Ops<unknown> | ArrayOps<unknown> | ObjectOps<unknown>;
6343
+ export type AnyCondition = Primitive | Ops<unknown> | ArrayOps<unknown> | ObjectOps<unknown> | RelationCondition;
5989
6344
  /**
5990
6345
  * A filter on known fields that also accepts conditions on any other field,
5991
6346
  * for example the fields of the entry types an entry picker can select
@@ -6038,6 +6393,11 @@ declare module 'alinea/core/Filter' {
6038
6393
  } | {
6039
6394
  op: 'includes';
6040
6395
  item: ConditionNode;
6396
+ }
6397
+ /** Holds when the query of related entries finds an entry */
6398
+ | {
6399
+ op: 'relation';
6400
+ query: RelationCondition;
6041
6401
  };
6042
6402
  export function parseFilter(filter: unknown): FilterNode;
6043
6403
  export function parseCondition(condition: unknown): ConditionNode;
@@ -6098,7 +6458,15 @@ declare module 'alinea/core/Graph' {
6098
6458
  edge: 'entrySingle';
6099
6459
  field: HasField & Expr;
6100
6460
  }
6101
- export type Edge = EdgeTranslations | EdgeChildren | EdgeParents | EdgeSiblings | EdgeParent | EdgeNext | EdgePrevious | EdgeEntries | EdgeEntry;
6461
+ /**
6462
+ * Entries that link to the entry, through any field or through the given
6463
+ * link field of their type
6464
+ */
6465
+ export interface EdgeReferencedBy {
6466
+ edge: 'referencedBy';
6467
+ field?: HasField & Expr;
6468
+ }
6469
+ export type Edge = EdgeTranslations | EdgeChildren | EdgeParents | EdgeSiblings | EdgeParent | EdgeNext | EdgePrevious | EdgeEntries | EdgeEntry | EdgeReferencedBy;
6102
6470
  /** Options that change a query result into a single entry or a count */
6103
6471
  export interface QueryModifiers {
6104
6472
  first?: true;
@@ -6247,7 +6615,7 @@ declare module 'alinea/core/Graph' {
6247
6615
  get<Selection extends SelectionGuard = undefined, const Type extends TypeGuard = undefined, Include extends IncludeGuard = undefined>(query: GraphQuery<Selection, Type, Include>): Promise<QueryResult<Selection, Type, Include>>;
6248
6616
  count<Selection extends SelectionGuard = undefined, const Type extends TypeGuard = undefined, Include extends IncludeGuard = undefined>(query: GraphQuery<Selection, Type, Include>): Promise<number>;
6249
6617
  }
6250
- export function querySource(query: unknown): "children" | "entryMultiple" | "entrySingle" | "next" | "parent" | "parents" | "previous" | "siblings" | "translations" | undefined;
6618
+ export function querySource(query: unknown): "children" | "entryMultiple" | "entrySingle" | "next" | "parent" | "parents" | "previous" | "referencedBy" | "siblings" | "translations" | undefined;
6251
6619
  export {};
6252
6620
 
6253
6621
  }
@@ -6834,7 +7202,7 @@ declare module 'alinea/core/Overview' {
6834
7202
  import type { EntryFilterFields } from 'alinea/core/EntryFields';
6835
7203
  import { Expr } from 'alinea/core/Expr';
6836
7204
  import type { OpenFilter } from 'alinea/core/Filter';
6837
- import type { EdgeQuery, GraphQuery, InferProjection, Order, Projection } from 'alinea/core/Graph';
7205
+ import type { EdgeQuery, Graph, GraphQuery, InferProjection, Order, Projection } from 'alinea/core/Graph';
6838
7206
  import type { Type } from 'alinea/core/Type';
6839
7207
  import type { View } from 'alinea/core/View';
6840
7208
  /** Horizontal alignment of a column's header and cells */
@@ -6844,9 +7212,10 @@ declare module 'alinea/core/Overview' {
6844
7212
  /** A fixed width in pixels or a fraction of the remaining space */
6845
7213
  export type OverviewColumnWidth = number | `${number}fr`;
6846
7214
  /**
6847
- * A value to sort entries by: an expression such as a field, or a query of a
7215
+ * A value to sort entries by: an expression such as a field, a query of a
6848
7216
  * linked entry that selects a single expression, eg.
6849
- * `Product.brand.first({select: Entry.title})`
7217
+ * `Product.brand.first({select: Entry.title})`, or a count of related
7218
+ * entries, eg. `Query.referencedBy({type: Product, count: true})`
6850
7219
  */
6851
7220
  export type OverviewSortValue = Expr<any> | EdgeQuery<Expr<any>>;
6852
7221
  /** One projection per entry type, keyed by the type names of the schema */
@@ -6931,12 +7300,39 @@ declare module 'alinea/core/Overview' {
6931
7300
  /** The condition entries match when the option is picked */
6932
7301
  filter: OpenFilter<EntryFilterFields>;
6933
7302
  }
7303
+ /** The choices of an overview filter, keyed by a name unique within it */
7304
+ export interface OverviewFilterOptions {
7305
+ [key: string]: OverviewFilterOption;
7306
+ }
7307
+ /** What the options of an overview filter can be loaded with */
7308
+ export interface OverviewFilterContext {
7309
+ /** Query the content, eg. to list an option per entry of a type */
7310
+ graph: Graph;
7311
+ /** The locale the overview lists, null for unlocalised roots */
7312
+ locale: string | null;
7313
+ }
6934
7314
  /** A filter editors can apply in the "Filter and sort" menu of an overview */
6935
7315
  export interface OverviewFilter {
6936
7316
  /** The heading of the options in the menu, eg. `File type` */
6937
7317
  label: string;
6938
- /** The choices, keyed by a name that is unique within the filter */
6939
- options: Record<string, OverviewFilterOption>;
7318
+ /**
7319
+ * The choices, keyed by a name that is unique within the filter, or a
7320
+ * function that loads them, eg. an option per category:
7321
+ *
7322
+ * ```ts
7323
+ * async options({graph}) {
7324
+ * const categories = await graph.find({
7325
+ * type: Category,
7326
+ * select: {id: Entry.id, title: Entry.title}
7327
+ * })
7328
+ * return Object.fromEntries(categories.map(category => [category.id, {
7329
+ * label: category.title,
7330
+ * filter: {category: {has: {_entry: category.id}}}
7331
+ * }]))
7332
+ * }
7333
+ * ```
7334
+ */
7335
+ options: OverviewFilterOptions | ((context: OverviewFilterContext) => OverviewFilterOptions | Promise<OverviewFilterOptions>);
6940
7336
  /**
6941
7337
  * Editors can pick several options, entries then match any of them.
6942
7338
  * Defaults to false: picking an option replaces the picked one.
@@ -6960,6 +7356,36 @@ declare module 'alinea/core/Overview' {
6960
7356
  */
6961
7357
  query: GraphQuery<undefined, Type | Array<Type> | undefined, undefined>;
6962
7358
  }
7359
+ /** A tab above the listed entries, each lists the entries of its filter */
7360
+ export interface OverviewTab {
7361
+ /** Shown on the tab, eg. `Active` */
7362
+ label: string;
7363
+ /**
7364
+ * The condition entries match when the tab is selected, like the options of
7365
+ * a filter. A tab without a filter lists every child.
7366
+ */
7367
+ filter?: OpenFilter<EntryFilterFields>;
7368
+ }
7369
+ /** Props of an `overview.header` view */
7370
+ export interface OverviewHeaderProps {
7371
+ workspace: string;
7372
+ root: string;
7373
+ /** The entry whose children are listed, null for the root */
7374
+ parentId: string | null;
7375
+ locale: string | null;
7376
+ /** The search terms entered in the overview */
7377
+ search: string;
7378
+ /**
7379
+ * The condition of the selected tab and the applied filters, undefined
7380
+ * when none applies
7381
+ */
7382
+ filter: OpenFilter<EntryFilterFields> | undefined;
7383
+ /**
7384
+ * A query for the listed entries, without paging. Pass it to
7385
+ * `useGraph().find({...query, select})`, eg. to total them.
7386
+ */
7387
+ query: GraphQuery<undefined, Type | Array<Type> | undefined, undefined>;
7388
+ }
6963
7389
  interface OverviewColumnBase<Value> {
6964
7390
  /** The column header */
6965
7391
  header: string;
@@ -7044,6 +7470,12 @@ declare module 'alinea/core/Overview' {
7044
7470
  * applied filter, and the search terms.
7045
7471
  */
7046
7472
  filters?: Record<string, OverviewFilter>;
7473
+ /**
7474
+ * Tabs above the listed children, keyed by a name kept in the url. The
7475
+ * first tab is selected by default. Entries match the filter of the
7476
+ * selected tab, the applied filters and the search terms.
7477
+ */
7478
+ tabs?: Record<string, OverviewTab>;
7047
7479
  /** The default layout, editors can still switch */
7048
7480
  layout?: 'table' | 'cards';
7049
7481
  /**
@@ -7053,6 +7485,11 @@ declare module 'alinea/core/Overview' {
7053
7485
  thumbnail?: Expr<any> | Record<string, Expr<any>>;
7054
7486
  /** Components rendered in the overview toolbar, eg. an export button */
7055
7487
  actions?: Array<View<OverviewActionProps>>;
7488
+ /**
7489
+ * A component rendered above the listed children, eg. totals or a chart of
7490
+ * the listed entries
7491
+ */
7492
+ header?: View<OverviewHeaderProps>;
7056
7493
  }
7057
7494
  /** Create a column for an overview */
7058
7495
  export function column<const Select extends OverviewColumnSelect | undefined = undefined>(options: OverviewColumnOptions<Select, OverviewColumnValue<Select>>): OverviewColumn<OverviewColumnValue<Select>>;
@@ -7489,6 +7926,11 @@ declare module 'alinea/core/Scope' {
7489
7926
  fieldOf(field: Field): [type: string, path: string] | undefined;
7490
7927
  locationOf(entity: Entity): string[] | undefined;
7491
7928
  nameOf(entity: Entity): string | undefined;
7929
+ /**
7930
+ * Where a field stores its value in the entry data, eg. `['seo', 'title']`
7931
+ * for a field of an object field
7932
+ */
7933
+ dataPathOf(field: Field): Array<string> | undefined;
7492
7934
  /** Configured spellings of a locale, matched case-insensitively. */
7493
7935
  locales(locale: string): Array<string>;
7494
7936
  stringify(input: any): string;
@@ -7836,6 +8278,8 @@ declare module 'alinea/core/source/Source' {
7836
8278
  /** Consume remote blobs incrementally instead of materializing one bundle. */
7837
8279
  applyChangesFrom?(remote: RemoteSource, batch: ChangesBatch, tree: ReadonlyTree): Promise<void>;
7838
8280
  }
8281
+ /** Pin the tree while streaming blobs from the revision already fetched. */
8282
+ export function sourceAtTree(source: RemoteSource, tree: ReadonlyTree): RemoteSource;
7839
8283
  export function applyChangesFrom(source: Source, remote: RemoteSource, batch: ChangesBatch, tree: ReadonlyTree): Promise<void>;
7840
8284
  export function bundleContents(source: RemoteSource, batch: ChangesBatch): Promise<ChangesBatch>;
7841
8285
  export function diff(source: Source, remote: RemoteSource): Promise<ChangesBatch>;
@@ -8182,6 +8626,7 @@ declare module 'alinea/core/Trigger' {
8182
8626
 
8183
8627
  declare module 'alinea/core/Type' {
8184
8628
 
8629
+ import type { EntryActionProps } from 'alinea/core/DashboardEntry';
8185
8630
  import type { ComponentType } from 'react';
8186
8631
  import type { Config } from 'alinea/core/Config';
8187
8632
  import type { EntryStatus } from 'alinea/core/Entry';
@@ -8231,6 +8676,8 @@ declare module 'alinea/core/Type' {
8231
8676
  function references(type: Type, value: Record<string, unknown>, path?: Array<string>, labels?: Array<string>): Array<EntryReferenceTarget>;
8232
8677
  function fields(type: Type): Record<string, Field>;
8233
8678
  function sections(type: Type): Section[];
8679
+ /** Fields edited in the settings of a block, or a Settings tab of an entry */
8680
+ function settings(type: Type): FieldsDefinition | undefined;
8234
8681
  function isContainer(type: Type): boolean;
8235
8682
  function field(type: Type, name: string): Field | undefined;
8236
8683
  function isType(type: any): type is Type;
@@ -8249,12 +8696,24 @@ declare module 'alinea/core/Type' {
8249
8696
  export interface FieldsDefinition {
8250
8697
  [key: string]: Field;
8251
8698
  }
8252
- export interface TypeConfig<Definition> {
8699
+ export interface TypeConfig<Definition, Settings = {}> {
8253
8700
  fields: Definition;
8701
+ /**
8702
+ * Fields shown in the settings sheet of a block, below its label and
8703
+ * anchor. Entries of the type show them in a Settings tab. They are stored
8704
+ * and queried like the other fields.
8705
+ */
8706
+ settings?: Settings;
8254
8707
  /** Accepts entries of these types as children */
8255
8708
  contains?: Array<string | Type>;
8256
8709
  /** How the dashboard lists the children of entries of this type */
8257
8710
  overview?: OverviewOptions;
8711
+ /**
8712
+ * Components rendered in the header of the entry editor, next to the
8713
+ * publish button, eg. a button that exports or sends the entry. They can
8714
+ * read the fields of the entry with the hooks of `alinea/cms`.
8715
+ */
8716
+ actions?: Array<View<EntryActionProps>>;
8258
8717
  /**
8259
8718
  * Order children entries in the sidebar content tree
8260
8719
  * @deprecated Use `overview.sort`
@@ -8272,7 +8731,11 @@ declare module 'alinea/core/Type' {
8272
8731
  collapsed?: boolean;
8273
8732
  /** An icon (React component) to represent this type in the dashboard */
8274
8733
  icon?: ComponentType;
8275
- /** A React component used to view an entry of this type in the dashboard */
8734
+ /**
8735
+ * A React component, or a path to one, that replaces the form of entries of
8736
+ * this type below the entry header. It edits the fields of the entry with
8737
+ * the hooks of `alinea/cms`.
8738
+ */
8276
8739
  view?: View<{
8277
8740
  type: Type;
8278
8741
  }>;
@@ -8296,19 +8759,19 @@ declare module 'alinea/core/Type' {
8296
8759
  preview?: Preview;
8297
8760
  }
8298
8761
  /** Types that only hold children can leave out their fields */
8299
- export interface ContainerTypeConfig<Definition> extends Omit<TypeConfig<Definition>, 'fields' | 'contains'> {
8762
+ export interface ContainerTypeConfig<Definition, Settings = {}> extends Omit<TypeConfig<Definition, Settings>, 'fields' | 'contains'> {
8300
8763
  fields?: Definition;
8301
8764
  /** Accepts entries of these types as children */
8302
8765
  contains: Array<string | Type>;
8303
8766
  }
8304
- export interface TypeInternal extends TypeConfig<FieldsDefinition> {
8767
+ export interface TypeInternal extends TypeConfig<FieldsDefinition, FieldsDefinition> {
8305
8768
  label: string;
8306
8769
  allFields: Record<string, Field>;
8307
8770
  sections: Array<Section>;
8308
8771
  }
8309
8772
  /** Create a new type */
8310
- export function type<Fields extends FieldsDefinition = {}>(label: string, config: TypeConfig<Fields> | ContainerTypeConfig<Fields>): Type<Fields>;
8311
- export function createType<Fields extends FieldsDefinition = {}>(label: string, { fields: definition, ...config }: TypeConfig<Fields> | ContainerTypeConfig<Fields>): Type<Fields>;
8773
+ export function type<Fields extends FieldsDefinition = {}, Settings extends FieldsDefinition = {}>(label: string, config: TypeConfig<Fields, Settings> | ContainerTypeConfig<Fields, Settings>): Type<Fields & Settings>;
8774
+ export function createType<Fields extends FieldsDefinition = {}, Settings extends FieldsDefinition = {}>(label: string, { fields: definition, ...config }: TypeConfig<Fields, Settings> | ContainerTypeConfig<Fields, Settings>): Type<Fields & Settings>;
8312
8775
  export {};
8313
8776
 
8314
8777
  }
@@ -8391,6 +8854,8 @@ declare module 'alinea/core/util/Async' {
8391
8854
  /** Map concurrently while yielding results as they complete. */
8392
8855
  export function mapConcurrent<T, TResult>(iterable: Iterable<T>, mapper: (item: T, signal?: AbortSignal) => Promise<TResult>, options: MapConcurrentOptions): AsyncGenerator<TResult>;
8393
8856
  export function genEffect<T, TReturn>(gen: AsyncIterable<T, TReturn>, effect: (result: T) => void): AsyncIterable<T>;
8857
+ /** Settle like `promise`, or reject when it takes longer than `ms`. */
8858
+ export function within<T>(promise: Promise<T>, ms: number): Promise<T>;
8394
8859
  /** Run tasks one after another; a failed task does not block the next one. */
8395
8860
  export class TaskQueue {
8396
8861
  #private;
@@ -9180,6 +9645,7 @@ declare module 'alinea/dashboard/App' {
9180
9645
  }
9181
9646
  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]>;
9182
9647
  export function App(props: AppProps): import("react").JSX.Element;
9648
+ export function AppLoading(): import("react").JSX.Element;
9183
9649
 
9184
9650
  }
9185
9651
 
@@ -9252,6 +9718,20 @@ declare module 'alinea/dashboard/app/AuthView' {
9252
9718
 
9253
9719
  }
9254
9720
 
9721
+ declare module 'alinea/dashboard/app/BlockSettings' {
9722
+
9723
+ import { Type } from 'alinea/core/Type';
9724
+ import type { EditorNode } from 'alinea/dashboard/atoms/editor';
9725
+ export interface BlockSettingsProps {
9726
+ node: EditorNode;
9727
+ type: Type;
9728
+ readOnly?: boolean;
9729
+ }
9730
+ /** The settings fields of a block's type, for its settings sheet */
9731
+ export function BlockSettings({ node, type, readOnly }: BlockSettingsProps): import("react").JSX.Element | null;
9732
+
9733
+ }
9734
+
9255
9735
  declare module 'alinea/dashboard/app/BlockSheet' {
9256
9736
 
9257
9737
  import type { StyleProps } from 'alinea/components';
@@ -9384,7 +9864,7 @@ declare module 'alinea/dashboard/app/DashboardLayout' {
9384
9864
  import type { WorkspaceInternal } from 'alinea/core/Workspace';
9385
9865
  import type { Page } from 'alinea/dashboard/atoms/nav';
9386
9866
  import type { RootAtoms, TreeView } from 'alinea/dashboard/atoms/root';
9387
- import type { PropsWithChildren } from 'react';
9867
+ import { type PropsWithChildren } from 'react';
9388
9868
  export interface DashboardLayoutProps extends PropsWithChildren {
9389
9869
  canManageMembers: boolean;
9390
9870
  page: Page;
@@ -9454,21 +9934,50 @@ declare module 'alinea/dashboard/app/editor/MediaAltField.view' {
9454
9934
 
9455
9935
  }
9456
9936
 
9937
+ declare module 'alinea/dashboard/app/EntryActions' {
9938
+
9939
+ import type { Entry } from 'alinea/core/Entry';
9940
+ import type { Type } from 'alinea/core/Type';
9941
+ import type { EditorNode } from 'alinea/dashboard/atoms/editor';
9942
+ export interface EntryActionsProps {
9943
+ type: Type;
9944
+ node: EditorNode;
9945
+ entry: Entry;
9946
+ locale: string | null;
9947
+ }
9948
+ /** The actions configured with the `actions` option of a type */
9949
+ export function EntryActions({ type, node, entry, locale }: EntryActionsProps): import("react").JSX.Element | null;
9950
+
9951
+ }
9952
+
9457
9953
  declare module 'alinea/dashboard/app/EntryFields' {
9458
9954
 
9459
9955
  import { Field } from 'alinea/core/Field';
9460
9956
  import { Section } from 'alinea/core/Section';
9957
+ import type { ObjectField } from 'alinea/field/object';
9461
9958
  export function FieldsEditor(): import("react").JSX.Element[];
9462
9959
  export function EntryFields(): import("react").JSX.Element[];
9463
9960
  export interface EditFieldsProps {
9961
+ /**
9962
+ * An object field to render the fields of, in the scope of its value.
9963
+ * Without `fields` all its fields are rendered.
9964
+ */
9965
+ field?: ObjectField<object>;
9464
9966
  /** The fields and sections to render, keyed by name */
9465
- fields: Record<string, Field | Section>;
9967
+ fields?: Record<string, Field | Section>;
9466
9968
  }
9467
9969
  /**
9468
9970
  * Renders a set of fields and sections with their configured views, laid out
9469
9971
  * like the default entry form (including each field's `width`).
9972
+ *
9973
+ * @example
9974
+ * // Some fields of an object field, in a custom view of that object
9975
+ * <EditFields
9976
+ * field={Product.dimensions}
9977
+ * fields={{width: Product.dimensions.width}}
9978
+ * />
9470
9979
  */
9471
- export const EditFields: import("react").MemoExoticComponent<({ fields }: EditFieldsProps) => import("react").JSX.Element>;
9980
+ export const EditFields: import("react").MemoExoticComponent<({ field, fields }: EditFieldsProps) => import("react").JSX.Element | null>;
9472
9981
  export interface EditFieldProps {
9473
9982
  /** The field to render, it must belong to the entry, row or object being edited */
9474
9983
  field: Field;
@@ -9882,6 +10391,17 @@ declare module 'alinea/dashboard/app/ExplorerModal' {
9882
10391
 
9883
10392
  }
9884
10393
 
10394
+ declare module 'alinea/dashboard/app/ExplorerOverviewHeader' {
10395
+
10396
+ import type { ReactNode } from 'react';
10397
+ export interface ExplorerOverviewHeaderProps {
10398
+ children: ReactNode;
10399
+ }
10400
+ /** Places the `overview.header` view between the toolbar and the list */
10401
+ export function ExplorerOverviewHeader({ children }: ExplorerOverviewHeaderProps): import("react").JSX.Element;
10402
+
10403
+ }
10404
+
9885
10405
  declare module 'alinea/dashboard/app/ExplorerPickerContent' {
9886
10406
 
9887
10407
  import { type DashboardEntry, type DashboardExplorer, type ExplorerLocation, type ExplorerOptions, type ExplorerReadyPage, type ExplorerView } from 'alinea/dashboard/atoms/explorer';
@@ -9939,6 +10459,23 @@ declare module 'alinea/dashboard/app/ExplorerTable' {
9939
10459
 
9940
10460
  }
9941
10461
 
10462
+ declare module 'alinea/dashboard/app/ExplorerTabs' {
10463
+
10464
+ import type { ReactNode } from 'react';
10465
+ import type { OverviewTabState } from 'alinea/dashboard/atoms/overview';
10466
+ export interface ExplorerTabsProps {
10467
+ tabs: Array<OverviewTabState>;
10468
+ /** The key of the selected tab */
10469
+ value: string;
10470
+ onValueChange: (value: string) => void;
10471
+ /** The listed entries of the selected tab */
10472
+ children: ReactNode;
10473
+ }
10474
+ /** The tabs of an overview, above the entries listed for the selected tab */
10475
+ export function ExplorerTabs({ tabs, value, onValueChange, children }: ExplorerTabsProps): import("react").JSX.Element;
10476
+
10477
+ }
10478
+
9942
10479
  declare module 'alinea/dashboard/app/ExternalLinkPicker' {
9943
10480
 
9944
10481
  export interface ExternalLinkValue {
@@ -10328,6 +10865,17 @@ declare module 'alinea/dashboard/app/SearchBar' {
10328
10865
 
10329
10866
  }
10330
10867
 
10868
+ declare module 'alinea/dashboard/app/SettingsTab' {
10869
+
10870
+ import { Type } from 'alinea/core/Type';
10871
+ /**
10872
+ * The type an entry's form renders: the settings of a type that is not a
10873
+ * document (documents add their own tab) move to a Settings tab.
10874
+ */
10875
+ export function withSettingsTab(entryType: Type): Type;
10876
+
10877
+ }
10878
+
10331
10879
  declare module 'alinea/dashboard/app/SidebarLayout' {
10332
10880
 
10333
10881
  import type { ReactNode } from 'react';
@@ -10497,6 +11045,7 @@ declare module 'alinea/dashboard/atoms/activity' {
10497
11045
  export interface DashboardActivity {
10498
11046
  items: Array<Activity>;
10499
11047
  isMutating: boolean;
11048
+ isSyncing: boolean;
10500
11049
  hasFailed: boolean;
10501
11050
  hasBlocked: boolean;
10502
11051
  canRetry: boolean;
@@ -10594,8 +11143,10 @@ declare module 'alinea/dashboard/atoms/config' {
10594
11143
  export const viewAtoms: (key: string) => import("jotai").Atom<ComponentType | undefined>;
10595
11144
  export const typeAtoms: (typeName: string) => import("jotai").Atom<{
10596
11145
  fields: import("alinea/core/Type").FieldsDefinition;
11146
+ settings?: import("alinea/core/Type").FieldsDefinition | undefined;
10597
11147
  contains?: Array<string | import("alinea/types").Type>;
10598
11148
  overview?: import("alinea/types").OverviewOptions;
11149
+ actions?: Array<import("alinea/core/View").View<import("alinea/types").EntryActionProps>>;
10599
11150
  orderChildrenBy?: import("alinea/core/OrderBy").OrderBy | Array<import("alinea/core/OrderBy").OrderBy>;
10600
11151
  hidden?: true;
10601
11152
  collapsed?: boolean;
@@ -10693,6 +11244,17 @@ declare module 'alinea/dashboard/atoms/dashboard' {
10693
11244
  export const entrySidebarOpenAtom: import("jotai").PrimitiveAtom<boolean> & {
10694
11245
  init: boolean;
10695
11246
  };
11247
+ /**
11248
+ * Entries without a preview only have history and references in the sidebar,
11249
+ * so it starts closed for them (like the 1.x preview pane) and remembers its
11250
+ * own state, leaving the room to the form and wide custom views
11251
+ */
11252
+ export const entrySidebarOpenWithoutPreviewAtom: import("jotai").PrimitiveAtom<boolean> & {
11253
+ init: boolean;
11254
+ };
11255
+ export function entrySidebarOpen(hasPreview: boolean): import("jotai").PrimitiveAtom<boolean> & {
11256
+ init: boolean;
11257
+ };
10696
11258
  export const themeAtom: import("jotai").WritableAtom<DashboardTheme, [update: SetStateAction<DashboardTheme>], void> & {
10697
11259
  onMount(setTheme: (update: SetStateAction<DashboardTheme>) => void): void;
10698
11260
  };
@@ -10739,8 +11301,12 @@ declare module 'alinea/dashboard/atoms/editor' {
10739
11301
  }> | ((props: {
10740
11302
  section: Section;
10741
11303
  }) => ReactNode) | undefined>;
11304
+ /** The section has a view or at least one field that is not hidden */
11305
+ visible: Atom<boolean>;
10742
11306
  }
10743
11307
  export interface EditorField {
11308
+ /** The editor of the entry, row or object the field belongs to */
11309
+ editor: EditorModel;
10744
11310
  key: string;
10745
11311
  value: WritableAtom<unknown, [unknown], void>;
10746
11312
  options: Atom<FieldOptions<unknown>>;
@@ -10768,13 +11334,15 @@ declare module 'alinea/dashboard/atoms/editor' {
10768
11334
  }
10769
11335
  export function rootEditor(editor: EditorModel): EditorModel;
10770
11336
  export class EntryEditorSection implements EditorSection {
11337
+ editor: EditorModel;
10771
11338
  section: Section;
10772
- constructor(section: Section);
11339
+ constructor(editor: EditorModel, section: Section);
10773
11340
  view: Atom<((props: {
10774
11341
  section: Section;
10775
11342
  }) => ReactNode) | ComponentType<{
10776
11343
  section: Section;
10777
11344
  }> | undefined>;
11345
+ visible: Atom<boolean>;
10778
11346
  }
10779
11347
  class EntryEditorField implements EditorField {
10780
11348
  #private;
@@ -11079,6 +11647,7 @@ declare module 'alinea/dashboard/atoms/explorer' {
11079
11647
  import type { Config } from 'alinea/core/Config';
11080
11648
  import { type EntryStatus } from 'alinea/core/Entry';
11081
11649
  import type { EntryFields } from 'alinea/core/EntryFields';
11650
+ import { type OpenFilter } from 'alinea/core/Filter';
11082
11651
  import type { Filter } from 'alinea/core/Filter';
11083
11652
  import type { GraphQuery } from 'alinea/core/Graph';
11084
11653
  import type { OverviewSort } from 'alinea/core/Overview';
@@ -11151,6 +11720,11 @@ declare module 'alinea/dashboard/atoms/explorer' {
11151
11720
  * explorer. The explorers of a root share them.
11152
11721
  */
11153
11722
  filterState?: PrimitiveAtom<OverviewFilterSelection>;
11723
+ /**
11724
+ * The key of the tab the editor picked, defaults to state kept by the
11725
+ * explorer. Page explorers keep it in the url.
11726
+ */
11727
+ tabState?: WritableAtom<string | undefined, [string | undefined], void>;
11154
11728
  showSelectionControls?: boolean;
11155
11729
  initialSelection?: Array<string>;
11156
11730
  /**
@@ -11189,6 +11763,11 @@ declare module 'alinea/dashboard/atoms/explorer' {
11189
11763
  }
11190
11764
  export interface ExplorerReadyPage {
11191
11765
  canUpload: boolean;
11766
+ /**
11767
+ * The condition of the selected tab and the picked filters, undefined when
11768
+ * none applies
11769
+ */
11770
+ filter: OpenFilter<EntryFields> | undefined;
11192
11771
  /** The filter options the listed entries match, by filter key */
11193
11772
  filters: OverviewFilterSelection;
11194
11773
  isMedia: boolean;
@@ -11206,6 +11785,8 @@ declare module 'alinea/dashboard/atoms/explorer' {
11206
11785
  searchScope: 'workspace' | 'everything';
11207
11786
  searchesEverything: boolean;
11208
11787
  sort: ExplorerSortState;
11788
+ /** The key of the tab shown, undefined when the overview has no tabs */
11789
+ tab: string | undefined;
11209
11790
  view: ExplorerView;
11210
11791
  }
11211
11792
  export function explorerPageIsPending(page: ExplorerReadyPage, location: ExplorerLocation, locale: string | null): boolean;
@@ -11393,6 +11974,8 @@ declare module 'alinea/dashboard/atoms/explorer' {
11393
11974
  * key. The page lists the ones that apply to its overview.
11394
11975
  */
11395
11976
  requestedFilters: PrimitiveAtom<OverviewFilterSelection>;
11977
+ /** The key of the tab the editor picked, undefined for the first tab */
11978
+ requestedTab: WritableAtom<string | undefined, [string | undefined], void>;
11396
11979
  selectedLocale: WritableAtom<string | null, [
11397
11980
  SetStateAction<string | null>
11398
11981
  ], void>;
@@ -11482,6 +12065,11 @@ declare module 'alinea/dashboard/atoms/explorer' {
11482
12065
 
11483
12066
  declare module 'alinea/dashboard/atoms/graph' {
11484
12067
 
12068
+ import type { SyncProgress } from 'alinea/core/db/LocalStore';
12069
+ /** The files fetched by a running sync from the remote. */
12070
+ export const syncProgressAtom: import("jotai").WritableAtom<SyncProgress | undefined, [], () => void> & {
12071
+ onMount: (init: () => () => void) => () => void;
12072
+ };
11485
12073
  export const shaAtom: import("jotai").Atom<Promise<string | undefined>>;
11486
12074
  export const entryRevisionAtom: (id: string) => import("jotai").Atom<string | undefined>;
11487
12075
  export const syncAtom: import("jotai").WritableAtom<null, [], Promise<string | undefined>> & {
@@ -11636,6 +12224,8 @@ declare module 'alinea/dashboard/atoms/nav' {
11636
12224
  import type { OverviewSort } from 'alinea/core/Overview';
11637
12225
  import type { EntryDefaultView } from 'alinea/core/Type';
11638
12226
  import { ReactNode } from 'react';
12227
+ import { nav } from 'alinea/dashboard/Href';
12228
+ export { nav };
11639
12229
  export interface RouteBlock {
11640
12230
  confirm: () => void | Promise<void>;
11641
12231
  }
@@ -11646,7 +12236,10 @@ declare module 'alinea/dashboard/atoms/nav' {
11646
12236
  init: RouteBlock | null;
11647
12237
  };
11648
12238
  export interface DashboardRoute {
11649
- /** An edit link names an entry by its url, until it is looked up */
12239
+ /**
12240
+ * An edit link names an entry by its url, or by its id without workspace
12241
+ * and root, until it is looked up
12242
+ */
11650
12243
  page?: 'splash' | 'entry' | 'users' | 'edit';
11651
12244
  url?: string;
11652
12245
  workspace?: string;
@@ -11656,22 +12249,19 @@ declare module 'alinea/dashboard/atoms/nav' {
11656
12249
  view?: EntryDefaultView;
11657
12250
  /** The column an overview is sorted by, eg. `price` or `-price` */
11658
12251
  sort?: string;
12252
+ /** The tab an overview shows, eg. `active` */
12253
+ tab?: string;
11659
12254
  /** Replace the current history entry instead of adding one */
11660
12255
  replace?: boolean;
11661
12256
  }
11662
- interface ResolvedDashboardRoute extends DashboardRoute {
12257
+ export interface ResolvedDashboardRoute extends DashboardRoute {
11663
12258
  page: 'splash' | 'entry' | 'users' | 'edit';
11664
12259
  }
11665
- export const nav: {
11666
- splash(): string;
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;
11670
- entry(workspace?: string, root?: string, entryId?: string, locale?: string | null, view?: EntryDefaultView, sort?: string): string;
11671
- };
11672
12260
  /** The url parameter of an overview sort: the column, prefixed with - when descending */
11673
12261
  export function formatOverviewSort(sort?: OverviewSort): string | undefined;
11674
12262
  export function parseOverviewSort(value: string | undefined): OverviewSort | undefined;
12263
+ /** @internal */
12264
+ export function hashFromRoute(route: ResolvedDashboardRoute): string;
11675
12265
  interface NavigationRequest {
11676
12266
  browser?: boolean;
11677
12267
  replace?: boolean;
@@ -11696,8 +12286,12 @@ declare module 'alinea/dashboard/atoms/nav' {
11696
12286
  * when `entry` is null. The overview of the current page keeps it in the url.
11697
12287
  */
11698
12288
  export const overviewSortAtom: (workspace: string, root: string, entry: string | null) => import("jotai").WritableAtom<OverviewSort | undefined, [sort: OverviewSort | undefined], void>;
12289
+ /**
12290
+ * The tab of the overview listing the children of an entry, or of a root
12291
+ * when `entry` is null. The overview of the current page keeps it in the url.
12292
+ */
12293
+ export const overviewTabAtom: (workspace: string, root: string, entry: string | null) => import("jotai").WritableAtom<string | undefined, [tab: string | undefined], void>;
11699
12294
  export function page(render: (page: Page, get: Getter) => ReactNode | Promise<ReactNode>): (page: Page, get: Getter) => ReactNode | Promise<ReactNode>;
11700
- export {};
11701
12295
 
11702
12296
  }
11703
12297
 
@@ -11711,7 +12305,7 @@ declare module 'alinea/dashboard/atoms/overview' {
11711
12305
  import type { OrderBy } from 'alinea/core/OrderBy';
11712
12306
  import type { EntryFields } from 'alinea/core/EntryFields';
11713
12307
  import type { OpenFilter } from 'alinea/core/Filter';
11714
- import { type OverviewActionProps, type OverviewCellProps, type OverviewColumnAlign, type OverviewColumnWidth, type OverviewEntry, type OverviewFormatContext, type OverviewOptions, type OverviewSort, type OverviewSortDirection } from 'alinea/core/Overview';
12308
+ import { type OverviewActionProps, type OverviewCellProps, type OverviewColumnAlign, type OverviewColumnWidth, type OverviewEntry, type OverviewFilterContext, type OverviewFilterOptions, type OverviewFormatContext, type OverviewHeaderProps, type OverviewOptions, type OverviewSort, type OverviewSortDirection } from 'alinea/core/Overview';
11715
12309
  import { type RootData } from 'alinea/core/Root';
11716
12310
  import { Type } from 'alinea/core/Type';
11717
12311
  import type { View } from 'alinea/core/View';
@@ -11770,7 +12364,16 @@ declare module 'alinea/dashboard/atoms/overview' {
11770
12364
  key: string;
11771
12365
  label: string;
11772
12366
  multiple: boolean;
12367
+ /** The options, empty until `loadFilterOptions` loaded those of `load` */
11773
12368
  options: Array<OverviewFilterOptionState>;
12369
+ /** Loads the options of filters that build them from the content */
12370
+ load?: (context: OverviewFilterContext) => OverviewFilterOptions | Promise<OverviewFilterOptions>;
12371
+ }
12372
+ /** A tab of an overview */
12373
+ export interface OverviewTabState {
12374
+ key: string;
12375
+ label: string;
12376
+ filter?: OpenFilter<EntryFields>;
11774
12377
  }
11775
12378
  /** The keys of the options editors picked, by the key of their filter */
11776
12379
  export interface OverviewFilterSelection {
@@ -11787,11 +12390,14 @@ declare module 'alinea/dashboard/atoms/overview' {
11787
12390
  sorts: Array<OverviewSortState>;
11788
12391
  /** The filters editors can apply */
11789
12392
  filters: Array<OverviewFilterState>;
12393
+ /** The tabs above the list, the first one is selected by default */
12394
+ tabs: Array<OverviewTabState>;
11790
12395
  /** The order children are listed in when the editor did not sort */
11791
12396
  sort?: OrderBy | Array<OrderBy>;
11792
12397
  layout?: 'table' | 'cards';
11793
12398
  thumbnail?: OverviewSelection;
11794
12399
  actions: Array<View<OverviewActionProps>>;
12400
+ header?: View<OverviewHeaderProps>;
11795
12401
  /** The names of the types the parent contains */
11796
12402
  types: Array<string>;
11797
12403
  }
@@ -11824,6 +12430,10 @@ declare module 'alinea/dashboard/atoms/overview' {
11824
12430
  export function resolveOverview(config: Config, parent: OverviewParent, options?: OverviewResolveOptions): OverviewState;
11825
12431
  /** Resolves an overview for a list of entries of the given types */
11826
12432
  export function resolveOverviewOptions(config: Config, overview: OverviewOptions | undefined, types: Array<string>, sort: OrderBy | Array<OrderBy> | undefined, options?: OverviewResolveOptions): OverviewState;
12433
+ /** Whether an overview has filters whose options load from the content */
12434
+ export function hasFilterLoaders(overview: OverviewState): boolean;
12435
+ /** Loads the options of the filters that build them from the content */
12436
+ export function loadFilterOptions(overview: OverviewState, context: OverviewFilterContext): Promise<OverviewState>;
11827
12437
  /** The projection a column selects for entries of a type */
11828
12438
  export function columnProjection(select: OverviewSelection | undefined, typeName: string): Projection | undefined;
11829
12439
  /**
@@ -11851,10 +12461,16 @@ declare module 'alinea/dashboard/atoms/overview' {
11851
12461
  */
11852
12462
  export function pickedFilters(overview: OverviewState, selection: OverviewFilterSelection): OverviewFilterSelection;
11853
12463
  /**
11854
- * The condition of the picked filters: entries match every filter, and any
11855
- * of the options picked of a filter. Undefined when nothing is picked.
12464
+ * The tab the editor picked, or the first tab when it is unknown. Undefined
12465
+ * when the overview has no tabs.
12466
+ */
12467
+ export function pickedTab(overview: OverviewState, tab: string | undefined): OverviewTabState | undefined;
12468
+ /**
12469
+ * The condition of the selected tab and the picked filters: entries match
12470
+ * the tab, every filter, and any of the options picked of a filter.
12471
+ * Undefined when nothing applies.
11856
12472
  */
11857
- export function overviewFilter(overview: OverviewState, selection: OverviewFilterSelection): OpenFilter<EntryFields> | undefined;
12473
+ export function overviewFilter(overview: OverviewState, selection: OverviewFilterSelection, tab?: string): OpenFilter<EntryFields> | undefined;
11858
12474
  /** The rows an overview renders, as loaded by the explorer or an EntryTable */
11859
12475
  export interface OverviewRow {
11860
12476
  id: string;
@@ -12358,7 +12974,7 @@ declare module 'alinea/dashboard/atoms/utils' {
12358
12974
 
12359
12975
  declare module 'alinea/dashboard/boot/ActivityEvent' {
12360
12976
 
12361
- export type ActivityType = 'mutation' | 'upload';
12977
+ export type ActivityType = 'mutation' | 'upload' | 'sync';
12362
12978
  export type ActivityStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'blocked' | 'discarded' | 'cancelled';
12363
12979
  export interface Activity {
12364
12980
  id: string;
@@ -12459,6 +13075,12 @@ declare module 'alinea/dashboard/boot/DashboardWorker' {
12459
13075
  constructor(source?: Source);
12460
13076
  get db(): EntryStore | Promise<EntryStore>;
12461
13077
  sync(): Promise<string>;
13078
+ /**
13079
+ * Resolve once the dashboard can show content. Stored content shows right
13080
+ * away and catches up with the remote as an activity; an empty store, or
13081
+ * one whose cached content failed, waits for its first sync.
13082
+ */
13083
+ ready(): Promise<void>;
12462
13084
  sha(): Promise<string>;
12463
13085
  queue(id: string, mutations: Array<Mutation>): Promise<string>;
12464
13086
  retryActivity(): Promise<void>;
@@ -12478,6 +13100,18 @@ declare module 'alinea/dashboard/boot/LoadWorker' {
12478
13100
 
12479
13101
  }
12480
13102
 
13103
+ declare module 'alinea/dashboard/boot/SyncEvent' {
13104
+
13105
+ import type { SyncProgress } from 'alinea/core/db/LocalStore';
13106
+ /** Progress of a sync from the remote, without progress once it finished. */
13107
+ export class SyncEvent extends Event {
13108
+ progress?: SyncProgress | undefined;
13109
+ static readonly type = "sync";
13110
+ constructor(progress?: SyncProgress | undefined);
13111
+ }
13112
+
13113
+ }
13114
+
12481
13115
  declare module 'alinea/dashboard/boot/WorkerDB' {
12482
13116
 
12483
13117
  import type { Config } from 'alinea/core/Config';
@@ -12498,6 +13132,7 @@ declare module 'alinea/dashboard/boot/WorkerDB' {
12498
13132
  sha: string;
12499
13133
  }>;
12500
13134
  resolve<Query extends GraphQuery>(query: Query): Promise<AnyQueryResult<Query>>;
13135
+ ready(): Promise<void>;
12501
13136
  sync(): Promise<string>;
12502
13137
  get sha(): Promise<string>;
12503
13138
  referencesTo(query: EntryReferenceQuery): Promise<EntryReferenceResult>;
@@ -12511,7 +13146,7 @@ declare module 'alinea/dashboard/boot/WorkerDB' {
12511
13146
 
12512
13147
  declare module 'alinea/dashboard/cms/EntryTable' {
12513
13148
 
12514
- import type { EntryFields } from 'alinea/core/EntryFields';
13149
+ import type { EntryFilterFields } from 'alinea/core/EntryFields';
12515
13150
  import type { OpenFilter } from 'alinea/core/Filter';
12516
13151
  import type { OrderBy } from 'alinea/core/OrderBy';
12517
13152
  import type { OverviewColumn, OverviewOptions } from 'alinea/core/Overview';
@@ -12524,9 +13159,10 @@ declare module 'alinea/dashboard/cms/EntryTable' {
12524
13159
  type?: Type | Array<Type>;
12525
13160
  /**
12526
13161
  * Only list entries matching this filter, like the `filter` of a query,
12527
- * eg. `{brand: {has: {_entry: entryId}}}`
13162
+ * eg. `{brand: {has: {_entry: entryId}}}` or
13163
+ * `{_referenced: Query.referencedBy({type: Product})}`
12528
13164
  */
12529
- filter?: OpenFilter<EntryFields>;
13165
+ filter?: OpenFilter<EntryFilterFields>;
12530
13166
  /** Only list entries of this workspace */
12531
13167
  workspace?: string;
12532
13168
  /** Only list entries of this root */
@@ -12601,13 +13237,15 @@ declare module 'alinea/dashboard/cms/hooks' {
12601
13237
  * The hooks of custom field and dashboard views, exported from `alinea/cms`.
12602
13238
  * Their types do not expose the internal state of the dashboard.
12603
13239
  */
13240
+ import type { DashboardEntry } from 'alinea/core/DashboardEntry';
12604
13241
  import type { WriteableGraph } from 'alinea/core/db/WriteableGraph';
12605
- import type { Entry as EntryRecord } from 'alinea/core/Entry';
13242
+ import type { Entry } from 'alinea/core/Entry';
12606
13243
  import type { Field } from 'alinea/core/Field';
12607
13244
  import type { PreviewMetadata } from 'alinea/core/Preview';
12608
13245
  import type { Policy } from 'alinea/core/Role';
12609
13246
  import type { User } from 'alinea/core/User';
12610
13247
  import { type Dispatch, type SetStateAction } from 'react';
13248
+ import type { DashboardLocation } from 'alinea/dashboard/Href';
12611
13249
  /**
12612
13250
  * Returns the permissions of the signed-in user. Check access with
12613
13251
  * `policy.get({workspace, root, id, ...})`, which returns flags such as
@@ -12631,8 +13269,24 @@ declare module 'alinea/dashboard/cms/hooks' {
12631
13269
  /**
12632
13270
  * Returns the stored value of a field in the entry, list row or object being
12633
13271
  * edited. Use it in a view that only reads the value.
13272
+ *
13273
+ * The field-based hooks (`useFieldValue`, `useField`, `useFieldSetter`, ...)
13274
+ * also read the fields around a custom field: pass a field of the same row,
13275
+ * of a parent row or of the entry, and it resolves in the nearest scope that
13276
+ * holds it. Import the type from your schema or pass the field through the
13277
+ * options of your field.
13278
+ *
13279
+ * A view shared by types that each define their own field can pass its key
13280
+ * instead, which reads the field of the same row, untyped.
13281
+ *
13282
+ * @example
13283
+ * // In the view of a field inside a list row of `BlogPost`
13284
+ * const title = useFieldValue(BlogPost.title) // a field of the entry
13285
+ * const price = useFieldValue(options.price) // a field passed as option
13286
+ * const path = useFieldValue('path') // the path field of this type
12634
13287
  */
12635
13288
  export function useFieldValue<StoredValue, QueryValue, Mutator, Options>(field: Field<StoredValue, QueryValue, Mutator, Options>): StoredValue;
13289
+ export function useFieldValue(key: string): unknown;
12636
13290
  /**
12637
13291
  * Returns the stored value of a field and a setter, like `useState`. The
12638
13292
  * setter also accepts an updater function that receives the current value.
@@ -12665,40 +13319,38 @@ declare module 'alinea/dashboard/cms/hooks' {
12665
13319
  /**
12666
13320
  * Returns the stored value of another field in the same entry, list row or
12667
13321
  * object, by its key.
13322
+ *
13323
+ * @deprecated Use `useFieldValue`: pass the field, which is typed and also
13324
+ * reads fields of a parent row or the entry (`useFieldValue(BlogPost.title)`),
13325
+ * or its key (`useFieldValue('title')`).
12668
13326
  */
12669
13327
  export function useSiblingFieldValue(key: string): unknown;
12670
13328
  /**
12671
- * Returns the selected entry version as a plain Entry object.
13329
+ * Returns the entry being edited, in the selected locale and version.
13330
+ * `entry.data` holds the field values as saved: use `useFieldValue(field)`
13331
+ * for the live value while the editor changes it.
12672
13332
  *
12673
- * Returns null outside an entry scope, or when the current entry cannot be
12674
- * resolved. The returned value follows locale/status selection and does not
12675
- * expose internal atoms.
13333
+ * Returns null outside an entry, eg. in a root view.
12676
13334
  */
12677
- export function useEntry(): EntryRecord<Record<string, unknown>> | null;
13335
+ export function useEntry(): DashboardEntry | null;
13336
+ /** @internal */
13337
+ export function dashboardEntry(entry: Entry): DashboardEntry;
12678
13338
  /**
12679
13339
  * Returns the current locale of the dashboard: the locale of the entry being
12680
13340
  * edited, or the selected locale of the current root. `null` for content
12681
13341
  * without translations or outside the dashboard layout.
12682
13342
  */
12683
13343
  export function useLocale(): string | null;
12684
- /** A place in the dashboard to navigate to */
12685
- export interface DashboardLocation {
12686
- /** Name of the workspace */
12687
- workspace: string;
12688
- /** Name of the root */
12689
- root: string;
12690
- /** Id of the entry to open, leave out to open the root */
12691
- entryId?: string;
12692
- /** Locale to open, for roots with translations */
12693
- locale?: string | null;
12694
- }
12695
13344
  /**
12696
13345
  * Returns a function that navigates the dashboard to an entry or root. The
12697
13346
  * user is asked to confirm first when the current entry has unsaved changes.
13347
+ * An entry can be opened by its id alone, its workspace and root are looked
13348
+ * up.
12698
13349
  *
12699
13350
  * @example
12700
13351
  * const navigate = useNavigate()
12701
- * navigate({workspace: entry.workspace, root: entry.root, entryId: entry.id})
13352
+ * navigate({entryId: post.id, locale: 'en'})
13353
+ * navigate({workspace: 'main', root: 'pages'})
12702
13354
  */
12703
13355
  export function useNavigate(): (location: DashboardLocation) => void;
12704
13356
 
@@ -12732,8 +13384,9 @@ declare module 'alinea/dashboard/cms/ViewProps' {
12732
13384
  section: Section;
12733
13385
  }
12734
13386
  /**
12735
- * Props of the component a type points to with `view`. It replaces the
12736
- * default entry editor for entries of that type.
13387
+ * Props of the component a type points to with `view`. It replaces the form
13388
+ * of entries of that type below the entry header, the field hooks edit the
13389
+ * fields of the entry.
12737
13390
  */
12738
13391
  export interface TypeViewProps {
12739
13392
  /** The type of the entry being shown */
@@ -12803,11 +13456,12 @@ declare module 'alinea/dashboard/contexts' {
12803
13456
  */
12804
13457
  export function useEditor(): EditorModel;
12805
13458
  /**
12806
- * Returns the editor metadata for a field in the active editor scope.
13459
+ * Returns the editor metadata for a field in the active editor scope, or in
13460
+ * the scope of a parent row or the entry when the field belongs there.
12807
13461
  */
12808
13462
  export function useFieldInfo(field: Field): import("alinea/dashboard/atoms/editor").EditorField;
12809
13463
  /**
12810
- * Returns the reactive node backing a field in the active editor.
13464
+ * Returns the reactive node backing a field in the editor it belongs to.
12811
13465
  */
12812
13466
  export function useFieldNode<Value>(field: Field): ReactiveNode<Value>;
12813
13467
 
@@ -13012,13 +13666,13 @@ declare module 'alinea/dashboard/hook/UseEntryEditor' {
13012
13666
  import type { EntryStatus } from 'alinea/core/Entry';
13013
13667
  export interface EntryEditorVersionData {
13014
13668
  path: string;
13015
- status: EntryStatus;
13016
13669
  }
13017
13670
  export interface EntryEditorVersion {
13018
13671
  data: EntryEditorVersionData;
13019
13672
  id: string;
13020
13673
  locale: string | null;
13021
13674
  path: string;
13675
+ status: EntryStatus;
13022
13676
  title: string;
13023
13677
  type: string;
13024
13678
  }
@@ -13198,6 +13852,48 @@ declare module 'alinea/dashboard/hooks' {
13198
13852
 
13199
13853
  }
13200
13854
 
13855
+ declare module 'alinea/dashboard/Href' {
13856
+
13857
+ import type { EntryDefaultView } from 'alinea/core/Type';
13858
+ /** A place in the dashboard to navigate to */
13859
+ export interface DashboardLocation {
13860
+ /** Name of the workspace, leave out with `root` to open an entry by id */
13861
+ workspace?: string;
13862
+ /** Name of the root, leave out with `workspace` to open an entry by id */
13863
+ root?: string;
13864
+ /** Id of the entry to open, leave out to open the root */
13865
+ entryId?: string;
13866
+ /** Locale to open, for roots with translations */
13867
+ locale?: string | null;
13868
+ /**
13869
+ * Open the entry's form (`edit`) or the list of its children (`overview`),
13870
+ * for entries with both
13871
+ */
13872
+ view?: EntryDefaultView;
13873
+ }
13874
+ /** @internal */
13875
+ export const nav: {
13876
+ splash(): string;
13877
+ users(): string;
13878
+ /** The editor of the entry at `url`, as linked from a page of the site */
13879
+ edit(url: string, workspace?: string, root?: string): string;
13880
+ /** The editor of an entry by id, its workspace and root are looked up */
13881
+ link(entryId: string, locale?: string | null, view?: EntryDefaultView): string;
13882
+ entry(workspace?: string, root?: string, entryId?: string, locale?: string | null, view?: EntryDefaultView, sort?: string, tab?: string): string;
13883
+ };
13884
+ /**
13885
+ * The href of a place in the dashboard, relative to the dashboard's page. An
13886
+ * entry can be linked by its id alone, its workspace and root are looked up
13887
+ * when the link opens. Outside the dashboard, prefix the dashboard's url.
13888
+ *
13889
+ * @example
13890
+ * <a href={href({entryId: post.id})}>{post.title}</a>
13891
+ * const url = `${baseUrl}/admin${href({entryId: post.id})}`
13892
+ */
13893
+ export function href(location: DashboardLocation): string;
13894
+
13895
+ }
13896
+
13201
13897
  declare module 'alinea/dashboard/icons' {
13202
13898
 
13203
13899
  import type { SVGProps } from 'react';
@@ -13383,6 +14079,7 @@ declare module 'alinea/dashboard/icons' {
13383
14079
  export function IcRoundRotateRight(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
13384
14080
  export function IcRoundCrop(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
13385
14081
  export function IcRoundDriveFileMove(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
14082
+ export function IcRoundDownload(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
13386
14083
 
13387
14084
  }
13388
14085
 
@@ -13780,9 +14477,11 @@ declare module 'alinea/database/entry/EntryTable' {
13780
14477
  /** Metadata fields whose queries order by their stored value. */
13781
14478
  export const orderedMetadata: Set<string>;
13782
14479
  /**
13783
- * Index the stored value of every ordered field in the config, per type, and
13784
- * of the ordered metadata. Field names are identifiers, so the dotted metadata
13785
- * names cannot collide with them.
14480
+ * Index the stored value of every ordered field in the config, and of the
14481
+ * ordered metadata. A field is indexed per type that has it, over the rows of
14482
+ * that type only: queries of one type find the index, and the rows of other
14483
+ * types don't take up space in it. The metadata is indexed over every row.
14484
+ * Index names are `type:field` and `metadata.name`, which can't collide.
13786
14485
  */
13787
14486
  export function syncFieldIndexes(db: Database, config: Config): Promise<void>;
13788
14487
  /** An Entry plus the physical-version and local-index fields. */
@@ -13928,7 +14627,16 @@ declare module 'alinea/database/EntryDatabase' {
13928
14627
  getSourceFileStats(): Promise<Map<string, FileStat>>;
13929
14628
  /** Replace the recorded file stats after syncing from a filesystem source. */
13930
14629
  setSourceFileStats(stats: ReadonlyMap<string, FileStat>): Promise<void>;
13931
- compact(): Promise<void>;
14630
+ /**
14631
+ * Optimize the database and rewrite it when it has pages to reclaim, or
14632
+ * always with `rewrite`. A file that synced many changes keeps the pages of
14633
+ * its tables scattered over it, without any to reclaim: readers that read
14634
+ * ahead, such as the dashboard reading a stored copy, then read it page by
14635
+ * page. Rewriting lays every table out in order.
14636
+ */
14637
+ compact(options?: {
14638
+ rewrite?: boolean;
14639
+ }): Promise<void>;
13932
14640
  }
13933
14641
 
13934
14642
  }
@@ -14086,8 +14794,11 @@ declare module 'alinea/database/LocalDB' {
14086
14794
 
14087
14795
  declare module 'alinea/database/query/Condition' {
14088
14796
 
14797
+ import { type RelationCondition } from 'alinea/core/Filter';
14089
14798
  import { type HasSql, type Sql } from 'rado';
14090
14799
  type FieldSql = (name: string) => HasSql;
14800
+ /** Whether a query of entries related to a field finds an entry */
14801
+ type RelationSql = (name: string, query: RelationCondition) => Sql<boolean>;
14091
14802
  export function jsonField(target: HasSql, segments: Array<string>): HasSql<unknown>;
14092
14803
  /**
14093
14804
  * Match a column against a JSON array parameter, so one prepared statement
@@ -14096,7 +14807,7 @@ declare module 'alinea/database/query/Condition' {
14096
14807
  export function inJson(column: HasSql, values: HasSql<string>): Sql<boolean>;
14097
14808
  /** Compile a condition; `depth` names the array items it iterates. */
14098
14809
  export function compileCondition(field: HasSql, condition: unknown, depth?: number): Sql<boolean>;
14099
- export function compileFilter(filter: unknown, field: FieldSql): Sql<boolean>;
14810
+ export function compileFilter(filter: unknown, field: FieldSql, relation?: RelationSql): Sql<boolean>;
14100
14811
  export function arrayIncludes(field: HasSql, predicate: (item: HasSql) => Sql<boolean>, depth?: number): Sql<boolean>;
14101
14812
  export {};
14102
14813
 
@@ -14169,7 +14880,7 @@ declare module 'alinea/database/query/Relation' {
14169
14880
 
14170
14881
  import type { EdgeQuery } from 'alinea/core/Graph';
14171
14882
  import { type Sql } from 'rado';
14172
- import type { EntryIndexTarget } from 'alinea/database/entry/EntryTable';
14883
+ import { type EntryIndexTarget } from 'alinea/database/entry/EntryTable';
14173
14884
  /** Expand the stored references as SQL rows, retaining list order and duplicates. */
14174
14885
  export function linkRelation(source: EntryIndexTarget, field: string, multiple: boolean): {
14175
14886
  target: Sql<unknown>;
@@ -14178,6 +14889,18 @@ declare module 'alinea/database/query/Relation' {
14178
14889
  };
14179
14890
  /** Restrict related identities in SQL before the query's own filters/paging. */
14180
14891
  export function relationCondition(entry: EntryIndexTarget, query: EdgeQuery, source: EntryIndexTarget): Sql<boolean>;
14892
+ /** A link field the referencing entries link through */
14893
+ export interface ReferenceField {
14894
+ /** Where the field is stored in the entry data */
14895
+ path: Array<string>;
14896
+ multiple: boolean;
14897
+ }
14898
+ /**
14899
+ * Entries that link to the source: the reference index finds them, a link
14900
+ * field narrows them down to the ones linking through it. They share the
14901
+ * locale of the source, unless either has none.
14902
+ */
14903
+ export function referencedByCondition(entry: EntryIndexTarget, source: EntryIndexTarget, field?: ReferenceField, sameLocale?: boolean): Sql<boolean>;
14181
14904
 
14182
14905
  }
14183
14906
 
@@ -14217,7 +14940,13 @@ declare module 'alinea/database/query/Search' {
14217
14940
  export function createSearch(db: Database): Promise<void>;
14218
14941
  /** The searchable text of an entry row, read from its search row. */
14219
14942
  export function searchableText(entry: EntryIndexTarget): Sql<string>;
14220
- export function searchTokens(input: string | Array<string> | undefined): RegExpMatchArray | [] | undefined;
14943
+ /**
14944
+ * The words of a search, each with the tokens the index splits it in: the
14945
+ * index splits `multi-modal` in `multi` and `modal`.
14946
+ */
14947
+ export function searchWords(input: string | Array<string> | undefined): Array<Array<string>> | undefined;
14948
+ /** The tokens of a search, and the joined spelling of split words. */
14949
+ export function searchTokens(input: string | Array<string> | undefined): string[] | undefined;
14221
14950
  export interface SearchQueryOptions {
14222
14951
  /** Indexed spellings a token may also match, besides its prefix. */
14223
14952
  alternatives?(token: string): ReadonlyArray<string>;
@@ -14273,6 +15002,7 @@ declare module 'alinea/database/sync/EntryParser' {
14273
15002
  declare module 'alinea/database/sync/EntrySyncer' {
14274
15003
 
14275
15004
  import type { Config } from 'alinea/core/Config';
15005
+ import type { SyncProgress } from 'alinea/core/db/LocalStore';
14276
15006
  import type { RemoteSource } from 'alinea/core/source/Source';
14277
15007
  import { ReadonlyTree } from 'alinea/core/source/Tree';
14278
15008
  import type { Database } from 'rado';
@@ -14286,6 +15016,7 @@ declare module 'alinea/database/sync/EntrySyncer' {
14286
15016
  * reopened and synced from it. Defaults to true.
14287
15017
  */
14288
15018
  recordsTree?: boolean;
15019
+ onProgress?(progress: SyncProgress): void;
14289
15020
  }
14290
15021
  /** Prepared, serialized source synchronization for one database connection. */
14291
15022
  export class EntrySyncer {
@@ -14303,6 +15034,7 @@ declare module 'alinea/database/sync/EntrySyncer' {
14303
15034
  declare module 'alinea/database/sync/Ingest' {
14304
15035
 
14305
15036
  import type { Config } from 'alinea/core/Config';
15037
+ import type { SyncProgress } from 'alinea/core/db/LocalStore';
14306
15038
  import type { RemoteSource } from 'alinea/core/source/Source';
14307
15039
  import { ReadonlyTree } from 'alinea/core/source/Tree';
14308
15040
  import { type SyncQueries } from 'alinea/database/sync/SyncQueries';
@@ -14322,7 +15054,7 @@ declare module 'alinea/database/sync/Ingest' {
14322
15054
  /** Whether a directory hash stands for a directory with files. */
14323
15055
  export function hasChildren(childrenSha: string | null): boolean;
14324
15056
  /** Write the files that differ between two source trees to the entry table. */
14325
- export function mergeTrees(config: Config, source: RemoteSource, previousTree: ReadonlyTree, tree: ReadonlyTree, queries: SyncQueries): Promise<SyncChanges>;
15057
+ export function mergeTrees(config: Config, source: RemoteSource, previousTree: ReadonlyTree, tree: ReadonlyTree, queries: SyncQueries, onProgress?: (progress: SyncProgress) => void): Promise<SyncChanges>;
14326
15058
 
14327
15059
  }
14328
15060
 
@@ -14851,17 +15583,16 @@ declare module 'alinea/field/link/EntryLink' {
14851
15583
  const href: import("alinea/core/Expr").Expr<string>;
14852
15584
  const path: import("alinea/core/Expr").Expr<string>;
14853
15585
  }
14854
- interface EntryOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
15586
+ export interface EntryOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14855
15587
  }
14856
15588
  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>>>;
15589
+ export interface EntriesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
15590
+ }
14857
15591
  export namespace entry {
14858
15592
  type EntryRow<Fields> = EntryLink<LinkFields<Type.Infer<Fields>>> & ListRow;
14859
- interface EntryOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14860
- }
14861
- export function multiple<Fields = undefined>(label: Label, options?: WithoutLabel<EntryOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<EntryReference & ListRow & InferStoredValue<Fields>, EntryRow<Fields>>;
15593
+ export function multiple<Fields = undefined>(label: Label, options?: WithoutLabel<EntriesOptions<Fields>>): import("alinea/field/link/LinkField").LinksField<EntryReference & ListRow & InferStoredValue<Fields>, EntryRow<Fields>>;
14862
15594
  export {};
14863
15595
  }
14864
- export {};
14865
15596
 
14866
15597
  }
14867
15598
 
@@ -14895,9 +15626,9 @@ declare module 'alinea/field/link/FileLink' {
14895
15626
  export interface FileOptions<Fields> extends LinkFieldOptions<EntryReference & InferStoredValue<Fields>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14896
15627
  }
14897
15628
  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>>>;
15629
+ export interface FilesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
15630
+ }
14898
15631
  export namespace file {
14899
- interface FilesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14900
- }
14901
15632
  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>>>;
14902
15633
  }
14903
15634
 
@@ -14918,8 +15649,6 @@ declare module 'alinea/field/link/ImageLink' {
14918
15649
  alt?: string;
14919
15650
  /** Public url of the image file, versioned by its hash */
14920
15651
  src: string;
14921
- /** Same as src */
14922
- url: string;
14923
15652
  extension: string;
14924
15653
  size: number;
14925
15654
  hash: string;
@@ -14937,7 +15666,6 @@ declare module 'alinea/field/link/ImageLink' {
14937
15666
  const title: import("alinea/core/Expr").Expr<string>;
14938
15667
  const alt: import("alinea/core/media/MediaAltField").MediaAltField;
14939
15668
  const src: import("alinea/core/Expr").Expr<string>;
14940
- const url: import("alinea/core/Expr").Expr<string>;
14941
15669
  const extension: import("alinea/field/hidden").HiddenField<string>;
14942
15670
  const size: import("alinea/field/hidden").HiddenField<number>;
14943
15671
  const hash: import("alinea/field/hidden").HiddenField<string>;
@@ -14957,9 +15685,9 @@ declare module 'alinea/field/link/ImageLink' {
14957
15685
  export function image<Fields = undefined>(label: Label, options?: WithoutLabel<ImageOptions<Fields>>): ImageField<Fields>;
14958
15686
  export interface ImagesField<Fields = undefined> extends LinksField<EntryReference & ListRow & InferStoredValue<Fields>, ImageLink<LinkFields<Type.Infer<Fields>>>> {
14959
15687
  }
15688
+ export interface ImagesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
15689
+ }
14960
15690
  export namespace image {
14961
- interface ImagesOptions<Fields> extends LinkFieldOptions<Array<EntryReference & ListRow & InferStoredValue<Fields>>>, Omit<EntryPickerOptions<Fields>, 'label' | 'selection'> {
14962
- }
14963
15691
  function multiple<Fields = undefined>(label: Label, options?: WithoutLabel<ImagesOptions<Fields>>): ImagesField<Fields>;
14964
15692
  }
14965
15693
 
@@ -16079,6 +16807,8 @@ declare module 'alinea/field/richtext/Toolbar' {
16079
16807
  export interface ToolbarMenu {
16080
16808
  icon?: (ctx: RichTextToolbarContext) => IconType;
16081
16809
  label?: ReactNode | ((ctx: RichTextToolbarContext) => ReactNode);
16810
+ /** Marks the menu trigger, eg. while a value other than the default applies */
16811
+ active?: (ctx: RichTextToolbarContext) => boolean;
16082
16812
  items: ToolbarConfig | ((ctx: RichTextToolbarContext) => ToolbarConfig);
16083
16813
  }
16084
16814
  export interface ToolbarGroup {
@@ -16103,6 +16833,7 @@ declare module 'alinea/field/richtext/Toolbar' {
16103
16833
  export const headings: {
16104
16834
  icon: () => import("react").JSX.Element;
16105
16835
  label({ editor }: RichTextToolbarContext): string;
16836
+ active: ({ editor }: RichTextToolbarContext) => boolean;
16106
16837
  items: {
16107
16838
  styles: {
16108
16839
  group: {
@@ -16112,22 +16843,27 @@ declare module 'alinea/field/richtext/Toolbar' {
16112
16843
  };
16113
16844
  h1: {
16114
16845
  label: () => import("react").JSX.Element;
16846
+ active: ({ editor }: RichTextToolbarContext) => boolean;
16115
16847
  onSelect: ({ exec }: RichTextToolbarContext) => boolean;
16116
16848
  };
16117
16849
  h2: {
16118
16850
  label: () => import("react").JSX.Element;
16851
+ active: ({ editor }: RichTextToolbarContext) => boolean;
16119
16852
  onSelect: ({ exec }: RichTextToolbarContext) => boolean;
16120
16853
  };
16121
16854
  h3: {
16122
16855
  label: () => import("react").JSX.Element;
16856
+ active: ({ editor }: RichTextToolbarContext) => boolean;
16123
16857
  onSelect: ({ exec }: RichTextToolbarContext) => boolean;
16124
16858
  };
16125
16859
  h4: {
16126
16860
  label: () => import("react").JSX.Element;
16861
+ active: ({ editor }: RichTextToolbarContext) => boolean;
16127
16862
  onSelect: ({ exec }: RichTextToolbarContext) => boolean;
16128
16863
  };
16129
16864
  h5: {
16130
16865
  label: () => import("react").JSX.Element;
16866
+ active: ({ editor }: RichTextToolbarContext) => boolean;
16131
16867
  onSelect: ({ exec }: RichTextToolbarContext) => boolean;
16132
16868
  };
16133
16869
  };
@@ -16192,11 +16928,11 @@ declare module 'alinea/field/richtext/Toolbar' {
16192
16928
  };
16193
16929
  export const alignment: {
16194
16930
  icon({ editor }: RichTextToolbarContext): import("react").JSX.Element;
16931
+ active: ({ editor }: RichTextToolbarContext) => boolean;
16195
16932
  items: {
16196
16933
  left: {
16197
16934
  icon: () => import("react").JSX.Element;
16198
16935
  label: string;
16199
- active: ({ editor }: RichTextToolbarContext) => boolean;
16200
16936
  onSelect: ({ exec }: RichTextToolbarContext) => boolean;
16201
16937
  };
16202
16938
  center: {
@@ -16524,6 +17260,7 @@ declare module 'alinea' {
16524
17260
  export * as Edit from 'alinea/edit';
16525
17261
  export * as Field from 'alinea/field';
16526
17262
  export * as Query from 'alinea/query';
17263
+ export { href } from 'alinea/dashboard/Href';
16527
17264
  export type * from 'alinea/types';
16528
17265
 
16529
17266
  }
@@ -16534,7 +17271,9 @@ declare module 'alinea/next' {
16534
17271
  export type { NextHandlerOptions } from 'alinea/adapter/next/handler';
16535
17272
  export { createCMS, createHandler } from 'alinea/adapter/next/node';
16536
17273
  export { withAlinea } from 'alinea/adapter/next/with-alinea';
17274
+ export type { RemovedEntry } from 'alinea/backend/CommitEntries';
16537
17275
  export type { AfterCommitContext, BeforeCommitContext } from 'alinea/backend/Handler';
17276
+ export type { DashboardEntry } from 'alinea/core/DashboardEntry';
16538
17277
 
16539
17278
  }
16540
17279
 
@@ -16542,7 +17281,9 @@ declare module 'alinea/next.edge' {
16542
17281
 
16543
17282
  export { createCMS, type SyncStatus } from 'alinea/adapter/next/cms';
16544
17283
  export { createHandler, type NextHandlerOptions } from 'alinea/adapter/next/handler';
17284
+ export type { RemovedEntry } from 'alinea/backend/CommitEntries';
16545
17285
  export type { AfterCommitContext, BeforeCommitContext } from 'alinea/backend/Handler';
17286
+ export type { DashboardEntry } from 'alinea/core/DashboardEntry';
16546
17287
 
16547
17288
  }
16548
17289
 
@@ -16555,7 +17296,7 @@ declare module 'alinea/picker/entry' {
16555
17296
  declare module 'alinea/picker/entry/EntryPicker' {
16556
17297
 
16557
17298
  import type { EntryFields } from 'alinea/core/EntryFields';
16558
- import type { OpenFilter } from 'alinea/core/Filter';
17299
+ import { type OpenFilter } from 'alinea/core/Filter';
16559
17300
  import type { Graph, Projection } from 'alinea/core/Graph';
16560
17301
  import type { Label } from 'alinea/core/Label';
16561
17302
  import type { Picker } from 'alinea/core/Picker';
@@ -16608,6 +17349,8 @@ declare module 'alinea/picker/entry/EntryPicker' {
16608
17349
  fields?: Definition | Type<Definition>;
16609
17350
  }
16610
17351
  export function entryPicker<Ref extends EntryReference, Fields>(options: EntryPickerOptions<Fields>): Picker<Ref, EntryPickerOptions<Fields>>;
17352
+ /** @internal Throws for conditions an entry picker can't check, eg. relation filters */
17353
+ export function assertPickerCondition(condition: OpenFilter<EntryFields>): void;
16611
17354
  export {};
16612
17355
 
16613
17356
  }
@@ -16819,21 +17562,23 @@ declare module 'alinea/preview/widget' {
16819
17562
 
16820
17563
  declare module 'alinea/query' {
16821
17564
 
17565
+ import type { Expr } from 'alinea/core/Expr';
16822
17566
  import type { GraphQuery, IncludeGuard, PickModifiers, QueryModifiers, SelectionGuard, TypeGuard } from 'alinea/core/Graph';
16823
- export const id: import("alinea/core/Expr").Expr<string>;
16824
- export const title: import("alinea/core/Expr").Expr<string>;
16825
- export const type: import("alinea/core/Expr").Expr<string>;
16826
- export const index: import("alinea/core/Expr").Expr<string>;
16827
- export const workspace: import("alinea/core/Expr").Expr<string>;
16828
- export const root: import("alinea/core/Expr").Expr<string>;
16829
- export const status: import("alinea/core/Expr").Expr<import("alinea/core/Entry").EntryStatus>;
16830
- export const parentId: import("alinea/core/Expr").Expr<string | null>;
16831
- export const locale: import("alinea/core/Expr").Expr<string | null>;
16832
- export const path: import("alinea/core/Expr").Expr<string>;
16833
- export const url: import("alinea/core/Expr").Expr<string>;
16834
- export const aliases: import("alinea/core/Expr").Expr<import("alinea/core/Entry").EntryAlias[]>;
16835
- export const createdAt: import("alinea/core/Expr").Expr<number | null>;
16836
- export const updatedAt: import("alinea/core/Expr").Expr<number | null>;
17567
+ import type { HasField } from 'alinea/core/Internal';
17568
+ export const id: Expr<string>;
17569
+ export const title: Expr<string>;
17570
+ export const type: Expr<string>;
17571
+ export const index: Expr<string>;
17572
+ export const workspace: Expr<string>;
17573
+ export const root: Expr<string>;
17574
+ export const status: Expr<import("alinea/core/Entry").EntryStatus>;
17575
+ export const parentId: Expr<string | null>;
17576
+ export const locale: Expr<string | null>;
17577
+ export const path: Expr<string>;
17578
+ export const url: Expr<string>;
17579
+ export const aliases: Expr<import("alinea/core/Entry").EntryAlias[]>;
17580
+ export const createdAt: Expr<number | null>;
17581
+ export const updatedAt: Expr<number | null>;
16837
17582
  export { snippet } from 'alinea/core/pages/Snippet';
16838
17583
  /** A relation query, see PickModifiers */
16839
17584
  type RelationQuery<Edge extends string, Selection, Type, Include, Modifiers> = {
@@ -16862,24 +17607,37 @@ declare module 'alinea/query' {
16862
17607
  export function parent<Selection extends SelectionGuard = undefined, Type extends TypeGuard = undefined, Include extends IncludeGuard = undefined, Modifiers extends QueryModifiers = {}>(query: GraphQuery<Selection, Type, Include> & Modifiers): RelationQuery<'parent', Selection, Type, Include, Modifiers>;
16863
17608
  export function next<Selection extends SelectionGuard = undefined, Type extends TypeGuard = undefined, Include extends IncludeGuard = undefined, Modifiers extends QueryModifiers = {}>(query: GraphQuery<Selection, Type, Include> & Modifiers): RelationQuery<'next', Selection, Type, Include, Modifiers>;
16864
17609
  export function previous<Selection extends SelectionGuard = undefined, Type extends TypeGuard = undefined, Include extends IncludeGuard = undefined, Modifiers extends QueryModifiers = {}>(query: GraphQuery<Selection, Type, Include> & Modifiers): RelationQuery<'previous', Selection, Type, Include, Modifiers>;
17610
+ /**
17611
+ * The entries that link to the entry, eg. the products of a brand:
17612
+ * `Query.referencedBy({type: Product})`. Set `field` to only follow one
17613
+ * link field, eg. `{type: Product, field: Product.brand}`.
17614
+ */
17615
+ export function referencedBy<Selection extends SelectionGuard = undefined, Type extends TypeGuard = undefined, Include extends IncludeGuard = undefined, Modifiers extends QueryModifiers = {}>(query: GraphQuery<Selection, Type, Include> & Modifiers & {
17616
+ field?: HasField & Expr;
17617
+ }): RelationQuery<'referencedBy', Selection, Type, Include, Modifiers> & {
17618
+ field?: HasField & Expr;
17619
+ };
16865
17620
 
16866
17621
  }
16867
17622
 
16868
17623
  declare module 'alinea/types' {
16869
17624
 
17625
+ export type { DashboardEntry, EntryActionProps } from 'alinea/core/DashboardEntry';
17626
+ export type { FieldOptions, WithoutLabel } from 'alinea/core/Field';
16870
17627
  export type { Filter } from 'alinea/core/Filter';
16871
- export type { Graph, GraphQuery } from 'alinea/core/Graph';
17628
+ export type { Graph, GraphQuery, QuerySettings } from 'alinea/core/Graph';
16872
17629
  export type { Infer } from 'alinea/core/Infer';
16873
17630
  export type { ListRow } from 'alinea/core/ListRow';
16874
- export type { OverviewActionProps, OverviewBuiltins, OverviewCellProps, OverviewColumn, OverviewEntry, OverviewFilter, OverviewFilterOption, OverviewFormatContext, OverviewOptions, OverviewSort, OverviewSortBy, OverviewSortDirection, OverviewSortOption } from 'alinea/core/Overview';
17631
+ export type { OverviewActionProps, OverviewBuiltins, OverviewCellProps, OverviewColumn, OverviewEntry, OverviewFilter, OverviewFilterContext, OverviewFilterOption, OverviewFilterOptions, OverviewFormatContext, OverviewHeaderProps, OverviewOptions, OverviewSort, OverviewSortBy, OverviewSortDirection, OverviewSortOption, OverviewTab } from 'alinea/core/Overview';
16875
17632
  export type { Root } from 'alinea/core/Root';
16876
17633
  export type { Schema } from 'alinea/core/Schema';
16877
17634
  export type { BlockNode, ElementNode, ImageNode, LinkMark, Mark, TextDoc, TextNode } from 'alinea/core/TextDoc';
16878
17635
  export type { EntryUrlMeta, Type } from 'alinea/core/Type';
16879
17636
  export type { Workspace } from 'alinea/core/Workspace';
16880
- export type { EntryLink } from 'alinea/field/link/EntryLink';
16881
- export type { FileLink } from 'alinea/field/link/FileLink';
16882
- export type { ImageLink } from 'alinea/field/link/ImageLink';
17637
+ export type { DashboardLocation } from 'alinea/dashboard/Href';
17638
+ export type { EntriesOptions, EntryLink, EntryOptions } from 'alinea/field/link/EntryLink';
17639
+ export type { FileLink, FileOptions, FilesOptions } from 'alinea/field/link/FileLink';
17640
+ export type { ImageLink, ImageOptions, ImagesOptions } from 'alinea/field/link/ImageLink';
16883
17641
  export type { Link } from 'alinea/field/link/Link';
16884
17642
  export type { UrlLink } from 'alinea/field/link/UrlLink';
16885
17643
  export type { EntryReference } from 'alinea/picker/entry/EntryReference';
@@ -17151,11 +17909,13 @@ declare module 'alinea/ui/Stack' {
17151
17909
  autoWidth?: boolean;
17152
17910
  }
17153
17911
  /**
17154
- * @deprecated Compatibility component for legacy dashboard extensions.
17912
+ * @deprecated Compatibility component for legacy dashboard extensions, use
17913
+ * `Stack` from `alinea/components`.
17155
17914
  */
17156
17915
  export const VStack: import("react").ForwardRefExoticComponent<StackProps & import("react").RefAttributes<HTMLElement>>;
17157
17916
  /**
17158
- * @deprecated Compatibility component for legacy dashboard extensions.
17917
+ * @deprecated Compatibility component for legacy dashboard extensions, use
17918
+ * `<Stack direction="row">` from `alinea/components`.
17159
17919
  */
17160
17920
  export const HStack: import("react").ForwardRefExoticComponent<StackProps & import("react").RefAttributes<HTMLElement>>;
17161
17921