alinea 2.0.0-preview.12 → 2.0.0-preview.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/.mcp/later/dev.json +1 -1
  3. package/dist/.mcp/online/dev.json +1 -1
  4. package/dist/adapter/next/cms.js +16 -8
  5. package/dist/adapter/next/dashboardUrl.d.ts +6 -0
  6. package/dist/adapter/next/dashboardUrl.js +13 -0
  7. package/dist/adapter/next/syncCheck.d.ts +15 -2
  8. package/dist/adapter/next/syncCheck.js +26 -9
  9. package/dist/backend/Handler.js +5 -1
  10. package/dist/backend/api/BlobStore.d.ts +21 -0
  11. package/dist/backend/api/BlobStore.js +63 -0
  12. package/dist/backend/api/CreateBackend.js +10 -8
  13. package/dist/backend/api/DatabaseApi.d.ts +4 -1
  14. package/dist/backend/api/DatabaseApi.js +88 -4
  15. package/dist/backend/api/GithubApi.js +47 -24
  16. package/dist/backend/api/database.js +6 -6
  17. package/dist/bundled.d.ts +676 -280
  18. package/dist/chunks/{bun-sqlite-5SHXN3AT.js → bun-sqlite-RI6BYU5S.js} +3 -3
  19. package/dist/chunks/{chunk-OHGAVV4G.js → chunk-3QFSGTGF.js} +2 -3
  20. package/dist/chunks/{chunk-QJGGYF6E.js → chunk-4ZA6KZHS.js} +5 -5
  21. package/dist/chunks/{chunk-ASYKC6IE.js → chunk-5H4D5JQL.js} +1 -1
  22. package/dist/chunks/{chunk-ETWOY4AL.js → chunk-A2I6MCCS.js} +1 -1
  23. package/dist/chunks/{chunk-W5AXN7GH.js → chunk-CYVOGRSD.js} +1 -2
  24. package/dist/chunks/{chunk-TIJTT5ZI.js → chunk-G43W7AME.js} +1 -0
  25. package/dist/chunks/{chunk-RE523BQ7.js → chunk-HOETF6HN.js} +5829 -5830
  26. package/dist/chunks/{chunk-AIU6CMVT.js → chunk-PTYZMV5G.js} +1 -1
  27. package/dist/chunks/{chunk-JOF663EG.js → chunk-QHHMKJUO.js} +1 -1
  28. package/dist/chunks/{chunk-66YZ47TY.js → chunk-RAR44T5O.js} +1 -1
  29. package/dist/chunks/{chunk-QSN6RH6X.js → chunk-VA6NC3MI.js} +1 -1
  30. package/dist/chunks/{node-sqlite-XVNTNMCY.js → node-sqlite-C7VJ2NSN.js} +3 -3
  31. package/dist/cli/Mcp.js +1 -1
  32. package/dist/cli/Serve.js +1 -1
  33. package/dist/cli/bin.js +2 -3
  34. package/dist/cli/serve/mcp/DevMcp.js +1 -1
  35. package/dist/cli/serve/mcp/McpTools.js +29 -1
  36. package/dist/cloud/CloudRemote.js +1 -1
  37. package/dist/components/Alert.js +2 -2
  38. package/dist/components/AppShell.js +2 -2
  39. package/dist/components/Badge.d.ts +3 -1
  40. package/dist/components/Badge.js +5 -3
  41. package/dist/components/Breadcrumbs.js +7 -7
  42. package/dist/components/Button.d.ts +6 -1
  43. package/dist/components/Button.js +16 -7
  44. package/dist/components/Calendar.js +20 -20
  45. package/dist/components/Checkbox.js +7 -7
  46. package/dist/components/CheckboxGroup.js +4 -4
  47. package/dist/components/Code.js +2 -2
  48. package/dist/components/Collapsible.js +3 -3
  49. package/dist/components/ColorSwatch.js +2 -2
  50. package/dist/components/ColorSwatchPicker.js +2 -2
  51. package/dist/components/ComboBox.js +7 -7
  52. package/dist/components/Command.js +14 -13
  53. package/dist/components/ContentCard.d.ts +3 -1
  54. package/dist/components/ContentCard.js +46 -24
  55. package/dist/components/ContentGrid.js +17 -17
  56. package/dist/components/DataList.js +2 -2
  57. package/dist/components/DateField.js +2 -2
  58. package/dist/components/DatePicker.js +2 -2
  59. package/dist/components/DateRangePicker.js +2 -2
  60. package/dist/components/Dialog.js +7 -7
  61. package/dist/components/DropZone.js +6 -6
  62. package/dist/components/DropdownMenu.js +9 -9
  63. package/dist/components/Empty.js +5 -5
  64. package/dist/components/Field.js +8 -8
  65. package/dist/components/List.js +27 -27
  66. package/dist/components/MediaPreview.js +5 -5
  67. package/dist/components/MultipleSelect.js +22 -22
  68. package/dist/components/NavRail.js +4 -4
  69. package/dist/components/NumberField.js +4 -4
  70. package/dist/components/Page.js +3 -3
  71. package/dist/components/Popover.js +2 -2
  72. package/dist/components/PreviewFrame.js +9 -9
  73. package/dist/components/RadioGroup.js +9 -9
  74. package/dist/components/Resizable.js +4 -4
  75. package/dist/components/SearchField.d.ts +6 -1
  76. package/dist/components/SearchField.js +5 -3
  77. package/dist/components/Select.js +13 -13
  78. package/dist/components/Sheet.d.ts +50 -0
  79. package/dist/components/Sheet.js +202 -0
  80. package/dist/components/Sidebar.js +4 -4
  81. package/dist/components/SortableList.d.ts +34 -30
  82. package/dist/components/SortableList.js +161 -264
  83. package/dist/components/Spinner.js +2 -2
  84. package/dist/components/Surface.js +2 -2
  85. package/dist/components/Switch.js +8 -8
  86. package/dist/components/Table.d.ts +3 -1
  87. package/dist/components/Table.js +57 -46
  88. package/dist/components/Tabs.js +5 -5
  89. package/dist/components/TagGroup.js +8 -8
  90. package/dist/components/TextField.js +10 -10
  91. package/dist/components/TimeField.js +6 -6
  92. package/dist/components/Timestamp.js +1 -1
  93. package/dist/components/ToggleGroup.js +2 -2
  94. package/dist/components/Toolbar.js +2 -2
  95. package/dist/components/Tooltip.js +5 -5
  96. package/dist/components/Tree.js +24 -24
  97. package/dist/components/internal/DateControl.js +12 -12
  98. package/dist/components/internal/ListBoxOption.js +6 -6
  99. package/dist/components/internal/Locale.js +2 -2
  100. package/dist/components/internal/PopoverSurface.js +4 -4
  101. package/dist/components/internal/SelectionCheckbox.js +5 -5
  102. package/dist/components/internal/Slot.js +1 -1
  103. package/dist/components/internal/ToggleButton.js +3 -3
  104. package/dist/components/internal/Trigger.js +2 -2
  105. package/dist/components/internal/sortableDrag.d.ts +19 -0
  106. package/dist/components/internal/sortableDrag.js +175 -0
  107. package/dist/components/internal/useDragDrop.js +2 -2
  108. package/dist/components.d.ts +1 -0
  109. package/dist/components.js +1 -0
  110. package/dist/core/Config.d.ts +6 -1
  111. package/dist/core/Document.d.ts +9 -1
  112. package/dist/core/Document.js +31 -6
  113. package/dist/core/Overview.d.ts +2 -4
  114. package/dist/core/TextDoc.d.ts +4 -0
  115. package/dist/core/Validation.d.ts +0 -1
  116. package/dist/core/Validation.js +0 -6
  117. package/dist/core/field/RichTextField.js +23 -4
  118. package/dist/core/media/MediaTypes.js +1 -1
  119. package/dist/core/source/GithubSource.d.ts +4 -0
  120. package/dist/core/source/GithubSource.js +127 -25
  121. package/dist/core/source/GithubUsage.d.ts +21 -0
  122. package/dist/core/source/GithubUsage.js +47 -0
  123. package/dist/dashboard/App.d.ts +2 -2
  124. package/dist/dashboard/App.js +5 -3
  125. package/dist/dashboard/ViewKeys.d.ts +1 -0
  126. package/dist/dashboard/ViewKeys.js +1 -0
  127. package/dist/dashboard/app/ActivityStatus.js +7 -7
  128. package/dist/dashboard/app/AuthView.js +5 -5
  129. package/dist/dashboard/app/BlockSheet.d.ts +50 -0
  130. package/dist/dashboard/app/BlockSheet.js +207 -0
  131. package/dist/dashboard/app/BlockSheetStoryFrame.d.ts +6 -0
  132. package/dist/dashboard/app/BlockSheetStoryFrame.js +29 -0
  133. package/dist/dashboard/app/CompactField.js +16 -16
  134. package/dist/dashboard/app/DashboardLayout.js +3 -1
  135. package/dist/dashboard/app/EntryFields.js +2 -2
  136. package/dist/dashboard/app/EntryHeader.js +33 -34
  137. package/dist/dashboard/app/EntryReferences.js +7 -7
  138. package/dist/dashboard/app/EntrySidebar.js +15 -15
  139. package/dist/dashboard/app/EntrySidebarPreview.js +3 -3
  140. package/dist/dashboard/app/EntryStatusIcon.d.ts +23 -0
  141. package/dist/dashboard/app/EntryStatusIcon.js +58 -0
  142. package/dist/dashboard/app/EntryValidationModal.d.ts +6 -5
  143. package/dist/dashboard/app/EntryValidationModal.js +20 -20
  144. package/dist/dashboard/app/Explorer.d.ts +3 -1
  145. package/dist/dashboard/app/Explorer.js +48 -44
  146. package/dist/dashboard/app/ExplorerBatchActions.d.ts +3 -1
  147. package/dist/dashboard/app/ExplorerBatchActions.js +68 -43
  148. package/dist/dashboard/app/ExplorerCards.js +7 -1
  149. package/dist/dashboard/app/ExplorerControls.js +6 -6
  150. package/dist/dashboard/app/ExplorerList.js +5 -5
  151. package/dist/dashboard/app/ExplorerModal.js +2 -2
  152. package/dist/dashboard/app/ExplorerTable.js +11 -8
  153. package/dist/dashboard/app/ImageEditor.js +28 -28
  154. package/dist/dashboard/app/ImagePicker.js +3 -13
  155. package/dist/dashboard/app/LinkPicker.js +28 -26
  156. package/dist/dashboard/app/LocaleMenu.js +4 -4
  157. package/dist/dashboard/app/MediaPreviewCell.js +1 -1
  158. package/dist/dashboard/app/MoveConfirmDialog.d.ts +5 -0
  159. package/dist/dashboard/app/MoveConfirmDialog.js +79 -0
  160. package/dist/dashboard/app/OverviewCell.js +8 -13
  161. package/dist/dashboard/app/PendingUploadsView.js +3 -3
  162. package/dist/dashboard/app/ReadOnlyBadge.d.ts +1 -1
  163. package/dist/dashboard/app/RemoveDialog.d.ts +17 -0
  164. package/dist/dashboard/app/{DeleteDialog.js → RemoveDialog.js} +67 -27
  165. package/dist/dashboard/app/RemoveRedirect.d.ts +7 -0
  166. package/dist/dashboard/app/RemoveRedirect.js +123 -0
  167. package/dist/dashboard/app/SearchBar.d.ts +9 -0
  168. package/dist/dashboard/app/SearchBar.js +31 -0
  169. package/dist/dashboard/app/SidebarTree.js +27 -72
  170. package/dist/dashboard/app/WorkspaceMenu.js +7 -7
  171. package/dist/dashboard/app/WorkspaceRoots.js +12 -12
  172. package/dist/dashboard/app/editor/FileEditor.js +14 -14
  173. package/dist/dashboard/app/modals/CreateEntry.js +3 -3
  174. package/dist/dashboard/app/pages/EntryPage.d.ts +2 -1
  175. package/dist/dashboard/app/pages/EntryPage.js +34 -15
  176. package/dist/dashboard/app/pages/SplashPage.js +37 -37
  177. package/dist/dashboard/app/pages/UsersPage.js +12 -12
  178. package/dist/dashboard/app/ui/DashboardModal.js +15 -14
  179. package/dist/dashboard/atoms/dashboard.js +1 -1
  180. package/dist/dashboard/atoms/entry.js +1 -14
  181. package/dist/dashboard/atoms/entryTable.js +1 -0
  182. package/dist/dashboard/atoms/explorer.d.ts +4 -0
  183. package/dist/dashboard/atoms/explorer.js +19 -7
  184. package/dist/dashboard/atoms/graph.js +1 -1
  185. package/dist/dashboard/atoms/link.js +5 -2
  186. package/dist/dashboard/atoms/move.d.ts +5 -0
  187. package/dist/dashboard/atoms/move.js +29 -3
  188. package/dist/dashboard/atoms/moveConfirmation.d.ts +34 -0
  189. package/dist/dashboard/atoms/moveConfirmation.js +95 -0
  190. package/dist/dashboard/atoms/nav.d.ts +9 -5
  191. package/dist/dashboard/atoms/nav.js +120 -45
  192. package/dist/dashboard/atoms/overview.d.ts +6 -5
  193. package/dist/dashboard/atoms/overview.js +2 -16
  194. package/dist/dashboard/atoms/remove.d.ts +112 -0
  195. package/dist/dashboard/atoms/remove.js +268 -0
  196. package/dist/dashboard/atoms/root.d.ts +2 -2
  197. package/dist/dashboard/atoms/root.js +41 -26
  198. package/dist/dashboard/atoms/user.js +1 -1
  199. package/dist/dashboard/atoms/utils.js +1 -1
  200. package/dist/dashboard/boot/Boot.js +25 -17
  201. package/dist/dashboard/boot/DashboardWorker.d.ts +1 -0
  202. package/dist/dashboard/boot/DashboardWorker.js +10 -46
  203. package/dist/dashboard/boot/LoadWorker.js +17 -10
  204. package/dist/dashboard/cms/EntryTable.js +8 -5
  205. package/dist/database/BrowserEntryStore.d.ts +12 -62
  206. package/dist/database/BrowserEntryStore.js +97 -196
  207. package/dist/database/DatabaseTables.js +2 -2
  208. package/dist/database/EntryDatabase.js +21 -1
  209. package/dist/database/EntryTransaction.js +0 -35
  210. package/dist/database/EntryUrlAliases.d.ts +4 -0
  211. package/dist/database/EntryUrlAliases.js +16 -10
  212. package/dist/database/driver/RuntimeDatabase.js +3 -3
  213. package/dist/database/driver/WasmDatabase.d.ts +5 -18
  214. package/dist/database/driver/WasmDatabase.js +15 -16
  215. package/dist/database/entry/EntryTable.js +2 -2
  216. package/dist/database/query/EntryQuery.js +1 -1
  217. package/dist/database/query/Relation.js +1 -1
  218. package/dist/database/query/Search.js +2 -2
  219. package/dist/field/code/CodeField.view.js +16 -16
  220. package/dist/field/link/EntryLink.d.ts +4 -4
  221. package/dist/field/link/FileLink.d.ts +4 -4
  222. package/dist/field/link/ImageLink.d.ts +4 -4
  223. package/dist/field/link/Link.d.ts +1 -1
  224. package/dist/field/link/LinkField.d.ts +5 -0
  225. package/dist/field/link/LinkField.view.js +244 -370
  226. package/dist/field/link/UrlLink.d.ts +4 -4
  227. package/dist/field/list/ListField.d.ts +2 -0
  228. package/dist/field/list/ListField.view.d.ts +15 -0
  229. package/dist/field/list/ListField.view.js +343 -301
  230. package/dist/field/metadata/MetadataField.d.ts +17 -7
  231. package/dist/field/metadata/MetadataField.js +14 -3
  232. package/dist/field/metadata/MetadataField.view.d.ts +1 -0
  233. package/dist/field/metadata/MetadataField.view.js +25 -21
  234. package/dist/field/richtext/RichTextBlock.d.ts +3 -1
  235. package/dist/field/richtext/RichTextBlock.js +177 -74
  236. package/dist/field/richtext/RichTextBlockHost.js +1 -1
  237. package/dist/field/richtext/RichTextExtensions.js +31 -31
  238. package/dist/field/richtext/RichTextField.view.js +14 -8
  239. package/dist/field/richtext/RichTextInsertMenu.js +43 -27
  240. package/dist/field/views.js +2 -0
  241. package/dist/index.css +490 -309
  242. package/dist/picker/url/UrlPicker.js +1 -0
  243. package/docs/components/badge.md +1 -0
  244. package/docs/components/button.md +1 -0
  245. package/docs/components/content-card.md +1 -0
  246. package/docs/components/search-field.md +1 -0
  247. package/docs/components/sortable-list.md +5 -20
  248. package/docs/components/table.md +1 -0
  249. package/docs/configuration.md +1 -1
  250. package/docs/deploy/self-host.md +1 -1
  251. package/docs/deploy.md +5 -4
  252. package/docs/fields.md +1 -1
  253. package/docs/index.md +1 -1
  254. package/docs/instant-publishing.md +7 -22
  255. package/docs/overviews.md +3 -4
  256. package/docs/upgrading.md +3 -2
  257. package/llms-full.txt +31 -55
  258. package/package.json +2 -3
  259. package/dist/core/db/EntryValidationError.d.ts +0 -13
  260. package/dist/core/db/EntryValidationError.js +0 -23
  261. package/dist/dashboard/app/DeleteDialog.d.ts +0 -11
  262. package/dist/dashboard/atoms/delete.d.ts +0 -68
  263. package/dist/dashboard/atoms/delete.js +0 -143
  264. package/dist/chunks/{chunk-MOWR65LM.js → chunk-PYDOMKJZ.js} +71 -71
@@ -0,0 +1,175 @@
1
+ import "../../chunks/chunk-CQ52GEHY.js";
2
+
3
+ // src/components/internal/sortableDrag.ts
4
+ var threshold = 4;
5
+ var duration = 200;
6
+ var easing = "cubic-bezier(0.2, 0, 0, 1)";
7
+ var scrollEdge = 48;
8
+ var scrollSpeed = 12;
9
+ var settling = /* @__PURE__ */ new Map();
10
+ function pressSortableItem({
11
+ event,
12
+ list,
13
+ item,
14
+ items,
15
+ onDrop
16
+ }) {
17
+ const startX = event.clientX;
18
+ const startY = event.clientY;
19
+ let pointerY = startY;
20
+ let drag = null;
21
+ function onMove(event2) {
22
+ if (event2.pointerId !== pointerId) return;
23
+ pointerY = event2.clientY;
24
+ if (!drag) {
25
+ const distance = Math.hypot(event2.clientX - startX, pointerY - startY);
26
+ if (distance < threshold) return;
27
+ drag = startDrag();
28
+ if (!drag) return finish(false);
29
+ }
30
+ drag.update();
31
+ }
32
+ function onUp(event2) {
33
+ if (event2.pointerId === pointerId) finish(true);
34
+ }
35
+ function onCancel(event2) {
36
+ if (event2.pointerId === pointerId) finish(false);
37
+ }
38
+ function onKeyDown(event2) {
39
+ if (event2.key !== "Escape" || !drag) return;
40
+ event2.preventDefault();
41
+ event2.stopPropagation();
42
+ finish(false);
43
+ }
44
+ function onBlur() {
45
+ finish(false);
46
+ }
47
+ const pointerId = event.pointerId;
48
+ window.addEventListener("pointermove", onMove, true);
49
+ window.addEventListener("pointerup", onUp, true);
50
+ window.addEventListener("pointercancel", onCancel, true);
51
+ window.addEventListener("keydown", onKeyDown, true);
52
+ window.addEventListener("blur", onBlur);
53
+ function finish(commit) {
54
+ window.removeEventListener("pointermove", onMove, true);
55
+ window.removeEventListener("pointerup", onUp, true);
56
+ window.removeEventListener("pointercancel", onCancel, true);
57
+ window.removeEventListener("keydown", onKeyDown, true);
58
+ window.removeEventListener("blur", onBlur);
59
+ if (!drag) return;
60
+ drag.end(commit);
61
+ drag = null;
62
+ const block = (event2) => {
63
+ if (!(event2.target instanceof Node) || !list.contains(event2.target))
64
+ return;
65
+ event2.preventDefault();
66
+ event2.stopPropagation();
67
+ };
68
+ window.addEventListener("click", block, true);
69
+ setTimeout(() => window.removeEventListener("click", block, true), 100);
70
+ }
71
+ function startDrag() {
72
+ for (const settle of Array.from(settling.values())) settle();
73
+ const elements = items();
74
+ const from = elements.indexOf(item);
75
+ if (from === -1) return null;
76
+ const rects = elements.map((element) => element.getBoundingClientRect());
77
+ const scroller = scrollParent(list);
78
+ const startScroll = scroller.scrollTop;
79
+ const others = elements.filter((element) => element !== item);
80
+ let to = from;
81
+ let frame = 0;
82
+ const userSelect = document.body.style.userSelect;
83
+ document.body.style.userSelect = "none";
84
+ document.documentElement.dataset.sortableDragging = "";
85
+ window.getSelection()?.removeAllRanges();
86
+ list.dataset.dragging = "";
87
+ item.dataset.dragging = "";
88
+ for (const other of others)
89
+ other.style.transition = `transform ${duration}ms ${easing}`;
90
+ function offset() {
91
+ const dy = pointerY - startY + scroller.scrollTop - startScroll;
92
+ const min = rects[0].top - rects[from].top;
93
+ const max = rects[rects.length - 1].bottom - rects[from].bottom;
94
+ return Math.min(max, Math.max(min, dy));
95
+ }
96
+ function update() {
97
+ const dy = offset();
98
+ item.style.transform = `translateY(${dy}px)`;
99
+ const top = rects[from].top + dy;
100
+ const bottom = rects[from].bottom + dy;
101
+ to = from;
102
+ for (let index = from - 1; index >= 0; index--)
103
+ if (top < middle(rects[index])) to = index;
104
+ for (let index = from + 1; index < rects.length; index++)
105
+ if (bottom > middle(rects[index])) to = index;
106
+ const size = rects[from].height;
107
+ elements.forEach((element, index) => {
108
+ if (element === item) return;
109
+ const shift = from < to && index > from && index <= to ? -size : to < from && index >= to && index < from ? size : 0;
110
+ element.style.transform = shift ? `translateY(${shift}px)` : "";
111
+ });
112
+ }
113
+ function autoScroll() {
114
+ const bounds = scrollBounds(scroller);
115
+ const step = pointerY < bounds.top + scrollEdge ? -scrollSpeed : pointerY > bounds.bottom - scrollEdge ? scrollSpeed : 0;
116
+ if (step) {
117
+ const before = scroller.scrollTop;
118
+ scroller.scrollTop += step;
119
+ if (scroller.scrollTop !== before) update();
120
+ }
121
+ frame = requestAnimationFrame(autoScroll);
122
+ }
123
+ frame = requestAnimationFrame(autoScroll);
124
+ function end(commit) {
125
+ cancelAnimationFrame(frame);
126
+ document.body.style.userSelect = userSelect;
127
+ delete document.documentElement.dataset.sortableDragging;
128
+ const moved = commit && to !== from;
129
+ for (const other of others) {
130
+ if (moved) other.style.transition = "";
131
+ other.style.transform = "";
132
+ }
133
+ const visualTop = item.getBoundingClientRect().top;
134
+ item.style.transform = "";
135
+ if (moved) onDrop(item, elements[to], to > from ? "after" : "before");
136
+ const distance = visualTop - item.getBoundingClientRect().top;
137
+ item.style.transition = "none";
138
+ item.style.transform = `translateY(${distance}px)`;
139
+ item.getBoundingClientRect();
140
+ item.style.transition = `transform ${duration}ms ${easing}`;
141
+ item.style.transform = "";
142
+ delete list.dataset.dragging;
143
+ const timeout = setTimeout(settle, duration);
144
+ function settle() {
145
+ clearTimeout(timeout);
146
+ settling.delete(item);
147
+ item.style.transition = "";
148
+ for (const other of others) other.style.transition = "";
149
+ delete item.dataset.dragging;
150
+ }
151
+ settling.set(item, settle);
152
+ }
153
+ return { update, end };
154
+ }
155
+ }
156
+ function middle(rect) {
157
+ return rect.top + rect.height / 2;
158
+ }
159
+ function scrollParent(element) {
160
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
161
+ const { overflowY } = getComputedStyle(parent);
162
+ if ((overflowY === "auto" || overflowY === "scroll") && parent.scrollHeight > parent.clientHeight)
163
+ return parent;
164
+ }
165
+ return document.scrollingElement ?? document.documentElement;
166
+ }
167
+ function scrollBounds(scroller) {
168
+ if (scroller === document.scrollingElement)
169
+ return { top: 0, bottom: window.innerHeight };
170
+ const rect = scroller.getBoundingClientRect();
171
+ return { top: rect.top, bottom: rect.bottom };
172
+ }
173
+ export {
174
+ pressSortableItem
175
+ };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  $32ef394310635fd5$export$2cfc5be7a55829f6,
3
3
  $f9554a667e4f0374$export$62ed72bc21f6b8a6
4
- } from "../../chunks/chunk-JOF663EG.js";
4
+ } from "../../chunks/chunk-QHHMKJUO.js";
5
5
  import "../../chunks/chunk-NUFRM6SI.js";
6
- import "../../chunks/chunk-RE523BQ7.js";
6
+ import "../../chunks/chunk-HOETF6HN.js";
7
7
  import "../../chunks/chunk-RKDPDEF7.js";
8
8
  import "../../chunks/chunk-PSSMQKAH.js";
9
9
  import "../../chunks/chunk-CQ52GEHY.js";
@@ -42,6 +42,7 @@ export * from './components/RadioGroup.js';
42
42
  export * from './components/Resizable.js';
43
43
  export * from './components/SearchField.js';
44
44
  export * from './components/Select.js';
45
+ export * from './components/Sheet.js';
45
46
  export * from './components/Sidebar.js';
46
47
  export * from './components/SortableList.js';
47
48
  export * from './components/Spinner.js';
@@ -43,6 +43,7 @@ export * from "./components/RadioGroup.js";
43
43
  export * from "./components/Resizable.js";
44
44
  export * from "./components/SearchField.js";
45
45
  export * from "./components/Select.js";
46
+ export * from "./components/Sheet.js";
46
47
  export * from "./components/Sidebar.js";
47
48
  export * from "./components/SortableList.js";
48
49
  export * from "./components/Spinner.js";
@@ -19,7 +19,12 @@ export interface Config {
19
19
  preview?: Preview;
20
20
  /** Every edit will pass through a draft status before being published */
21
21
  enableDrafts?: boolean;
22
- /** Fallback interval in seconds for polling content updates (freshness is primarily sha-driven) */
22
+ /**
23
+ * Fallback interval in seconds for polling content updates (freshness is
24
+ * primarily sha-driven). In Next.js it also sets how long renders share the
25
+ * latest content sha, which bounds how soon edits made outside the handler
26
+ * show up: five minutes when unset.
27
+ */
23
28
  syncInterval?: number;
24
29
  /** Maximum file upload size in bytes */
25
30
  maxUploadSize?: number;
@@ -7,4 +7,12 @@ export type Document = {
7
7
  path: PathField;
8
8
  metadata: MetadataField;
9
9
  };
10
- export declare function document<Fields extends FieldsDefinition = {}>(label: string, { fields, ...config }: TypeConfig<Fields> | ContainerTypeConfig<Fields>): Type<Document & Fields>;
10
+ export type DocumentConfig<Fields, Seo, Details> = (TypeConfig<Fields> | ContainerTypeConfig<Fields>) & {
11
+ /** Fields shown in the SEO tab, above the previews */
12
+ seo?: Seo;
13
+ /** Fields shown in the Details tab, above the created and updated details */
14
+ details?: Details;
15
+ };
16
+ export declare 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>;
17
+ /** Whether `type` was created with `Config.document` */
18
+ export declare function isDocument(type: Type): boolean;
@@ -8,24 +8,40 @@ import {
8
8
  } from "alinea/dashboard/icons";
9
9
  import {
10
10
  metadata as createMetadata,
11
- metadataDetails
11
+ metadataDetails,
12
+ metadataPreviews
12
13
  } from "alinea/field/metadata";
13
14
  import { path as createPath } from "alinea/field/path";
14
15
  import { tab, tabs } from "alinea/field/tabs";
15
16
  import { text } from "alinea/field/text";
17
+ import { Section } from "./Section.js";
16
18
  import {
17
19
  type
18
20
  } from "./Type.js";
21
+ import { entries, keys } from "./util/Objects.js";
19
22
  var documentMarker = /* @__PURE__ */ Symbol.for("@alinea.Document");
20
23
  function documentFields() {
21
24
  return {
22
25
  title: text("Title", { required: true, width: 0.5 }),
23
26
  path: createPath("Path", { required: true, width: 0.5 }),
24
- metadata: createMetadata("Metadata", { detailsSection: true })
27
+ metadata: createMetadata("Metadata", { sections: true })
25
28
  };
26
29
  }
27
- function document(label, { fields, ...config }) {
30
+ function document(label, {
31
+ fields = {},
32
+ seo = {},
33
+ details = {},
34
+ ...config
35
+ }) {
28
36
  const { title, path, metadata } = documentFields();
37
+ const taken = /* @__PURE__ */ new Set([
38
+ ...keys({ title, path, metadata }),
39
+ ...fieldNames(fields)
40
+ ]);
41
+ for (const name of [...fieldNames(seo), ...fieldNames(details)]) {
42
+ if (taken.has(name)) throw new Error(`Field "${name}" is defined twice`);
43
+ taken.add(name);
44
+ }
29
45
  const fieldsWithMeta = tabs(
30
46
  tab("Document", {
31
47
  icon: IcRoundDescription,
@@ -33,11 +49,11 @@ function document(label, { fields, ...config }) {
33
49
  }),
34
50
  tab("SEO", {
35
51
  icon: IcRoundBolt,
36
- fields: { metadata }
52
+ fields: { metadata, ...seo, ...metadataPreviews() }
37
53
  }),
38
54
  tab("Details", {
39
55
  icon: IcRoundInfo,
40
- fields: { ...metadataDetails(metadata) }
56
+ fields: { ...details, ...metadataDetails(metadata) }
41
57
  })
42
58
  );
43
59
  const result = type(label, {
@@ -46,6 +62,15 @@ function document(label, { fields, ...config }) {
46
62
  });
47
63
  return Object.assign(result, { [documentMarker]: true });
48
64
  }
65
+ function isDocument(type2) {
66
+ return documentMarker in type2;
67
+ }
68
+ function fieldNames(definition) {
69
+ return entries(definition).flatMap(
70
+ ([name, value]) => Section.isSection(value) ? keys(Section.fields(value)) : [name]
71
+ );
72
+ }
49
73
  export {
50
- document
74
+ document,
75
+ isDocument
51
76
  };
@@ -70,7 +70,7 @@ export type OverviewSortDirection = 'asc' | 'desc';
70
70
  export interface OverviewSort {
71
71
  /**
72
72
  * A key of `overview.sorts`, of `overview.columns`, or a built-in column:
73
- * `title`, `type`, `status`, `updated`, `author`
73
+ * `title`, `type`, `updated`, `author`
74
74
  */
75
75
  column: string;
76
76
  direction: OverviewSortDirection;
@@ -180,8 +180,6 @@ export interface OverviewColumn<Value = any> extends OverviewColumnBase<Value> {
180
180
  export interface OverviewBuiltins {
181
181
  /** The entry type, shown by default when the children have several types */
182
182
  type?: boolean;
183
- /** The publication status, shown by default when the statuses differ */
184
- status?: boolean;
185
183
  /** When the entry was last edited, shown by default when recorded */
186
184
  updated?: boolean;
187
185
  /** Who last edited the entry, shown by default when recorded */
@@ -192,7 +190,7 @@ export interface OverviewOptions {
192
190
  /**
193
191
  * Columns shown after the title and the built-in columns, or before the
194
192
  * built-in columns with `position: 'start'`. A column keyed `type`,
195
- * `status`, `updated` or `author` replaces that built-in column.
193
+ * `updated` or `author` replaces that built-in column.
196
194
  */
197
195
  columns?: Record<string, OverviewColumn>;
198
196
  /** Show or hide the built-in columns */
@@ -70,6 +70,10 @@ export interface ImageNode extends ElementNode {
70
70
  export interface BlockNode {
71
71
  _id: string;
72
72
  _type: string;
73
+ /** A label editors give the block, its anchor follows it by default */
74
+ _label?: string;
75
+ /** Lets links point to the block, unique within the entry */
76
+ _anchor?: string;
73
77
  }
74
78
  export declare namespace BlockNode {
75
79
  const id = "_id";
@@ -53,4 +53,3 @@ export declare function validateType(type: Type, value: unknown, context: FieldV
53
53
  /** Validate entry data: required, min, max and validate options */
54
54
  export declare function validateEntry(type: Type, data: unknown, options?: ValidateEntryOptions): Array<FieldValidationError>;
55
55
  export declare function formatFieldPath(path: FieldPath): string;
56
- export declare function formatValidationErrors(errors: ReadonlyArray<FieldValidationError>): string;
@@ -104,15 +104,9 @@ function formatFieldPath(path) {
104
104
  }
105
105
  return result;
106
106
  }
107
- function formatValidationErrors(errors) {
108
- return errors.map(
109
- (error) => `- ${formatFieldPath(error.path)} (${error.labels.join(" \u203A ")}): ${error.message}`
110
- ).join("\n");
111
- }
112
107
  export {
113
108
  fieldError,
114
109
  formatFieldPath,
115
- formatValidationErrors,
116
110
  policyFieldOptions,
117
111
  scopeGetter,
118
112
  trackedFieldOptions,
@@ -1911,8 +1911,11 @@ function normalizeRichTextAnchors(schema, doc, anchors) {
1911
1911
  function normalizeNode(node, insideHeading, activeManualAnchors2) {
1912
1912
  if (Node.isBlock(node)) {
1913
1913
  activeManualAnchors2.clear();
1914
+ const source2 = blockAnchor(node);
1915
+ const unique2 = createUniqueAnchor(source2, anchors);
1916
+ const anchored = source2 !== void 0 && unique2 !== node._anchor ? { ...node, _anchor: unique2 } : node;
1914
1917
  const type = schema?.[node[Node.type]];
1915
- return type ? Type.normalizeAnchors(type, node, { anchors }) : node;
1918
+ return type ? Type.normalizeAnchors(type, anchored, { anchors }) : anchored;
1916
1919
  }
1917
1920
  if (Node.isText(node)) {
1918
1921
  const marks = node.marks;
@@ -2052,20 +2055,36 @@ function richTextAnchors(schema, doc, context) {
2052
2055
  }
2053
2056
  });
2054
2057
  doc.forEach((row, index) => {
2055
- if (!schema || !Node.isBlock(row)) return;
2056
- const type = schema[row[Node.type]];
2058
+ if (!Node.isBlock(row)) return;
2059
+ const segment = row._id ?? String(index);
2060
+ const anchor = blockAnchor(row);
2061
+ if (anchor && !anchors.has(anchor)) {
2062
+ anchors.add(anchor);
2063
+ result.push({
2064
+ id: anchor,
2065
+ label: `#${anchor}`,
2066
+ fieldPath: referenceFieldPath([...path, segment, anchor]),
2067
+ fieldLabel: label
2068
+ });
2069
+ }
2070
+ const type = schema?.[row[Node.type]];
2057
2071
  if (!type) return;
2058
2072
  result.push(
2059
2073
  ...Type.anchors(
2060
2074
  type,
2061
2075
  row,
2062
- [...path, row._id ?? String(index)],
2076
+ [...path, segment],
2063
2077
  [...labels, Type.label(type)]
2064
2078
  )
2065
2079
  );
2066
2080
  });
2067
2081
  return result;
2068
2082
  }
2083
+ function blockAnchor(block) {
2084
+ if (typeof block._anchor === "string") return block._anchor;
2085
+ if (typeof block._label === "string" && block._label)
2086
+ return slugify(block._label);
2087
+ }
2069
2088
  function richTextReferences(schema, doc, context) {
2070
2089
  const { path, label, labels } = context;
2071
2090
  const result = [];
@@ -82,7 +82,7 @@ function mediaOverview() {
82
82
  MediaLibrary: new Expr({ type: "value", value: 0 })
83
83
  });
84
84
  return {
85
- builtins: { type: false, status: false },
85
+ builtins: { type: false },
86
86
  // Folders first in their manual order, files have none and come after,
87
87
  // newest first: entry ids start with their creation time. Indexes are
88
88
  // keys that only order as stored.
@@ -13,6 +13,10 @@ export declare function normalizeGithubSourceOptions<Options extends GithubSourc
13
13
  export declare class GithubSource implements Source {
14
14
  #private;
15
15
  constructor(options: GithubSourceOptions);
16
+ /** Request GitHub, counting the request against its budget. */
17
+ protected githubFetch(input: string, init?: RequestInit): Promise<Response>;
18
+ /** Run an operation, and log what it spent of GitHub's budgets. */
19
+ protected spending<T>(operation: string, run: () => Promise<T>): Promise<T>;
16
20
  protected get contentLocation(): string;
17
21
  getTree(): Promise<ReadonlyTree>;
18
22
  shaAt(ref: string): Promise<string>;
@@ -8,9 +8,14 @@ import * as paths from "alinea/core/util/Paths";
8
8
  import { HttpError } from "../HttpError.js";
9
9
  import { assert } from "../util/Assert.js";
10
10
  import { isRecord } from "alinea/core/util/Objects";
11
+ import { chunks } from "../util/Arrays.js";
12
+ import { hashBlob } from "./GitUtils.js";
13
+ import { GithubUsage } from "./GithubUsage.js";
11
14
  import { Leaf, ReadonlyTree } from "./Tree.js";
12
15
  var shaCache = /* @__PURE__ */ new Map();
13
16
  var truncatedTrees = /* @__PURE__ */ new Set();
17
+ var blobsPerQuery = 100;
18
+ var smallestBlobQuery = 25;
14
19
  function normalizeGithubSourceOptions(options) {
15
20
  return {
16
21
  ...options,
@@ -22,9 +27,27 @@ var GithubSource = class {
22
27
  #current = ReadonlyTree.EMPTY;
23
28
  #options;
24
29
  #limit = pLimit(8);
30
+ /** Blob queries in flight, each holding up to a hundred blobs in memory. */
31
+ #blobQueries = pLimit(2);
32
+ #usage = new GithubUsage();
25
33
  constructor(options) {
26
34
  this.#options = normalizeGithubSourceOptions(options);
27
35
  }
36
+ /** Request GitHub, counting the request against its budget. */
37
+ async githubFetch(input, init) {
38
+ const response = await fetch(input, init);
39
+ this.#usage.record(response);
40
+ return response;
41
+ }
42
+ /** Run an operation, and log what it spent of GitHub's budgets. */
43
+ async spending(operation, run) {
44
+ const before = this.#usage.count();
45
+ try {
46
+ return await run();
47
+ } finally {
48
+ this.#usage.log(operation, before);
49
+ }
50
+ }
28
51
  get contentLocation() {
29
52
  const { contentDir, rootDir } = this.#options;
30
53
  return paths.join(rootDir, contentDir);
@@ -42,7 +65,7 @@ var GithubSource = class {
42
65
  const cached = shaCache.get(url);
43
66
  const headers = new Headers({ Authorization: `Bearer ${authToken}` });
44
67
  if (cached) headers.set("If-None-Match", cached.etag);
45
- const parentInfo = await fetch(url, { headers });
68
+ const parentInfo = await this.githubFetch(url, { headers });
46
69
  if (parentInfo.status === 304) {
47
70
  assert(cached, "Received 304 without a cached GitHub response");
48
71
  return cached.sha;
@@ -59,10 +82,12 @@ var GithubSource = class {
59
82
  else shaCache.delete(url);
60
83
  return sha;
61
84
  }
62
- async getTreeIfDifferent(sha) {
63
- const remoteSha = await this.shaAt(this.#options.branch);
64
- if (remoteSha === sha) return void 0;
65
- return this.#fetchTree(remoteSha, this.contentLocation);
85
+ getTreeIfDifferent(sha) {
86
+ return this.spending("sync", async () => {
87
+ const remoteSha = await this.shaAt(this.#options.branch);
88
+ if (remoteSha === sha) return void 0;
89
+ return this.#fetchTree(remoteSha, this.contentLocation);
90
+ });
66
91
  }
67
92
  /**
68
93
  * GitHub truncates recursive listings above 100k entries or 7 MB, so a
@@ -73,7 +98,7 @@ var GithubSource = class {
73
98
  const key = `${owner}/${repo}:${location}`;
74
99
  const list = async (query) => {
75
100
  const response = await this.#limit(
76
- () => fetch(
101
+ () => this.githubFetch(
77
102
  `https://api.github.com/repos/${owner}/${repo}/git/trees/${sha}${query}`,
78
103
  { headers: { Authorization: `Bearer ${authToken}` } }
79
104
  )
@@ -101,28 +126,105 @@ var GithubSource = class {
101
126
  return ReadonlyTree.fromNodes(new Map(nodes), sha);
102
127
  }
103
128
  async *getBlobs(shas, options = {}) {
129
+ const before = this.#usage.count();
130
+ const batches = Array.from(
131
+ chunks(shas, blobsPerQuery),
132
+ (batch) => this.#blobQueries(() => this.#queryBlobs(batch, options.signal))
133
+ );
134
+ for (const batch of batches) batch.catch(() => {
135
+ });
136
+ try {
137
+ for (const batch of batches) yield* await batch;
138
+ } finally {
139
+ this.#usage.log("blobs", before);
140
+ }
141
+ }
142
+ /**
143
+ * Look blobs up in one GraphQL query, which costs a single point of
144
+ * GitHub's GraphQL budget however many it holds, where the REST API counts
145
+ * a request per blob. A query GitHub cannot answer in time, such as one
146
+ * for many large files, is asked again in halves, down to a small one.
147
+ */
148
+ async #queryBlobs(shas, signal) {
149
+ const blobs = await this.#graphqlBlobs(shas, signal);
150
+ if (blobs) return blobs;
151
+ if (shas.length <= smallestBlobQuery)
152
+ throw new HttpError(502, "Failed to get blobs: GitHub did not answer");
153
+ const half = Math.ceil(shas.length / 2);
154
+ return [
155
+ ...await this.#queryBlobs(shas.slice(0, half), signal),
156
+ ...await this.#queryBlobs(shas.slice(half), signal)
157
+ ];
158
+ }
159
+ /**
160
+ * Blobs found by one GraphQL query, or undefined if GitHub could not
161
+ * answer it, such as when it timed out. Blobs it has no text for, such as
162
+ * binary or very large files, are fetched one by one.
163
+ */
164
+ async #graphqlBlobs(shas, signal) {
104
165
  const { owner, repo, authToken } = this.#options;
105
- const responses = shas.map((sha) => {
106
- const promise = this.#limit(
107
- () => fetch(
108
- `https://api.github.com/repos/${owner}/${repo}/git/blobs/${sha}`,
109
- {
110
- headers: { Authorization: `Bearer ${authToken}` },
111
- signal: options.signal
112
- }
113
- )
114
- );
115
- return [sha, promise];
166
+ const params = shas.map((_, i) => `$b${i}: GitObjectID!`).join(", ");
167
+ const fields = shas.map(
168
+ (_, i) => `b${i}: object(oid: $b${i}) { ... on Blob { text isBinary isTruncated } }`
169
+ ).join("\n");
170
+ const query = `query ($owner: String!, $repo: String!, ${params}) {
171
+ repository(owner: $owner, name: $repo) {
172
+ ${fields}
173
+ }
174
+ }`;
175
+ const variables = { owner, repo };
176
+ for (const [i, sha] of shas.entries()) variables[`b${i}`] = sha;
177
+ const response = await this.githubFetch("https://api.github.com/graphql", {
178
+ method: "POST",
179
+ headers: {
180
+ Authorization: `Bearer ${authToken}`,
181
+ "Content-Type": "application/json"
182
+ },
183
+ body: JSON.stringify({ query, variables }),
184
+ signal
116
185
  });
117
- for (const [sha, promise] of responses) {
118
- const response = await promise;
119
- if (!response.ok) throw await githubError(response, "Failed to get blob");
120
- const blobData = await response.json();
121
- assert(blobData.encoding === "base64");
122
- assert(typeof blobData.content === "string");
123
- assert(blobData.size > 0);
124
- yield [sha, Uint8Array.from(atob(blobData.content), (c) => c.charCodeAt(0))];
186
+ if (response.status >= 500) return void 0;
187
+ if (!response.ok) throw await githubError(response, "Failed to get blobs");
188
+ const result = await response.json();
189
+ assert(isRecord(result));
190
+ const errors = Array.isArray(result.errors) ? result.errors : [];
191
+ if (errors.length > 0) {
192
+ const typed = errors.some(
193
+ (error) => isRecord(error) && typeof error.type === "string"
194
+ );
195
+ if (!typed) return void 0;
196
+ const reasons = errors.map(
197
+ (error) => isRecord(error) ? String(error.message) : String(error)
198
+ );
199
+ throw new Error(`Failed to get blobs: ${reasons.join("; ")}`);
200
+ }
201
+ const data = isRecord(result.data) ? result.data : {};
202
+ const repository = isRecord(data.repository) ? data.repository : {};
203
+ const encoder = new TextEncoder();
204
+ const blobs = Array();
205
+ for (const [i, sha] of shas.entries()) {
206
+ const blob = repository[`b${i}`];
207
+ const text = isRecord(blob) && !blob.isBinary && !blob.isTruncated ? blob.text : void 0;
208
+ const bytes = typeof text === "string" ? encoder.encode(text) : void 0;
209
+ blobs.push([
210
+ sha,
211
+ bytes && await hashBlob(bytes) === sha ? bytes : await this.#fetchBlob(sha, signal)
212
+ ]);
125
213
  }
214
+ return blobs;
215
+ }
216
+ async #fetchBlob(sha, signal) {
217
+ const { owner, repo, authToken } = this.#options;
218
+ const response = await this.githubFetch(
219
+ `https://api.github.com/repos/${owner}/${repo}/git/blobs/${sha}`,
220
+ { headers: { Authorization: `Bearer ${authToken}` }, signal }
221
+ );
222
+ if (!response.ok) throw await githubError(response, "Failed to get blob");
223
+ const blobData = await response.json();
224
+ assert(blobData.encoding === "base64");
225
+ assert(typeof blobData.content === "string");
226
+ assert(blobData.size > 0);
227
+ return Uint8Array.from(atob(blobData.content), (c) => c.charCodeAt(0));
126
228
  }
127
229
  async applyChanges(batch) {
128
230
  throw new Error("Not implemented");
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Counts the GitHub requests a source makes against its REST and GraphQL
3
+ * budgets, and logs what each operation spent and what is left, so the logs
4
+ * show where a rate limit goes. Answers of 304 are free and not counted.
5
+ */
6
+ export declare class GithubUsage {
7
+ #private;
8
+ rest: number;
9
+ graphql: number;
10
+ record(response: Response): void;
11
+ /** The requests counted so far, to log what an operation adds. */
12
+ count(): {
13
+ rest: number;
14
+ graphql: number;
15
+ };
16
+ /** Log what `operation` spent since `before`, if anything. */
17
+ log(operation: string, before: {
18
+ rest: number;
19
+ graphql: number;
20
+ }): void;
21
+ }