@morphemecms/cms 0.1.0 → 0.2.0-next-20261104150211

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 (908) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -21
  3. package/README.md +51 -51
  4. package/astro.mjs +162 -162
  5. package/bin/morpheme.mjs +38 -38
  6. package/client.d.ts +10 -10
  7. package/defaults/blockTexts.ts +33 -33
  8. package/defaults/collections.ts +774 -774
  9. package/defaults/colors.ts +125 -125
  10. package/defaults/company.ts +20 -20
  11. package/defaults/editor.ts +178 -178
  12. package/defaults/internalLinks.ts +30 -30
  13. package/defaults/opens.ts +18 -18
  14. package/defaults/popup.tsx +93 -93
  15. package/defaults/privacy.ts +79 -79
  16. package/defaults/rules.ts +450 -450
  17. package/defaults/schema.ts +16 -16
  18. package/defaults/sitedata.ts +129 -129
  19. package/defaults/standard.ts +106 -106
  20. package/defaults/standardPages.ts +87 -87
  21. package/defaults/texts.ts +481 -481
  22. package/demo/README.md +26 -26
  23. package/demo/src/content/blog/build-a-page-from-blocks.md +19 -19
  24. package/demo/src/content/blog/colors-everyone-can-read.md +19 -19
  25. package/demo/src/content/blog/edit-your-website-from-your-phone.md +21 -21
  26. package/demo/src/content/blog/every-save-is-a-version.md +21 -21
  27. package/demo/src/content/blog/schedule-a-post.md +17 -17
  28. package/demo/src/content/blog/secure-from-day-one.md +17 -17
  29. package/demo/src/content/blog/seo-without-the-guesswork.md +23 -23
  30. package/demo/src/content/blog/switch-themes-without-losing-anything.md +23 -23
  31. package/demo/src/content/blog/the-app-store.md +21 -21
  32. package/demo/src/content/pages/about.md +22 -22
  33. package/demo/src/content/pages/app-store.md +30 -30
  34. package/demo/src/content/pages/blocks.md +98 -98
  35. package/demo/src/content/pages/blog-page.md +11 -11
  36. package/demo/src/content/pages/contact.md +16 -16
  37. package/demo/src/content/pages/get-started.md +20 -20
  38. package/demo/src/content/pages/home.md +26 -26
  39. package/demo/src/content/pages/page-not-found.md +11 -11
  40. package/demo/src/content/pages/privacy-policy.md +24 -24
  41. package/demo/src/data/blocks.json +25 -25
  42. package/demo/src/data/booking.json +6 -6
  43. package/demo/src/data/categories/apps.json +6 -6
  44. package/demo/src/data/categories/design.json +6 -6
  45. package/demo/src/data/categories/editing.json +6 -6
  46. package/demo/src/data/categories/security.json +6 -6
  47. package/demo/src/data/categories/seo.json +6 -6
  48. package/demo/src/data/colors.json +75 -75
  49. package/demo/src/data/contact.json +8 -8
  50. package/demo/src/data/cookies.json +21 -21
  51. package/demo/src/data/footer.json +27 -27
  52. package/demo/src/data/menu.json +35 -35
  53. package/demo/src/data/mobile-menu.json +25 -25
  54. package/demo/src/data/popups/booking-bar.json +13 -13
  55. package/demo/src/data/popups/try-the-editor.json +16 -16
  56. package/demo/src/data/standard-pages.json +8 -8
  57. package/demo/src/data/testimonials/sample-bakery.json +10 -10
  58. package/demo/src/data/testimonials/sample-clinic.json +10 -10
  59. package/demo/src/data/testimonials/sample-studio.json +10 -10
  60. package/demo/src/site/colors.ts +11 -11
  61. package/demo/src/site/config.ts +19 -19
  62. package/icons/lucide/LICENSE +43 -43
  63. package/icons/lucide/address-book.svg +1 -1
  64. package/icons/lucide/app-window.svg +1 -1
  65. package/icons/lucide/arrow-counter-clockwise.svg +1 -1
  66. package/icons/lucide/arrow-down.svg +1 -1
  67. package/icons/lucide/arrow-left.svg +1 -1
  68. package/icons/lucide/arrow-right.svg +1 -1
  69. package/icons/lucide/arrow-square-out.svg +1 -1
  70. package/icons/lucide/arrow-u-up-left.svg +1 -1
  71. package/icons/lucide/arrow-u-up-right.svg +1 -1
  72. package/icons/lucide/arrow-up-right.svg +1 -1
  73. package/icons/lucide/arrow-up.svg +1 -1
  74. package/icons/lucide/arrows-down-up.svg +1 -1
  75. package/icons/lucide/arrows-in-line-horizontal.svg +1 -1
  76. package/icons/lucide/arrows-out-line-horizontal.svg +1 -1
  77. package/icons/lucide/article.svg +1 -1
  78. package/icons/lucide/bank.svg +1 -1
  79. package/icons/lucide/bell.svg +1 -1
  80. package/icons/lucide/bicycle.svg +1 -1
  81. package/icons/lucide/binoculars.svg +1 -1
  82. package/icons/lucide/book-open-text.svg +1 -1
  83. package/icons/lucide/book-open.svg +1 -1
  84. package/icons/lucide/browser.svg +1 -1
  85. package/icons/lucide/buildings.svg +1 -1
  86. package/icons/lucide/bus.svg +1 -1
  87. package/icons/lucide/calendar-blank.svg +1 -1
  88. package/icons/lucide/calendar-check.svg +1 -1
  89. package/icons/lucide/car.svg +1 -1
  90. package/icons/lucide/cards.svg +1 -1
  91. package/icons/lucide/caret-down.svg +1 -1
  92. package/icons/lucide/caret-left.svg +1 -1
  93. package/icons/lucide/caret-right.svg +1 -1
  94. package/icons/lucide/caret-up.svg +1 -1
  95. package/icons/lucide/certificate.svg +1 -1
  96. package/icons/lucide/chart-line-up.svg +1 -1
  97. package/icons/lucide/chats.svg +1 -1
  98. package/icons/lucide/check-circle.svg +1 -1
  99. package/icons/lucide/check.svg +1 -1
  100. package/icons/lucide/circle-half.svg +1 -1
  101. package/icons/lucide/circle.svg +1 -1
  102. package/icons/lucide/clock-counter-clockwise.svg +1 -1
  103. package/icons/lucide/clock.svg +1 -1
  104. package/icons/lucide/code.svg +1 -1
  105. package/icons/lucide/coffee.svg +1 -1
  106. package/icons/lucide/columns.svg +1 -1
  107. package/icons/lucide/cookie.svg +1 -1
  108. package/icons/lucide/copy.svg +1 -1
  109. package/icons/lucide/credit-card.svg +1 -1
  110. package/icons/lucide/currency-circle-dollar.svg +1 -1
  111. package/icons/lucide/cursor-click.svg +1 -1
  112. package/icons/lucide/database.svg +1 -1
  113. package/icons/lucide/desktop.svg +1 -1
  114. package/icons/lucide/device-mobile.svg +1 -1
  115. package/icons/lucide/door-open.svg +1 -1
  116. package/icons/lucide/download-simple.svg +1 -1
  117. package/icons/lucide/envelope-simple.svg +1 -1
  118. package/icons/lucide/eye-slash.svg +1 -1
  119. package/icons/lucide/eye.svg +1 -1
  120. package/icons/lucide/file-pdf.svg +1 -1
  121. package/icons/lucide/file-text.svg +1 -1
  122. package/icons/lucide/file-video.svg +1 -1
  123. package/icons/lucide/file-zip.svg +1 -1
  124. package/icons/lucide/flower-lotus.svg +1 -1
  125. package/icons/lucide/folder-simple.svg +1 -1
  126. package/icons/lucide/funnel.svg +1 -1
  127. package/icons/lucide/gauge.svg +1 -1
  128. package/icons/lucide/gear.svg +1 -1
  129. package/icons/lucide/github-logo.svg +1 -1
  130. package/icons/lucide/globe.svg +1 -1
  131. package/icons/lucide/google-logo.svg +1 -1
  132. package/icons/lucide/graduation-cap.svg +1 -1
  133. package/icons/lucide/hammer.svg +1 -1
  134. package/icons/lucide/hand-heart.svg +1 -1
  135. package/icons/lucide/handshake.svg +1 -1
  136. package/icons/lucide/hard-drives.svg +1 -1
  137. package/icons/lucide/heart.svg +1 -1
  138. package/icons/lucide/house.svg +1 -1
  139. package/icons/lucide/image-square.svg +1 -1
  140. package/icons/lucide/image.svg +1 -1
  141. package/icons/lucide/info.svg +1 -1
  142. package/icons/lucide/keyboard.svg +1 -1
  143. package/icons/lucide/leaf.svg +1 -1
  144. package/icons/lucide/lifebuoy.svg +1 -1
  145. package/icons/lucide/lightbulb.svg +1 -1
  146. package/icons/lucide/link-break.svg +1 -1
  147. package/icons/lucide/link-simple.svg +1 -1
  148. package/icons/lucide/list-bullets.svg +1 -1
  149. package/icons/lucide/list-checks.svg +1 -1
  150. package/icons/lucide/list-numbers.svg +1 -1
  151. package/icons/lucide/list.svg +1 -1
  152. package/icons/lucide/lock-simple-open.svg +1 -1
  153. package/icons/lucide/lock-simple.svg +1 -1
  154. package/icons/lucide/magnifying-glass.svg +1 -1
  155. package/icons/lucide/map-pin.svg +1 -1
  156. package/icons/lucide/map-trifold.svg +1 -1
  157. package/icons/lucide/medal.svg +1 -1
  158. package/icons/lucide/megaphone.svg +1 -1
  159. package/icons/lucide/minus.svg +1 -1
  160. package/icons/lucide/moon.svg +1 -1
  161. package/icons/lucide/newspaper.svg +1 -1
  162. package/icons/lucide/pack.json +23 -23
  163. package/icons/lucide/paint-brush-broad.svg +1 -1
  164. package/icons/lucide/palette.svg +1 -1
  165. package/icons/lucide/paper-plane-tilt.svg +1 -1
  166. package/icons/lucide/pencil-simple.svg +1 -1
  167. package/icons/lucide/phone.svg +1 -1
  168. package/icons/lucide/play-fill.svg +1 -1
  169. package/icons/lucide/plus.svg +1 -1
  170. package/icons/lucide/prohibit.svg +1 -1
  171. package/icons/lucide/puzzle-piece.svg +1 -1
  172. package/icons/lucide/question.svg +1 -1
  173. package/icons/lucide/quotes.svg +1 -1
  174. package/icons/lucide/rows.svg +1 -1
  175. package/icons/lucide/ruler.svg +1 -1
  176. package/icons/lucide/scan.svg +1 -1
  177. package/icons/lucide/shapes.svg +1 -1
  178. package/icons/lucide/share-network.svg +1 -1
  179. package/icons/lucide/shield-check.svg +1 -1
  180. package/icons/lucide/sidebar-simple.svg +1 -1
  181. package/icons/lucide/sign-out.svg +1 -1
  182. package/icons/lucide/signpost.svg +1 -1
  183. package/icons/lucide/sliders-horizontal.svg +1 -1
  184. package/icons/lucide/smiley.svg +1 -1
  185. package/icons/lucide/sparkle.svg +1 -1
  186. package/icons/lucide/square-half-bottom.svg +1 -1
  187. package/icons/lucide/square.svg +1 -1
  188. package/icons/lucide/squares-four.svg +1 -1
  189. package/icons/lucide/sticker.svg +1 -1
  190. package/icons/lucide/storefront.svg +1 -1
  191. package/icons/lucide/sun.svg +1 -1
  192. package/icons/lucide/swatches.svg +1 -1
  193. package/icons/lucide/tag.svg +1 -1
  194. package/icons/lucide/text-aa.svg +1 -1
  195. package/icons/lucide/text-align-left.svg +1 -1
  196. package/icons/lucide/text-b.svg +1 -1
  197. package/icons/lucide/text-h-one.svg +1 -1
  198. package/icons/lucide/text-h-three.svg +1 -1
  199. package/icons/lucide/text-h-two.svg +1 -1
  200. package/icons/lucide/text-italic.svg +1 -1
  201. package/icons/lucide/text-t.svg +1 -1
  202. package/icons/lucide/ticket.svg +1 -1
  203. package/icons/lucide/toggle-right.svg +1 -1
  204. package/icons/lucide/train.svg +1 -1
  205. package/icons/lucide/trash.svg +1 -1
  206. package/icons/lucide/upload-simple.svg +1 -1
  207. package/icons/lucide/user-circle.svg +1 -1
  208. package/icons/lucide/user.svg +1 -1
  209. package/icons/lucide/users-three.svg +1 -1
  210. package/icons/lucide/users.svg +1 -1
  211. package/icons/lucide/video-camera.svg +1 -1
  212. package/icons/lucide/warning-circle.svg +1 -1
  213. package/icons/lucide/warning.svg +1 -1
  214. package/icons/lucide/wave-sine.svg +1 -1
  215. package/icons/lucide/wheelchair.svg +1 -1
  216. package/icons/lucide/wrench.svg +1 -1
  217. package/icons/lucide/x.svg +1 -1
  218. package/icons/material-symbols/LICENSE +201 -201
  219. package/icons/material-symbols/address-book.svg +1 -1
  220. package/icons/material-symbols/app-window.svg +1 -1
  221. package/icons/material-symbols/arrow-counter-clockwise.svg +1 -1
  222. package/icons/material-symbols/arrow-down.svg +1 -1
  223. package/icons/material-symbols/arrow-left.svg +1 -1
  224. package/icons/material-symbols/arrow-right.svg +1 -1
  225. package/icons/material-symbols/arrow-square-out.svg +1 -1
  226. package/icons/material-symbols/arrow-u-up-left.svg +1 -1
  227. package/icons/material-symbols/arrow-u-up-right.svg +1 -1
  228. package/icons/material-symbols/arrow-up-right.svg +1 -1
  229. package/icons/material-symbols/arrow-up.svg +1 -1
  230. package/icons/material-symbols/arrows-down-up.svg +1 -1
  231. package/icons/material-symbols/arrows-in-line-horizontal.svg +1 -1
  232. package/icons/material-symbols/arrows-out-line-horizontal.svg +1 -1
  233. package/icons/material-symbols/article.svg +1 -1
  234. package/icons/material-symbols/bank.svg +1 -1
  235. package/icons/material-symbols/bell.svg +1 -1
  236. package/icons/material-symbols/bicycle.svg +1 -1
  237. package/icons/material-symbols/binoculars.svg +1 -1
  238. package/icons/material-symbols/book-open-text.svg +1 -1
  239. package/icons/material-symbols/book-open.svg +1 -1
  240. package/icons/material-symbols/browser.svg +1 -1
  241. package/icons/material-symbols/buildings.svg +1 -1
  242. package/icons/material-symbols/bus.svg +1 -1
  243. package/icons/material-symbols/calendar-blank.svg +1 -1
  244. package/icons/material-symbols/calendar-check.svg +1 -1
  245. package/icons/material-symbols/car.svg +1 -1
  246. package/icons/material-symbols/cards.svg +1 -1
  247. package/icons/material-symbols/caret-down.svg +1 -1
  248. package/icons/material-symbols/caret-left.svg +1 -1
  249. package/icons/material-symbols/caret-right.svg +1 -1
  250. package/icons/material-symbols/caret-up.svg +1 -1
  251. package/icons/material-symbols/certificate.svg +1 -1
  252. package/icons/material-symbols/chart-line-up.svg +1 -1
  253. package/icons/material-symbols/chats.svg +1 -1
  254. package/icons/material-symbols/check-circle.svg +1 -1
  255. package/icons/material-symbols/check.svg +1 -1
  256. package/icons/material-symbols/circle-half.svg +1 -1
  257. package/icons/material-symbols/circle.svg +1 -1
  258. package/icons/material-symbols/clock-counter-clockwise.svg +1 -1
  259. package/icons/material-symbols/clock.svg +1 -1
  260. package/icons/material-symbols/code.svg +1 -1
  261. package/icons/material-symbols/coffee.svg +1 -1
  262. package/icons/material-symbols/columns.svg +1 -1
  263. package/icons/material-symbols/cookie.svg +1 -1
  264. package/icons/material-symbols/copy.svg +1 -1
  265. package/icons/material-symbols/credit-card.svg +1 -1
  266. package/icons/material-symbols/currency-circle-dollar.svg +1 -1
  267. package/icons/material-symbols/cursor-click.svg +1 -1
  268. package/icons/material-symbols/database.svg +1 -1
  269. package/icons/material-symbols/desktop.svg +1 -1
  270. package/icons/material-symbols/device-mobile.svg +1 -1
  271. package/icons/material-symbols/door-open.svg +1 -1
  272. package/icons/material-symbols/download-simple.svg +1 -1
  273. package/icons/material-symbols/envelope-simple.svg +1 -1
  274. package/icons/material-symbols/eye-slash.svg +1 -1
  275. package/icons/material-symbols/eye.svg +1 -1
  276. package/icons/material-symbols/file-pdf.svg +1 -1
  277. package/icons/material-symbols/file-text.svg +1 -1
  278. package/icons/material-symbols/file-video.svg +1 -1
  279. package/icons/material-symbols/file-zip.svg +1 -1
  280. package/icons/material-symbols/flower-lotus.svg +1 -1
  281. package/icons/material-symbols/folder-simple.svg +1 -1
  282. package/icons/material-symbols/funnel.svg +1 -1
  283. package/icons/material-symbols/gauge.svg +1 -1
  284. package/icons/material-symbols/gear.svg +1 -1
  285. package/icons/material-symbols/github-logo.svg +1 -1
  286. package/icons/material-symbols/globe.svg +1 -1
  287. package/icons/material-symbols/google-logo.svg +1 -1
  288. package/icons/material-symbols/graduation-cap.svg +1 -1
  289. package/icons/material-symbols/hammer.svg +1 -1
  290. package/icons/material-symbols/hand-heart.svg +1 -1
  291. package/icons/material-symbols/handshake.svg +1 -1
  292. package/icons/material-symbols/hard-drives.svg +1 -1
  293. package/icons/material-symbols/heart.svg +1 -1
  294. package/icons/material-symbols/house.svg +1 -1
  295. package/icons/material-symbols/image-square.svg +1 -1
  296. package/icons/material-symbols/image.svg +1 -1
  297. package/icons/material-symbols/info.svg +1 -1
  298. package/icons/material-symbols/keyboard.svg +1 -1
  299. package/icons/material-symbols/leaf.svg +1 -1
  300. package/icons/material-symbols/lifebuoy.svg +1 -1
  301. package/icons/material-symbols/lightbulb.svg +1 -1
  302. package/icons/material-symbols/link-break.svg +1 -1
  303. package/icons/material-symbols/link-simple.svg +1 -1
  304. package/icons/material-symbols/list-bullets.svg +1 -1
  305. package/icons/material-symbols/list-checks.svg +1 -1
  306. package/icons/material-symbols/list-numbers.svg +1 -1
  307. package/icons/material-symbols/list.svg +1 -1
  308. package/icons/material-symbols/lock-simple-open.svg +1 -1
  309. package/icons/material-symbols/lock-simple.svg +1 -1
  310. package/icons/material-symbols/magnifying-glass.svg +1 -1
  311. package/icons/material-symbols/map-pin.svg +1 -1
  312. package/icons/material-symbols/map-trifold.svg +1 -1
  313. package/icons/material-symbols/medal.svg +1 -1
  314. package/icons/material-symbols/megaphone.svg +1 -1
  315. package/icons/material-symbols/minus.svg +1 -1
  316. package/icons/material-symbols/moon.svg +1 -1
  317. package/icons/material-symbols/newspaper.svg +1 -1
  318. package/icons/material-symbols/pack.json +29 -29
  319. package/icons/material-symbols/paint-brush-broad.svg +1 -1
  320. package/icons/material-symbols/palette.svg +1 -1
  321. package/icons/material-symbols/paper-plane-tilt.svg +1 -1
  322. package/icons/material-symbols/pencil-simple.svg +1 -1
  323. package/icons/material-symbols/phone.svg +1 -1
  324. package/icons/material-symbols/play-fill.svg +1 -1
  325. package/icons/material-symbols/plus.svg +1 -1
  326. package/icons/material-symbols/prohibit.svg +1 -1
  327. package/icons/material-symbols/puzzle-piece.svg +1 -1
  328. package/icons/material-symbols/question.svg +1 -1
  329. package/icons/material-symbols/quotes.svg +1 -1
  330. package/icons/material-symbols/rows.svg +1 -1
  331. package/icons/material-symbols/ruler.svg +1 -1
  332. package/icons/material-symbols/scan.svg +1 -1
  333. package/icons/material-symbols/shapes.svg +1 -1
  334. package/icons/material-symbols/share-network.svg +1 -1
  335. package/icons/material-symbols/shield-check.svg +1 -1
  336. package/icons/material-symbols/sidebar-simple.svg +1 -1
  337. package/icons/material-symbols/sign-out.svg +1 -1
  338. package/icons/material-symbols/signpost.svg +1 -1
  339. package/icons/material-symbols/sliders-horizontal.svg +1 -1
  340. package/icons/material-symbols/smiley.svg +1 -1
  341. package/icons/material-symbols/sparkle.svg +1 -1
  342. package/icons/material-symbols/square-half-bottom.svg +1 -1
  343. package/icons/material-symbols/square.svg +1 -1
  344. package/icons/material-symbols/squares-four.svg +1 -1
  345. package/icons/material-symbols/sticker.svg +1 -1
  346. package/icons/material-symbols/storefront.svg +1 -1
  347. package/icons/material-symbols/sun.svg +1 -1
  348. package/icons/material-symbols/swatches.svg +1 -1
  349. package/icons/material-symbols/tag.svg +1 -1
  350. package/icons/material-symbols/text-aa.svg +1 -1
  351. package/icons/material-symbols/text-align-left.svg +1 -1
  352. package/icons/material-symbols/text-b.svg +1 -1
  353. package/icons/material-symbols/text-h-one.svg +1 -1
  354. package/icons/material-symbols/text-h-three.svg +1 -1
  355. package/icons/material-symbols/text-h-two.svg +1 -1
  356. package/icons/material-symbols/text-italic.svg +1 -1
  357. package/icons/material-symbols/text-t.svg +1 -1
  358. package/icons/material-symbols/ticket.svg +1 -1
  359. package/icons/material-symbols/toggle-right.svg +1 -1
  360. package/icons/material-symbols/train.svg +1 -1
  361. package/icons/material-symbols/trash.svg +1 -1
  362. package/icons/material-symbols/upload-simple.svg +1 -1
  363. package/icons/material-symbols/user-circle.svg +1 -1
  364. package/icons/material-symbols/user.svg +1 -1
  365. package/icons/material-symbols/users-three.svg +1 -1
  366. package/icons/material-symbols/users.svg +1 -1
  367. package/icons/material-symbols/video-camera.svg +1 -1
  368. package/icons/material-symbols/warning-circle.svg +1 -1
  369. package/icons/material-symbols/warning.svg +1 -1
  370. package/icons/material-symbols/wave-sine.svg +1 -1
  371. package/icons/material-symbols/wheelchair.svg +1 -1
  372. package/icons/material-symbols/wrench.svg +1 -1
  373. package/icons/material-symbols/x.svg +1 -1
  374. package/icons/phosphor/pack.json +8 -8
  375. package/lib/apps.mjs +142 -139
  376. package/lib/apps.test.d.mts +1 -0
  377. package/lib/apps.test.mjs +50 -0
  378. package/lib/blocks.mjs +90 -90
  379. package/lib/caseCollisions.d.mts +4 -0
  380. package/lib/caseCollisions.mjs +62 -0
  381. package/lib/caseCollisions.test.d.mts +1 -0
  382. package/lib/caseCollisions.test.mjs +74 -0
  383. package/lib/colors.ts +86 -86
  384. package/lib/contactBar.ts +18 -18
  385. package/lib/contracts.d.mts +15 -0
  386. package/lib/contracts.mjs +17 -0
  387. package/lib/cookieCategories.mjs +28 -28
  388. package/lib/crop.mjs +83 -83
  389. package/lib/csp.mjs +130 -130
  390. package/lib/cspSources.mjs +60 -60
  391. package/lib/dynamic-colors.ts +426 -426
  392. package/lib/git.mjs +64 -64
  393. package/lib/hooks.ts +51 -51
  394. package/lib/inline.ts +20 -20
  395. package/lib/license-texts/Apache-2.0.txt +190 -190
  396. package/lib/license-texts/BSD-2-Clause.txt +24 -24
  397. package/lib/license-texts/BSD-3-Clause.txt +28 -28
  398. package/lib/license-texts/ISC.txt +15 -15
  399. package/lib/license-texts/MIT.txt +21 -21
  400. package/lib/licenses.d.mts +1 -1
  401. package/lib/licenses.mjs +683 -683
  402. package/lib/markdown.ts +42 -42
  403. package/lib/motion-schemes.mjs +5 -5
  404. package/lib/motion.ts +131 -131
  405. package/lib/notice.mjs +34 -34
  406. package/lib/scheduled.mjs +37 -37
  407. package/lib/snackbar.ts +149 -149
  408. package/lib/status.mjs +18 -18
  409. package/lib/storeEntries.d.mts +18 -0
  410. package/lib/storeEntries.mjs +64 -0
  411. package/lib/storeEntries.test.d.mts +1 -0
  412. package/lib/storeEntries.test.mjs +71 -0
  413. package/lib/svg.mjs +231 -231
  414. package/lib/text-colors.mjs +43 -43
  415. package/lib/video-id.mjs +72 -72
  416. package/lib/video.mjs +91 -91
  417. package/package.json +136 -131
  418. package/pages/404.astro +10 -10
  419. package/pages/[...slug].astro +51 -51
  420. package/pages/_headers.txt +31 -31
  421. package/pages/blog/index.astro +10 -10
  422. package/pages/blog/kategori/[category].astro +18 -18
  423. package/pages/editor/code-images.json.js +25 -25
  424. package/pages/editor/index.astro +75 -75
  425. package/pages/editor/manifest.webmanifest.js +23 -23
  426. package/pages/editor/sw.js +33 -33
  427. package/pages/editor/sw.js.js +4 -4
  428. package/pages/editor/thumbnails.json.js +21 -21
  429. package/pages/editor/version.json.js +22 -22
  430. package/pages/headers.js +4 -4
  431. package/pages/index.astro +10 -10
  432. package/pages/ny.astro +6 -6
  433. package/pages/robots.txt.js +3 -3
  434. package/pages/rss.xml.js +20 -20
  435. package/scripts/app-screenshots.mjs +164 -164
  436. package/scripts/app-workers.mjs +6 -6
  437. package/scripts/bin.mjs +12 -12
  438. package/scripts/built-site.mjs +134 -99
  439. package/scripts/check-consent.mjs +113 -113
  440. package/scripts/check-content.mjs +151 -149
  441. package/scripts/check-demo.mjs +24 -24
  442. package/scripts/check-store.mjs +160 -0
  443. package/scripts/check-themes.mjs +45 -45
  444. package/scripts/check-worker.mjs +569 -569
  445. package/scripts/compare-html.mjs +81 -81
  446. package/scripts/declarations.mjs +10 -0
  447. package/scripts/declarations.test.mjs +32 -0
  448. package/scripts/demo-site.mjs +42 -42
  449. package/scripts/editor-icons.mjs +56 -56
  450. package/scripts/editor-roundtrip.mjs +134 -134
  451. package/scripts/editor-screens.mjs +101 -101
  452. package/scripts/git-env.mjs +9 -9
  453. package/scripts/icon-packs.mjs +151 -151
  454. package/scripts/link-counts.mjs +22 -22
  455. package/scripts/page-colors.mjs +42 -42
  456. package/scripts/releases.mjs +80 -80
  457. package/scripts/responsive-images.mjs +94 -94
  458. package/scripts/scheduled-list.mjs +15 -15
  459. package/scripts/theme-screenshots.mjs +88 -88
  460. package/scripts/types.mjs +17 -21
  461. package/scripts/video-thumbnails.mjs +89 -89
  462. package/scripts/worker-modules.mjs +34 -34
  463. package/site/appCookies.mjs +74 -74
  464. package/site/apps.ts +21 -21
  465. package/site/autolinks.mjs +200 -200
  466. package/site/blockScripts.ts +31 -31
  467. package/site/categories.ts +16 -16
  468. package/site/consent.ts +26 -26
  469. package/site/content.config.ts +84 -84
  470. package/site/content.ts +104 -104
  471. package/site/cookieList.ts +17 -17
  472. package/site/defineSite.d.mts +2 -14
  473. package/site/defineSite.mjs +131 -136
  474. package/site/directives/interact.mjs +31 -31
  475. package/site/editor-version.mjs +26 -26
  476. package/site/fill.ts +13 -13
  477. package/site/format.ts +11 -11
  478. package/site/home.ts +66 -66
  479. package/site/iconPacks.mjs +43 -43
  480. package/site/icons.mjs +605 -605
  481. package/site/images.mjs +157 -157
  482. package/site/motion.ts +13 -13
  483. package/site/noindex.mjs +21 -21
  484. package/site/pageColors.ts +110 -110
  485. package/site/popups.ts +98 -98
  486. package/site/schema.ts +189 -189
  487. package/site/sections.ts +3 -3
  488. package/site/seo.ts +28 -28
  489. package/site/site.ts +76 -76
  490. package/site/siteConfig.mjs +8 -8
  491. package/site/siteCsp.mjs +75 -75
  492. package/site/siteModules.mjs +27 -27
  493. package/site/standardPages.ts +59 -59
  494. package/site/storeTheme.d.mts +6 -0
  495. package/site/storeTheme.mjs +29 -0
  496. package/site/storeTheme.test.d.mts +1 -0
  497. package/site/storeTheme.test.mjs +53 -0
  498. package/site/testimonials.ts +48 -48
  499. package/site/text.ts +43 -43
  500. package/site/theme.mjs +483 -472
  501. package/site/tracking.client.ts +225 -225
  502. package/site/tracking.ts +9 -9
  503. package/src/ActionParts.tsx +205 -205
  504. package/src/Advanced.tsx +152 -152
  505. package/src/App.tsx +901 -904
  506. package/src/AppList.tsx +1362 -1345
  507. package/src/BlockDialog.tsx +360 -360
  508. package/src/BlockParts.tsx +652 -652
  509. package/src/BlockSettings.tsx +533 -533
  510. package/src/BrandForm.tsx +164 -164
  511. package/src/Colors.tsx +476 -476
  512. package/src/CommunicationParts.tsx +199 -199
  513. package/src/CompanyForm.tsx +209 -209
  514. package/src/Consents.tsx +175 -175
  515. package/src/ContainmentParts.tsx +185 -185
  516. package/src/Crop.tsx +152 -152
  517. package/src/DataForm.tsx +434 -434
  518. package/src/Deleted.tsx +177 -177
  519. package/src/EntryForm.tsx +740 -740
  520. package/src/FileView.tsx +30 -30
  521. package/src/FormFrame.tsx +237 -237
  522. package/src/Keywords.tsx +225 -225
  523. package/src/Library.tsx +1563 -1563
  524. package/src/Licenses.tsx +336 -336
  525. package/src/LinkSheet.tsx +138 -138
  526. package/src/MarkdownEditor.tsx +1075 -1075
  527. package/src/Media.tsx +677 -677
  528. package/src/NavigationParts.tsx +350 -350
  529. package/src/NavigationRestParts.tsx +247 -247
  530. package/src/Notifications.tsx +59 -59
  531. package/src/PagePicker.tsx +136 -136
  532. package/src/Parts.tsx +991 -991
  533. package/src/PartsShown.tsx +168 -168
  534. package/src/PickerParts.tsx +183 -183
  535. package/src/Redirects.tsx +450 -450
  536. package/src/Screen.tsx +184 -184
  537. package/src/SectionsField.tsx +65 -65
  538. package/src/SelectionParts.tsx +134 -134
  539. package/src/SeoScores.tsx +184 -184
  540. package/src/Sheet.tsx +35 -35
  541. package/src/ThemeStore.tsx +376 -376
  542. package/src/Tokens.tsx +346 -346
  543. package/src/Versions.tsx +214 -214
  544. package/src/addresses.ts +75 -75
  545. package/src/allIcons.ts +7 -7
  546. package/src/appFiles.ts +29 -29
  547. package/src/apps.ts +281 -281
  548. package/src/ask.tsx +123 -123
  549. package/src/blocks.ts +251 -251
  550. package/src/brand.ts +153 -153
  551. package/src/cache.ts +42 -42
  552. package/src/check.ts +285 -285
  553. package/src/checkSite.ts +256 -256
  554. package/src/{consents.ts → consentData.ts} +26 -26
  555. package/src/cookieList.ts +70 -70
  556. package/src/countries.ts +59 -59
  557. package/src/deploy.ts +155 -155
  558. package/src/editor.css +602 -602
  559. package/src/examples.ts +85 -85
  560. package/src/fields.tsx +942 -942
  561. package/src/frontmatter.mjs +42 -42
  562. package/src/github.ts +376 -376
  563. package/src/i18n/da-DK/advanced.ts +28 -28
  564. package/src/i18n/da-DK/app.ts +71 -71
  565. package/src/i18n/da-DK/apps.ts +90 -90
  566. package/src/i18n/da-DK/blockSettings.ts +38 -38
  567. package/src/i18n/da-DK/blocks.ts +408 -408
  568. package/src/i18n/da-DK/check.ts +25 -25
  569. package/src/i18n/da-DK/colors.ts +64 -64
  570. package/src/i18n/da-DK/common.ts +118 -118
  571. package/src/i18n/da-DK/company.ts +24 -24
  572. package/src/i18n/da-DK/consents.ts +23 -23
  573. package/src/i18n/da-DK/forms.ts +116 -116
  574. package/src/i18n/da-DK/history.ts +35 -35
  575. package/src/i18n/da-DK/index.ts +49 -49
  576. package/src/i18n/da-DK/keywords.ts +19 -19
  577. package/src/i18n/da-DK/library.ts +135 -135
  578. package/src/i18n/da-DK/licenses.ts +25 -25
  579. package/src/i18n/da-DK/media.ts +79 -79
  580. package/src/i18n/da-DK/parts.ts +696 -696
  581. package/src/i18n/da-DK/preview.ts +13 -13
  582. package/src/i18n/da-DK/redirects.ts +51 -51
  583. package/src/i18n/da-DK/seo.ts +20 -20
  584. package/src/i18n/da-DK/text.ts +35 -35
  585. package/src/i18n/da-DK/theme.ts +41 -41
  586. package/src/i18n/en-US/advanced.ts +32 -32
  587. package/src/i18n/en-US/app.ts +84 -84
  588. package/src/i18n/en-US/apps.ts +134 -134
  589. package/src/i18n/en-US/blockSettings.ts +48 -48
  590. package/src/i18n/en-US/blocks.ts +441 -441
  591. package/src/i18n/en-US/check.ts +31 -31
  592. package/src/i18n/en-US/colors.ts +82 -82
  593. package/src/i18n/en-US/common.ts +144 -144
  594. package/src/i18n/en-US/company.ts +25 -25
  595. package/src/i18n/en-US/consents.ts +25 -25
  596. package/src/i18n/en-US/forms.ts +145 -145
  597. package/src/i18n/en-US/history.ts +43 -43
  598. package/src/i18n/en-US/index.ts +51 -51
  599. package/src/i18n/en-US/keywords.ts +24 -24
  600. package/src/i18n/en-US/library.ts +176 -176
  601. package/src/i18n/en-US/licenses.ts +36 -36
  602. package/src/i18n/en-US/media.ts +80 -80
  603. package/src/i18n/en-US/parts.ts +718 -718
  604. package/src/i18n/en-US/preview.ts +15 -15
  605. package/src/i18n/en-US/redirects.ts +66 -66
  606. package/src/i18n/en-US/seo.ts +26 -26
  607. package/src/i18n/en-US/text.ts +41 -41
  608. package/src/i18n/en-US/theme.ts +41 -41
  609. package/src/i18n/index.ts +124 -124
  610. package/src/icon.tsx +160 -160
  611. package/src/iconPacks.ts +89 -89
  612. package/src/images.ts +163 -163
  613. package/src/{licenses.ts → licenseData.ts} +79 -79
  614. package/src/markdown.mjs +204 -204
  615. package/src/nav.ts +68 -68
  616. package/src/notFound.ts +28 -28
  617. package/src/{notifications.ts → notificationData.ts} +223 -223
  618. package/src/pagePreview.tsx +204 -204
  619. package/src/r2Caching.ts +55 -55
  620. package/src/{redirects.ts → redirectData.ts} +149 -149
  621. package/src/registry.tsx +188 -188
  622. package/src/schema.ts +417 -417
  623. package/src/sections.mjs +303 -303
  624. package/src/seo.ts +333 -333
  625. package/src/shared.ts +44 -44
  626. package/src/sidebar.ts +31 -31
  627. package/src/site.ts +317 -317
  628. package/src/standard/Block.astro +19 -19
  629. package/src/standard/BlockSite.astro +18 -18
  630. package/src/standard/accordion/index.tsx +47 -47
  631. package/src/standard/accordion/load.ts +23 -23
  632. package/src/standard/accordion/shared.ts +15 -15
  633. package/src/standard/accordion/view.ts +32 -32
  634. package/src/standard/buttons/fields.ts +98 -98
  635. package/src/standard/buttons/index.tsx +23 -23
  636. package/src/standard/buttons/load.ts +34 -34
  637. package/src/standard/buttons/shared.ts +12 -12
  638. package/src/standard/buttons/view.ts +30 -30
  639. package/src/standard/cards/index.tsx +71 -71
  640. package/src/standard/cards/load.ts +17 -17
  641. package/src/standard/cards/shared.ts +27 -27
  642. package/src/standard/cards/view.ts +29 -29
  643. package/src/standard/checklist/index.tsx +27 -27
  644. package/src/standard/checklist/load.ts +5 -5
  645. package/src/standard/checklist/view.ts +13 -13
  646. package/src/standard/cookies/index.tsx +17 -17
  647. package/src/standard/cookies/list.ts +56 -56
  648. package/src/standard/cookies/load.ts +11 -11
  649. package/src/standard/cookies/shared.ts +20 -20
  650. package/src/standard/cookies/view.ts +28 -28
  651. package/src/standard/faq/index.tsx +29 -29
  652. package/src/standard/faq/load.ts +14 -14
  653. package/src/standard/faq/view.ts +21 -21
  654. package/src/standard/head.ts +23 -23
  655. package/src/standard/hero/fields.ts +48 -48
  656. package/src/standard/hero/index.tsx +57 -57
  657. package/src/standard/hero/load.ts +23 -23
  658. package/src/standard/hero/view.ts +38 -38
  659. package/src/standard/links/index.tsx +41 -41
  660. package/src/standard/links/load.ts +17 -17
  661. package/src/standard/links/view.ts +25 -25
  662. package/src/standard/list/index.tsx +50 -50
  663. package/src/standard/list/load.ts +5 -5
  664. package/src/standard/list/view.ts +18 -18
  665. package/src/standard/load.ts +7 -7
  666. package/src/standard/notice/index.tsx +30 -30
  667. package/src/standard/notice/load.ts +16 -16
  668. package/src/standard/notice/view.ts +10 -10
  669. package/src/standard/points/index.tsx +42 -42
  670. package/src/standard/points/load.ts +5 -5
  671. package/src/standard/points/view.ts +12 -12
  672. package/src/standard/posts/index.tsx +132 -132
  673. package/src/standard/posts/load.ts +36 -36
  674. package/src/standard/posts/shared.ts +21 -21
  675. package/src/standard/posts/view.ts +63 -63
  676. package/src/standard/pricecards/index.tsx +57 -57
  677. package/src/standard/pricecards/load.ts +11 -11
  678. package/src/standard/pricecards/view.ts +50 -50
  679. package/src/standard/sharedTexts.ts +6 -6
  680. package/src/standard/site.ts +124 -124
  681. package/src/standard/siteLooks.ts +19 -19
  682. package/src/standard/sites.ts +13 -13
  683. package/src/standard/steps/index.tsx +28 -28
  684. package/src/standard/steps/load.ts +5 -5
  685. package/src/standard/steps/view.ts +11 -11
  686. package/src/standard/testimonials/index.tsx +87 -87
  687. package/src/standard/testimonials/list.ts +43 -43
  688. package/src/standard/testimonials/load.ts +58 -58
  689. package/src/standard/testimonials/shared.ts +13 -13
  690. package/src/standard/testimonials/view.ts +76 -76
  691. package/src/standard/themeBlocks.ts +13 -13
  692. package/src/standard/video/index.tsx +23 -23
  693. package/src/standard/video/load.ts +10 -10
  694. package/src/standard/video/view.ts +42 -42
  695. package/src/standard/view.tsx +105 -105
  696. package/src/standard/views.ts +37 -37
  697. package/src/store.ts +479 -499
  698. package/src/theme.ts +90 -90
  699. package/src/themeUi.ts +97 -97
  700. package/src/themes.ts +91 -91
  701. package/src/ui.tsx +3402 -3402
  702. package/src/virtual.d.ts +5 -5
  703. package/src/when.ts +22 -22
  704. package/starter/.githooks/pre-push +4 -4
  705. package/starter/.nvmrc +1 -1
  706. package/starter/.prettierignore +11 -11
  707. package/starter/.prettierrc.json +7 -7
  708. package/starter/README.md +27 -26
  709. package/starter/astro.config.mjs +5 -5
  710. package/starter/package.json +67 -67
  711. package/starter/public/_redirects +1 -1
  712. package/starter/public/favicon.svg +1 -1
  713. package/starter/public/logo.svg +1 -1
  714. package/starter/src/content/pages/blog.md +8 -8
  715. package/starter/src/content/pages/forside.md +9 -9
  716. package/starter/src/content/pages/kontakt.md +7 -7
  717. package/starter/src/content/pages/privatlivspolitik.md +9 -9
  718. package/starter/src/content/pages/siden-findes-ikke.md +7 -7
  719. package/starter/src/content.config.ts +2 -2
  720. package/starter/src/data/apps.json +1 -1
  721. package/starter/src/data/block-settings.json +3 -3
  722. package/starter/src/data/blocks.json +1 -1
  723. package/starter/src/data/booking.json +6 -6
  724. package/starter/src/data/colors.json +70 -70
  725. package/starter/src/data/contact.json +8 -8
  726. package/starter/src/data/cookies.json +21 -21
  727. package/starter/src/data/csp.json +1 -1
  728. package/starter/src/data/footer.json +15 -15
  729. package/starter/src/data/internal-links.json +4 -4
  730. package/starter/src/data/menu.json +4 -4
  731. package/starter/src/data/mobile-menu.json +4 -4
  732. package/starter/src/data/old-addresses.json +1 -1
  733. package/starter/src/data/redirects.json +1 -1
  734. package/starter/src/data/standard-pages.json +8 -8
  735. package/starter/src/data/theme.json +1 -1
  736. package/starter/src/env.d.ts +2 -2
  737. package/starter/src/site/config.ts +13 -13
  738. package/starter/src/site/worker.js +14 -14
  739. package/starter/tsconfig.json +17 -17
  740. package/starter/worker/apps.js +6 -6
  741. package/starter/wrangler.jsonc +39 -39
  742. package/theme/LICENSE +21 -21
  743. package/theme/blocks/ArrowLink.tsx +14 -14
  744. package/theme/blocks/BlockHead.tsx +26 -26
  745. package/theme/blocks/Panel.tsx +32 -32
  746. package/theme/blocks/Person.tsx +26 -26
  747. package/theme/blocks/accordion/PageRow.tsx +25 -25
  748. package/theme/blocks/accordion/View.tsx +17 -17
  749. package/theme/blocks/buttons/Buttons.tsx +28 -28
  750. package/theme/blocks/buttons/View.tsx +12 -12
  751. package/theme/blocks/cards/LinkCards.tsx +72 -72
  752. package/theme/blocks/cards/View.tsx +20 -20
  753. package/theme/blocks/checklist/Checks.tsx +15 -15
  754. package/theme/blocks/checklist/View.tsx +14 -14
  755. package/theme/blocks/cookies/View.tsx +36 -36
  756. package/theme/blocks/faq/Accordion.tsx +34 -34
  757. package/theme/blocks/faq/View.tsx +16 -16
  758. package/theme/blocks/faq/script.ts +56 -56
  759. package/theme/blocks/hero/View.tsx +44 -44
  760. package/theme/blocks/links/LinkGroups.tsx +24 -24
  761. package/theme/blocks/links/View.tsx +17 -17
  762. package/theme/blocks/list/Rows.tsx +24 -24
  763. package/theme/blocks/list/View.tsx +14 -14
  764. package/theme/blocks/notice/View.tsx +13 -13
  765. package/theme/blocks/points/PointList.tsx +34 -34
  766. package/theme/blocks/points/View.tsx +14 -14
  767. package/theme/blocks/posts/View.tsx +26 -26
  768. package/theme/blocks/pricecards/View.tsx +40 -40
  769. package/theme/blocks/steps/StepList.tsx +23 -23
  770. package/theme/blocks/steps/View.tsx +14 -14
  771. package/theme/blocks/testimonials/List.tsx +46 -46
  772. package/theme/blocks/testimonials/Slider.tsx +44 -44
  773. package/theme/blocks/testimonials/Testimonial.tsx +124 -124
  774. package/theme/blocks/testimonials/View.tsx +21 -21
  775. package/theme/blocks/testimonials/script.ts +52 -52
  776. package/theme/blocks/video/View.tsx +41 -41
  777. package/theme/components/ActionBar.astro +63 -63
  778. package/theme/components/AppSlot.astro +21 -21
  779. package/theme/components/ArrowLink.tsx +21 -21
  780. package/theme/components/BlogIndex.astro +48 -48
  781. package/theme/components/BlogPosts.astro +18 -18
  782. package/theme/components/BlogPosts.tsx +45 -45
  783. package/theme/components/BookButton.tsx +22 -22
  784. package/theme/components/Container.astro +11 -11
  785. package/theme/components/CookieConsent.astro +121 -121
  786. package/theme/components/EndBlock.astro +21 -21
  787. package/theme/components/FontPreload.astro +6 -6
  788. package/theme/components/Footer.astro +85 -85
  789. package/theme/components/Header.astro +186 -186
  790. package/theme/components/Icon.astro +16 -16
  791. package/theme/components/Img.astro +29 -29
  792. package/theme/components/Img.tsx +33 -33
  793. package/theme/components/Inline.astro +11 -11
  794. package/theme/components/Logo.astro +23 -23
  795. package/theme/components/LogoMark.astro +10 -10
  796. package/theme/components/Markdown.astro +17 -17
  797. package/theme/components/MobileMenu.tsx +224 -224
  798. package/theme/components/MobileNav.astro +33 -33
  799. package/theme/components/PageHeader.astro +22 -22
  800. package/theme/components/PageSection.astro +24 -24
  801. package/theme/components/PhoneButton.tsx +11 -11
  802. package/theme/components/Popup.astro +145 -145
  803. package/theme/components/PopupButton.tsx +24 -24
  804. package/theme/components/PopupView.tsx +48 -48
  805. package/theme/components/PostList.tsx +58 -58
  806. package/theme/components/PostMeta.tsx +17 -17
  807. package/theme/components/RichText.astro +28 -28
  808. package/theme/components/Section.astro +29 -29
  809. package/theme/components/SectionHeading.tsx +8 -8
  810. package/theme/components/TextSection.astro +16 -16
  811. package/theme/components/home/PageBlock.astro +19 -19
  812. package/theme/components/home/Text.astro +13 -13
  813. package/theme/components/loadPopup.ts +22 -22
  814. package/theme/components/loadPosts.ts +16 -16
  815. package/theme/components/posts.ts +15 -15
  816. package/theme/editor/looks.mjs +7 -7
  817. package/theme/editor/previews.tsx +56 -56
  818. package/theme/layouts/Base.astro +273 -273
  819. package/theme/layouts/PageLayout.astro +126 -126
  820. package/theme/layouts/PostLayout.astro +137 -137
  821. package/theme/scripts/motion.ts +29 -29
  822. package/theme/styles/base.css +320 -320
  823. package/theme/styles/blocks.css +1808 -1808
  824. package/theme/styles/components.css +206 -206
  825. package/theme/styles/global.css +1302 -1302
  826. package/theme/styles/ui.css +6544 -6544
  827. package/theme/templates/Blog.astro +38 -38
  828. package/theme/templates/Category.astro +19 -19
  829. package/theme/templates/Home.astro +36 -36
  830. package/theme/templates/New.astro +68 -68
  831. package/theme/templates/NotFound.astro +117 -117
  832. package/theme/theme.json +28 -28
  833. package/theme/ui/Badge.tsx +45 -45
  834. package/theme/ui/BottomSheet.tsx +148 -148
  835. package/theme/ui/Button.tsx +79 -79
  836. package/theme/ui/ButtonGroup.tsx +31 -31
  837. package/theme/ui/Card.tsx +45 -45
  838. package/theme/ui/Carousel.tsx +124 -124
  839. package/theme/ui/Checkbox.tsx +59 -59
  840. package/theme/ui/Chip.tsx +101 -101
  841. package/theme/ui/ChipSet.tsx +27 -27
  842. package/theme/ui/DatePicker.tsx +149 -149
  843. package/theme/ui/DatePickerContainer.tsx +316 -316
  844. package/theme/ui/Dialog.tsx +100 -100
  845. package/theme/ui/Divider.tsx +27 -27
  846. package/theme/ui/ExposedDropdownMenu.tsx +77 -77
  847. package/theme/ui/Fab.tsx +88 -88
  848. package/theme/ui/FabMenu.tsx +67 -67
  849. package/theme/ui/IconButton.tsx +78 -78
  850. package/theme/ui/List.tsx +287 -287
  851. package/theme/ui/LoadingIndicator.tsx +29 -29
  852. package/theme/ui/Menu.tsx +278 -278
  853. package/theme/ui/NavigationBar.tsx +62 -62
  854. package/theme/ui/NavigationDrawer.tsx +80 -80
  855. package/theme/ui/NavigationRail.tsx +154 -154
  856. package/theme/ui/ProgressIndicator.tsx +162 -162
  857. package/theme/ui/RadioButton.tsx +50 -50
  858. package/theme/ui/Search.tsx +129 -129
  859. package/theme/ui/SegmentedButton.tsx +49 -49
  860. package/theme/ui/SideSheet.tsx +54 -54
  861. package/theme/ui/Slider.tsx +155 -155
  862. package/theme/ui/Snackbar.tsx +67 -67
  863. package/theme/ui/SplitButton.tsx +67 -67
  864. package/theme/ui/Switch.tsx +60 -60
  865. package/theme/ui/Tabs.tsx +114 -114
  866. package/theme/ui/TextField.tsx +278 -278
  867. package/theme/ui/TimePicker.tsx +128 -128
  868. package/theme/ui/TimePickerContainer.tsx +234 -234
  869. package/theme/ui/Toolbar.tsx +67 -67
  870. package/theme/ui/Tooltip.tsx +58 -58
  871. package/theme/ui/TopAppBar.tsx +77 -77
  872. package/theme/ui/bottomSheet.script.ts +8 -8
  873. package/theme/ui/{bottomSheet.ts → bottomSheetControl.ts} +211 -211
  874. package/theme/ui/carousel.script.ts +28 -28
  875. package/theme/ui/carouselLayout.ts +229 -229
  876. package/theme/ui/checkbox.script.ts +8 -8
  877. package/theme/ui/datePicker.script.ts +302 -302
  878. package/theme/ui/dates.ts +185 -185
  879. package/theme/ui/{dialog.ts → dialogControl.ts} +48 -48
  880. package/theme/ui/dropdownMenu.script.ts +166 -166
  881. package/theme/ui/fabMenu.script.ts +57 -57
  882. package/theme/ui/navigationRail.script.ts +47 -47
  883. package/theme/ui/ripple.script.ts +159 -159
  884. package/theme/ui/search.script.ts +92 -92
  885. package/theme/ui/{sideSheet.ts → sideSheetControl.ts} +48 -48
  886. package/theme/ui/slider.script.ts +12 -12
  887. package/theme/ui/sliderUpdate.ts +52 -52
  888. package/theme/ui/snackbar.script.ts +16 -16
  889. package/theme/ui/splitButton.script.ts +63 -63
  890. package/theme/ui/tabs.script.ts +48 -48
  891. package/theme/ui/textField.script.ts +17 -17
  892. package/theme/ui/textFieldUpdate.ts +44 -44
  893. package/theme/ui/timePicker.script.ts +248 -248
  894. package/theme/ui/toolbar.script.ts +43 -43
  895. package/theme/ui/tooltip.script.ts +68 -68
  896. package/worker/consent.js +141 -141
  897. package/worker/editor-github.js +167 -167
  898. package/worker/editor-host.js +74 -74
  899. package/worker/files.js +401 -401
  900. package/worker/github.js +36 -36
  901. package/worker/index.js +188 -184
  902. package/worker/not-found.js +97 -97
  903. package/worker/oauth.js +95 -95
  904. package/worker/publish.js +52 -52
  905. package/worker/push.js +270 -270
  906. package/worker/r2-status.js +50 -50
  907. package/worker/session.js +58 -58
  908. package/worker/store.js +46 -0
@@ -1,167 +1,167 @@
1
- // What the editor asks of GitHub, through the Worker (packages/editor/src/github.ts): the browser never has the token
2
- // (packages/editor/worker/session.js), so every request to GitHub goes from here with it. The site's files come through the cache in
3
- // R2 (packages/editor/worker/files.js), and only to a sign-in GitHub says may read the repository (`access`, ten minutes).
4
- // GET /api/github/head the branch's commit and root tree (always from GitHub)
5
- // POST /api/github/files { tree?, folders?, depth?, paths? }: the files in folders (`depth` levels down) and at
6
- // paths, at a root tree, or at the branch's head
7
- // POST /api/github/trees { at: [{ root, path }] }: the folders at paths under root trees, with their entries
8
- // POST /api/github/texts { oids }: files' texts by their blob ids (at most 100)
9
- // GET /api/github/blob/<oid> a file's bytes; ?repo=<owner>/<name> from another repository (the store), not cached
10
- // POST /api/github/commit { message, changes: [{ path, content (base64) | null, oid | null }] }: one commit,
11
- // refused (409) when a file was changed or created elsewhere since the editor loaded it
12
- // POST /api/github/graphql any other question to GitHub's GraphQL API (who is signed in, the history, the
13
- // store): only questions, never a change
14
- // POST /api/github/logout "Log ud": the cookie goes, and GitHub forgets the token
15
- import { config } from '@site/config';
16
- import { access, forget, noSession, sessionToken } from './session.js';
17
- import { filesOf, GitHubError } from './files.js';
18
- import { json } from './not-found.js';
19
-
20
- const OID = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
21
- const isOid = (s) => typeof s === 'string' && OID.test(s);
22
- const isPath = (s) => typeof s === 'string' && s.length > 0 && s.length <= 1000 && !s.startsWith('/') && !s.split('/').includes('..');
23
-
24
- export async function handleGitHub(request, env, ctx) {
25
- const route = new URL(request.url).pathname.slice('/api/github/'.length);
26
- const method = request.method;
27
- if (route === 'logout' && method === 'POST') return logout(request, env, ctx);
28
- const token = sessionToken(request);
29
- if (!token) return json({ error: 'signed out' }, 401);
30
- const files = filesOf(env, token, ctx);
31
- try {
32
- if (route === 'graphql' && method === 'POST') return await question(request, files);
33
- // The site's own files: only to someone GitHub says may read the repository.
34
- const may = await access(request, files.ask);
35
- if (!may) return json({ error: 'signed out' }, 401);
36
- if (!may.read) return json({ error: 'forbidden' }, 403);
37
- if (route === 'head' && method === 'GET') return json(await files.head());
38
- if (route.startsWith('blob/') && method === 'GET') return await blob(request, route.slice('blob/'.length), files);
39
- if (method !== 'POST') return json({ error: 'method' }, 405);
40
- const body = await request.json().catch(() => ({}));
41
- if (route === 'files') return await listed(body, files);
42
- if (route === 'trees') return await folders(body, files);
43
- if (route === 'texts') return await texts(body, files);
44
- if (route === 'commit') return await commit(body, files);
45
- return json({ error: 'not found' }, 404);
46
- } catch (e) {
47
- if (e instanceof GitHubError) return json({ error: e.message || undefined, status: e.status }, e.status === 401 ? 401 : 502);
48
- throw e;
49
- } finally {
50
- await files.finish(route.replace(/\/.*/, ''));
51
- }
52
- }
53
-
54
- // A question to GitHub's GraphQL API as it is, and GitHub's answer. A change (a mutation) only goes through `commit`.
55
- async function question(request, files) {
56
- const body = await request.text();
57
- let query;
58
- try {
59
- ({ query } = JSON.parse(body));
60
- } catch {}
61
- if (typeof query !== 'string' || /\b(mutation|subscription)\b/.test(withoutStrings(query))) return json({ error: 'only questions' }, 400);
62
- const res = await files.ask('https://api.github.com/graphql', { method: 'POST', headers: { 'content-type': 'application/json' }, body });
63
- return new Response(res.body, { status: res.status, headers: { 'content-type': 'application/json', 'cache-control': 'no-store' } });
64
- }
65
-
66
- // GraphQL without its strings and comments, where a file's name could say anything.
67
- const withoutStrings = (query) => query.replace(/"""[\s\S]*?"""|"(?:\\.|[^"\\])*"|#[^\n]*/g, '""');
68
-
69
- async function listed({ tree, folders = [], depth = 3, paths = [] }, files) {
70
- if (
71
- (tree !== undefined && !isOid(tree)) ||
72
- !Array.isArray(folders) ||
73
- !folders.every(isPath) ||
74
- !Array.isArray(paths) ||
75
- !paths.every(isPath) ||
76
- !Number.isInteger(depth) ||
77
- depth < 1 ||
78
- depth > 20
79
- )
80
- return json({ error: 'request' }, 400);
81
- const at = tree ? { tree } : await files.head();
82
- const [lists, entries] = await Promise.all([files.filesIn(at.tree, folders, depth), files.entriesAt(at.tree, paths)]);
83
- return json({
84
- ...at,
85
- folders: lists,
86
- // At a path: a file, or anything else git has there (a folder), whose text is never asked.
87
- paths: entries.map((e, i) => e && { path: paths[i], oid: e.oid, binary: e.type !== 'blob' || e.binary === true }),
88
- });
89
- }
90
-
91
- async function folders({ at }, files) {
92
- if (!Array.isArray(at) || at.length > 200 || !at.every((a) => isOid(a?.root) && (a.path === '' || isPath(a.path))))
93
- return json({ error: 'request' }, 400);
94
- const oids = await files.foldersAt(at);
95
- const trees = await files.treesOf(oids.filter(Boolean));
96
- return json({
97
- trees: oids.map((oid) => {
98
- const entries = oid && trees.get(oid);
99
- return entries ? { oid, entries: entries.map((e) => ({ name: e.name, oid: e.oid, type: e.type, binary: e.binary === true })) } : null;
100
- }),
101
- });
102
- }
103
-
104
- async function texts({ oids }, files) {
105
- if (!Array.isArray(oids) || oids.length > 100 || !oids.every(isOid)) return json({ error: 'request' }, 400);
106
- const found = await files.texts(oids);
107
- return json({ texts: Object.fromEntries([...found].filter(([, text]) => text !== undefined)) });
108
- }
109
-
110
- async function blob(request, oid, files) {
111
- const repo = new URL(request.url).searchParams.get('repo')?.split('/');
112
- if (!isOid(oid) || (repo && (repo.length !== 2 || !repo.every((p) => /^[\w.-]+$/.test(p))))) return json({ error: 'request' }, 400);
113
- const bytes = await files.bytes(oid, repo ? { owner: repo[0], name: repo[1] } : config.repo);
114
- if (!bytes) return json({ error: 'not found' }, 404);
115
- // A blob id's bytes never change.
116
- return new Response(bytes, {
117
- headers: { 'content-type': 'application/octet-stream', 'cache-control': 'private, max-age=31536000, immutable' },
118
- });
119
- }
120
-
121
- async function commit({ message, changes }, files) {
122
- if (
123
- typeof message !== 'string' ||
124
- !message ||
125
- !Array.isArray(changes) ||
126
- !changes.length ||
127
- !changes.every((c) => isPath(c?.path) && (c.content === null || typeof c.content === 'string') && (c.oid === null || isOid(c.oid)))
128
- )
129
- return json({ error: 'request' }, 400);
130
- // The branch's commit, then the files in that same commit, so a push in between cannot slip past the check (the
131
- // commit is refused unless the branch is still at this commit).
132
- const at = await files.head();
133
- const now = await files.entriesAt(
134
- at.tree,
135
- changes.map((c) => c.path),
136
- );
137
- const conflict = changes.findIndex((c, i) => (now[i]?.oid ?? null) !== c.oid);
138
- if (conflict >= 0) return json({ conflict: changes[conflict].oid ? 'changed' : 'exists' }, 409);
139
- return json(
140
- await files.save(
141
- at,
142
- message,
143
- changes.map((c) => ({ path: c.path, contents: c.content })),
144
- ),
145
- );
146
- }
147
-
148
- async function logout(request, env, ctx) {
149
- const token = sessionToken(request);
150
- if (token) {
151
- await forget(token);
152
- // GitHub forgets the token too, so it is no good to anyone after "Log ud".
153
- if (env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
154
- ctx.waitUntil(
155
- fetch(`https://api.github.com/applications/${env.GITHUB_CLIENT_ID}/token`, {
156
- method: 'DELETE',
157
- headers: {
158
- authorization: `Basic ${btoa(`${env.GITHUB_CLIENT_ID}:${env.GITHUB_CLIENT_SECRET}`)}`,
159
- accept: 'application/vnd.github+json',
160
- 'user-agent': `${config.repo.name}-worker`,
161
- },
162
- body: JSON.stringify({ access_token: token }),
163
- }).catch(() => {}),
164
- );
165
- }
166
- return new Response(null, { status: 204, headers: { 'set-cookie': noSession(), 'cache-control': 'no-store' } });
167
- }
1
+ // What the editor asks of GitHub, through the Worker (packages/editor/src/github.ts): the browser never has the token
2
+ // (packages/editor/worker/session.js), so every request to GitHub goes from here with it. The site's files come through the cache in
3
+ // R2 (packages/editor/worker/files.js), and only to a sign-in GitHub says may read the repository (`access`, ten minutes).
4
+ // GET /api/github/head the branch's commit and root tree (always from GitHub)
5
+ // POST /api/github/files { tree?, folders?, depth?, paths? }: the files in folders (`depth` levels down) and at
6
+ // paths, at a root tree, or at the branch's head
7
+ // POST /api/github/trees { at: [{ root, path }] }: the folders at paths under root trees, with their entries
8
+ // POST /api/github/texts { oids }: files' texts by their blob ids (at most 100)
9
+ // GET /api/github/blob/<oid> a file's bytes; ?repo=<owner>/<name> from another repository (the store), not cached
10
+ // POST /api/github/commit { message, changes: [{ path, content (base64) | null, oid | null }] }: one commit,
11
+ // refused (409) when a file was changed or created elsewhere since the editor loaded it
12
+ // POST /api/github/graphql any other question to GitHub's GraphQL API (who is signed in, the history, the
13
+ // store): only questions, never a change
14
+ // POST /api/github/logout "Log ud": the cookie goes, and GitHub forgets the token
15
+ import { config } from '@site/config';
16
+ import { access, forget, noSession, sessionToken } from './session.js';
17
+ import { filesOf, GitHubError } from './files.js';
18
+ import { json } from './not-found.js';
19
+
20
+ const OID = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
21
+ const isOid = (s) => typeof s === 'string' && OID.test(s);
22
+ const isPath = (s) => typeof s === 'string' && s.length > 0 && s.length <= 1000 && !s.startsWith('/') && !s.split('/').includes('..');
23
+
24
+ export async function handleGitHub(request, env, ctx) {
25
+ const route = new URL(request.url).pathname.slice('/api/github/'.length);
26
+ const method = request.method;
27
+ if (route === 'logout' && method === 'POST') return logout(request, env, ctx);
28
+ const token = sessionToken(request);
29
+ if (!token) return json({ error: 'signed out' }, 401);
30
+ const files = filesOf(env, token, ctx);
31
+ try {
32
+ if (route === 'graphql' && method === 'POST') return await question(request, files);
33
+ // The site's own files: only to someone GitHub says may read the repository.
34
+ const may = await access(request, files.ask);
35
+ if (!may) return json({ error: 'signed out' }, 401);
36
+ if (!may.read) return json({ error: 'forbidden' }, 403);
37
+ if (route === 'head' && method === 'GET') return json(await files.head());
38
+ if (route.startsWith('blob/') && method === 'GET') return await blob(request, route.slice('blob/'.length), files);
39
+ if (method !== 'POST') return json({ error: 'method' }, 405);
40
+ const body = await request.json().catch(() => ({}));
41
+ if (route === 'files') return await listed(body, files);
42
+ if (route === 'trees') return await folders(body, files);
43
+ if (route === 'texts') return await texts(body, files);
44
+ if (route === 'commit') return await commit(body, files);
45
+ return json({ error: 'not found' }, 404);
46
+ } catch (e) {
47
+ if (e instanceof GitHubError) return json({ error: e.message || undefined, status: e.status }, e.status === 401 ? 401 : 502);
48
+ throw e;
49
+ } finally {
50
+ await files.finish(route.replace(/\/.*/, ''));
51
+ }
52
+ }
53
+
54
+ // A question to GitHub's GraphQL API as it is, and GitHub's answer. A change (a mutation) only goes through `commit`.
55
+ async function question(request, files) {
56
+ const body = await request.text();
57
+ let query;
58
+ try {
59
+ ({ query } = JSON.parse(body));
60
+ } catch {}
61
+ if (typeof query !== 'string' || /\b(mutation|subscription)\b/.test(withoutStrings(query))) return json({ error: 'only questions' }, 400);
62
+ const res = await files.ask('https://api.github.com/graphql', { method: 'POST', headers: { 'content-type': 'application/json' }, body });
63
+ return new Response(res.body, { status: res.status, headers: { 'content-type': 'application/json', 'cache-control': 'no-store' } });
64
+ }
65
+
66
+ // GraphQL without its strings and comments, where a file's name could say anything.
67
+ const withoutStrings = (query) => query.replace(/"""[\s\S]*?"""|"(?:\\.|[^"\\])*"|#[^\n]*/g, '""');
68
+
69
+ async function listed({ tree, folders = [], depth = 3, paths = [] }, files) {
70
+ if (
71
+ (tree !== undefined && !isOid(tree)) ||
72
+ !Array.isArray(folders) ||
73
+ !folders.every(isPath) ||
74
+ !Array.isArray(paths) ||
75
+ !paths.every(isPath) ||
76
+ !Number.isInteger(depth) ||
77
+ depth < 1 ||
78
+ depth > 20
79
+ )
80
+ return json({ error: 'request' }, 400);
81
+ const at = tree ? { tree } : await files.head();
82
+ const [lists, entries] = await Promise.all([files.filesIn(at.tree, folders, depth), files.entriesAt(at.tree, paths)]);
83
+ return json({
84
+ ...at,
85
+ folders: lists,
86
+ // At a path: a file, or anything else git has there (a folder), whose text is never asked.
87
+ paths: entries.map((e, i) => e && { path: paths[i], oid: e.oid, binary: e.type !== 'blob' || e.binary === true }),
88
+ });
89
+ }
90
+
91
+ async function folders({ at }, files) {
92
+ if (!Array.isArray(at) || at.length > 200 || !at.every((a) => isOid(a?.root) && (a.path === '' || isPath(a.path))))
93
+ return json({ error: 'request' }, 400);
94
+ const oids = await files.foldersAt(at);
95
+ const trees = await files.treesOf(oids.filter(Boolean));
96
+ return json({
97
+ trees: oids.map((oid) => {
98
+ const entries = oid && trees.get(oid);
99
+ return entries ? { oid, entries: entries.map((e) => ({ name: e.name, oid: e.oid, type: e.type, binary: e.binary === true })) } : null;
100
+ }),
101
+ });
102
+ }
103
+
104
+ async function texts({ oids }, files) {
105
+ if (!Array.isArray(oids) || oids.length > 100 || !oids.every(isOid)) return json({ error: 'request' }, 400);
106
+ const found = await files.texts(oids);
107
+ return json({ texts: Object.fromEntries([...found].filter(([, text]) => text !== undefined)) });
108
+ }
109
+
110
+ async function blob(request, oid, files) {
111
+ const repo = new URL(request.url).searchParams.get('repo')?.split('/');
112
+ if (!isOid(oid) || (repo && (repo.length !== 2 || !repo.every((p) => /^[\w.-]+$/.test(p))))) return json({ error: 'request' }, 400);
113
+ const bytes = await files.bytes(oid, repo ? { owner: repo[0], name: repo[1] } : config.repo);
114
+ if (!bytes) return json({ error: 'not found' }, 404);
115
+ // A blob id's bytes never change.
116
+ return new Response(bytes, {
117
+ headers: { 'content-type': 'application/octet-stream', 'cache-control': 'private, max-age=31536000, immutable' },
118
+ });
119
+ }
120
+
121
+ async function commit({ message, changes }, files) {
122
+ if (
123
+ typeof message !== 'string' ||
124
+ !message ||
125
+ !Array.isArray(changes) ||
126
+ !changes.length ||
127
+ !changes.every((c) => isPath(c?.path) && (c.content === null || typeof c.content === 'string') && (c.oid === null || isOid(c.oid)))
128
+ )
129
+ return json({ error: 'request' }, 400);
130
+ // The branch's commit, then the files in that same commit, so a push in between cannot slip past the check (the
131
+ // commit is refused unless the branch is still at this commit).
132
+ const at = await files.head();
133
+ const now = await files.entriesAt(
134
+ at.tree,
135
+ changes.map((c) => c.path),
136
+ );
137
+ const conflict = changes.findIndex((c, i) => (now[i]?.oid ?? null) !== c.oid);
138
+ if (conflict >= 0) return json({ conflict: changes[conflict].oid ? 'changed' : 'exists' }, 409);
139
+ return json(
140
+ await files.save(
141
+ at,
142
+ message,
143
+ changes.map((c) => ({ path: c.path, contents: c.content })),
144
+ ),
145
+ );
146
+ }
147
+
148
+ async function logout(request, env, ctx) {
149
+ const token = sessionToken(request);
150
+ if (token) {
151
+ await forget(token);
152
+ // GitHub forgets the token too, so it is no good to anyone after "Log ud".
153
+ if (env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
154
+ ctx.waitUntil(
155
+ fetch(`https://api.github.com/applications/${env.GITHUB_CLIENT_ID}/token`, {
156
+ method: 'DELETE',
157
+ headers: {
158
+ authorization: `Basic ${btoa(`${env.GITHUB_CLIENT_ID}:${env.GITHUB_CLIENT_SECRET}`)}`,
159
+ accept: 'application/vnd.github+json',
160
+ 'user-agent': `${config.repo.name}-worker`,
161
+ },
162
+ body: JSON.stringify({ access_token: token }),
163
+ }).catch(() => {}),
164
+ );
165
+ }
166
+ return new Response(null, { status: 204, headers: { 'set-cookie': noSession(), 'cache-control': 'no-store' } });
167
+ }
@@ -1,74 +1,74 @@
1
- // The editor on its own address (EDITOR_HOST, e.g. editor.example.dk), apart from the public
2
- // site: the editor's GitHub sign-in is a cookie no script can read (packages/editor/worker/session.js), but a script on the
3
- // same address (the site's third-party scripts, Google after consent, Calendly) could ask the Worker in the
4
- // editor's name, and read what the editor keeps in the browser. See docs/editor.md.
5
- // Without EDITOR_HOST (a variable on the Worker) the editor is at /editor/ on every address, as before.
6
- // On EDITOR_HOST: the editor (/editor/), the sign-in (/oauth/), what the editor asks the Worker
7
- // (/api/, packages/editor/worker/not-found.js) and files (scripts, styles, images)
8
- // are served; the address alone opens the editor; any other page goes to the public site
9
- // (PUBLIC_HOST, else EDITOR_HOST without "editor."), so no page of the site runs there.
10
- // On any other address: the editor, the sign-in and /api/ go to EDITOR_HOST. The editor's page first
11
- // removes what the editor kept on that address (the sign-in's cookie above all).
12
- // A page the editor fetches as text for its preview ("Forhåndsvis", packages/editor/src/pagePreview.tsx,
13
- // with the header X-Editor-Preview) is served on EDITOR_HOST too; the editor shows it without the
14
- // site's scripts.
15
-
16
- import { noSession } from './session.js';
17
-
18
- // The site's pages and feeds: no file ending, or one of these.
19
- const PAGE = /(^|\/)[^./]*$|\.(html?|xml)$/i;
20
-
21
- /** A response for a request that belongs on the other address, or undefined to serve it here. */
22
- export function editorHost(url, env, request) {
23
- const editor = env.EDITOR_HOST?.trim().toLowerCase();
24
- if (!editor) return undefined;
25
- const own =
26
- url.pathname.startsWith('/editor/') ||
27
- url.pathname === '/editor' ||
28
- url.pathname.startsWith('/oauth/') ||
29
- url.pathname.startsWith('/api/');
30
-
31
- if (url.hostname === editor) {
32
- if (url.pathname === '/' && !url.search) return redirect(`https://${editor}/editor/`, 302);
33
- if (own || !PAGE.test(url.pathname) || request?.headers.get('x-editor-preview')) return undefined;
34
- const site = env.PUBLIC_HOST?.trim() || editor.replace(/^editor\./, '');
35
- return redirect(`https://${site}${url.pathname}${url.search}`, 302);
36
- }
37
-
38
- if (!own) return undefined;
39
- const to = `https://${editor}${url.pathname}${url.search}`;
40
- if (url.pathname === '/editor/' || url.pathname === '/editor') return cleanUp(to);
41
- return redirect(to, 301);
42
- }
43
-
44
- const redirect = (location, status) => new Response(null, { status, headers: { location, 'cache-control': 'no-store' } });
45
-
46
- // The editor's page on the old address: removes the sign-in (its cookie), the editor's settings and its copy of the
47
- // files (IndexedDB "editor") and its service worker there, then opens the editor on its own address,
48
- // with what came after "#" (the open screen).
49
- function cleanUp(to) {
50
- const js = JSON.stringify(to).replace(/</g, '\\u003c');
51
- const html = `<!doctype html><html lang="da"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="robots" content="noindex"><title>Editoren er flyttet</title></head>
52
- <body style="font-family: system-ui, sans-serif; padding: 2rem"><p>Editoren er flyttet til <a href=${JSON.stringify(to)}>${to.replace(/^https:\/\//, '').replace(/[?#].*$/, '')}</a> …</p><script>
53
- (async () => {
54
- try {
55
- for (const key of Object.keys(localStorage)) if (key.startsWith('editor-')) localStorage.removeItem(key);
56
- } catch {}
57
- try {
58
- indexedDB.deleteDatabase('editor');
59
- } catch {}
60
- try {
61
- for (const r of (await navigator.serviceWorker?.getRegistrations()) ?? []) if (new URL(r.scope).pathname.startsWith('/editor/')) await r.unregister();
62
- } catch {}
63
- location.replace(${js} + location.hash);
64
- })();
65
- </script></body></html>`;
66
- return new Response(html, {
67
- headers: {
68
- 'content-type': 'text/html; charset=utf-8',
69
- 'cache-control': 'no-store',
70
- 'x-robots-tag': 'noindex',
71
- 'set-cookie': noSession(),
72
- },
73
- });
74
- }
1
+ // The editor on its own address (EDITOR_HOST, e.g. editor.example.dk), apart from the public
2
+ // site: the editor's GitHub sign-in is a cookie no script can read (packages/editor/worker/session.js), but a script on the
3
+ // same address (the site's third-party scripts, Google after consent, Calendly) could ask the Worker in the
4
+ // editor's name, and read what the editor keeps in the browser. See docs/editor.md.
5
+ // Without EDITOR_HOST (a variable on the Worker) the editor is at /editor/ on every address, as before.
6
+ // On EDITOR_HOST: the editor (/editor/), the sign-in (/oauth/), what the editor asks the Worker
7
+ // (/api/, packages/editor/worker/not-found.js) and files (scripts, styles, images)
8
+ // are served; the address alone opens the editor; any other page goes to the public site
9
+ // (PUBLIC_HOST, else EDITOR_HOST without "editor."), so no page of the site runs there.
10
+ // On any other address: the editor, the sign-in and /api/ go to EDITOR_HOST. The editor's page first
11
+ // removes what the editor kept on that address (the sign-in's cookie above all).
12
+ // A page the editor fetches as text for its preview ("Forhåndsvis", packages/editor/src/pagePreview.tsx,
13
+ // with the header X-Editor-Preview) is served on EDITOR_HOST too; the editor shows it without the
14
+ // site's scripts.
15
+
16
+ import { noSession } from './session.js';
17
+
18
+ // The site's pages and feeds: no file ending, or one of these.
19
+ const PAGE = /(^|\/)[^./]*$|\.(html?|xml)$/i;
20
+
21
+ /** A response for a request that belongs on the other address, or undefined to serve it here. */
22
+ export function editorHost(url, env, request) {
23
+ const editor = env.EDITOR_HOST?.trim().toLowerCase();
24
+ if (!editor) return undefined;
25
+ const own =
26
+ url.pathname.startsWith('/editor/') ||
27
+ url.pathname === '/editor' ||
28
+ url.pathname.startsWith('/oauth/') ||
29
+ url.pathname.startsWith('/api/');
30
+
31
+ if (url.hostname === editor) {
32
+ if (url.pathname === '/' && !url.search) return redirect(`https://${editor}/editor/`, 302);
33
+ if (own || !PAGE.test(url.pathname) || request?.headers.get('x-editor-preview')) return undefined;
34
+ const site = env.PUBLIC_HOST?.trim() || editor.replace(/^editor\./, '');
35
+ return redirect(`https://${site}${url.pathname}${url.search}`, 302);
36
+ }
37
+
38
+ if (!own) return undefined;
39
+ const to = `https://${editor}${url.pathname}${url.search}`;
40
+ if (url.pathname === '/editor/' || url.pathname === '/editor') return cleanUp(to);
41
+ return redirect(to, 301);
42
+ }
43
+
44
+ const redirect = (location, status) => new Response(null, { status, headers: { location, 'cache-control': 'no-store' } });
45
+
46
+ // The editor's page on the old address: removes the sign-in (its cookie), the editor's settings and its copy of the
47
+ // files (IndexedDB "editor") and its service worker there, then opens the editor on its own address,
48
+ // with what came after "#" (the open screen).
49
+ function cleanUp(to) {
50
+ const js = JSON.stringify(to).replace(/</g, '\\u003c');
51
+ const html = `<!doctype html><html lang="da"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="robots" content="noindex"><title>Editoren er flyttet</title></head>
52
+ <body style="font-family: system-ui, sans-serif; padding: 2rem"><p>Editoren er flyttet til <a href=${JSON.stringify(to)}>${to.replace(/^https:\/\//, '').replace(/[?#].*$/, '')}</a> …</p><script>
53
+ (async () => {
54
+ try {
55
+ for (const key of Object.keys(localStorage)) if (key.startsWith('editor-')) localStorage.removeItem(key);
56
+ } catch {}
57
+ try {
58
+ indexedDB.deleteDatabase('editor');
59
+ } catch {}
60
+ try {
61
+ for (const r of (await navigator.serviceWorker?.getRegistrations()) ?? []) if (new URL(r.scope).pathname.startsWith('/editor/')) await r.unregister();
62
+ } catch {}
63
+ location.replace(${js} + location.hash);
64
+ })();
65
+ </script></body></html>`;
66
+ return new Response(html, {
67
+ headers: {
68
+ 'content-type': 'text/html; charset=utf-8',
69
+ 'cache-control': 'no-store',
70
+ 'x-robots-tag': 'noindex',
71
+ 'set-cookie': noSession(),
72
+ },
73
+ });
74
+ }