@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,376 +1,376 @@
1
- // The themes in the app store (store.ts): their tab in "Appstore" (#indstillinger/appstore/temaer, StoreThemes), all themes, those
2
- // in the store and those installed, with the same search, order and status filter as the apps; the page of one that
3
- // is not installed (#udseende/temaer/<id>, StoreThemePage), drawn from the catalog as a theme's page (its picture,
4
- // name, version, who made it, "Installer", its pictures of a site in it, what it is and gives the site, and who made
5
- // it); and on the page of one installed from the store (ThemePage in Library.tsx) "Opdater til …" and "Afinstaller"
6
- // (ThemeStoreButtons). Installing copies its folder into the site's themes (config.themes/<id>/, with its store.json),
7
- // updating copies the newer version over it and uninstalling deletes it, each saved at once in its own commit
8
- // (themeChanges); the site's next build has it, and only then can it be chosen ("Brug temaet"). The theme in use, the one
9
- // the site is changing to and the standard theme cannot be uninstalled.
10
- import { useEffect, useState } from 'react';
11
- import { Check, Smiley } from '@phosphor-icons/react';
12
- import Screen from './Screen';
13
- import { Button, EmptyNote, Group, GroupTitle, ImageCarousel, SupportingText, ListItem } from './ui';
14
- import { ConflictError, type RepoFile } from './github';
15
- import type { Env } from './App';
16
- import { ask } from './ask';
17
- import { config } from '@site/editor';
18
- import { iconPacks, packOfTheme } from './iconPacks';
19
- import { theme, themes, type ThemeInfo } from './themes';
20
- import {
21
- changedFiles,
22
- newer,
23
- notesSince,
24
- STANDARD_THEME,
25
- STORE_THEMES,
26
- themeChanges,
27
- themeFolder,
28
- type ShownTheme,
29
- type StoreRecord,
30
- } from './store';
31
- import { askUpdate, DeveloperRow } from './AppList';
32
- import { StoreLicenseRows } from './Licenses';
33
- import { siteT, t } from './i18n';
34
- import { kindAndName } from './deploy';
35
-
36
- /** A theme's page (installed, or in the store). */
37
- export const themeHref = (id: string) => `#udseende/temaer/${id}`;
38
-
39
- /** A theme's picture (its theme.json's icon), where a row has its icon. */
40
- export function ThemePicture({ src }: { src: string }) {
41
- return <img src={src} alt="" className="size-9 shrink-0 rounded-(--block-radius-small)" />;
42
- }
43
-
44
- /** The store's themes for a screen: null while they are read, 'failed' when they could not be. */
45
- export function useThemeCatalog(env: Env): ShownTheme[] | null | 'failed' {
46
- const [catalog, setCatalog] = useState<ShownTheme[] | null | 'failed'>(null);
47
- useEffect(() => {
48
- let on = true;
49
- env.store.themes().then(
50
- (found) => on && setCatalog(found),
51
- () => on && setCatalog('failed'),
52
- );
53
- return () => {
54
- on = false;
55
- };
56
- }, []);
57
- return catalog;
58
- }
59
-
60
- // The themes installed, updated or uninstalled while the editor is open, by their id: 'installed', 'uninstalled' or the
61
- // version updated to, until the site's build has it (and the editor reloads with it, deploy.ts keepCurrent).
62
- const changed = new Map<string, string>();
63
-
64
- /** Whether a theme is in the site now: installed and not uninstalled since, or installed since. */
65
- export const themeInstalled = (id: string) => (changed.has(id) ? changed.get(id) !== 'uninstalled' : themes.some((x) => x.id === id));
66
-
67
- /** A theme installed from the store that the site's build does not have yet. */
68
- export const themePending = (id: string) => changed.get(id) === 'installed' && !themes.some((x) => x.id === id);
69
-
70
- /** The version of a theme installed, as updated while the editor is open. */
71
- const versionOf = (x: ThemeInfo) => (changed.has(x.id) && changed.get(x.id) !== 'installed' ? changed.get(x.id)! : x.version);
72
-
73
- /** The newer version of a theme from the store, when the store has one. */
74
- export const themeUpdateOf = (x: ThemeInfo, catalog: ShownTheme[] | null | 'failed') => {
75
- const found = x.store && Array.isArray(catalog) ? catalog.find((c) => c.id === x.id) : undefined;
76
- return found && newer(found.version, versionOf(x)) ? found : undefined;
77
- };
78
-
79
- /** The theme the site's file says it is built with (config.theme): the one it is, or the one it is changing to. */
80
- export function chosenTheme(files: RepoFile[]): string {
81
- try {
82
- return String(JSON.parse(files.find((f) => f.path === config.theme)?.text ?? '{}').theme ?? theme.id);
83
- } catch {
84
- return theme.id;
85
- }
86
- }
87
-
88
- /** The icon pack chosen in the site's file (config.icons, `{ "pack": "<id>" }`), as it is saved: else the one the theme
89
- * chosen uses (packOfTheme), so after a save, until the site is built with it, the new one. */
90
- export function chosenPack(files: RepoFile[]): string {
91
- let pack: unknown;
92
- try {
93
- pack = JSON.parse(files.find((f) => f.path === config.icons)?.text ?? '{}').pack;
94
- } catch {}
95
- return typeof pack === 'string' && pack ? pack : packOfTheme(chosenTheme(files));
96
- }
97
-
98
- /** The folder in a theme's own folder of one of its icon packs, by the pack's id (`<theme>/<pack>`: icon-packs/<pack>). */
99
- const packFolderInTheme = (id: string) => `icon-packs/${id.split('/')[1]}`;
100
-
101
- /** The theme the icon pack chosen came with (its own folder has it, icon-packs/<pack>/), if it came with one. */
102
- const themeOfPack = (files: RepoFile[]) => iconPacks.find((x) => x.id === chosenPack(files))?.theme;
103
-
104
- /** Whether a theme can be uninstalled: from the store, and neither the standard theme nor in use or chosen, nor the one
105
- * the icon pack chosen came with (the pack goes with the theme). */
106
- const canUninstall = (id: string, files: RepoFile[]) =>
107
- id !== STANDARD_THEME && id !== theme.id && id !== chosenTheme(files) && id !== themeOfPack(files);
108
-
109
- /** Installing, updating and uninstalling a theme from the store, each saved at once in its own commit (themeChanges).
110
- * Each says true when it was saved. */
111
- function useThemeActions(env: Env) {
112
- const [saving, setSaving] = useState(false);
113
- const recordOf = (x: ShownTheme): StoreRecord => ({ version: x.version, ref: x.ref, files: x.files });
114
-
115
- async function change(
116
- id: string,
117
- text: { busy: string; done: string; commit: string; change: string },
118
- next: () => Promise<Parameters<typeof themeChanges>[2]>,
119
- go: (current: RepoFile[]) => Promise<boolean> = async () => true,
120
- ): Promise<boolean> {
121
- setSaving(true);
122
- env.notify({ text: text.busy });
123
- try {
124
- const [current, wanted] = await Promise.all([env.folder(themeFolder(id)), next()]);
125
- if (!(await go(current))) {
126
- env.notify(null);
127
- return false;
128
- }
129
- await env.commit(text.commit, themeChanges(id, current, wanted), text.change);
130
- env.notify({ text: text.done, kind: 'saved' });
131
- return true;
132
- } catch (e) {
133
- env.notify({ text: e instanceof ConflictError ? e.message : t.common.notSaved((e as Error).message), error: true, kind: 'failed' });
134
- return false;
135
- } finally {
136
- setSaving(false);
137
- }
138
- }
139
-
140
- return {
141
- saving,
142
- async install(x: ShownTheme) {
143
- const done = await change(
144
- x.id,
145
- {
146
- busy: t.apps.installing,
147
- done: t.library.themeInstalled(x.name),
148
- commit: siteT.library.themeCommits.install(x.name),
149
- change: t.app.changes.installed(kindAndName(t.app.changes.theme, x.name)),
150
- },
151
- async () => ({ files: await env.store.files(x), record: recordOf(x) }),
152
- );
153
- if (done) changed.set(x.id, 'installed');
154
- return done;
155
- },
156
- async update(x: ShownTheme, record: StoreRecord) {
157
- const done = await change(
158
- x.id,
159
- {
160
- busy: t.apps.updating,
161
- done: t.library.themeUpdated(x.name, x.version),
162
- commit: siteT.library.themeCommits.update(x.name, x.version),
163
- change: t.app.changes.updated(kindAndName(t.app.changes.theme, x.name), x.version),
164
- },
165
- async () => ({ files: await env.store.files(x), record: recordOf(x) }),
166
- async (current) => {
167
- env.notify(null);
168
- // The icon pack chosen, when it came with this theme, must be in the new version too: it goes with the theme.
169
- const pack = iconPacks.find((p) => p.id === chosenPack(env.files));
170
- if (pack?.theme === x.id && !x.files.some((f) => f.path === `${packFolderInTheme(pack.id)}/pack.json`)) {
171
- env.notify({ text: t.library.themeDropsIconPack(x.name, pack.name), error: true });
172
- return false;
173
- }
174
- return askUpdate(
175
- x.name,
176
- record.version,
177
- x.version,
178
- notesSince(x.releaseNotes, record.version, x.version),
179
- changedFiles(x.id, record, current, themeFolder(x.id)),
180
- );
181
- },
182
- );
183
- if (done) changed.set(x.id, x.version);
184
- return done;
185
- },
186
- async uninstall(id: string, name: string) {
187
- if (!(await ask(t.library.askUninstallTheme(name), t.apps.uninstall))) return false;
188
- const done = await change(
189
- id,
190
- {
191
- busy: t.apps.uninstalling,
192
- done: t.library.themeUninstalled(name),
193
- commit: siteT.library.themeCommits.uninstall(name),
194
- change: t.app.changes.uninstalled(kindAndName(t.app.changes.theme, name)),
195
- },
196
- async () => null,
197
- );
198
- if (done) changed.set(id, 'uninstalled');
199
- return done;
200
- },
201
- };
202
- }
203
-
204
- /** All themes, as "Appstore" lists them: those in the store's catalog and those installed (not from the store too),
205
- * each with its words for the search. */
206
- export function allThemes(catalog: ShownTheme[] | null | 'failed') {
207
- const store = Array.isArray(catalog) ? catalog : [];
208
- return [
209
- ...store.map((x) => ({ id: x.id, name: x.name, author: x.author, version: x.version, icon: x.icon, words: x.words })),
210
- ...themes
211
- .filter((x) => !store.some((s) => s.id === x.id))
212
- .map((x) => ({
213
- id: x.id,
214
- name: x.name,
215
- author: x.author,
216
- version: x.version,
217
- icon: x.icon,
218
- words: [x.name, x.author, ...Object.values(x.description ?? {}), ...Object.values(x.highlights ?? {}).flat()]
219
- .join(' ')
220
- .toLowerCase(),
221
- })),
222
- ];
223
- }
224
-
225
- /** A theme's row in "Appstore" and on a developer's page: its picture, its name, its version and who made it, and
226
- * whether it is installed, opening its page. */
227
- export function ThemeRow({ of }: { of: ReturnType<typeof allThemes>[number] }) {
228
- return (
229
- <ListItem
230
- href={themeHref(of.id)}
231
- leading={<ThemePicture src={of.icon} />}
232
- headline={of.name}
233
- supporting={t.library.themeByline(of.version, of.author)}
234
- truncate
235
- count={themeInstalled(of.id) && (themePending(of.id) ? t.apps.pending : t.apps.installed)}
236
- arrow
237
- />
238
- );
239
- }
240
-
241
- /** The page of a theme in the store that is not installed, or installed but not yet built (#udseende/temaer/<id>),
242
- * from the catalog: as a theme's page, with "Installer" (or "Afinstaller"). */
243
- export function StoreThemePage({ id, env }: { id: string; env: Env }) {
244
- const catalog = useThemeCatalog(env);
245
- const actions = useThemeActions(env);
246
- const [, redraw] = useState(0);
247
- const of = Array.isArray(catalog) ? catalog.find((x) => x.id === id) : undefined;
248
- const pending = themePending(id);
249
- const [pictures, setPictures] = useState<{ key: string; src: string }[]>([]);
250
- useEffect(() => {
251
- if (!of?.screenshots?.length) return;
252
- let on = true;
253
- let made: string[] = [];
254
- env.store.themeScreenshots(of).then((found) => {
255
- made = found.map((x) => x.src);
256
- if (on) setPictures(found);
257
- else made.forEach((src) => URL.revokeObjectURL(src));
258
- });
259
- return () => {
260
- on = false;
261
- made.forEach((src) => URL.revokeObjectURL(src));
262
- };
263
- }, [of?.id, of?.version]);
264
- const shotName = (key: string) => t.library.themeShots[key] ?? of?.name ?? '';
265
-
266
- return (
267
- <Screen title={of?.name ?? t.common.parts.themes} wide back={{ href: `#${STORE_THEMES}`, label: t.apps.find }}>
268
- {of ? (
269
- <>
270
- <div className="mb-8 flex items-center gap-4 px-4">
271
- <img src={of.icon} alt="" className="size-16 shrink-0 rounded-2xl" />
272
- <span className="min-w-0 flex-1">
273
- <span className="block truncate font-medium">{of.name}</span>
274
- <span className="block truncate text-sm text-muted">{t.library.themeByline(of.version, of.author)}</span>
275
- <span className="block text-sm text-muted">{pending ? t.apps.pending : t.apps.notInstalled}</span>
276
- </span>
277
- </div>
278
- {!pending && (
279
- <div className="mb-8">
280
- <Button wide disabled={actions.saving} onClick={async () => (await actions.install(of)) && redraw((n) => n + 1)}>
281
- {t.apps.install}
282
- </Button>
283
- </div>
284
- )}
285
- {pictures.length > 0 && (
286
- <section className="mb-8">
287
- <GroupTitle>{t.library.themeLooks}</GroupTitle>
288
- <ImageCarousel label={t.library.themeLooks} images={pictures.map((x) => ({ src: x.src, label: shotName(x.key) }))} />
289
- </section>
290
- )}
291
- {of.description && (
292
- <section className="mb-8">
293
- <GroupTitle>{t.library.themeAbout}</GroupTitle>
294
- <p className="px-4">{of.description}</p>
295
- </section>
296
- )}
297
- {of.highlights.length > 0 && (
298
- <Group title={t.library.themeHighlights}>
299
- {of.highlights.map((h) => (
300
- <ListItem key={h} icon={Check} headline={h} />
301
- ))}
302
- </Group>
303
- )}
304
- {!!of.iconPacks?.length && (
305
- <Group title={t.common.parts.iconPacks}>
306
- {of.iconPacks.map((p) => (
307
- <ListItem key={p.id} icon={Smiley} headline={p.name} supporting={t.library.iconPackByline(p.author, p.license)} truncate />
308
- ))}
309
- </Group>
310
- )}
311
- <StoreLicenseRows licenses={of.licenses} name={of.name} author={of.author} version={of.version} />
312
- <DeveloperRow name={of.author} />
313
- {pending && (
314
- <div>
315
- <Button
316
- kind="outlined"
317
-
318
- wide
319
- disabled={actions.saving}
320
- onClick={async () => (await actions.uninstall(of.id, of.name)) && redraw((n) => n + 1)}
321
- >
322
- {t.apps.uninstall}
323
- </Button>
324
- <SupportingText className="mt-2">{t.library.themePendingHint}</SupportingText>
325
- </div>
326
- )}
327
- </>
328
- ) : catalog === null ? (
329
- <EmptyNote busy>{t.apps.loadingStore}</EmptyNote>
330
- ) : (
331
- <EmptyNote>{catalog === 'failed' ? t.apps.storeFailed : t.library.themeNotInStore}</EmptyNote>
332
- )}
333
- </Screen>
334
- );
335
- }
336
-
337
- /** On the page of a theme installed from the store: "Opdater til …" (`top`: under its name), when the store has a newer
338
- * version; or "Afinstaller" (at the end), which cannot be pressed while it is in use. */
339
- export function ThemeStoreButtons({ of, env, files, top }: { of: ThemeInfo; env: Env; files: RepoFile[]; top?: boolean }) {
340
- const catalog = useThemeCatalog(env);
341
- const actions = useThemeActions(env);
342
- const [, redraw] = useState(0);
343
- if (!of.store) return null;
344
- const update = themeUpdateOf(of, catalog);
345
- if (top)
346
- return update ? (
347
- <div className="mb-8">
348
- <Button wide disabled={actions.saving} onClick={() => actions.update(update, of.store!)}>
349
- {t.apps.update(update.version)}
350
- </Button>
351
- </div>
352
- ) : null;
353
- const free = canUninstall(of.id, files) && themeInstalled(of.id);
354
- return (
355
- <div>
356
- <Button
357
- kind="outlined"
358
-
359
- wide
360
- disabled={actions.saving || !free}
361
- onClick={async () => (await actions.uninstall(of.id, of.name)) && redraw((n) => n + 1)}
362
- >
363
- {t.apps.uninstall}
364
- </Button>
365
- <SupportingText className="mt-2">
366
- {!themeInstalled(of.id)
367
- ? t.library.themeUninstalled(of.name)
368
- : free
369
- ? t.library.themeUninstallHint
370
- : of.id === themeOfPack(files) && of.id !== theme.id && of.id !== chosenTheme(files)
371
- ? t.library.themeIconPackStuck
372
- : t.library.themeStuck}
373
- </SupportingText>
374
- </div>
375
- );
376
- }
1
+ // The themes in the app store (store.ts): their tab in "Appstore" (#indstillinger/appstore/temaer, StoreThemes), all themes, those
2
+ // in the store and those installed, with the same search, order and status filter as the apps; the page of one that
3
+ // is not installed (#udseende/temaer/<id>, StoreThemePage), drawn from the catalog as a theme's page (its picture,
4
+ // name, version, who made it, "Installer", its pictures of a site in it, what it is and gives the site, and who made
5
+ // it); and on the page of one installed from the store (ThemePage in Library.tsx) "Opdater til …" and "Afinstaller"
6
+ // (ThemeStoreButtons). Installing copies its folder into the site's themes (config.themes/<id>/, with its store.json),
7
+ // updating copies the newer version over it and uninstalling deletes it, each saved at once in its own commit
8
+ // (themeChanges); the site's next build has it, and only then can it be chosen ("Brug temaet"). The theme in use, the one
9
+ // the site is changing to and the standard theme cannot be uninstalled.
10
+ import { useEffect, useState } from 'react';
11
+ import { Check, Smiley } from '@phosphor-icons/react';
12
+ import Screen from './Screen';
13
+ import { Button, EmptyNote, Group, GroupTitle, ImageCarousel, SupportingText, ListItem } from './ui';
14
+ import { ConflictError, type RepoFile } from './github';
15
+ import type { Env } from './App';
16
+ import { ask } from './ask';
17
+ import { config } from '@site/editor';
18
+ import { iconPacks, packOfTheme } from './iconPacks';
19
+ import { theme, themes, type ThemeInfo } from './themes';
20
+ import {
21
+ changedFiles,
22
+ newer,
23
+ notesSince,
24
+ STANDARD_THEME,
25
+ STORE_THEMES,
26
+ themeChanges,
27
+ themeFolder,
28
+ type ShownTheme,
29
+ type StoreRecord,
30
+ } from './store';
31
+ import { askUpdate, DeveloperRow } from './AppList';
32
+ import { StoreLicenseRows } from './Licenses';
33
+ import { siteT, t } from './i18n';
34
+ import { kindAndName } from './deploy';
35
+
36
+ /** A theme's page (installed, or in the store). */
37
+ export const themeHref = (id: string) => `#udseende/temaer/${id}`;
38
+
39
+ /** A theme's picture (its theme.json's icon), where a row has its icon. */
40
+ export function ThemePicture({ src }: { src: string }) {
41
+ return <img src={src} alt="" className="size-9 shrink-0 rounded-(--block-radius-small)" />;
42
+ }
43
+
44
+ /** The store's themes for a screen: null while they are read, 'failed' when they could not be. */
45
+ export function useThemeCatalog(env: Env): ShownTheme[] | null | 'failed' {
46
+ const [catalog, setCatalog] = useState<ShownTheme[] | null | 'failed'>(null);
47
+ useEffect(() => {
48
+ let on = true;
49
+ env.store.themes().then(
50
+ (found) => on && setCatalog(found),
51
+ () => on && setCatalog('failed'),
52
+ );
53
+ return () => {
54
+ on = false;
55
+ };
56
+ }, []);
57
+ return catalog;
58
+ }
59
+
60
+ // The themes installed, updated or uninstalled while the editor is open, by their id: 'installed', 'uninstalled' or the
61
+ // version updated to, until the site's build has it (and the editor reloads with it, deploy.ts keepCurrent).
62
+ const changed = new Map<string, string>();
63
+
64
+ /** Whether a theme is in the site now: installed and not uninstalled since, or installed since. */
65
+ export const themeInstalled = (id: string) => (changed.has(id) ? changed.get(id) !== 'uninstalled' : themes.some((x) => x.id === id));
66
+
67
+ /** A theme installed from the store that the site's build does not have yet. */
68
+ export const themePending = (id: string) => changed.get(id) === 'installed' && !themes.some((x) => x.id === id);
69
+
70
+ /** The version of a theme installed, as updated while the editor is open. */
71
+ const versionOf = (x: ThemeInfo) => (changed.has(x.id) && changed.get(x.id) !== 'installed' ? changed.get(x.id)! : x.version);
72
+
73
+ /** The newer version of a theme from the store, when the store has one. */
74
+ export const themeUpdateOf = (x: ThemeInfo, catalog: ShownTheme[] | null | 'failed') => {
75
+ const found = x.store && Array.isArray(catalog) ? catalog.find((c) => c.id === x.id) : undefined;
76
+ return found && newer(found.version, versionOf(x)) ? found : undefined;
77
+ };
78
+
79
+ /** The theme the site's file says it is built with (config.theme): the one it is, or the one it is changing to. */
80
+ export function chosenTheme(files: RepoFile[]): string {
81
+ try {
82
+ return String(JSON.parse(files.find((f) => f.path === config.theme)?.text ?? '{}').theme ?? theme.id);
83
+ } catch {
84
+ return theme.id;
85
+ }
86
+ }
87
+
88
+ /** The icon pack chosen in the site's file (config.icons, `{ "pack": "<id>" }`), as it is saved: else the one the theme
89
+ * chosen uses (packOfTheme), so after a save, until the site is built with it, the new one. */
90
+ export function chosenPack(files: RepoFile[]): string {
91
+ let pack: unknown;
92
+ try {
93
+ pack = JSON.parse(files.find((f) => f.path === config.icons)?.text ?? '{}').pack;
94
+ } catch {}
95
+ return typeof pack === 'string' && pack ? pack : packOfTheme(chosenTheme(files));
96
+ }
97
+
98
+ /** The folder in a theme's own folder of one of its icon packs, by the pack's id (`<theme>/<pack>`: icon-packs/<pack>). */
99
+ const packFolderInTheme = (id: string) => `icon-packs/${id.split('/')[1]}`;
100
+
101
+ /** The theme the icon pack chosen came with (its own folder has it, icon-packs/<pack>/), if it came with one. */
102
+ const themeOfPack = (files: RepoFile[]) => iconPacks.find((x) => x.id === chosenPack(files))?.theme;
103
+
104
+ /** Whether a theme can be uninstalled: from the store, and neither the standard theme nor in use or chosen, nor the one
105
+ * the icon pack chosen came with (the pack goes with the theme). */
106
+ const canUninstall = (id: string, files: RepoFile[]) =>
107
+ id !== STANDARD_THEME && id !== theme.id && id !== chosenTheme(files) && id !== themeOfPack(files);
108
+
109
+ /** Installing, updating and uninstalling a theme from the store, each saved at once in its own commit (themeChanges).
110
+ * Each says true when it was saved. */
111
+ function useThemeActions(env: Env) {
112
+ const [saving, setSaving] = useState(false);
113
+ const recordOf = (x: ShownTheme): StoreRecord => ({ version: x.version, ref: x.version, files: x.files });
114
+
115
+ async function change(
116
+ id: string,
117
+ text: { busy: string; done: string; commit: string; change: string },
118
+ next: () => Promise<Parameters<typeof themeChanges>[2]>,
119
+ go: (current: RepoFile[]) => Promise<boolean> = async () => true,
120
+ ): Promise<boolean> {
121
+ setSaving(true);
122
+ env.notify({ text: text.busy });
123
+ try {
124
+ const [current, wanted] = await Promise.all([env.folder(themeFolder(id)), next()]);
125
+ if (!(await go(current))) {
126
+ env.notify(null);
127
+ return false;
128
+ }
129
+ await env.commit(text.commit, themeChanges(id, current, wanted), text.change);
130
+ env.notify({ text: text.done, kind: 'saved' });
131
+ return true;
132
+ } catch (e) {
133
+ env.notify({ text: e instanceof ConflictError ? e.message : t.common.notSaved((e as Error).message), error: true, kind: 'failed' });
134
+ return false;
135
+ } finally {
136
+ setSaving(false);
137
+ }
138
+ }
139
+
140
+ return {
141
+ saving,
142
+ async install(x: ShownTheme) {
143
+ const done = await change(
144
+ x.id,
145
+ {
146
+ busy: t.apps.installing,
147
+ done: t.library.themeInstalled(x.name),
148
+ commit: siteT.library.themeCommits.install(x.name),
149
+ change: t.app.changes.installed(kindAndName(t.app.changes.theme, x.name)),
150
+ },
151
+ async () => ({ files: await env.store.files(x), record: recordOf(x) }),
152
+ );
153
+ if (done) changed.set(x.id, 'installed');
154
+ return done;
155
+ },
156
+ async update(x: ShownTheme, record: StoreRecord) {
157
+ const done = await change(
158
+ x.id,
159
+ {
160
+ busy: t.apps.updating,
161
+ done: t.library.themeUpdated(x.name, x.version),
162
+ commit: siteT.library.themeCommits.update(x.name, x.version),
163
+ change: t.app.changes.updated(kindAndName(t.app.changes.theme, x.name), x.version),
164
+ },
165
+ async () => ({ files: await env.store.files(x), record: recordOf(x) }),
166
+ async (current) => {
167
+ env.notify(null);
168
+ // The icon pack chosen, when it came with this theme, must be in the new version too: it goes with the theme.
169
+ const pack = iconPacks.find((p) => p.id === chosenPack(env.files));
170
+ if (pack?.theme === x.id && !x.files.some((f) => f.path === `${packFolderInTheme(pack.id)}/pack.json`)) {
171
+ env.notify({ text: t.library.themeDropsIconPack(x.name, pack.name), error: true });
172
+ return false;
173
+ }
174
+ return askUpdate(
175
+ x.name,
176
+ record.version,
177
+ x.version,
178
+ notesSince(x.releaseNotes, record.version, x.version),
179
+ changedFiles(x.id, record, current, themeFolder(x.id)),
180
+ );
181
+ },
182
+ );
183
+ if (done) changed.set(x.id, x.version);
184
+ return done;
185
+ },
186
+ async uninstall(id: string, name: string) {
187
+ if (!(await ask(t.library.askUninstallTheme(name), t.apps.uninstall))) return false;
188
+ const done = await change(
189
+ id,
190
+ {
191
+ busy: t.apps.uninstalling,
192
+ done: t.library.themeUninstalled(name),
193
+ commit: siteT.library.themeCommits.uninstall(name),
194
+ change: t.app.changes.uninstalled(kindAndName(t.app.changes.theme, name)),
195
+ },
196
+ async () => null,
197
+ );
198
+ if (done) changed.set(id, 'uninstalled');
199
+ return done;
200
+ },
201
+ };
202
+ }
203
+
204
+ /** All themes, as "Appstore" lists them: those in the store's catalog and those installed (not from the store too),
205
+ * each with its words for the search. */
206
+ export function allThemes(catalog: ShownTheme[] | null | 'failed') {
207
+ const store = Array.isArray(catalog) ? catalog : [];
208
+ return [
209
+ ...store.map((x) => ({ id: x.id, name: x.name, author: x.author, version: x.version, icon: x.icon, words: x.words })),
210
+ ...themes
211
+ .filter((x) => !store.some((s) => s.id === x.id))
212
+ .map((x) => ({
213
+ id: x.id,
214
+ name: x.name,
215
+ author: x.author,
216
+ version: x.version,
217
+ icon: x.icon,
218
+ words: [x.name, x.author, ...Object.values(x.description ?? {}), ...Object.values(x.highlights ?? {}).flat()]
219
+ .join(' ')
220
+ .toLowerCase(),
221
+ })),
222
+ ];
223
+ }
224
+
225
+ /** A theme's row in "Appstore" and on a developer's page: its picture, its name, its version and who made it, and
226
+ * whether it is installed, opening its page. */
227
+ export function ThemeRow({ of }: { of: ReturnType<typeof allThemes>[number] }) {
228
+ return (
229
+ <ListItem
230
+ href={themeHref(of.id)}
231
+ leading={<ThemePicture src={of.icon} />}
232
+ headline={of.name}
233
+ supporting={t.library.themeByline(of.version, of.author)}
234
+ truncate
235
+ count={themeInstalled(of.id) && (themePending(of.id) ? t.apps.pending : t.apps.installed)}
236
+ arrow
237
+ />
238
+ );
239
+ }
240
+
241
+ /** The page of a theme in the store that is not installed, or installed but not yet built (#udseende/temaer/<id>),
242
+ * from the catalog: as a theme's page, with "Installer" (or "Afinstaller"). */
243
+ export function StoreThemePage({ id, env }: { id: string; env: Env }) {
244
+ const catalog = useThemeCatalog(env);
245
+ const actions = useThemeActions(env);
246
+ const [, redraw] = useState(0);
247
+ const of = Array.isArray(catalog) ? catalog.find((x) => x.id === id) : undefined;
248
+ const pending = themePending(id);
249
+ const [pictures, setPictures] = useState<{ key: string; src: string }[]>([]);
250
+ useEffect(() => {
251
+ if (!of?.screenshots?.length) return;
252
+ let on = true;
253
+ let made: string[] = [];
254
+ env.store.themeScreenshots(of).then((found) => {
255
+ made = found.map((x) => x.src);
256
+ if (on) setPictures(found);
257
+ else made.forEach((src) => URL.revokeObjectURL(src));
258
+ });
259
+ return () => {
260
+ on = false;
261
+ made.forEach((src) => URL.revokeObjectURL(src));
262
+ };
263
+ }, [of?.id, of?.version]);
264
+ const shotName = (key: string) => t.library.themeShots[key] ?? of?.name ?? '';
265
+
266
+ return (
267
+ <Screen title={of?.name ?? t.common.parts.themes} wide back={{ href: `#${STORE_THEMES}`, label: t.apps.find }}>
268
+ {of ? (
269
+ <>
270
+ <div className="mb-8 flex items-center gap-4 px-4">
271
+ <img src={of.icon} alt="" className="size-16 shrink-0 rounded-2xl" />
272
+ <span className="min-w-0 flex-1">
273
+ <span className="block truncate font-medium">{of.name}</span>
274
+ <span className="block truncate text-sm text-muted">{t.library.themeByline(of.version, of.author)}</span>
275
+ <span className="block text-sm text-muted">{pending ? t.apps.pending : t.apps.notInstalled}</span>
276
+ </span>
277
+ </div>
278
+ {!pending && (
279
+ <div className="mb-8">
280
+ <Button wide disabled={actions.saving} onClick={async () => (await actions.install(of)) && redraw((n) => n + 1)}>
281
+ {t.apps.install}
282
+ </Button>
283
+ </div>
284
+ )}
285
+ {pictures.length > 0 && (
286
+ <section className="mb-8">
287
+ <GroupTitle>{t.library.themeLooks}</GroupTitle>
288
+ <ImageCarousel label={t.library.themeLooks} images={pictures.map((x) => ({ src: x.src, label: shotName(x.key) }))} />
289
+ </section>
290
+ )}
291
+ {of.description && (
292
+ <section className="mb-8">
293
+ <GroupTitle>{t.library.themeAbout}</GroupTitle>
294
+ <p className="px-4">{of.description}</p>
295
+ </section>
296
+ )}
297
+ {of.highlights.length > 0 && (
298
+ <Group title={t.library.themeHighlights}>
299
+ {of.highlights.map((h) => (
300
+ <ListItem key={h} icon={Check} headline={h} />
301
+ ))}
302
+ </Group>
303
+ )}
304
+ {!!of.iconPacks?.length && (
305
+ <Group title={t.common.parts.iconPacks}>
306
+ {of.iconPacks.map((p) => (
307
+ <ListItem key={p.id} icon={Smiley} headline={p.name} supporting={t.library.iconPackByline(p.author, p.license)} truncate />
308
+ ))}
309
+ </Group>
310
+ )}
311
+ <StoreLicenseRows licenses={of.licenses} name={of.name} author={of.author} version={of.version} />
312
+ <DeveloperRow name={of.author} />
313
+ {pending && (
314
+ <div>
315
+ <Button
316
+ kind="outlined"
317
+
318
+ wide
319
+ disabled={actions.saving}
320
+ onClick={async () => (await actions.uninstall(of.id, of.name)) && redraw((n) => n + 1)}
321
+ >
322
+ {t.apps.uninstall}
323
+ </Button>
324
+ <SupportingText className="mt-2">{t.library.themePendingHint}</SupportingText>
325
+ </div>
326
+ )}
327
+ </>
328
+ ) : catalog === null ? (
329
+ <EmptyNote busy>{t.apps.loadingStore}</EmptyNote>
330
+ ) : (
331
+ <EmptyNote>{catalog === 'failed' ? t.apps.storeFailed : t.library.themeNotInStore}</EmptyNote>
332
+ )}
333
+ </Screen>
334
+ );
335
+ }
336
+
337
+ /** On the page of a theme installed from the store: "Opdater til …" (`top`: under its name), when the store has a newer
338
+ * version; or "Afinstaller" (at the end), which cannot be pressed while it is in use. */
339
+ export function ThemeStoreButtons({ of, env, files, top }: { of: ThemeInfo; env: Env; files: RepoFile[]; top?: boolean }) {
340
+ const catalog = useThemeCatalog(env);
341
+ const actions = useThemeActions(env);
342
+ const [, redraw] = useState(0);
343
+ if (!of.store) return null;
344
+ const update = themeUpdateOf(of, catalog);
345
+ if (top)
346
+ return update ? (
347
+ <div className="mb-8">
348
+ <Button wide disabled={actions.saving} onClick={() => actions.update(update, of.store!)}>
349
+ {t.apps.update(update.version)}
350
+ </Button>
351
+ </div>
352
+ ) : null;
353
+ const free = canUninstall(of.id, files) && themeInstalled(of.id);
354
+ return (
355
+ <div>
356
+ <Button
357
+ kind="outlined"
358
+
359
+ wide
360
+ disabled={actions.saving || !free}
361
+ onClick={async () => (await actions.uninstall(of.id, of.name)) && redraw((n) => n + 1)}
362
+ >
363
+ {t.apps.uninstall}
364
+ </Button>
365
+ <SupportingText className="mt-2">
366
+ {!themeInstalled(of.id)
367
+ ? t.library.themeUninstalled(of.name)
368
+ : free
369
+ ? t.library.themeUninstallHint
370
+ : of.id === themeOfPack(files) && of.id !== theme.id && of.id !== chosenTheme(files)
371
+ ? t.library.themeIconPackStuck
372
+ : t.library.themeStuck}
373
+ </SupportingText>
374
+ </div>
375
+ );
376
+ }