@morphemecms/cms 0.5.0 → 0.7.0

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 (396) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/astro.mjs +3 -0
  3. package/bin/morpheme.mjs +3 -1
  4. package/defaults/blockTexts.ts +2 -2
  5. package/defaults/collections.ts +10 -0
  6. package/defaults/editor.ts +57 -53
  7. package/defaults/privacy.ts +4 -1
  8. package/defaults/rules.ts +36 -38
  9. package/defaults/standardPages.ts +0 -3
  10. package/defaults/texts.ts +11 -2
  11. package/demo/src/content/pages/about.md +2 -0
  12. package/icons/lucide/browser-fill.svg +1 -0
  13. package/icons/lucide/buildings-fill.svg +1 -0
  14. package/icons/lucide/certificate-fill.svg +1 -0
  15. package/icons/lucide/chart-line-up-fill.svg +1 -0
  16. package/icons/lucide/chats-fill.svg +1 -0
  17. package/icons/lucide/envelope-simple-fill.svg +1 -0
  18. package/icons/lucide/file-text-fill.svg +1 -0
  19. package/icons/lucide/folder-simple-fill.svg +1 -0
  20. package/icons/lucide/gauge.svg +1 -1
  21. package/icons/lucide/gear-six.svg +1 -0
  22. package/icons/lucide/globe-simple.svg +1 -0
  23. package/icons/lucide/heart-fill.svg +1 -0
  24. package/icons/lucide/layout.svg +1 -0
  25. package/icons/lucide/list-checks-fill.svg +1 -0
  26. package/icons/lucide/magnifying-glass-fill.svg +1 -0
  27. package/icons/lucide/megaphone-fill.svg +1 -0
  28. package/icons/lucide/newspaper-fill.svg +1 -0
  29. package/icons/lucide/paint-brush-broad-fill.svg +1 -0
  30. package/icons/lucide/puzzle-piece-fill.svg +1 -0
  31. package/icons/lucide/shield-check-fill.svg +1 -0
  32. package/icons/lucide/sidebar-simple.svg +1 -1
  33. package/icons/lucide/sidebar.svg +1 -0
  34. package/icons/lucide/square-half-bottom-fill.svg +1 -0
  35. package/icons/lucide/square-half.svg +1 -0
  36. package/icons/lucide/squares-four-fill.svg +1 -0
  37. package/icons/lucide/storefront-fill.svg +1 -0
  38. package/icons/lucide/tag-fill.svg +1 -0
  39. package/icons/lucide/toggle-right-fill.svg +1 -0
  40. package/icons/lucide/trash-fill.svg +1 -0
  41. package/icons/lucide/user-fill.svg +1 -0
  42. package/icons/lucide/wrench-fill.svg +1 -0
  43. package/icons/material-symbols/browser-fill.svg +1 -0
  44. package/icons/material-symbols/buildings-fill.svg +1 -0
  45. package/icons/material-symbols/certificate-fill.svg +1 -0
  46. package/icons/material-symbols/chart-line-up-fill.svg +1 -0
  47. package/icons/material-symbols/chats-fill.svg +1 -0
  48. package/icons/material-symbols/envelope-simple-fill.svg +1 -0
  49. package/icons/material-symbols/file-text-fill.svg +1 -0
  50. package/icons/material-symbols/folder-simple-fill.svg +1 -0
  51. package/icons/material-symbols/gauge.svg +1 -1
  52. package/icons/material-symbols/gear-six.svg +1 -0
  53. package/icons/material-symbols/globe-simple.svg +1 -0
  54. package/icons/material-symbols/heart-fill.svg +1 -0
  55. package/icons/material-symbols/layout.svg +1 -0
  56. package/icons/material-symbols/list-checks-fill.svg +1 -0
  57. package/icons/material-symbols/magnifying-glass-fill.svg +1 -0
  58. package/icons/material-symbols/megaphone-fill.svg +1 -0
  59. package/icons/material-symbols/newspaper-fill.svg +1 -0
  60. package/icons/material-symbols/paint-brush-broad-fill.svg +1 -0
  61. package/icons/material-symbols/puzzle-piece-fill.svg +1 -0
  62. package/icons/material-symbols/shield-check-fill.svg +1 -0
  63. package/icons/material-symbols/sidebar-simple.svg +1 -1
  64. package/icons/material-symbols/sidebar.svg +1 -0
  65. package/icons/material-symbols/square-half-bottom-fill.svg +1 -0
  66. package/icons/material-symbols/square-half.svg +1 -0
  67. package/icons/material-symbols/squares-four-fill.svg +1 -0
  68. package/icons/material-symbols/storefront-fill.svg +1 -0
  69. package/icons/material-symbols/tag-fill.svg +1 -0
  70. package/icons/material-symbols/toggle-right-fill.svg +1 -0
  71. package/icons/material-symbols/trash-fill.svg +1 -0
  72. package/icons/material-symbols/user-fill.svg +1 -0
  73. package/icons/material-symbols/wrench-fill.svg +1 -0
  74. package/icons/phosphor/browser-fill.svg +1 -0
  75. package/icons/phosphor/buildings-fill.svg +1 -0
  76. package/icons/phosphor/certificate-fill.svg +1 -0
  77. package/icons/phosphor/chart-line-up-fill.svg +1 -0
  78. package/icons/phosphor/chats-fill.svg +1 -0
  79. package/icons/phosphor/envelope-simple-fill.svg +1 -0
  80. package/icons/phosphor/file-text-fill.svg +1 -0
  81. package/icons/phosphor/folder-simple-fill.svg +1 -0
  82. package/icons/phosphor/gear-six.svg +1 -0
  83. package/icons/phosphor/globe-simple.svg +1 -0
  84. package/icons/phosphor/heart-fill.svg +1 -0
  85. package/icons/phosphor/layout.svg +1 -0
  86. package/icons/phosphor/list-checks-fill.svg +1 -0
  87. package/icons/phosphor/magnifying-glass-fill.svg +1 -0
  88. package/icons/phosphor/megaphone-fill.svg +1 -0
  89. package/icons/phosphor/newspaper-fill.svg +1 -0
  90. package/icons/phosphor/paint-brush-broad-fill.svg +1 -0
  91. package/icons/phosphor/puzzle-piece-fill.svg +1 -0
  92. package/icons/phosphor/shield-check-fill.svg +1 -0
  93. package/icons/phosphor/sidebar.svg +1 -0
  94. package/icons/phosphor/square-half-bottom-fill.svg +1 -0
  95. package/icons/phosphor/square-half.svg +1 -0
  96. package/icons/phosphor/squares-four-fill.svg +1 -0
  97. package/icons/phosphor/storefront-fill.svg +1 -0
  98. package/icons/phosphor/tag-fill.svg +1 -0
  99. package/icons/phosphor/toggle-right-fill.svg +1 -0
  100. package/icons/phosphor/trash-fill.svg +1 -0
  101. package/icons/phosphor/user-fill.svg +1 -0
  102. package/icons/phosphor/wrench-fill.svg +1 -0
  103. package/lib/breakpoints.d.mts +13 -0
  104. package/lib/breakpoints.mjs +20 -0
  105. package/lib/breakpoints.test.mjs +39 -0
  106. package/lib/build-lane.d.mts +15 -0
  107. package/lib/build-lane.mjs +23 -0
  108. package/lib/build-lane.test.mjs +74 -0
  109. package/lib/build-settings.d.mts +16 -0
  110. package/lib/build-settings.mjs +39 -0
  111. package/lib/build-settings.test.mjs +41 -0
  112. package/lib/contracts.d.mts +3 -2
  113. package/lib/contracts.mjs +3 -2
  114. package/lib/drawer-swipe.d.mts +10 -0
  115. package/lib/drawer-swipe.mjs +17 -0
  116. package/lib/drawer-swipe.test.mjs +33 -0
  117. package/lib/drawerKeys.d.mts +9 -0
  118. package/lib/drawerKeys.mjs +26 -0
  119. package/lib/drawerKeys.test.mjs +21 -0
  120. package/lib/eyebrow.d.mts +6 -0
  121. package/lib/eyebrow.mjs +24 -0
  122. package/lib/eyebrow.test.mjs +20 -0
  123. package/lib/front-sections.test.mjs +82 -0
  124. package/lib/heading-levels.d.mts +7 -0
  125. package/lib/heading-levels.mjs +16 -0
  126. package/lib/heading-levels.test.mjs +37 -0
  127. package/lib/inline.ts +3 -2
  128. package/lib/markdown.ts +2 -0
  129. package/lib/materialIcon.d.mts +21 -0
  130. package/lib/materialIcon.mjs +29 -0
  131. package/lib/materialIcon.test.mjs +45 -0
  132. package/lib/menu-layout.d.mts +11 -0
  133. package/lib/menu-layout.mjs +14 -0
  134. package/lib/menu-layout.test.mjs +23 -0
  135. package/lib/menu.d.mts +59 -0
  136. package/lib/menu.mjs +129 -0
  137. package/lib/menu.test.mjs +291 -0
  138. package/lib/rehype-emphasize.d.mts +4 -0
  139. package/lib/rehype-emphasize.mjs +20 -0
  140. package/lib/rehype-emphasize.test.mjs +39 -0
  141. package/lib/section-in-view.d.mts +8 -0
  142. package/lib/section-in-view.mjs +25 -0
  143. package/lib/section-in-view.test.mjs +35 -0
  144. package/lib/section-marker.d.mts +5 -0
  145. package/lib/section-marker.mjs +18 -0
  146. package/lib/section-marker.test.mjs +22 -0
  147. package/lib/section-ranges.d.mts +12 -0
  148. package/lib/section-ranges.mjs +43 -0
  149. package/lib/section-ranges.test.mjs +61 -0
  150. package/lib/sheet-stops.d.mts +68 -0
  151. package/lib/sheet-stops.mjs +198 -0
  152. package/lib/sheet-stops.test.mjs +155 -0
  153. package/lib/still-timer.d.mts +5 -0
  154. package/lib/still-timer.mjs +17 -0
  155. package/lib/still-timer.test.mjs +42 -0
  156. package/lib/text-sections.d.mts +10 -0
  157. package/lib/text-sections.mjs +45 -0
  158. package/lib/text-sections.test.mjs +78 -0
  159. package/lib/video.mjs +2 -1
  160. package/lib/without-h1.d.mts +1 -0
  161. package/lib/without-h1.mjs +7 -0
  162. package/lib/without-h1.test.mjs +26 -0
  163. package/lib/word-at.d.mts +6 -0
  164. package/lib/word-at.mjs +20 -0
  165. package/lib/word-at.test.mjs +19 -0
  166. package/package.json +3 -1
  167. package/pages/editor/version.json.js +22 -2
  168. package/scripts/already-checked.mjs +11 -0
  169. package/scripts/already-checked.test.mjs +18 -0
  170. package/scripts/build-settings-problems.mjs +27 -0
  171. package/scripts/build-settings-problems.test.mjs +35 -0
  172. package/scripts/check-content.mjs +42 -4
  173. package/scripts/check-worker.mjs +96 -4
  174. package/scripts/ci-build.mjs +85 -0
  175. package/scripts/ci-build.test.mjs +137 -0
  176. package/scripts/editor-roundtrip.mjs +62 -3
  177. package/scripts/editor-screens.mjs +21 -7
  178. package/scripts/git-env.mjs +6 -0
  179. package/scripts/step-command.mjs +34 -0
  180. package/scripts/step-command.test.mjs +50 -0
  181. package/scripts/text-problems.mjs +83 -0
  182. package/scripts/text-problems.test.mjs +74 -0
  183. package/scripts/worker-modules.mjs +11 -1
  184. package/site/breakpoints.d.mts +6 -0
  185. package/site/breakpoints.mjs +52 -0
  186. package/site/breakpoints.test.mjs +36 -0
  187. package/site/casing.d.mts +3 -0
  188. package/site/casing.mjs +26 -0
  189. package/site/content.config.ts +2 -0
  190. package/site/content.ts +2 -4
  191. package/site/defineSite.d.mts +1 -0
  192. package/site/defineSite.mjs +3 -0
  193. package/site/home.ts +3 -2
  194. package/site/icons.d.mts +116 -4
  195. package/site/icons.mjs +62 -6
  196. package/site/images.mjs +1 -1
  197. package/site/live-diff.d.mts +22 -0
  198. package/site/live-diff.mjs +98 -0
  199. package/site/live-diff.test.mjs +235 -0
  200. package/site/materialSymbols.d.mts +5 -0
  201. package/site/materialSymbols.mjs +19 -0
  202. package/site/sections.ts +1 -1
  203. package/site/theme.d.mts +1 -1
  204. package/site/theme.mjs +7 -1
  205. package/src/ActionParts.tsx +1 -1
  206. package/src/Advanced.tsx +101 -20
  207. package/src/App.tsx +47 -121
  208. package/src/AppList.tsx +1 -1
  209. package/src/BlockParts.tsx +3 -3
  210. package/src/Colors.tsx +1 -1
  211. package/src/CommunicationParts.tsx +1 -1
  212. package/src/ContainmentParts.tsx +80 -28
  213. package/src/Deleted.tsx +18 -4
  214. package/src/EditorFooter.tsx +14 -0
  215. package/src/EntryForm.tsx +75 -39
  216. package/src/FormFrame.tsx +3 -8
  217. package/src/Library.tsx +46 -51
  218. package/src/Licenses.tsx +27 -11
  219. package/src/MainDrawerParts.tsx +60 -0
  220. package/src/MainMenu.tsx +136 -0
  221. package/src/MarkdownEditor.tsx +175 -32
  222. package/src/Media.tsx +69 -16
  223. package/src/MediaTags.tsx +91 -0
  224. package/src/ModalSheetSample.tsx +47 -0
  225. package/src/NavigationParts.tsx +67 -21
  226. package/src/NavigationRestParts.tsx +1 -1
  227. package/src/Parts.tsx +71 -0
  228. package/src/PartsShown.tsx +1 -1
  229. package/src/PickerParts.tsx +2 -2
  230. package/src/Redirects.tsx +3 -2
  231. package/src/Screen.tsx +93 -90
  232. package/src/SelectionParts.tsx +22 -6
  233. package/src/SiteLogoLink.tsx +14 -0
  234. package/src/StartFab.tsx +22 -0
  235. package/src/StartStatus.tsx +1 -1
  236. package/src/autoTags.d.mts +28 -0
  237. package/src/autoTags.mjs +68 -0
  238. package/src/autoTags.test.mjs +109 -0
  239. package/src/buildSettings.test.mjs +20 -0
  240. package/src/buildSettings.ts +20 -0
  241. package/src/checkSite.ts +14 -1
  242. package/src/deploy.ts +7 -9
  243. package/src/editor.css +150 -17
  244. package/src/eyebrow.d.mts +5 -0
  245. package/src/eyebrow.mjs +53 -0
  246. package/src/eyebrowState.d.mts +21 -0
  247. package/src/eyebrowState.mjs +62 -0
  248. package/src/eyebrowState.test.mjs +78 -0
  249. package/src/fileRecords.d.mts +115 -0
  250. package/src/fileRecords.mjs +224 -0
  251. package/src/fileRecords.test.mjs +224 -0
  252. package/src/fileTagFilter.d.mts +30 -0
  253. package/src/fileTagFilter.mjs +44 -0
  254. package/src/fileTagFilter.test.mjs +57 -0
  255. package/src/fileTags.d.mts +56 -0
  256. package/src/fileTags.mjs +100 -0
  257. package/src/fileTags.test.mjs +127 -0
  258. package/src/firstHeading.d.mts +6 -0
  259. package/src/firstHeading.mjs +37 -0
  260. package/src/i18n/da-DK/advanced.ts +27 -12
  261. package/src/i18n/da-DK/app.ts +9 -7
  262. package/src/i18n/da-DK/apps.ts +16 -16
  263. package/src/i18n/da-DK/blockSettings.ts +9 -9
  264. package/src/i18n/da-DK/blocks.ts +2 -2
  265. package/src/i18n/da-DK/check.ts +5 -2
  266. package/src/i18n/da-DK/cms.ts +14 -12
  267. package/src/i18n/da-DK/colors.ts +5 -5
  268. package/src/i18n/da-DK/common.ts +2 -2
  269. package/src/i18n/da-DK/company.ts +3 -3
  270. package/src/i18n/da-DK/consents.ts +6 -6
  271. package/src/i18n/da-DK/forms.ts +6 -6
  272. package/src/i18n/da-DK/history.ts +6 -6
  273. package/src/i18n/da-DK/keywords.ts +2 -2
  274. package/src/i18n/da-DK/library.ts +17 -15
  275. package/src/i18n/da-DK/licenses.ts +4 -4
  276. package/src/i18n/da-DK/media.ts +10 -4
  277. package/src/i18n/da-DK/parts.ts +62 -25
  278. package/src/i18n/da-DK/preview.ts +5 -6
  279. package/src/i18n/da-DK/redirects.ts +7 -7
  280. package/src/i18n/da-DK/seo.ts +1 -1
  281. package/src/i18n/da-DK/text.ts +2 -0
  282. package/src/i18n/da-DK/theme.ts +5 -5
  283. package/src/i18n/en-US/advanced.ts +34 -18
  284. package/src/i18n/en-US/app.ts +7 -5
  285. package/src/i18n/en-US/apps.ts +9 -9
  286. package/src/i18n/en-US/blockSettings.ts +4 -4
  287. package/src/i18n/en-US/blocks.ts +6 -6
  288. package/src/i18n/en-US/check.ts +6 -3
  289. package/src/i18n/en-US/cms.ts +15 -14
  290. package/src/i18n/en-US/colors.ts +18 -18
  291. package/src/i18n/en-US/common.ts +3 -3
  292. package/src/i18n/en-US/consents.ts +4 -4
  293. package/src/i18n/en-US/forms.ts +5 -5
  294. package/src/i18n/en-US/history.ts +2 -2
  295. package/src/i18n/en-US/library.ts +21 -15
  296. package/src/i18n/en-US/licenses.ts +6 -6
  297. package/src/i18n/en-US/media.ts +7 -1
  298. package/src/i18n/en-US/parts.ts +60 -21
  299. package/src/i18n/en-US/preview.ts +5 -6
  300. package/src/i18n/en-US/redirects.ts +3 -3
  301. package/src/i18n/en-US/seo.ts +1 -1
  302. package/src/i18n/en-US/text.ts +3 -0
  303. package/src/i18n/en-US/theme.ts +6 -6
  304. package/src/icon.tsx +90 -27
  305. package/src/licenseData.test.mjs +28 -0
  306. package/src/licenseData.ts +12 -1
  307. package/src/markdown.d.mts +2 -0
  308. package/src/markdown.mjs +73 -5
  309. package/src/markdown.test.mjs +74 -0
  310. package/src/materialIcons.ts +8 -0
  311. package/src/menuChildren.ts +36 -0
  312. package/src/menuOpen.ts +34 -0
  313. package/src/pagePreview.tsx +23 -19
  314. package/src/r2Caching.ts +14 -0
  315. package/src/sectionFrame.ts +39 -0
  316. package/src/sectionState.d.mts +19 -0
  317. package/src/sectionState.mjs +31 -0
  318. package/src/sectionState.test.mjs +59 -0
  319. package/src/sections.d.mts +9 -7
  320. package/src/sections.mjs +74 -27
  321. package/src/sections.test.mjs +59 -0
  322. package/src/sidebar.ts +2 -2
  323. package/src/site.ts +27 -17
  324. package/src/stillCursor.d.mts +8 -0
  325. package/src/stillCursor.mjs +19 -0
  326. package/src/stillCursor.test.mjs +26 -0
  327. package/src/tagUses.d.mts +47 -0
  328. package/src/tagUses.mjs +121 -0
  329. package/src/tagUses.test.mjs +141 -0
  330. package/src/themeUi.ts +10 -0
  331. package/src/topBarSlot.tsx +37 -0
  332. package/src/ui.tsx +654 -150
  333. package/src/virtual.d.ts +5 -0
  334. package/starter/README.md +2 -1
  335. package/starter/package.json +2 -1
  336. package/starter/wrangler.jsonc +3 -2
  337. package/theme/blocks/cards/LinkCards.tsx +2 -1
  338. package/theme/blocks/hero/View.tsx +2 -1
  339. package/theme/blocks/video/View.tsx +2 -1
  340. package/theme/components/BlogIndex.astro +2 -2
  341. package/theme/components/BlogPosts.astro +1 -1
  342. package/theme/components/BlogPosts.tsx +9 -6
  343. package/theme/components/Container.astro +1 -1
  344. package/theme/components/CookieConsent.astro +1 -1
  345. package/theme/components/EndBlock.astro +1 -1
  346. package/theme/components/Footer.astro +5 -5
  347. package/theme/components/Header.astro +3 -3
  348. package/theme/components/Icon.astro +2 -2
  349. package/theme/components/MobileMenu.tsx +72 -61
  350. package/theme/components/MobileNav.astro +2 -2
  351. package/theme/components/PageHeader.astro +2 -2
  352. package/theme/components/PageSection.astro +1 -1
  353. package/theme/components/PopupButton.tsx +2 -2
  354. package/theme/components/PopupView.tsx +2 -2
  355. package/theme/components/PostList.tsx +7 -3
  356. package/theme/components/RichText.astro +22 -7
  357. package/theme/components/Section.astro +17 -10
  358. package/theme/components/SectionHeading.tsx +1 -1
  359. package/theme/components/home/PageBlock.astro +4 -4
  360. package/theme/components/home/Text.astro +3 -3
  361. package/theme/layouts/PageLayout.astro +3 -3
  362. package/theme/layouts/PostLayout.astro +9 -8
  363. package/theme/styles/base.css +13 -3
  364. package/theme/styles/blocks.css +97 -10
  365. package/theme/styles/components.css +9 -9
  366. package/theme/styles/elevation.test.mjs +168 -0
  367. package/theme/styles/global.css +65 -59
  368. package/theme/styles/ui.css +460 -131
  369. package/theme/templates/Home.astro +1 -1
  370. package/theme/templates/New.astro +5 -5
  371. package/theme/templates/NotFound.astro +2 -2
  372. package/theme/ui/BottomSheet.tsx +118 -21
  373. package/theme/ui/BottomSheetResizeIcon.tsx +13 -0
  374. package/theme/ui/Button.tsx +4 -3
  375. package/theme/ui/Dialog.tsx +1 -1
  376. package/theme/ui/FabMenu.tsx +13 -12
  377. package/theme/ui/LoadingIndicator.tsx +1 -1
  378. package/theme/ui/NavigationDrawer.tsx +116 -15
  379. package/theme/ui/NavigationRail.tsx +22 -13
  380. package/theme/ui/Search.tsx +1 -1
  381. package/theme/ui/SettingRow.tsx +31 -0
  382. package/theme/ui/Switch.tsx +11 -11
  383. package/theme/ui/TextField.tsx +17 -6
  384. package/theme/ui/bottomSheet.script.ts +3 -2
  385. package/theme/ui/bottomSheetControl.ts +131 -46
  386. package/theme/ui/fabMenu.script.ts +69 -53
  387. package/theme/ui/modalSheetSize.ts +214 -0
  388. package/theme/ui/navigationDrawerControl.ts +35 -0
  389. package/theme/ui/search.script.ts +4 -2
  390. package/worker/index.js +3 -0
  391. package/worker/plan-status.js +46 -0
  392. package/worker/push.js +7 -5
  393. package/worker/r2-status.js +2 -1
  394. package/icons/lucide/desktop.svg +0 -1
  395. package/icons/material-symbols/desktop.svg +0 -1
  396. package/icons/phosphor/desktop.svg +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @morphemecms/cms
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e15a802: Bring the editor up to date with morpheme-website's copy: the navigation rail and two-level main menu, and the other editor changes made there since 3 November.
8
+
9
+ ## 0.6.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 3723ec1: The front page no longer forces a Banner. A Banner there is an ordinary block: it can be moved, hidden or removed. An app block can be the top of the front page instead.
14
+
15
+ ### Patch Changes
16
+
17
+ - f0080db: `morpheme check:content` reads a site's files with `/` in their paths on Windows too, so it finds the standard pages there.
18
+
3
19
  ## 0.5.0
4
20
 
5
21
  ### Minor Changes
package/astro.mjs CHANGED
@@ -17,6 +17,7 @@ import { withSiteModules } from './site/siteModules.mjs';
17
17
  import { rehypeResponsiveImages } from './site/images.mjs';
18
18
  import { linksDigest, rehypeAutoLinks } from './site/autolinks.mjs';
19
19
  import { rehypeVideo } from './lib/video.mjs';
20
+ import { rehypeEmphasize } from './lib/rehype-emphasize.mjs';
20
21
  import { csp, joined } from './lib/csp.mjs';
21
22
  import { appBuilds, appFoldersPlugin } from './lib/apps.mjs';
22
23
  import { isNoindex } from './site/noindex.mjs';
@@ -111,6 +112,8 @@ export async function siteConfig() {
111
112
  // HTML written in the Markdown (tables, iframes, links) as elements before the plugins below,
112
113
  // so they see it too (Astro would parse it only after them).
113
114
  rehypeRaw,
115
+ // Bold as <strong class="emphasize">, for a theme to style (lib/rehype-emphasize.mjs).
116
+ rehypeEmphasize,
114
117
  [rehypeResponsiveImages, { sizes: '(min-width: 800px) 720px, calc(100vw - 40px)' }],
115
118
  // Internal links from the keywords (src/data/internal-links.json); with what they are made from, so the pages
116
119
  // rendered before (node_modules/.astro) are rendered again when it changes (linksDigest).
package/bin/morpheme.mjs CHANGED
@@ -10,9 +10,11 @@ const COMMANDS = {
10
10
  images: ['responsive-images', 'video-thumbnails'],
11
11
  /** After the site is built: what the Worker and the editor read from dist/. */
12
12
  'after-build': ['scheduled-list', 'link-counts', 'page-colors'],
13
+ /** The Cloudflare build command: the content lane or the full lane, then the build. */
14
+ 'ci-build': ['ci-build'],
13
15
  'check:content': ['check-content'],
14
16
  'check:editor': ['editor-roundtrip'],
15
- 'check:worker': ['check-worker', 'check-consent', 'check-store', 'check-cms-update', 'check-store-connection'],
17
+ 'check:worker': ['check-worker', 'check-consent', 'check-cms-update', 'check-store', 'check-store-connection'],
16
18
  'check:screens': ['editor-screens'],
17
19
  'check:themes': ['check-themes'],
18
20
  'check:demo': ['check-demo'],
@@ -1,4 +1,4 @@
1
- // "Standardblokke" on the start screen: the texts every copy of a block has, a screen for each block
1
+ // "Blokskabeloner" in the menu: the texts every copy of a block has, a screen for each block
2
2
  // (CompanyForm.tsx). The blocks' own are in their folders (shared.ts in src/apps/<app>/blocks/<type>/, saved in
3
3
  // src/data/blocks.json), and the editor makes their screens itself (sharedParts in
4
4
  // packages/editor/src/site.ts). Here only what is the site's: "Book en tid", whose texts are the
@@ -6,7 +6,7 @@
6
6
  import { CalendarCheck } from '@phosphor-icons/react';
7
7
  import type { Part } from '../src/schema';
8
8
 
9
- const back = { href: '#blokke', label: 'Standardblokke' };
9
+ const back = { href: '#blokke', label: 'Blokskabeloner' };
10
10
 
11
11
  export const blockParts: Part[] = (
12
12
  [
@@ -340,6 +340,16 @@ export const collections: Collection[] = [
340
340
  { name: 'title', label: 'Titel', type: 'string', required: true },
341
341
  { name: 'path', label: 'Adresse', type: 'string', address: 'page' },
342
342
  { name: 'description', label: 'Kort beskrivelse', type: 'text' },
343
+ {
344
+ name: 'categories',
345
+ label: 'Tags',
346
+ type: 'relation',
347
+ to: 'categories',
348
+ multiple: true,
349
+ add: 'Tilføj tag',
350
+ edit: { to: 'ny/kategorier', text: 'Opret et tag' },
351
+ hint: 'Vises ikke på hjemmesiden.',
352
+ },
343
353
  {
344
354
  name: 'wide',
345
355
  label: 'Hele bredden',
@@ -3,11 +3,11 @@
3
3
  // standard parts: a site has them unless its src/site/ has a file of the same name (siteFile in site/theme.mjs), which
4
4
  // may take the standard one from @morphemecms/cms/defaults/<file> and add its own.
5
5
  // collections the parts of the site and their fields (collections.ts)
6
- // groups, lists, header, icons the start screen
6
+ // groups, settings, lists, header, icons the menu and the start screen
7
7
  // appearance the site's own parts under "Udseende", after the logo and icon ("Standardsider")
8
8
  // companyParts, blockParts the screens under "Virksomheden" and "Blokke" (company.ts, blockTexts.ts)
9
9
  // partScreens parts of the start screen that are screens with parts of their own (privacy.ts)
10
- // seoParts the site's own parts under "Søgemaskineoptimering", after the redirects (internalLinks.ts)
10
+ // seoParts the site's own parts under "Søgemaskineoptimering" (#seo/<id>), in the menu under "Indhold" (internalLinks.ts)
11
11
  // relationsOf, usedBy, keptBy what a field picks from, what cannot be deleted, and what cannot be saved so (rules.ts)
12
12
  // siteProblems what is wrong by the site's own rules, before it is built (rules.ts)
13
13
  // usesOf, countsOf the places that use an entry (a tag's "Bruges på") and their number at it in its list (rules.ts)
@@ -21,14 +21,17 @@
21
21
  // siteName the title of the start screen and the way back to it
22
22
  // sharing what a page shared on social media shows when it has nothing of its own (Base.astro)
23
23
  import {
24
+ Article,
24
25
  Browser,
25
26
  Buildings,
26
27
  Certificate,
27
- ChartLineUp,
28
28
  DeviceMobile,
29
29
  FileText,
30
30
  House,
31
31
  FolderSimple,
32
+ Gauge,
33
+ GearSix,
34
+ GlobeSimple,
32
35
  List as ListIcon,
33
36
  MagnifyingGlass,
34
37
  Megaphone,
@@ -42,7 +45,6 @@ import {
42
45
  SidebarSimple,
43
46
  Signpost,
44
47
  Smiley,
45
- SlidersHorizontal,
46
48
  Sparkle,
47
49
  SquareHalfBottom,
48
50
  SquaresFour,
@@ -83,15 +85,10 @@ export { HEADINGS as headingLooks, LOOKS as sectionLooks } from '@theme/editor/l
83
85
  export { frontLeads, standardPages, standardPaths, type StandardPage } from '@site/standardPages';
84
86
  export { colorFields, colorPairs, colorRoles, defaultColors } from '@site/colors';
85
87
 
86
- /** Parts of the start screen that are a screen with parts, each a screen of its own: "Privatliv" (privacy.ts), and
87
- * "Analytics" and "Promovering" with the settings of the apps installed that measure (Google Analytics, Google Ads,
88
- * Google Tag Manager) and of those that add a kind of promotion (Calendly, Mailchimp), by their kind
89
- * (AppDefinition.category, the app store's); the apps' own pages under "Apps" only install them. */
90
- export const partScreens: PartScreen[] = [
91
- { id: 'maaling', label: 'Analytics', parts: [], apps: ['analytics'] },
92
- { id: 'promovering', label: 'Promovering', parts: [], apps: ['booking', 'newsletter'] },
93
- privacy,
94
- ];
88
+ /** Parts of the start screen that are a screen with parts, each a screen of its own: "Privatliv" (privacy.ts). The
89
+ * settings of the apps, also those that measure (Google Analytics …) or add a kind of promotion (Calendly, Mailchimp),
90
+ * are on their own app pages under "Apps" (AppList.tsx settingsRoute). */
91
+ export const partScreens: PartScreen[] = [privacy];
95
92
 
96
93
  /** The site's own parts under "Søgemaskineoptimering", each a screen of its own at #seo/<id>, after the editor's
97
94
  * "Videresendelser": "Interne links" (internalLinks.ts). */
@@ -106,6 +103,7 @@ export const sharing = { name: site.name, description: site.description, picture
106
103
 
107
104
  /** The icon of each part on the start screen and in the sidebar, by its id. */
108
105
  export const icons: Record<string, Icon> = {
106
+ home: Gauge,
109
107
  blog: Newspaper,
110
108
  pages: FileText,
111
109
  header: Browser,
@@ -114,11 +112,13 @@ export const icons: Record<string, Icon> = {
114
112
  kategorier: Tag,
115
113
  popups: Megaphone,
116
114
  virksomheden: Buildings,
115
+ // The sections of the main menu that are not also a part.
116
+ indhold: Article,
117
+ globalt: GlobeSimple,
118
+ indstillinger: GearSix,
117
119
  blokke: SquaresFour,
118
120
  filer: FolderSimple,
119
121
  privatliv: ShieldCheck,
120
- maaling: ChartLineUp,
121
- promovering: SlidersHorizontal,
122
122
  lister: ListChecks,
123
123
  footer: SquareHalfBottom,
124
124
  seo: MagnifyingGlass,
@@ -140,44 +140,6 @@ export const icons: Record<string, Icon> = {
140
140
  dele: Shapes,
141
141
  };
142
142
 
143
- // The parts of the site on the start screen, in groups: the content the owner writes and uploads (the
144
- // pages, the blog, its tags, the pop-ups and the files), what is on every page (the
145
- // header with the menus, the footer and "Virksomheden": name, contact and booking), the blocks (their
146
- // shared texts, "Lister", the testimonials and prices they show, which the standard blocks and the apps bring, and
147
- // "Blokindstillinger", which of them can be put in); the settings of the whole site are on a page of their own (settings).
148
- // Each is a collection (collections.ts), except "maaling", "promovering" and "privatliv" (partScreens) and the editor's own "virksomheden" (companyParts), "blokke"
149
- // (blockParts), "filer" (the pictures, PDFs, videos and zip files), "header", "lister", "udseende" (the theme, the colours, the logo
150
- // and icon, the standard pages (appearance) and the editor's parts: Colors.tsx, BrandForm.tsx and Parts.tsx), "blokvalg" (which blocks can be put in, BlockSettings.tsx), "apps"
151
- // (the apps that come with the site, src/apps/, AppList.tsx) and "seo" (the redirects, Redirects.tsx, and seoParts). The editor shows
152
- // each group's parts A–Å by their names in its language (partsIn in Library.tsx), whatever the order here.
153
- export const groups: { title: string; ids: string[] }[] = [
154
- { title: 'Indhold', ids: ['pages', 'blog', 'kategorier', 'popups', 'filer'] },
155
- { title: 'På alle sider', ids: ['header', 'footer', 'virksomheden'] },
156
- { title: 'Blokke', ids: ['blokke', 'lister', 'blokvalg'] },
157
- ];
158
-
159
- /** The settings of the whole site, not on the start screen but on "Indstillinger" (#indstillinger, Library.tsx
160
- * Settings), which the gear in the top bar opens (Screen.tsx SettingsLink); A–Å as a group's. */
161
- export const settings: string[] = [
162
- 'udseende',
163
- 'apps',
164
- 'appstore',
165
- 'morpheme',
166
- 'maaling',
167
- 'promovering',
168
- 'privatliv',
169
- 'seo',
170
- 'avanceret',
171
- 'licenser',
172
- ];
173
-
174
- /** The site's own short lists, on a screen of their own under "Lister" (#lister), with those the standard blocks
175
- * bring ("Udtalelser") and the apps (their lists.ts). */
176
- export const lists: string[] = [];
177
-
178
- /** What is in the header on every page, on a screen of its own under "Header" (#header). */
179
- export const header = ['menu', 'mobilmenu'];
180
-
181
143
  /** The site's own parts under "Udseende" (#udseende), after the logo and icon, each a screen of its own at #udseende/<id>:
182
144
  * "Standardsider", which pages are the front page, the blog page, the 404 page and the contact page, as a list
183
145
  * (standardPageFieldsOf in collections.ts; the privacy policy and the cookie policy are under "Privatliv", privacy.ts). */
@@ -191,3 +153,45 @@ export const appearance: Part[] = [
191
153
  rows: true,
192
154
  },
193
155
  ];
156
+
157
+ // The editor's menu: eight sections in a fixed order, shown in the order written here, never A–Å. "Oversigt" is a link to the
158
+ // start screen (`href`) without a list; the others have a list of parts, each an id: the content the owner writes and
159
+ // uploads ("Indhold": the pages, the blog, the short lists, the pop-ups, the tags, the files, "interne-links" (seoParts)
160
+ // and "videresendelser", Redirects.tsx), what is on every page ("Globalt": the header with the menus, the footer and
161
+ // "Virksomheden": name, contact and booking), the apps ("Apps": the apps that come with the site, src/apps/, AppList.tsx,
162
+ // and "appstore"), the blocks ("Blokke": their shared texts, and "Blokvalg", which of them can be put in), how the site
163
+ // looks ("Udseende": the theme, the icon packs, the motion, the side column (SideColumn.tsx), the colours, the logo and icon, the standard pages
164
+ // (appearance) and the editor's parts: Colors.tsx, BrandForm.tsx and Parts.tsx), the parts of "Privatliv" (privacy.ts)
165
+ // and the settings of the whole site ("Indstillinger").
166
+ // Each part is a collection (collections.ts), except those named above, "privatliv"'s (partScreens) and the editor's own
167
+ // "virksomheden" (companyParts), "blokke" (blockParts), "filer" (the pictures, PDFs, videos and zip files), "header", "lister"
168
+ // and "blokvalg" (BlockSettings.tsx).
169
+ const settingsIds = ['morpheme', 'avanceret'];
170
+ export const groups: { id: string; title: string; href?: string; ids: string[] }[] = [
171
+ { id: 'home', title: 'Oversigt', href: '#', ids: [] },
172
+ {
173
+ id: 'indhold',
174
+ title: 'Indhold',
175
+ ids: ['pages', 'blog', 'lister', 'popups', 'kategorier', 'filer', internalLinks.id, 'videresendelser'],
176
+ },
177
+ { id: 'globalt', title: 'Globalt', ids: ['header', 'footer', 'virksomheden'] },
178
+ { id: 'apps', title: 'Apps', ids: ['apps', 'appstore'] },
179
+ { id: 'blokke', title: 'Blokke', ids: ['blokke', 'blokvalg'] },
180
+ {
181
+ id: 'udseende',
182
+ title: 'Udseende',
183
+ ids: ['temaer', 'ikonpakker', 'bevaegelse', 'sidekolonne', 'farver', 'logo', ...appearance.map((p) => p.id), 'dele'],
184
+ },
185
+ { id: 'privatliv', title: 'Privatliv', ids: privacy.parts.map((p) => p.id) },
186
+ { id: 'indstillinger', title: 'Indstillinger', ids: settingsIds },
187
+ ];
188
+
189
+ /** The settings of the whole site: the ids of the last section, "Indstillinger", written once. */
190
+ export const settings: string[] = settingsIds;
191
+
192
+ /** The site's own short lists, on a screen of their own under "Lister" (#lister), with those the standard blocks
193
+ * bring ("Udtalelser") and the apps (their lists.ts). */
194
+ export const lists: string[] = [];
195
+
196
+ /** What is in the header on every page, on a screen of its own under "Header" (#header). */
197
+ export const header = ['menu', 'mobilmenu'];
@@ -1,5 +1,5 @@
1
1
  // "Privatliv": what the site asks and tells its visitors about their data, as screens of their own (CompanyForm.tsx),
2
- // at #privatliv/<id>: the cookie banner (src/data/cookies.json, read by packages/editor/theme/components/CookieConsent.astro),
2
+ // at #privatliv/<id>, and the cookie list's row: the cookie banner (src/data/cookies.json, read by packages/editor/theme/components/CookieConsent.astro),
3
3
  // "Privatlivssider", which pages are the privacy policy and the cookie policy, as a list like "Standardsider" (in the
4
4
  // same file, config.standardPages; standardPages.ts), and "Samtykker", the visitors' answers in the banner.
5
5
  import { Cookie, FileText, ListChecks } from '@phosphor-icons/react';
@@ -65,6 +65,9 @@ export const privacy: PartScreen = {
65
65
  ...categoryFields('targeting', 'målrettede annoncer'),
66
66
  ],
67
67
  },
68
+ // The site's list of cookies ("Cookies", the standard block "Cookieliste"), a screen of its own under "Lister"'s
69
+ // address space (#liste/<id>): only its row is here, and the list goes back to this screen (Library.tsx).
70
+ { id: 'cookies', label: 'Cookies', icon: Cookie, href: `#liste/${config.lists.cookies}` },
68
71
  {
69
72
  id: 'privatlivssider',
70
73
  label: 'Privatlivssider',
package/defaults/rules.ts CHANGED
@@ -8,6 +8,8 @@ import type { Status } from '../src/FormFrame';
8
8
  import { blocksIn } from '../lib/blocks.mjs';
9
9
  import { readFields, splitFrontmatter } from '../src/frontmatter.mjs';
10
10
  import { siteText } from '../src/i18n';
11
+ import { promotionTags, recordUse, tagUses, taggedWith } from '../src/tagUses.mjs';
12
+ import type { TagFolders } from '../src/tagUses.mjs';
11
13
  import { byText, fixedName, slugOf, type Collection, type Relation } from '../src/schema';
12
14
  import { collections } from '@site/collections';
13
15
  import { config } from '@site/config';
@@ -256,8 +258,15 @@ export function siteProblems(files: { path: string; text: string }[]): string[]
256
258
  return problems;
257
259
  }
258
260
 
259
- // The tags a promotion is shown on the posts of (Promovering, "Vises på": "Blogindlæg med tags").
260
- const promotionTags = (d: Record<string, unknown>): string[] => (d.where === 'tags' && Array.isArray(d.tags) ? d.tags.map(String) : []);
261
+ // Where the tag uses are (tagUses.mjs): the blog and pages folders, the testimonials, the promotions and the
262
+ // file records.
263
+ const TAG_FOLDERS: TagFolders = {
264
+ blog: config.folders.blog,
265
+ pages: config.folders.pages,
266
+ testimonials: 'src/data/testimonials',
267
+ popups: 'src/data/popups',
268
+ fileRecords: config.fileRecords,
269
+ };
261
270
 
262
271
  // Why an entry cannot be deleted (something on the site uses it), with a link to where, or null.
263
272
  export function usedBy(collection: Collection, file: RepoFile, files: RepoFile[]): Status {
@@ -282,9 +291,7 @@ export function usedBy(collection: Collection, file: RepoFile, files: RepoFile[]
282
291
  (home ? (blocksIn(home.text) as { type: string; props: Record<string, unknown> }[]) : []).map((b) => [b.type, b.props]),
283
292
  );
284
293
  if (collection.id === 'kategorier') {
285
- const posts = files.filter(
286
- (f) => f.path.startsWith(`${config.folders.blog}/`) && ((fm(f).categories as string[]) ?? []).includes(slug),
287
- );
294
+ const { posts, pages, testimonials: quotes, records } = taggedWith(files, slug, TAG_FOLDERS);
288
295
  if (posts.length)
289
296
  return {
290
297
  text:
@@ -293,9 +300,15 @@ export function usedBy(collection: Collection, file: RepoFile, files: RepoFile[]
293
300
  : siteText('Tagget bruges på {antal} blogindlæg. Fjern det fra dem først.', { antal: posts.length }),
294
301
  error: true,
295
302
  };
296
- const quotes = files.filter(
297
- (f) => f.path.startsWith('src/data/testimonials/') && ((json(f).categories as string[] | undefined) ?? []).includes(slug),
298
- );
303
+ if (pages.length)
304
+ return {
305
+ text:
306
+ pages.length === 1
307
+ ? siteText('Tagget bruges på en side. Fjern det fra den først.')
308
+ : siteText('Tagget bruges på {antal} sider. Fjern det fra dem først.', { antal: pages.length }),
309
+ error: true,
310
+ ...(pages.length === 1 ? { edit: { to: pages[0].path, text: siteText('Rediger siden') } } : {}),
311
+ };
299
312
  if (quotes.length)
300
313
  return {
301
314
  text:
@@ -304,6 +317,15 @@ export function usedBy(collection: Collection, file: RepoFile, files: RepoFile[]
304
317
  : siteText('Tagget bruges på {antal} udtalelser. Fjern det fra dem først.', { antal: quotes.length }),
305
318
  error: true,
306
319
  };
320
+ if (records.length)
321
+ return {
322
+ text:
323
+ records.length === 1
324
+ ? siteText('Tagget bruges på en fil. Fjern det fra den først.')
325
+ : siteText('Tagget bruges på {antal} filer. Fjern det fra dem først.', { antal: records.length }),
326
+ error: true,
327
+ ...(records.length === 1 ? { edit: { to: recordUse(records[0], config.fileRecords)!.path, text: siteText('Åbn filen') } } : {}),
328
+ };
307
329
  // A promotion shown on the posts with the tag (Promovering, "Blogindlæg med tags").
308
330
  const promotion = files.find(
309
331
  (f) => f.path.startsWith('src/data/popups/') && f.path.endsWith('.json') && promotionTags(json(f)).includes(slug),
@@ -346,7 +368,7 @@ export function usedBy(collection: Collection, file: RepoFile, files: RepoFile[]
346
368
  };
347
369
  }
348
370
  // Any other place under "Bruges på" (usesOf): a "Blogindlæg" or "Udtalelser" block that picks the tag, on a
349
- // page or in a post.
371
+ // page or in a post. Files with the tag are handled above (records), so the first use is never a file record.
350
372
  if (collection.id === 'kategorier') {
351
373
  const use = usesOf(collection, files)(file)[0];
352
374
  if (use) {
@@ -381,47 +403,23 @@ function nameOf(file: RepoFile): string {
381
403
  export type Use = { path: string; name: string };
382
404
 
383
405
  // The places on the site that use an entry, by the entry's file, each once (the tag's "Bruges på", DataForm.tsx,
384
- // and the number at it in its list, Library.tsx); [] for none. For a tag: the blog posts, testimonials and
385
- // promotions that have it, and the pages and posts with a "Blogindlæg" or "Udtalelser" block that
406
+ // and the number at it in its list, Library.tsx); [] for none. For a tag: the blog posts, pages, testimonials,
407
+ // promotions and files (by the route "filer/<path>" and the file's name) that have it, and the pages and posts with a "Blogindlæg" or "Udtalelser" block that
386
408
  // picks it (its tags, or the tag "Alle indlæg" goes to). Made once for the whole list.
387
409
  export function usesOf(collection: Collection, files: RepoFile[]): (file: RepoFile) => Use[] {
388
410
  if (collection.kind === 'files' || collection.id !== 'kategorier') return () => [];
389
- const uses = tagUses(files);
411
+ const uses = tagUses(files, TAG_FOLDERS);
390
412
  return (file) =>
391
413
  [...(uses.get(slugOf(file.path, collection.folder))?.values() ?? [])]
392
- .map((f) => ({ path: f.path, name: nameOf(f) }))
414
+ .map((f) => recordUse(f, config.fileRecords) ?? { path: f.path, name: nameOf(f) })
393
415
  .sort((a, b) => byText(a.name, b.name));
394
416
  }
395
417
 
396
- // The files that use each tag (usesOf), by the tag, each file once.
397
- function tagUses(files: RepoFile[]): Map<string, Map<string, RepoFile>> {
398
- const uses = new Map<string, Map<string, RepoFile>>();
399
- const add = (f: RepoFile, slugs: unknown) => {
400
- for (const slug of Array.isArray(slugs) ? slugs.map(String) : []) {
401
- if (!uses.has(slug)) uses.set(slug, new Map());
402
- uses.get(slug)!.set(f.path, f);
403
- }
404
- };
405
- const block = (f: RepoFile, type: unknown, props: Record<string, unknown>) => {
406
- if (type === 'posts' || type === 'testimonials')
407
- add(f, [...(Array.isArray(props.categories) ? props.categories : []), ...(props.link ? [props.link] : [])]);
408
- };
409
- const inFolder = (f: RepoFile, folder: string, ext: string) => f.path.startsWith(`${folder}/`) && f.path.endsWith(ext);
410
- for (const f of files) {
411
- if (inFolder(f, config.folders.blog, '.md') || inFolder(f, config.folders.pages, '.md')) {
412
- if (inFolder(f, config.folders.blog, '.md')) add(f, fm(f).categories);
413
- for (const b of blocksIn(f.text) as { type: string; props: Record<string, unknown> }[]) block(f, b.type, b.props);
414
- } else if (inFolder(f, 'src/data/testimonials', '.json')) add(f, json(f).categories);
415
- else if (inFolder(f, 'src/data/popups', '.json')) add(f, promotionTags(json(f)));
416
- }
417
- return uses;
418
- }
419
-
420
418
  // The number at the right of an entry in the list of a part, by the entry's file, or null for none
421
419
  // (Library.tsx): at each tag, the number of places that use it (usesOf).
422
420
  export function countsOf(collection: Collection, files: RepoFile[]): (file: RepoFile) => number | null {
423
421
  if (collection.kind === 'files' || collection.id !== 'kategorier') return () => null;
424
- const uses = tagUses(files);
422
+ const uses = tagUses(files, TAG_FOLDERS);
425
423
  return (file) => uses.get(slugOf(file.path, collection.folder))?.size ?? 0;
426
424
  }
427
425
 
@@ -30,14 +30,11 @@ export type StandardPage = {
30
30
  };
31
31
 
32
32
  export const standardPages: StandardPage[] = [
33
- // The top ("Banner") stays first, and is the page's only "Banner".
34
33
  {
35
34
  key: 'front',
36
35
  label: 'Forside',
37
36
  the: 'Forsiden',
38
37
  at: '/',
39
- template: { locked: ['hero'], max: { hero: 1 } },
40
- needs: { type: 'hero', label: 'Banner' },
41
38
  },
42
39
  // The posts ("Blogindlæg"), once.
43
40
  {
package/defaults/texts.ts CHANGED
@@ -226,6 +226,7 @@ export const texts: Record<string, Record<string, string>> = {
226
226
  // "Privatliv" (privacy.ts).
227
227
  Privatliv: 'Privacy',
228
228
  Cookiebanner: 'Cookie banner',
229
+ Cookies: 'Cookies',
229
230
  'Når banneret er fra, er der ingen måling eller annoncer.': 'When the banner is off, there is no analytics or ads.',
230
231
  'Rediger privatlivspolitik': 'Edit privacy policy',
231
232
  Aktiveret: 'Activated',
@@ -290,7 +291,7 @@ export const texts: Record<string, Record<string, string>> = {
290
291
  'Knaptekst (ring)': 'Button text (call)',
291
292
  'Telefonnummeret vises efter teksten.': 'The phone number is shown after the text.',
292
293
  Blokke: 'Blocks',
293
- Standardblokke: 'Default blocks',
294
+ Blokskabeloner: 'Block templates',
294
295
  Knapper: 'Buttons',
295
296
  Ring: 'Call',
296
297
  'Ved Ring vises telefonnummeret efter teksten.': 'With Call, the phone number is shown after the text.',
@@ -300,13 +301,18 @@ export const texts: Record<string, Record<string, string>> = {
300
301
  '{hvad} ({navn})': '{hvad} ({navn})',
301
302
  Indhold: 'Content',
302
303
  Indstillinger: 'Settings',
303
- 'På alle sider': 'On every page',
304
+ Globalt: 'Globals',
305
+ Oversigt: 'Dashboard',
306
+ Apps: 'Apps',
307
+ Udseende: 'Appearance',
304
308
  'Tilføjer et "Læs mere"-link under teksten.': 'Adds a "Læs mere" link under the text.',
305
309
  // Said by the editor from the site's rules (rules.ts).
306
310
  'Rediger forsiden': 'Edit the front page',
307
311
  'Rediger siden': 'Edit the page',
308
312
  'Tagget bruges på et blogindlæg. Fjern det fra det først.': 'The tag is used on a blog post. Remove it from the post first.',
309
313
  'Tagget bruges på {antal} blogindlæg. Fjern det fra dem først.': 'The tag is used on {antal} blog posts. Remove it from them first.',
314
+ 'Tagget bruges på en side. Fjern det fra den først.': 'The tag is used on a page. Remove it from the page first.',
315
+ 'Tagget bruges på {antal} sider. Fjern det fra dem først.': 'The tag is used on {antal} pages. Remove it from them first.',
310
316
  'Tagget bruges på en udtalelse. Fjern det fra den først.': 'The tag is used on a testimonial. Remove it from the testimonial first.',
311
317
  'Tagget bruges i promoveringen "{navn}". Fjern det fra den først.':
312
318
  'The tag is used in the promotion "{navn}". Remove it from the promotion first.',
@@ -327,6 +333,9 @@ export const texts: Record<string, Record<string, string>> = {
327
333
  Standardknapper: 'Default buttons',
328
334
  'Rediger standardknapperne': 'Edit the default buttons',
329
335
  'Tagget bruges på {antal} udtalelser. Fjern det fra dem først.': 'The tag is used on {antal} testimonials. Remove it from them first.',
336
+ 'Tagget bruges på en fil. Fjern det fra den først.': 'The tag is used on a file. Remove it from the file first.',
337
+ 'Tagget bruges på {antal} filer. Fjern det fra dem først.': 'The tag is used on {antal} files. Remove it from them first.',
338
+ 'Åbn filen': 'Open the file',
330
339
  'Tagget vælger indlæggene på forsiden under "Fra bloggen". Vælg et andet først.':
331
340
  'The tag picks the posts on the front page under "Fra bloggen". Pick another one first.',
332
341
  'Indlægget er valgt til forsiden under "Fra bloggen". Vælg et andet først.':
@@ -13,6 +13,8 @@ Morpheme is a website editor for sites built with [Astro](https://astro.build) a
13
13
 
14
14
  <div data-block="points" data-props="{&quot;eyebrow&quot;:&quot;Under the hood&quot;,&quot;heading&quot;:&quot;Three proven parts&quot;,&quot;items&quot;:[{&quot;icon&quot;:&quot;book-open-text&quot;,&quot;title&quot;:&quot;GitHub&quot;,&quot;text&quot;:&quot;Your content lives as plain files in your own repository. Every save is one commit.&quot;},{&quot;icon&quot;:&quot;lightbulb&quot;,&quot;title&quot;:&quot;Astro&quot;,&quot;text&quot;:&quot;Your site is built ahead of time into finished pages that open instantly.&quot;},{&quot;icon&quot;:&quot;lifebuoy&quot;,&quot;title&quot;:&quot;Cloudflare&quot;,&quot;text&quot;:&quot;Pages are served from a location near each visitor, and sign-in runs on Cloudflare Workers.&quot;}]}"></div>
15
15
 
16
+ <p class="block-eyebrow">Step by step</p>
17
+
16
18
  ## How saving works
17
19
 
18
20
  When you tap Save, the editor first checks the page. If something’s missing, it tells you what and highlights the field until it’s fixed. Then your change is saved to GitHub, Cloudflare rebuilds your site, and you get a notification when it’s live.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M3 9h18" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 12h4" /> <path d="M10 8h4" /> <path d="M14 21v-3a2 2 0 0 0-4 0v3" /> <path d="M6 10H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2" /> <path d="M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526" /> <circle cx="12" cy="8" r="6" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v16a2 2 0 0 0 2 2h16" /> <path d="m19 9-5 5-4-4-3 3" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" /> <path d="M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" /> <rect x="2" y="4" width="20" height="16" rx="2" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></g></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 14 4-4" /> <path d="M3.34 19a10 10 0 1 1 17.32 0" /></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1" /> <rect width="7" height="5" x="14" y="3" rx="1" /> <rect width="7" height="9" x="14" y="12" rx="1" /> <rect width="7" height="5" x="3" y="16" rx="1" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /> <path d="M2 12h20" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M15 3v18" /> <path d="m10 15-3-3 3-3" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 5h8" /> <path d="M13 12h8" /> <path d="M13 19h8" /> <path d="m3 17 2 2 4-4" /> <path d="m3 7 2 2 4-4" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z" /> <path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14" /> <path d="M8 6v8" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18h-5" /> <path d="M18 14h-8" /> <path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-4 0v-9a2 2 0 0 1 2-2h2" /> <rect width="8" height="4" x="10" y="6" rx="1" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m14.622 17.897-10.68-2.913" /> <path d="M18.376 2.622a1 1 0 1 1 3.002 3.002L17.36 9.643a.5.5 0 0 0 0 .707l.944.944a2.41 2.41 0 0 1 0 3.408l-.944.944a.5.5 0 0 1-.707 0L8.354 7.348a.5.5 0 0 1 0-.707l.944-.944a2.41 2.41 0 0 1 3.408 0l.944.944a.5.5 0 0 0 .707 0z" /> <path d="M9 8c-1.804 2.71-3.97 3.46-6.583 3.948a.507.507 0 0 0-.302.819l7.32 8.883a1 1 0 0 0 1.185.204C12.735 20.405 16 16.792 16 15" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /> <path d="m9 12 2 2 4-4" /></g></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M9 3v18" /></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M9 3v18" /> <path d="m14 9 3 3-3 3" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M9 3v18" /> <path d="m16 15-3-3 3-3" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M3 15h18" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M15 3v18" /> <path d="m8 9 3 3-3 3" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5" /> <path d="M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244" /> <path d="M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z" /> <circle cx="7.5" cy="7.5" r=".5" fill="currentColor" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="15" cy="12" r="3" /> <rect width="20" height="14" x="2" y="5" rx="7" /></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></g></svg>