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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/.mcp/later/dev.json +1 -1
  3. package/dist/.mcp/online/dev.json +1 -1
  4. package/dist/adapter/next/cms.js +16 -8
  5. package/dist/adapter/next/dashboardUrl.d.ts +6 -0
  6. package/dist/adapter/next/dashboardUrl.js +13 -0
  7. package/dist/adapter/next/syncCheck.d.ts +15 -2
  8. package/dist/adapter/next/syncCheck.js +26 -9
  9. package/dist/backend/Handler.js +5 -1
  10. package/dist/backend/api/BlobStore.d.ts +21 -0
  11. package/dist/backend/api/BlobStore.js +63 -0
  12. package/dist/backend/api/CreateBackend.js +10 -8
  13. package/dist/backend/api/DatabaseApi.d.ts +4 -1
  14. package/dist/backend/api/DatabaseApi.js +88 -4
  15. package/dist/backend/api/GithubApi.js +47 -24
  16. package/dist/backend/api/database.js +6 -6
  17. package/dist/bundled.d.ts +687 -248
  18. package/dist/chunks/{bun-sqlite-5SHXN3AT.js → bun-sqlite-RI6BYU5S.js} +3 -3
  19. package/dist/chunks/{chunk-YCQR5GRT.js → chunk-3QFSGTGF.js} +2 -3
  20. package/dist/chunks/{chunk-QJGGYF6E.js → chunk-4ZA6KZHS.js} +5 -5
  21. package/dist/chunks/{chunk-ASYKC6IE.js → chunk-5H4D5JQL.js} +1 -1
  22. package/dist/chunks/{chunk-ETWOY4AL.js → chunk-A2I6MCCS.js} +1 -1
  23. package/dist/chunks/{chunk-W5AXN7GH.js → chunk-CYVOGRSD.js} +1 -2
  24. package/dist/chunks/{chunk-TIJTT5ZI.js → chunk-G43W7AME.js} +1 -0
  25. package/dist/chunks/{chunk-RE523BQ7.js → chunk-HOETF6HN.js} +5829 -5830
  26. package/dist/chunks/{chunk-AIU6CMVT.js → chunk-PTYZMV5G.js} +1 -1
  27. package/dist/chunks/{chunk-JOF663EG.js → chunk-QHHMKJUO.js} +1 -1
  28. package/dist/chunks/{chunk-66YZ47TY.js → chunk-RAR44T5O.js} +1 -1
  29. package/dist/chunks/{chunk-QSN6RH6X.js → chunk-VA6NC3MI.js} +1 -1
  30. package/dist/chunks/{node-sqlite-XVNTNMCY.js → node-sqlite-C7VJ2NSN.js} +3 -3
  31. package/dist/cli/Mcp.js +1 -1
  32. package/dist/cli/Serve.js +1 -1
  33. package/dist/cli/bin.js +2 -3
  34. package/dist/cli/generate/GenerateDashboard.js +17 -5
  35. package/dist/cli/serve/mcp/DevMcp.js +1 -1
  36. package/dist/cli/serve/mcp/McpTools.js +29 -1
  37. package/dist/cli/static/dashboard/entry.js +2 -1
  38. package/dist/cloud/CloudRemote.js +1 -1
  39. package/dist/components/Alert.js +3 -3
  40. package/dist/components/AppShell.js +2 -2
  41. package/dist/components/Badge.d.ts +3 -1
  42. package/dist/components/Badge.js +5 -3
  43. package/dist/components/Breadcrumbs.js +6 -6
  44. package/dist/components/Button.d.ts +6 -1
  45. package/dist/components/Button.js +14 -5
  46. package/dist/components/Calendar.js +20 -20
  47. package/dist/components/Checkbox.js +9 -9
  48. package/dist/components/CheckboxGroup.js +4 -4
  49. package/dist/components/Collapsible.js +3 -3
  50. package/dist/components/ColorSwatch.js +2 -2
  51. package/dist/components/ColorSwatchPicker.js +2 -2
  52. package/dist/components/ComboBox.js +7 -7
  53. package/dist/components/Command.js +10 -9
  54. package/dist/components/ContentCard.d.ts +3 -1
  55. package/dist/components/ContentCard.js +39 -17
  56. package/dist/components/ContentGrid.js +15 -15
  57. package/dist/components/DataList.js +2 -2
  58. package/dist/components/DateField.js +2 -2
  59. package/dist/components/DatePicker.js +2 -2
  60. package/dist/components/DateRangePicker.js +2 -2
  61. package/dist/components/Dialog.js +6 -6
  62. package/dist/components/DropZone.js +4 -4
  63. package/dist/components/DropdownMenu.js +10 -10
  64. package/dist/components/Empty.js +4 -4
  65. package/dist/components/Field.js +8 -8
  66. package/dist/components/List.js +23 -23
  67. package/dist/components/MediaPreview.js +5 -5
  68. package/dist/components/MultipleSelect.js +20 -20
  69. package/dist/components/NavRail.js +4 -4
  70. package/dist/components/NumberField.js +5 -5
  71. package/dist/components/Page.js +5 -5
  72. package/dist/components/Popover.js +2 -2
  73. package/dist/components/PreviewFrame.js +5 -5
  74. package/dist/components/RadioGroup.js +6 -6
  75. package/dist/components/Resizable.js +5 -5
  76. package/dist/components/SearchField.d.ts +6 -1
  77. package/dist/components/SearchField.js +7 -5
  78. package/dist/components/Select.js +14 -14
  79. package/dist/components/Sheet.d.ts +50 -0
  80. package/dist/components/Sheet.js +202 -0
  81. package/dist/components/Sidebar.js +4 -4
  82. package/dist/components/SortableList.d.ts +34 -30
  83. package/dist/components/SortableList.js +162 -265
  84. package/dist/components/Spinner.js +1 -1
  85. package/dist/components/Surface.js +2 -2
  86. package/dist/components/Switch.js +7 -7
  87. package/dist/components/Table.d.ts +3 -1
  88. package/dist/components/Table.js +53 -42
  89. package/dist/components/Tabs.js +5 -5
  90. package/dist/components/TagGroup.js +9 -9
  91. package/dist/components/TextField.js +10 -10
  92. package/dist/components/TimeField.js +8 -8
  93. package/dist/components/Timestamp.js +1 -1
  94. package/dist/components/ToggleGroup.js +2 -2
  95. package/dist/components/Toolbar.js +4 -4
  96. package/dist/components/Tooltip.js +5 -5
  97. package/dist/components/Tree.js +29 -29
  98. package/dist/components/internal/DateControl.js +10 -10
  99. package/dist/components/internal/ListBoxOption.js +13 -13
  100. package/dist/components/internal/Locale.js +2 -2
  101. package/dist/components/internal/PopoverSurface.js +2 -2
  102. package/dist/components/internal/SelectionCheckbox.js +6 -6
  103. package/dist/components/internal/Slot.js +1 -1
  104. package/dist/components/internal/ToggleButton.js +3 -3
  105. package/dist/components/internal/Trigger.js +2 -2
  106. package/dist/components/internal/sortableDrag.d.ts +19 -0
  107. package/dist/components/internal/sortableDrag.js +175 -0
  108. package/dist/components/internal/useDragDrop.js +2 -2
  109. package/dist/components.d.ts +1 -0
  110. package/dist/components.js +1 -0
  111. package/dist/core/Config.d.ts +6 -1
  112. package/dist/core/Document.d.ts +9 -1
  113. package/dist/core/Document.js +31 -6
  114. package/dist/core/Overview.d.ts +2 -4
  115. package/dist/core/TextDoc.d.ts +4 -0
  116. package/dist/core/Validation.d.ts +0 -1
  117. package/dist/core/Validation.js +0 -6
  118. package/dist/core/field/RichTextField.js +23 -4
  119. package/dist/core/media/MediaTypes.js +1 -1
  120. package/dist/core/source/GithubSource.d.ts +4 -0
  121. package/dist/core/source/GithubSource.js +127 -25
  122. package/dist/core/source/GithubUsage.d.ts +21 -0
  123. package/dist/core/source/GithubUsage.js +47 -0
  124. package/dist/dashboard/App.d.ts +2 -2
  125. package/dist/dashboard/App.js +5 -3
  126. package/dist/dashboard/ViewKeys.d.ts +1 -0
  127. package/dist/dashboard/ViewKeys.js +1 -0
  128. package/dist/dashboard/app/ActivityStatus.js +11 -11
  129. package/dist/dashboard/app/AuthView.js +5 -5
  130. package/dist/dashboard/app/BlockSheet.d.ts +50 -0
  131. package/dist/dashboard/app/BlockSheet.js +207 -0
  132. package/dist/dashboard/app/BlockSheetStoryFrame.d.ts +6 -0
  133. package/dist/dashboard/app/BlockSheetStoryFrame.js +29 -0
  134. package/dist/dashboard/app/CompactField.js +15 -15
  135. package/dist/dashboard/app/CroppedImage.js +2 -2
  136. package/dist/dashboard/app/DashboardErrorBoundary.js +3 -3
  137. package/dist/dashboard/app/DashboardLayout.js +3 -1
  138. package/dist/dashboard/app/EntryHeader.js +32 -33
  139. package/dist/dashboard/app/EntryReferences.js +5 -5
  140. package/dist/dashboard/app/EntrySidebar.js +8 -8
  141. package/dist/dashboard/app/EntrySidebarPreview.js +4 -4
  142. package/dist/dashboard/app/EntryStatusIcon.d.ts +23 -0
  143. package/dist/dashboard/app/EntryStatusIcon.js +58 -0
  144. package/dist/dashboard/app/EntryValidationModal.d.ts +6 -5
  145. package/dist/dashboard/app/EntryValidationModal.js +18 -18
  146. package/dist/dashboard/app/Explorer.d.ts +3 -1
  147. package/dist/dashboard/app/Explorer.js +51 -47
  148. package/dist/dashboard/app/ExplorerBatchActions.d.ts +3 -1
  149. package/dist/dashboard/app/ExplorerBatchActions.js +68 -43
  150. package/dist/dashboard/app/ExplorerCards.js +7 -1
  151. package/dist/dashboard/app/ExplorerControls.js +6 -6
  152. package/dist/dashboard/app/ExplorerList.js +5 -5
  153. package/dist/dashboard/app/ExplorerModal.js +1 -1
  154. package/dist/dashboard/app/ExplorerTable.js +14 -11
  155. package/dist/dashboard/app/ImageEditor.js +25 -25
  156. package/dist/dashboard/app/ImagePicker.js +3 -13
  157. package/dist/dashboard/app/LinkPicker.js +29 -27
  158. package/dist/dashboard/app/LocaleMenu.js +7 -7
  159. package/dist/dashboard/app/MediaPreviewCell.js +2 -2
  160. package/dist/dashboard/app/MoveConfirmDialog.d.ts +5 -0
  161. package/dist/dashboard/app/MoveConfirmDialog.js +79 -0
  162. package/dist/dashboard/app/OverviewCell.js +8 -13
  163. package/dist/dashboard/app/ReadOnlyBadge.d.ts +1 -1
  164. package/dist/dashboard/app/RemoveDialog.d.ts +17 -0
  165. package/dist/dashboard/app/{DeleteDialog.js → RemoveDialog.js} +67 -27
  166. package/dist/dashboard/app/RemoveRedirect.d.ts +7 -0
  167. package/dist/dashboard/app/RemoveRedirect.js +123 -0
  168. package/dist/dashboard/app/SearchBar.d.ts +9 -0
  169. package/dist/dashboard/app/SearchBar.js +31 -0
  170. package/dist/dashboard/app/SidebarTree.js +29 -74
  171. package/dist/dashboard/app/WorkspaceMenu.js +8 -8
  172. package/dist/dashboard/app/WorkspaceRoots.js +11 -11
  173. package/dist/dashboard/app/editor/FileEditor.js +16 -16
  174. package/dist/dashboard/app/modals/CreateEntry.js +5 -5
  175. package/dist/dashboard/app/pages/EntryPage.d.ts +2 -1
  176. package/dist/dashboard/app/pages/EntryPage.js +35 -16
  177. package/dist/dashboard/app/pages/SplashPage.js +35 -35
  178. package/dist/dashboard/app/pages/UsersPage.js +14 -14
  179. package/dist/dashboard/app/ui/DashboardModal.js +17 -16
  180. package/dist/dashboard/atoms/dashboard.js +1 -1
  181. package/dist/dashboard/atoms/entry.js +1 -14
  182. package/dist/dashboard/atoms/entryTable.js +1 -0
  183. package/dist/dashboard/atoms/explorer.d.ts +4 -0
  184. package/dist/dashboard/atoms/explorer.js +19 -7
  185. package/dist/dashboard/atoms/graph.js +1 -1
  186. package/dist/dashboard/atoms/link.js +5 -2
  187. package/dist/dashboard/atoms/move.d.ts +5 -0
  188. package/dist/dashboard/atoms/move.js +29 -3
  189. package/dist/dashboard/atoms/moveConfirmation.d.ts +34 -0
  190. package/dist/dashboard/atoms/moveConfirmation.js +95 -0
  191. package/dist/dashboard/atoms/nav.d.ts +9 -5
  192. package/dist/dashboard/atoms/nav.js +120 -45
  193. package/dist/dashboard/atoms/overview.d.ts +6 -5
  194. package/dist/dashboard/atoms/overview.js +2 -16
  195. package/dist/dashboard/atoms/remove.d.ts +112 -0
  196. package/dist/dashboard/atoms/remove.js +268 -0
  197. package/dist/dashboard/atoms/root.d.ts +2 -2
  198. package/dist/dashboard/atoms/root.js +41 -26
  199. package/dist/dashboard/atoms/user.js +1 -1
  200. package/dist/dashboard/atoms/utils.js +1 -1
  201. package/dist/dashboard/boot/Boot.js +25 -17
  202. package/dist/dashboard/boot/BootProd.d.ts +6 -1
  203. package/dist/dashboard/boot/BootProd.js +2 -2
  204. package/dist/dashboard/boot/DashboardWorker.d.ts +1 -0
  205. package/dist/dashboard/boot/DashboardWorker.js +18 -18
  206. package/dist/dashboard/boot/LoadWorker.js +17 -10
  207. package/dist/dashboard/cms/EntryTable.js +6 -3
  208. package/dist/database/BrowserEntryStore.d.ts +17 -29
  209. package/dist/database/BrowserEntryStore.js +116 -144
  210. package/dist/database/DatabaseTables.js +2 -2
  211. package/dist/database/EntryDatabase.js +21 -1
  212. package/dist/database/EntryTransaction.js +0 -35
  213. package/dist/database/EntryUrlAliases.d.ts +4 -0
  214. package/dist/database/EntryUrlAliases.js +16 -10
  215. package/dist/database/driver/RuntimeDatabase.js +3 -3
  216. package/dist/database/driver/WasmDatabase.d.ts +5 -18
  217. package/dist/database/driver/WasmDatabase.js +15 -16
  218. package/dist/database/entry/EntryTable.js +2 -2
  219. package/dist/database/query/EntryQuery.js +1 -1
  220. package/dist/database/query/Relation.js +1 -1
  221. package/dist/database/query/Search.js +2 -2
  222. package/dist/field/check/CheckField.view.js +2 -2
  223. package/dist/field/code/CodeField.view.js +14 -14
  224. package/dist/field/link/EntryLink.d.ts +4 -4
  225. package/dist/field/link/FileLink.d.ts +4 -4
  226. package/dist/field/link/ImageLink.d.ts +4 -4
  227. package/dist/field/link/Link.d.ts +1 -1
  228. package/dist/field/link/LinkField.d.ts +5 -0
  229. package/dist/field/link/LinkField.view.js +246 -372
  230. package/dist/field/link/UrlLink.d.ts +4 -4
  231. package/dist/field/list/ListField.d.ts +2 -0
  232. package/dist/field/list/ListField.view.d.ts +15 -0
  233. package/dist/field/list/ListField.view.js +343 -301
  234. package/dist/field/metadata/MetadataField.d.ts +17 -7
  235. package/dist/field/metadata/MetadataField.js +14 -3
  236. package/dist/field/metadata/MetadataField.view.d.ts +1 -0
  237. package/dist/field/metadata/MetadataField.view.js +27 -23
  238. package/dist/field/richtext/RichTextBlock.d.ts +3 -1
  239. package/dist/field/richtext/RichTextBlock.js +178 -75
  240. package/dist/field/richtext/RichTextBlockHost.js +1 -1
  241. package/dist/field/richtext/RichTextExtensions.js +35 -35
  242. package/dist/field/richtext/RichTextField.view.js +14 -8
  243. package/dist/field/richtext/RichTextInsertMenu.js +43 -27
  244. package/dist/field/views.js +2 -0
  245. package/dist/index.css +490 -309
  246. package/dist/picker/url/UrlPicker.js +1 -0
  247. package/docs/components/badge.md +1 -0
  248. package/docs/components/button.md +1 -0
  249. package/docs/components/content-card.md +1 -0
  250. package/docs/components/search-field.md +1 -0
  251. package/docs/components/sortable-list.md +5 -20
  252. package/docs/components/table.md +1 -0
  253. package/docs/configuration.md +1 -1
  254. package/docs/deploy/self-host.md +1 -1
  255. package/docs/deploy.md +5 -4
  256. package/docs/fields.md +1 -1
  257. package/docs/index.md +1 -1
  258. package/docs/instant-publishing.md +7 -22
  259. package/docs/overviews.md +3 -4
  260. package/docs/upgrading.md +3 -2
  261. package/llms-full.txt +31 -55
  262. package/package.json +2 -3
  263. package/dist/core/db/EntryValidationError.d.ts +0 -13
  264. package/dist/core/db/EntryValidationError.js +0 -23
  265. package/dist/dashboard/app/DeleteDialog.d.ts +0 -11
  266. package/dist/dashboard/atoms/delete.d.ts +0 -68
  267. package/dist/dashboard/atoms/delete.js +0 -143
  268. package/dist/chunks/{chunk-MOWR65LM.js → chunk-PYDOMKJZ.js} +71 -71
@@ -7,12 +7,12 @@ import "../../chunks/chunk-CQ52GEHY.js";
7
7
  import { Config } from "alinea/core/Config";
8
8
  import { MoveOperation } from "alinea/core/db/Operation";
9
9
  import { Entry } from "alinea/core/Entry";
10
+ import { getRoot, getWorkspace } from "alinea/core/Internal";
10
11
  import { MediaFile } from "alinea/core/media/MediaTypes";
11
12
  import { Schema } from "alinea/core/Schema";
12
13
  import { Type } from "alinea/core/Type";
13
14
  import { entries } from "alinea/core/util/Objects";
14
15
  import { configAtom, graphAtom } from "./core.js";
15
- import { rootAtoms } from "./root.js";
16
16
  import { policyAtom } from "./user.js";
17
17
  function rootAcceptsType(config, rootData, typeName) {
18
18
  const contains = rootData.contains;
@@ -36,10 +36,34 @@ function moveTargets(config, policy, rootData, selected, translated) {
36
36
  const containers = entries(config.schema).filter(
37
37
  ([, parent]) => movable && !Type.isHidden(parent) && types.every((type) => type && Config.typeContains(config, parent, type))
38
38
  ).map(([name]) => name);
39
+ const canSelect = (item) => !moving.has(item.id) && !item.parents.some((parent) => moving.has(parent)) && (!translated || translated.has(item.id)) && policy.canMove(item);
39
40
  return {
40
41
  subjects,
41
42
  condition: { _type: { in: containers } },
42
- canSelect: (item) => !moving.has(item.id) && !item.parents.some((parent) => moving.has(parent)) && (!translated || translated.has(item.id)) && policy.canMove(item),
43
+ canSelect,
44
+ refusal(parent) {
45
+ if (!movable) return "These entries can't be moved.";
46
+ const single = subjects.length === 1;
47
+ const these = single ? `"${first.title}"` : "these entries";
48
+ const These = single ? these : "These entries";
49
+ const kinds = typeNames.map((name) => Type.label(config.schema[name])).join(" or ");
50
+ if (!parent) {
51
+ if (!typeNames.every((name) => rootAcceptsType(config, rootData, name)))
52
+ return `${rootData.label} doesn't hold ${kinds} at its top level.`;
53
+ if (!policy.canMove({ workspace: first.workspace, root: first.root }))
54
+ return `You can't move entries to the top level of ${rootData.label}.`;
55
+ return void 0;
56
+ }
57
+ if (moving.has(parent.id) || parent.parents.some((id) => moving.has(id)))
58
+ return `${These} can't be moved into itself.`;
59
+ if (!containers.includes(parent.type))
60
+ return `"${parent.title}" can't hold ${kinds}.`;
61
+ if (translated && !translated.has(parent.id))
62
+ return `"${parent.title}" doesn't exist in every language of ${these}.`;
63
+ if (!policy.canMove(parent))
64
+ return `You can't move entries into "${parent.title}".`;
65
+ return void 0;
66
+ },
43
67
  rootAccepts: movable && typeNames.every((name) => rootAcceptsType(config, rootData, name)) && policy.canMove({ workspace: first.workspace, root: first.root })
44
68
  };
45
69
  }
@@ -49,7 +73,9 @@ var loadMoveTargetsAtom = atom(
49
73
  const config = get(configAtom);
50
74
  const policy = get(policyAtom);
51
75
  const [{ workspace, root }] = subjects;
52
- const rootData = get(rootAtoms(workspace, root).data);
76
+ const rootData = getRoot(
77
+ getWorkspace(config.workspaces[workspace]).roots[root]
78
+ );
53
79
  const targets = moveTargets(config, policy, rootData, subjects);
54
80
  if (!rootData.i18n) return targets;
55
81
  const graph = get(graphAtom);
@@ -0,0 +1,34 @@
1
+ import type { DropTarget, Key } from 'alinea/components';
2
+ /**
3
+ * A dragged move to confirm: the pages it gives another parent, and so
4
+ * another url, or why it can not move there
5
+ */
6
+ export interface MoveConfirmation {
7
+ pages: Array<{
8
+ id: string;
9
+ title: string;
10
+ }>;
11
+ refusal?: string;
12
+ resolve(confirmed: boolean): void;
13
+ }
14
+ /** Whether moving pages asks first, until the editor stops it here */
15
+ export declare const confirmMovesAtom: import("jotai").WritableAtom<boolean, [boolean | typeof import("jotai/utils").RESET | ((prev: boolean) => boolean | typeof import("jotai/utils").RESET)], void>;
16
+ /**
17
+ * The entries being dragged, known from the start of a drag so that drop
18
+ * targets can turn down the ones they can not hold
19
+ */
20
+ export declare const draggedEntriesAtom: import("jotai").PrimitiveAtom<ReadonlySet<Key>> & {
21
+ init: ReadonlySet<Key>;
22
+ };
23
+ export declare const moveConfirmationAtom: import("jotai").PrimitiveAtom<MoveConfirmation | undefined> & {
24
+ init: MoveConfirmation | undefined;
25
+ };
26
+ /**
27
+ * Whether entries dropped on a target can move there, and the editor agrees:
28
+ * entries that get another parent follow the rules of moving them with the
29
+ * move dialog, and entries of a document type ask first, as their url and
30
+ * those of the pages below them change.
31
+ */
32
+ export declare const confirmMoveAtom: import("jotai").WritableAtom<null, [ids: Iterable<string>, target: DropTarget], Promise<boolean>> & {
33
+ init: null;
34
+ };
@@ -0,0 +1,95 @@
1
+ import {
2
+ atomWithStorage
3
+ } from "../../chunks/chunk-G43W7AME.js";
4
+ import {
5
+ atom
6
+ } from "../../chunks/chunk-CFNRVI3Y.js";
7
+ import "../../chunks/chunk-CQ52GEHY.js";
8
+
9
+ // src/dashboard/atoms/moveConfirmation.ts
10
+ import { isDocument } from "alinea/core/Document";
11
+ import { Entry } from "alinea/core/Entry";
12
+ import { configAtom, graphAtom } from "./core.js";
13
+ import { loadMoveTargetsAtom } from "./move.js";
14
+ import { policyAtom } from "./user.js";
15
+ var confirmMovesAtom = atomWithStorage(
16
+ "alinea-dashboard-confirm-moves",
17
+ true,
18
+ void 0,
19
+ { getOnInit: true }
20
+ );
21
+ var draggedEntriesAtom = atom(/* @__PURE__ */ new Set());
22
+ var moveConfirmationAtom = atom(
23
+ void 0
24
+ );
25
+ var moveItem = {
26
+ id: Entry.id,
27
+ title: Entry.title,
28
+ type: Entry.type,
29
+ workspace: Entry.workspace,
30
+ root: Entry.root,
31
+ locale: Entry.locale,
32
+ parentId: Entry.parentId,
33
+ parents: Entry.parents,
34
+ seeded: Entry.seeded
35
+ };
36
+ var confirmMoveAtom = atom(
37
+ null,
38
+ async (get, set, ids, target) => {
39
+ const graph = get(graphAtom);
40
+ const query = { status: "preferDraft", select: moveItem };
41
+ const [rows, dropped] = await Promise.all([
42
+ graph.find({ ...query, id: { in: [...ids] } }),
43
+ graph.first({ ...query, id: String(target.key) })
44
+ ]);
45
+ if (!dropped) return false;
46
+ const subjects = [...new Map(rows.map((row) => [row.id, row])).values()];
47
+ const parentId = target.position === "on" ? dropped.id : dropped.parentId;
48
+ const moved = subjects.filter(
49
+ (subject) => subject.parentId !== parentId || subject.workspace !== dropped.workspace || subject.root !== dropped.root
50
+ );
51
+ if (moved.length === 0) return true;
52
+ const ask = (question) => new Promise(
53
+ (resolve) => set(moveConfirmationAtom, {
54
+ ...question,
55
+ resolve(confirmed) {
56
+ set(moveConfirmationAtom, void 0);
57
+ resolve(confirmed);
58
+ }
59
+ })
60
+ );
61
+ const refusal = await moveRefusal(get, set, moved, dropped, parentId);
62
+ if (refusal) return ask({ pages: [], refusal }).then(() => false);
63
+ const { schema } = get(configAtom);
64
+ const pages = moved.filter((subject) => {
65
+ const type = schema[subject.type];
66
+ return type && isDocument(type);
67
+ });
68
+ if (pages.length === 0 || !get(confirmMovesAtom)) return true;
69
+ return ask({ pages: pages.map(({ id, title }) => ({ id, title })) });
70
+ }
71
+ );
72
+ async function moveRefusal(get, set, moved, dropped, parentId) {
73
+ const policy = get(policyAtom);
74
+ for (const subject of moved) {
75
+ if (subject.workspace !== dropped.workspace || subject.root !== dropped.root)
76
+ return "Entries can't be moved out of their root.";
77
+ if (subject.seeded)
78
+ return `"${subject.title}" is part of the site setup and can't be moved.`;
79
+ if (!policy.canMove(subject)) return `You can't move "${subject.title}".`;
80
+ }
81
+ const targets = await set(loadMoveTargetsAtom, moved);
82
+ if (parentId === null) return targets.refusal(null);
83
+ const parent = parentId === dropped.id ? dropped : await get(graphAtom).first({
84
+ id: parentId,
85
+ status: "preferDraft",
86
+ select: moveItem
87
+ });
88
+ return parent ? targets.refusal(parent) : "The new parent can't be found.";
89
+ }
90
+ export {
91
+ confirmMoveAtom,
92
+ confirmMovesAtom,
93
+ draggedEntriesAtom,
94
+ moveConfirmationAtom
95
+ };
@@ -1,4 +1,4 @@
1
- import { Atom, Getter } from 'jotai';
1
+ import { Atom, type Getter } from 'jotai';
2
2
  import type { OverviewSort } from 'alinea/core/Overview';
3
3
  import type { EntryDefaultView } from 'alinea/core/Type';
4
4
  import { ReactNode } from 'react';
@@ -12,7 +12,9 @@ export declare const routeBlockAtom: import("jotai").PrimitiveAtom<RouteBlock |
12
12
  init: RouteBlock | null;
13
13
  };
14
14
  export interface DashboardRoute {
15
- page?: 'splash' | 'entry' | 'users';
15
+ /** An edit link names an entry by its url, until it is looked up */
16
+ page?: 'splash' | 'entry' | 'users' | 'edit';
17
+ url?: string;
16
18
  workspace?: string;
17
19
  root?: string;
18
20
  entry?: string;
@@ -24,11 +26,13 @@ export interface DashboardRoute {
24
26
  replace?: boolean;
25
27
  }
26
28
  interface ResolvedDashboardRoute extends DashboardRoute {
27
- page: 'splash' | 'entry' | 'users';
29
+ page: 'splash' | 'entry' | 'users' | 'edit';
28
30
  }
29
31
  export declare const nav: {
30
32
  splash(): string;
31
33
  users(): string;
34
+ /** The editor of the entry at `url`, as linked from a page of the site */
35
+ edit(url: string, workspace?: string, root?: string): string;
32
36
  entry(workspace?: string, root?: string, entryId?: string, locale?: string | null, view?: EntryDefaultView, sort?: string): string;
33
37
  };
34
38
  /** The url parameter of an overview sort: the column, prefixed with - when descending */
@@ -40,11 +44,11 @@ interface NavigationRequest {
40
44
  route: ResolvedDashboardRoute;
41
45
  }
42
46
  /** @internal */
43
- export declare const routeAtom: import("jotai").WritableAtom<ResolvedDashboardRoute, [update: DashboardRoute | NavigationRequest], void> & {
47
+ export declare const routeAtom: import("jotai").WritableAtom<ResolvedDashboardRoute, [update: DashboardRoute | NavigationRequest], void | Promise<void>> & {
44
48
  onMount(navigate: (request: NavigationRequest) => void): (() => void) | undefined;
45
49
  };
46
50
  export interface Page {
47
- type: 'splash' | 'users' | 'entry';
51
+ type: 'splash' | 'users' | 'entry' | 'edit';
48
52
  workspace: string | undefined;
49
53
  root: string | undefined;
50
54
  requestedRoot?: string;
@@ -58,9 +58,10 @@ function atomWithLocation(options) {
58
58
  }
59
59
 
60
60
  // src/dashboard/atoms/nav.ts
61
+ import { Entry } from "alinea/core/Entry";
61
62
  import { getRoot } from "alinea/core/Internal";
62
63
  import { workspaceAtom, workspacesAtom } from "./config.js";
63
- import { configAtom } from "./core.js";
64
+ import { configAtom, graphAtom } from "./core.js";
64
65
  import { policyAtom } from "./user.js";
65
66
  import { dispense } from "./utils.js";
66
67
  var routeGuardAtom = atom(null);
@@ -72,6 +73,13 @@ var nav = {
72
73
  users() {
73
74
  return "/users";
74
75
  },
76
+ /** The editor of the entry at `url`, as linked from a page of the site */
77
+ edit(url, workspace, root) {
78
+ const params = new URLSearchParams({ url });
79
+ if (workspace) params.set("workspace", workspace);
80
+ if (root) params.set("root", root);
81
+ return `/edit?${params}`;
82
+ },
75
83
  entry(workspace, root, entryId, locale, view, sort) {
76
84
  const rootPart = root ? `${root}${locale ? `:${locale}` : ""}` : "";
77
85
  const path = `/entry/${[workspace, rootPart, entryId].filter(Boolean).join("/")}`;
@@ -100,9 +108,17 @@ var overviewSortMemoryAtom = atom(/* @__PURE__ */ new Map());
100
108
  function routeFromHash(hash) {
101
109
  const [path, search = ""] = hash.slice(1).split("?");
102
110
  const [action, workspace, rootPart = "", entry] = path.split("/").slice(1);
111
+ const params = new URLSearchParams(search);
112
+ const url = params.get("url");
113
+ if (action === "edit" && url)
114
+ return {
115
+ page: "edit",
116
+ url,
117
+ workspace: params.get("workspace") ?? void 0,
118
+ root: params.get("root") ?? void 0
119
+ };
103
120
  const page2 = action === "users" ? "users" : action === "entry" ? "entry" : "splash";
104
121
  const [root, locale] = rootPart.split(":");
105
- const params = new URLSearchParams(search);
106
122
  const view = params.get("view");
107
123
  const sort = params.get("sort");
108
124
  return {
@@ -118,6 +134,13 @@ function routeFromHash(hash) {
118
134
  function routeFromUpdate(update) {
119
135
  if (update.page === "users") return { page: "users" };
120
136
  if (update.page === "splash") return { page: "splash" };
137
+ if (update.page === "edit")
138
+ return update.url ? {
139
+ page: "edit",
140
+ url: update.url,
141
+ workspace: update.workspace,
142
+ root: update.root
143
+ } : { page: "splash" };
121
144
  return {
122
145
  page: "entry",
123
146
  workspace: update.workspace,
@@ -129,6 +152,8 @@ function routeFromUpdate(update) {
129
152
  };
130
153
  }
131
154
  function hashFromRoute(route) {
155
+ if (route.page === "edit")
156
+ return `#${nav.edit(route.url ?? "/", route.workspace, route.root)}`;
132
157
  return route.page === "splash" ? `#${nav.splash()}` : route.page === "users" ? `#${nav.users()}` : `#${nav.entry(
133
158
  route.workspace,
134
159
  route.root,
@@ -155,6 +180,7 @@ var initialRoute = routeFromHash(
155
180
  );
156
181
  var currentRouteAtom = atom(initialRoute);
157
182
  var ignoredBrowserHash;
183
+ var linkLookups = 0;
158
184
  var routeAtom = Object.assign(
159
185
  atom(
160
186
  (get) => get(currentRouteAtom),
@@ -163,58 +189,31 @@ var routeAtom = Object.assign(
163
189
  route: routeFromUpdate(update),
164
190
  replace: update.replace
165
191
  };
166
- let { route } = request;
167
- const key = sortKey(get, request);
168
- const explicit = request.browser || route.sort !== void 0 || "sort" in update;
169
- if (key && !explicit) {
170
- const sort = get(overviewSortMemoryAtom).get(key);
171
- if (sort) route = { ...route, sort: formatOverviewSort(sort) };
172
- }
173
- const previous = get(currentRouteAtom);
174
- const commit = (replace = request.replace ?? false, syncLocation = !request.browser) => {
175
- set(currentRouteAtom, route);
176
- if (key)
177
- set(
178
- overviewSortMemoryAtom,
179
- (memory) => remember(memory, key, route.sort)
180
- );
181
- if (!syncLocation) return;
182
- set(
183
- locationAtom,
184
- (location) => ({ ...location, hash: hashFromRoute(route) }),
185
- { replace }
186
- );
187
- };
188
- const guard = get(routeGuardAtom);
189
- if (guard && get(guard)) {
190
- if (request.browser) {
191
- ignoredBrowserHash = hashFromRoute(previous);
192
- set(
193
- locationAtom,
194
- (location) => ({ ...location, hash: hashFromRoute(previous) }),
195
- { replace: true }
196
- );
197
- }
198
- set(routeBlockAtom, {
199
- confirm() {
200
- set(routeBlockAtom, null);
201
- commit(request.browser, true);
202
- }
203
- });
204
- return;
205
- }
206
- commit();
192
+ if (request.route.page !== "edit")
193
+ return navigate(get, set, request, "sort" in update);
194
+ const current = get(currentRouteAtom);
195
+ const lookup = ++linkLookups;
196
+ set(
197
+ locationAtom,
198
+ (location) => ({ ...location, hash: hashFromRoute(current) }),
199
+ { replace: true }
200
+ );
201
+ return linkedRoute(get, request.route).catch(() => ({ page: "splash" })).then((route) => {
202
+ if (lookup !== linkLookups || get(currentRouteAtom) !== current)
203
+ return;
204
+ navigate(get, set, { route, replace: true }, true);
205
+ });
207
206
  }
208
207
  ),
209
208
  {
210
- onMount(navigate) {
209
+ onMount(navigate2) {
211
210
  if (typeof window === "undefined") return;
212
211
  const onNavigation = () => {
213
212
  if (window.location.hash === ignoredBrowserHash) {
214
213
  ignoredBrowserHash = void 0;
215
214
  return;
216
215
  }
217
- navigate({ browser: true, route: routeFromHash(window.location.hash) });
216
+ navigate2({ browser: true, route: routeFromHash(window.location.hash) });
218
217
  };
219
218
  window.addEventListener("hashchange", onNavigation);
220
219
  window.addEventListener("popstate", onNavigation);
@@ -226,6 +225,73 @@ var routeAtom = Object.assign(
226
225
  }
227
226
  }
228
227
  );
228
+ function navigate(get, set, request, sortGiven) {
229
+ let { route } = request;
230
+ const key = sortKey(get, request);
231
+ const explicit = request.browser || route.sort !== void 0 || sortGiven;
232
+ if (key && !explicit) {
233
+ const sort = get(overviewSortMemoryAtom).get(key);
234
+ if (sort) route = { ...route, sort: formatOverviewSort(sort) };
235
+ }
236
+ const previous = get(currentRouteAtom);
237
+ const commit = (replace = request.replace ?? false, syncLocation = !request.browser) => {
238
+ set(currentRouteAtom, route);
239
+ if (key)
240
+ set(overviewSortMemoryAtom, (memory) => remember(memory, key, route.sort));
241
+ if (!syncLocation) return;
242
+ set(locationAtom, (location) => ({ ...location, hash: hashFromRoute(route) }), {
243
+ replace
244
+ });
245
+ };
246
+ const guard = get(routeGuardAtom);
247
+ if (guard && get(guard)) {
248
+ if (request.browser) {
249
+ ignoredBrowserHash = hashFromRoute(previous);
250
+ set(
251
+ locationAtom,
252
+ (location) => ({ ...location, hash: hashFromRoute(previous) }),
253
+ { replace: true }
254
+ );
255
+ }
256
+ set(routeBlockAtom, {
257
+ confirm() {
258
+ set(routeBlockAtom, null);
259
+ commit(request.browser, true);
260
+ }
261
+ });
262
+ return;
263
+ }
264
+ commit();
265
+ }
266
+ async function linkedRoute(get, { url = "/", workspace, root }) {
267
+ const graph = get(graphAtom);
268
+ const query = {
269
+ workspace,
270
+ root,
271
+ status: "preferDraft",
272
+ select: {
273
+ entry: Entry.id,
274
+ locale: Entry.locale,
275
+ workspace: Entry.workspace,
276
+ root: Entry.root
277
+ }
278
+ };
279
+ const urls = [.../* @__PURE__ */ new Set([withoutSlash(url), withoutSlash(decoded(url))])];
280
+ const live = { ...query, filter: { _status: { isNot: "archived" } } };
281
+ const found = await graph.first({ ...live, url: { in: urls } }) ?? await graph.first({ ...live, alias: { in: urls } }) ?? await graph.first({ ...query, url: { in: urls } });
282
+ if (!found) return { page: "splash" };
283
+ return { page: "entry", ...found, locale: found.locale ?? void 0 };
284
+ }
285
+ function decoded(url) {
286
+ try {
287
+ return decodeURI(url);
288
+ } catch {
289
+ return url;
290
+ }
291
+ }
292
+ function withoutSlash(url) {
293
+ return url.length > 1 && url.endsWith("/") ? url.slice(0, -1) : url;
294
+ }
229
295
  function sortKey(get, { browser, route }) {
230
296
  if (route.page !== "entry") return void 0;
231
297
  if (route.workspace && route.root)
@@ -244,6 +310,15 @@ function remember(memory, key, value) {
244
310
  }
245
311
  var pageAtom = atom((get) => resolvePage(get, get(routeAtom)));
246
312
  function resolvePage(get, route) {
313
+ if (route.page === "edit")
314
+ return {
315
+ type: "edit",
316
+ workspace: void 0,
317
+ root: void 0,
318
+ entry: void 0,
319
+ locale: null,
320
+ view: void 0
321
+ };
247
322
  const config = get(configAtom);
248
323
  const policy = get(policyAtom);
249
324
  const workspaces = get(workspacesAtom);
@@ -11,7 +11,7 @@ import { type RootData } from 'alinea/core/Root';
11
11
  import { Type } from 'alinea/core/Type';
12
12
  import type { View } from 'alinea/core/View';
13
13
  /** The columns every overview can show next to its configured columns */
14
- export type OverviewBuiltinColumn = 'type' | 'status' | 'updated' | 'author';
14
+ export type OverviewBuiltinColumn = 'type' | 'updated' | 'author';
15
15
  /** The number of `overview: true` fields shown when a parent has no columns */
16
16
  export declare const overviewFieldColumnLimit = 5;
17
17
  /** Where a list of entries lives, its overview is configured there */
@@ -109,7 +109,7 @@ export interface OverviewResolveOptions {
109
109
  */
110
110
  mixed?: boolean;
111
111
  /**
112
- * The listed children grouped by type and status, see `summarizeRows`.
112
+ * The listed children grouped by type, see `summarizeRows`.
113
113
  * The columns then follow the children: the types present, and built-in
114
114
  * columns only when they tell the children apart.
115
115
  */
@@ -158,6 +158,8 @@ export interface OverviewRow {
158
158
  path: string;
159
159
  url?: string;
160
160
  status?: EntryStatus;
161
+ /** The version is the main one, a draft then was never published */
162
+ main?: boolean;
161
163
  locale: string | null;
162
164
  workspace: string;
163
165
  root: string;
@@ -186,17 +188,16 @@ export declare function linkedEntryId(value: unknown): string | undefined;
186
188
  export declare const openEntryAtom: import("jotai").WritableAtom<null, [id: string, locale: string | null], Promise<void>> & {
187
189
  init: null;
188
190
  };
189
- /** The listed children that share a type and status */
191
+ /** The listed children that share a type */
190
192
  export interface OverviewChildren {
191
193
  type: string;
192
- status: EntryStatus;
193
194
  /** Some of these children store when they were last edited */
194
195
  updatedAt: boolean;
195
196
  /** Some of these children store who last edited them */
196
197
  updatedBy: boolean;
197
198
  }
198
199
  /**
199
- * Groups rows by type and status, noting whether they store audit
200
+ * Groups rows by type, noting whether they store audit
200
201
  * metadata. Lists load every child of their parent and searches their
201
202
  * shown results, so this covers every row and the columns stay put.
202
203
  */
@@ -23,7 +23,6 @@ import { graphAtom } from "./core.js";
23
23
  import { routeAtom } from "./nav.js";
24
24
  var builtinColumns = [
25
25
  "type",
26
- "status",
27
26
  "updated",
28
27
  "author"
29
28
  ];
@@ -49,15 +48,6 @@ function builtinColumn(key) {
49
48
  collapsible: true,
50
49
  sortBy: Entry.type
51
50
  };
52
- case "status":
53
- return {
54
- key,
55
- builtin: key,
56
- header: "Status",
57
- width: 120,
58
- collapsible: true,
59
- sortBy: Entry.status
60
- };
61
51
  case "updated":
62
52
  return {
63
53
  key,
@@ -129,7 +119,6 @@ function resolveOverviewOptions(config, overview, types, sort, options = {}) {
129
119
  const audited = childTypes.some(hasAuditMetadata);
130
120
  const defaults = {
131
121
  type: children ? new Set(children.map((group) => group.type)).size > 1 : Boolean(options.mixed) || listed.length > 1,
132
- status: children ? new Set(children.map((group) => group.status)).size > 1 : true,
133
122
  updated: children ? children.some((group) => group.updatedAt) : audited,
134
123
  author: children ? children.some((group) => group.updatedBy) : audited
135
124
  };
@@ -456,17 +445,14 @@ var openEntryAtom = atom(
456
445
  function summarizeRows(rows) {
457
446
  const groups = /* @__PURE__ */ new Map();
458
447
  for (const row of rows) {
459
- const status = row.status ?? "published";
460
- const key = `${row.type}\0${status}`;
461
- const group = groups.get(key) ?? {
448
+ const group = groups.get(row.type) ?? {
462
449
  type: row.type,
463
- status,
464
450
  updatedAt: false,
465
451
  updatedBy: false
466
452
  };
467
453
  group.updatedAt ||= hasAuditValue(row, "updatedAt");
468
454
  group.updatedBy ||= hasAuditValue(row, "updatedBy");
469
- groups.set(key, group);
455
+ groups.set(row.type, group);
470
456
  }
471
457
  return [...groups.values()];
472
458
  }