@morphemecms/cms 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/astro.mjs +3 -0
  3. package/bin/morpheme.mjs +3 -1
  4. package/defaults/blockTexts.ts +2 -2
  5. package/defaults/collections.ts +10 -0
  6. package/defaults/editor.ts +57 -53
  7. package/defaults/privacy.ts +4 -1
  8. package/defaults/rules.ts +36 -38
  9. package/defaults/standardPages.ts +0 -3
  10. package/defaults/texts.ts +11 -2
  11. package/demo/src/content/pages/about.md +2 -0
  12. package/icons/lucide/browser-fill.svg +1 -0
  13. package/icons/lucide/buildings-fill.svg +1 -0
  14. package/icons/lucide/certificate-fill.svg +1 -0
  15. package/icons/lucide/chart-line-up-fill.svg +1 -0
  16. package/icons/lucide/chats-fill.svg +1 -0
  17. package/icons/lucide/envelope-simple-fill.svg +1 -0
  18. package/icons/lucide/file-text-fill.svg +1 -0
  19. package/icons/lucide/folder-simple-fill.svg +1 -0
  20. package/icons/lucide/gauge.svg +1 -1
  21. package/icons/lucide/gear-six.svg +1 -0
  22. package/icons/lucide/globe-simple.svg +1 -0
  23. package/icons/lucide/heart-fill.svg +1 -0
  24. package/icons/lucide/layout.svg +1 -0
  25. package/icons/lucide/list-checks-fill.svg +1 -0
  26. package/icons/lucide/magnifying-glass-fill.svg +1 -0
  27. package/icons/lucide/megaphone-fill.svg +1 -0
  28. package/icons/lucide/newspaper-fill.svg +1 -0
  29. package/icons/lucide/paint-brush-broad-fill.svg +1 -0
  30. package/icons/lucide/puzzle-piece-fill.svg +1 -0
  31. package/icons/lucide/shield-check-fill.svg +1 -0
  32. package/icons/lucide/sidebar-simple.svg +1 -1
  33. package/icons/lucide/sidebar.svg +1 -0
  34. package/icons/lucide/square-half-bottom-fill.svg +1 -0
  35. package/icons/lucide/square-half.svg +1 -0
  36. package/icons/lucide/squares-four-fill.svg +1 -0
  37. package/icons/lucide/storefront-fill.svg +1 -0
  38. package/icons/lucide/tag-fill.svg +1 -0
  39. package/icons/lucide/toggle-right-fill.svg +1 -0
  40. package/icons/lucide/trash-fill.svg +1 -0
  41. package/icons/lucide/user-fill.svg +1 -0
  42. package/icons/lucide/wrench-fill.svg +1 -0
  43. package/icons/material-symbols/browser-fill.svg +1 -0
  44. package/icons/material-symbols/buildings-fill.svg +1 -0
  45. package/icons/material-symbols/certificate-fill.svg +1 -0
  46. package/icons/material-symbols/chart-line-up-fill.svg +1 -0
  47. package/icons/material-symbols/chats-fill.svg +1 -0
  48. package/icons/material-symbols/envelope-simple-fill.svg +1 -0
  49. package/icons/material-symbols/file-text-fill.svg +1 -0
  50. package/icons/material-symbols/folder-simple-fill.svg +1 -0
  51. package/icons/material-symbols/gauge.svg +1 -1
  52. package/icons/material-symbols/gear-six.svg +1 -0
  53. package/icons/material-symbols/globe-simple.svg +1 -0
  54. package/icons/material-symbols/heart-fill.svg +1 -0
  55. package/icons/material-symbols/layout.svg +1 -0
  56. package/icons/material-symbols/list-checks-fill.svg +1 -0
  57. package/icons/material-symbols/magnifying-glass-fill.svg +1 -0
  58. package/icons/material-symbols/megaphone-fill.svg +1 -0
  59. package/icons/material-symbols/newspaper-fill.svg +1 -0
  60. package/icons/material-symbols/paint-brush-broad-fill.svg +1 -0
  61. package/icons/material-symbols/puzzle-piece-fill.svg +1 -0
  62. package/icons/material-symbols/shield-check-fill.svg +1 -0
  63. package/icons/material-symbols/sidebar-simple.svg +1 -1
  64. package/icons/material-symbols/sidebar.svg +1 -0
  65. package/icons/material-symbols/square-half-bottom-fill.svg +1 -0
  66. package/icons/material-symbols/square-half.svg +1 -0
  67. package/icons/material-symbols/squares-four-fill.svg +1 -0
  68. package/icons/material-symbols/storefront-fill.svg +1 -0
  69. package/icons/material-symbols/tag-fill.svg +1 -0
  70. package/icons/material-symbols/toggle-right-fill.svg +1 -0
  71. package/icons/material-symbols/trash-fill.svg +1 -0
  72. package/icons/material-symbols/user-fill.svg +1 -0
  73. package/icons/material-symbols/wrench-fill.svg +1 -0
  74. package/icons/phosphor/browser-fill.svg +1 -0
  75. package/icons/phosphor/buildings-fill.svg +1 -0
  76. package/icons/phosphor/certificate-fill.svg +1 -0
  77. package/icons/phosphor/chart-line-up-fill.svg +1 -0
  78. package/icons/phosphor/chats-fill.svg +1 -0
  79. package/icons/phosphor/envelope-simple-fill.svg +1 -0
  80. package/icons/phosphor/file-text-fill.svg +1 -0
  81. package/icons/phosphor/folder-simple-fill.svg +1 -0
  82. package/icons/phosphor/gear-six.svg +1 -0
  83. package/icons/phosphor/globe-simple.svg +1 -0
  84. package/icons/phosphor/heart-fill.svg +1 -0
  85. package/icons/phosphor/layout.svg +1 -0
  86. package/icons/phosphor/list-checks-fill.svg +1 -0
  87. package/icons/phosphor/magnifying-glass-fill.svg +1 -0
  88. package/icons/phosphor/megaphone-fill.svg +1 -0
  89. package/icons/phosphor/newspaper-fill.svg +1 -0
  90. package/icons/phosphor/paint-brush-broad-fill.svg +1 -0
  91. package/icons/phosphor/puzzle-piece-fill.svg +1 -0
  92. package/icons/phosphor/shield-check-fill.svg +1 -0
  93. package/icons/phosphor/sidebar.svg +1 -0
  94. package/icons/phosphor/square-half-bottom-fill.svg +1 -0
  95. package/icons/phosphor/square-half.svg +1 -0
  96. package/icons/phosphor/squares-four-fill.svg +1 -0
  97. package/icons/phosphor/storefront-fill.svg +1 -0
  98. package/icons/phosphor/tag-fill.svg +1 -0
  99. package/icons/phosphor/toggle-right-fill.svg +1 -0
  100. package/icons/phosphor/trash-fill.svg +1 -0
  101. package/icons/phosphor/user-fill.svg +1 -0
  102. package/icons/phosphor/wrench-fill.svg +1 -0
  103. package/lib/breakpoints.d.mts +13 -0
  104. package/lib/breakpoints.mjs +20 -0
  105. package/lib/breakpoints.test.mjs +39 -0
  106. package/lib/build-lane.d.mts +15 -0
  107. package/lib/build-lane.mjs +23 -0
  108. package/lib/build-lane.test.mjs +74 -0
  109. package/lib/build-settings.d.mts +16 -0
  110. package/lib/build-settings.mjs +39 -0
  111. package/lib/build-settings.test.mjs +41 -0
  112. package/lib/contracts.d.mts +3 -2
  113. package/lib/contracts.mjs +3 -2
  114. package/lib/drawer-swipe.d.mts +10 -0
  115. package/lib/drawer-swipe.mjs +17 -0
  116. package/lib/drawer-swipe.test.mjs +33 -0
  117. package/lib/drawerKeys.d.mts +9 -0
  118. package/lib/drawerKeys.mjs +26 -0
  119. package/lib/drawerKeys.test.mjs +21 -0
  120. package/lib/eyebrow.d.mts +6 -0
  121. package/lib/eyebrow.mjs +24 -0
  122. package/lib/eyebrow.test.mjs +20 -0
  123. package/lib/front-sections.test.mjs +82 -0
  124. package/lib/heading-levels.d.mts +7 -0
  125. package/lib/heading-levels.mjs +16 -0
  126. package/lib/heading-levels.test.mjs +37 -0
  127. package/lib/inline.ts +3 -2
  128. package/lib/markdown.ts +2 -0
  129. package/lib/materialIcon.d.mts +21 -0
  130. package/lib/materialIcon.mjs +29 -0
  131. package/lib/materialIcon.test.mjs +45 -0
  132. package/lib/menu-layout.d.mts +11 -0
  133. package/lib/menu-layout.mjs +14 -0
  134. package/lib/menu-layout.test.mjs +23 -0
  135. package/lib/menu.d.mts +59 -0
  136. package/lib/menu.mjs +129 -0
  137. package/lib/menu.test.mjs +291 -0
  138. package/lib/rehype-emphasize.d.mts +4 -0
  139. package/lib/rehype-emphasize.mjs +20 -0
  140. package/lib/rehype-emphasize.test.mjs +39 -0
  141. package/lib/section-in-view.d.mts +8 -0
  142. package/lib/section-in-view.mjs +25 -0
  143. package/lib/section-in-view.test.mjs +35 -0
  144. package/lib/section-marker.d.mts +5 -0
  145. package/lib/section-marker.mjs +18 -0
  146. package/lib/section-marker.test.mjs +22 -0
  147. package/lib/section-ranges.d.mts +12 -0
  148. package/lib/section-ranges.mjs +43 -0
  149. package/lib/section-ranges.test.mjs +61 -0
  150. package/lib/sheet-stops.d.mts +68 -0
  151. package/lib/sheet-stops.mjs +198 -0
  152. package/lib/sheet-stops.test.mjs +155 -0
  153. package/lib/still-timer.d.mts +5 -0
  154. package/lib/still-timer.mjs +17 -0
  155. package/lib/still-timer.test.mjs +42 -0
  156. package/lib/text-sections.d.mts +10 -0
  157. package/lib/text-sections.mjs +45 -0
  158. package/lib/text-sections.test.mjs +78 -0
  159. package/lib/video.mjs +2 -1
  160. package/lib/without-h1.d.mts +1 -0
  161. package/lib/without-h1.mjs +7 -0
  162. package/lib/without-h1.test.mjs +26 -0
  163. package/lib/word-at.d.mts +6 -0
  164. package/lib/word-at.mjs +20 -0
  165. package/lib/word-at.test.mjs +19 -0
  166. package/package.json +3 -1
  167. package/pages/editor/version.json.js +22 -2
  168. package/scripts/already-checked.mjs +11 -0
  169. package/scripts/already-checked.test.mjs +18 -0
  170. package/scripts/build-settings-problems.mjs +27 -0
  171. package/scripts/build-settings-problems.test.mjs +35 -0
  172. package/scripts/check-content.mjs +42 -4
  173. package/scripts/check-worker.mjs +96 -4
  174. package/scripts/ci-build.mjs +85 -0
  175. package/scripts/ci-build.test.mjs +137 -0
  176. package/scripts/editor-roundtrip.mjs +62 -3
  177. package/scripts/editor-screens.mjs +21 -7
  178. package/scripts/git-env.mjs +6 -0
  179. package/scripts/step-command.mjs +34 -0
  180. package/scripts/step-command.test.mjs +50 -0
  181. package/scripts/text-problems.mjs +83 -0
  182. package/scripts/text-problems.test.mjs +74 -0
  183. package/scripts/worker-modules.mjs +11 -1
  184. package/site/breakpoints.d.mts +6 -0
  185. package/site/breakpoints.mjs +52 -0
  186. package/site/breakpoints.test.mjs +36 -0
  187. package/site/casing.d.mts +3 -0
  188. package/site/casing.mjs +26 -0
  189. package/site/content.config.ts +2 -0
  190. package/site/content.ts +2 -4
  191. package/site/defineSite.d.mts +1 -0
  192. package/site/defineSite.mjs +3 -0
  193. package/site/home.ts +3 -2
  194. package/site/icons.d.mts +116 -4
  195. package/site/icons.mjs +62 -6
  196. package/site/images.mjs +1 -1
  197. package/site/live-diff.d.mts +22 -0
  198. package/site/live-diff.mjs +98 -0
  199. package/site/live-diff.test.mjs +235 -0
  200. package/site/materialSymbols.d.mts +5 -0
  201. package/site/materialSymbols.mjs +19 -0
  202. package/site/sections.ts +1 -1
  203. package/site/theme.d.mts +1 -1
  204. package/site/theme.mjs +7 -1
  205. package/src/ActionParts.tsx +1 -1
  206. package/src/Advanced.tsx +101 -20
  207. package/src/App.tsx +47 -121
  208. package/src/AppList.tsx +1 -1
  209. package/src/BlockParts.tsx +3 -3
  210. package/src/Colors.tsx +1 -1
  211. package/src/CommunicationParts.tsx +1 -1
  212. package/src/ContainmentParts.tsx +80 -28
  213. package/src/Deleted.tsx +18 -4
  214. package/src/EditorFooter.tsx +14 -0
  215. package/src/EntryForm.tsx +75 -39
  216. package/src/FormFrame.tsx +3 -8
  217. package/src/Library.tsx +46 -51
  218. package/src/Licenses.tsx +27 -11
  219. package/src/MainDrawerParts.tsx +60 -0
  220. package/src/MainMenu.tsx +136 -0
  221. package/src/MarkdownEditor.tsx +175 -32
  222. package/src/Media.tsx +69 -16
  223. package/src/MediaTags.tsx +91 -0
  224. package/src/ModalSheetSample.tsx +47 -0
  225. package/src/NavigationParts.tsx +67 -21
  226. package/src/NavigationRestParts.tsx +1 -1
  227. package/src/Parts.tsx +71 -0
  228. package/src/PartsShown.tsx +1 -1
  229. package/src/PickerParts.tsx +2 -2
  230. package/src/Redirects.tsx +3 -2
  231. package/src/Screen.tsx +93 -90
  232. package/src/SelectionParts.tsx +22 -6
  233. package/src/SiteLogoLink.tsx +14 -0
  234. package/src/StartFab.tsx +22 -0
  235. package/src/StartStatus.tsx +1 -1
  236. package/src/autoTags.d.mts +28 -0
  237. package/src/autoTags.mjs +68 -0
  238. package/src/autoTags.test.mjs +109 -0
  239. package/src/buildSettings.test.mjs +20 -0
  240. package/src/buildSettings.ts +20 -0
  241. package/src/checkSite.ts +14 -1
  242. package/src/deploy.ts +7 -9
  243. package/src/editor.css +150 -17
  244. package/src/eyebrow.d.mts +5 -0
  245. package/src/eyebrow.mjs +53 -0
  246. package/src/eyebrowState.d.mts +21 -0
  247. package/src/eyebrowState.mjs +62 -0
  248. package/src/eyebrowState.test.mjs +78 -0
  249. package/src/fileRecords.d.mts +115 -0
  250. package/src/fileRecords.mjs +224 -0
  251. package/src/fileRecords.test.mjs +224 -0
  252. package/src/fileTagFilter.d.mts +30 -0
  253. package/src/fileTagFilter.mjs +44 -0
  254. package/src/fileTagFilter.test.mjs +57 -0
  255. package/src/fileTags.d.mts +56 -0
  256. package/src/fileTags.mjs +100 -0
  257. package/src/fileTags.test.mjs +127 -0
  258. package/src/firstHeading.d.mts +6 -0
  259. package/src/firstHeading.mjs +37 -0
  260. package/src/i18n/da-DK/advanced.ts +27 -12
  261. package/src/i18n/da-DK/app.ts +9 -7
  262. package/src/i18n/da-DK/apps.ts +16 -16
  263. package/src/i18n/da-DK/blockSettings.ts +9 -9
  264. package/src/i18n/da-DK/blocks.ts +2 -2
  265. package/src/i18n/da-DK/check.ts +5 -2
  266. package/src/i18n/da-DK/cms.ts +14 -12
  267. package/src/i18n/da-DK/colors.ts +5 -5
  268. package/src/i18n/da-DK/common.ts +2 -2
  269. package/src/i18n/da-DK/company.ts +3 -3
  270. package/src/i18n/da-DK/consents.ts +6 -6
  271. package/src/i18n/da-DK/forms.ts +6 -6
  272. package/src/i18n/da-DK/history.ts +6 -6
  273. package/src/i18n/da-DK/keywords.ts +2 -2
  274. package/src/i18n/da-DK/library.ts +17 -15
  275. package/src/i18n/da-DK/licenses.ts +4 -4
  276. package/src/i18n/da-DK/media.ts +10 -4
  277. package/src/i18n/da-DK/parts.ts +62 -25
  278. package/src/i18n/da-DK/preview.ts +5 -6
  279. package/src/i18n/da-DK/redirects.ts +7 -7
  280. package/src/i18n/da-DK/seo.ts +1 -1
  281. package/src/i18n/da-DK/text.ts +2 -0
  282. package/src/i18n/da-DK/theme.ts +5 -5
  283. package/src/i18n/en-US/advanced.ts +34 -18
  284. package/src/i18n/en-US/app.ts +7 -5
  285. package/src/i18n/en-US/apps.ts +9 -9
  286. package/src/i18n/en-US/blockSettings.ts +4 -4
  287. package/src/i18n/en-US/blocks.ts +6 -6
  288. package/src/i18n/en-US/check.ts +6 -3
  289. package/src/i18n/en-US/cms.ts +15 -14
  290. package/src/i18n/en-US/colors.ts +18 -18
  291. package/src/i18n/en-US/common.ts +3 -3
  292. package/src/i18n/en-US/consents.ts +4 -4
  293. package/src/i18n/en-US/forms.ts +5 -5
  294. package/src/i18n/en-US/history.ts +2 -2
  295. package/src/i18n/en-US/library.ts +21 -15
  296. package/src/i18n/en-US/licenses.ts +6 -6
  297. package/src/i18n/en-US/media.ts +7 -1
  298. package/src/i18n/en-US/parts.ts +60 -21
  299. package/src/i18n/en-US/preview.ts +5 -6
  300. package/src/i18n/en-US/redirects.ts +3 -3
  301. package/src/i18n/en-US/seo.ts +1 -1
  302. package/src/i18n/en-US/text.ts +3 -0
  303. package/src/i18n/en-US/theme.ts +6 -6
  304. package/src/icon.tsx +90 -27
  305. package/src/licenseData.test.mjs +28 -0
  306. package/src/licenseData.ts +12 -1
  307. package/src/markdown.d.mts +2 -0
  308. package/src/markdown.mjs +73 -5
  309. package/src/markdown.test.mjs +74 -0
  310. package/src/materialIcons.ts +8 -0
  311. package/src/menuChildren.ts +36 -0
  312. package/src/menuOpen.ts +34 -0
  313. package/src/pagePreview.tsx +23 -19
  314. package/src/r2Caching.ts +14 -0
  315. package/src/sectionFrame.ts +39 -0
  316. package/src/sectionState.d.mts +19 -0
  317. package/src/sectionState.mjs +31 -0
  318. package/src/sectionState.test.mjs +59 -0
  319. package/src/sections.d.mts +9 -7
  320. package/src/sections.mjs +74 -27
  321. package/src/sections.test.mjs +59 -0
  322. package/src/sidebar.ts +2 -2
  323. package/src/site.ts +27 -17
  324. package/src/stillCursor.d.mts +8 -0
  325. package/src/stillCursor.mjs +19 -0
  326. package/src/stillCursor.test.mjs +26 -0
  327. package/src/tagUses.d.mts +47 -0
  328. package/src/tagUses.mjs +121 -0
  329. package/src/tagUses.test.mjs +141 -0
  330. package/src/themeUi.ts +10 -0
  331. package/src/topBarSlot.tsx +37 -0
  332. package/src/ui.tsx +654 -150
  333. package/src/virtual.d.ts +5 -0
  334. package/starter/README.md +2 -1
  335. package/starter/package.json +2 -1
  336. package/starter/wrangler.jsonc +3 -2
  337. package/theme/blocks/cards/LinkCards.tsx +2 -1
  338. package/theme/blocks/hero/View.tsx +2 -1
  339. package/theme/blocks/video/View.tsx +2 -1
  340. package/theme/components/BlogIndex.astro +2 -2
  341. package/theme/components/BlogPosts.astro +1 -1
  342. package/theme/components/BlogPosts.tsx +9 -6
  343. package/theme/components/Container.astro +1 -1
  344. package/theme/components/CookieConsent.astro +1 -1
  345. package/theme/components/EndBlock.astro +1 -1
  346. package/theme/components/Footer.astro +5 -5
  347. package/theme/components/Header.astro +3 -3
  348. package/theme/components/Icon.astro +2 -2
  349. package/theme/components/MobileMenu.tsx +72 -61
  350. package/theme/components/MobileNav.astro +2 -2
  351. package/theme/components/PageHeader.astro +2 -2
  352. package/theme/components/PageSection.astro +1 -1
  353. package/theme/components/PopupButton.tsx +2 -2
  354. package/theme/components/PopupView.tsx +2 -2
  355. package/theme/components/PostList.tsx +7 -3
  356. package/theme/components/RichText.astro +22 -7
  357. package/theme/components/Section.astro +17 -10
  358. package/theme/components/SectionHeading.tsx +1 -1
  359. package/theme/components/home/PageBlock.astro +4 -4
  360. package/theme/components/home/Text.astro +3 -3
  361. package/theme/layouts/PageLayout.astro +3 -3
  362. package/theme/layouts/PostLayout.astro +9 -8
  363. package/theme/styles/base.css +13 -3
  364. package/theme/styles/blocks.css +97 -10
  365. package/theme/styles/components.css +9 -9
  366. package/theme/styles/elevation.test.mjs +168 -0
  367. package/theme/styles/global.css +65 -59
  368. package/theme/styles/ui.css +460 -131
  369. package/theme/templates/Home.astro +1 -1
  370. package/theme/templates/New.astro +5 -5
  371. package/theme/templates/NotFound.astro +2 -2
  372. package/theme/ui/BottomSheet.tsx +118 -21
  373. package/theme/ui/BottomSheetResizeIcon.tsx +13 -0
  374. package/theme/ui/Button.tsx +4 -3
  375. package/theme/ui/Dialog.tsx +1 -1
  376. package/theme/ui/FabMenu.tsx +13 -12
  377. package/theme/ui/LoadingIndicator.tsx +1 -1
  378. package/theme/ui/NavigationDrawer.tsx +116 -15
  379. package/theme/ui/NavigationRail.tsx +22 -13
  380. package/theme/ui/Search.tsx +1 -1
  381. package/theme/ui/SettingRow.tsx +31 -0
  382. package/theme/ui/Switch.tsx +11 -11
  383. package/theme/ui/TextField.tsx +17 -6
  384. package/theme/ui/bottomSheet.script.ts +3 -2
  385. package/theme/ui/bottomSheetControl.ts +131 -46
  386. package/theme/ui/fabMenu.script.ts +69 -53
  387. package/theme/ui/modalSheetSize.ts +214 -0
  388. package/theme/ui/navigationDrawerControl.ts +35 -0
  389. package/theme/ui/search.script.ts +4 -2
  390. package/worker/index.js +3 -0
  391. package/worker/plan-status.js +46 -0
  392. package/worker/push.js +7 -5
  393. package/worker/r2-status.js +2 -1
  394. package/icons/lucide/desktop.svg +0 -1
  395. package/icons/material-symbols/desktop.svg +0 -1
  396. package/icons/phosphor/desktop.svg +0 -1
package/site/icons.mjs CHANGED
@@ -114,7 +114,9 @@ export const MATERIAL_SYMBOLS = {
114
114
  'book-open': 'import_contacts',
115
115
  'book-open-text': 'menu_book',
116
116
  browser: 'web',
117
+ 'browser-fill': 'web-fill',
117
118
  buildings: 'domain',
119
+ 'buildings-fill': 'domain-fill',
118
120
  bus: 'directions_bus',
119
121
  'calendar-blank': 'calendar_today',
120
122
  'calendar-check': 'event_available',
@@ -126,8 +128,11 @@ export const MATERIAL_SYMBOLS = {
126
128
  'caret-right': 'chevron_right',
127
129
  'caret-up': 'keyboard_arrow_up',
128
130
  certificate: 'license',
131
+ 'certificate-fill': 'license-fill',
129
132
  'chart-line-up': 'monitoring',
133
+ 'chart-line-up-fill': 'monitoring-fill',
130
134
  chats: 'forum',
135
+ 'chats-fill': 'forum-fill',
131
136
  check: 'check',
132
137
  'check-circle': 'check_circle',
133
138
  circle: 'circle',
@@ -143,28 +148,33 @@ export const MATERIAL_SYMBOLS = {
143
148
  'currency-circle-dollar': 'paid',
144
149
  'cursor-click': 'ads_click',
145
150
  database: 'database',
146
- desktop: 'desktop_windows',
147
151
  'device-mobile': 'mobile',
148
152
  'door-open': 'door_open',
149
153
  'download-simple': 'download',
150
154
  'envelope-simple': 'mail',
155
+ 'envelope-simple-fill': 'mail-fill',
151
156
  eye: 'visibility',
152
157
  'eye-slash': 'visibility_off',
153
158
  'file-pdf': 'picture_as_pdf',
154
159
  'file-text': 'description',
160
+ 'file-text-fill': 'description-fill',
155
161
  'file-video': 'video_file',
156
162
  'file-zip': 'folder_zip',
157
163
  'flower-lotus': 'spa',
158
164
  'folder-simple': 'folder',
165
+ 'folder-simple-fill': 'folder-fill',
159
166
  funnel: 'filter_alt',
160
- gauge: 'speed',
167
+ gauge: 'dashboard',
161
168
  gear: 'settings',
162
169
  globe: 'language',
170
+ 'globe-simple': 'language',
171
+ 'gear-six': 'settings',
163
172
  'graduation-cap': 'school',
164
173
  hammer: 'hardware',
165
174
  'hand-heart': 'volunteer_activism',
166
175
  handshake: 'handshake',
167
176
  heart: 'favorite',
177
+ 'heart-fill': 'favorite-fill',
168
178
  house: 'home',
169
179
  'identification-card': 'id_card',
170
180
  image: 'image',
@@ -173,25 +183,31 @@ export const MATERIAL_SYMBOLS = {
173
183
  keyboard: 'keyboard',
174
184
  leaf: 'eco',
175
185
  lifebuoy: 'support',
186
+ layout: 'right_panel_open',
176
187
  lightbulb: 'lightbulb',
177
188
  'link-break': 'link_off',
178
189
  'link-simple': 'link',
179
190
  list: 'menu',
180
191
  'list-bullets': 'format_list_bulleted',
181
192
  'list-checks': 'checklist',
193
+ 'list-checks-fill': 'checklist-fill',
182
194
  'list-numbers': 'format_list_numbered',
183
195
  'lock-simple': 'lock',
184
196
  'lock-simple-open': 'lock_open',
185
197
  'magnifying-glass': 'search',
198
+ 'magnifying-glass-fill': 'search-fill',
186
199
  'map-pin': 'location_on',
187
200
  'map-trifold': 'map',
188
201
  medal: 'military_tech',
189
202
  megaphone: 'campaign',
203
+ 'megaphone-fill': 'campaign-fill',
190
204
  minus: 'remove',
191
205
  money: 'payments',
192
206
  moon: 'dark_mode',
193
207
  newspaper: 'newspaper',
208
+ 'newspaper-fill': 'newspaper-fill',
194
209
  'paint-brush-broad': 'brush',
210
+ 'paint-brush-broad-fill': 'brush-fill',
195
211
  palette: 'palette',
196
212
  'paper-plane-tilt': 'send',
197
213
  'pencil-simple': 'edit',
@@ -200,6 +216,7 @@ export const MATERIAL_SYMBOLS = {
200
216
  plus: 'add',
201
217
  prohibit: 'block',
202
218
  'puzzle-piece': 'extension',
219
+ 'puzzle-piece-fill': 'extension-fill',
203
220
  question: 'help',
204
221
  quotes: 'format_quote',
205
222
  receipt: 'receipt',
@@ -208,19 +225,26 @@ export const MATERIAL_SYMBOLS = {
208
225
  shapes: 'shapes',
209
226
  'share-network': 'share',
210
227
  'shield-check': 'verified_user',
211
- 'sidebar-simple': 'side_navigation',
228
+ 'shield-check-fill': 'verified_user-fill',
229
+ sidebar: 'left_panel_close',
230
+ 'sidebar-simple': 'left_panel_open',
212
231
  'sign-out': 'logout',
213
232
  signpost: 'signpost',
214
233
  'sliders-horizontal': 'tune',
215
234
  smiley: 'mood',
216
235
  sparkle: 'star_shine',
217
236
  square: 'square',
237
+ 'square-half': 'right_panel_close',
218
238
  'square-half-bottom': 'call_to_action',
239
+ 'square-half-bottom-fill': 'call_to_action-fill',
219
240
  'squares-four': 'grid_view',
241
+ 'squares-four-fill': 'grid_view-fill',
220
242
  sticker: 'brand_family',
221
243
  storefront: 'storefront',
244
+ 'storefront-fill': 'storefront-fill',
222
245
  sun: 'light_mode',
223
246
  tag: 'sell',
247
+ 'tag-fill': 'sell-fill',
224
248
  'text-aa': 'match_case',
225
249
  'text-align-left': 'format_align_left',
226
250
  'text-b': 'format_bold',
@@ -231,10 +255,13 @@ export const MATERIAL_SYMBOLS = {
231
255
  'text-t': 'title',
232
256
  ticket: 'confirmation_number',
233
257
  'toggle-right': 'toggle_on',
258
+ 'toggle-right-fill': 'toggle_on-fill',
234
259
  train: 'train',
235
260
  trash: 'delete',
261
+ 'trash-fill': 'delete-fill',
236
262
  'upload-simple': 'upload',
237
263
  user: 'person',
264
+ 'user-fill': 'person-fill',
238
265
  'user-circle': 'account_circle',
239
266
  users: 'group',
240
267
  'users-three': 'groups',
@@ -245,6 +272,7 @@ export const MATERIAL_SYMBOLS = {
245
272
  'wave-sine': 'animation',
246
273
  wheelchair: 'accessible',
247
274
  wrench: 'build',
275
+ 'wrench-fill': 'build-fill',
248
276
  x: 'close',
249
277
  };
250
278
 
@@ -273,7 +301,9 @@ export const LUCIDE = {
273
301
  'book-open': 'book-open',
274
302
  'book-open-text': 'book-open-text',
275
303
  browser: 'panel-top',
304
+ 'browser-fill': 'panel-top-fill',
276
305
  buildings: 'building-2',
306
+ 'buildings-fill': 'building-2-fill',
277
307
  bus: 'bus',
278
308
  'calendar-blank': 'calendar',
279
309
  'calendar-check': 'calendar-check',
@@ -285,8 +315,11 @@ export const LUCIDE = {
285
315
  'caret-right': 'chevron-right',
286
316
  'caret-up': 'chevron-up',
287
317
  certificate: 'award',
318
+ 'certificate-fill': 'award-fill',
288
319
  'chart-line-up': 'chart-line',
320
+ 'chart-line-up-fill': 'chart-line-fill',
289
321
  chats: 'messages-square',
322
+ 'chats-fill': 'messages-square-fill',
290
323
  check: 'check',
291
324
  'check-circle': 'circle-check',
292
325
  circle: 'circle',
@@ -302,29 +335,34 @@ export const LUCIDE = {
302
335
  'currency-circle-dollar': 'circle-dollar-sign',
303
336
  'cursor-click': 'mouse-pointer-click',
304
337
  database: 'database',
305
- desktop: 'monitor',
306
338
  'device-mobile': 'smartphone',
307
339
  'door-open': 'door-open',
308
340
  'download-simple': 'download',
309
341
  'envelope-simple': 'mail',
342
+ 'envelope-simple-fill': 'mail-fill',
310
343
  eye: 'eye',
311
344
  'eye-slash': 'eye-off',
312
345
  'file-pdf': 'file-text',
313
346
  'file-text': 'file-text',
347
+ 'file-text-fill': 'file-text-fill',
314
348
  'file-video': 'file-video-camera',
315
349
  'file-zip': 'file-archive',
316
350
  'flower-lotus': 'flower-2',
317
351
  'folder-simple': 'folder',
352
+ 'folder-simple-fill': 'folder-fill',
318
353
  funnel: 'funnel',
319
- gauge: 'gauge',
354
+ gauge: 'layout-dashboard',
320
355
  gear: 'settings',
321
356
  globe: 'globe',
357
+ 'globe-simple': 'globe',
358
+ 'gear-six': 'settings',
322
359
  'graduation-cap': 'graduation-cap',
323
360
  hammer: 'hammer',
324
361
  'hand-heart': 'hand-heart',
325
362
  handshake: 'handshake',
326
363
  'hard-drives': 'server',
327
364
  heart: 'heart',
365
+ 'heart-fill': 'heart-fill',
328
366
  house: 'house',
329
367
  'identification-card': 'id-card',
330
368
  image: 'image',
@@ -333,25 +371,31 @@ export const LUCIDE = {
333
371
  keyboard: 'keyboard',
334
372
  leaf: 'leaf',
335
373
  lifebuoy: 'life-buoy',
374
+ layout: 'panel-right-open',
336
375
  lightbulb: 'lightbulb',
337
376
  'link-break': 'unlink',
338
377
  'link-simple': 'link',
339
378
  list: 'menu',
340
379
  'list-bullets': 'list',
341
380
  'list-checks': 'list-checks',
381
+ 'list-checks-fill': 'list-checks-fill',
342
382
  'list-numbers': 'list-ordered',
343
383
  'lock-simple': 'lock',
344
384
  'lock-simple-open': 'lock-open',
345
385
  'magnifying-glass': 'search',
386
+ 'magnifying-glass-fill': 'search-fill',
346
387
  'map-pin': 'map-pin',
347
388
  'map-trifold': 'map',
348
389
  medal: 'medal',
349
390
  megaphone: 'megaphone',
391
+ 'megaphone-fill': 'megaphone-fill',
350
392
  minus: 'minus',
351
393
  money: 'banknote',
352
394
  moon: 'moon',
353
395
  newspaper: 'newspaper',
396
+ 'newspaper-fill': 'newspaper-fill',
354
397
  'paint-brush-broad': 'paintbrush',
398
+ 'paint-brush-broad-fill': 'paintbrush-fill',
355
399
  palette: 'palette',
356
400
  'paper-plane-tilt': 'send',
357
401
  'pencil-simple': 'pencil',
@@ -360,6 +404,7 @@ export const LUCIDE = {
360
404
  plus: 'plus',
361
405
  prohibit: 'ban',
362
406
  'puzzle-piece': 'puzzle',
407
+ 'puzzle-piece-fill': 'puzzle-fill',
363
408
  question: 'circle-question-mark',
364
409
  quotes: 'quote',
365
410
  receipt: 'receipt',
@@ -369,20 +414,27 @@ export const LUCIDE = {
369
414
  shapes: 'shapes',
370
415
  'share-network': 'share-2',
371
416
  'shield-check': 'shield-check',
372
- 'sidebar-simple': 'panel-left',
417
+ 'shield-check-fill': 'shield-check-fill',
418
+ sidebar: 'panel-left-close',
419
+ 'sidebar-simple': 'panel-left-open',
373
420
  'sign-out': 'log-out',
374
421
  signpost: 'signpost',
375
422
  'sliders-horizontal': 'sliders-horizontal',
376
423
  smiley: 'smile',
377
424
  sparkle: 'sparkle',
378
425
  square: 'square',
426
+ 'square-half': 'panel-right-close',
379
427
  'square-half-bottom': 'panel-bottom',
428
+ 'square-half-bottom-fill': 'panel-bottom-fill',
380
429
  'squares-four': 'layout-grid',
430
+ 'squares-four-fill': 'layout-grid-fill',
381
431
  sticker: 'sticker',
382
432
  storefront: 'store',
433
+ 'storefront-fill': 'store-fill',
383
434
  sun: 'sun',
384
435
  swatches: 'swatch-book',
385
436
  tag: 'tag',
437
+ 'tag-fill': 'tag-fill',
386
438
  'text-aa': 'case-sensitive',
387
439
  'text-align-left': 'text-align-start',
388
440
  'text-b': 'bold',
@@ -393,10 +445,13 @@ export const LUCIDE = {
393
445
  'text-t': 'type',
394
446
  ticket: 'ticket',
395
447
  'toggle-right': 'toggle-right',
448
+ 'toggle-right-fill': 'toggle-right-fill',
396
449
  train: 'train-front',
397
450
  trash: 'trash-2',
451
+ 'trash-fill': 'trash-2-fill',
398
452
  'upload-simple': 'upload',
399
453
  user: 'user',
454
+ 'user-fill': 'user-fill',
400
455
  'user-circle': 'circle-user',
401
456
  users: 'users',
402
457
  'users-three': 'users-round',
@@ -407,6 +462,7 @@ export const LUCIDE = {
407
462
  'wave-sine': 'spline',
408
463
  wheelchair: 'accessibility',
409
464
  wrench: 'wrench',
465
+ 'wrench-fill': 'wrench-fill',
410
466
  x: 'x',
411
467
  };
412
468
 
package/site/images.mjs CHANGED
@@ -23,7 +23,7 @@ export function responsive(src) {
23
23
  }
24
24
 
25
25
  // The sizes of a picture shown `scale` times wider than its box (cut to a format, crop.mjs), e.g.
26
- // "(min-width: 800px) 720px, calc(100vw - 40px)" twice as wide.
26
+ // a list like "720px, calc(100vw - 40px)" twice as wide.
27
27
  const scaled = (sizes, scale) =>
28
28
  scale === 1
29
29
  ? sizes
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @param {object} o
3
+ * @param {string} o.siteUrl the live site, `https://<domain>`
4
+ * @param {string} o.cwd the repository
5
+ * @param {typeof globalThis.fetch} [o.fetch]
6
+ * @param {string} [o.branch] the branch being built (WORKERS_CI_BRANCH; undefined outside Cloudflare)
7
+ * @param {string} o.repoBranch the site's own branch (repo.branch in its config)
8
+ * @param {number} [o.gitTimeout] milliseconds a git call may take (default 60 s)
9
+ * @returns {Promise<{ paths: string[] | null, reason: string, live: string | null }>}
10
+ */
11
+ export function changedSinceLive({ siteUrl, cwd, fetch, branch, repoBranch, gitTimeout }: {
12
+ siteUrl: string;
13
+ cwd: string;
14
+ fetch?: typeof globalThis.fetch | undefined;
15
+ branch?: string | undefined;
16
+ repoBranch: string;
17
+ gitTimeout?: number | undefined;
18
+ }): Promise<{
19
+ paths: string[] | null;
20
+ reason: string;
21
+ live: string | null;
22
+ }>;
@@ -0,0 +1,98 @@
1
+ // The files changed between the commit that is live (the site's /editor/version.json) and HEAD, for morpheme ci-build's
2
+ // lane decision (lib/build-lane.mjs); every doubt gives no list and a reason. Exposes changedSinceLive. Depends on git
3
+ // and fetch.
4
+ import { execFileSync } from 'node:child_process';
5
+
6
+ /** how long a git call may take; a fetch that stalls longer counts as failed, which gives the full lane. */
7
+ const GIT_TIMEOUT = 60_000;
8
+
9
+ /**
10
+ * Runs git in `cwd` and returns its stdout. Throws when git fails.
11
+ * @param {string} cwd
12
+ * @param {string[]} args
13
+ * @param {number} [timeout] milliseconds
14
+ * @returns {string}
15
+ */
16
+ function git(cwd, args, timeout = GIT_TIMEOUT) {
17
+ return execFileSync('git', args, {
18
+ cwd,
19
+ encoding: 'utf8',
20
+ stdio: ['ignore', 'pipe', 'pipe'],
21
+ timeout,
22
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
23
+ });
24
+ }
25
+
26
+ /**
27
+ * @param {object} o
28
+ * @param {string} o.siteUrl the live site, `https://<domain>`
29
+ * @param {string} o.cwd the repository
30
+ * @param {typeof globalThis.fetch} [o.fetch]
31
+ * @param {string} [o.branch] the branch being built (WORKERS_CI_BRANCH; undefined outside Cloudflare)
32
+ * @param {string} o.repoBranch the site's own branch (repo.branch in its config)
33
+ * @param {number} [o.gitTimeout] milliseconds a git call may take (default 60 s)
34
+ * @returns {Promise<{ paths: string[] | null, reason: string, live: string | null }>}
35
+ */
36
+ export async function changedSinceLive({ siteUrl, cwd, fetch = globalThis.fetch, branch, repoBranch, gitTimeout }) {
37
+ if (branch && branch !== repoBranch) {
38
+ return { paths: null, reason: 'branch preview', live: null };
39
+ }
40
+
41
+ let live;
42
+ try {
43
+ const res = await fetch(siteUrl + '/editor/version.json', { signal: AbortSignal.timeout(5000) });
44
+ if (res.status !== 200) {
45
+ return { paths: null, reason: 'live commit unknown', live: null };
46
+ }
47
+ const body = await res.json();
48
+ live = body?.commit;
49
+ if (typeof live !== 'string' || !/^[0-9a-f]{40}$/.test(live)) {
50
+ return { paths: null, reason: 'live commit unknown', live: null };
51
+ }
52
+ } catch {
53
+ return { paths: null, reason: 'live commit unknown', live: null };
54
+ }
55
+
56
+ try {
57
+ git(cwd, ['cat-file', '-e', live + '^{commit}']);
58
+ } catch {
59
+ try {
60
+ git(cwd, ['fetch', '--deepen=50', 'origin'], gitTimeout);
61
+ } catch {
62
+ // ignored: the retry below decides
63
+ }
64
+ try {
65
+ git(cwd, ['cat-file', '-e', live + '^{commit}']);
66
+ } catch {
67
+ return { paths: null, reason: 'live commit not in history', live };
68
+ }
69
+ }
70
+
71
+ try {
72
+ git(cwd, ['merge-base', '--is-ancestor', live, 'HEAD']);
73
+ } catch {
74
+ return { paths: null, reason: 'live commit not an ancestor of HEAD', live };
75
+ }
76
+
77
+ // The build runs on the working tree, so tracked changes that are not committed are code the diff cannot see.
78
+ if (git(cwd, ['status', '--porcelain', '--untracked-files=no']).trim().length > 0) {
79
+ return { paths: null, reason: 'uncommitted changes in the working tree', live };
80
+ }
81
+
82
+ // --no-renames lists both sides of a rename (else a code file moved into a content folder shows only its new path), -z and
83
+ // core.quotePath=false keep every path as it is, and diff.relative=false lists changes outside the working directory too.
84
+ const out = git(cwd, [
85
+ '-c',
86
+ 'core.quotePath=false',
87
+ '-c',
88
+ 'diff.relative=false',
89
+ 'diff',
90
+ '--no-renames',
91
+ '--name-only',
92
+ '-z',
93
+ live,
94
+ 'HEAD',
95
+ ]);
96
+ const paths = out.split('\0').filter((line) => line.length > 0);
97
+ return { paths, reason: 'diff against ' + live.slice(0, 7), live };
98
+ }
@@ -0,0 +1,235 @@
1
+ // Tests for site/live-diff.mjs: the files changed since the live commit, in a temporary git repository with the variables that
2
+ // point git at a repository removed (scripts/git-env.mjs) so no test touches the real one. Run: node --test packages/editor/site/*.test.mjs
3
+ import { test } from 'node:test';
4
+ import assert from 'node:assert/strict';
5
+ import { execFileSync } from 'node:child_process';
6
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
8
+ import { join, dirname } from 'node:path';
9
+ import { clearRepoEnv } from '../scripts/git-env.mjs';
10
+ import { changedSinceLive } from './live-diff.mjs';
11
+
12
+ // git sets GIT_DIR and friends for hooks (the pre-push hook runs these tests), and they would point every git call here at the real repository.
13
+ clearRepoEnv();
14
+
15
+ function repo() {
16
+ const dir = mkdtempSync(join(tmpdir(), 'live-diff-'));
17
+ execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: dir, encoding: 'utf8' });
18
+ return dir;
19
+ }
20
+
21
+ function git(dir, ...args) {
22
+ return execFileSync('git', ['-c', 'user.name=Test', '-c', 'user.email=test@example.com', ...args], {
23
+ cwd: dir,
24
+ encoding: 'utf8',
25
+ }).trim();
26
+ }
27
+
28
+ function commit(dir, file, text) {
29
+ mkdirSync(dirname(join(dir, file)), { recursive: true });
30
+ writeFileSync(join(dir, file), text);
31
+ git(dir, 'add', '-A');
32
+ git(dir, 'commit', '-q', '-m', file);
33
+ return git(dir, 'rev-parse', 'HEAD');
34
+ }
35
+
36
+ const serve = (body) => async () => ({ status: 200, json: async () => body });
37
+
38
+ const site = { siteUrl: 'https://example.com', repoBranch: 'main' };
39
+
40
+ test('lists the files changed since the live commit', async () => {
41
+ const dir = repo();
42
+ try {
43
+ const a = commit(dir, 'README.md', 'x');
44
+ commit(dir, 'src/content/a.md', 'y');
45
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: a }) });
46
+ assert.deepEqual(result.paths, ['src/content/a.md']);
47
+ assert.equal(result.live, a);
48
+ assert.equal(result.reason, 'diff against ' + a.slice(0, 7));
49
+ } finally {
50
+ rmSync(dir, { recursive: true, force: true });
51
+ }
52
+ });
53
+
54
+ test('a code commit that was never built is still listed', async () => {
55
+ const dir = repo();
56
+ try {
57
+ const a = commit(dir, 'README.md', 'x');
58
+ commit(dir, 'packages/editor/src/ui.tsx', 'code');
59
+ commit(dir, 'src/content/a.md', 'y');
60
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: a }) });
61
+ assert.deepEqual(result.paths, ['packages/editor/src/ui.tsx', 'src/content/a.md']);
62
+ } finally {
63
+ rmSync(dir, { recursive: true, force: true });
64
+ }
65
+ });
66
+
67
+ test('an unreachable site gives no list', async () => {
68
+ const dir = repo();
69
+ try {
70
+ commit(dir, 'README.md', 'x');
71
+ const result = await changedSinceLive({
72
+ ...site,
73
+ cwd: dir,
74
+ fetch: async () => {
75
+ throw new Error('offline');
76
+ },
77
+ });
78
+ assert.deepEqual(result, { paths: null, reason: 'live commit unknown', live: null });
79
+ } finally {
80
+ rmSync(dir, { recursive: true, force: true });
81
+ }
82
+ });
83
+
84
+ test('a malformed commit gives no list', async () => {
85
+ const dir = repo();
86
+ try {
87
+ commit(dir, 'README.md', 'x');
88
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: 'nope' }) });
89
+ assert.equal(result.reason, 'live commit unknown');
90
+ assert.equal(result.paths, null);
91
+ } finally {
92
+ rmSync(dir, { recursive: true, force: true });
93
+ }
94
+ });
95
+
96
+ test('a commit the repository does not have gives no list', async () => {
97
+ const dir = repo();
98
+ try {
99
+ commit(dir, 'README.md', 'x');
100
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: 'a'.repeat(40) }) });
101
+ assert.equal(result.paths, null);
102
+ assert.equal(result.reason, 'live commit not in history');
103
+ assert.equal(result.live, 'a'.repeat(40));
104
+ } finally {
105
+ rmSync(dir, { recursive: true, force: true });
106
+ }
107
+ });
108
+
109
+ test('a live commit off the branch gives no list', async () => {
110
+ const dir = repo();
111
+ try {
112
+ commit(dir, 'README.md', 'x');
113
+ git(dir, 'checkout', '-q', '-b', 'side');
114
+ const side = commit(dir, 'side.md', 's');
115
+ git(dir, 'checkout', '-q', 'main');
116
+ commit(dir, 'src/content/a.md', 'y');
117
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: side }) });
118
+ assert.equal(result.paths, null);
119
+ assert.equal(result.reason, 'live commit not an ancestor of HEAD');
120
+ } finally {
121
+ rmSync(dir, { recursive: true, force: true });
122
+ }
123
+ });
124
+
125
+ test('a branch preview gives no list and does not ask the site', async () => {
126
+ const dir = repo();
127
+ try {
128
+ commit(dir, 'README.md', 'x');
129
+ let asked = false;
130
+ const result = await changedSinceLive({
131
+ ...site,
132
+ cwd: dir,
133
+ branch: 'feature-x',
134
+ fetch: async () => {
135
+ asked = true;
136
+ throw new Error('asked');
137
+ },
138
+ });
139
+ assert.deepEqual(result, { paths: null, reason: 'branch preview', live: null });
140
+ assert.equal(asked, false);
141
+ } finally {
142
+ rmSync(dir, { recursive: true, force: true });
143
+ }
144
+ });
145
+
146
+ test('the live commit is HEAD: nothing changed', async () => {
147
+ const dir = repo();
148
+ try {
149
+ const a = commit(dir, 'README.md', 'x');
150
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: a }) });
151
+ assert.deepEqual(result.paths, []);
152
+ } finally {
153
+ rmSync(dir, { recursive: true, force: true });
154
+ }
155
+ });
156
+
157
+ test('a code file renamed into src/content/ lists the old path too', async () => {
158
+ const dir = repo();
159
+ try {
160
+ const a = commit(dir, 'src/site/config.ts', 'export const config = {};\n'.repeat(20));
161
+ mkdirSync(join(dir, 'src/content'), { recursive: true });
162
+ git(dir, 'mv', 'src/site/config.ts', 'src/content/config.ts');
163
+ git(dir, 'commit', '-q', '-m', 'move');
164
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: a }) });
165
+ assert.deepEqual([...result.paths].sort(), ['src/content/config.ts', 'src/site/config.ts']);
166
+ } finally {
167
+ rmSync(dir, { recursive: true, force: true });
168
+ }
169
+ });
170
+
171
+ test('a path with non-ASCII letters or spaces is listed as it is', async () => {
172
+ const dir = repo();
173
+ try {
174
+ const a = commit(dir, 'README.md', 'x');
175
+ commit(dir, 'src/content/blå post.md', 'y');
176
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: a }) });
177
+ assert.deepEqual(result.paths, ['src/content/blå post.md']);
178
+ } finally {
179
+ rmSync(dir, { recursive: true, force: true });
180
+ }
181
+ });
182
+
183
+ test('an uncommitted change to a tracked file gives no list', async () => {
184
+ const dir = repo();
185
+ try {
186
+ const a = commit(dir, 'src/site/config.ts', 'x');
187
+ commit(dir, 'src/content/a.md', 'y');
188
+ writeFileSync(join(dir, 'src/site/config.ts'), 'edited');
189
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: a }) });
190
+ assert.equal(result.paths, null);
191
+ assert.equal(result.reason, 'uncommitted changes in the working tree');
192
+ } finally {
193
+ rmSync(dir, { recursive: true, force: true });
194
+ }
195
+ });
196
+
197
+ test('an untracked file does not count as an uncommitted change', async () => {
198
+ const dir = repo();
199
+ try {
200
+ const a = commit(dir, 'README.md', 'x');
201
+ commit(dir, 'src/content/a.md', 'y');
202
+ writeFileSync(join(dir, 'dist.txt'), 'build output');
203
+ const result = await changedSinceLive({ ...site, cwd: dir, fetch: serve({ commit: a }) });
204
+ assert.deepEqual(result.paths, ['src/content/a.md']);
205
+ } finally {
206
+ rmSync(dir, { recursive: true, force: true });
207
+ }
208
+ });
209
+
210
+ test('a fetch that stalls past the timeout gives the full lane', async () => {
211
+ const dir = repo();
212
+ try {
213
+ commit(dir, 'README.md', 'x');
214
+ // an origin whose transport never answers
215
+ git(dir, 'config', 'protocol.ext.allow', 'always');
216
+ git(dir, 'remote', 'add', 'origin', 'ext::node -e setTimeout(()=>{},1500)');
217
+ const started = Date.now();
218
+ const result = await changedSinceLive({
219
+ ...site,
220
+ cwd: dir,
221
+ gitTimeout: 300,
222
+ fetch: serve({ commit: 'a'.repeat(40) }),
223
+ });
224
+ assert.equal(result.paths, null);
225
+ assert.equal(result.reason, 'live commit not in history');
226
+ assert.ok(Date.now() - started < 1400, 'the stalled fetch was cut off');
227
+ } finally {
228
+ // the stalled transport process may still hold the directory (Windows); the temp dir is then left to the OS
229
+ try {
230
+ rmSync(dir, { recursive: true, force: true });
231
+ } catch {
232
+ // best effort
233
+ }
234
+ }
235
+ });
@@ -0,0 +1,5 @@
1
+ /** The module's source: `{ '/node_modules/@material-symbols/svg-400/outlined/<name>.svg': '<svg …>' }`, the keys as
2
+ * import.meta.glob names them (src/materialIcons.ts). With `ssr` false: an empty object. */
3
+ export function materialSsrModule(ssr: any): string;
4
+ /** The virtual module's id as imported (packages/editor/src/icon.tsx). */
5
+ export const MATERIAL_SSR: "virtual:material-symbols-ssr";