@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
package/src/iconPacks.ts CHANGED
@@ -1,89 +1,89 @@
1
- // The site's icon packs, for the editor ("Indstillinger" › "Udseende" › "Ikonpakker", IconPacks in Library.tsx): the
2
- // system's own (packages/editor/icons/<pack>/: Material Symbols, Phosphor and Lucide) and those the themes installed bring
3
- // (src/themes/<theme>/icon-packs/<pack>/), each by its manifest (pack.json) with a few of its icons to show; and the one
4
- // the site and this editor are built with (packages/editor/site/iconPacks.mjs: the one chosen, src/data/icons.json, else the theme's,
5
- // else the standard theme's). The packs' icons are read as files: the editor draws them as they are, beside the icons it
6
- // draws itself, which are the pack in use's (themePlugin in packages/editor/site/theme.mjs).
7
- import { packInUse, packTheme, themePackId } from '../site/iconPacks.mjs';
8
- import { STANDARD_THEME } from './store';
9
- import { theme, themes } from './themes';
10
-
11
- export type IconPack = {
12
- /** Its id: a system pack's folder (`lucide`), a theme's `<theme>/<folder>` (packages/editor/site/iconPacks.mjs). */
13
- id: string;
14
- name: string;
15
- /** Who made it. */
16
- author: string;
17
- /** Its licence, as an SPDX id (`Apache-2.0`), in its folder as LICENSE. */
18
- license: string;
19
- version?: string;
20
- url?: string;
21
- /** The theme it came with; none for the system's own. */
22
- theme?: string;
23
- /** The sets it has icons from besides its own (Material Design Icons' logos), each with its licence in the folder. */
24
- includes?: { name: string; author: string; license: string; licenseFile: string; icons: string[] }[];
25
- /** A few of its icons' files, to show what it looks like (SAMPLES). */
26
- samples: string[];
27
- };
28
-
29
- /** The icons each pack shows of itself, by the names the site draws them by. */
30
- const SAMPLES = ['house', 'magnifying-glass', 'calendar-blank', 'envelope-simple', 'heart'];
31
-
32
- type Manifest = Omit<IconPack, 'id' | 'theme' | 'samples'>;
33
- const manifests = import.meta.glob<Manifest>(['@icons/*/pack.json', '/src/themes/*/icon-packs/*/pack.json'], {
34
- eager: true,
35
- import: 'default',
36
- });
37
- const files = import.meta.glob<string>(
38
- [
39
- '@icons/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
40
- '/src/themes/*/icon-packs/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
41
- ],
42
- { eager: true, query: '?raw', import: 'default' },
43
- );
44
- const chosen = import.meta.glob<{ pack?: string }>('/src/data/icons.json', { eager: true, import: 'default' })['/src/data/icons.json'];
45
-
46
- /** A pack's id from the path of a file in it: a theme's (src/themes/<theme>/icon-packs/<pack>/) or the system's (the
47
- * CMS's icons/<pack>/, @icons). */
48
- const idOf = (path: string) => {
49
- const own = path.match(/\/themes\/([^/]+)\/icon-packs\/([^/]+)\//);
50
- return own ? themePackId(own[1], own[2]) : path.split('/').at(-2)!;
51
- };
52
- const folderOf = (path: string) => path.slice(0, path.lastIndexOf('/') + 1);
53
-
54
- /** The icon packs installed, by name. */
55
- export const iconPacks: IconPack[] = Object.entries(manifests)
56
- .map(([path, m]) => {
57
- const id = idOf(path);
58
- const folder = folderOf(path);
59
- const pack: IconPack = {
60
- id,
61
- name: m.name,
62
- author: m.author,
63
- license: m.license,
64
- samples: SAMPLES.map((name) => files[`${folder}${name}.svg`]).filter(Boolean),
65
- };
66
- if (m.version) pack.version = m.version;
67
- if (m.url) pack.url = m.url;
68
- if (m.includes?.length) pack.includes = m.includes;
69
- const from = packTheme(id);
70
- if (from) pack.theme = from;
71
- return pack;
72
- })
73
- .sort((a, b) => a.name.localeCompare(b.name));
74
-
75
- const installed = iconPacks.map((p) => p.id);
76
- const standard = themes.find((x) => x.id === STANDARD_THEME);
77
-
78
- /** The pack a theme uses while none is chosen, by its id. */
79
- export const packOfTheme = (id: string): string =>
80
- packInUse({ chosen: undefined, theme: id, icons: themes.find((x) => x.id === id)?.icons, standardIcons: standard?.icons, installed });
81
-
82
- /** The pack the site and this editor are built with, by its id. */
83
- export const iconPack: string = packInUse({
84
- chosen: chosen?.pack,
85
- theme: theme.id,
86
- icons: theme.icons,
87
- standardIcons: standard?.icons,
88
- installed,
89
- });
1
+ // The site's icon packs, for the editor ("Indstillinger" › "Udseende" › "Ikonpakker", IconPacks in Library.tsx): the
2
+ // system's own (packages/editor/icons/<pack>/: Material Symbols, Phosphor and Lucide) and those the themes installed bring
3
+ // (src/themes/<theme>/icon-packs/<pack>/), each by its manifest (pack.json) with a few of its icons to show; and the one
4
+ // the site and this editor are built with (packages/editor/site/iconPacks.mjs: the one chosen, src/data/icons.json, else the theme's,
5
+ // else the standard theme's). The packs' icons are read as files: the editor draws them as they are, beside the icons it
6
+ // draws itself, which are the pack in use's (themePlugin in packages/editor/site/theme.mjs).
7
+ import { packInUse, packTheme, themePackId } from '../site/iconPacks.mjs';
8
+ import { STANDARD_THEME } from './store';
9
+ import { theme, themes } from './themes';
10
+
11
+ export type IconPack = {
12
+ /** Its id: a system pack's folder (`lucide`), a theme's `<theme>/<folder>` (packages/editor/site/iconPacks.mjs). */
13
+ id: string;
14
+ name: string;
15
+ /** Who made it. */
16
+ author: string;
17
+ /** Its licence, as an SPDX id (`Apache-2.0`), in its folder as LICENSE. */
18
+ license: string;
19
+ version?: string;
20
+ url?: string;
21
+ /** The theme it came with; none for the system's own. */
22
+ theme?: string;
23
+ /** The sets it has icons from besides its own (Material Design Icons' logos), each with its licence in the folder. */
24
+ includes?: { name: string; author: string; license: string; licenseFile: string; icons: string[] }[];
25
+ /** A few of its icons' files, to show what it looks like (SAMPLES). */
26
+ samples: string[];
27
+ };
28
+
29
+ /** The icons each pack shows of itself, by the names the site draws them by. */
30
+ const SAMPLES = ['house', 'magnifying-glass', 'calendar-blank', 'envelope-simple', 'heart'];
31
+
32
+ type Manifest = Omit<IconPack, 'id' | 'theme' | 'samples'>;
33
+ const manifests = import.meta.glob<Manifest>(['@icons/*/pack.json', '/src/themes/*/icon-packs/*/pack.json'], {
34
+ eager: true,
35
+ import: 'default',
36
+ });
37
+ const files = import.meta.glob<string>(
38
+ [
39
+ '@icons/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
40
+ '/src/themes/*/icon-packs/*/{house,magnifying-glass,calendar-blank,envelope-simple,heart}.svg',
41
+ ],
42
+ { eager: true, query: '?raw', import: 'default' },
43
+ );
44
+ const chosen = import.meta.glob<{ pack?: string }>('/src/data/icons.json', { eager: true, import: 'default' })['/src/data/icons.json'];
45
+
46
+ /** A pack's id from the path of a file in it: a theme's (src/themes/<theme>/icon-packs/<pack>/) or the system's (the
47
+ * CMS's icons/<pack>/, @icons). */
48
+ const idOf = (path: string) => {
49
+ const own = path.match(/\/themes\/([^/]+)\/icon-packs\/([^/]+)\//);
50
+ return own ? themePackId(own[1], own[2]) : path.split('/').at(-2)!;
51
+ };
52
+ const folderOf = (path: string) => path.slice(0, path.lastIndexOf('/') + 1);
53
+
54
+ /** The icon packs installed, by name. */
55
+ export const iconPacks: IconPack[] = Object.entries(manifests)
56
+ .map(([path, m]) => {
57
+ const id = idOf(path);
58
+ const folder = folderOf(path);
59
+ const pack: IconPack = {
60
+ id,
61
+ name: m.name,
62
+ author: m.author,
63
+ license: m.license,
64
+ samples: SAMPLES.map((name) => files[`${folder}${name}.svg`]).filter(Boolean),
65
+ };
66
+ if (m.version) pack.version = m.version;
67
+ if (m.url) pack.url = m.url;
68
+ if (m.includes?.length) pack.includes = m.includes;
69
+ const from = packTheme(id);
70
+ if (from) pack.theme = from;
71
+ return pack;
72
+ })
73
+ .sort((a, b) => a.name.localeCompare(b.name));
74
+
75
+ const installed = iconPacks.map((p) => p.id);
76
+ const standard = themes.find((x) => x.id === STANDARD_THEME);
77
+
78
+ /** The pack a theme uses while none is chosen, by its id. */
79
+ export const packOfTheme = (id: string): string =>
80
+ packInUse({ chosen: undefined, theme: id, icons: themes.find((x) => x.id === id)?.icons, standardIcons: standard?.icons, installed });
81
+
82
+ /** The pack the site and this editor are built with, by its id. */
83
+ export const iconPack: string = packInUse({
84
+ chosen: chosen?.pack,
85
+ theme: theme.id,
86
+ icons: theme.icons,
87
+ standardIcons: standard?.icons,
88
+ installed,
89
+ });
package/src/images.ts CHANGED
@@ -1,163 +1,163 @@
1
- // Pictures added in the editor. Before upload they are made at most 2000 px and WebP (JPEG in
2
- // browsers that cannot write WebP, like Safari), as the old editor did; packages/editor/scripts/responsive-images.mjs
3
- // makes the smaller sizes when the site is built. They go in public/images/uploads/<year>/<month>/
4
- // like imported pictures, and are saved in the same commit as the page that uses them (on
5
- // their own from Filer, Media.tsx). PDFs, videos and zip files, from Filer, are saved as they are, in
6
- // public/filer/ (prepareFile).
7
- import { useState } from 'react';
8
- import { slugify } from './schema';
9
- import { sourceColorsOf } from '../lib/dynamic-colors';
10
- import { config } from '@site/config';
11
- import type { Status } from './FormFrame';
12
- import { t } from './i18n';
13
-
14
- const MAX = 2000;
15
-
16
- // url: a temporary address of the picture in this browser, shown until the site is rebuilt.
17
- export type Upload = { url: string; path: string; bytes: Uint8Array; saved: boolean };
18
-
19
- // The pictures made ready in this visit, by their address on the site: shown from the browser
20
- // (shownAt), as the site has them only once it has been rebuilt.
21
- const fresh = new Map<string, string>();
22
- export const shownAt = (src: string) => fresh.get(src) ?? src;
23
- // A picture renamed in this visit is shown from its old address until the site is rebuilt.
24
- export const showMoved = (from: string, to: string) => fresh.set(to, shownAt(from));
25
-
26
- /** What a file in the site's folder of files is, by its ending: a PDF, a video or a zip file. */
27
- export type FileKind = 'pdf' | 'video' | 'zip';
28
-
29
- // The endings of each kind, with the type a browser shows it as.
30
- const ENDINGS: Record<string, [FileKind, string]> = {
31
- pdf: ['pdf', 'application/pdf'],
32
- mp4: ['video', 'video/mp4'],
33
- m4v: ['video', 'video/mp4'],
34
- mov: ['video', 'video/quicktime'],
35
- webm: ['video', 'video/webm'],
36
- zip: ['zip', 'application/zip'],
37
- };
38
- const endingOf = (name: string) => /\.([^./]+)$/.exec(name)?.[1].toLowerCase() ?? '';
39
-
40
- /** A file's kind by its name or address, or null for one that is not a PDF, a video or a zip file. */
41
- export const fileKindOf = (name: string): FileKind | null => ENDINGS[endingOf(name)]?.[0] ?? null;
42
-
43
- /** The type a browser shows a file as, by its name ('' for a picture). */
44
- export const typeOf = (name: string) => ENDINGS[endingOf(name)]?.[1] ?? '';
45
-
46
- // A picked file's ending: its own, or for one without it the first of its type ("Guide" from a phone,
47
- // application/pdf, as pdf); '' for another kind of file.
48
- const pickedEnding = (file: File) =>
49
- ENDINGS[endingOf(file.name)] ? endingOf(file.name) : (Object.keys(ENDINGS).find((e) => ENDINGS[e][1] === file.type) ?? '');
50
-
51
- /** A picked file's kind, by its ending or else its type, or null for one that is not a PDF, a video or a zip file. */
52
- export const pickedKindOf = (file: File): FileKind | null => ENDINGS[pickedEnding(file)]?.[0] ?? null;
53
-
54
- /** A file picked on the phone or computer: a picture, or (`all`, in Filer) a picture, a PDF, a video or a
55
- * zip file. */
56
- export function pickImage(all = false): Promise<File | null> {
57
- return new Promise((resolve) => {
58
- const input = document.createElement('input');
59
- input.type = 'file';
60
- input.accept = all
61
- ? ['image/*', ...new Set(Object.values(ENDINGS).map(([, type]) => type)), ...Object.keys(ENDINGS).map((e) => `.${e}`)].join(',')
62
- : 'image/*';
63
- input.addEventListener('change', () => resolve(input.files?.[0] ?? null));
64
- input.addEventListener('cancel', () => resolve(null));
65
- input.click();
66
- });
67
- }
68
-
69
- /** The colours in a picture that are best as a source colour (dynamic colour, sourceColorsOf), best first: one on the
70
- * phone or computer (it is not uploaded), or one at an address of the editor's own. Read small, which is enough and quick. */
71
- export async function colorsInPicture(file: Blob): Promise<string[]> {
72
- const bitmap = await createImageBitmap(file);
73
- const scale = Math.min(1, 128 / Math.max(bitmap.width, bitmap.height));
74
- const canvas = document.createElement('canvas');
75
- canvas.width = Math.max(1, Math.round(bitmap.width * scale));
76
- canvas.height = Math.max(1, Math.round(bitmap.height * scale));
77
- const context = canvas.getContext('2d')!;
78
- context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
79
- bitmap.close();
80
- return sourceColorsOf(context.getImageData(0, 0, canvas.width, canvas.height).data);
81
- }
82
-
83
- export async function prepareImage(file: File): Promise<Upload> {
84
- const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' }).catch(() => {
85
- throw new Error(t.app.pictureNotOpened);
86
- });
87
- const scale = Math.min(1, MAX / Math.max(bitmap.width, bitmap.height));
88
- const canvas = document.createElement('canvas');
89
- canvas.width = Math.round(bitmap.width * scale);
90
- canvas.height = Math.round(bitmap.height * scale);
91
- canvas.getContext('2d')!.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
92
- bitmap.close();
93
- const encode = (type: string) => new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, type, 0.82));
94
- let blob = await encode('image/webp');
95
- if (blob?.type !== 'image/webp') blob = await encode('image/jpeg');
96
- if (!blob) throw new Error(t.app.pictureNotReady);
97
-
98
- const now = new Date();
99
- const folder = `${config.folders.media}/${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}`;
100
- // A short random ending, so two pictures called IMG_1234 do not get the same name.
101
- const name = `${slugify(file.name.replace(/\.[^.]*$/, '')) || 'billede'}-${Math.random().toString(36).slice(2, 6)}`;
102
- const ext = blob.type === 'image/webp' ? 'webp' : 'jpg';
103
- const upload = {
104
- url: URL.createObjectURL(blob),
105
- path: `${folder}/${name}.${ext}`,
106
- bytes: new Uint8Array(await blob.arrayBuffer()),
107
- saved: false,
108
- };
109
- fresh.set(publicPath(upload.path), upload.url);
110
- return upload;
111
- }
112
-
113
- /** The most a PDF, a video or a zip file may weigh, in MB: it goes to GitHub in one request with the commit. */
114
- export const FILE_MAX_MB = 10;
115
-
116
- /** A PDF, a video or a zip file as it is, in the site's folder of files (`config.folders.files`, /filer/ on
117
- * the site), named by its own name as an address ("Stress guide.pdf" as stress-guide.pdf, the ending in
118
- * small letters), with -2, -3 … when the name is taken (`taken`: the paths there are). */
119
- export async function prepareFile(file: File, taken: string[]): Promise<Upload> {
120
- if (file.size > FILE_MAX_MB * 1024 * 1024) throw new Error(t.media.tooBig(FILE_MAX_MB));
121
- const folder = config.folders.files;
122
- const stem = slugify(file.name.replace(/\.[^.]*$/, '')) || 'fil';
123
- const ending = pickedEnding(file);
124
- let path = `${folder}/${stem}.${ending}`;
125
- for (let n = 2; taken.includes(path); n++) path = `${folder}/${stem}-${n}.${ending}`;
126
- const upload = { url: URL.createObjectURL(file), path, bytes: new Uint8Array(await file.arrayBuffer()), saved: false };
127
- fresh.set(publicPath(upload.path), upload.url);
128
- return upload;
129
- }
130
-
131
- // The address of an uploaded picture on the site.
132
- export const publicPath = (path: string) => path.replace(/^public/, '');
133
-
134
- // The pictures added in a form (a page, a fixed page), kept in the browser until "Gem", which saves
135
- // them in the same commit. upload: pick one and make it ready; added: those not saved yet, newest
136
- // first (offered in the gallery, MediaPicker.tsx); saving: the pictures a text uses.
137
- export function useUploads(notify: (status: Status) => void) {
138
- const [uploads, setUploads] = useState<Upload[]>([]);
139
-
140
- async function upload(): Promise<Upload | null> {
141
- const file = await pickImage();
142
- if (!file) return null;
143
- notify({ text: t.app.preparingPicture });
144
- try {
145
- const prepared = await prepareImage(file);
146
- setUploads((u) => [...u, prepared]);
147
- notify(null);
148
- return prepared;
149
- } catch (e) {
150
- notify({ text: (e as Error).message, error: true });
151
- return null;
152
- }
153
- }
154
-
155
- const added = uploads.filter((u) => !u.saved).reverse();
156
- /** The pictures not saved yet that a text uses. */
157
- const usedIn = (text: string) => uploads.filter((u) => !u.saved && text.includes(publicPath(u.path)));
158
- const markSaved = (pictures: Upload[]) => setUploads((all) => all.map((u) => (pictures.includes(u) ? { ...u, saved: true } : u)));
159
-
160
- return { upload, added, usedIn, markSaved };
161
- }
162
-
163
- export type Uploads = ReturnType<typeof useUploads>;
1
+ // Pictures added in the editor. Before upload they are made at most 2000 px and WebP (JPEG in
2
+ // browsers that cannot write WebP, like Safari), as the old editor did; packages/editor/scripts/responsive-images.mjs
3
+ // makes the smaller sizes when the site is built. They go in public/images/uploads/<year>/<month>/
4
+ // like imported pictures, and are saved in the same commit as the page that uses them (on
5
+ // their own from Filer, Media.tsx). PDFs, videos and zip files, from Filer, are saved as they are, in
6
+ // public/filer/ (prepareFile).
7
+ import { useState } from 'react';
8
+ import { slugify } from './schema';
9
+ import { sourceColorsOf } from '../lib/dynamic-colors';
10
+ import { config } from '@site/config';
11
+ import type { Status } from './FormFrame';
12
+ import { t } from './i18n';
13
+
14
+ const MAX = 2000;
15
+
16
+ // url: a temporary address of the picture in this browser, shown until the site is rebuilt.
17
+ export type Upload = { url: string; path: string; bytes: Uint8Array; saved: boolean };
18
+
19
+ // The pictures made ready in this visit, by their address on the site: shown from the browser
20
+ // (shownAt), as the site has them only once it has been rebuilt.
21
+ const fresh = new Map<string, string>();
22
+ export const shownAt = (src: string) => fresh.get(src) ?? src;
23
+ // A picture renamed in this visit is shown from its old address until the site is rebuilt.
24
+ export const showMoved = (from: string, to: string) => fresh.set(to, shownAt(from));
25
+
26
+ /** What a file in the site's folder of files is, by its ending: a PDF, a video or a zip file. */
27
+ export type FileKind = 'pdf' | 'video' | 'zip';
28
+
29
+ // The endings of each kind, with the type a browser shows it as.
30
+ const ENDINGS: Record<string, [FileKind, string]> = {
31
+ pdf: ['pdf', 'application/pdf'],
32
+ mp4: ['video', 'video/mp4'],
33
+ m4v: ['video', 'video/mp4'],
34
+ mov: ['video', 'video/quicktime'],
35
+ webm: ['video', 'video/webm'],
36
+ zip: ['zip', 'application/zip'],
37
+ };
38
+ const endingOf = (name: string) => /\.([^./]+)$/.exec(name)?.[1].toLowerCase() ?? '';
39
+
40
+ /** A file's kind by its name or address, or null for one that is not a PDF, a video or a zip file. */
41
+ export const fileKindOf = (name: string): FileKind | null => ENDINGS[endingOf(name)]?.[0] ?? null;
42
+
43
+ /** The type a browser shows a file as, by its name ('' for a picture). */
44
+ export const typeOf = (name: string) => ENDINGS[endingOf(name)]?.[1] ?? '';
45
+
46
+ // A picked file's ending: its own, or for one without it the first of its type ("Guide" from a phone,
47
+ // application/pdf, as pdf); '' for another kind of file.
48
+ const pickedEnding = (file: File) =>
49
+ ENDINGS[endingOf(file.name)] ? endingOf(file.name) : (Object.keys(ENDINGS).find((e) => ENDINGS[e][1] === file.type) ?? '');
50
+
51
+ /** A picked file's kind, by its ending or else its type, or null for one that is not a PDF, a video or a zip file. */
52
+ export const pickedKindOf = (file: File): FileKind | null => ENDINGS[pickedEnding(file)]?.[0] ?? null;
53
+
54
+ /** A file picked on the phone or computer: a picture, or (`all`, in Filer) a picture, a PDF, a video or a
55
+ * zip file. */
56
+ export function pickImage(all = false): Promise<File | null> {
57
+ return new Promise((resolve) => {
58
+ const input = document.createElement('input');
59
+ input.type = 'file';
60
+ input.accept = all
61
+ ? ['image/*', ...new Set(Object.values(ENDINGS).map(([, type]) => type)), ...Object.keys(ENDINGS).map((e) => `.${e}`)].join(',')
62
+ : 'image/*';
63
+ input.addEventListener('change', () => resolve(input.files?.[0] ?? null));
64
+ input.addEventListener('cancel', () => resolve(null));
65
+ input.click();
66
+ });
67
+ }
68
+
69
+ /** The colours in a picture that are best as a source colour (dynamic colour, sourceColorsOf), best first: one on the
70
+ * phone or computer (it is not uploaded), or one at an address of the editor's own. Read small, which is enough and quick. */
71
+ export async function colorsInPicture(file: Blob): Promise<string[]> {
72
+ const bitmap = await createImageBitmap(file);
73
+ const scale = Math.min(1, 128 / Math.max(bitmap.width, bitmap.height));
74
+ const canvas = document.createElement('canvas');
75
+ canvas.width = Math.max(1, Math.round(bitmap.width * scale));
76
+ canvas.height = Math.max(1, Math.round(bitmap.height * scale));
77
+ const context = canvas.getContext('2d')!;
78
+ context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
79
+ bitmap.close();
80
+ return sourceColorsOf(context.getImageData(0, 0, canvas.width, canvas.height).data);
81
+ }
82
+
83
+ export async function prepareImage(file: File): Promise<Upload> {
84
+ const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' }).catch(() => {
85
+ throw new Error(t.app.pictureNotOpened);
86
+ });
87
+ const scale = Math.min(1, MAX / Math.max(bitmap.width, bitmap.height));
88
+ const canvas = document.createElement('canvas');
89
+ canvas.width = Math.round(bitmap.width * scale);
90
+ canvas.height = Math.round(bitmap.height * scale);
91
+ canvas.getContext('2d')!.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
92
+ bitmap.close();
93
+ const encode = (type: string) => new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, type, 0.82));
94
+ let blob = await encode('image/webp');
95
+ if (blob?.type !== 'image/webp') blob = await encode('image/jpeg');
96
+ if (!blob) throw new Error(t.app.pictureNotReady);
97
+
98
+ const now = new Date();
99
+ const folder = `${config.folders.media}/${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}`;
100
+ // A short random ending, so two pictures called IMG_1234 do not get the same name.
101
+ const name = `${slugify(file.name.replace(/\.[^.]*$/, '')) || 'billede'}-${Math.random().toString(36).slice(2, 6)}`;
102
+ const ext = blob.type === 'image/webp' ? 'webp' : 'jpg';
103
+ const upload = {
104
+ url: URL.createObjectURL(blob),
105
+ path: `${folder}/${name}.${ext}`,
106
+ bytes: new Uint8Array(await blob.arrayBuffer()),
107
+ saved: false,
108
+ };
109
+ fresh.set(publicPath(upload.path), upload.url);
110
+ return upload;
111
+ }
112
+
113
+ /** The most a PDF, a video or a zip file may weigh, in MB: it goes to GitHub in one request with the commit. */
114
+ export const FILE_MAX_MB = 10;
115
+
116
+ /** A PDF, a video or a zip file as it is, in the site's folder of files (`config.folders.files`, /filer/ on
117
+ * the site), named by its own name as an address ("Stress guide.pdf" as stress-guide.pdf, the ending in
118
+ * small letters), with -2, -3 … when the name is taken (`taken`: the paths there are). */
119
+ export async function prepareFile(file: File, taken: string[]): Promise<Upload> {
120
+ if (file.size > FILE_MAX_MB * 1024 * 1024) throw new Error(t.media.tooBig(FILE_MAX_MB));
121
+ const folder = config.folders.files;
122
+ const stem = slugify(file.name.replace(/\.[^.]*$/, '')) || 'fil';
123
+ const ending = pickedEnding(file);
124
+ let path = `${folder}/${stem}.${ending}`;
125
+ for (let n = 2; taken.includes(path); n++) path = `${folder}/${stem}-${n}.${ending}`;
126
+ const upload = { url: URL.createObjectURL(file), path, bytes: new Uint8Array(await file.arrayBuffer()), saved: false };
127
+ fresh.set(publicPath(upload.path), upload.url);
128
+ return upload;
129
+ }
130
+
131
+ // The address of an uploaded picture on the site.
132
+ export const publicPath = (path: string) => path.replace(/^public/, '');
133
+
134
+ // The pictures added in a form (a page, a fixed page), kept in the browser until "Gem", which saves
135
+ // them in the same commit. upload: pick one and make it ready; added: those not saved yet, newest
136
+ // first (offered in the gallery, MediaPicker.tsx); saving: the pictures a text uses.
137
+ export function useUploads(notify: (status: Status) => void) {
138
+ const [uploads, setUploads] = useState<Upload[]>([]);
139
+
140
+ async function upload(): Promise<Upload | null> {
141
+ const file = await pickImage();
142
+ if (!file) return null;
143
+ notify({ text: t.app.preparingPicture });
144
+ try {
145
+ const prepared = await prepareImage(file);
146
+ setUploads((u) => [...u, prepared]);
147
+ notify(null);
148
+ return prepared;
149
+ } catch (e) {
150
+ notify({ text: (e as Error).message, error: true });
151
+ return null;
152
+ }
153
+ }
154
+
155
+ const added = uploads.filter((u) => !u.saved).reverse();
156
+ /** The pictures not saved yet that a text uses. */
157
+ const usedIn = (text: string) => uploads.filter((u) => !u.saved && text.includes(publicPath(u.path)));
158
+ const markSaved = (pictures: Upload[]) => setUploads((all) => all.map((u) => (pictures.includes(u) ? { ...u, saved: true } : u)));
159
+
160
+ return { upload, added, usedIn, markSaved };
161
+ }
162
+
163
+ export type Uploads = ReturnType<typeof useUploads>;